Compare commits
No commits in common. "dkondor-upstream-pr-2551" and "0.4.1" have entirely different histories.
dkondor-up
...
0.4.1
|
@ -2,35 +2,21 @@ image: alpine/edge
|
||||||
packages:
|
packages:
|
||||||
- eudev-dev
|
- eudev-dev
|
||||||
- ffmpeg-dev
|
- ffmpeg-dev
|
||||||
- glslang
|
- libcap-dev
|
||||||
- libinput-dev
|
- libinput-dev
|
||||||
- libxkbcommon-dev
|
- libxkbcommon-dev
|
||||||
- mesa-dev
|
- mesa-dev
|
||||||
- meson
|
- meson
|
||||||
- pixman-dev
|
- pixman-dev
|
||||||
- vulkan-headers
|
|
||||||
- vulkan-loader-dev
|
|
||||||
- wayland-dev
|
- wayland-dev
|
||||||
- wayland-protocols
|
- wayland-protocols
|
||||||
- xcb-util-image-dev
|
- xcb-util-image-dev
|
||||||
- xcb-util-renderutil-dev
|
|
||||||
- xcb-util-wm-dev
|
|
||||||
- xwayland
|
|
||||||
- libseat-dev
|
|
||||||
sources:
|
sources:
|
||||||
- https://gitlab.freedesktop.org/wlroots/wlroots.git
|
- https://github.com/swaywm/wlroots
|
||||||
tasks:
|
tasks:
|
||||||
- setup: |
|
- setup: |
|
||||||
cd wlroots
|
cd wlroots
|
||||||
meson build --fatal-meson-warnings --default-library=both -Dauto_features=enabled -Dxcb-errors=disabled
|
meson build
|
||||||
- build: |
|
- build: |
|
||||||
cd wlroots
|
cd wlroots
|
||||||
ninja -C build
|
ninja -C build
|
||||||
sudo ninja -C build install
|
|
||||||
- build-features-disabled: |
|
|
||||||
cd wlroots
|
|
||||||
meson build --reconfigure -Dauto_features=disabled
|
|
||||||
ninja -C build
|
|
||||||
- tinywl: |
|
|
||||||
cd wlroots/tinywl
|
|
||||||
make
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ image: archlinux
|
||||||
packages:
|
packages:
|
||||||
- clang
|
- clang
|
||||||
- ffmpeg
|
- ffmpeg
|
||||||
|
- libcap
|
||||||
- libinput
|
- libinput
|
||||||
- libxkbcommon
|
- libxkbcommon
|
||||||
- mesa
|
- mesa
|
||||||
|
@ -9,37 +10,17 @@ packages:
|
||||||
- pixman
|
- pixman
|
||||||
- wayland
|
- wayland
|
||||||
- wayland-protocols
|
- wayland-protocols
|
||||||
- xcb-util-errors
|
|
||||||
- xcb-util-image
|
- xcb-util-image
|
||||||
- xcb-util-renderutil
|
|
||||||
- xcb-util-wm
|
|
||||||
- xorg-xwayland
|
|
||||||
- seatd
|
|
||||||
- vulkan-icd-loader
|
|
||||||
- vulkan-headers
|
|
||||||
- glslang
|
|
||||||
sources:
|
sources:
|
||||||
- https://gitlab.freedesktop.org/wlroots/wlroots.git
|
- https://github.com/swaywm/wlroots
|
||||||
tasks:
|
tasks:
|
||||||
- setup: |
|
- setup: |
|
||||||
cd wlroots
|
cd wlroots
|
||||||
CC=gcc meson build-gcc --fatal-meson-warnings --default-library=both -Dauto_features=enabled --prefix /usr -Db_sanitize=address,undefined
|
CC=gcc meson build-gcc
|
||||||
CC=clang meson build-clang --fatal-meson-warnings -Dauto_features=enabled
|
CC=clang meson build-clang
|
||||||
- gcc: |
|
- gcc: |
|
||||||
cd wlroots/build-gcc
|
cd wlroots/build-gcc
|
||||||
ninja
|
ninja
|
||||||
sudo ninja install
|
|
||||||
cd ../tinywl
|
|
||||||
CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" make
|
|
||||||
- clang: |
|
- clang: |
|
||||||
cd wlroots/build-clang
|
cd wlroots/build-clang
|
||||||
ninja
|
ninja
|
||||||
- smoke-test: |
|
|
||||||
cd wlroots/tinywl
|
|
||||||
sudo modprobe vkms
|
|
||||||
udevadm settle
|
|
||||||
export WLR_BACKENDS=drm
|
|
||||||
export WLR_RENDERER=pixman
|
|
||||||
export WLR_DRM_DEVICES=/dev/dri/by-path/platform-vkms-card
|
|
||||||
sudo chmod ugo+rw /dev/dri/by-path/platform-vkms-card
|
|
||||||
sudo -E seatd-launch -- ./tinywl -s 'kill $PPID' || [ $? = 143 ]
|
|
||||||
|
|
|
@ -1,38 +1,42 @@
|
||||||
image: freebsd/latest
|
image: freebsd/latest
|
||||||
packages:
|
packages:
|
||||||
- devel/evdev-proto
|
- devel/evdev-proto
|
||||||
- devel/libepoll-shim
|
- devel/libepoll-shim
|
||||||
- devel/libudev-devd
|
- devel/libudev-devd
|
||||||
- devel/meson # implies ninja
|
- devel/meson # implies ninja
|
||||||
- devel/pkgconf
|
- devel/pkgconf
|
||||||
- graphics/glslang
|
- graphics/libdrm
|
||||||
- graphics/libdrm
|
- graphics/mesa-libs
|
||||||
- graphics/mesa-libs
|
- graphics/png
|
||||||
- graphics/png
|
- graphics/wayland
|
||||||
- graphics/vulkan-headers
|
- graphics/wayland-protocols
|
||||||
- graphics/vulkan-loader
|
- multimedia/ffmpeg
|
||||||
- graphics/wayland
|
- x11/libX11
|
||||||
- graphics/wayland-protocols
|
- x11/libinput
|
||||||
- multimedia/ffmpeg
|
- x11/libxcb
|
||||||
- x11/libX11
|
- x11/libxkbcommon
|
||||||
- x11/libinput
|
- x11/pixman
|
||||||
- x11/libxcb
|
- x11/xcb-util-errors
|
||||||
- x11/libxkbcommon
|
- x11/xcb-util-wm
|
||||||
- x11/pixman
|
|
||||||
- x11/xcb-util-errors
|
|
||||||
- x11/xcb-util-renderutil
|
|
||||||
- x11/xcb-util-wm
|
|
||||||
- x11-servers/xwayland
|
|
||||||
- sysutils/seatd
|
|
||||||
- gmake
|
|
||||||
sources:
|
sources:
|
||||||
- https://gitlab.freedesktop.org/wlroots/wlroots.git
|
- https://github.com/swaywm/wlroots
|
||||||
tasks:
|
tasks:
|
||||||
- wlroots: |
|
- fixup_epoll: |
|
||||||
cd wlroots
|
cat << 'EOF' | sudo tee /usr/local/libdata/pkgconfig/epoll-shim.pc
|
||||||
meson build --fatal-meson-warnings -Dauto_features=enabled
|
prefix=/usr/local
|
||||||
ninja -C build
|
exec_prefix=\$\{\$prefix\}
|
||||||
sudo ninja -C build install
|
libdir=${prefix}/lib
|
||||||
- tinywl: |
|
sharedlibdir=${prefix}/lib
|
||||||
cd wlroots/tinywl
|
includedir=${prefix}/include/libepoll-shim
|
||||||
gmake
|
Name: epoll-shim
|
||||||
|
Description: epoll shim implemented using kevent
|
||||||
|
Version: 0
|
||||||
|
Requires:
|
||||||
|
Libs: -L${libdir} -L${sharedlibdir} -lepoll-shim
|
||||||
|
Libs.private: -pthread -lrt
|
||||||
|
Cflags: -I${includedir}
|
||||||
|
EOF
|
||||||
|
- wlroots: |
|
||||||
|
cd wlroots
|
||||||
|
meson build
|
||||||
|
ninja -C build
|
||||||
|
|
|
@ -7,3 +7,4 @@ build/
|
||||||
build-*/
|
build-*/
|
||||||
wayland-*-protocol.*
|
wayland-*-protocol.*
|
||||||
wlr-example.ini
|
wlr-example.ini
|
||||||
|
rootston.ini
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
include: https://git.sr.ht/~emersion/dalligi/blob/master/templates/multi.yml
|
|
||||||
alpine:
|
|
||||||
extends: .dalligi
|
|
||||||
archlinux:
|
|
||||||
extends: .dalligi
|
|
||||||
freebsd:
|
|
||||||
extends: .dalligi
|
|
131
CONTRIBUTING.md
131
CONTRIBUTING.md
|
@ -1,21 +1,22 @@
|
||||||
# Contributing to wlroots
|
# Contributing to wlroots
|
||||||
|
|
||||||
Contributing just involves sending a merge request. You will probably be more
|
Contributing just involves sending a pull request. You will probably be more
|
||||||
successful with your contribution if you visit [#sway-devel on Libera Chat]
|
successful with your contribution if you visit
|
||||||
upfront and discuss your plans.
|
[#sway-devel](https://webchat.freenode.net/?channels=sway-devel) on
|
||||||
|
irc.freenode.net upfront and discuss your plans.
|
||||||
|
|
||||||
Note: rules are made to be broken. Adjust or ignore any/all of these as you see
|
Note: rules are made to be broken. Adjust or ignore any/all of these as you see
|
||||||
fit, but be prepared to justify it to your peers.
|
fit, but be prepared to justify it to your peers.
|
||||||
|
|
||||||
## Merge Requests
|
## Pull Requests
|
||||||
|
|
||||||
If you already have your own merge request habits, feel free to use them. If you
|
If you already have your own pull request habits, feel free to use them. If you
|
||||||
don't, however, allow me to make a suggestion: feature branches pulled from
|
don't, however, allow me to make a suggestion: feature branches pulled from
|
||||||
upstream. Try this:
|
upstream. Try this:
|
||||||
|
|
||||||
1. Fork wlroots
|
1. Fork wlroots
|
||||||
2. `git clone git@gitlab.freedesktop.org:<username>/wlroots.git && cd wlroots`
|
2. `git clone https://github.com/username/wlroots && cd wlroots`
|
||||||
3. `git remote add upstream https://gitlab.freedesktop.org/wlroots/wlroots.git`
|
3. `git remote add upstream https://github.com/swaywm/wlroots`
|
||||||
|
|
||||||
You only need to do this once. You're never going to use your fork's master
|
You only need to do this once. You're never going to use your fork's master
|
||||||
branch. Instead, when you start working on a feature, do this:
|
branch. Instead, when you start working on a feature, do this:
|
||||||
|
@ -24,74 +25,42 @@ branch. Instead, when you start working on a feature, do this:
|
||||||
2. `git checkout -b add-so-and-so-feature upstream/master`
|
2. `git checkout -b add-so-and-so-feature upstream/master`
|
||||||
3. Add and commit your changes
|
3. Add and commit your changes
|
||||||
4. `git push -u origin add-so-and-so-feature`
|
4. `git push -u origin add-so-and-so-feature`
|
||||||
5. Make a merge request from your feature branch
|
5. Make a pull request from your feature branch
|
||||||
|
|
||||||
When you submit your merge request, your commit log should do most of the talking
|
When you submit your pull request, your commit log should do most of the talking
|
||||||
when it comes to describing your changes and their motivation. In addition to
|
when it comes to describing your changes and their motivation. In addition to
|
||||||
this, your merge request's comments will ideally include a test plan that the
|
this, your pull request's comments will ideally include a test plan that the
|
||||||
reviewers can use to (1) demonstrate the problem on master, if applicable and
|
reviewers can use to (1) demonstrate the problem on master, if applicable and
|
||||||
(2) verify that the problem no longer exists with your changes applied (or that
|
(2) verify that the problem no longer exists with your changes applied (or that
|
||||||
your new features work correctly). Document all of the edge cases you're aware
|
your new features work correctly). Document all of the edge cases you're aware
|
||||||
of so we can adequately test them - then verify the test plan yourself before
|
of so we can adequately test them - then verify the test plan yourself before
|
||||||
submitting.
|
submitting.
|
||||||
|
|
||||||
## Commit Log
|
|
||||||
|
|
||||||
Unlike many projects using GitHub and GitLab, wlroots has a [linear, "recipe"
|
|
||||||
style] history. This means that every commit should be small, digestible,
|
|
||||||
stand-alone, and functional. Rather than a purely chronological commit history
|
|
||||||
like this:
|
|
||||||
|
|
||||||
```
|
|
||||||
doc: final docs for view transforms
|
|
||||||
fix tests when disabled, redo broken doc formatting
|
|
||||||
better transformed-view iteration (thanks Hannah!)
|
|
||||||
try to catch more cases in tests
|
|
||||||
tests: add new spline test
|
|
||||||
fix compilation on splines
|
|
||||||
doc: notes on reticulating splines
|
|
||||||
compositor: add spline reticulation for view transforms
|
|
||||||
```
|
|
||||||
|
|
||||||
We aim to have a clean history which only reflects the final state, broken up
|
|
||||||
into functional groupings:
|
|
||||||
|
|
||||||
```
|
|
||||||
compositor: add spline reticulation for view transforms
|
|
||||||
compositor: new iterator for view transforms
|
|
||||||
tests: add view-transform correctness tests
|
|
||||||
doc: fix formatting for view transforms
|
|
||||||
```
|
|
||||||
|
|
||||||
This ensures that the final patch series only contains the final state,
|
|
||||||
without the changes and missteps taken along the development process. A linear
|
|
||||||
history eases reviewing, cherry-picking and reverting changes.
|
|
||||||
|
|
||||||
If you aren't comfortable with manipulating the Git history, have a look at
|
|
||||||
[git-rebase.io].
|
|
||||||
|
|
||||||
## Commit Messages
|
## Commit Messages
|
||||||
|
|
||||||
Please strive to write good commit messages. Here's some guidelines to follow:
|
Please strive to write good commit messages. Here's some guidelines to follow:
|
||||||
|
|
||||||
The first line should be limited to 50 characters and should be a sentence that
|
The first line should be limited to 50 characters and should be a sentence that
|
||||||
completes the thought [When applied, this commit will...] *"Implement
|
completes the thought [When applied, this commit will...] *"Implement
|
||||||
cmd_move"* or *"Improve performance of arrange_windows on ARM"* or similar.
|
cmd_move"* or *"Fix #742"* or *"Improve performance of arrange_windows on ARM"*
|
||||||
|
or similar.
|
||||||
|
|
||||||
The subsequent lines should be separated from the subject line by a single
|
The subsequent lines should be separated from the subject line by a single
|
||||||
blank line, and include optional details. In this you can give justification
|
blank line, and include optional details. In this you can give justification
|
||||||
for the change, [reference issues], or explain some of the subtler
|
for the change, [reference Github
|
||||||
details of your patch. This is important because when someone finds a line of
|
issues](https://help.github.com/articles/closing-issues-via-commit-messages/),
|
||||||
code they don't understand later, they can use the `git blame` command to find
|
or explain some of the subtler details of your patch. This is important because
|
||||||
out what the author was thinking when they wrote it. It's also easier to review
|
when someone finds a line of code they don't understand later, they can use the
|
||||||
your merge requests if they're separated into logical commits that have good
|
`git blame` command to find out what the author was thinking when they wrote
|
||||||
commit messages and justify themselves in the extended commit description.
|
it. It's also easier to review your pull requests if they're separated into
|
||||||
|
logical commits that have good commit messages and justify themselves in the
|
||||||
|
extended commit description.
|
||||||
|
|
||||||
As a good rule of thumb, anything you might put into the merge request
|
As a good rule of thumb, anything you might put into the pull request
|
||||||
description on GitLab is probably fair game for going into the extended commit
|
description on Github is probably fair game for going into the extended commit
|
||||||
message as well.
|
message as well.
|
||||||
|
|
||||||
See [How to Write a Git Commit Message] for more details.
|
See [here](https://chris.beams.io/posts/git-commit/) for more details.
|
||||||
|
|
||||||
## Code Review
|
## Code Review
|
||||||
|
|
||||||
|
@ -101,29 +70,23 @@ changes will typically see review from several people. Be prepared to receive
|
||||||
some feedback - you may be asked to make changes to your work. Our code review
|
some feedback - you may be asked to make changes to your work. Our code review
|
||||||
process is:
|
process is:
|
||||||
|
|
||||||
1. **Triage** the merge request. Do the commit messages make sense? Is a test
|
1. **Triage** the pull request. Do the commit messages make sense? Is a test
|
||||||
plan necessary and/or present? Add anyone as reviewers that you think should
|
plan necessary and/or present? Add anyone as reviewers that you think should
|
||||||
be there (using the relevant GitLab feature, if you have the permissions, or
|
be there (using the relevant GitHub feature, if you have the permissions, or
|
||||||
with an @mention if necessary).
|
with an @mention if necessary).
|
||||||
2. **Review** the code. Look for code style violations, naming convention
|
2. **Review** the code. Look for code style violations, naming convention
|
||||||
violations, buffer overflows, memory leaks, logic errors, non-portable code
|
violations, buffer overflows, memory leaks, logic errors, non-portable code
|
||||||
(including GNU-isms), etc. For significant changes to the public API, loop in
|
(including GNU-isms), etc. For significant changes to the public API, loop in
|
||||||
a couple more people for discussion.
|
a couple more people for discussion.
|
||||||
3. **Execute** the test plan, if present.
|
3. **Execute** the test plan, if present.
|
||||||
4. **Merge** the merge request when all reviewers approve.
|
4. **Merge** the pull request when all reviewers approve.
|
||||||
5. **File** follow-up tickets if appropriate.
|
5. **File** follow-up tickets if appropriate.
|
||||||
|
|
||||||
## Code of Conduct
|
|
||||||
|
|
||||||
Note that as a project hosted on freedesktop.org, wlroots follows its
|
|
||||||
[Code of Conduct], based on the Contributor Covenant. Please conduct yourself
|
|
||||||
in a respectful and civilized manner when communicating with community members
|
|
||||||
on IRC and bug tracker.
|
|
||||||
|
|
||||||
## Style Reference
|
## Style Reference
|
||||||
|
|
||||||
wlroots is written in C with a style similar to the [kernel style], but with a
|
wlroots is written in C with a style similar to the [kernel
|
||||||
few notable differences.
|
style](https://www.kernel.org/doc/Documentation/process/coding-style.rst), but
|
||||||
|
with a few notable differences.
|
||||||
|
|
||||||
Try to keep your code conforming to C11 and POSIX as much as possible, and do
|
Try to keep your code conforming to C11 and POSIX as much as possible, and do
|
||||||
not use GNU extensions.
|
not use GNU extensions.
|
||||||
|
@ -200,7 +163,7 @@ zeroed value and exit cleanly; this simplifies error handling a lot.
|
||||||
### Error Codes
|
### Error Codes
|
||||||
|
|
||||||
For functions not returning a value, they should return a (stdbool.h) bool to
|
For functions not returning a value, they should return a (stdbool.h) bool to
|
||||||
indicate whether they succeeded or not.
|
indicated if they succeeded or not.
|
||||||
|
|
||||||
### Macros
|
### Macros
|
||||||
|
|
||||||
|
@ -279,12 +242,14 @@ at least one struct for each interface in the protocol. For instance,
|
||||||
### Globals
|
### Globals
|
||||||
|
|
||||||
Global interfaces generally have public constructors and destructors. Their
|
Global interfaces generally have public constructors and destructors. Their
|
||||||
struct has a field holding the `wl_global` itself, a destroy signal and a
|
struct has a field holding the `wl_global` itself, a list of resources clients
|
||||||
`wl_display` destroy listener. Example:
|
created by binding to the global, a destroy signal and a `wl_display` destroy
|
||||||
|
listener. Example:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
struct wlr_compositor {
|
struct wlr_compositor {
|
||||||
struct wl_global *global;
|
struct wl_global *global;
|
||||||
|
struct wl_list resources;
|
||||||
…
|
…
|
||||||
|
|
||||||
struct wl_listener display_destroy;
|
struct wl_listener display_destroy;
|
||||||
|
@ -297,9 +262,8 @@ struct wlr_compositor {
|
||||||
```
|
```
|
||||||
|
|
||||||
When the destructor is called, it should emit the destroy signal, remove the
|
When the destructor is called, it should emit the destroy signal, remove the
|
||||||
display destroy listener, destroy the `wl_global` and then destroy the struct.
|
display destroy listener, destroy the `wl_global`, destroy all bound resources
|
||||||
The destructor can assume all clients and resources have been already
|
and then destroy the struct.
|
||||||
destroyed.
|
|
||||||
|
|
||||||
### Resources
|
### Resources
|
||||||
|
|
||||||
|
@ -321,22 +285,13 @@ struct wlr_surface *wlr_surface_from_resource(struct wl_resource *resource) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If a pointer to a `wl_resource` is stored, a resource destroy handler needs to
|
|
||||||
be registered to clean it up. libwayland will automatically destroy resources
|
|
||||||
in an arbitrary order when a client is disconnected, the compositor must handle
|
|
||||||
this correctly.
|
|
||||||
|
|
||||||
### Destroying resources
|
### Destroying resources
|
||||||
|
|
||||||
Object structs should only be destroyed when their resource is destroyed, ie.
|
Object structs should only be destroyed when their resource is destroyed, ie.
|
||||||
in the resource destroy handler (set with `wl_resource_set_implementation`).
|
in the resource destroy handler (set with `wl_resource_set_implementation`).
|
||||||
|
Destructor requests should only call `wl_resource_destroy`.
|
||||||
|
|
||||||
- If the object has a destructor request: the request handler should just call
|
The compositor should not destroy resources on its own.
|
||||||
`wl_resource_destroy` and do nothing else. The compositor must not destroy
|
|
||||||
resources on its own outside the destructor request handler.
|
|
||||||
- If the protocol specifies that an object is destroyed when an event is sent:
|
|
||||||
it's the only case where the compositor is allowed to send the event and then
|
|
||||||
call `wl_resource_destroy`. An example of this is `wl_callback`.
|
|
||||||
|
|
||||||
### Inert resources
|
### Inert resources
|
||||||
|
|
||||||
|
@ -399,11 +354,3 @@ static void subsurface_handle_surface_destroy(struct wl_listener *listener,
|
||||||
subsurface_destroy(subsurface);
|
subsurface_destroy(subsurface);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
[#sway-devel on Libera Chat]: https://web.libera.chat/gamja/?channels=#sway-devel
|
|
||||||
[linear, "recipe" style]: https://www.bitsnbites.eu/git-history-work-log-vs-recipe/
|
|
||||||
[git-rebase.io]: https://git-rebase.io/
|
|
||||||
[reference issues]: https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically
|
|
||||||
[Code of Conduct]: https://www.freedesktop.org/wiki/CodeOfConduct/
|
|
||||||
[How to Write a Git Commit Message]: https://chris.beams.io/posts/git-commit/
|
|
||||||
[kernel style]: https://www.kernel.org/doc/Documentation/process/coding-style.rst
|
|
||||||
|
|
86
README.md
86
README.md
|
@ -1,31 +1,26 @@
|
||||||
# blankie/wlroots - dkondor-upstream-pr-2551
|
|
||||||
|
|
||||||
This branch "frontports" https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/2551,
|
|
||||||
should be updated when there's a new wlroots version and be unmaintained
|
|
||||||
when the pull request above is merged
|
|
||||||
|
|
||||||
# wlroots
|
# wlroots
|
||||||
|
|
||||||
Pluggable, composable, unopinionated modules for building a [Wayland]
|
Pluggable, composable, unopinionated modules for building a
|
||||||
compositor; or about 60,000 lines of code you were going to write anyway.
|
[Wayland](http://wayland.freedesktop.org/) compositor; or about 50,000 lines of
|
||||||
|
code you were going to write anyway.
|
||||||
|
|
||||||
- wlroots provides backends that abstract the underlying display and input
|
- wlroots provides backends that abstract the underlying display and input
|
||||||
hardware, including KMS/DRM, libinput, Wayland, X11, and headless backends,
|
hardware, including KMS/DRM, libinput, Wayland, X11, and headless backends,
|
||||||
plus any custom backends you choose to write, which can all be created or
|
plus any custom backends you choose to write, which can all be created or
|
||||||
destroyed at runtime and used in concert with each other.
|
destroyed at runtime and used in concert with each other.
|
||||||
- wlroots provides unopinionated, mostly standalone implementations of many
|
- wlroots provides unopinionated, mostly standalone implementations of many
|
||||||
Wayland interfaces, both from wayland.xml and various protocol extensions.
|
Wayland interfaces, both from wayland.xml and various protocol extensions.
|
||||||
We also promote the standardization of portable extensions across
|
We also promote the standardization of portable extensions across
|
||||||
many compositors.
|
many compositors.
|
||||||
- wlroots provides several powerful, standalone, and optional tools that
|
- wlroots provides several powerful, standalone, and optional tools that
|
||||||
implement components common to many compositors, such as the arrangement of
|
implement components common to many compositors, such as the arrangement of
|
||||||
outputs in physical space.
|
outputs in physical space.
|
||||||
- wlroots provides an Xwayland abstraction that allows you to have excellent
|
- wlroots provides an Xwayland abstraction that allows you to have excellent
|
||||||
Xwayland support without worrying about writing your own X11 window manager
|
Xwayland support without worrying about writing your own X11 window manager
|
||||||
on top of writing your compositor.
|
on top of writing your compositor.
|
||||||
- wlroots provides a renderer abstraction that simple compositors can use to
|
- wlroots provides a renderer abstraction that simple compositors can use to
|
||||||
avoid writing GL code directly, but which steps out of the way when your
|
avoid writing GL code directly, but which steps out of the way when your
|
||||||
needs demand custom rendering code.
|
needs demand custom rendering code.
|
||||||
|
|
||||||
wlroots implements a huge variety of Wayland compositor features and implements
|
wlroots implements a huge variety of Wayland compositor features and implements
|
||||||
them *right*, so you can focus on the features that make your compositor
|
them *right*, so you can focus on the features that make your compositor
|
||||||
|
@ -35,12 +30,13 @@ development tools - or any subset of these features you like, because all of
|
||||||
them work independently of one another and freely compose with anything you want
|
them work independently of one another and freely compose with anything you want
|
||||||
to implement yourself.
|
to implement yourself.
|
||||||
|
|
||||||
Check out our [wiki] to get started with wlroots. Join our IRC channel:
|
Check out our [wiki](https://github.com/swaywm/wlroots/wiki/Getting-started) to
|
||||||
[#sway-devel on Libera Chat].
|
get started with wlroots.
|
||||||
|
|
||||||
wlroots is developed under the direction of the [sway] project. A variety of
|
wlroots is developed under the direction of the
|
||||||
[wrapper libraries] are available for using it with your favorite programming
|
[sway](https://github.com/swaywm/sway) project. A variety of wrapper libraries
|
||||||
language.
|
[are available](https://github.com/swaywm) for using it with your favorite
|
||||||
|
programming language.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
|
@ -49,41 +45,39 @@ Install dependencies:
|
||||||
* meson
|
* meson
|
||||||
* wayland
|
* wayland
|
||||||
* wayland-protocols
|
* wayland-protocols
|
||||||
* EGL and GLESv2 (optional, for the GLES2 renderer)
|
* EGL
|
||||||
* Vulkan loader, headers and glslang (optional, for the Vulkan renderer)
|
* GLESv2
|
||||||
* libdrm
|
* libdrm
|
||||||
* GBM
|
* GBM
|
||||||
* libinput (optional, for the libinput backend)
|
* libinput
|
||||||
* xkbcommon
|
* xkbcommon
|
||||||
* udev
|
* udev
|
||||||
* pixman
|
* pixman
|
||||||
* [libseat]
|
* systemd (optional, for logind support)
|
||||||
|
* elogind (optional, for logind support on systems without systemd)
|
||||||
|
* libcap (optional, for capability support)
|
||||||
|
|
||||||
If you choose to enable X11 support:
|
If you choose to enable X11 support:
|
||||||
|
|
||||||
* xwayland (build-time only, optional at runtime)
|
* xcb
|
||||||
* libxcb
|
* xcb-composite
|
||||||
* libxcb-render-util
|
* xcb-xfixes
|
||||||
* libxcb-wm
|
* xcb-xinput
|
||||||
* libxcb-errors (optional, for improved error reporting)
|
* xcb-image
|
||||||
|
* xcb-render
|
||||||
|
* x11-xcb
|
||||||
|
* xcb-errors (optional, for improved error reporting)
|
||||||
|
* x11-icccm (optional, for improved Xwayland introspection)
|
||||||
|
|
||||||
Run these commands:
|
Run these commands:
|
||||||
|
|
||||||
meson build/
|
meson build
|
||||||
ninja -C build/
|
ninja -C build
|
||||||
|
|
||||||
Install like so:
|
Install like so:
|
||||||
|
|
||||||
sudo ninja -C build/ install
|
sudo ninja -C build install
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
See [CONTRIBUTING.md].
|
See [CONTRIBUTING.md](https://github.com/swaywm/wlroots/blob/master/CONTRIBUTING.md).
|
||||||
|
|
||||||
[Wayland]: https://wayland.freedesktop.org/
|
|
||||||
[wiki]: https://gitlab.freedesktop.org/wlroots/wlroots/-/wikis/Getting-started
|
|
||||||
[#sway-devel on Libera Chat]: https://web.libera.chat/gamja/?channels=#sway-devel
|
|
||||||
[Sway]: https://github.com/swaywm/sway
|
|
||||||
[wrapper libraries]: https://gitlab.freedesktop.org/wlroots/wlroots/-/wikis/Projects-which-use-wlroots#wrapper-libraries
|
|
||||||
[libseat]: https://git.sr.ht/~kennylevinsen/seatd
|
|
||||||
[CONTRIBUTING.md]: https://gitlab.freedesktop.org/wlroots/wlroots/-/blob/master/CONTRIBUTING.md
|
|
||||||
|
|
|
@ -1,39 +1,27 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <libinput.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server.h>
|
||||||
|
#include <wlr/backend/drm.h>
|
||||||
#include <wlr/backend/headless.h>
|
#include <wlr/backend/headless.h>
|
||||||
#include <wlr/backend/interface.h>
|
#include <wlr/backend/interface.h>
|
||||||
|
#include <wlr/backend/libinput.h>
|
||||||
#include <wlr/backend/multi.h>
|
#include <wlr/backend/multi.h>
|
||||||
|
#include <wlr/backend/noop.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/backend/wayland.h>
|
#include <wlr/backend/wayland.h>
|
||||||
#include <wlr/config.h>
|
#include <wlr/config.h>
|
||||||
#include <wlr/render/wlr_renderer.h>
|
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include "backend/backend.h"
|
|
||||||
#include "backend/multi.h"
|
#include "backend/multi.h"
|
||||||
#include "render/allocator/allocator.h"
|
|
||||||
#include "util/signal.h"
|
|
||||||
|
|
||||||
#if WLR_HAS_DRM_BACKEND
|
|
||||||
#include <wlr/backend/drm.h>
|
|
||||||
#include "backend/drm/monitor.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if WLR_HAS_LIBINPUT_BACKEND
|
|
||||||
#include <wlr/backend/libinput.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if WLR_HAS_X11_BACKEND
|
#if WLR_HAS_X11_BACKEND
|
||||||
#include <wlr/backend/x11.h>
|
#include <wlr/backend/x11.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define WAIT_SESSION_TIMEOUT 10000 // ms
|
|
||||||
|
|
||||||
void wlr_backend_init(struct wlr_backend *backend,
|
void wlr_backend_init(struct wlr_backend *backend,
|
||||||
const struct wlr_backend_impl *impl) {
|
const struct wlr_backend_impl *impl) {
|
||||||
assert(backend);
|
assert(backend);
|
||||||
|
@ -43,10 +31,6 @@ void wlr_backend_init(struct wlr_backend *backend,
|
||||||
wl_signal_init(&backend->events.new_output);
|
wl_signal_init(&backend->events.new_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void wlr_backend_finish(struct wlr_backend *backend) {
|
|
||||||
wlr_signal_emit_safe(&backend->events.destroy, backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool wlr_backend_start(struct wlr_backend *backend) {
|
bool wlr_backend_start(struct wlr_backend *backend) {
|
||||||
if (backend->impl->start) {
|
if (backend->impl->start) {
|
||||||
return backend->impl->start(backend);
|
return backend->impl->start(backend);
|
||||||
|
@ -66,6 +50,13 @@ void wlr_backend_destroy(struct wlr_backend *backend) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct wlr_renderer *wlr_backend_get_renderer(struct wlr_backend *backend) {
|
||||||
|
if (backend->impl->get_renderer) {
|
||||||
|
return backend->impl->get_renderer(backend);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
struct wlr_session *wlr_backend_get_session(struct wlr_backend *backend) {
|
struct wlr_session *wlr_backend_get_session(struct wlr_backend *backend) {
|
||||||
if (backend->impl->get_session) {
|
if (backend->impl->get_session) {
|
||||||
return backend->impl->get_session(backend);
|
return backend->impl->get_session(backend);
|
||||||
|
@ -73,52 +64,6 @@ struct wlr_session *wlr_backend_get_session(struct wlr_backend *backend) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint64_t get_current_time_ms(void) {
|
|
||||||
struct timespec ts = {0};
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
||||||
return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wlr_session *session_create_and_wait(struct wl_display *disp) {
|
|
||||||
struct wlr_session *session = wlr_session_create(disp);
|
|
||||||
|
|
||||||
if (!session) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to start a session");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!session->active) {
|
|
||||||
wlr_log(WLR_INFO, "Waiting for a session to become active");
|
|
||||||
|
|
||||||
uint64_t started_at = get_current_time_ms();
|
|
||||||
uint64_t timeout = WAIT_SESSION_TIMEOUT;
|
|
||||||
struct wl_event_loop *event_loop =
|
|
||||||
wl_display_get_event_loop(session->display);
|
|
||||||
|
|
||||||
while (!session->active) {
|
|
||||||
int ret = wl_event_loop_dispatch(event_loop, (int)timeout);
|
|
||||||
if (ret < 0) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Failed to wait for session active: "
|
|
||||||
"wl_event_loop_dispatch failed");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t now = get_current_time_ms();
|
|
||||||
if (now >= started_at + WAIT_SESSION_TIMEOUT) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
timeout = started_at + WAIT_SESSION_TIMEOUT - now;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!session->active) {
|
|
||||||
wlr_log(WLR_ERROR, "Timeout waiting session to become active");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
clockid_t wlr_backend_get_presentation_clock(struct wlr_backend *backend) {
|
clockid_t wlr_backend_get_presentation_clock(struct wlr_backend *backend) {
|
||||||
if (backend->impl->get_presentation_clock) {
|
if (backend->impl->get_presentation_clock) {
|
||||||
return backend->impl->get_presentation_clock(backend);
|
return backend->impl->get_presentation_clock(backend);
|
||||||
|
@ -126,21 +71,6 @@ clockid_t wlr_backend_get_presentation_clock(struct wlr_backend *backend) {
|
||||||
return CLOCK_MONOTONIC;
|
return CLOCK_MONOTONIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
int wlr_backend_get_drm_fd(struct wlr_backend *backend) {
|
|
||||||
if (!backend->impl->get_drm_fd) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return backend->impl->get_drm_fd(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t backend_get_buffer_caps(struct wlr_backend *backend) {
|
|
||||||
if (!backend->impl->get_buffer_caps) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return backend->impl->get_buffer_caps(backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t parse_outputs_env(const char *name) {
|
static size_t parse_outputs_env(const char *name) {
|
||||||
const char *outputs_str = getenv(name);
|
const char *outputs_str = getenv(name);
|
||||||
if (outputs_str == NULL) {
|
if (outputs_str == NULL) {
|
||||||
|
@ -157,8 +87,9 @@ static size_t parse_outputs_env(const char *name) {
|
||||||
return outputs;
|
return outputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct wlr_backend *attempt_wl_backend(struct wl_display *display) {
|
static struct wlr_backend *attempt_wl_backend(struct wl_display *display,
|
||||||
struct wlr_backend *backend = wlr_wl_backend_create(display, NULL);
|
wlr_renderer_create_func_t create_renderer_func) {
|
||||||
|
struct wlr_backend *backend = wlr_wl_backend_create(display, NULL, create_renderer_func);
|
||||||
if (backend == NULL) {
|
if (backend == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -173,8 +104,8 @@ static struct wlr_backend *attempt_wl_backend(struct wl_display *display) {
|
||||||
|
|
||||||
#if WLR_HAS_X11_BACKEND
|
#if WLR_HAS_X11_BACKEND
|
||||||
static struct wlr_backend *attempt_x11_backend(struct wl_display *display,
|
static struct wlr_backend *attempt_x11_backend(struct wl_display *display,
|
||||||
const char *x11_display) {
|
const char *x11_display, wlr_renderer_create_func_t create_renderer_func) {
|
||||||
struct wlr_backend *backend = wlr_x11_backend_create(display, x11_display);
|
struct wlr_backend *backend = wlr_x11_backend_create(display, x11_display, create_renderer_func);
|
||||||
if (backend == NULL) {
|
if (backend == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -189,8 +120,8 @@ static struct wlr_backend *attempt_x11_backend(struct wl_display *display,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static struct wlr_backend *attempt_headless_backend(
|
static struct wlr_backend *attempt_headless_backend(
|
||||||
struct wl_display *display) {
|
struct wl_display *display, wlr_renderer_create_func_t create_renderer_func) {
|
||||||
struct wlr_backend *backend = wlr_headless_backend_create(display);
|
struct wlr_backend *backend = wlr_headless_backend_create(display, create_renderer_func);
|
||||||
if (backend == NULL) {
|
if (backend == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -203,29 +134,33 @@ static struct wlr_backend *attempt_headless_backend(
|
||||||
return backend;
|
return backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if WLR_HAS_DRM_BACKEND
|
static struct wlr_backend *attempt_noop_backend(struct wl_display *display) {
|
||||||
|
struct wlr_backend *backend = wlr_noop_backend_create(display);
|
||||||
|
if (backend == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t outputs = parse_outputs_env("WLR_NOOP_OUTPUTS");
|
||||||
|
for (size_t i = 0; i < outputs; ++i) {
|
||||||
|
wlr_noop_add_output(backend);
|
||||||
|
}
|
||||||
|
|
||||||
|
return backend;
|
||||||
|
}
|
||||||
|
|
||||||
static struct wlr_backend *attempt_drm_backend(struct wl_display *display,
|
static struct wlr_backend *attempt_drm_backend(struct wl_display *display,
|
||||||
struct wlr_backend *backend, struct wlr_session *session) {
|
struct wlr_backend *backend, struct wlr_session *session,
|
||||||
struct wlr_device *gpus[8];
|
wlr_renderer_create_func_t create_renderer_func) {
|
||||||
ssize_t num_gpus = wlr_session_find_gpus(session, 8, gpus);
|
int gpus[8];
|
||||||
if (num_gpus < 0) {
|
size_t num_gpus = wlr_session_find_gpus(session, 8, gpus);
|
||||||
wlr_log(WLR_ERROR, "Failed to find GPUs");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (num_gpus == 0) {
|
|
||||||
wlr_log(WLR_ERROR, "Found 0 GPUs, cannot create backend");
|
|
||||||
return NULL;
|
|
||||||
} else {
|
|
||||||
wlr_log(WLR_INFO, "Found %zu GPUs", num_gpus);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_backend *primary_drm = NULL;
|
struct wlr_backend *primary_drm = NULL;
|
||||||
for (size_t i = 0; i < (size_t)num_gpus; ++i) {
|
wlr_log(WLR_INFO, "Found %zu GPUs", num_gpus);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < num_gpus; ++i) {
|
||||||
struct wlr_backend *drm = wlr_drm_backend_create(display, session,
|
struct wlr_backend *drm = wlr_drm_backend_create(display, session,
|
||||||
gpus[i], primary_drm);
|
gpus[i], primary_drm, create_renderer_func);
|
||||||
if (!drm) {
|
if (!drm) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create DRM backend");
|
wlr_log(WLR_ERROR, "Failed to open DRM device %d", gpus[i]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,56 +170,46 @@ static struct wlr_backend *attempt_drm_backend(struct wl_display *display,
|
||||||
|
|
||||||
wlr_multi_backend_add(backend, drm);
|
wlr_multi_backend_add(backend, drm);
|
||||||
}
|
}
|
||||||
if (!primary_drm) {
|
|
||||||
wlr_log(WLR_ERROR, "Could not successfully create backend on any GPU");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return primary_drm;
|
return primary_drm;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
static bool attempt_backend_by_name(struct wl_display *display,
|
static struct wlr_backend *attempt_backend_by_name(struct wl_display *display,
|
||||||
struct wlr_multi_backend *multi, char *name) {
|
struct wlr_backend *backend, struct wlr_session **session,
|
||||||
struct wlr_backend *backend = NULL;
|
const char *name, wlr_renderer_create_func_t create_renderer_func) {
|
||||||
if (strcmp(name, "wayland") == 0) {
|
if (strcmp(name, "wayland") == 0) {
|
||||||
backend = attempt_wl_backend(display);
|
return attempt_wl_backend(display, create_renderer_func);
|
||||||
#if WLR_HAS_X11_BACKEND
|
#if WLR_HAS_X11_BACKEND
|
||||||
} else if (strcmp(name, "x11") == 0) {
|
} else if (strcmp(name, "x11") == 0) {
|
||||||
backend = attempt_x11_backend(display, NULL);
|
return attempt_x11_backend(display, NULL, create_renderer_func);
|
||||||
#endif
|
#endif
|
||||||
} else if (strcmp(name, "headless") == 0) {
|
} else if (strcmp(name, "headless") == 0) {
|
||||||
backend = attempt_headless_backend(display);
|
return attempt_headless_backend(display, create_renderer_func);
|
||||||
|
} else if (strcmp(name, "noop") == 0) {
|
||||||
|
return attempt_noop_backend(display);
|
||||||
} else if (strcmp(name, "drm") == 0 || strcmp(name, "libinput") == 0) {
|
} else if (strcmp(name, "drm") == 0 || strcmp(name, "libinput") == 0) {
|
||||||
// DRM and libinput need a session
|
// DRM and libinput need a session
|
||||||
if (multi->session == NULL) {
|
if (!*session) {
|
||||||
multi->session = session_create_and_wait(display);
|
*session = wlr_session_create(display);
|
||||||
if (multi->session == NULL) {
|
if (!*session) {
|
||||||
wlr_log(WLR_ERROR, "failed to start a session");
|
wlr_log(WLR_ERROR, "failed to start a session");
|
||||||
return false;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(name, "libinput") == 0) {
|
if (strcmp(name, "libinput") == 0) {
|
||||||
#if WLR_HAS_LIBINPUT_BACKEND
|
return wlr_libinput_backend_create(display, *session);
|
||||||
backend = wlr_libinput_backend_create(display, multi->session);
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
#if WLR_HAS_DRM_BACKEND
|
return attempt_drm_backend(display, backend, *session, create_renderer_func);
|
||||||
// attempt_drm_backend adds the multi drm backends itself
|
|
||||||
return attempt_drm_backend(display, &multi->backend,
|
|
||||||
multi->session) != NULL;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
wlr_log(WLR_ERROR, "unrecognized backend '%s'", name);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return wlr_multi_backend_add(&multi->backend, backend);
|
wlr_log(WLR_ERROR, "unrecognized backend '%s'", name);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) {
|
struct wlr_backend *wlr_backend_autocreate(struct wl_display *display,
|
||||||
|
wlr_renderer_create_func_t create_renderer_func) {
|
||||||
struct wlr_backend *backend = wlr_multi_backend_create(display);
|
struct wlr_backend *backend = wlr_multi_backend_create(display);
|
||||||
struct wlr_multi_backend *multi = (struct wlr_multi_backend *)backend;
|
struct wlr_multi_backend *multi = (struct wlr_multi_backend *)backend;
|
||||||
if (!backend) {
|
if (!backend) {
|
||||||
|
@ -294,9 +219,6 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) {
|
||||||
|
|
||||||
char *names = getenv("WLR_BACKENDS");
|
char *names = getenv("WLR_BACKENDS");
|
||||||
if (names) {
|
if (names) {
|
||||||
wlr_log(WLR_INFO, "Loading user-specified backends due to WLR_BACKENDS: %s",
|
|
||||||
names);
|
|
||||||
|
|
||||||
names = strdup(names);
|
names = strdup(names);
|
||||||
if (names == NULL) {
|
if (names == NULL) {
|
||||||
wlr_log(WLR_ERROR, "allocation failed");
|
wlr_log(WLR_ERROR, "allocation failed");
|
||||||
|
@ -307,7 +229,17 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) {
|
||||||
char *saveptr;
|
char *saveptr;
|
||||||
char *name = strtok_r(names, ",", &saveptr);
|
char *name = strtok_r(names, ",", &saveptr);
|
||||||
while (name != NULL) {
|
while (name != NULL) {
|
||||||
if (!attempt_backend_by_name(display, multi, name)) {
|
struct wlr_backend *subbackend = attempt_backend_by_name(display,
|
||||||
|
backend, &multi->session, name, create_renderer_func);
|
||||||
|
if (subbackend == NULL) {
|
||||||
|
wlr_log(WLR_ERROR, "failed to start backend '%s'", name);
|
||||||
|
wlr_session_destroy(multi->session);
|
||||||
|
wlr_backend_destroy(backend);
|
||||||
|
free(names);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wlr_multi_backend_add(backend, subbackend)) {
|
||||||
wlr_log(WLR_ERROR, "failed to add backend '%s'", name);
|
wlr_log(WLR_ERROR, "failed to add backend '%s'", name);
|
||||||
wlr_session_destroy(multi->session);
|
wlr_session_destroy(multi->session);
|
||||||
wlr_backend_destroy(backend);
|
wlr_backend_destroy(backend);
|
||||||
|
@ -322,39 +254,36 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) {
|
||||||
return backend;
|
return backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getenv("WAYLAND_DISPLAY") || getenv("WAYLAND_SOCKET")) {
|
if (getenv("WAYLAND_DISPLAY") || getenv("_WAYLAND_DISPLAY") ||
|
||||||
struct wlr_backend *wl_backend = attempt_wl_backend(display);
|
getenv("WAYLAND_SOCKET")) {
|
||||||
if (!wl_backend) {
|
struct wlr_backend *wl_backend = attempt_wl_backend(display,
|
||||||
goto error;
|
create_renderer_func);
|
||||||
|
if (wl_backend) {
|
||||||
|
wlr_multi_backend_add(backend, wl_backend);
|
||||||
|
return backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_multi_backend_add(backend, wl_backend);
|
|
||||||
return backend;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if WLR_HAS_X11_BACKEND
|
#if WLR_HAS_X11_BACKEND
|
||||||
const char *x11_display = getenv("DISPLAY");
|
const char *x11_display = getenv("DISPLAY");
|
||||||
if (x11_display) {
|
if (x11_display) {
|
||||||
struct wlr_backend *x11_backend =
|
struct wlr_backend *x11_backend =
|
||||||
attempt_x11_backend(display, x11_display);
|
attempt_x11_backend(display, x11_display, create_renderer_func);
|
||||||
if (!x11_backend) {
|
if (x11_backend) {
|
||||||
goto error;
|
wlr_multi_backend_add(backend, x11_backend);
|
||||||
|
return backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_multi_backend_add(backend, x11_backend);
|
|
||||||
return backend;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Attempt DRM+libinput
|
// Attempt DRM+libinput
|
||||||
multi->session = session_create_and_wait(display);
|
multi->session = wlr_session_create(display);
|
||||||
if (!multi->session) {
|
if (!multi->session) {
|
||||||
wlr_log(WLR_ERROR, "Failed to start a DRM session");
|
wlr_log(WLR_ERROR, "Failed to start a DRM session");
|
||||||
wlr_backend_destroy(backend);
|
wlr_backend_destroy(backend);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if WLR_HAS_LIBINPUT_BACKEND
|
|
||||||
struct wlr_backend *libinput = wlr_libinput_backend_create(display,
|
struct wlr_backend *libinput = wlr_libinput_backend_create(display,
|
||||||
multi->session);
|
multi->session);
|
||||||
if (!libinput) {
|
if (!libinput) {
|
||||||
|
@ -364,37 +293,16 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
wlr_multi_backend_add(backend, libinput);
|
wlr_multi_backend_add(backend, libinput);
|
||||||
#else
|
|
||||||
const char *no_devs = getenv("WLR_LIBINPUT_NO_DEVICES");
|
|
||||||
if (no_devs && strcmp(no_devs, "1") == 0) {
|
|
||||||
wlr_log(WLR_INFO, "WLR_LIBINPUT_NO_DEVICES is set, "
|
|
||||||
"starting without libinput backend");
|
|
||||||
} else {
|
|
||||||
wlr_log(WLR_ERROR, "libinput support is not compiled in, "
|
|
||||||
"refusing to start");
|
|
||||||
wlr_log(WLR_ERROR, "Set WLR_LIBINPUT_NO_DEVICES=1 to suppress this check");
|
|
||||||
wlr_session_destroy(multi->session);
|
|
||||||
wlr_backend_destroy(backend);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if WLR_HAS_DRM_BACKEND
|
struct wlr_backend *primary_drm = attempt_drm_backend(display, backend,
|
||||||
struct wlr_backend *primary_drm =
|
multi->session, create_renderer_func);
|
||||||
attempt_drm_backend(display, backend, multi->session);
|
|
||||||
if (!primary_drm) {
|
if (!primary_drm) {
|
||||||
wlr_log(WLR_ERROR, "Failed to open any DRM device");
|
wlr_log(WLR_ERROR, "Failed to open any DRM device");
|
||||||
|
wlr_backend_destroy(libinput);
|
||||||
wlr_session_destroy(multi->session);
|
wlr_session_destroy(multi->session);
|
||||||
wlr_backend_destroy(backend);
|
wlr_backend_destroy(backend);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
drm_backend_monitor_create(backend, primary_drm, multi->session);
|
|
||||||
|
|
||||||
return backend;
|
return backend;
|
||||||
#endif
|
|
||||||
|
|
||||||
error:
|
|
||||||
wlr_backend_destroy(backend);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <gbm.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <xf86drm.h>
|
#include <xf86drm.h>
|
||||||
|
@ -8,290 +9,266 @@
|
||||||
|
|
||||||
struct atomic {
|
struct atomic {
|
||||||
drmModeAtomicReq *req;
|
drmModeAtomicReq *req;
|
||||||
|
int cursor;
|
||||||
bool failed;
|
bool failed;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void atomic_begin(struct atomic *atom) {
|
static void atomic_begin(struct wlr_drm_crtc *crtc, struct atomic *atom) {
|
||||||
memset(atom, 0, sizeof(*atom));
|
if (!crtc->atomic) {
|
||||||
|
crtc->atomic = drmModeAtomicAlloc();
|
||||||
atom->req = drmModeAtomicAlloc();
|
if (!crtc->atomic) {
|
||||||
if (!atom->req) {
|
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
atom->failed = true;
|
||||||
atom->failed = true;
|
return;
|
||||||
return;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
atom->req = crtc->atomic;
|
||||||
|
atom->cursor = drmModeAtomicGetCursor(atom->req);
|
||||||
|
atom->failed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool atomic_commit(struct atomic *atom,
|
static bool atomic_end(int drm_fd, struct atomic *atom) {
|
||||||
struct wlr_drm_connector *conn, uint32_t flags) {
|
|
||||||
struct wlr_drm_backend *drm = conn->backend;
|
|
||||||
if (atom->failed) {
|
if (atom->failed) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ret = drmModeAtomicCommit(drm->fd, atom->req, flags, drm);
|
uint32_t flags = DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_NONBLOCK;
|
||||||
if (ret != 0) {
|
if (drmModeAtomicCommit(drm_fd, atom->req, flags, NULL)) {
|
||||||
wlr_drm_conn_log_errno(conn,
|
wlr_log_errno(WLR_ERROR, "Atomic test failed");
|
||||||
(flags & DRM_MODE_ATOMIC_TEST_ONLY) ? WLR_DEBUG : WLR_ERROR,
|
drmModeAtomicSetCursor(atom->req, atom->cursor);
|
||||||
"Atomic %s failed (%s)",
|
|
||||||
(flags & DRM_MODE_ATOMIC_TEST_ONLY) ? "test" : "commit",
|
|
||||||
(flags & DRM_MODE_ATOMIC_ALLOW_MODESET) ? "modeset" : "pageflip");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void atomic_finish(struct atomic *atom) {
|
static bool atomic_commit(int drm_fd, struct atomic *atom,
|
||||||
drmModeAtomicFree(atom->req);
|
struct wlr_drm_connector *conn, uint32_t flags, bool modeset) {
|
||||||
|
if (atom->failed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = drmModeAtomicCommit(drm_fd, atom->req, flags, conn);
|
||||||
|
if (ret) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "%s: Atomic commit failed (%s)",
|
||||||
|
conn->output.name, modeset ? "modeset" : "pageflip");
|
||||||
|
|
||||||
|
// Try to commit without new changes
|
||||||
|
drmModeAtomicSetCursor(atom->req, atom->cursor);
|
||||||
|
if (drmModeAtomicCommit(drm_fd, atom->req, flags, conn)) {
|
||||||
|
wlr_log_errno(WLR_ERROR,
|
||||||
|
"%s: Atomic commit without new changes failed (%s)",
|
||||||
|
conn->output.name, modeset ? "modeset" : "pageflip");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drmModeAtomicSetCursor(atom->req, 0);
|
||||||
|
|
||||||
|
return !ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void atomic_add(struct atomic *atom, uint32_t id, uint32_t prop, uint64_t val) {
|
static inline void atomic_add(struct atomic *atom, uint32_t id, uint32_t prop, uint64_t val) {
|
||||||
if (!atom->failed && drmModeAtomicAddProperty(atom->req, id, prop, val) < 0) {
|
if (!atom->failed && drmModeAtomicAddProperty(atom->req, id, prop, val) < 0) {
|
||||||
wlr_log_errno(WLR_ERROR, "Failed to add atomic DRM property");
|
wlr_log_errno(WLR_ERROR, "Failed to add atomic DRM property");
|
||||||
atom->failed = true;
|
atom->failed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool create_mode_blob(struct wlr_drm_backend *drm,
|
static void set_plane_props(struct atomic *atom, struct wlr_drm_plane *plane,
|
||||||
struct wlr_drm_connector *conn,
|
uint32_t crtc_id, uint32_t fb_id, bool set_crtc_xy) {
|
||||||
const struct wlr_drm_connector_state *state, uint32_t *blob_id) {
|
uint32_t id = plane->id;
|
||||||
if (!state->active) {
|
const union wlr_drm_plane_props *props = &plane->props;
|
||||||
*blob_id = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (drmModeCreatePropertyBlob(drm->fd, &state->mode,
|
// The src_* properties are in 16.16 fixed point
|
||||||
sizeof(drmModeModeInfo), blob_id)) {
|
atomic_add(atom, id, props->src_x, 0);
|
||||||
wlr_log_errno(WLR_ERROR, "Unable to create mode property blob");
|
atomic_add(atom, id, props->src_y, 0);
|
||||||
return false;
|
atomic_add(atom, id, props->src_w, (uint64_t)plane->surf.width << 16);
|
||||||
|
atomic_add(atom, id, props->src_h, (uint64_t)plane->surf.height << 16);
|
||||||
|
atomic_add(atom, id, props->crtc_w, plane->surf.width);
|
||||||
|
atomic_add(atom, id, props->crtc_h, plane->surf.height);
|
||||||
|
atomic_add(atom, id, props->fb_id, fb_id);
|
||||||
|
atomic_add(atom, id, props->crtc_id, crtc_id);
|
||||||
|
if (set_crtc_xy) {
|
||||||
|
atomic_add(atom, id, props->crtc_x, 0);
|
||||||
|
atomic_add(atom, id, props->crtc_y, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool create_gamma_lut_blob(struct wlr_drm_backend *drm,
|
static bool atomic_crtc_pageflip(struct wlr_drm_backend *drm,
|
||||||
size_t size, const uint16_t *lut, uint32_t *blob_id) {
|
struct wlr_drm_connector *conn,
|
||||||
if (size == 0) {
|
struct wlr_drm_crtc *crtc,
|
||||||
*blob_id = 0;
|
uint32_t fb_id, drmModeModeInfo *mode) {
|
||||||
|
if (mode != NULL) {
|
||||||
|
if (crtc->mode_id != 0) {
|
||||||
|
drmModeDestroyPropertyBlob(drm->fd, crtc->mode_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (drmModeCreatePropertyBlob(drm->fd, mode, sizeof(*mode),
|
||||||
|
&crtc->mode_id)) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Unable to create property blob");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t flags = DRM_MODE_PAGE_FLIP_EVENT;
|
||||||
|
if (mode != NULL) {
|
||||||
|
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
||||||
|
} else {
|
||||||
|
flags |= DRM_MODE_ATOMIC_NONBLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct atomic atom;
|
||||||
|
atomic_begin(crtc, &atom);
|
||||||
|
atomic_add(&atom, conn->id, conn->props.crtc_id, crtc->id);
|
||||||
|
if (mode != NULL && conn->props.link_status != 0) {
|
||||||
|
atomic_add(&atom, conn->id, conn->props.link_status,
|
||||||
|
DRM_MODE_LINK_STATUS_GOOD);
|
||||||
|
}
|
||||||
|
atomic_add(&atom, crtc->id, crtc->props.mode_id, crtc->mode_id);
|
||||||
|
atomic_add(&atom, crtc->id, crtc->props.active, 1);
|
||||||
|
set_plane_props(&atom, crtc->primary, crtc->id, fb_id, true);
|
||||||
|
return atomic_commit(drm->fd, &atom, conn, flags, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool atomic_conn_enable(struct wlr_drm_backend *drm,
|
||||||
|
struct wlr_drm_connector *conn, bool enable) {
|
||||||
|
struct wlr_drm_crtc *crtc = conn->crtc;
|
||||||
|
if (crtc == NULL) {
|
||||||
|
return !enable;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct atomic atom;
|
||||||
|
atomic_begin(crtc, &atom);
|
||||||
|
atomic_add(&atom, crtc->id, crtc->props.active, enable);
|
||||||
|
if (enable) {
|
||||||
|
atomic_add(&atom, conn->id, conn->props.crtc_id, crtc->id);
|
||||||
|
atomic_add(&atom, crtc->id, crtc->props.mode_id, crtc->mode_id);
|
||||||
|
} else {
|
||||||
|
atomic_add(&atom, conn->id, conn->props.crtc_id, 0);
|
||||||
|
atomic_add(&atom, crtc->id, crtc->props.mode_id, 0);
|
||||||
|
}
|
||||||
|
return atomic_commit(drm->fd, &atom, conn, DRM_MODE_ATOMIC_ALLOW_MODESET,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool legacy_crtc_set_cursor(struct wlr_drm_backend *drm,
|
||||||
|
struct wlr_drm_crtc *crtc, struct gbm_bo *bo);
|
||||||
|
|
||||||
|
static bool atomic_crtc_set_cursor(struct wlr_drm_backend *drm,
|
||||||
|
struct wlr_drm_crtc *crtc, struct gbm_bo *bo) {
|
||||||
|
if (!crtc || !crtc->cursor) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct wlr_drm_plane *plane = crtc->cursor;
|
||||||
|
// We can't use atomic operations on fake planes
|
||||||
|
if (plane->id == 0) {
|
||||||
|
return legacy_crtc_set_cursor(drm, crtc, bo);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct atomic atom;
|
||||||
|
|
||||||
|
atomic_begin(crtc, &atom);
|
||||||
|
|
||||||
|
if (bo) {
|
||||||
|
uint32_t fb_id = get_fb_for_bo(bo, plane->drm_format);
|
||||||
|
set_plane_props(&atom, plane, crtc->id, fb_id, false);
|
||||||
|
} else {
|
||||||
|
atomic_add(&atom, plane->id, plane->props.fb_id, 0);
|
||||||
|
atomic_add(&atom, plane->id, plane->props.crtc_id, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return atomic_end(drm->fd, &atom);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool legacy_crtc_move_cursor(struct wlr_drm_backend *drm,
|
||||||
|
struct wlr_drm_crtc *crtc, int x, int y);
|
||||||
|
|
||||||
|
static bool atomic_crtc_move_cursor(struct wlr_drm_backend *drm,
|
||||||
|
struct wlr_drm_crtc *crtc, int x, int y) {
|
||||||
|
if (!crtc || !crtc->cursor) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_drm_plane *plane = crtc->cursor;
|
||||||
|
// We can't use atomic operations on fake planes
|
||||||
|
if (plane->id == 0) {
|
||||||
|
return legacy_crtc_move_cursor(drm, crtc, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct atomic atom;
|
||||||
|
|
||||||
|
atomic_begin(crtc, &atom);
|
||||||
|
atomic_add(&atom, plane->id, plane->props.crtc_x, x);
|
||||||
|
atomic_add(&atom, plane->id, plane->props.crtc_y, y);
|
||||||
|
return atomic_end(drm->fd, &atom);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool atomic_crtc_set_gamma(struct wlr_drm_backend *drm,
|
||||||
|
struct wlr_drm_crtc *crtc, size_t size,
|
||||||
|
uint16_t *r, uint16_t *g, uint16_t *b) {
|
||||||
|
// Fallback to legacy gamma interface when gamma properties are not available
|
||||||
|
// (can happen on older Intel GPUs that support gamma but not degamma).
|
||||||
|
// TEMP: This is broken on AMDGPU. Provide a fallback to legacy until they
|
||||||
|
// get it fixed. Ref https://bugs.freedesktop.org/show_bug.cgi?id=107459
|
||||||
|
const char *no_atomic_str = getenv("WLR_DRM_NO_ATOMIC_GAMMA");
|
||||||
|
bool no_atomic = no_atomic_str != NULL && strcmp(no_atomic_str, "1") == 0;
|
||||||
|
if (crtc->props.gamma_lut == 0 || no_atomic) {
|
||||||
|
return legacy_iface.crtc_set_gamma(drm, crtc, size, r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
struct drm_color_lut *gamma = malloc(size * sizeof(struct drm_color_lut));
|
struct drm_color_lut *gamma = malloc(size * sizeof(struct drm_color_lut));
|
||||||
if (gamma == NULL) {
|
if (gamma == NULL) {
|
||||||
wlr_log(WLR_ERROR, "Failed to allocate gamma table");
|
wlr_log(WLR_ERROR, "Failed to allocate gamma table");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint16_t *r = lut;
|
|
||||||
const uint16_t *g = lut + size;
|
|
||||||
const uint16_t *b = lut + 2 * size;
|
|
||||||
for (size_t i = 0; i < size; i++) {
|
for (size_t i = 0; i < size; i++) {
|
||||||
gamma[i].red = r[i];
|
gamma[i].red = r[i];
|
||||||
gamma[i].green = g[i];
|
gamma[i].green = g[i];
|
||||||
gamma[i].blue = b[i];
|
gamma[i].blue = b[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (crtc->gamma_lut != 0) {
|
||||||
|
drmModeDestroyPropertyBlob(drm->fd, crtc->gamma_lut);
|
||||||
|
}
|
||||||
|
|
||||||
if (drmModeCreatePropertyBlob(drm->fd, gamma,
|
if (drmModeCreatePropertyBlob(drm->fd, gamma,
|
||||||
size * sizeof(struct drm_color_lut), blob_id) != 0) {
|
size * sizeof(struct drm_color_lut), &crtc->gamma_lut)) {
|
||||||
wlr_log_errno(WLR_ERROR, "Unable to create gamma LUT property blob");
|
|
||||||
free(gamma);
|
free(gamma);
|
||||||
|
wlr_log_errno(WLR_ERROR, "Unable to create property blob");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
free(gamma);
|
free(gamma);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void commit_blob(struct wlr_drm_backend *drm,
|
|
||||||
uint32_t *current, uint32_t next) {
|
|
||||||
if (*current == next) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (*current != 0) {
|
|
||||||
drmModeDestroyPropertyBlob(drm->fd, *current);
|
|
||||||
}
|
|
||||||
*current = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rollback_blob(struct wlr_drm_backend *drm,
|
|
||||||
uint32_t *current, uint32_t next) {
|
|
||||||
if (*current == next) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (next != 0) {
|
|
||||||
drmModeDestroyPropertyBlob(drm->fd, next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void plane_disable(struct atomic *atom, struct wlr_drm_plane *plane) {
|
|
||||||
uint32_t id = plane->id;
|
|
||||||
const union wlr_drm_plane_props *props = &plane->props;
|
|
||||||
atomic_add(atom, id, props->fb_id, 0);
|
|
||||||
atomic_add(atom, id, props->crtc_id, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_plane_props(struct atomic *atom, struct wlr_drm_backend *drm,
|
|
||||||
struct wlr_drm_plane *plane, uint32_t crtc_id, int32_t x, int32_t y) {
|
|
||||||
uint32_t id = plane->id;
|
|
||||||
const union wlr_drm_plane_props *props = &plane->props;
|
|
||||||
struct wlr_drm_fb *fb = plane_get_next_fb(plane);
|
|
||||||
if (fb == NULL) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to acquire FB");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t width = fb->wlr_buf->width;
|
|
||||||
uint32_t height = fb->wlr_buf->height;
|
|
||||||
|
|
||||||
// The src_* properties are in 16.16 fixed point
|
|
||||||
atomic_add(atom, id, props->src_x, 0);
|
|
||||||
atomic_add(atom, id, props->src_y, 0);
|
|
||||||
atomic_add(atom, id, props->src_w, (uint64_t)width << 16);
|
|
||||||
atomic_add(atom, id, props->src_h, (uint64_t)height << 16);
|
|
||||||
atomic_add(atom, id, props->crtc_w, width);
|
|
||||||
atomic_add(atom, id, props->crtc_h, height);
|
|
||||||
atomic_add(atom, id, props->fb_id, fb->id);
|
|
||||||
atomic_add(atom, id, props->crtc_id, crtc_id);
|
|
||||||
atomic_add(atom, id, props->crtc_x, (uint64_t)x);
|
|
||||||
atomic_add(atom, id, props->crtc_y, (uint64_t)y);
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
error:
|
|
||||||
wlr_log(WLR_ERROR, "Failed to set plane %"PRIu32" properties", plane->id);
|
|
||||||
atom->failed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool atomic_crtc_commit(struct wlr_drm_connector *conn,
|
|
||||||
const struct wlr_drm_connector_state *state, uint32_t flags,
|
|
||||||
bool test_only) {
|
|
||||||
struct wlr_drm_backend *drm = conn->backend;
|
|
||||||
struct wlr_output *output = &conn->output;
|
|
||||||
struct wlr_drm_crtc *crtc = conn->crtc;
|
|
||||||
|
|
||||||
bool modeset = state->modeset;
|
|
||||||
bool active = state->active;
|
|
||||||
|
|
||||||
uint32_t mode_id = crtc->mode_id;
|
|
||||||
if (modeset) {
|
|
||||||
if (!create_mode_blob(drm, conn, state, &mode_id)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t gamma_lut = crtc->gamma_lut;
|
|
||||||
if (state->base->committed & WLR_OUTPUT_STATE_GAMMA_LUT) {
|
|
||||||
// Fallback to legacy gamma interface when gamma properties are not
|
|
||||||
// available (can happen on older Intel GPUs that support gamma but not
|
|
||||||
// degamma).
|
|
||||||
if (crtc->props.gamma_lut == 0) {
|
|
||||||
if (!drm_legacy_crtc_set_gamma(drm, crtc,
|
|
||||||
state->base->gamma_lut_size,
|
|
||||||
state->base->gamma_lut)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!create_gamma_lut_blob(drm, state->base->gamma_lut_size,
|
|
||||||
state->base->gamma_lut, &gamma_lut)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t fb_damage_clips = 0;
|
|
||||||
if ((state->base->committed & WLR_OUTPUT_STATE_DAMAGE) &&
|
|
||||||
pixman_region32_not_empty((pixman_region32_t *)&state->base->damage) &&
|
|
||||||
crtc->primary->props.fb_damage_clips != 0) {
|
|
||||||
int rects_len;
|
|
||||||
const pixman_box32_t *rects = pixman_region32_rectangles(
|
|
||||||
(pixman_region32_t *)&state->base->damage, &rects_len);
|
|
||||||
if (drmModeCreatePropertyBlob(drm->fd, rects,
|
|
||||||
sizeof(*rects) * rects_len, &fb_damage_clips) != 0) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Failed to create FB_DAMAGE_CLIPS property blob");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool prev_vrr_enabled =
|
|
||||||
output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED;
|
|
||||||
bool vrr_enabled = prev_vrr_enabled;
|
|
||||||
if ((state->base->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) &&
|
|
||||||
drm_connector_supports_vrr(conn)) {
|
|
||||||
vrr_enabled = state->base->adaptive_sync_enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (test_only) {
|
|
||||||
flags |= DRM_MODE_ATOMIC_TEST_ONLY;
|
|
||||||
}
|
|
||||||
if (modeset) {
|
|
||||||
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
|
||||||
} else if (!test_only) {
|
|
||||||
flags |= DRM_MODE_ATOMIC_NONBLOCK;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct atomic atom;
|
struct atomic atom;
|
||||||
atomic_begin(&atom);
|
atomic_begin(crtc, &atom);
|
||||||
atomic_add(&atom, conn->id, conn->props.crtc_id, active ? crtc->id : 0);
|
atomic_add(&atom, crtc->id, crtc->props.gamma_lut, crtc->gamma_lut);
|
||||||
if (modeset && active && conn->props.link_status != 0) {
|
return atomic_end(drm->fd, &atom);
|
||||||
atomic_add(&atom, conn->id, conn->props.link_status,
|
}
|
||||||
DRM_MODE_LINK_STATUS_GOOD);
|
|
||||||
}
|
static size_t atomic_crtc_get_gamma_size(struct wlr_drm_backend *drm,
|
||||||
atomic_add(&atom, crtc->id, crtc->props.mode_id, mode_id);
|
struct wlr_drm_crtc *crtc) {
|
||||||
atomic_add(&atom, crtc->id, crtc->props.active, active);
|
if (crtc->props.gamma_lut_size == 0) {
|
||||||
if (active) {
|
return legacy_iface.crtc_get_gamma_size(drm, crtc);
|
||||||
if (crtc->props.gamma_lut != 0) {
|
|
||||||
atomic_add(&atom, crtc->id, crtc->props.gamma_lut, gamma_lut);
|
|
||||||
}
|
|
||||||
if (crtc->props.vrr_enabled != 0) {
|
|
||||||
atomic_add(&atom, crtc->id, crtc->props.vrr_enabled, vrr_enabled);
|
|
||||||
}
|
|
||||||
set_plane_props(&atom, drm, crtc->primary, crtc->id, 0, 0);
|
|
||||||
if (crtc->primary->props.fb_damage_clips != 0) {
|
|
||||||
atomic_add(&atom, crtc->primary->id,
|
|
||||||
crtc->primary->props.fb_damage_clips, fb_damage_clips);
|
|
||||||
}
|
|
||||||
if (crtc->cursor) {
|
|
||||||
if (drm_connector_is_cursor_visible(conn)) {
|
|
||||||
set_plane_props(&atom, drm, crtc->cursor, crtc->id,
|
|
||||||
conn->cursor_x, conn->cursor_y);
|
|
||||||
} else {
|
|
||||||
plane_disable(&atom, crtc->cursor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
plane_disable(&atom, crtc->primary);
|
|
||||||
if (crtc->cursor) {
|
|
||||||
plane_disable(&atom, crtc->cursor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ok = atomic_commit(&atom, conn, flags);
|
uint64_t gamma_lut_size;
|
||||||
atomic_finish(&atom);
|
if (!get_drm_prop(drm->fd, crtc->id, crtc->props.gamma_lut_size,
|
||||||
|
&gamma_lut_size)) {
|
||||||
if (ok && !test_only) {
|
wlr_log(WLR_ERROR, "Unable to get gamma lut size");
|
||||||
commit_blob(drm, &crtc->mode_id, mode_id);
|
return 0;
|
||||||
commit_blob(drm, &crtc->gamma_lut, gamma_lut);
|
|
||||||
|
|
||||||
if (vrr_enabled != prev_vrr_enabled) {
|
|
||||||
output->adaptive_sync_status = vrr_enabled ?
|
|
||||||
WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED :
|
|
||||||
WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED;
|
|
||||||
wlr_drm_conn_log(conn, WLR_DEBUG, "VRR %s",
|
|
||||||
vrr_enabled ? "enabled" : "disabled");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
rollback_blob(drm, &crtc->mode_id, mode_id);
|
|
||||||
rollback_blob(drm, &crtc->gamma_lut, gamma_lut);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fb_damage_clips != 0 &&
|
return (size_t)gamma_lut_size;
|
||||||
drmModeDestroyPropertyBlob(drm->fd, fb_damage_clips) != 0) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Failed to destroy FB_DAMAGE_CLIPS property blob");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ok;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct wlr_drm_interface atomic_iface = {
|
const struct wlr_drm_interface atomic_iface = {
|
||||||
.crtc_commit = atomic_crtc_commit,
|
.conn_enable = atomic_conn_enable,
|
||||||
|
.crtc_pageflip = atomic_crtc_pageflip,
|
||||||
|
.crtc_set_cursor = atomic_crtc_set_cursor,
|
||||||
|
.crtc_move_cursor = atomic_crtc_move_cursor,
|
||||||
|
.crtc_set_gamma = atomic_crtc_set_gamma,
|
||||||
|
.crtc_get_gamma_size = atomic_crtc_get_gamma_size,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <drm_fourcc.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server.h>
|
||||||
#include <wlr/backend/interface.h>
|
#include <wlr/backend/interface.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/interfaces/wlr_output.h>
|
#include <wlr/interfaces/wlr_output.h>
|
||||||
|
#include <wlr/render/egl.h>
|
||||||
|
#include <wlr/types/wlr_list.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <xf86drm.h>
|
#include <xf86drm.h>
|
||||||
#include "backend/drm/drm.h"
|
#include "backend/drm/drm.h"
|
||||||
|
@ -22,7 +23,7 @@ struct wlr_drm_backend *get_drm_backend_from_backend(
|
||||||
|
|
||||||
static bool backend_start(struct wlr_backend *backend) {
|
static bool backend_start(struct wlr_backend *backend) {
|
||||||
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
|
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
|
||||||
scan_drm_connectors(drm, NULL);
|
scan_drm_connectors(drm);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,131 +34,105 @@ static void backend_destroy(struct wlr_backend *backend) {
|
||||||
|
|
||||||
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
|
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
|
||||||
|
|
||||||
|
restore_drm_outputs(drm);
|
||||||
|
|
||||||
struct wlr_drm_connector *conn, *next;
|
struct wlr_drm_connector *conn, *next;
|
||||||
wl_list_for_each_safe(conn, next, &drm->outputs, link) {
|
wl_list_for_each_safe(conn, next, &drm->outputs, link) {
|
||||||
destroy_drm_connector(conn);
|
wlr_output_destroy(&conn->output);
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_backend_finish(backend);
|
wlr_signal_emit_safe(&backend->events.destroy, backend);
|
||||||
|
|
||||||
struct wlr_drm_fb *fb, *fb_tmp;
|
|
||||||
wl_list_for_each_safe(fb, fb_tmp, &drm->fbs, link) {
|
|
||||||
drm_fb_destroy(fb);
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_list_remove(&drm->display_destroy.link);
|
wl_list_remove(&drm->display_destroy.link);
|
||||||
wl_list_remove(&drm->session_destroy.link);
|
wl_list_remove(&drm->session_signal.link);
|
||||||
wl_list_remove(&drm->session_active.link);
|
wl_list_remove(&drm->drm_invalidated.link);
|
||||||
wl_list_remove(&drm->parent_destroy.link);
|
|
||||||
wl_list_remove(&drm->dev_change.link);
|
|
||||||
wl_list_remove(&drm->dev_remove.link);
|
|
||||||
|
|
||||||
if (drm->parent) {
|
|
||||||
finish_drm_renderer(&drm->mgpu_renderer);
|
|
||||||
}
|
|
||||||
|
|
||||||
finish_drm_resources(drm);
|
finish_drm_resources(drm);
|
||||||
|
finish_drm_renderer(&drm->renderer);
|
||||||
free(drm->name);
|
wlr_session_close_file(drm->session, drm->fd);
|
||||||
wlr_session_close_file(drm->session, drm->dev);
|
|
||||||
wl_event_source_remove(drm->drm_event);
|
wl_event_source_remove(drm->drm_event);
|
||||||
free(drm);
|
free(drm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct wlr_renderer *backend_get_renderer(
|
||||||
|
struct wlr_backend *backend) {
|
||||||
|
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
|
||||||
|
|
||||||
|
if (drm->parent) {
|
||||||
|
return drm->parent->renderer.wlr_rend;
|
||||||
|
} else {
|
||||||
|
return drm->renderer.wlr_rend;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static clockid_t backend_get_presentation_clock(struct wlr_backend *backend) {
|
static clockid_t backend_get_presentation_clock(struct wlr_backend *backend) {
|
||||||
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
|
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
|
||||||
return drm->clock;
|
return drm->clock;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int backend_get_drm_fd(struct wlr_backend *backend) {
|
static struct wlr_backend_impl backend_impl = {
|
||||||
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
|
|
||||||
|
|
||||||
if (drm->parent) {
|
|
||||||
return drm->parent->fd;
|
|
||||||
} else {
|
|
||||||
return drm->fd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t drm_backend_get_buffer_caps(struct wlr_backend *backend) {
|
|
||||||
return WLR_BUFFER_CAP_DMABUF;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wlr_backend_impl backend_impl = {
|
|
||||||
.start = backend_start,
|
.start = backend_start,
|
||||||
.destroy = backend_destroy,
|
.destroy = backend_destroy,
|
||||||
|
.get_renderer = backend_get_renderer,
|
||||||
.get_presentation_clock = backend_get_presentation_clock,
|
.get_presentation_clock = backend_get_presentation_clock,
|
||||||
.get_drm_fd = backend_get_drm_fd,
|
|
||||||
.get_buffer_caps = drm_backend_get_buffer_caps,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool wlr_backend_is_drm(struct wlr_backend *b) {
|
bool wlr_backend_is_drm(struct wlr_backend *b) {
|
||||||
return b->impl == &backend_impl;
|
return b->impl == &backend_impl;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_session_active(struct wl_listener *listener, void *data) {
|
static void session_signal(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_drm_backend *drm =
|
struct wlr_drm_backend *drm =
|
||||||
wl_container_of(listener, drm, session_active);
|
wl_container_of(listener, drm, session_signal);
|
||||||
struct wlr_session *session = drm->session;
|
struct wlr_session *session = data;
|
||||||
|
|
||||||
if (session->active) {
|
if (session->active) {
|
||||||
wlr_log(WLR_INFO, "DRM fd resumed");
|
wlr_log(WLR_INFO, "DRM fd resumed");
|
||||||
scan_drm_connectors(drm, NULL);
|
scan_drm_connectors(drm);
|
||||||
|
|
||||||
struct wlr_drm_connector *conn;
|
struct wlr_drm_connector *conn;
|
||||||
wl_list_for_each(conn, &drm->outputs, link) {
|
wl_list_for_each(conn, &drm->outputs, link){
|
||||||
struct wlr_output_mode *mode = NULL;
|
if (conn->output.enabled) {
|
||||||
uint32_t committed = WLR_OUTPUT_STATE_ENABLED;
|
drm_connector_set_mode(&conn->output,
|
||||||
if (conn->output.enabled && conn->output.current_mode != NULL) {
|
conn->output.current_mode);
|
||||||
committed |= WLR_OUTPUT_STATE_MODE;
|
} else {
|
||||||
mode = conn->output.current_mode;
|
enable_drm_connector(&conn->output, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!conn->crtc) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_drm_plane *plane = conn->crtc->cursor;
|
||||||
|
drm->iface->crtc_set_cursor(drm, conn->crtc,
|
||||||
|
(plane && plane->cursor_enabled) ? plane->surf.back : NULL);
|
||||||
|
drm->iface->crtc_move_cursor(drm, conn->crtc, conn->cursor_x,
|
||||||
|
conn->cursor_y);
|
||||||
|
|
||||||
|
if (conn->crtc->gamma_table != NULL) {
|
||||||
|
size_t size = conn->crtc->gamma_table_size;
|
||||||
|
uint16_t *r = conn->crtc->gamma_table;
|
||||||
|
uint16_t *g = conn->crtc->gamma_table + size;
|
||||||
|
uint16_t *b = conn->crtc->gamma_table + 2 * size;
|
||||||
|
drm->iface->crtc_set_gamma(drm, conn->crtc, size, r, g, b);
|
||||||
|
} else {
|
||||||
|
set_drm_connector_gamma(&conn->output, 0, NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
struct wlr_output_state state = {
|
|
||||||
.committed = committed,
|
|
||||||
.enabled = mode != NULL,
|
|
||||||
.mode_type = WLR_OUTPUT_STATE_MODE_FIXED,
|
|
||||||
.mode = mode,
|
|
||||||
};
|
|
||||||
drm_connector_commit_state(conn, &state);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
wlr_log(WLR_INFO, "DRM fd paused");
|
wlr_log(WLR_INFO, "DRM fd paused");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_dev_change(struct wl_listener *listener, void *data) {
|
static void drm_invalidated(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_drm_backend *drm = wl_container_of(listener, drm, dev_change);
|
|
||||||
struct wlr_device_change_event *change = data;
|
|
||||||
|
|
||||||
if (!drm->session->active) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (change->type) {
|
|
||||||
case WLR_DEVICE_HOTPLUG:
|
|
||||||
wlr_log(WLR_DEBUG, "Received hotplug event for %s", drm->name);
|
|
||||||
scan_drm_connectors(drm, &change->hotplug);
|
|
||||||
break;
|
|
||||||
case WLR_DEVICE_LEASE:
|
|
||||||
wlr_log(WLR_DEBUG, "Received lease event for %s", drm->name);
|
|
||||||
scan_drm_leases(drm);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
wlr_log(WLR_DEBUG, "Received unknown change event for %s", drm->name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_dev_remove(struct wl_listener *listener, void *data) {
|
|
||||||
struct wlr_drm_backend *drm = wl_container_of(listener, drm, dev_remove);
|
|
||||||
|
|
||||||
wlr_log(WLR_INFO, "Destroying DRM backend for %s", drm->name);
|
|
||||||
backend_destroy(&drm->backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_session_destroy(struct wl_listener *listener, void *data) {
|
|
||||||
struct wlr_drm_backend *drm =
|
struct wlr_drm_backend *drm =
|
||||||
wl_container_of(listener, drm, session_destroy);
|
wl_container_of(listener, drm, drm_invalidated);
|
||||||
backend_destroy(&drm->backend);
|
|
||||||
|
char *name = drmGetDeviceNameFromFd2(drm->fd);
|
||||||
|
wlr_log(WLR_DEBUG, "%s invalidated", name);
|
||||||
|
free(name);
|
||||||
|
|
||||||
|
scan_drm_connectors(drm);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
|
@ -166,21 +141,16 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
backend_destroy(&drm->backend);
|
backend_destroy(&drm->backend);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_parent_destroy(struct wl_listener *listener, void *data) {
|
|
||||||
struct wlr_drm_backend *drm =
|
|
||||||
wl_container_of(listener, drm, parent_destroy);
|
|
||||||
backend_destroy(&drm->backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
|
struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
|
||||||
struct wlr_session *session, struct wlr_device *dev,
|
struct wlr_session *session, int gpu_fd, struct wlr_backend *parent,
|
||||||
struct wlr_backend *parent) {
|
wlr_renderer_create_func_t create_renderer_func) {
|
||||||
assert(display && session && dev);
|
assert(display && session && gpu_fd >= 0);
|
||||||
assert(!parent || wlr_backend_is_drm(parent));
|
assert(!parent || wlr_backend_is_drm(parent));
|
||||||
|
|
||||||
char *name = drmGetDeviceNameFromFd2(dev->fd);
|
char *name = drmGetDeviceNameFromFd2(gpu_fd);
|
||||||
drmVersion *version = drmGetVersion(dev->fd);
|
drmVersion *version = drmGetVersion(gpu_fd);
|
||||||
wlr_log(WLR_INFO, "Initializing DRM backend for %s (%s)", name, version->name);
|
wlr_log(WLR_INFO, "Initializing DRM backend for %s (%s)", name, version->name);
|
||||||
|
free(name);
|
||||||
drmFreeVersion(version);
|
drmFreeVersion(version);
|
||||||
|
|
||||||
struct wlr_drm_backend *drm = calloc(1, sizeof(struct wlr_drm_backend));
|
struct wlr_drm_backend *drm = calloc(1, sizeof(struct wlr_drm_backend));
|
||||||
|
@ -191,40 +161,28 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
|
||||||
wlr_backend_init(&drm->backend, &backend_impl);
|
wlr_backend_init(&drm->backend, &backend_impl);
|
||||||
|
|
||||||
drm->session = session;
|
drm->session = session;
|
||||||
wl_list_init(&drm->fbs);
|
|
||||||
wl_list_init(&drm->outputs);
|
wl_list_init(&drm->outputs);
|
||||||
|
|
||||||
drm->dev = dev;
|
drm->fd = gpu_fd;
|
||||||
drm->fd = dev->fd;
|
|
||||||
drm->name = name;
|
|
||||||
|
|
||||||
if (parent != NULL) {
|
if (parent != NULL) {
|
||||||
drm->parent = get_drm_backend_from_backend(parent);
|
drm->parent = get_drm_backend_from_backend(parent);
|
||||||
|
|
||||||
drm->parent_destroy.notify = handle_parent_destroy;
|
|
||||||
wl_signal_add(&parent->events.destroy, &drm->parent_destroy);
|
|
||||||
} else {
|
|
||||||
wl_list_init(&drm->parent_destroy.link);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
drm->dev_change.notify = handle_dev_change;
|
drm->drm_invalidated.notify = drm_invalidated;
|
||||||
wl_signal_add(&dev->events.change, &drm->dev_change);
|
wlr_session_signal_add(session, gpu_fd, &drm->drm_invalidated);
|
||||||
|
|
||||||
drm->dev_remove.notify = handle_dev_remove;
|
|
||||||
wl_signal_add(&dev->events.remove, &drm->dev_remove);
|
|
||||||
|
|
||||||
drm->display = display;
|
drm->display = display;
|
||||||
|
|
||||||
struct wl_event_loop *event_loop = wl_display_get_event_loop(display);
|
struct wl_event_loop *event_loop = wl_display_get_event_loop(display);
|
||||||
|
|
||||||
drm->drm_event = wl_event_loop_add_fd(event_loop, drm->fd,
|
drm->drm_event = wl_event_loop_add_fd(event_loop, drm->fd,
|
||||||
WL_EVENT_READABLE, handle_drm_event, drm);
|
WL_EVENT_READABLE, handle_drm_event, NULL);
|
||||||
if (!drm->drm_event) {
|
if (!drm->drm_event) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create DRM event source");
|
wlr_log(WLR_ERROR, "Failed to create DRM event source");
|
||||||
goto error_fd;
|
goto error_fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
drm->session_active.notify = handle_session_active;
|
drm->session_signal.notify = session_signal;
|
||||||
wl_signal_add(&session->events.active, &drm->session_active);
|
wl_signal_add(&session->session_signal, &drm->session_signal);
|
||||||
|
|
||||||
if (!check_drm_features(drm)) {
|
if (!check_drm_features(drm)) {
|
||||||
goto error_event;
|
goto error_event;
|
||||||
|
@ -234,54 +192,21 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
|
||||||
goto error_event;
|
goto error_event;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (drm->parent) {
|
if (!init_drm_renderer(drm, &drm->renderer, create_renderer_func)) {
|
||||||
if (!init_drm_renderer(drm, &drm->mgpu_renderer)) {
|
wlr_log(WLR_ERROR, "Failed to initialize renderer");
|
||||||
wlr_log(WLR_ERROR, "Failed to initialize renderer");
|
goto error_event;
|
||||||
goto error_resources;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We'll perform a multi-GPU copy for all submitted buffers, we need
|
|
||||||
// to be able to texture from them
|
|
||||||
struct wlr_renderer *renderer = drm->mgpu_renderer.wlr_rend;
|
|
||||||
const struct wlr_drm_format_set *texture_formats =
|
|
||||||
wlr_renderer_get_dmabuf_texture_formats(renderer);
|
|
||||||
if (texture_formats == NULL) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to query renderer texture formats");
|
|
||||||
goto error_mgpu_renderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force a linear layout. In case explicit modifiers aren't supported,
|
|
||||||
// the meaning of implicit modifiers changes from one GPU to the other.
|
|
||||||
// In case explicit modifiers are supported, we still have no guarantee
|
|
||||||
// that the buffer producer will support these, so they might fallback
|
|
||||||
// to implicit modifiers.
|
|
||||||
for (size_t i = 0; i < texture_formats->len; i++) {
|
|
||||||
const struct wlr_drm_format *fmt = texture_formats->formats[i];
|
|
||||||
wlr_drm_format_set_add(&drm->mgpu_formats, fmt->format,
|
|
||||||
DRM_FORMAT_MOD_LINEAR);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
drm->session_destroy.notify = handle_session_destroy;
|
|
||||||
wl_signal_add(&session->events.destroy, &drm->session_destroy);
|
|
||||||
|
|
||||||
drm->display_destroy.notify = handle_display_destroy;
|
drm->display_destroy.notify = handle_display_destroy;
|
||||||
wl_display_add_destroy_listener(display, &drm->display_destroy);
|
wl_display_add_destroy_listener(display, &drm->display_destroy);
|
||||||
|
|
||||||
return &drm->backend;
|
return &drm->backend;
|
||||||
|
|
||||||
error_mgpu_renderer:
|
|
||||||
finish_drm_renderer(&drm->mgpu_renderer);
|
|
||||||
error_resources:
|
|
||||||
finish_drm_resources(drm);
|
|
||||||
error_event:
|
error_event:
|
||||||
wl_list_remove(&drm->session_active.link);
|
wl_list_remove(&drm->session_signal.link);
|
||||||
wl_event_source_remove(drm->drm_event);
|
wl_event_source_remove(drm->drm_event);
|
||||||
error_fd:
|
error_fd:
|
||||||
wl_list_remove(&drm->dev_remove.link);
|
wlr_session_close_file(drm->session, drm->fd);
|
||||||
wl_list_remove(&drm->dev_change.link);
|
|
||||||
wl_list_remove(&drm->parent_destroy.link);
|
|
||||||
wlr_session_close_file(drm->session, dev);
|
|
||||||
free(drm);
|
free(drm);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,269 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2005-2006 Luc Verhaegen.
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
|
||||||
* to deal in the Software without restriction, including without limitation
|
|
||||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
* and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
* Software is furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
||||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
|
||||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
||||||
* OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "backend/drm/cvt.h"
|
|
||||||
|
|
||||||
/* top/bottom margin size (% of height) - default: 1.8 */
|
|
||||||
#define CVT_MARGIN_PERCENTAGE 1.8
|
|
||||||
|
|
||||||
/* character cell horizontal granularity (pixels) - default 8 */
|
|
||||||
#define CVT_H_GRANULARITY 8
|
|
||||||
|
|
||||||
/* Minimum vertical porch (lines) - default 3 */
|
|
||||||
#define CVT_MIN_V_PORCH 3
|
|
||||||
|
|
||||||
/* Minimum number of vertical back porch lines - default 6 */
|
|
||||||
#define CVT_MIN_V_BPORCH 6
|
|
||||||
|
|
||||||
/* Pixel clock step (kHz) */
|
|
||||||
#define CVT_CLOCK_STEP 250
|
|
||||||
|
|
||||||
/* Minimum time of vertical sync + back porch interval (µs)
|
|
||||||
* default 550.0 */
|
|
||||||
#define CVT_MIN_VSYNC_BP 550.0
|
|
||||||
|
|
||||||
/* Nominal hsync width (% of line period) - default 8 */
|
|
||||||
#define CVT_HSYNC_PERCENTAGE 8
|
|
||||||
|
|
||||||
/* Definition of Horizontal blanking time limitation */
|
|
||||||
/* Gradient (%/kHz) - default 600 */
|
|
||||||
#define CVT_M_FACTOR 600
|
|
||||||
|
|
||||||
/* Offset (%) - default 40 */
|
|
||||||
#define CVT_C_FACTOR 40
|
|
||||||
|
|
||||||
/* Blanking time scaling factor - default 128 */
|
|
||||||
#define CVT_K_FACTOR 128
|
|
||||||
|
|
||||||
/* Scaling factor weighting - default 20 */
|
|
||||||
#define CVT_J_FACTOR 20
|
|
||||||
|
|
||||||
#define CVT_M_PRIME CVT_M_FACTOR * CVT_K_FACTOR / 256
|
|
||||||
#define CVT_C_PRIME (CVT_C_FACTOR - CVT_J_FACTOR) * CVT_K_FACTOR / 256 + \
|
|
||||||
CVT_J_FACTOR
|
|
||||||
|
|
||||||
/* Minimum vertical blanking interval time (µs) - default 460 */
|
|
||||||
#define CVT_RB_MIN_VBLANK 460.0
|
|
||||||
|
|
||||||
/* Fixed number of clocks for horizontal sync */
|
|
||||||
#define CVT_RB_H_SYNC 32.0
|
|
||||||
|
|
||||||
/* Fixed number of clocks for horizontal blanking */
|
|
||||||
#define CVT_RB_H_BLANK 160.0
|
|
||||||
|
|
||||||
/* Fixed number of lines for vertical front porch - default 3 */
|
|
||||||
#define CVT_RB_VFPORCH 3
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Generate a CVT standard mode from hdisplay, vdisplay and vrefresh.
|
|
||||||
*
|
|
||||||
* These calculations are stolen from the CVT calculation spreadsheet written
|
|
||||||
* by Graham Loveridge. He seems to be claiming no copyright and there seems to
|
|
||||||
* be no license attached to this. He apparently just wants to see his name
|
|
||||||
* mentioned.
|
|
||||||
*
|
|
||||||
* This file can be found at http://www.vesa.org/Public/CVT/CVTd6r1.xls
|
|
||||||
*
|
|
||||||
* Comments and structure corresponds to the comments and structure of the xls.
|
|
||||||
* This should ease importing of future changes to the standard (not very
|
|
||||||
* likely though).
|
|
||||||
*
|
|
||||||
* This function is borrowed from xorg-xserver's xf86CVTmode.
|
|
||||||
*/
|
|
||||||
void generate_cvt_mode(drmModeModeInfo *mode, int hdisplay, int vdisplay,
|
|
||||||
float vrefresh, bool reduced, bool interlaced) {
|
|
||||||
bool margins = false;
|
|
||||||
float vfield_rate, hperiod;
|
|
||||||
int hdisplay_rnd, hmargin;
|
|
||||||
int vdisplay_rnd, vmargin, vsync;
|
|
||||||
float interlace; /* Please rename this */
|
|
||||||
|
|
||||||
/* CVT default is 60.0Hz */
|
|
||||||
if (!vrefresh) {
|
|
||||||
vrefresh = 60.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 1. Required field rate */
|
|
||||||
if (interlaced) {
|
|
||||||
vfield_rate = vrefresh * 2;
|
|
||||||
} else {
|
|
||||||
vfield_rate = vrefresh;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 2. Horizontal pixels */
|
|
||||||
hdisplay_rnd = hdisplay - (hdisplay % CVT_H_GRANULARITY);
|
|
||||||
|
|
||||||
/* 3. Determine left and right borders */
|
|
||||||
if (margins) {
|
|
||||||
/* right margin is actually exactly the same as left */
|
|
||||||
hmargin = (((float) hdisplay_rnd) * CVT_MARGIN_PERCENTAGE / 100.0);
|
|
||||||
hmargin -= hmargin % CVT_H_GRANULARITY;
|
|
||||||
} else {
|
|
||||||
hmargin = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 4. Find total active pixels */
|
|
||||||
mode->hdisplay = hdisplay_rnd + 2 * hmargin;
|
|
||||||
|
|
||||||
/* 5. Find number of lines per field */
|
|
||||||
if (interlaced) {
|
|
||||||
vdisplay_rnd = vdisplay / 2;
|
|
||||||
} else {
|
|
||||||
vdisplay_rnd = vdisplay;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 6. Find top and bottom margins */
|
|
||||||
/* nope. */
|
|
||||||
if (margins) {
|
|
||||||
/* top and bottom margins are equal again. */
|
|
||||||
vmargin = (((float) vdisplay_rnd) * CVT_MARGIN_PERCENTAGE / 100.0);
|
|
||||||
} else {
|
|
||||||
vmargin = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
mode->vdisplay = vdisplay + 2 * vmargin;
|
|
||||||
|
|
||||||
/* 7. interlace */
|
|
||||||
if (interlaced) {
|
|
||||||
interlace = 0.5;
|
|
||||||
} else {
|
|
||||||
interlace = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Determine vsync Width from aspect ratio */
|
|
||||||
if (!(vdisplay % 3) && ((vdisplay * 4 / 3) == hdisplay)) {
|
|
||||||
vsync = 4;
|
|
||||||
} else if (!(vdisplay % 9) && ((vdisplay * 16 / 9) == hdisplay)) {
|
|
||||||
vsync = 5;
|
|
||||||
} else if (!(vdisplay % 10) && ((vdisplay * 16 / 10) == hdisplay)) {
|
|
||||||
vsync = 6;
|
|
||||||
} else if (!(vdisplay % 4) && ((vdisplay * 5 / 4) == hdisplay)) {
|
|
||||||
vsync = 7;
|
|
||||||
} else if (!(vdisplay % 9) && ((vdisplay * 15 / 9) == hdisplay)) {
|
|
||||||
vsync = 7;
|
|
||||||
} else { /* Custom */
|
|
||||||
vsync = 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!reduced) { /* simplified GTF calculation */
|
|
||||||
float hblank_percentage;
|
|
||||||
int vsync_and_back_porch, vblank_porch;
|
|
||||||
int hblank;
|
|
||||||
|
|
||||||
/* 8. Estimated Horizontal period */
|
|
||||||
hperiod = ((float) (1000000.0 / vfield_rate - CVT_MIN_VSYNC_BP)) /
|
|
||||||
(vdisplay_rnd + 2 * vmargin + CVT_MIN_V_PORCH + interlace);
|
|
||||||
|
|
||||||
/* 9. Find number of lines in sync + backporch */
|
|
||||||
if (((int) (CVT_MIN_VSYNC_BP / hperiod) + 1) <
|
|
||||||
(vsync + CVT_MIN_V_PORCH)) {
|
|
||||||
vsync_and_back_porch = vsync + CVT_MIN_V_PORCH;
|
|
||||||
} else {
|
|
||||||
vsync_and_back_porch = (int) (CVT_MIN_VSYNC_BP / hperiod) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 10. Find number of lines in back porch */
|
|
||||||
vblank_porch = vsync_and_back_porch - vsync;
|
|
||||||
(void) vblank_porch;
|
|
||||||
|
|
||||||
/* 11. Find total number of lines in vertical field */
|
|
||||||
mode->vtotal = vdisplay_rnd + 2 * vmargin + vsync_and_back_porch + interlace
|
|
||||||
+ CVT_MIN_V_PORCH;
|
|
||||||
|
|
||||||
/* 12. Find ideal blanking duty cycle from formula */
|
|
||||||
hblank_percentage = CVT_C_PRIME - CVT_M_PRIME * hperiod / 1000.0;
|
|
||||||
|
|
||||||
/* 13. Blanking time */
|
|
||||||
if (hblank_percentage < 20) {
|
|
||||||
hblank_percentage = 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
hblank = mode->hdisplay * hblank_percentage / (100.0 - hblank_percentage);
|
|
||||||
hblank -= hblank % (2 * CVT_H_GRANULARITY);
|
|
||||||
|
|
||||||
/* 14. Find total number of pixels in a line. */
|
|
||||||
mode->htotal = mode->hdisplay + hblank;
|
|
||||||
|
|
||||||
/* Fill in hsync values */
|
|
||||||
mode->hsync_end = mode->hdisplay + hblank / 2;
|
|
||||||
|
|
||||||
mode->hsync_start = mode->hsync_end -
|
|
||||||
(mode->htotal * CVT_HSYNC_PERCENTAGE) / 100;
|
|
||||||
mode->hsync_start += CVT_H_GRANULARITY -
|
|
||||||
mode->hsync_start % CVT_H_GRANULARITY;
|
|
||||||
|
|
||||||
/* Fill in vsync values */
|
|
||||||
mode->vsync_start = mode->vdisplay + CVT_MIN_V_PORCH;
|
|
||||||
mode->vsync_end = mode->vsync_start + vsync;
|
|
||||||
} else { /* reduced blanking */
|
|
||||||
int vbi_lines;
|
|
||||||
|
|
||||||
/* 8. Estimate Horizontal period. */
|
|
||||||
hperiod = ((float) (1000000.0 / vfield_rate - CVT_RB_MIN_VBLANK)) /
|
|
||||||
(vdisplay_rnd + 2 * vmargin);
|
|
||||||
|
|
||||||
/* 9. Find number of lines in vertical blanking */
|
|
||||||
vbi_lines = ((float) CVT_RB_MIN_VBLANK) / hperiod + 1;
|
|
||||||
|
|
||||||
/* 10. Check if vertical blanking is sufficient */
|
|
||||||
if (vbi_lines < (CVT_RB_VFPORCH + vsync + CVT_MIN_V_BPORCH)) {
|
|
||||||
vbi_lines = CVT_RB_VFPORCH + vsync + CVT_MIN_V_BPORCH;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 11. Find total number of lines in vertical field */
|
|
||||||
mode->vtotal = vdisplay_rnd + 2 * vmargin + interlace + vbi_lines;
|
|
||||||
|
|
||||||
/* 12. Find total number of pixels in a line */
|
|
||||||
mode->htotal = mode->hdisplay + CVT_RB_H_BLANK;
|
|
||||||
|
|
||||||
/* Fill in hsync values */
|
|
||||||
mode->hsync_end = mode->hdisplay + CVT_RB_H_BLANK / 2;
|
|
||||||
mode->hsync_start = mode->hsync_end - CVT_RB_H_SYNC;
|
|
||||||
|
|
||||||
/* Fill in vsync values */
|
|
||||||
mode->vsync_start = mode->vdisplay + CVT_RB_VFPORCH;
|
|
||||||
mode->vsync_end = mode->vsync_start + vsync;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 15/13. Find pixel clock frequency (kHz for xf86) */
|
|
||||||
mode->clock = mode->htotal * 1000.0 / hperiod;
|
|
||||||
mode->clock -= mode->clock % CVT_CLOCK_STEP;
|
|
||||||
|
|
||||||
/* 17/15. Find actual Field rate */
|
|
||||||
mode->vrefresh = (1000.0 * ((float) mode->clock)) /
|
|
||||||
((float) (mode->htotal * mode->vtotal));
|
|
||||||
|
|
||||||
/* 18/16. Find actual vertical frame frequency */
|
|
||||||
/* ignore - just set the mode flag for interlaced */
|
|
||||||
if (interlaced) {
|
|
||||||
mode->vtotal *= 2;
|
|
||||||
mode->flags |= DRM_MODE_FLAG_INTERLACE;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(mode->name, sizeof(mode->name), "%dx%d", hdisplay, vdisplay);
|
|
||||||
|
|
||||||
if (reduced) {
|
|
||||||
mode->flags |= DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC;
|
|
||||||
} else {
|
|
||||||
mode->flags |= DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC;
|
|
||||||
}
|
|
||||||
}
|
|
1907
backend/drm/drm.c
1907
backend/drm/drm.c
File diff suppressed because it is too large
Load Diff
|
@ -1,5 +1,4 @@
|
||||||
#include <assert.h>
|
#include <gbm.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <xf86drm.h>
|
#include <xf86drm.h>
|
||||||
#include <xf86drmMode.h>
|
#include <xf86drmMode.h>
|
||||||
|
@ -7,225 +6,78 @@
|
||||||
#include "backend/drm/iface.h"
|
#include "backend/drm/iface.h"
|
||||||
#include "backend/drm/util.h"
|
#include "backend/drm/util.h"
|
||||||
|
|
||||||
static bool legacy_fb_props_match(struct wlr_drm_fb *fb1,
|
static bool legacy_crtc_pageflip(struct wlr_drm_backend *drm,
|
||||||
struct wlr_drm_fb *fb2) {
|
struct wlr_drm_connector *conn, struct wlr_drm_crtc *crtc,
|
||||||
struct wlr_dmabuf_attributes dmabuf1 = {0}, dmabuf2 = {0};
|
uint32_t fb_id, drmModeModeInfo *mode) {
|
||||||
if (!wlr_buffer_get_dmabuf(fb1->wlr_buf, &dmabuf1) ||
|
if (mode) {
|
||||||
!wlr_buffer_get_dmabuf(fb2->wlr_buf, &dmabuf2)) {
|
if (drmModeSetCrtc(drm->fd, crtc->id, fb_id, 0, 0,
|
||||||
return false;
|
&conn->id, 1, mode)) {
|
||||||
}
|
wlr_log_errno(WLR_ERROR, "%s: Failed to set CRTC", conn->output.name);
|
||||||
|
|
||||||
if (dmabuf1.width != dmabuf2.width ||
|
|
||||||
dmabuf1.height != dmabuf2.height ||
|
|
||||||
dmabuf1.format != dmabuf2.format ||
|
|
||||||
dmabuf1.modifier != dmabuf2.modifier ||
|
|
||||||
dmabuf1.n_planes != dmabuf2.n_planes) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < dmabuf1.n_planes; i++) {
|
|
||||||
if (dmabuf1.stride[i] != dmabuf2.stride[i] ||
|
|
||||||
dmabuf1.offset[i] != dmabuf2.offset[i]) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (drmModePageFlip(drm->fd, crtc->id, fb_id, DRM_MODE_PAGE_FLIP_EVENT, conn)) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "%s: Failed to page flip", conn->output.name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool legacy_crtc_test(struct wlr_drm_connector *conn,
|
static bool legacy_conn_enable(struct wlr_drm_backend *drm,
|
||||||
const struct wlr_drm_connector_state *state) {
|
struct wlr_drm_connector *conn, bool enable) {
|
||||||
struct wlr_drm_crtc *crtc = conn->crtc;
|
int ret = drmModeConnectorSetProperty(drm->fd, conn->id, conn->props.dpms,
|
||||||
|
enable ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF);
|
||||||
if ((state->base->committed & WLR_OUTPUT_STATE_BUFFER) && !state->modeset) {
|
return ret >= 0;
|
||||||
struct wlr_drm_fb *pending_fb = crtc->primary->pending_fb;
|
|
||||||
|
|
||||||
struct wlr_drm_fb *prev_fb = crtc->primary->queued_fb;
|
|
||||||
if (!prev_fb) {
|
|
||||||
prev_fb = crtc->primary->current_fb;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Legacy is only guaranteed to be able to display a FB if it's been
|
|
||||||
* allocated the same way as the previous one. */
|
|
||||||
if (prev_fb != NULL && !legacy_fb_props_match(prev_fb, pending_fb)) {
|
|
||||||
wlr_drm_conn_log(conn, WLR_DEBUG,
|
|
||||||
"Cannot change scan-out buffer parameters with legacy KMS API");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool legacy_crtc_commit(struct wlr_drm_connector *conn,
|
bool legacy_crtc_set_cursor(struct wlr_drm_backend *drm,
|
||||||
const struct wlr_drm_connector_state *state,
|
struct wlr_drm_crtc *crtc, struct gbm_bo *bo) {
|
||||||
uint32_t flags, bool test_only) {
|
if (!crtc || !crtc->cursor) {
|
||||||
if (!legacy_crtc_test(conn, state)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (test_only) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_drm_backend *drm = conn->backend;
|
if (!bo) {
|
||||||
struct wlr_output *output = &conn->output;
|
|
||||||
struct wlr_drm_crtc *crtc = conn->crtc;
|
|
||||||
struct wlr_drm_plane *cursor = crtc->cursor;
|
|
||||||
|
|
||||||
uint32_t fb_id = 0;
|
|
||||||
if (state->active) {
|
|
||||||
struct wlr_drm_fb *fb = plane_get_next_fb(crtc->primary);
|
|
||||||
if (fb == NULL) {
|
|
||||||
wlr_log(WLR_ERROR, "%s: failed to acquire primary FB",
|
|
||||||
conn->output.name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
fb_id = fb->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->modeset) {
|
|
||||||
uint32_t *conns = NULL;
|
|
||||||
size_t conns_len = 0;
|
|
||||||
drmModeModeInfo *mode = NULL;
|
|
||||||
if (state->active) {
|
|
||||||
conns = &conn->id;
|
|
||||||
conns_len = 1;
|
|
||||||
mode = (drmModeModeInfo *)&state->mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t dpms = state->active ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF;
|
|
||||||
if (drmModeConnectorSetProperty(drm->fd, conn->id, conn->props.dpms,
|
|
||||||
dpms) != 0) {
|
|
||||||
wlr_drm_conn_log_errno(conn, WLR_ERROR,
|
|
||||||
"Failed to set DPMS property");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (drmModeSetCrtc(drm->fd, crtc->id, fb_id, 0, 0,
|
|
||||||
conns, conns_len, mode)) {
|
|
||||||
wlr_drm_conn_log_errno(conn, WLR_ERROR, "Failed to set CRTC");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->base->committed & WLR_OUTPUT_STATE_GAMMA_LUT) {
|
|
||||||
if (!drm_legacy_crtc_set_gamma(drm, crtc,
|
|
||||||
state->base->gamma_lut_size, state->base->gamma_lut)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((state->base->committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED) &&
|
|
||||||
drm_connector_supports_vrr(conn)) {
|
|
||||||
if (drmModeObjectSetProperty(drm->fd, crtc->id, DRM_MODE_OBJECT_CRTC,
|
|
||||||
crtc->props.vrr_enabled,
|
|
||||||
state->base->adaptive_sync_enabled) != 0) {
|
|
||||||
wlr_drm_conn_log_errno(conn, WLR_ERROR,
|
|
||||||
"drmModeObjectSetProperty(VRR_ENABLED) failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
output->adaptive_sync_status = state->base->adaptive_sync_enabled ?
|
|
||||||
WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED :
|
|
||||||
WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED;
|
|
||||||
wlr_drm_conn_log(conn, WLR_DEBUG, "VRR %s",
|
|
||||||
state->base->adaptive_sync_enabled ? "enabled" : "disabled");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cursor != NULL && drm_connector_is_cursor_visible(conn)) {
|
|
||||||
struct wlr_drm_fb *cursor_fb = plane_get_next_fb(cursor);
|
|
||||||
if (cursor_fb == NULL) {
|
|
||||||
wlr_drm_conn_log(conn, WLR_DEBUG, "Failed to acquire cursor FB");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
drmModeFB *drm_fb = drmModeGetFB(drm->fd, cursor_fb->id);
|
|
||||||
if (drm_fb == NULL) {
|
|
||||||
wlr_drm_conn_log_errno(conn, WLR_DEBUG, "Failed to get cursor "
|
|
||||||
"BO handle: drmModeGetFB failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
uint32_t cursor_handle = drm_fb->handle;
|
|
||||||
uint32_t cursor_width = drm_fb->width;
|
|
||||||
uint32_t cursor_height = drm_fb->height;
|
|
||||||
drmModeFreeFB(drm_fb);
|
|
||||||
|
|
||||||
int ret = drmModeSetCursor(drm->fd, crtc->id, cursor_handle,
|
|
||||||
cursor_width, cursor_height);
|
|
||||||
int set_cursor_errno = errno;
|
|
||||||
if (drmCloseBufferHandle(drm->fd, cursor_handle) != 0) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "drmCloseBufferHandle failed");
|
|
||||||
}
|
|
||||||
if (ret != 0) {
|
|
||||||
wlr_drm_conn_log(conn, WLR_DEBUG, "drmModeSetCursor failed: %s",
|
|
||||||
strerror(set_cursor_errno));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (drmModeMoveCursor(drm->fd,
|
|
||||||
crtc->id, conn->cursor_x, conn->cursor_y) != 0) {
|
|
||||||
wlr_drm_conn_log_errno(conn, WLR_ERROR, "drmModeMoveCursor failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (drmModeSetCursor(drm->fd, crtc->id, 0, 0, 0)) {
|
if (drmModeSetCursor(drm->fd, crtc->id, 0, 0, 0)) {
|
||||||
wlr_drm_conn_log_errno(conn, WLR_DEBUG, "drmModeSetCursor failed");
|
wlr_log_errno(WLR_DEBUG, "Failed to clear hardware cursor");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & DRM_MODE_PAGE_FLIP_EVENT) {
|
struct wlr_drm_plane *plane = crtc->cursor;
|
||||||
if (drmModePageFlip(drm->fd, crtc->id, fb_id,
|
|
||||||
DRM_MODE_PAGE_FLIP_EVENT, drm)) {
|
|
||||||
wlr_drm_conn_log_errno(conn, WLR_ERROR, "drmModePageFlip failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
if (drmModeSetCursor(drm->fd, crtc->id, gbm_bo_get_handle(bo).u32,
|
||||||
}
|
plane->surf.width, plane->surf.height)) {
|
||||||
|
wlr_log_errno(WLR_DEBUG, "Failed to set hardware cursor");
|
||||||
static void fill_empty_gamma_table(size_t size,
|
|
||||||
uint16_t *r, uint16_t *g, uint16_t *b) {
|
|
||||||
assert(0xFFFF < UINT64_MAX / (size - 1));
|
|
||||||
for (uint32_t i = 0; i < size; ++i) {
|
|
||||||
uint16_t val = (uint64_t)0xFFFF * i / (size - 1);
|
|
||||||
r[i] = g[i] = b[i] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool drm_legacy_crtc_set_gamma(struct wlr_drm_backend *drm,
|
|
||||||
struct wlr_drm_crtc *crtc, size_t size, uint16_t *lut) {
|
|
||||||
uint16_t *linear_lut = NULL;
|
|
||||||
if (size == 0) {
|
|
||||||
// The legacy interface doesn't offer a way to reset the gamma LUT
|
|
||||||
size = drm_crtc_get_gamma_lut_size(drm, crtc);
|
|
||||||
if (size == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
linear_lut = malloc(3 * size * sizeof(uint16_t));
|
|
||||||
if (linear_lut == NULL) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
fill_empty_gamma_table(size, linear_lut, linear_lut + size,
|
|
||||||
linear_lut + 2 * size);
|
|
||||||
|
|
||||||
lut = linear_lut;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t *r = lut, *g = lut + size, *b = lut + 2 * size;
|
|
||||||
if (drmModeCrtcSetGamma(drm->fd, crtc->id, size, r, g, b) != 0) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Failed to set gamma LUT on CRTC %"PRIu32,
|
|
||||||
crtc->id);
|
|
||||||
free(linear_lut);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(linear_lut);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool legacy_crtc_move_cursor(struct wlr_drm_backend *drm,
|
||||||
|
struct wlr_drm_crtc *crtc, int x, int y) {
|
||||||
|
return !drmModeMoveCursor(drm->fd, crtc->id, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool legacy_crtc_set_gamma(struct wlr_drm_backend *drm,
|
||||||
|
struct wlr_drm_crtc *crtc, size_t size,
|
||||||
|
uint16_t *r, uint16_t *g, uint16_t *b) {
|
||||||
|
return !drmModeCrtcSetGamma(drm->fd, crtc->id, (uint32_t)size, r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t legacy_crtc_get_gamma_size(struct wlr_drm_backend *drm,
|
||||||
|
struct wlr_drm_crtc *crtc) {
|
||||||
|
return (size_t)crtc->legacy_crtc->gamma_size;
|
||||||
|
}
|
||||||
|
|
||||||
const struct wlr_drm_interface legacy_iface = {
|
const struct wlr_drm_interface legacy_iface = {
|
||||||
.crtc_commit = legacy_crtc_commit,
|
.conn_enable = legacy_conn_enable,
|
||||||
|
.crtc_pageflip = legacy_crtc_pageflip,
|
||||||
|
.crtc_set_cursor = legacy_crtc_set_cursor,
|
||||||
|
.crtc_move_cursor = legacy_crtc_move_cursor,
|
||||||
|
.crtc_set_gamma = legacy_crtc_set_gamma,
|
||||||
|
.crtc_get_gamma_size = legacy_crtc_get_gamma_size,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
wlr_files += files(
|
|
||||||
'atomic.c',
|
|
||||||
'backend.c',
|
|
||||||
'cvt.c',
|
|
||||||
'drm.c',
|
|
||||||
'legacy.c',
|
|
||||||
'monitor.c',
|
|
||||||
'properties.c',
|
|
||||||
'renderer.c',
|
|
||||||
'util.c',
|
|
||||||
)
|
|
||||||
|
|
||||||
features += { 'drm-backend': true }
|
|
|
@ -1,94 +0,0 @@
|
||||||
#include <wlr/util/log.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "backend/drm/monitor.h"
|
|
||||||
#include "backend/multi.h"
|
|
||||||
#include "backend/session/session.h"
|
|
||||||
|
|
||||||
static void drm_backend_monitor_destroy(struct wlr_drm_backend_monitor* monitor) {
|
|
||||||
wl_list_remove(&monitor->session_add_drm_card.link);
|
|
||||||
wl_list_remove(&monitor->session_destroy.link);
|
|
||||||
wl_list_remove(&monitor->primary_drm_destroy.link);
|
|
||||||
wl_list_remove(&monitor->multi_destroy.link);
|
|
||||||
free(monitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_add_drm_card(struct wl_listener *listener, void *data) {
|
|
||||||
struct wlr_session_add_event *event = data;
|
|
||||||
struct wlr_drm_backend_monitor *backend_monitor =
|
|
||||||
wl_container_of(listener, backend_monitor, session_add_drm_card);
|
|
||||||
|
|
||||||
struct wlr_device *dev =
|
|
||||||
session_open_if_kms(backend_monitor->session, event->path);
|
|
||||||
if (!dev) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to open %s as DRM device", event->path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_log(WLR_DEBUG, "Creating DRM backend for %s after hotplug", event->path);
|
|
||||||
struct wlr_backend *child_drm = wlr_drm_backend_create(
|
|
||||||
backend_monitor->session->display, backend_monitor->session,
|
|
||||||
dev, backend_monitor->primary_drm);
|
|
||||||
if (!child_drm) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to create DRM backend after hotplug");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wlr_multi_backend_add(backend_monitor->multi, child_drm)) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to add new drm backend to multi backend");
|
|
||||||
wlr_backend_destroy(child_drm);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wlr_backend_start(child_drm)) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to start new child DRM backend");
|
|
||||||
wlr_backend_destroy(child_drm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_session_destroy(struct wl_listener *listener, void *data) {
|
|
||||||
struct wlr_drm_backend_monitor *backend_monitor =
|
|
||||||
wl_container_of(listener, backend_monitor, session_destroy);
|
|
||||||
drm_backend_monitor_destroy(backend_monitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_primary_drm_destroy(struct wl_listener *listener, void *data) {
|
|
||||||
struct wlr_drm_backend_monitor *backend_monitor =
|
|
||||||
wl_container_of(listener, backend_monitor, primary_drm_destroy);
|
|
||||||
drm_backend_monitor_destroy(backend_monitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_multi_destroy(struct wl_listener *listener, void *data) {
|
|
||||||
struct wlr_drm_backend_monitor *backend_monitor =
|
|
||||||
wl_container_of(listener, backend_monitor, multi_destroy);
|
|
||||||
drm_backend_monitor_destroy(backend_monitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_drm_backend_monitor *drm_backend_monitor_create(
|
|
||||||
struct wlr_backend *multi,
|
|
||||||
struct wlr_backend *primary_drm,
|
|
||||||
struct wlr_session *session) {
|
|
||||||
struct wlr_drm_backend_monitor *monitor =
|
|
||||||
calloc(1, sizeof(struct wlr_drm_backend_monitor));
|
|
||||||
if (!monitor) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
monitor->multi = multi;
|
|
||||||
monitor->primary_drm = primary_drm;
|
|
||||||
monitor->session = session;
|
|
||||||
|
|
||||||
monitor->session_add_drm_card.notify = handle_add_drm_card;
|
|
||||||
wl_signal_add(&session->events.add_drm_card, &monitor->session_add_drm_card);
|
|
||||||
|
|
||||||
monitor->session_destroy.notify = handle_session_destroy;
|
|
||||||
wl_signal_add(&session->events.destroy, &monitor->session_destroy);
|
|
||||||
|
|
||||||
monitor->primary_drm_destroy.notify = handle_primary_drm_destroy;
|
|
||||||
wl_signal_add(&primary_drm->events.destroy, &monitor->primary_drm_destroy);
|
|
||||||
|
|
||||||
monitor->multi_destroy.notify = handle_multi_destroy;
|
|
||||||
wl_signal_add(&multi->events.destroy, &monitor->multi_destroy);
|
|
||||||
|
|
||||||
return monitor;
|
|
||||||
}
|
|
|
@ -1,4 +1,3 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
@ -20,44 +19,38 @@ struct prop_info {
|
||||||
|
|
||||||
static const struct prop_info connector_info[] = {
|
static const struct prop_info connector_info[] = {
|
||||||
#define INDEX(name) (offsetof(union wlr_drm_connector_props, name) / sizeof(uint32_t))
|
#define INDEX(name) (offsetof(union wlr_drm_connector_props, name) / sizeof(uint32_t))
|
||||||
{ "CRTC_ID", INDEX(crtc_id) },
|
{ "CRTC_ID", INDEX(crtc_id) },
|
||||||
{ "DPMS", INDEX(dpms) },
|
{ "DPMS", INDEX(dpms) },
|
||||||
{ "EDID", INDEX(edid) },
|
{ "EDID", INDEX(edid) },
|
||||||
{ "PATH", INDEX(path) },
|
{ "PATH", INDEX(path) },
|
||||||
{ "link-status", INDEX(link_status) },
|
{ "link-status", INDEX(link_status) },
|
||||||
{ "non-desktop", INDEX(non_desktop) },
|
|
||||||
{ "panel orientation", INDEX(panel_orientation) },
|
|
||||||
{ "subconnector", INDEX(subconnector) },
|
|
||||||
{ "vrr_capable", INDEX(vrr_capable) },
|
|
||||||
#undef INDEX
|
#undef INDEX
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct prop_info crtc_info[] = {
|
static const struct prop_info crtc_info[] = {
|
||||||
#define INDEX(name) (offsetof(union wlr_drm_crtc_props, name) / sizeof(uint32_t))
|
#define INDEX(name) (offsetof(union wlr_drm_crtc_props, name) / sizeof(uint32_t))
|
||||||
{ "ACTIVE", INDEX(active) },
|
{ "ACTIVE", INDEX(active) },
|
||||||
{ "GAMMA_LUT", INDEX(gamma_lut) },
|
{ "GAMMA_LUT", INDEX(gamma_lut) },
|
||||||
{ "GAMMA_LUT_SIZE", INDEX(gamma_lut_size) },
|
{ "GAMMA_LUT_SIZE", INDEX(gamma_lut_size) },
|
||||||
{ "MODE_ID", INDEX(mode_id) },
|
{ "MODE_ID", INDEX(mode_id) },
|
||||||
{ "VRR_ENABLED", INDEX(vrr_enabled) },
|
{ "rotation", INDEX(rotation) },
|
||||||
|
{ "scaling mode", INDEX(scaling_mode) },
|
||||||
#undef INDEX
|
#undef INDEX
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct prop_info plane_info[] = {
|
static const struct prop_info plane_info[] = {
|
||||||
#define INDEX(name) (offsetof(union wlr_drm_plane_props, name) / sizeof(uint32_t))
|
#define INDEX(name) (offsetof(union wlr_drm_plane_props, name) / sizeof(uint32_t))
|
||||||
{ "CRTC_H", INDEX(crtc_h) },
|
{ "CRTC_H", INDEX(crtc_h) },
|
||||||
{ "CRTC_ID", INDEX(crtc_id) },
|
{ "CRTC_ID", INDEX(crtc_id) },
|
||||||
{ "CRTC_W", INDEX(crtc_w) },
|
{ "CRTC_W", INDEX(crtc_w) },
|
||||||
{ "CRTC_X", INDEX(crtc_x) },
|
{ "CRTC_X", INDEX(crtc_x) },
|
||||||
{ "CRTC_Y", INDEX(crtc_y) },
|
{ "CRTC_Y", INDEX(crtc_y) },
|
||||||
{ "FB_DAMAGE_CLIPS", INDEX(fb_damage_clips) },
|
{ "FB_ID", INDEX(fb_id) },
|
||||||
{ "FB_ID", INDEX(fb_id) },
|
{ "SRC_H", INDEX(src_h) },
|
||||||
{ "IN_FORMATS", INDEX(in_formats) },
|
{ "SRC_W", INDEX(src_w) },
|
||||||
{ "SRC_H", INDEX(src_h) },
|
{ "SRC_X", INDEX(src_x) },
|
||||||
{ "SRC_W", INDEX(src_w) },
|
{ "SRC_Y", INDEX(src_y) },
|
||||||
{ "SRC_X", INDEX(src_x) },
|
{ "type", INDEX(type) },
|
||||||
{ "SRC_Y", INDEX(src_y) },
|
|
||||||
{ "rotation", INDEX(rotation) },
|
|
||||||
{ "type", INDEX(type) },
|
|
||||||
#undef INDEX
|
#undef INDEX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -156,27 +149,3 @@ void *get_drm_prop_blob(int fd, uint32_t obj, uint32_t prop, size_t *ret_len) {
|
||||||
drmModeFreePropertyBlob(blob);
|
drmModeFreePropertyBlob(blob);
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *get_drm_prop_enum(int fd, uint32_t obj, uint32_t prop_id) {
|
|
||||||
uint64_t value;
|
|
||||||
if (!get_drm_prop(fd, obj, prop_id, &value)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
drmModePropertyRes *prop = drmModeGetProperty(fd, prop_id);
|
|
||||||
if (!prop) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *str = NULL;
|
|
||||||
for (int i = 0; i < prop->count_enums; i++) {
|
|
||||||
if (prop->enums[i].value == value) {
|
|
||||||
str = strdup(prop->enums[i].name);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
drmModeFreeProperty(prop);
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,42 +1,54 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <drm_fourcc.h>
|
#include <EGL/egl.h>
|
||||||
#include <fcntl.h>
|
#include <EGL/eglext.h>
|
||||||
|
#include <gbm.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-util.h>
|
#include <wayland-util.h>
|
||||||
|
#include <wlr/render/egl.h>
|
||||||
|
#include <wlr/render/gles2.h>
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/types/wlr_matrix.h>
|
#include <wlr/types/wlr_matrix.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include "backend/drm/drm.h"
|
#include "backend/drm/drm.h"
|
||||||
#include "backend/drm/util.h"
|
#include "glapi.h"
|
||||||
#include "render/drm_format_set.h"
|
|
||||||
#include "render/allocator/allocator.h"
|
|
||||||
#include "render/pixel_format.h"
|
|
||||||
#include "render/swapchain.h"
|
|
||||||
#include "render/wlr_renderer.h"
|
|
||||||
|
|
||||||
bool init_drm_renderer(struct wlr_drm_backend *drm,
|
bool init_drm_renderer(struct wlr_drm_backend *drm,
|
||||||
struct wlr_drm_renderer *renderer) {
|
struct wlr_drm_renderer *renderer, wlr_renderer_create_func_t create_renderer_func) {
|
||||||
renderer->backend = drm;
|
renderer->gbm = gbm_create_device(drm->fd);
|
||||||
|
if (!renderer->gbm) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to create GBM device");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
renderer->wlr_rend = renderer_autocreate_with_drm_fd(drm->fd);
|
if (!create_renderer_func) {
|
||||||
|
create_renderer_func = wlr_renderer_autocreate;
|
||||||
|
}
|
||||||
|
|
||||||
|
static EGLint config_attribs[] = {
|
||||||
|
EGL_RED_SIZE, 1,
|
||||||
|
EGL_GREEN_SIZE, 1,
|
||||||
|
EGL_BLUE_SIZE, 1,
|
||||||
|
EGL_ALPHA_SIZE, 1,
|
||||||
|
EGL_NONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
renderer->gbm_format = GBM_FORMAT_ARGB8888;
|
||||||
|
renderer->wlr_rend = create_renderer_func(&renderer->egl,
|
||||||
|
EGL_PLATFORM_GBM_MESA, renderer->gbm,
|
||||||
|
config_attribs, renderer->gbm_format);
|
||||||
if (!renderer->wlr_rend) {
|
if (!renderer->wlr_rend) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create renderer");
|
wlr_log(WLR_ERROR, "Failed to create EGL/WLR renderer");
|
||||||
return false;
|
goto error_gbm;
|
||||||
}
|
|
||||||
|
|
||||||
renderer->allocator = allocator_autocreate_with_drm_fd(&drm->backend,
|
|
||||||
renderer->wlr_rend, drm->fd);
|
|
||||||
if (renderer->allocator == NULL) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to create allocator");
|
|
||||||
wlr_renderer_destroy(renderer->wlr_rend);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderer->fd = drm->fd;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
error_gbm:
|
||||||
|
gbm_device_destroy(renderer->gbm);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void finish_drm_renderer(struct wlr_drm_renderer *renderer) {
|
void finish_drm_renderer(struct wlr_drm_renderer *renderer) {
|
||||||
|
@ -44,13 +56,14 @@ void finish_drm_renderer(struct wlr_drm_renderer *renderer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_allocator_destroy(renderer->allocator);
|
|
||||||
wlr_renderer_destroy(renderer->wlr_rend);
|
wlr_renderer_destroy(renderer->wlr_rend);
|
||||||
|
wlr_egl_finish(&renderer->egl);
|
||||||
|
gbm_device_destroy(renderer->gbm);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool init_drm_surface(struct wlr_drm_surface *surf,
|
bool init_drm_surface(struct wlr_drm_surface *surf,
|
||||||
struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height,
|
struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height,
|
||||||
const struct wlr_drm_format *drm_format) {
|
uint32_t format, uint32_t flags) {
|
||||||
if (surf->width == width && surf->height == height) {
|
if (surf->width == width && surf->height == height) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -59,357 +72,189 @@ bool init_drm_surface(struct wlr_drm_surface *surf,
|
||||||
surf->width = width;
|
surf->width = width;
|
||||||
surf->height = height;
|
surf->height = height;
|
||||||
|
|
||||||
wlr_swapchain_destroy(surf->swapchain);
|
if (surf->gbm) {
|
||||||
surf->swapchain = NULL;
|
if (surf->front) {
|
||||||
|
gbm_surface_release_buffer(surf->gbm, surf->front);
|
||||||
|
surf->front = NULL;
|
||||||
|
}
|
||||||
|
if (surf->back) {
|
||||||
|
gbm_surface_release_buffer(surf->gbm, surf->back);
|
||||||
|
surf->back = NULL;
|
||||||
|
}
|
||||||
|
gbm_surface_destroy(surf->gbm);
|
||||||
|
}
|
||||||
|
wlr_egl_destroy_surface(&surf->renderer->egl, surf->egl);
|
||||||
|
|
||||||
surf->swapchain = wlr_swapchain_create(renderer->allocator, width, height,
|
surf->gbm = gbm_surface_create(renderer->gbm, width, height,
|
||||||
drm_format);
|
format, GBM_BO_USE_RENDERING | flags);
|
||||||
if (surf->swapchain == NULL) {
|
if (!surf->gbm) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create swapchain");
|
wlr_log_errno(WLR_ERROR, "Failed to create GBM surface");
|
||||||
memset(surf, 0, sizeof(*surf));
|
goto error_zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
surf->egl = wlr_egl_create_surface(&renderer->egl, surf->gbm);
|
||||||
|
if (surf->egl == EGL_NO_SURFACE) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to create EGL surface");
|
||||||
|
goto error_gbm;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error_gbm:
|
||||||
|
gbm_surface_destroy(surf->gbm);
|
||||||
|
error_zero:
|
||||||
|
memset(surf, 0, sizeof(*surf));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void finish_drm_surface(struct wlr_drm_surface *surf) {
|
||||||
|
if (!surf || !surf->renderer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (surf->front) {
|
||||||
|
gbm_surface_release_buffer(surf->gbm, surf->front);
|
||||||
|
}
|
||||||
|
if (surf->back) {
|
||||||
|
gbm_surface_release_buffer(surf->gbm, surf->back);
|
||||||
|
}
|
||||||
|
|
||||||
|
wlr_egl_destroy_surface(&surf->renderer->egl, surf->egl);
|
||||||
|
if (surf->gbm) {
|
||||||
|
gbm_surface_destroy(surf->gbm);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(surf, 0, sizeof(*surf));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool make_drm_surface_current(struct wlr_drm_surface *surf,
|
||||||
|
int *buffer_damage) {
|
||||||
|
return wlr_egl_make_current(&surf->renderer->egl, surf->egl, buffer_damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct gbm_bo *swap_drm_surface_buffers(struct wlr_drm_surface *surf,
|
||||||
|
pixman_region32_t *damage) {
|
||||||
|
if (surf->front) {
|
||||||
|
gbm_surface_release_buffer(surf->gbm, surf->front);
|
||||||
|
}
|
||||||
|
|
||||||
|
wlr_egl_swap_buffers(&surf->renderer->egl, surf->egl, damage);
|
||||||
|
|
||||||
|
surf->front = surf->back;
|
||||||
|
surf->back = gbm_surface_lock_front_buffer(surf->gbm);
|
||||||
|
return surf->back;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct gbm_bo *get_drm_surface_front(struct wlr_drm_surface *surf) {
|
||||||
|
if (surf->front) {
|
||||||
|
return surf->front;
|
||||||
|
}
|
||||||
|
|
||||||
|
make_drm_surface_current(surf, NULL);
|
||||||
|
struct wlr_renderer *renderer = surf->renderer->wlr_rend;
|
||||||
|
wlr_renderer_begin(renderer, surf->width, surf->height);
|
||||||
|
wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 1.0 });
|
||||||
|
wlr_renderer_end(renderer);
|
||||||
|
return swap_drm_surface_buffers(surf, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void post_drm_surface(struct wlr_drm_surface *surf) {
|
||||||
|
if (surf->front) {
|
||||||
|
gbm_surface_release_buffer(surf->gbm, surf->front);
|
||||||
|
surf->front = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs) {
|
||||||
|
memset(attribs, 0, sizeof(struct wlr_dmabuf_attributes));
|
||||||
|
|
||||||
|
attribs->n_planes = gbm_bo_get_plane_count(bo);
|
||||||
|
if (attribs->n_planes > WLR_DMABUF_MAX_PLANES) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
attribs->width = gbm_bo_get_width(bo);
|
||||||
|
attribs->height = gbm_bo_get_height(bo);
|
||||||
|
attribs->format = gbm_bo_get_format(bo);
|
||||||
|
attribs->modifier = gbm_bo_get_modifier(bo);
|
||||||
|
|
||||||
|
for (int i = 0; i < attribs->n_planes; ++i) {
|
||||||
|
attribs->offset[i] = gbm_bo_get_offset(bo, i);
|
||||||
|
attribs->stride[i] = gbm_bo_get_stride_for_plane(bo, i);
|
||||||
|
attribs->fd[i] = gbm_bo_get_fd(bo);
|
||||||
|
if (attribs->fd[i] < 0) {
|
||||||
|
for (int j = 0; j < i; ++j) {
|
||||||
|
close(attribs->fd[j]);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_tex(struct gbm_bo *bo, void *data) {
|
||||||
|
struct wlr_texture *tex = data;
|
||||||
|
wlr_texture_destroy(tex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wlr_texture *get_tex_for_bo(struct wlr_drm_renderer *renderer,
|
||||||
|
struct gbm_bo *bo) {
|
||||||
|
struct wlr_texture *tex = gbm_bo_get_user_data(bo);
|
||||||
|
if (tex) {
|
||||||
|
return tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_dmabuf_attributes attribs;
|
||||||
|
if (!export_drm_bo(bo, &attribs)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
tex = wlr_texture_from_dmabuf(renderer->wlr_rend, &attribs);
|
||||||
|
if (tex) {
|
||||||
|
gbm_bo_set_user_data(bo, tex, free_tex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct gbm_bo *copy_drm_surface_mgpu(struct wlr_drm_surface *dest,
|
||||||
|
struct gbm_bo *src) {
|
||||||
|
make_drm_surface_current(dest, NULL);
|
||||||
|
|
||||||
|
struct wlr_texture *tex = get_tex_for_bo(dest->renderer, src);
|
||||||
|
assert(tex);
|
||||||
|
|
||||||
|
float mat[9];
|
||||||
|
wlr_matrix_projection(mat, 1, 1, WL_OUTPUT_TRANSFORM_NORMAL);
|
||||||
|
|
||||||
|
struct wlr_renderer *renderer = dest->renderer->wlr_rend;
|
||||||
|
wlr_renderer_begin(renderer, dest->width, dest->height);
|
||||||
|
wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 });
|
||||||
|
wlr_render_texture_with_matrix(renderer, tex, mat, 1.0f);
|
||||||
|
wlr_renderer_end(renderer);
|
||||||
|
|
||||||
|
return swap_drm_surface_buffers(dest, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool init_drm_plane_surfaces(struct wlr_drm_plane *plane,
|
||||||
|
struct wlr_drm_backend *drm, int32_t width, uint32_t height,
|
||||||
|
uint32_t format) {
|
||||||
|
if (!drm->parent) {
|
||||||
|
return init_drm_surface(&plane->surf, &drm->renderer, width, height,
|
||||||
|
format, GBM_BO_USE_SCANOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!init_drm_surface(&plane->surf, &drm->parent->renderer,
|
||||||
|
width, height, format, GBM_BO_USE_LINEAR)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!init_drm_surface(&plane->mgpu_surf, &drm->renderer,
|
||||||
|
width, height, format, GBM_BO_USE_SCANOUT)) {
|
||||||
|
finish_drm_surface(&plane->surf);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void finish_drm_surface(struct wlr_drm_surface *surf) {
|
|
||||||
if (!surf || !surf->renderer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_swapchain_destroy(surf->swapchain);
|
|
||||||
|
|
||||||
memset(surf, 0, sizeof(*surf));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf,
|
|
||||||
struct wlr_buffer *buffer) {
|
|
||||||
struct wlr_renderer *renderer = surf->renderer->wlr_rend;
|
|
||||||
|
|
||||||
if (surf->width != (uint32_t)buffer->width ||
|
|
||||||
surf->height != (uint32_t)buffer->height) {
|
|
||||||
wlr_log(WLR_ERROR, "Surface size doesn't match buffer size");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_texture *tex = wlr_texture_from_buffer(renderer, buffer);
|
|
||||||
if (tex == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_buffer *dst = wlr_swapchain_acquire(surf->swapchain, NULL);
|
|
||||||
if (!dst) {
|
|
||||||
wlr_texture_destroy(tex);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
float mat[9];
|
|
||||||
wlr_matrix_identity(mat);
|
|
||||||
wlr_matrix_scale(mat, surf->width, surf->height);
|
|
||||||
|
|
||||||
if (!wlr_renderer_begin_with_buffer(renderer, dst)) {
|
|
||||||
wlr_buffer_unlock(dst);
|
|
||||||
wlr_texture_destroy(tex);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 });
|
|
||||||
wlr_render_texture_with_matrix(renderer, tex, mat, 1.0f);
|
|
||||||
|
|
||||||
wlr_renderer_end(renderer);
|
|
||||||
|
|
||||||
wlr_texture_destroy(tex);
|
|
||||||
|
|
||||||
return dst;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void drm_plane_finish_surface(struct wlr_drm_plane *plane) {
|
|
||||||
if (!plane) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
drm_fb_clear(&plane->pending_fb);
|
|
||||||
drm_fb_clear(&plane->queued_fb);
|
|
||||||
drm_fb_clear(&plane->current_fb);
|
|
||||||
|
|
||||||
finish_drm_surface(&plane->mgpu_surf);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_drm_format *drm_plane_pick_render_format(
|
|
||||||
struct wlr_drm_plane *plane, struct wlr_drm_renderer *renderer) {
|
|
||||||
const struct wlr_drm_format_set *render_formats =
|
|
||||||
wlr_renderer_get_render_formats(renderer->wlr_rend);
|
|
||||||
if (render_formats == NULL) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to get render formats");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct wlr_drm_format_set *plane_formats = &plane->formats;
|
|
||||||
|
|
||||||
uint32_t fmt = DRM_FORMAT_ARGB8888;
|
|
||||||
if (!wlr_drm_format_set_get(&plane->formats, fmt)) {
|
|
||||||
const struct wlr_pixel_format_info *format_info =
|
|
||||||
drm_get_pixel_format_info(fmt);
|
|
||||||
assert(format_info != NULL &&
|
|
||||||
format_info->opaque_substitute != DRM_FORMAT_INVALID);
|
|
||||||
fmt = format_info->opaque_substitute;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct wlr_drm_format *render_format =
|
|
||||||
wlr_drm_format_set_get(render_formats, fmt);
|
|
||||||
if (render_format == NULL) {
|
|
||||||
wlr_log(WLR_DEBUG, "Renderer doesn't support format 0x%"PRIX32, fmt);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct wlr_drm_format *plane_format =
|
|
||||||
wlr_drm_format_set_get(plane_formats, fmt);
|
|
||||||
if (plane_format == NULL) {
|
|
||||||
wlr_log(WLR_DEBUG, "Plane %"PRIu32" doesn't support format 0x%"PRIX32,
|
|
||||||
plane->id, fmt);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_drm_format *format =
|
|
||||||
wlr_drm_format_intersect(plane_format, render_format);
|
|
||||||
if (format == NULL) {
|
|
||||||
wlr_log(WLR_DEBUG, "Failed to intersect plane and render "
|
|
||||||
"modifiers for format 0x%"PRIX32, fmt);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
|
|
||||||
void drm_fb_clear(struct wlr_drm_fb **fb_ptr) {
|
|
||||||
if (*fb_ptr == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_drm_fb *fb = *fb_ptr;
|
|
||||||
wlr_buffer_unlock(fb->wlr_buf); // may destroy the buffer
|
|
||||||
|
|
||||||
*fb_ptr = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void drm_fb_handle_destroy(struct wlr_addon *addon) {
|
|
||||||
struct wlr_drm_fb *fb = wl_container_of(addon, fb, addon);
|
|
||||||
drm_fb_destroy(fb);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wlr_addon_interface fb_addon_impl = {
|
|
||||||
.name = "wlr_drm_fb",
|
|
||||||
.destroy = drm_fb_handle_destroy,
|
|
||||||
};
|
|
||||||
|
|
||||||
static uint32_t get_fb_for_bo(struct wlr_drm_backend *drm,
|
|
||||||
struct wlr_dmabuf_attributes *dmabuf, uint32_t handles[static 4]) {
|
|
||||||
uint64_t modifiers[4] = {0};
|
|
||||||
for (int i = 0; i < dmabuf->n_planes; i++) {
|
|
||||||
// KMS requires all BO planes to have the same modifier
|
|
||||||
modifiers[i] = dmabuf->modifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t id = 0;
|
|
||||||
if (drm->addfb2_modifiers && dmabuf->modifier != DRM_FORMAT_MOD_INVALID) {
|
|
||||||
if (drmModeAddFB2WithModifiers(drm->fd, dmabuf->width, dmabuf->height,
|
|
||||||
dmabuf->format, handles, dmabuf->stride, dmabuf->offset,
|
|
||||||
modifiers, &id, DRM_MODE_FB_MODIFIERS) != 0) {
|
|
||||||
wlr_log_errno(WLR_DEBUG, "drmModeAddFB2WithModifiers failed");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (dmabuf->modifier != DRM_FORMAT_MOD_INVALID &&
|
|
||||||
dmabuf->modifier != DRM_FORMAT_MOD_LINEAR) {
|
|
||||||
wlr_log(WLR_ERROR, "Cannot import DRM framebuffer with explicit "
|
|
||||||
"modifier 0x%"PRIX64, dmabuf->modifier);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret = drmModeAddFB2(drm->fd, dmabuf->width, dmabuf->height,
|
|
||||||
dmabuf->format, handles, dmabuf->stride, dmabuf->offset, &id, 0);
|
|
||||||
if (ret != 0 && dmabuf->format == DRM_FORMAT_ARGB8888 &&
|
|
||||||
dmabuf->n_planes == 1 && dmabuf->offset[0] == 0) {
|
|
||||||
// Some big-endian machines don't support drmModeAddFB2. Try a
|
|
||||||
// last-resort fallback for ARGB8888 buffers, like Xorg's
|
|
||||||
// modesetting driver does.
|
|
||||||
wlr_log(WLR_DEBUG, "drmModeAddFB2 failed (%s), falling back to "
|
|
||||||
"legacy drmModeAddFB", strerror(-ret));
|
|
||||||
|
|
||||||
uint32_t depth = 32;
|
|
||||||
uint32_t bpp = 32;
|
|
||||||
ret = drmModeAddFB(drm->fd, dmabuf->width, dmabuf->height, depth,
|
|
||||||
bpp, dmabuf->stride[0], handles[0], &id);
|
|
||||||
if (ret != 0) {
|
|
||||||
wlr_log_errno(WLR_DEBUG, "drmModeAddFB failed");
|
|
||||||
}
|
|
||||||
} else if (ret != 0) {
|
|
||||||
wlr_log_errno(WLR_DEBUG, "drmModeAddFB2 failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void close_all_bo_handles(struct wlr_drm_backend *drm,
|
|
||||||
uint32_t handles[static 4]) {
|
|
||||||
for (int i = 0; i < 4; ++i) {
|
|
||||||
if (handles[i] == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If multiple planes share the same BO handle, avoid double-closing it
|
|
||||||
bool already_closed = false;
|
|
||||||
for (int j = 0; j < i; ++j) {
|
|
||||||
if (handles[i] == handles[j]) {
|
|
||||||
already_closed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (already_closed) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (drmCloseBufferHandle(drm->fd, handles[i]) != 0) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "drmCloseBufferHandle failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void drm_poisoned_fb_handle_destroy(struct wlr_addon *addon) {
|
|
||||||
wlr_addon_finish(addon);
|
|
||||||
free(addon);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wlr_addon_interface poisoned_fb_addon_impl = {
|
|
||||||
.name = "wlr_drm_poisoned_fb",
|
|
||||||
.destroy = drm_poisoned_fb_handle_destroy,
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool is_buffer_poisoned(struct wlr_drm_backend *drm,
|
|
||||||
struct wlr_buffer *buf) {
|
|
||||||
return wlr_addon_find(&buf->addons, drm, &poisoned_fb_addon_impl) != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark the buffer as "poisoned", ie. it cannot be imported into KMS. This
|
|
||||||
* allows us to avoid repeatedly trying to import it when it's not
|
|
||||||
* scanout-capable.
|
|
||||||
*/
|
|
||||||
static void poison_buffer(struct wlr_drm_backend *drm,
|
|
||||||
struct wlr_buffer *buf) {
|
|
||||||
struct wlr_addon *addon = calloc(1, sizeof(*addon));
|
|
||||||
if (addon == NULL) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wlr_addon_init(addon, &buf->addons, drm, &poisoned_fb_addon_impl);
|
|
||||||
wlr_log(WLR_DEBUG, "Poisoning buffer");
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wlr_drm_fb *drm_fb_create(struct wlr_drm_backend *drm,
|
|
||||||
struct wlr_buffer *buf, const struct wlr_drm_format_set *formats) {
|
|
||||||
struct wlr_dmabuf_attributes attribs;
|
|
||||||
if (!wlr_buffer_get_dmabuf(buf, &attribs)) {
|
|
||||||
wlr_log(WLR_DEBUG, "Failed to get DMA-BUF from buffer");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_buffer_poisoned(drm, buf)) {
|
|
||||||
wlr_log(WLR_DEBUG, "Buffer is poisoned");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_drm_fb *fb = calloc(1, sizeof(*fb));
|
|
||||||
if (!fb) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (formats && !wlr_drm_format_set_has(formats, attribs.format,
|
|
||||||
attribs.modifier)) {
|
|
||||||
// The format isn't supported by the plane. Try stripping the alpha
|
|
||||||
// channel, if any.
|
|
||||||
const struct wlr_pixel_format_info *info =
|
|
||||||
drm_get_pixel_format_info(attribs.format);
|
|
||||||
if (info != NULL && info->opaque_substitute != DRM_FORMAT_INVALID &&
|
|
||||||
wlr_drm_format_set_has(formats, info->opaque_substitute, attribs.modifier)) {
|
|
||||||
attribs.format = info->opaque_substitute;
|
|
||||||
} else {
|
|
||||||
wlr_log(WLR_DEBUG, "Buffer format 0x%"PRIX32" with modifier "
|
|
||||||
"0x%"PRIX64" cannot be scanned out",
|
|
||||||
attribs.format, attribs.modifier);
|
|
||||||
goto error_fb;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t handles[4] = {0};
|
|
||||||
for (int i = 0; i < attribs.n_planes; ++i) {
|
|
||||||
int ret = drmPrimeFDToHandle(drm->fd, attribs.fd[i], &handles[i]);
|
|
||||||
if (ret != 0) {
|
|
||||||
wlr_log_errno(WLR_DEBUG, "drmPrimeFDToHandle failed");
|
|
||||||
goto error_bo_handle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fb->id = get_fb_for_bo(drm, &attribs, handles);
|
|
||||||
if (!fb->id) {
|
|
||||||
wlr_log(WLR_DEBUG, "Failed to import BO in KMS");
|
|
||||||
poison_buffer(drm, buf);
|
|
||||||
goto error_bo_handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
close_all_bo_handles(drm, handles);
|
|
||||||
|
|
||||||
fb->backend = drm;
|
|
||||||
fb->wlr_buf = buf;
|
|
||||||
|
|
||||||
wlr_addon_init(&fb->addon, &buf->addons, drm, &fb_addon_impl);
|
|
||||||
wl_list_insert(&drm->fbs, &fb->link);
|
|
||||||
|
|
||||||
return fb;
|
|
||||||
|
|
||||||
error_bo_handle:
|
|
||||||
close_all_bo_handles(drm, handles);
|
|
||||||
error_fb:
|
|
||||||
free(fb);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void drm_fb_destroy(struct wlr_drm_fb *fb) {
|
|
||||||
struct wlr_drm_backend *drm = fb->backend;
|
|
||||||
|
|
||||||
wl_list_remove(&fb->link);
|
|
||||||
wlr_addon_finish(&fb->addon);
|
|
||||||
|
|
||||||
if (drmModeRmFB(drm->fd, fb->id) != 0) {
|
|
||||||
wlr_log(WLR_ERROR, "drmModeRmFB failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
free(fb);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool drm_fb_import(struct wlr_drm_fb **fb_ptr, struct wlr_drm_backend *drm,
|
|
||||||
struct wlr_buffer *buf, const struct wlr_drm_format_set *formats) {
|
|
||||||
struct wlr_drm_fb *fb;
|
|
||||||
struct wlr_addon *addon = wlr_addon_find(&buf->addons, drm, &fb_addon_impl);
|
|
||||||
if (addon != NULL) {
|
|
||||||
fb = wl_container_of(addon, fb, addon);
|
|
||||||
} else {
|
|
||||||
fb = drm_fb_create(drm, buf, formats);
|
|
||||||
if (!fb) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_buffer_lock(buf);
|
|
||||||
drm_fb_move(fb_ptr, &fb);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old) {
|
|
||||||
drm_fb_clear(new);
|
|
||||||
*new = *old;
|
|
||||||
*old = NULL;
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include <drm_fourcc.h>
|
#include <drm_fourcc.h>
|
||||||
#include <drm_mode.h>
|
#include <drm_mode.h>
|
||||||
#include <drm.h>
|
#include <drm.h>
|
||||||
|
#include <gbm.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
@ -38,7 +39,6 @@ static const char *get_manufacturer(uint16_t id) {
|
||||||
case ID('A', 'S', 'K'): return "Ask A/S";
|
case ID('A', 'S', 'K'): return "Ask A/S";
|
||||||
case ID('A', 'V', 'T'): return "Avtek (Electronics) Pty Ltd";
|
case ID('A', 'V', 'T'): return "Avtek (Electronics) Pty Ltd";
|
||||||
case ID('B', 'N', 'O'): return "Bang & Olufsen";
|
case ID('B', 'N', 'O'): return "Bang & Olufsen";
|
||||||
case ID('B', 'N', 'Q'): return "BenQ Corporation";
|
|
||||||
case ID('C', 'M', 'N'): return "Chimei Innolux Corporation";
|
case ID('C', 'M', 'N'): return "Chimei Innolux Corporation";
|
||||||
case ID('C', 'M', 'O'): return "Chi Mei Optoelectronics corp.";
|
case ID('C', 'M', 'O'): return "Chi Mei Optoelectronics corp.";
|
||||||
case ID('C', 'R', 'O'): return "Extraordinary Technologies PTY Limited";
|
case ID('C', 'R', 'O'): return "Extraordinary Technologies PTY Limited";
|
||||||
|
@ -94,7 +94,6 @@ static const char *get_manufacturer(uint16_t id) {
|
||||||
case ID('V', 'E', 'S'): return "Vestel Elektronik Sanayi ve Ticaret A. S.";
|
case ID('V', 'E', 'S'): return "Vestel Elektronik Sanayi ve Ticaret A. S.";
|
||||||
case ID('V', 'I', 'T'): return "Visitech AS";
|
case ID('V', 'I', 'T'): return "Visitech AS";
|
||||||
case ID('V', 'I', 'Z'): return "VIZIO, Inc";
|
case ID('V', 'I', 'Z'): return "VIZIO, Inc";
|
||||||
case ID('V', 'L', 'V'): return "Valve";
|
|
||||||
case ID('V', 'S', 'C'): return "ViewSonic Corporation";
|
case ID('V', 'S', 'C'): return "ViewSonic Corporation";
|
||||||
case ID('Y', 'M', 'H'): return "Yamaha Corporation";
|
case ID('Y', 'M', 'H'): return "Yamaha Corporation";
|
||||||
default: return "Unknown";
|
default: return "Unknown";
|
||||||
|
@ -163,19 +162,52 @@ const char *conn_get_name(uint32_t type_id) {
|
||||||
case DRM_MODE_CONNECTOR_eDP: return "eDP";
|
case DRM_MODE_CONNECTOR_eDP: return "eDP";
|
||||||
case DRM_MODE_CONNECTOR_VIRTUAL: return "Virtual";
|
case DRM_MODE_CONNECTOR_VIRTUAL: return "Virtual";
|
||||||
case DRM_MODE_CONNECTOR_DSI: return "DSI";
|
case DRM_MODE_CONNECTOR_DSI: return "DSI";
|
||||||
|
#ifdef DRM_MODE_CONNECTOR_DPI
|
||||||
case DRM_MODE_CONNECTOR_DPI: return "DPI";
|
case DRM_MODE_CONNECTOR_DPI: return "DPI";
|
||||||
case DRM_MODE_CONNECTOR_WRITEBACK: return "Writeback";
|
|
||||||
#ifdef DRM_MODE_CONNECTOR_SPI
|
|
||||||
case DRM_MODE_CONNECTOR_SPI: return "SPI";
|
|
||||||
#endif
|
|
||||||
#ifdef DRM_MODE_CONNECTOR_USB
|
|
||||||
case DRM_MODE_CONNECTOR_USB: return "USB";
|
|
||||||
#endif
|
#endif
|
||||||
default: return "Unknown";
|
default: return "Unknown";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_taken(size_t n, const uint32_t arr[static n], uint32_t key) {
|
static void free_fb(struct gbm_bo *bo, void *data) {
|
||||||
|
uint32_t id = (uintptr_t)data;
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
struct gbm_device *gbm = gbm_bo_get_device(bo);
|
||||||
|
drmModeRmFB(gbm_device_get_fd(gbm), id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t get_fb_for_bo(struct gbm_bo *bo, uint32_t drm_format) {
|
||||||
|
uint32_t id = (uintptr_t)gbm_bo_get_user_data(bo);
|
||||||
|
if (id) {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(gbm_bo_get_format(bo) == GBM_FORMAT_ARGB8888);
|
||||||
|
assert(drm_format == DRM_FORMAT_ARGB8888 ||
|
||||||
|
drm_format == DRM_FORMAT_XRGB8888);
|
||||||
|
|
||||||
|
struct gbm_device *gbm = gbm_bo_get_device(bo);
|
||||||
|
|
||||||
|
int fd = gbm_device_get_fd(gbm);
|
||||||
|
uint32_t width = gbm_bo_get_width(bo);
|
||||||
|
uint32_t height = gbm_bo_get_height(bo);
|
||||||
|
uint32_t handles[4] = {gbm_bo_get_handle(bo).u32};
|
||||||
|
uint32_t pitches[4] = {gbm_bo_get_stride(bo)};
|
||||||
|
uint32_t offsets[4] = {gbm_bo_get_offset(bo, 0)};
|
||||||
|
|
||||||
|
if (drmModeAddFB2(fd, width, height, drm_format,
|
||||||
|
handles, pitches, offsets, &id, 0)) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Unable to add DRM framebuffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
gbm_bo_set_user_data(bo, (void *)(uintptr_t)id, free_fb);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool is_taken(size_t n, const uint32_t arr[static n], uint32_t key) {
|
||||||
for (size_t i = 0; i < n; ++i) {
|
for (size_t i = 0; i < n; ++i) {
|
||||||
if (arr[i] == key) {
|
if (arr[i] == key) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -2,8 +2,11 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <wlr/interfaces/wlr_input_device.h>
|
#include <wlr/interfaces/wlr_input_device.h>
|
||||||
#include <wlr/interfaces/wlr_output.h>
|
#include <wlr/interfaces/wlr_output.h>
|
||||||
|
#include <wlr/render/egl.h>
|
||||||
|
#include <wlr/render/gles2.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include "backend/headless.h"
|
#include "backend/headless.h"
|
||||||
|
#include "glapi.h"
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
|
|
||||||
struct wlr_headless_backend *headless_backend_from_backend(
|
struct wlr_headless_backend *headless_backend_from_backend(
|
||||||
|
@ -26,7 +29,8 @@ static bool backend_start(struct wlr_backend *wlr_backend) {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_headless_input_device *input_device;
|
struct wlr_headless_input_device *input_device;
|
||||||
wl_list_for_each(input_device, &backend->input_devices, link) {
|
wl_list_for_each(input_device, &backend->input_devices,
|
||||||
|
wlr_input_device.link) {
|
||||||
wlr_signal_emit_safe(&backend->backend.events.new_input,
|
wlr_signal_emit_safe(&backend->backend.events.new_input,
|
||||||
&input_device->wlr_input_device);
|
&input_device->wlr_input_device);
|
||||||
}
|
}
|
||||||
|
@ -51,25 +55,28 @@ static void backend_destroy(struct wlr_backend *wlr_backend) {
|
||||||
|
|
||||||
struct wlr_headless_input_device *input_device, *input_device_tmp;
|
struct wlr_headless_input_device *input_device, *input_device_tmp;
|
||||||
wl_list_for_each_safe(input_device, input_device_tmp,
|
wl_list_for_each_safe(input_device, input_device_tmp,
|
||||||
&backend->input_devices, link) {
|
&backend->input_devices, wlr_input_device.link) {
|
||||||
wlr_input_device_destroy(&input_device->wlr_input_device);
|
wlr_input_device_destroy(&input_device->wlr_input_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_backend_finish(wlr_backend);
|
wlr_signal_emit_safe(&wlr_backend->events.destroy, backend);
|
||||||
|
|
||||||
|
wlr_renderer_destroy(backend->renderer);
|
||||||
|
wlr_egl_finish(&backend->egl);
|
||||||
free(backend);
|
free(backend);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t get_buffer_caps(struct wlr_backend *wlr_backend) {
|
static struct wlr_renderer *backend_get_renderer(
|
||||||
return WLR_BUFFER_CAP_DATA_PTR
|
struct wlr_backend *wlr_backend) {
|
||||||
| WLR_BUFFER_CAP_DMABUF
|
struct wlr_headless_backend *backend =
|
||||||
| WLR_BUFFER_CAP_SHM;
|
headless_backend_from_backend(wlr_backend);
|
||||||
|
return backend->renderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct wlr_backend_impl backend_impl = {
|
static const struct wlr_backend_impl backend_impl = {
|
||||||
.start = backend_start,
|
.start = backend_start,
|
||||||
.destroy = backend_destroy,
|
.destroy = backend_destroy,
|
||||||
.get_buffer_caps = get_buffer_caps,
|
.get_renderer = backend_get_renderer,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
|
@ -78,7 +85,8 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
backend_destroy(&backend->backend);
|
backend_destroy(&backend->backend);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_backend *wlr_headless_backend_create(struct wl_display *display) {
|
struct wlr_backend *wlr_headless_backend_create(struct wl_display *display,
|
||||||
|
wlr_renderer_create_func_t create_renderer_func) {
|
||||||
wlr_log(WLR_INFO, "Creating headless backend");
|
wlr_log(WLR_INFO, "Creating headless backend");
|
||||||
|
|
||||||
struct wlr_headless_backend *backend =
|
struct wlr_headless_backend *backend =
|
||||||
|
@ -87,13 +95,32 @@ struct wlr_backend *wlr_headless_backend_create(struct wl_display *display) {
|
||||||
wlr_log(WLR_ERROR, "Failed to allocate wlr_headless_backend");
|
wlr_log(WLR_ERROR, "Failed to allocate wlr_headless_backend");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_backend_init(&backend->backend, &backend_impl);
|
wlr_backend_init(&backend->backend, &backend_impl);
|
||||||
|
|
||||||
backend->display = display;
|
backend->display = display;
|
||||||
wl_list_init(&backend->outputs);
|
wl_list_init(&backend->outputs);
|
||||||
wl_list_init(&backend->input_devices);
|
wl_list_init(&backend->input_devices);
|
||||||
|
|
||||||
|
static const EGLint config_attribs[] = {
|
||||||
|
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
|
||||||
|
EGL_ALPHA_SIZE, 0,
|
||||||
|
EGL_BLUE_SIZE, 1,
|
||||||
|
EGL_GREEN_SIZE, 1,
|
||||||
|
EGL_RED_SIZE, 1,
|
||||||
|
EGL_NONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!create_renderer_func) {
|
||||||
|
create_renderer_func = wlr_renderer_autocreate;
|
||||||
|
}
|
||||||
|
|
||||||
|
backend->renderer = create_renderer_func(&backend->egl,
|
||||||
|
EGL_PLATFORM_SURFACELESS_MESA, NULL, (EGLint*)config_attribs, 0);
|
||||||
|
if (!backend->renderer) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to create renderer");
|
||||||
|
free(backend);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
backend->display_destroy.notify = handle_display_destroy;
|
backend->display_destroy.notify = handle_display_destroy;
|
||||||
wl_display_add_destroy_listener(display, &backend->display_destroy);
|
wl_display_add_destroy_listener(display, &backend->display_destroy);
|
||||||
|
|
||||||
|
|
|
@ -11,16 +11,7 @@
|
||||||
#include "backend/headless.h"
|
#include "backend/headless.h"
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
|
|
||||||
static void input_device_destroy(struct wlr_input_device *wlr_dev) {
|
static const struct wlr_input_device_impl input_device_impl = { 0 };
|
||||||
struct wlr_headless_input_device *dev =
|
|
||||||
wl_container_of(wlr_dev, dev, wlr_input_device);
|
|
||||||
wl_list_remove(&dev->link);
|
|
||||||
free(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wlr_input_device_impl input_device_impl = {
|
|
||||||
.destroy = input_device_destroy,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool wlr_input_device_is_headless(struct wlr_input_device *wlr_dev) {
|
bool wlr_input_device_is_headless(struct wlr_input_device *wlr_dev) {
|
||||||
return wlr_dev->impl == &input_device_impl;
|
return wlr_dev->impl == &input_device_impl;
|
||||||
|
@ -87,15 +78,15 @@ struct wlr_input_device *wlr_headless_add_input_device(
|
||||||
wlr_tablet_pad_init(wlr_device->tablet_pad, NULL);
|
wlr_tablet_pad_init(wlr_device->tablet_pad, NULL);
|
||||||
break;
|
break;
|
||||||
case WLR_INPUT_DEVICE_SWITCH:
|
case WLR_INPUT_DEVICE_SWITCH:
|
||||||
wlr_device->switch_device = calloc(1, sizeof(struct wlr_switch));
|
wlr_device->lid_switch = calloc(1, sizeof(struct wlr_switch));
|
||||||
if (wlr_device->switch_device == NULL) {
|
if (wlr_device->lid_switch == NULL) {
|
||||||
wlr_log(WLR_ERROR, "Unable to allocate wlr_switch");
|
wlr_log(WLR_ERROR, "Unable to allocate wlr_switch");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
wlr_switch_init(wlr_device->switch_device, NULL);
|
wlr_switch_init(wlr_device->lid_switch, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
wl_list_insert(&backend->input_devices, &device->link);
|
wl_list_insert(&backend->input_devices, &wlr_device->link);
|
||||||
|
|
||||||
if (backend->started) {
|
if (backend->started) {
|
||||||
wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_device);
|
wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_device);
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
wlr_files += files(
|
|
||||||
'backend.c',
|
|
||||||
'input_device.c',
|
|
||||||
'output.c',
|
|
||||||
)
|
|
|
@ -1,89 +1,95 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <EGL/egl.h>
|
||||||
|
#include <EGL/eglext.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <wlr/interfaces/wlr_output.h>
|
#include <wlr/interfaces/wlr_output.h>
|
||||||
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include "backend/headless.h"
|
#include "backend/headless.h"
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
|
|
||||||
static const uint32_t SUPPORTED_OUTPUT_STATE =
|
|
||||||
WLR_OUTPUT_STATE_BACKEND_OPTIONAL |
|
|
||||||
WLR_OUTPUT_STATE_BUFFER |
|
|
||||||
WLR_OUTPUT_STATE_MODE;
|
|
||||||
|
|
||||||
static struct wlr_headless_output *headless_output_from_output(
|
static struct wlr_headless_output *headless_output_from_output(
|
||||||
struct wlr_output *wlr_output) {
|
struct wlr_output *wlr_output) {
|
||||||
assert(wlr_output_is_headless(wlr_output));
|
assert(wlr_output_is_headless(wlr_output));
|
||||||
return (struct wlr_headless_output *)wlr_output;
|
return (struct wlr_headless_output *)wlr_output;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool output_set_custom_mode(struct wlr_headless_output *output,
|
static EGLSurface egl_create_surface(struct wlr_egl *egl, unsigned int width,
|
||||||
int32_t width, int32_t height, int32_t refresh) {
|
unsigned int height) {
|
||||||
|
EGLint attribs[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE};
|
||||||
|
|
||||||
|
EGLSurface surf = eglCreatePbufferSurface(egl->display, egl->config, attribs);
|
||||||
|
if (surf == EGL_NO_SURFACE) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to create EGL surface");
|
||||||
|
return EGL_NO_SURFACE;
|
||||||
|
}
|
||||||
|
return surf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width,
|
||||||
|
int32_t height, int32_t refresh) {
|
||||||
|
struct wlr_headless_output *output =
|
||||||
|
headless_output_from_output(wlr_output);
|
||||||
|
struct wlr_headless_backend *backend = output->backend;
|
||||||
|
|
||||||
if (refresh <= 0) {
|
if (refresh <= 0) {
|
||||||
refresh = HEADLESS_DEFAULT_REFRESH;
|
refresh = HEADLESS_DEFAULT_REFRESH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wlr_egl_destroy_surface(&backend->egl, output->egl_surface);
|
||||||
|
|
||||||
|
output->egl_surface = egl_create_surface(&backend->egl, width, height);
|
||||||
|
if (output->egl_surface == EGL_NO_SURFACE) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to recreate EGL surface");
|
||||||
|
wlr_output_destroy(wlr_output);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
output->frame_delay = 1000000 / refresh;
|
output->frame_delay = 1000000 / refresh;
|
||||||
|
|
||||||
wlr_output_update_custom_mode(&output->wlr_output, width, height, refresh);
|
wlr_output_update_custom_mode(&output->wlr_output, width, height, refresh);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool output_test(struct wlr_output *wlr_output) {
|
static void output_transform(struct wlr_output *wlr_output,
|
||||||
uint32_t unsupported =
|
enum wl_output_transform transform) {
|
||||||
wlr_output->pending.committed & ~SUPPORTED_OUTPUT_STATE;
|
|
||||||
if (unsupported != 0) {
|
|
||||||
wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32,
|
|
||||||
unsupported);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
|
|
||||||
assert(wlr_output->pending.mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool output_commit(struct wlr_output *wlr_output) {
|
|
||||||
struct wlr_headless_output *output =
|
struct wlr_headless_output *output =
|
||||||
headless_output_from_output(wlr_output);
|
headless_output_from_output(wlr_output);
|
||||||
|
output->wlr_output.transform = transform;
|
||||||
|
}
|
||||||
|
|
||||||
if (!output_test(wlr_output)) {
|
static bool output_make_current(struct wlr_output *wlr_output, int *buffer_age) {
|
||||||
return false;
|
struct wlr_headless_output *output =
|
||||||
}
|
headless_output_from_output(wlr_output);
|
||||||
|
return wlr_egl_make_current(&output->backend->egl, output->egl_surface,
|
||||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
|
buffer_age);
|
||||||
if (!output_set_custom_mode(output,
|
}
|
||||||
wlr_output->pending.custom_mode.width,
|
|
||||||
wlr_output->pending.custom_mode.height,
|
|
||||||
wlr_output->pending.custom_mode.refresh)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) {
|
|
||||||
struct wlr_output_event_present present_event = {
|
|
||||||
.commit_seq = wlr_output->commit_seq + 1,
|
|
||||||
.presented = true,
|
|
||||||
};
|
|
||||||
wlr_output_send_present(wlr_output, &present_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
static bool output_swap_buffers(struct wlr_output *wlr_output,
|
||||||
|
pixman_region32_t *damage) {
|
||||||
|
// Nothing needs to be done for pbuffers
|
||||||
|
wlr_output_send_present(wlr_output, NULL);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void output_destroy(struct wlr_output *wlr_output) {
|
static void output_destroy(struct wlr_output *wlr_output) {
|
||||||
struct wlr_headless_output *output =
|
struct wlr_headless_output *output =
|
||||||
headless_output_from_output(wlr_output);
|
headless_output_from_output(wlr_output);
|
||||||
|
|
||||||
wl_list_remove(&output->link);
|
wl_list_remove(&output->link);
|
||||||
|
|
||||||
wl_event_source_remove(output->frame_timer);
|
wl_event_source_remove(output->frame_timer);
|
||||||
|
|
||||||
|
wlr_egl_destroy_surface(&output->backend->egl, output->egl_surface);
|
||||||
free(output);
|
free(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct wlr_output_impl output_impl = {
|
static const struct wlr_output_impl output_impl = {
|
||||||
|
.set_custom_mode = output_set_custom_mode,
|
||||||
|
.transform = output_transform,
|
||||||
.destroy = output_destroy,
|
.destroy = output_destroy,
|
||||||
.commit = output_commit,
|
.make_current = output_make_current,
|
||||||
|
.swap_buffers = output_swap_buffers,
|
||||||
};
|
};
|
||||||
|
|
||||||
bool wlr_output_is_headless(struct wlr_output *wlr_output) {
|
bool wlr_output_is_headless(struct wlr_output *wlr_output) {
|
||||||
|
@ -113,18 +119,26 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend,
|
||||||
backend->display);
|
backend->display);
|
||||||
struct wlr_output *wlr_output = &output->wlr_output;
|
struct wlr_output *wlr_output = &output->wlr_output;
|
||||||
|
|
||||||
output_set_custom_mode(output, width, height, 0);
|
output->egl_surface = egl_create_surface(&backend->egl, width, height);
|
||||||
|
if (output->egl_surface == EGL_NO_SURFACE) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to create EGL surface");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
output_set_custom_mode(wlr_output, width, height, 0);
|
||||||
strncpy(wlr_output->make, "headless", sizeof(wlr_output->make));
|
strncpy(wlr_output->make, "headless", sizeof(wlr_output->make));
|
||||||
strncpy(wlr_output->model, "headless", sizeof(wlr_output->model));
|
strncpy(wlr_output->model, "headless", sizeof(wlr_output->model));
|
||||||
|
snprintf(wlr_output->name, sizeof(wlr_output->name), "HEADLESS-%d",
|
||||||
|
wl_list_length(&backend->outputs) + 1);
|
||||||
|
|
||||||
char name[64];
|
if (!wlr_egl_make_current(&output->backend->egl, output->egl_surface,
|
||||||
snprintf(name, sizeof(name), "HEADLESS-%zu", ++backend->last_output_num);
|
NULL)) {
|
||||||
wlr_output_set_name(wlr_output, name);
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
char description[128];
|
wlr_renderer_begin(backend->renderer, wlr_output->width, wlr_output->height);
|
||||||
snprintf(description, sizeof(description),
|
wlr_renderer_clear(backend->renderer, (float[]){ 1.0, 1.0, 1.0, 1.0 });
|
||||||
"Headless output %zu", backend->last_output_num);
|
wlr_renderer_end(backend->renderer);
|
||||||
wlr_output_set_description(wlr_output, description);
|
|
||||||
|
|
||||||
struct wl_event_loop *ev = wl_display_get_event_loop(backend->display);
|
struct wl_event_loop *ev = wl_display_get_event_loop(backend->display);
|
||||||
output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output);
|
output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output);
|
||||||
|
@ -138,4 +152,8 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend,
|
||||||
}
|
}
|
||||||
|
|
||||||
return wlr_output;
|
return wlr_output;
|
||||||
|
|
||||||
|
error:
|
||||||
|
wlr_output_destroy(&output->wlr_output);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <libinput.h>
|
#include <libinput.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <wlr/backend/interface.h>
|
#include <wlr/backend/interface.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
@ -17,27 +16,12 @@ static struct wlr_libinput_backend *get_libinput_backend_from_backend(
|
||||||
static int libinput_open_restricted(const char *path,
|
static int libinput_open_restricted(const char *path,
|
||||||
int flags, void *_backend) {
|
int flags, void *_backend) {
|
||||||
struct wlr_libinput_backend *backend = _backend;
|
struct wlr_libinput_backend *backend = _backend;
|
||||||
struct wlr_device *dev = wlr_session_open_file(backend->session, path);
|
return wlr_session_open_file(backend->session, path);
|
||||||
if (dev == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return dev->fd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void libinput_close_restricted(int fd, void *_backend) {
|
static void libinput_close_restricted(int fd, void *_backend) {
|
||||||
struct wlr_libinput_backend *backend = _backend;
|
struct wlr_libinput_backend *backend = _backend;
|
||||||
|
wlr_session_close_file(backend->session, fd);
|
||||||
struct wlr_device *dev;
|
|
||||||
bool found = false;
|
|
||||||
wl_list_for_each(dev, &backend->session->devices, link) {
|
|
||||||
if (dev->fd == fd) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (found) {
|
|
||||||
wlr_session_close_file(backend->session, dev);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct libinput_interface libinput_impl = {
|
static const struct libinput_interface libinput_impl = {
|
||||||
|
@ -47,10 +31,9 @@ static const struct libinput_interface libinput_impl = {
|
||||||
|
|
||||||
static int handle_libinput_readable(int fd, uint32_t mask, void *_backend) {
|
static int handle_libinput_readable(int fd, uint32_t mask, void *_backend) {
|
||||||
struct wlr_libinput_backend *backend = _backend;
|
struct wlr_libinput_backend *backend = _backend;
|
||||||
int ret = libinput_dispatch(backend->libinput_context);
|
if (libinput_dispatch(backend->libinput_context) != 0) {
|
||||||
if (ret != 0) {
|
wlr_log(WLR_ERROR, "Failed to dispatch libinput");
|
||||||
wlr_log(WLR_ERROR, "Failed to dispatch libinput: %s", strerror(-ret));
|
// TODO: some kind of abort?
|
||||||
wl_display_terminate(backend->display);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
struct libinput_event *event;
|
struct libinput_event *event;
|
||||||
|
@ -61,30 +44,15 @@ static int handle_libinput_readable(int fd, uint32_t mask, void *_backend) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum wlr_log_importance libinput_log_priority_to_wlr(
|
|
||||||
enum libinput_log_priority priority) {
|
|
||||||
switch (priority) {
|
|
||||||
case LIBINPUT_LOG_PRIORITY_ERROR:
|
|
||||||
return WLR_ERROR;
|
|
||||||
case LIBINPUT_LOG_PRIORITY_INFO:
|
|
||||||
return WLR_INFO;
|
|
||||||
default:
|
|
||||||
return WLR_DEBUG;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void log_libinput(struct libinput *libinput_context,
|
static void log_libinput(struct libinput *libinput_context,
|
||||||
enum libinput_log_priority priority, const char *fmt, va_list args) {
|
enum libinput_log_priority priority, const char *fmt, va_list args) {
|
||||||
enum wlr_log_importance importance = libinput_log_priority_to_wlr(priority);
|
_wlr_vlog(WLR_ERROR, fmt, args);
|
||||||
static char wlr_fmt[1024];
|
|
||||||
snprintf(wlr_fmt, sizeof(wlr_fmt), "[libinput] %s", fmt);
|
|
||||||
_wlr_vlog(importance, wlr_fmt, args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool backend_start(struct wlr_backend *wlr_backend) {
|
static bool backend_start(struct wlr_backend *wlr_backend) {
|
||||||
struct wlr_libinput_backend *backend =
|
struct wlr_libinput_backend *backend =
|
||||||
get_libinput_backend_from_backend(wlr_backend);
|
get_libinput_backend_from_backend(wlr_backend);
|
||||||
wlr_log(WLR_DEBUG, "Starting libinput backend");
|
wlr_log(WLR_DEBUG, "Initializing libinput");
|
||||||
|
|
||||||
backend->libinput_context = libinput_udev_create_context(&libinput_impl,
|
backend->libinput_context = libinput_udev_create_context(&libinput_impl,
|
||||||
backend, backend->session->udev);
|
backend, backend->session->udev);
|
||||||
|
@ -110,9 +78,9 @@ static bool backend_start(struct wlr_backend *wlr_backend) {
|
||||||
no_devs = NULL;
|
no_devs = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!no_devs && backend->wlr_device_lists.size == 0) {
|
if (!no_devs && backend->wlr_device_lists.length == 0) {
|
||||||
handle_libinput_readable(libinput_fd, WL_EVENT_READABLE, backend);
|
handle_libinput_readable(libinput_fd, WL_EVENT_READABLE, backend);
|
||||||
if (backend->wlr_device_lists.size == 0) {
|
if (backend->wlr_device_lists.length == 0) {
|
||||||
wlr_log(WLR_ERROR, "libinput initialization failed, no input devices");
|
wlr_log(WLR_ERROR, "libinput initialization failed, no input devices");
|
||||||
wlr_log(WLR_ERROR, "Set WLR_LIBINPUT_NO_DEVICES=1 to suppress this check");
|
wlr_log(WLR_ERROR, "Set WLR_LIBINPUT_NO_DEVICES=1 to suppress this check");
|
||||||
return false;
|
return false;
|
||||||
|
@ -141,22 +109,21 @@ static void backend_destroy(struct wlr_backend *wlr_backend) {
|
||||||
struct wlr_libinput_backend *backend =
|
struct wlr_libinput_backend *backend =
|
||||||
get_libinput_backend_from_backend(wlr_backend);
|
get_libinput_backend_from_backend(wlr_backend);
|
||||||
|
|
||||||
struct wl_list **wlr_devices_ptr;
|
for (size_t i = 0; i < backend->wlr_device_lists.length; i++) {
|
||||||
wl_array_for_each(wlr_devices_ptr, &backend->wlr_device_lists) {
|
struct wl_list *wlr_devices = backend->wlr_device_lists.items[i];
|
||||||
struct wlr_libinput_input_device *dev, *tmp;
|
struct wlr_input_device *wlr_dev, *next;
|
||||||
wl_list_for_each_safe(dev, tmp, *wlr_devices_ptr, link) {
|
wl_list_for_each_safe(wlr_dev, next, wlr_devices, link) {
|
||||||
wlr_input_device_destroy(&dev->wlr_input_device);
|
wlr_input_device_destroy(wlr_dev);
|
||||||
}
|
}
|
||||||
free(*wlr_devices_ptr);
|
free(wlr_devices);
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_backend_finish(wlr_backend);
|
wlr_signal_emit_safe(&wlr_backend->events.destroy, wlr_backend);
|
||||||
|
|
||||||
wl_list_remove(&backend->display_destroy.link);
|
wl_list_remove(&backend->display_destroy.link);
|
||||||
wl_list_remove(&backend->session_destroy.link);
|
|
||||||
wl_list_remove(&backend->session_signal.link);
|
wl_list_remove(&backend->session_signal.link);
|
||||||
|
|
||||||
wl_array_release(&backend->wlr_device_lists);
|
wlr_list_finish(&backend->wlr_device_lists);
|
||||||
if (backend->input_event) {
|
if (backend->input_event) {
|
||||||
wl_event_source_remove(backend->input_event);
|
wl_event_source_remove(backend->input_event);
|
||||||
}
|
}
|
||||||
|
@ -176,7 +143,7 @@ bool wlr_backend_is_libinput(struct wlr_backend *b) {
|
||||||
static void session_signal(struct wl_listener *listener, void *data) {
|
static void session_signal(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_libinput_backend *backend =
|
struct wlr_libinput_backend *backend =
|
||||||
wl_container_of(listener, backend, session_signal);
|
wl_container_of(listener, backend, session_signal);
|
||||||
struct wlr_session *session = backend->session;
|
struct wlr_session *session = data;
|
||||||
|
|
||||||
if (!backend->libinput_context) {
|
if (!backend->libinput_context) {
|
||||||
return;
|
return;
|
||||||
|
@ -189,12 +156,6 @@ static void session_signal(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_session_destroy(struct wl_listener *listener, void *data) {
|
|
||||||
struct wlr_libinput_backend *backend =
|
|
||||||
wl_container_of(listener, backend, session_destroy);
|
|
||||||
backend_destroy(&backend->backend);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_libinput_backend *backend =
|
struct wlr_libinput_backend *backend =
|
||||||
wl_container_of(listener, backend, display_destroy);
|
wl_container_of(listener, backend, display_destroy);
|
||||||
|
@ -211,21 +172,24 @@ struct wlr_backend *wlr_libinput_backend_create(struct wl_display *display,
|
||||||
}
|
}
|
||||||
wlr_backend_init(&backend->backend, &backend_impl);
|
wlr_backend_init(&backend->backend, &backend_impl);
|
||||||
|
|
||||||
wl_array_init(&backend->wlr_device_lists);
|
if (!wlr_list_init(&backend->wlr_device_lists)) {
|
||||||
|
wlr_log(WLR_ERROR, "Allocation failed: %s", strerror(errno));
|
||||||
|
goto error_backend;
|
||||||
|
}
|
||||||
|
|
||||||
backend->session = session;
|
backend->session = session;
|
||||||
backend->display = display;
|
backend->display = display;
|
||||||
|
|
||||||
backend->session_signal.notify = session_signal;
|
backend->session_signal.notify = session_signal;
|
||||||
wl_signal_add(&session->events.active, &backend->session_signal);
|
wl_signal_add(&session->session_signal, &backend->session_signal);
|
||||||
|
|
||||||
backend->session_destroy.notify = handle_session_destroy;
|
|
||||||
wl_signal_add(&session->events.destroy, &backend->session_destroy);
|
|
||||||
|
|
||||||
backend->display_destroy.notify = handle_display_destroy;
|
backend->display_destroy.notify = handle_display_destroy;
|
||||||
wl_display_add_destroy_listener(display, &backend->display_destroy);
|
wl_display_add_destroy_listener(display, &backend->display_destroy);
|
||||||
|
|
||||||
return &backend->backend;
|
return &backend->backend;
|
||||||
|
error_backend:
|
||||||
|
free(backend);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct libinput_device *wlr_libinput_get_device_handle(
|
struct libinput_device *wlr_libinput_get_device_handle(
|
||||||
|
|
|
@ -7,10 +7,9 @@
|
||||||
#include <wlr/interfaces/wlr_input_device.h>
|
#include <wlr/interfaces/wlr_input_device.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include "backend/libinput.h"
|
#include "backend/libinput.h"
|
||||||
#include "util/array.h"
|
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
|
|
||||||
static struct wlr_libinput_input_device *get_libinput_device_from_device(
|
struct wlr_libinput_input_device *get_libinput_device_from_device(
|
||||||
struct wlr_input_device *wlr_dev) {
|
struct wlr_input_device *wlr_dev) {
|
||||||
assert(wlr_input_device_is_libinput(wlr_dev));
|
assert(wlr_input_device_is_libinput(wlr_dev));
|
||||||
return (struct wlr_libinput_input_device *)wlr_dev;
|
return (struct wlr_libinput_input_device *)wlr_dev;
|
||||||
|
@ -23,10 +22,10 @@ struct wlr_input_device *get_appropriate_device(
|
||||||
if (!wlr_devices) {
|
if (!wlr_devices) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
struct wlr_libinput_input_device *dev;
|
struct wlr_input_device *dev;
|
||||||
wl_list_for_each(dev, wlr_devices, link) {
|
wl_list_for_each(dev, wlr_devices, link) {
|
||||||
if (dev->wlr_input_device.type == desired_type) {
|
if (dev->type == desired_type) {
|
||||||
return &dev->wlr_input_device;
|
return dev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -36,7 +35,7 @@ static void input_device_destroy(struct wlr_input_device *wlr_dev) {
|
||||||
struct wlr_libinput_input_device *dev =
|
struct wlr_libinput_input_device *dev =
|
||||||
get_libinput_device_from_device(wlr_dev);
|
get_libinput_device_from_device(wlr_dev);
|
||||||
libinput_device_unref(dev->handle);
|
libinput_device_unref(dev->handle);
|
||||||
wl_list_remove(&dev->link);
|
wl_list_remove(&dev->wlr_input_device.link);
|
||||||
free(dev);
|
free(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +62,7 @@ static struct wlr_input_device *allocate_device(
|
||||||
if (output_name != NULL) {
|
if (output_name != NULL) {
|
||||||
wlr_dev->output_name = strdup(output_name);
|
wlr_dev->output_name = strdup(output_name);
|
||||||
}
|
}
|
||||||
wl_list_insert(wlr_devices, &dev->link);
|
wl_list_insert(wlr_devices, &wlr_dev->link);
|
||||||
dev->handle = libinput_dev;
|
dev->handle = libinput_dev;
|
||||||
libinput_device_ref(libinput_dev);
|
libinput_device_ref(libinput_dev);
|
||||||
wlr_input_device_init(wlr_dev, type, &input_device_impl,
|
wlr_input_device_init(wlr_dev, type, &input_device_impl,
|
||||||
|
@ -175,8 +174,8 @@ static void handle_device_added(struct wlr_libinput_backend *backend,
|
||||||
if (!wlr_dev) {
|
if (!wlr_dev) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
wlr_dev->switch_device = create_libinput_switch(libinput_dev);
|
wlr_dev->lid_switch = create_libinput_switch(libinput_dev);
|
||||||
if (!wlr_dev->switch_device) {
|
if (!wlr_dev->lid_switch) {
|
||||||
free(wlr_dev);
|
free(wlr_dev);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -184,13 +183,8 @@ static void handle_device_added(struct wlr_libinput_backend *backend,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wl_list_empty(wlr_devices)) {
|
if (!wl_list_empty(wlr_devices)) {
|
||||||
struct wl_list **dst = wl_array_add(&backend->wlr_device_lists, sizeof(wlr_devices));
|
|
||||||
if (!dst) {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
*dst = wlr_devices;
|
|
||||||
|
|
||||||
libinput_device_set_user_data(libinput_dev, wlr_devices);
|
libinput_device_set_user_data(libinput_dev, wlr_devices);
|
||||||
|
wlr_list_push(&backend->wlr_device_lists, wlr_devices);
|
||||||
} else {
|
} else {
|
||||||
free(wlr_devices);
|
free(wlr_devices);
|
||||||
}
|
}
|
||||||
|
@ -198,7 +192,7 @@ static void handle_device_added(struct wlr_libinput_backend *backend,
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
wlr_log(WLR_ERROR, "Could not allocate new device");
|
wlr_log(WLR_ERROR, "Could not allocate new device");
|
||||||
struct wlr_libinput_input_device *dev, *tmp_dev;
|
struct wlr_input_device *dev, *tmp_dev;
|
||||||
wl_list_for_each_safe(dev, tmp_dev, wlr_devices, link) {
|
wl_list_for_each_safe(dev, tmp_dev, wlr_devices, link) {
|
||||||
free(dev);
|
free(dev);
|
||||||
}
|
}
|
||||||
|
@ -215,20 +209,15 @@ static void handle_device_removed(struct wlr_libinput_backend *backend,
|
||||||
if (!wlr_devices) {
|
if (!wlr_devices) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
struct wlr_libinput_input_device *dev, *tmp_dev;
|
struct wlr_input_device *dev, *tmp_dev;
|
||||||
wl_list_for_each_safe(dev, tmp_dev, wlr_devices, link) {
|
wl_list_for_each_safe(dev, tmp_dev, wlr_devices, link) {
|
||||||
wlr_input_device_destroy(&dev->wlr_input_device);
|
wlr_input_device_destroy(dev);
|
||||||
}
|
}
|
||||||
size_t i = 0;
|
for (size_t i = 0; i < backend->wlr_device_lists.length; i++) {
|
||||||
struct wl_list **ptr;
|
if (backend->wlr_device_lists.items[i] == wlr_devices) {
|
||||||
wl_array_for_each(ptr, &backend->wlr_device_lists) {
|
wlr_list_del(&backend->wlr_device_lists, i);
|
||||||
struct wl_list *iter = *ptr;
|
|
||||||
if (iter == wlr_devices) {
|
|
||||||
array_remove_at(&backend->wlr_device_lists,
|
|
||||||
i * sizeof(struct wl_list *), sizeof(struct wl_list *));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
free(wlr_devices);
|
free(wlr_devices);
|
||||||
}
|
}
|
||||||
|
@ -272,7 +261,7 @@ void handle_libinput_event(struct wlr_libinput_backend *backend,
|
||||||
handle_touch_cancel(event, libinput_dev);
|
handle_touch_cancel(event, libinput_dev);
|
||||||
break;
|
break;
|
||||||
case LIBINPUT_EVENT_TOUCH_FRAME:
|
case LIBINPUT_EVENT_TOUCH_FRAME:
|
||||||
handle_touch_frame(event, libinput_dev);
|
// no-op (at least for now)
|
||||||
break;
|
break;
|
||||||
case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
|
case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
|
||||||
handle_tablet_tool_axis(event, libinput_dev);
|
handle_tablet_tool_axis(event, libinput_dev);
|
||||||
|
@ -316,14 +305,6 @@ void handle_libinput_event(struct wlr_libinput_backend *backend,
|
||||||
case LIBINPUT_EVENT_GESTURE_PINCH_END:
|
case LIBINPUT_EVENT_GESTURE_PINCH_END:
|
||||||
handle_pointer_pinch_end(event, libinput_dev);
|
handle_pointer_pinch_end(event, libinput_dev);
|
||||||
break;
|
break;
|
||||||
#if LIBINPUT_HAS_HOLD_GESTURES
|
|
||||||
case LIBINPUT_EVENT_GESTURE_HOLD_BEGIN:
|
|
||||||
handle_pointer_hold_begin(event, libinput_dev);
|
|
||||||
break;
|
|
||||||
case LIBINPUT_EVENT_GESTURE_HOLD_END:
|
|
||||||
handle_pointer_hold_end(event, libinput_dev);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
default:
|
default:
|
||||||
wlr_log(WLR_DEBUG, "Unknown libinput event %d", event_type);
|
wlr_log(WLR_DEBUG, "Unknown libinput event %d", event_type);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -72,10 +72,10 @@ void handle_keyboard_key(struct libinput_event *event,
|
||||||
libinput_event_keyboard_get_key_state(kbevent);
|
libinput_event_keyboard_get_key_state(kbevent);
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case LIBINPUT_KEY_STATE_RELEASED:
|
case LIBINPUT_KEY_STATE_RELEASED:
|
||||||
wlr_event.state = WL_KEYBOARD_KEY_STATE_RELEASED;
|
wlr_event.state = WLR_KEY_RELEASED;
|
||||||
break;
|
break;
|
||||||
case LIBINPUT_KEY_STATE_PRESSED:
|
case LIBINPUT_KEY_STATE_PRESSED:
|
||||||
wlr_event.state = WL_KEYBOARD_KEY_STATE_PRESSED;
|
wlr_event.state = WLR_KEY_PRESSED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
wlr_event.update_state = true;
|
wlr_event.update_state = true;
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
msg = ['Required for libinput backend support.']
|
|
||||||
if 'libinput' in backends
|
|
||||||
msg += 'Install "libinput" or disable the libinput backend.'
|
|
||||||
endif
|
|
||||||
|
|
||||||
libinput = dependency(
|
|
||||||
'libinput',
|
|
||||||
version: '>=1.14.0',
|
|
||||||
required: 'libinput' in backends,
|
|
||||||
not_found_message: '\n'.join(msg),
|
|
||||||
)
|
|
||||||
|
|
||||||
if not libinput.found()
|
|
||||||
subdir_done()
|
|
||||||
endif
|
|
||||||
|
|
||||||
wlr_files += files(
|
|
||||||
'backend.c',
|
|
||||||
'events.c',
|
|
||||||
'keyboard.c',
|
|
||||||
'pointer.c',
|
|
||||||
'switch.c',
|
|
||||||
'tablet_pad.c',
|
|
||||||
'tablet_tool.c',
|
|
||||||
'touch.c',
|
|
||||||
)
|
|
||||||
|
|
||||||
features += { 'libinput-backend': true }
|
|
||||||
wlr_deps += libinput
|
|
||||||
|
|
||||||
# libinput hold gestures are available since 1.19.0
|
|
||||||
add_project_arguments(
|
|
||||||
'-DLIBINPUT_HAS_HOLD_GESTURES=@0@'.format(libinput.version().version_compare('>=1.19.0').to_int()),
|
|
||||||
language: 'c',
|
|
||||||
)
|
|
|
@ -260,41 +260,3 @@ void handle_pointer_pinch_end(struct libinput_event *event,
|
||||||
};
|
};
|
||||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_end, &wlr_event);
|
wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_end, &wlr_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_pointer_hold_begin(struct libinput_event *event,
|
|
||||||
struct libinput_device *libinput_dev) {
|
|
||||||
struct wlr_input_device *wlr_dev =
|
|
||||||
get_appropriate_device(WLR_INPUT_DEVICE_POINTER, libinput_dev);
|
|
||||||
if (!wlr_dev) {
|
|
||||||
wlr_log(WLR_DEBUG, "Got a pointer gesture event for a device with no pointers?");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
struct libinput_event_gesture *gevent =
|
|
||||||
libinput_event_get_gesture_event(event);
|
|
||||||
struct wlr_event_pointer_hold_begin wlr_event = {
|
|
||||||
.device = wlr_dev,
|
|
||||||
.time_msec =
|
|
||||||
usec_to_msec(libinput_event_gesture_get_time_usec(gevent)),
|
|
||||||
.fingers = libinput_event_gesture_get_finger_count(gevent),
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.hold_begin, &wlr_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_pointer_hold_end(struct libinput_event *event,
|
|
||||||
struct libinput_device *libinput_dev) {
|
|
||||||
struct wlr_input_device *wlr_dev =
|
|
||||||
get_appropriate_device(WLR_INPUT_DEVICE_POINTER, libinput_dev);
|
|
||||||
if (!wlr_dev) {
|
|
||||||
wlr_log(WLR_DEBUG, "Got a pointer gesture event for a device with no pointers?");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
struct libinput_event_gesture *gevent =
|
|
||||||
libinput_event_get_gesture_event(event);
|
|
||||||
struct wlr_event_pointer_hold_end wlr_event = {
|
|
||||||
.device = wlr_dev,
|
|
||||||
.time_msec =
|
|
||||||
usec_to_msec(libinput_event_gesture_get_time_usec(gevent)),
|
|
||||||
.cancelled = libinput_event_gesture_get_cancelled(gevent),
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.hold_end, &wlr_event);
|
|
||||||
}
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ void handle_switch_toggle(struct libinput_event *event,
|
||||||
wlr_log(WLR_DEBUG, "Got a switch event for a device with no switch?");
|
wlr_log(WLR_DEBUG, "Got a switch event for a device with no switch?");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
struct libinput_event_switch *sevent =
|
struct libinput_event_switch *sevent =
|
||||||
libinput_event_get_switch_event (event);
|
libinput_event_get_switch_event (event);
|
||||||
struct wlr_event_switch_toggle wlr_event = { 0 };
|
struct wlr_event_switch_toggle wlr_event = { 0 };
|
||||||
wlr_event.device = wlr_dev;
|
wlr_event.device = wlr_dev;
|
||||||
|
@ -51,5 +51,5 @@ void handle_switch_toggle(struct libinput_event *event,
|
||||||
}
|
}
|
||||||
wlr_event.time_msec =
|
wlr_event.time_msec =
|
||||||
usec_to_msec(libinput_event_switch_get_time_usec(sevent));
|
usec_to_msec(libinput_event_switch_get_time_usec(sevent));
|
||||||
wlr_signal_emit_safe(&wlr_dev->switch_device->events.toggle, &wlr_event);
|
wlr_signal_emit_safe(&wlr_dev->lid_switch->events.toggle, &wlr_event);
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,6 @@ struct wlr_tablet_pad *create_libinput_tablet_pad(
|
||||||
wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet_pad");
|
wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet_pad");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
wlr_tablet_pad_init(wlr_tablet_pad, NULL);
|
|
||||||
|
|
||||||
wlr_tablet_pad->button_count =
|
wlr_tablet_pad->button_count =
|
||||||
libinput_device_tablet_pad_get_num_buttons(libinput_dev);
|
libinput_device_tablet_pad_get_num_buttons(libinput_dev);
|
||||||
|
@ -84,15 +83,17 @@ struct wlr_tablet_pad *create_libinput_tablet_pad(
|
||||||
wlr_tablet_pad->strip_count =
|
wlr_tablet_pad->strip_count =
|
||||||
libinput_device_tablet_pad_get_num_strips(libinput_dev);
|
libinput_device_tablet_pad_get_num_strips(libinput_dev);
|
||||||
|
|
||||||
|
wlr_list_init(&wlr_tablet_pad->paths);
|
||||||
struct udev_device *udev = libinput_device_get_udev_device(libinput_dev);
|
struct udev_device *udev = libinput_device_get_udev_device(libinput_dev);
|
||||||
char **dst = wl_array_add(&wlr_tablet_pad->paths, sizeof(char *));
|
wlr_list_push(&wlr_tablet_pad->paths, strdup(udev_device_get_syspath(udev)));
|
||||||
*dst = strdup(udev_device_get_syspath(udev));
|
|
||||||
|
|
||||||
|
wl_list_init(&wlr_tablet_pad->groups);
|
||||||
int groups = libinput_device_tablet_pad_get_num_mode_groups(libinput_dev);
|
int groups = libinput_device_tablet_pad_get_num_mode_groups(libinput_dev);
|
||||||
for (int i = 0; i < groups; ++i) {
|
for (int i = 0; i < groups; ++i) {
|
||||||
add_pad_group_from_libinput(wlr_tablet_pad, libinput_dev, i);
|
add_pad_group_from_libinput(wlr_tablet_pad, libinput_dev, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wlr_tablet_pad_init(wlr_tablet_pad, NULL);
|
||||||
return wlr_tablet_pad;
|
return wlr_tablet_pad;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,10 +11,9 @@
|
||||||
#include <wlr/types/wlr_input_device.h>
|
#include <wlr/types/wlr_input_device.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include "backend/libinput.h"
|
#include "backend/libinput.h"
|
||||||
#include "util/array.h"
|
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
|
|
||||||
static const struct wlr_tablet_impl tablet_impl;
|
static struct wlr_tablet_impl tablet_impl;
|
||||||
|
|
||||||
static bool tablet_is_libinput(struct wlr_tablet *tablet) {
|
static bool tablet_is_libinput(struct wlr_tablet *tablet) {
|
||||||
return tablet->impl == &tablet_impl;
|
return tablet->impl == &tablet_impl;
|
||||||
|
@ -30,9 +29,18 @@ struct wlr_libinput_tablet_tool {
|
||||||
size_t pad_refs;
|
size_t pad_refs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: Maybe this should be a wlr_list? Do we keep it, or want to get rid of
|
||||||
|
// it?
|
||||||
|
struct tablet_tool_list_elem {
|
||||||
|
struct wl_list link;
|
||||||
|
|
||||||
|
struct wlr_libinput_tablet_tool *tool;
|
||||||
|
};
|
||||||
|
|
||||||
struct wlr_libinput_tablet {
|
struct wlr_libinput_tablet {
|
||||||
struct wlr_tablet wlr_tablet;
|
struct wlr_tablet wlr_tablet;
|
||||||
struct wl_array tools; // struct wlr_libinput_tablet_tool *
|
|
||||||
|
struct wl_list tools; // tablet_tool_list_elem::link
|
||||||
};
|
};
|
||||||
|
|
||||||
static void destroy_tool(struct wlr_libinput_tablet_tool *tool) {
|
static void destroy_tool(struct wlr_libinput_tablet_tool *tool) {
|
||||||
|
@ -48,19 +56,22 @@ static void destroy_tablet(struct wlr_tablet *wlr_tablet) {
|
||||||
struct wlr_libinput_tablet *tablet =
|
struct wlr_libinput_tablet *tablet =
|
||||||
wl_container_of(wlr_tablet, tablet, wlr_tablet);
|
wl_container_of(wlr_tablet, tablet, wlr_tablet);
|
||||||
|
|
||||||
struct wlr_libinput_tablet_tool **tool_ptr;
|
struct tablet_tool_list_elem *pos;
|
||||||
wl_array_for_each(tool_ptr, &tablet->tools) {
|
struct tablet_tool_list_elem *tmp;
|
||||||
struct wlr_libinput_tablet_tool *tool = *tool_ptr;
|
wl_list_for_each_safe(pos, tmp, &tablet->tools, link) {
|
||||||
|
struct wlr_libinput_tablet_tool *tool = pos->tool;
|
||||||
|
wl_list_remove(&pos->link);
|
||||||
|
free(pos);
|
||||||
|
|
||||||
if (--tool->pad_refs == 0) {
|
if (--tool->pad_refs == 0) {
|
||||||
destroy_tool(tool);
|
destroy_tool(tool);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wl_array_release(&tablet->tools);
|
|
||||||
|
|
||||||
free(tablet);
|
free(tablet);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct wlr_tablet_impl tablet_impl = {
|
static struct wlr_tablet_impl tablet_impl = {
|
||||||
.destroy = destroy_tablet,
|
.destroy = destroy_tablet,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -73,18 +84,15 @@ struct wlr_tablet *create_libinput_tablet(
|
||||||
wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet_tool");
|
wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet_tool");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_tablet *wlr_tablet = &libinput_tablet->wlr_tablet;
|
struct wlr_tablet *wlr_tablet = &libinput_tablet->wlr_tablet;
|
||||||
wlr_tablet_init(wlr_tablet, &tablet_impl);
|
|
||||||
|
|
||||||
|
wlr_list_init(&wlr_tablet->paths);
|
||||||
struct udev_device *udev = libinput_device_get_udev_device(libinput_dev);
|
struct udev_device *udev = libinput_device_get_udev_device(libinput_dev);
|
||||||
char **dst = wl_array_add(&wlr_tablet->paths, sizeof(char *));
|
wlr_list_push(&wlr_tablet->paths, strdup(udev_device_get_syspath(udev)));
|
||||||
*dst = strdup(udev_device_get_syspath(udev));
|
|
||||||
|
|
||||||
wlr_tablet->name = strdup(libinput_device_get_name(libinput_dev));
|
wlr_tablet->name = strdup(libinput_device_get_name(libinput_dev));
|
||||||
|
wl_list_init(&libinput_tablet->tools);
|
||||||
|
|
||||||
wl_array_init(&libinput_tablet->tools);
|
wlr_tablet_init(wlr_tablet, &tablet_impl);
|
||||||
|
|
||||||
return wlr_tablet;
|
return wlr_tablet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,10 +113,9 @@ static enum wlr_tablet_tool_type wlr_type_from_libinput_type(
|
||||||
return WLR_TABLET_TOOL_TYPE_MOUSE;
|
return WLR_TABLET_TOOL_TYPE_MOUSE;
|
||||||
case LIBINPUT_TABLET_TOOL_TYPE_LENS:
|
case LIBINPUT_TABLET_TOOL_TYPE_LENS:
|
||||||
return WLR_TABLET_TOOL_TYPE_LENS;
|
return WLR_TABLET_TOOL_TYPE_LENS;
|
||||||
case LIBINPUT_TABLET_TOOL_TYPE_TOTEM:
|
|
||||||
return WLR_TABLET_TOOL_TYPE_TOTEM;
|
|
||||||
}
|
}
|
||||||
abort(); // unreachable
|
|
||||||
|
assert(false && "UNREACHABLE");
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct wlr_libinput_tablet_tool *get_wlr_tablet_tool(
|
static struct wlr_libinput_tablet_tool *get_wlr_tablet_tool(
|
||||||
|
@ -152,9 +159,9 @@ static void ensure_tool_reference(struct wlr_libinput_tablet_tool *tool,
|
||||||
struct wlr_libinput_tablet *tablet =
|
struct wlr_libinput_tablet *tablet =
|
||||||
wl_container_of(wlr_dev, tablet, wlr_tablet);
|
wl_container_of(wlr_dev, tablet, wlr_tablet);
|
||||||
|
|
||||||
struct wlr_libinput_tablet_tool **tool_ptr;
|
struct tablet_tool_list_elem *pos;
|
||||||
wl_array_for_each(tool_ptr, &tablet->tools) {
|
wl_list_for_each(pos, &tablet->tools, link) {
|
||||||
if (*tool_ptr == tool) { // We already have a ref
|
if (pos->tool == tool) { // We already have a ref
|
||||||
// XXX: We *could* optimize the tool to the front of
|
// XXX: We *could* optimize the tool to the front of
|
||||||
// the list here, since we will probably get the next
|
// the list here, since we will probably get the next
|
||||||
// couple of events from the same tool.
|
// couple of events from the same tool.
|
||||||
|
@ -165,13 +172,15 @@ static void ensure_tool_reference(struct wlr_libinput_tablet_tool *tool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_libinput_tablet_tool **dst =
|
struct tablet_tool_list_elem *new =
|
||||||
wl_array_add(&tablet->tools, sizeof(tool));
|
calloc(1, sizeof(struct tablet_tool_list_elem));
|
||||||
if (!dst) {
|
if (!new) {
|
||||||
wlr_log(WLR_ERROR, "Failed to allocate memory for tracking tablet tool");
|
wlr_log(WLR_ERROR, "Failed to allocate memory for tracking tablet tool");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
*dst = tool;
|
|
||||||
|
new->tool = tool;
|
||||||
|
wl_list_insert(&tablet->tools, &new->link);
|
||||||
++tool->pad_refs;
|
++tool->pad_refs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,9 +265,6 @@ void handle_tablet_tool_proximity(struct libinput_event *event,
|
||||||
wlr_event.device = wlr_dev;
|
wlr_event.device = wlr_dev;
|
||||||
wlr_event.time_msec =
|
wlr_event.time_msec =
|
||||||
usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent));
|
usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent));
|
||||||
wlr_event.x = libinput_event_tablet_tool_get_x_transformed(tevent, 1);
|
|
||||||
wlr_event.y = libinput_event_tablet_tool_get_y_transformed(tevent, 1);
|
|
||||||
|
|
||||||
switch (libinput_event_tablet_tool_get_proximity_state(tevent)) {
|
switch (libinput_event_tablet_tool_get_proximity_state(tevent)) {
|
||||||
case LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT:
|
case LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT:
|
||||||
wlr_event.state = WLR_TABLET_TOOL_PROXIMITY_OUT;
|
wlr_event.state = WLR_TABLET_TOOL_PROXIMITY_OUT;
|
||||||
|
@ -284,15 +290,15 @@ void handle_tablet_tool_proximity(struct libinput_event *event,
|
||||||
assert(tablet_is_libinput(wlr_dev->tablet));
|
assert(tablet_is_libinput(wlr_dev->tablet));
|
||||||
struct wlr_libinput_tablet *tablet =
|
struct wlr_libinput_tablet *tablet =
|
||||||
wl_container_of(wlr_dev->tablet, tablet, wlr_tablet);
|
wl_container_of(wlr_dev->tablet, tablet, wlr_tablet);
|
||||||
|
struct tablet_tool_list_elem *pos;
|
||||||
|
struct tablet_tool_list_elem *tmp;
|
||||||
|
|
||||||
size_t i = 0;
|
wl_list_for_each_safe(pos, tmp, &tablet->tools, link) {
|
||||||
struct wlr_libinput_tablet_tool **tool_ptr;
|
if (pos->tool == tool) {
|
||||||
wl_array_for_each(tool_ptr, &tablet->tools) {
|
wl_list_remove(&pos->link);
|
||||||
if (*tool_ptr == tool) {
|
free(pos);
|
||||||
array_remove_at(&tablet->tools, i * sizeof(tool), sizeof(tool));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy_tool(tool);
|
destroy_tool(tool);
|
||||||
|
@ -320,9 +326,6 @@ void handle_tablet_tool_tip(struct libinput_event *event,
|
||||||
wlr_event.tool = &tool->wlr_tool;
|
wlr_event.tool = &tool->wlr_tool;
|
||||||
wlr_event.time_msec =
|
wlr_event.time_msec =
|
||||||
usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent));
|
usec_to_msec(libinput_event_tablet_tool_get_time_usec(tevent));
|
||||||
wlr_event.x = libinput_event_tablet_tool_get_x_transformed(tevent, 1);
|
|
||||||
wlr_event.y = libinput_event_tablet_tool_get_y_transformed(tevent, 1);
|
|
||||||
|
|
||||||
switch (libinput_event_tablet_tool_get_tip_state(tevent)) {
|
switch (libinput_event_tablet_tool_get_tip_state(tevent)) {
|
||||||
case LIBINPUT_TABLET_TOOL_TIP_UP:
|
case LIBINPUT_TABLET_TOOL_TIP_UP:
|
||||||
wlr_event.state = WLR_TABLET_TOOL_TIP_UP;
|
wlr_event.state = WLR_TABLET_TOOL_TIP_UP;
|
||||||
|
|
|
@ -34,7 +34,7 @@ void handle_touch_down(struct libinput_event *event,
|
||||||
wlr_event.device = wlr_dev;
|
wlr_event.device = wlr_dev;
|
||||||
wlr_event.time_msec =
|
wlr_event.time_msec =
|
||||||
usec_to_msec(libinput_event_touch_get_time_usec(tevent));
|
usec_to_msec(libinput_event_touch_get_time_usec(tevent));
|
||||||
wlr_event.touch_id = libinput_event_touch_get_seat_slot(tevent);
|
wlr_event.touch_id = libinput_event_touch_get_slot(tevent);
|
||||||
wlr_event.x = libinput_event_touch_get_x_transformed(tevent, 1);
|
wlr_event.x = libinput_event_touch_get_x_transformed(tevent, 1);
|
||||||
wlr_event.y = libinput_event_touch_get_y_transformed(tevent, 1);
|
wlr_event.y = libinput_event_touch_get_y_transformed(tevent, 1);
|
||||||
wlr_signal_emit_safe(&wlr_dev->touch->events.down, &wlr_event);
|
wlr_signal_emit_safe(&wlr_dev->touch->events.down, &wlr_event);
|
||||||
|
@ -54,7 +54,7 @@ void handle_touch_up(struct libinput_event *event,
|
||||||
wlr_event.device = wlr_dev;
|
wlr_event.device = wlr_dev;
|
||||||
wlr_event.time_msec =
|
wlr_event.time_msec =
|
||||||
usec_to_msec(libinput_event_touch_get_time_usec(tevent));
|
usec_to_msec(libinput_event_touch_get_time_usec(tevent));
|
||||||
wlr_event.touch_id = libinput_event_touch_get_seat_slot(tevent);
|
wlr_event.touch_id = libinput_event_touch_get_slot(tevent);
|
||||||
wlr_signal_emit_safe(&wlr_dev->touch->events.up, &wlr_event);
|
wlr_signal_emit_safe(&wlr_dev->touch->events.up, &wlr_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ void handle_touch_motion(struct libinput_event *event,
|
||||||
wlr_event.device = wlr_dev;
|
wlr_event.device = wlr_dev;
|
||||||
wlr_event.time_msec =
|
wlr_event.time_msec =
|
||||||
usec_to_msec(libinput_event_touch_get_time_usec(tevent));
|
usec_to_msec(libinput_event_touch_get_time_usec(tevent));
|
||||||
wlr_event.touch_id = libinput_event_touch_get_seat_slot(tevent);
|
wlr_event.touch_id = libinput_event_touch_get_slot(tevent);
|
||||||
wlr_event.x = libinput_event_touch_get_x_transformed(tevent, 1);
|
wlr_event.x = libinput_event_touch_get_x_transformed(tevent, 1);
|
||||||
wlr_event.y = libinput_event_touch_get_y_transformed(tevent, 1);
|
wlr_event.y = libinput_event_touch_get_y_transformed(tevent, 1);
|
||||||
wlr_signal_emit_safe(&wlr_dev->touch->events.motion, &wlr_event);
|
wlr_signal_emit_safe(&wlr_dev->touch->events.motion, &wlr_event);
|
||||||
|
@ -92,17 +92,6 @@ void handle_touch_cancel(struct libinput_event *event,
|
||||||
wlr_event.device = wlr_dev;
|
wlr_event.device = wlr_dev;
|
||||||
wlr_event.time_msec =
|
wlr_event.time_msec =
|
||||||
usec_to_msec(libinput_event_touch_get_time_usec(tevent));
|
usec_to_msec(libinput_event_touch_get_time_usec(tevent));
|
||||||
wlr_event.touch_id = libinput_event_touch_get_seat_slot(tevent);
|
wlr_event.touch_id = libinput_event_touch_get_slot(tevent);
|
||||||
wlr_signal_emit_safe(&wlr_dev->touch->events.cancel, &wlr_event);
|
wlr_signal_emit_safe(&wlr_dev->touch->events.cancel, &wlr_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_touch_frame(struct libinput_event *event,
|
|
||||||
struct libinput_device *libinput_dev) {
|
|
||||||
struct wlr_input_device *wlr_dev =
|
|
||||||
get_appropriate_device(WLR_INPUT_DEVICE_TOUCH, libinput_dev);
|
|
||||||
if (!wlr_dev) {
|
|
||||||
wlr_log(WLR_DEBUG, "Got a touch event for a device with no touch?");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wlr_signal_emit_safe(&wlr_dev->touch->events.frame, NULL);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,21 +1,63 @@
|
||||||
wlr_files += files('backend.c')
|
backend_parts = []
|
||||||
|
backend_files = files(
|
||||||
|
'backend.c',
|
||||||
|
'drm/atomic.c',
|
||||||
|
'drm/backend.c',
|
||||||
|
'drm/drm.c',
|
||||||
|
'drm/legacy.c',
|
||||||
|
'drm/properties.c',
|
||||||
|
'drm/renderer.c',
|
||||||
|
'drm/util.c',
|
||||||
|
'headless/backend.c',
|
||||||
|
'headless/input_device.c',
|
||||||
|
'headless/output.c',
|
||||||
|
'libinput/backend.c',
|
||||||
|
'libinput/events.c',
|
||||||
|
'libinput/keyboard.c',
|
||||||
|
'libinput/pointer.c',
|
||||||
|
'libinput/switch.c',
|
||||||
|
'libinput/tablet_pad.c',
|
||||||
|
'libinput/tablet_tool.c',
|
||||||
|
'libinput/touch.c',
|
||||||
|
'multi/backend.c',
|
||||||
|
'noop/backend.c',
|
||||||
|
'noop/output.c',
|
||||||
|
'session/direct-ipc.c',
|
||||||
|
'session/session.c',
|
||||||
|
'wayland/backend.c',
|
||||||
|
'wayland/output.c',
|
||||||
|
'wayland/wl_seat.c',
|
||||||
|
)
|
||||||
|
|
||||||
all_backends = ['drm', 'libinput', 'x11']
|
backend_deps = [
|
||||||
backends = get_option('backends')
|
drm,
|
||||||
if 'auto' in backends and get_option('auto_features').enabled()
|
egl,
|
||||||
backends = all_backends
|
gbm,
|
||||||
elif 'auto' in backends and get_option('auto_features').disabled()
|
libinput,
|
||||||
backends = []
|
pixman,
|
||||||
|
xkbcommon,
|
||||||
|
wayland_server,
|
||||||
|
wlr_protos,
|
||||||
|
wlr_render,
|
||||||
|
]
|
||||||
|
|
||||||
|
if host_machine.system().startswith('freebsd')
|
||||||
|
backend_files += files('session/direct-freebsd.c')
|
||||||
|
else
|
||||||
|
backend_files += files('session/direct.c')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
foreach backend : all_backends
|
if logind.found()
|
||||||
if backend in backends or 'auto' in backends
|
backend_files += files('session/logind.c')
|
||||||
subdir(backend)
|
backend_deps += logind
|
||||||
endif
|
endif
|
||||||
endforeach
|
|
||||||
|
|
||||||
subdir('multi')
|
subdir('x11')
|
||||||
subdir('wayland')
|
|
||||||
subdir('headless')
|
|
||||||
|
|
||||||
subdir('session')
|
lib_wlr_backend = static_library(
|
||||||
|
'wlr_backend',
|
||||||
|
backend_files,
|
||||||
|
include_directories: wlr_inc,
|
||||||
|
link_whole: backend_parts,
|
||||||
|
dependencies: backend_deps,
|
||||||
|
)
|
||||||
|
|
|
@ -5,9 +5,7 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <wlr/backend/interface.h>
|
#include <wlr/backend/interface.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/types/wlr_buffer.h>
|
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include "backend/backend.h"
|
|
||||||
#include "backend/multi.h"
|
#include "backend/multi.h"
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
|
|
||||||
|
@ -51,19 +49,30 @@ static void multi_backend_destroy(struct wlr_backend *wlr_backend) {
|
||||||
|
|
||||||
wl_list_remove(&backend->display_destroy.link);
|
wl_list_remove(&backend->display_destroy.link);
|
||||||
|
|
||||||
// Some backends may depend on other backends, ie. destroying a backend may
|
struct subbackend_state *sub, *next;
|
||||||
// also destroy other backends
|
wl_list_for_each_safe(sub, next, &backend->backends, link) {
|
||||||
while (!wl_list_empty(&backend->backends)) {
|
|
||||||
struct subbackend_state *sub =
|
|
||||||
wl_container_of(backend->backends.next, sub, link);
|
|
||||||
wlr_backend_destroy(sub->backend);
|
wlr_backend_destroy(sub->backend);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy this backend only after removing all sub-backends
|
// Destroy this backend only after removing all sub-backends
|
||||||
wlr_backend_finish(wlr_backend);
|
wlr_signal_emit_safe(&wlr_backend->events.destroy, backend);
|
||||||
free(backend);
|
free(backend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct wlr_renderer *multi_backend_get_renderer(
|
||||||
|
struct wlr_backend *backend) {
|
||||||
|
struct wlr_multi_backend *multi = multi_backend_from_backend(backend);
|
||||||
|
|
||||||
|
struct subbackend_state *sub;
|
||||||
|
wl_list_for_each(sub, &multi->backends, link) {
|
||||||
|
struct wlr_renderer *rend = wlr_backend_get_renderer(sub->backend);
|
||||||
|
if (rend != NULL) {
|
||||||
|
return rend;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static struct wlr_session *multi_backend_get_session(
|
static struct wlr_session *multi_backend_get_session(
|
||||||
struct wlr_backend *_backend) {
|
struct wlr_backend *_backend) {
|
||||||
struct wlr_multi_backend *backend = multi_backend_from_backend(_backend);
|
struct wlr_multi_backend *backend = multi_backend_from_backend(_backend);
|
||||||
|
@ -84,48 +93,12 @@ static clockid_t multi_backend_get_presentation_clock(
|
||||||
return CLOCK_MONOTONIC;
|
return CLOCK_MONOTONIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int multi_backend_get_drm_fd(struct wlr_backend *backend) {
|
struct wlr_backend_impl backend_impl = {
|
||||||
struct wlr_multi_backend *multi = multi_backend_from_backend(backend);
|
|
||||||
|
|
||||||
struct subbackend_state *sub;
|
|
||||||
wl_list_for_each(sub, &multi->backends, link) {
|
|
||||||
if (sub->backend->impl->get_drm_fd) {
|
|
||||||
return wlr_backend_get_drm_fd(sub->backend);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t multi_backend_get_buffer_caps(struct wlr_backend *backend) {
|
|
||||||
struct wlr_multi_backend *multi = multi_backend_from_backend(backend);
|
|
||||||
|
|
||||||
if (wl_list_empty(&multi->backends)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t caps = WLR_BUFFER_CAP_DATA_PTR | WLR_BUFFER_CAP_DMABUF
|
|
||||||
| WLR_BUFFER_CAP_SHM;
|
|
||||||
|
|
||||||
struct subbackend_state *sub;
|
|
||||||
wl_list_for_each(sub, &multi->backends, link) {
|
|
||||||
uint32_t backend_caps = backend_get_buffer_caps(sub->backend);
|
|
||||||
if (backend_caps != 0) {
|
|
||||||
// only count backend capable of presenting a buffer
|
|
||||||
caps = caps & backend_caps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return caps;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wlr_backend_impl backend_impl = {
|
|
||||||
.start = multi_backend_start,
|
.start = multi_backend_start,
|
||||||
.destroy = multi_backend_destroy,
|
.destroy = multi_backend_destroy,
|
||||||
|
.get_renderer = multi_backend_get_renderer,
|
||||||
.get_session = multi_backend_get_session,
|
.get_session = multi_backend_get_session,
|
||||||
.get_presentation_clock = multi_backend_get_presentation_clock,
|
.get_presentation_clock = multi_backend_get_presentation_clock,
|
||||||
.get_drm_fd = multi_backend_get_drm_fd,
|
|
||||||
.get_buffer_caps = multi_backend_get_buffer_caps,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
|
@ -189,9 +162,6 @@ static struct subbackend_state *multi_backend_get_subbackend(struct wlr_multi_ba
|
||||||
|
|
||||||
bool wlr_multi_backend_add(struct wlr_backend *_multi,
|
bool wlr_multi_backend_add(struct wlr_backend *_multi,
|
||||||
struct wlr_backend *backend) {
|
struct wlr_backend *backend) {
|
||||||
assert(_multi && backend);
|
|
||||||
assert(_multi != backend);
|
|
||||||
|
|
||||||
struct wlr_multi_backend *multi = multi_backend_from_backend(_multi);
|
struct wlr_multi_backend *multi = multi_backend_from_backend(_multi);
|
||||||
|
|
||||||
if (multi_backend_get_subbackend(multi, backend)) {
|
if (multi_backend_get_subbackend(multi, backend)) {
|
||||||
|
@ -199,12 +169,21 @@ bool wlr_multi_backend_add(struct wlr_backend *_multi,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct wlr_renderer *multi_renderer =
|
||||||
|
multi_backend_get_renderer(&multi->backend);
|
||||||
|
struct wlr_renderer *backend_renderer = wlr_backend_get_renderer(backend);
|
||||||
|
if (multi_renderer != NULL && backend_renderer != NULL && multi_renderer != backend_renderer) {
|
||||||
|
wlr_log(WLR_ERROR, "Could not add backend: multiple renderers at the "
|
||||||
|
"same time aren't supported");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
struct subbackend_state *sub = calloc(1, sizeof(struct subbackend_state));
|
struct subbackend_state *sub = calloc(1, sizeof(struct subbackend_state));
|
||||||
if (sub == NULL) {
|
if (sub == NULL) {
|
||||||
wlr_log(WLR_ERROR, "Could not add backend: allocation failed");
|
wlr_log(WLR_ERROR, "Could not add backend: allocation failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
wl_list_insert(multi->backends.prev, &sub->link);
|
wl_list_insert(&multi->backends, &sub->link);
|
||||||
|
|
||||||
sub->backend = backend;
|
sub->backend = backend;
|
||||||
sub->container = &multi->backend;
|
sub->container = &multi->backend;
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
wlr_files += files('backend.c')
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <wlr/interfaces/wlr_output.h>
|
||||||
|
#include <wlr/util/log.h>
|
||||||
|
#include "backend/noop.h"
|
||||||
|
#include "util/signal.h"
|
||||||
|
|
||||||
|
struct wlr_noop_backend *noop_backend_from_backend(
|
||||||
|
struct wlr_backend *wlr_backend) {
|
||||||
|
assert(wlr_backend_is_noop(wlr_backend));
|
||||||
|
return (struct wlr_noop_backend *)wlr_backend;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool backend_start(struct wlr_backend *wlr_backend) {
|
||||||
|
struct wlr_noop_backend *backend = noop_backend_from_backend(wlr_backend);
|
||||||
|
wlr_log(WLR_INFO, "Starting noop backend");
|
||||||
|
|
||||||
|
struct wlr_noop_output *output;
|
||||||
|
wl_list_for_each(output, &backend->outputs, link) {
|
||||||
|
wlr_output_update_enabled(&output->wlr_output, true);
|
||||||
|
wlr_signal_emit_safe(&backend->backend.events.new_output,
|
||||||
|
&output->wlr_output);
|
||||||
|
}
|
||||||
|
|
||||||
|
backend->started = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void backend_destroy(struct wlr_backend *wlr_backend) {
|
||||||
|
struct wlr_noop_backend *backend = noop_backend_from_backend(wlr_backend);
|
||||||
|
if (!wlr_backend) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_noop_output *output, *output_tmp;
|
||||||
|
wl_list_for_each_safe(output, output_tmp, &backend->outputs, link) {
|
||||||
|
wlr_output_destroy(&output->wlr_output);
|
||||||
|
}
|
||||||
|
|
||||||
|
wlr_signal_emit_safe(&wlr_backend->events.destroy, backend);
|
||||||
|
|
||||||
|
free(backend);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wlr_backend_impl backend_impl = {
|
||||||
|
.start = backend_start,
|
||||||
|
.destroy = backend_destroy,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_backend *wlr_noop_backend_create(struct wl_display *display) {
|
||||||
|
wlr_log(WLR_INFO, "Creating noop backend");
|
||||||
|
|
||||||
|
struct wlr_noop_backend *backend =
|
||||||
|
calloc(1, sizeof(struct wlr_noop_backend));
|
||||||
|
if (!backend) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to allocate wlr_noop_backend");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
wlr_backend_init(&backend->backend, &backend_impl);
|
||||||
|
backend->display = display;
|
||||||
|
wl_list_init(&backend->outputs);
|
||||||
|
|
||||||
|
return &backend->backend;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wlr_backend_is_noop(struct wlr_backend *backend) {
|
||||||
|
return backend->impl == &backend_impl;
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <wlr/interfaces/wlr_output.h>
|
||||||
|
#include <wlr/util/log.h>
|
||||||
|
#include "backend/noop.h"
|
||||||
|
#include "util/signal.h"
|
||||||
|
|
||||||
|
static struct wlr_noop_output *noop_output_from_output(
|
||||||
|
struct wlr_output *wlr_output) {
|
||||||
|
assert(wlr_output_is_noop(wlr_output));
|
||||||
|
return (struct wlr_noop_output *)wlr_output;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_transform(struct wlr_output *wlr_output,
|
||||||
|
enum wl_output_transform transform) {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool output_make_current(struct wlr_output *wlr_output, int *buffer_age) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool output_swap_buffers(struct wlr_output *wlr_output,
|
||||||
|
pixman_region32_t *damage) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_destroy(struct wlr_output *wlr_output) {
|
||||||
|
struct wlr_noop_output *output =
|
||||||
|
noop_output_from_output(wlr_output);
|
||||||
|
|
||||||
|
wl_list_remove(&output->link);
|
||||||
|
|
||||||
|
free(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wlr_output_impl output_impl = {
|
||||||
|
.transform = output_transform,
|
||||||
|
.destroy = output_destroy,
|
||||||
|
.make_current = output_make_current,
|
||||||
|
.swap_buffers = output_swap_buffers,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool wlr_output_is_noop(struct wlr_output *wlr_output) {
|
||||||
|
return wlr_output->impl == &output_impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_output *wlr_noop_add_output(struct wlr_backend *wlr_backend) {
|
||||||
|
struct wlr_noop_backend *backend = noop_backend_from_backend(wlr_backend);
|
||||||
|
|
||||||
|
struct wlr_noop_output *output = calloc(1, sizeof(struct wlr_noop_output));
|
||||||
|
if (output == NULL) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to allocate wlr_noop_output");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
output->backend = backend;
|
||||||
|
wlr_output_init(&output->wlr_output, &backend->backend, &output_impl,
|
||||||
|
backend->display);
|
||||||
|
struct wlr_output *wlr_output = &output->wlr_output;
|
||||||
|
|
||||||
|
strncpy(wlr_output->make, "noop", sizeof(wlr_output->make));
|
||||||
|
strncpy(wlr_output->model, "noop", sizeof(wlr_output->model));
|
||||||
|
snprintf(wlr_output->name, sizeof(wlr_output->name), "NOOP-%d",
|
||||||
|
wl_list_length(&backend->outputs) + 1);
|
||||||
|
|
||||||
|
wl_list_insert(&backend->outputs, &output->link);
|
||||||
|
|
||||||
|
if (backend->started) {
|
||||||
|
wlr_output_update_enabled(wlr_output, true);
|
||||||
|
wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output);
|
||||||
|
}
|
||||||
|
|
||||||
|
return wlr_output;
|
||||||
|
}
|
|
@ -0,0 +1,268 @@
|
||||||
|
#include <assert.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/consio.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/kbio.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <wayland-server.h>
|
||||||
|
#include <wlr/backend/session/interface.h>
|
||||||
|
#include <wlr/util/log.h>
|
||||||
|
#include <xf86drm.h>
|
||||||
|
#include "backend/session/direct-ipc.h"
|
||||||
|
#include "util/signal.h"
|
||||||
|
|
||||||
|
const struct session_impl session_direct;
|
||||||
|
|
||||||
|
struct direct_session {
|
||||||
|
struct wlr_session base;
|
||||||
|
int tty_fd;
|
||||||
|
int old_tty;
|
||||||
|
int old_kbmode;
|
||||||
|
int sock;
|
||||||
|
pid_t child;
|
||||||
|
|
||||||
|
struct wl_event_source *vt_source;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct direct_session *direct_session_from_session(
|
||||||
|
struct wlr_session *base) {
|
||||||
|
assert(base->impl == &session_direct);
|
||||||
|
return (struct direct_session *)base;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int direct_session_open(struct wlr_session *base, const char *path) {
|
||||||
|
struct direct_session *session = direct_session_from_session(base);
|
||||||
|
|
||||||
|
int fd = direct_ipc_open(session->sock, path);
|
||||||
|
if (fd < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to open %s: %s%s", path, strerror(-fd),
|
||||||
|
fd == -EINVAL ? "; is another display server running?" : "");
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void direct_session_close(struct wlr_session *base, int fd) {
|
||||||
|
struct direct_session *session = direct_session_from_session(base);
|
||||||
|
|
||||||
|
int ev;
|
||||||
|
struct drm_version dv = {0};
|
||||||
|
if (ioctl(fd, DRM_IOCTL_VERSION, &dv) == 0) {
|
||||||
|
direct_ipc_dropmaster(session->sock, fd);
|
||||||
|
} else if (ioctl(fd, EVIOCGVERSION, &ev) == 0) {
|
||||||
|
ioctl(fd, EVIOCREVOKE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool direct_change_vt(struct wlr_session *base, unsigned vt) {
|
||||||
|
struct direct_session *session = direct_session_from_session(base);
|
||||||
|
|
||||||
|
// Only seat0 has VTs associated with it
|
||||||
|
if (strcmp(session->base.seat, "seat0") != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioctl(session->tty_fd, VT_ACTIVATE, (int)vt) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void direct_session_destroy(struct wlr_session *base) {
|
||||||
|
struct direct_session *session = direct_session_from_session(base);
|
||||||
|
|
||||||
|
if (strcmp(session->base.seat, "seat0") == 0) {
|
||||||
|
struct vt_mode mode = {
|
||||||
|
.mode = VT_AUTO,
|
||||||
|
};
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
ioctl(session->tty_fd, KDSKBMODE, session->old_kbmode);
|
||||||
|
ioctl(session->tty_fd, KDSETMODE, KD_TEXT);
|
||||||
|
ioctl(session->tty_fd, VT_SETMODE, &mode);
|
||||||
|
|
||||||
|
ioctl(session->tty_fd, VT_ACTIVATE, session->old_tty);
|
||||||
|
|
||||||
|
if (errno) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to restore tty");
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_event_source_remove(session->vt_source);
|
||||||
|
close(session->tty_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
direct_ipc_finish(session->sock, session->child);
|
||||||
|
close(session->sock);
|
||||||
|
|
||||||
|
free(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vt_handler(int signo, void *data) {
|
||||||
|
struct direct_session *session = data;
|
||||||
|
struct drm_version dv = {0};
|
||||||
|
struct wlr_device *dev;
|
||||||
|
|
||||||
|
if (session->base.active) {
|
||||||
|
session->base.active = false;
|
||||||
|
wlr_signal_emit_safe(&session->base.session_signal, session);
|
||||||
|
|
||||||
|
wl_list_for_each(dev, &session->base.devices, link) {
|
||||||
|
if (ioctl(dev->fd, DRM_IOCTL_VERSION, &dv) == 0) {
|
||||||
|
direct_ipc_dropmaster(session->sock, dev->fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ioctl(session->tty_fd, VT_RELDISP, 1);
|
||||||
|
} else {
|
||||||
|
ioctl(session->tty_fd, VT_RELDISP, VT_ACKACQ);
|
||||||
|
|
||||||
|
wl_list_for_each(dev, &session->base.devices, link) {
|
||||||
|
if (ioctl(dev->fd, DRM_IOCTL_VERSION, &dv) == 0) {
|
||||||
|
direct_ipc_setmaster(session->sock, dev->fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session->base.active = true;
|
||||||
|
wlr_signal_emit_safe(&session->base.session_signal, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool setup_tty(struct direct_session *session, struct wl_display *display) {
|
||||||
|
int fd = -1, tty = -1, tty0_fd = -1, old_tty = 1;
|
||||||
|
if ((tty0_fd = open("/dev/ttyv0", O_RDWR | O_CLOEXEC)) < 0) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Could not open /dev/ttyv0 to find a free vt");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (ioctl(tty0_fd, VT_GETACTIVE, &old_tty) != 0) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Could not get active vt");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (ioctl(tty0_fd, VT_OPENQRY, &tty) != 0) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Could not find a free vt");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
close(tty0_fd);
|
||||||
|
char tty_path[64];
|
||||||
|
snprintf(tty_path, sizeof(tty_path), "/dev/ttyv%d", tty - 1);
|
||||||
|
wlr_log(WLR_INFO, "Using tty %s", tty_path);
|
||||||
|
fd = open(tty_path, O_RDWR | O_NOCTTY | O_CLOEXEC);
|
||||||
|
|
||||||
|
if (fd == -1) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Cannot open tty");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ioctl(fd, VT_ACTIVATE, tty);
|
||||||
|
ioctl(fd, VT_WAITACTIVE, tty);
|
||||||
|
|
||||||
|
int old_kbmode;
|
||||||
|
if (ioctl(fd, KDGKBMODE, &old_kbmode)) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Failed to read tty %d keyboard mode", tty);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(fd, KDSKBMODE, K_CODE)) {
|
||||||
|
wlr_log_errno(WLR_ERROR,
|
||||||
|
"Failed to set keyboard mode K_CODE on tty %d", tty);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(fd, KDSETMODE, KD_GRAPHICS)) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Failed to set graphics mode on tty %d", tty);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct vt_mode mode = {
|
||||||
|
.mode = VT_PROCESS,
|
||||||
|
.relsig = SIGUSR2,
|
||||||
|
.acqsig = SIGUSR2,
|
||||||
|
.frsig = SIGIO, // has to be set
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ioctl(fd, VT_SETMODE, &mode) < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to take control of tty %d", tty);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wl_event_loop *loop = wl_display_get_event_loop(display);
|
||||||
|
session->vt_source = wl_event_loop_add_signal(loop, SIGUSR2,
|
||||||
|
vt_handler, session);
|
||||||
|
if (!session->vt_source) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
session->base.vtnr = tty;
|
||||||
|
session->tty_fd = fd;
|
||||||
|
session->old_tty = old_tty;
|
||||||
|
session->old_kbmode = old_kbmode;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error:
|
||||||
|
// In case we could not get the last active one, drop back to tty 1,
|
||||||
|
// better than hanging in a useless blank console. Otherwise activate the
|
||||||
|
// last active.
|
||||||
|
ioctl(fd, VT_ACTIVATE, old_tty);
|
||||||
|
close(fd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wlr_session *direct_session_create(struct wl_display *disp) {
|
||||||
|
struct direct_session *session = calloc(1, sizeof(*session));
|
||||||
|
if (!session) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
session->sock = direct_ipc_init(&session->child);
|
||||||
|
if (session->sock == -1) {
|
||||||
|
goto error_session;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *seat = getenv("XDG_SEAT");
|
||||||
|
if (!seat) {
|
||||||
|
seat = "seat0";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(seat, "seat0") == 0) {
|
||||||
|
if (!setup_tty(session, disp)) {
|
||||||
|
goto error_ipc;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
session->base.vtnr = 0;
|
||||||
|
session->tty_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
wlr_log(WLR_INFO, "Successfully loaded direct session");
|
||||||
|
|
||||||
|
snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat);
|
||||||
|
session->base.impl = &session_direct;
|
||||||
|
return &session->base;
|
||||||
|
|
||||||
|
error_ipc:
|
||||||
|
direct_ipc_finish(session->sock, session->child);
|
||||||
|
close(session->sock);
|
||||||
|
error_session:
|
||||||
|
free(session);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct session_impl session_direct = {
|
||||||
|
.create = direct_session_create,
|
||||||
|
.destroy = direct_session_destroy,
|
||||||
|
.open = direct_session_open,
|
||||||
|
.close = direct_session_close,
|
||||||
|
.change_vt = direct_change_vt,
|
||||||
|
};
|
|
@ -0,0 +1,269 @@
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
#define __BSD_VISIBLE 1
|
||||||
|
#include <linux/input.h>
|
||||||
|
#endif
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <wlr/config.h>
|
||||||
|
#include <wlr/util/log.h>
|
||||||
|
#include <xf86drm.h>
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <sys/sysmacros.h>
|
||||||
|
#include <linux/major.h>
|
||||||
|
#endif
|
||||||
|
#include "backend/session/direct-ipc.h"
|
||||||
|
|
||||||
|
enum { DRM_MAJOR = 226 };
|
||||||
|
|
||||||
|
#if WLR_HAS_LIBCAP
|
||||||
|
#include <sys/capability.h>
|
||||||
|
|
||||||
|
static bool have_permissions(void) {
|
||||||
|
cap_t cap = cap_get_proc();
|
||||||
|
cap_flag_value_t val;
|
||||||
|
|
||||||
|
if (!cap || cap_get_flag(cap, CAP_SYS_ADMIN, CAP_PERMITTED, &val) || val != CAP_SET) {
|
||||||
|
wlr_log(WLR_ERROR, "Do not have CAP_SYS_ADMIN; cannot become DRM master");
|
||||||
|
cap_free(cap);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cap_free(cap);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static bool have_permissions(void) {
|
||||||
|
#ifdef __linux__
|
||||||
|
if (geteuid() != 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Do not have root privileges; cannot become DRM master");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void send_msg(int sock, int fd, void *buf, size_t buf_len) {
|
||||||
|
char control[CMSG_SPACE(sizeof(fd))] = {0};
|
||||||
|
struct iovec iovec = { .iov_base = buf, .iov_len = buf_len };
|
||||||
|
struct msghdr msghdr = {0};
|
||||||
|
|
||||||
|
if (buf) {
|
||||||
|
msghdr.msg_iov = &iovec;
|
||||||
|
msghdr.msg_iovlen = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd >= 0) {
|
||||||
|
msghdr.msg_control = &control;
|
||||||
|
msghdr.msg_controllen = sizeof(control);
|
||||||
|
|
||||||
|
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msghdr);
|
||||||
|
*cmsg = (struct cmsghdr) {
|
||||||
|
.cmsg_level = SOL_SOCKET,
|
||||||
|
.cmsg_type = SCM_RIGHTS,
|
||||||
|
.cmsg_len = CMSG_LEN(sizeof(fd)),
|
||||||
|
};
|
||||||
|
memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t ret;
|
||||||
|
do {
|
||||||
|
ret = sendmsg(sock, &msghdr, 0);
|
||||||
|
} while (ret < 0 && errno == EINTR);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t recv_msg(int sock, int *fd_out, void *buf, size_t buf_len) {
|
||||||
|
char control[CMSG_SPACE(sizeof(*fd_out))] = {0};
|
||||||
|
struct iovec iovec = { .iov_base = buf, .iov_len = buf_len };
|
||||||
|
struct msghdr msghdr = {0};
|
||||||
|
|
||||||
|
if (buf) {
|
||||||
|
msghdr.msg_iov = &iovec;
|
||||||
|
msghdr.msg_iovlen = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd_out) {
|
||||||
|
msghdr.msg_control = &control;
|
||||||
|
msghdr.msg_controllen = sizeof(control);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t ret;
|
||||||
|
do {
|
||||||
|
ret = recvmsg(sock, &msghdr, MSG_CMSG_CLOEXEC);
|
||||||
|
} while (ret < 0 && errno == EINTR);
|
||||||
|
|
||||||
|
if (fd_out) {
|
||||||
|
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msghdr);
|
||||||
|
if (cmsg) {
|
||||||
|
memcpy(fd_out, CMSG_DATA(cmsg), sizeof(*fd_out));
|
||||||
|
} else {
|
||||||
|
*fd_out = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum msg_type {
|
||||||
|
MSG_OPEN,
|
||||||
|
MSG_SETMASTER,
|
||||||
|
MSG_DROPMASTER,
|
||||||
|
MSG_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct msg {
|
||||||
|
enum msg_type type;
|
||||||
|
char path[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
static void communicate(int sock) {
|
||||||
|
struct msg msg;
|
||||||
|
int drm_fd = -1;
|
||||||
|
bool running = true;
|
||||||
|
|
||||||
|
while (running && recv_msg(sock, &drm_fd, &msg, sizeof(msg)) > 0) {
|
||||||
|
switch (msg.type) {
|
||||||
|
case MSG_OPEN:
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
// These are the same flags that logind opens files with
|
||||||
|
int fd = open(msg.path, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
|
||||||
|
int ret = errno;
|
||||||
|
if (fd == -1) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef __FreeBSD__
|
||||||
|
struct stat st;
|
||||||
|
if (fstat(fd, &st) < 0) {
|
||||||
|
ret = errno;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t maj = major(st.st_rdev);
|
||||||
|
if (maj != INPUT_MAJOR && maj != DRM_MAJOR) {
|
||||||
|
ret = ENOTSUP;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maj == DRM_MAJOR && drmSetMaster(fd)) {
|
||||||
|
ret = errno;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
int ev;
|
||||||
|
struct drm_version dv = {0};
|
||||||
|
if (ioctl(fd, EVIOCGVERSION, &ev) == -1 &&
|
||||||
|
ioctl(fd, DRM_IOCTL_VERSION, &dv) == -1) {
|
||||||
|
ret = ENOTSUP;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dv.version_major != 0 && drmSetMaster(fd)) {
|
||||||
|
ret = errno;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
error:
|
||||||
|
send_msg(sock, ret ? -1 : fd, &ret, sizeof(ret));
|
||||||
|
if (fd >= 0) {
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MSG_SETMASTER:
|
||||||
|
drmSetMaster(drm_fd);
|
||||||
|
close(drm_fd);
|
||||||
|
send_msg(sock, -1, NULL, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MSG_DROPMASTER:
|
||||||
|
drmDropMaster(drm_fd);
|
||||||
|
close(drm_fd);
|
||||||
|
send_msg(sock, -1, NULL, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MSG_END:
|
||||||
|
running = false;
|
||||||
|
send_msg(sock, -1, NULL, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(sock);
|
||||||
|
}
|
||||||
|
|
||||||
|
int direct_ipc_open(int sock, const char *path) {
|
||||||
|
struct msg msg = { .type = MSG_OPEN };
|
||||||
|
snprintf(msg.path, sizeof(msg.path), "%s", path);
|
||||||
|
|
||||||
|
send_msg(sock, -1, &msg, sizeof(msg));
|
||||||
|
|
||||||
|
int fd, err, ret;
|
||||||
|
int retry = 0;
|
||||||
|
do {
|
||||||
|
ret = recv_msg(sock, &fd, &err, sizeof(err));
|
||||||
|
} while (ret == 0 && retry++ < 3);
|
||||||
|
|
||||||
|
return err ? -err : fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void direct_ipc_setmaster(int sock, int fd) {
|
||||||
|
struct msg msg = { .type = MSG_SETMASTER };
|
||||||
|
|
||||||
|
send_msg(sock, fd, &msg, sizeof(msg));
|
||||||
|
recv_msg(sock, NULL, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void direct_ipc_dropmaster(int sock, int fd) {
|
||||||
|
struct msg msg = { .type = MSG_DROPMASTER };
|
||||||
|
|
||||||
|
send_msg(sock, fd, &msg, sizeof(msg));
|
||||||
|
recv_msg(sock, NULL, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void direct_ipc_finish(int sock, pid_t pid) {
|
||||||
|
struct msg msg = { .type = MSG_END };
|
||||||
|
|
||||||
|
send_msg(sock, -1, &msg, sizeof(msg));
|
||||||
|
recv_msg(sock, NULL, NULL, 0);
|
||||||
|
|
||||||
|
waitpid(pid, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int direct_ipc_init(pid_t *pid_out) {
|
||||||
|
if (!have_permissions()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sock[2];
|
||||||
|
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sock) < 0) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Failed to create socket pair");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid < 0) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Fork failed");
|
||||||
|
close(sock[0]);
|
||||||
|
close(sock[1]);
|
||||||
|
return -1;
|
||||||
|
} else if (pid == 0) {
|
||||||
|
close(sock[0]);
|
||||||
|
communicate(sock[1]);
|
||||||
|
_Exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(sock[1]);
|
||||||
|
*pid_out = pid;
|
||||||
|
return sock[0];
|
||||||
|
}
|
|
@ -0,0 +1,278 @@
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <linux/kd.h>
|
||||||
|
#include <linux/major.h>
|
||||||
|
#include <linux/vt.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/sysmacros.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <wayland-server.h>
|
||||||
|
#include <wlr/backend/session/interface.h>
|
||||||
|
#include <wlr/util/log.h>
|
||||||
|
#include "backend/session/direct-ipc.h"
|
||||||
|
#include "util/signal.h"
|
||||||
|
|
||||||
|
enum { DRM_MAJOR = 226 };
|
||||||
|
|
||||||
|
const struct session_impl session_direct;
|
||||||
|
|
||||||
|
struct direct_session {
|
||||||
|
struct wlr_session base;
|
||||||
|
int tty_fd;
|
||||||
|
int old_kbmode;
|
||||||
|
int sock;
|
||||||
|
pid_t child;
|
||||||
|
|
||||||
|
struct wl_event_source *vt_source;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct direct_session *direct_session_from_session(
|
||||||
|
struct wlr_session *base) {
|
||||||
|
assert(base->impl == &session_direct);
|
||||||
|
return (struct direct_session *)base;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int direct_session_open(struct wlr_session *base, const char *path) {
|
||||||
|
struct direct_session *session = direct_session_from_session(base);
|
||||||
|
|
||||||
|
int fd = direct_ipc_open(session->sock, path);
|
||||||
|
if (fd < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to open %s: %s%s", path, strerror(-fd),
|
||||||
|
fd == -EINVAL ? "; is another display server running?" : "");
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if (fstat(fd, &st) < 0) {
|
||||||
|
close(fd);
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (major(st.st_rdev) == DRM_MAJOR) {
|
||||||
|
direct_ipc_setmaster(session->sock, fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void direct_session_close(struct wlr_session *base, int fd) {
|
||||||
|
struct direct_session *session = direct_session_from_session(base);
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if (fstat(fd, &st) < 0) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Stat failed");
|
||||||
|
close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (major(st.st_rdev) == DRM_MAJOR) {
|
||||||
|
direct_ipc_dropmaster(session->sock, fd);
|
||||||
|
} else if (major(st.st_rdev) == INPUT_MAJOR) {
|
||||||
|
ioctl(fd, EVIOCREVOKE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool direct_change_vt(struct wlr_session *base, unsigned vt) {
|
||||||
|
struct direct_session *session = direct_session_from_session(base);
|
||||||
|
|
||||||
|
// Only seat0 has VTs associated with it
|
||||||
|
if (strcmp(session->base.seat, "seat0") != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioctl(session->tty_fd, VT_ACTIVATE, (int)vt) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void direct_session_destroy(struct wlr_session *base) {
|
||||||
|
struct direct_session *session = direct_session_from_session(base);
|
||||||
|
|
||||||
|
if (strcmp(session->base.seat, "seat0") == 0) {
|
||||||
|
struct vt_mode mode = {
|
||||||
|
.mode = VT_AUTO,
|
||||||
|
};
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
ioctl(session->tty_fd, KDSKBMODE, session->old_kbmode);
|
||||||
|
ioctl(session->tty_fd, KDSETMODE, KD_TEXT);
|
||||||
|
ioctl(session->tty_fd, VT_SETMODE, &mode);
|
||||||
|
|
||||||
|
if (errno) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to restore tty");
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_event_source_remove(session->vt_source);
|
||||||
|
close(session->tty_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
direct_ipc_finish(session->sock, session->child);
|
||||||
|
close(session->sock);
|
||||||
|
|
||||||
|
free(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vt_handler(int signo, void *data) {
|
||||||
|
struct direct_session *session = data;
|
||||||
|
|
||||||
|
if (session->base.active) {
|
||||||
|
session->base.active = false;
|
||||||
|
wlr_signal_emit_safe(&session->base.session_signal, session);
|
||||||
|
|
||||||
|
struct wlr_device *dev;
|
||||||
|
wl_list_for_each(dev, &session->base.devices, link) {
|
||||||
|
if (major(dev->dev) == DRM_MAJOR) {
|
||||||
|
direct_ipc_dropmaster(session->sock,
|
||||||
|
dev->fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ioctl(session->tty_fd, VT_RELDISP, 1);
|
||||||
|
} else {
|
||||||
|
ioctl(session->tty_fd, VT_RELDISP, VT_ACKACQ);
|
||||||
|
|
||||||
|
struct wlr_device *dev;
|
||||||
|
wl_list_for_each(dev, &session->base.devices, link) {
|
||||||
|
if (major(dev->dev) == DRM_MAJOR) {
|
||||||
|
direct_ipc_setmaster(session->sock,
|
||||||
|
dev->fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
session->base.active = true;
|
||||||
|
wlr_signal_emit_safe(&session->base.session_signal, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool setup_tty(struct direct_session *session, struct wl_display *display) {
|
||||||
|
int fd = open("/dev/tty", O_RDWR);
|
||||||
|
if (fd == -1) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Cannot open /dev/tty");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct vt_stat vt_stat;
|
||||||
|
if (ioctl(fd, VT_GETSTATE, &vt_stat)) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Could not get current tty number");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tty = vt_stat.v_active;
|
||||||
|
int ret, kd_mode, old_kbmode;
|
||||||
|
|
||||||
|
ret = ioctl(fd, KDGETMODE, &kd_mode);
|
||||||
|
if (ret) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Failed to get tty mode");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kd_mode != KD_TEXT) {
|
||||||
|
wlr_log(WLR_ERROR,
|
||||||
|
"tty already in graphics mode; is another display server running?");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ioctl(fd, VT_ACTIVATE, tty);
|
||||||
|
ioctl(fd, VT_WAITACTIVE, tty);
|
||||||
|
|
||||||
|
if (ioctl(fd, KDGKBMODE, &old_kbmode)) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Failed to read keyboard mode");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(fd, KDSKBMODE, K_OFF)) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Failed to set keyboard mode");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(fd, KDSETMODE, KD_GRAPHICS)) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Failed to set graphics mode on tty");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct vt_mode mode = {
|
||||||
|
.mode = VT_PROCESS,
|
||||||
|
.relsig = SIGUSR2,
|
||||||
|
.acqsig = SIGUSR2,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ioctl(fd, VT_SETMODE, &mode) < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to take control of tty");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wl_event_loop *loop = wl_display_get_event_loop(display);
|
||||||
|
session->vt_source = wl_event_loop_add_signal(loop, SIGUSR2,
|
||||||
|
vt_handler, session);
|
||||||
|
if (!session->vt_source) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
session->base.vtnr = tty;
|
||||||
|
session->tty_fd = fd;
|
||||||
|
session->old_kbmode = old_kbmode;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error:
|
||||||
|
close(fd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wlr_session *direct_session_create(struct wl_display *disp) {
|
||||||
|
struct direct_session *session = calloc(1, sizeof(*session));
|
||||||
|
if (!session) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
session->sock = direct_ipc_init(&session->child);
|
||||||
|
if (session->sock == -1) {
|
||||||
|
goto error_session;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *seat = getenv("XDG_SEAT");
|
||||||
|
if (!seat) {
|
||||||
|
seat = "seat0";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(seat, "seat0") == 0) {
|
||||||
|
if (!setup_tty(session, disp)) {
|
||||||
|
goto error_ipc;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
session->base.vtnr = 0;
|
||||||
|
session->tty_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat);
|
||||||
|
session->base.impl = &session_direct;
|
||||||
|
|
||||||
|
wlr_log(WLR_INFO, "Successfully loaded direct session");
|
||||||
|
return &session->base;
|
||||||
|
|
||||||
|
error_ipc:
|
||||||
|
direct_ipc_finish(session->sock, session->child);
|
||||||
|
close(session->sock);
|
||||||
|
error_session:
|
||||||
|
free(session);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct session_impl session_direct = {
|
||||||
|
.create = direct_session_create,
|
||||||
|
.destroy = direct_session_destroy,
|
||||||
|
.open = direct_session_open,
|
||||||
|
.close = direct_session_close,
|
||||||
|
.change_vt = direct_change_vt,
|
||||||
|
};
|
|
@ -0,0 +1,683 @@
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
#include <assert.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/sysmacros.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <wayland-server.h>
|
||||||
|
#include <wlr/backend/session/interface.h>
|
||||||
|
#include <wlr/config.h>
|
||||||
|
#include <wlr/util/log.h>
|
||||||
|
#include "util/signal.h"
|
||||||
|
|
||||||
|
#if WLR_HAS_SYSTEMD
|
||||||
|
#include <systemd/sd-bus.h>
|
||||||
|
#include <systemd/sd-login.h>
|
||||||
|
#elif WLR_HAS_ELOGIND
|
||||||
|
#include <elogind/sd-bus.h>
|
||||||
|
#include <elogind/sd-login.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum { DRM_MAJOR = 226 };
|
||||||
|
|
||||||
|
const struct session_impl session_logind;
|
||||||
|
|
||||||
|
struct logind_session {
|
||||||
|
struct wlr_session base;
|
||||||
|
|
||||||
|
sd_bus *bus;
|
||||||
|
struct wl_event_source *event;
|
||||||
|
|
||||||
|
char *id;
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
// specifies whether a drm device was taken
|
||||||
|
// if so, the session will be (de)activated with the drm fd,
|
||||||
|
// otherwise with the dbus PropertiesChanged on "active" signal
|
||||||
|
bool has_drm;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct logind_session *logind_session_from_session(
|
||||||
|
struct wlr_session *base) {
|
||||||
|
assert(base->impl == &session_logind);
|
||||||
|
return (struct logind_session *)base;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int logind_take_device(struct wlr_session *base, const char *path) {
|
||||||
|
struct logind_session *session = logind_session_from_session(base);
|
||||||
|
|
||||||
|
int fd = -1;
|
||||||
|
int ret;
|
||||||
|
sd_bus_message *msg = NULL;
|
||||||
|
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if (stat(path, &st) < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to stat '%s'", path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (major(st.st_rdev) == DRM_MAJOR) {
|
||||||
|
session->has_drm = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
|
||||||
|
session->path, "org.freedesktop.login1.Session", "TakeDevice",
|
||||||
|
&error, &msg, "uu", major(st.st_rdev), minor(st.st_rdev));
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to take device '%s': %s", path,
|
||||||
|
error.message);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
int paused = 0;
|
||||||
|
ret = sd_bus_message_read(msg, "hb", &fd, &paused);
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to parse D-Bus response for '%s': %s",
|
||||||
|
path, strerror(-ret));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The original fd seems to be closed when the message is freed
|
||||||
|
// so we just clone it.
|
||||||
|
fd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
|
||||||
|
if (fd < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to clone file descriptor for '%s': %s",
|
||||||
|
path, strerror(errno));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
sd_bus_error_free(&error);
|
||||||
|
sd_bus_message_unref(msg);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void logind_release_device(struct wlr_session *base, int fd) {
|
||||||
|
struct logind_session *session = logind_session_from_session(base);
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if (fstat(fd, &st) < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to stat device '%d': %s", fd,
|
||||||
|
strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sd_bus_message *msg = NULL;
|
||||||
|
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
int ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
|
||||||
|
session->path, "org.freedesktop.login1.Session", "ReleaseDevice",
|
||||||
|
&error, &msg, "uu", major(st.st_rdev), minor(st.st_rdev));
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to release device '%d': %s", fd,
|
||||||
|
error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
sd_bus_error_free(&error);
|
||||||
|
sd_bus_message_unref(msg);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool logind_change_vt(struct wlr_session *base, unsigned vt) {
|
||||||
|
struct logind_session *session = logind_session_from_session(base);
|
||||||
|
|
||||||
|
// Only seat0 has VTs associated with it
|
||||||
|
if (strcmp(session->base.seat, "seat0") != 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
sd_bus_message *msg = NULL;
|
||||||
|
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
|
||||||
|
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
|
||||||
|
"/org/freedesktop/login1/seat/self", "org.freedesktop.login1.Seat", "SwitchTo",
|
||||||
|
&error, &msg, "u", (uint32_t)vt);
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to change to vt '%d'", vt);
|
||||||
|
}
|
||||||
|
|
||||||
|
sd_bus_error_free(&error);
|
||||||
|
sd_bus_message_unref(msg);
|
||||||
|
return ret >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool find_session_path(struct logind_session *session) {
|
||||||
|
int ret;
|
||||||
|
sd_bus_message *msg = NULL;
|
||||||
|
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
|
||||||
|
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
|
||||||
|
"/org/freedesktop/login1", "org.freedesktop.login1.Manager",
|
||||||
|
"GetSession", &error, &msg, "s", session->id);
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to get session path: %s", error.message);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *path;
|
||||||
|
ret = sd_bus_message_read(msg, "o", &path);
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Could not parse session path: %s", error.message);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
session->path = strdup(path);
|
||||||
|
|
||||||
|
out:
|
||||||
|
sd_bus_error_free(&error);
|
||||||
|
sd_bus_message_unref(msg);
|
||||||
|
|
||||||
|
return ret >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool session_activate(struct logind_session *session) {
|
||||||
|
int ret;
|
||||||
|
sd_bus_message *msg = NULL;
|
||||||
|
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
|
||||||
|
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
|
||||||
|
session->path, "org.freedesktop.login1.Session", "Activate",
|
||||||
|
&error, &msg, "");
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to activate session: %s", error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
sd_bus_error_free(&error);
|
||||||
|
sd_bus_message_unref(msg);
|
||||||
|
return ret >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool take_control(struct logind_session *session) {
|
||||||
|
int ret;
|
||||||
|
sd_bus_message *msg = NULL;
|
||||||
|
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
|
||||||
|
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
|
||||||
|
session->path, "org.freedesktop.login1.Session", "TakeControl",
|
||||||
|
&error, &msg, "b", false);
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to take control of session: %s",
|
||||||
|
error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
sd_bus_error_free(&error);
|
||||||
|
sd_bus_message_unref(msg);
|
||||||
|
return ret >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release_control(struct logind_session *session) {
|
||||||
|
int ret;
|
||||||
|
sd_bus_message *msg = NULL;
|
||||||
|
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
|
||||||
|
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
|
||||||
|
session->path, "org.freedesktop.login1.Session", "ReleaseControl",
|
||||||
|
&error, &msg, "");
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to release control of session: %s",
|
||||||
|
error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
sd_bus_error_free(&error);
|
||||||
|
sd_bus_message_unref(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void logind_session_destroy(struct wlr_session *base) {
|
||||||
|
struct logind_session *session = logind_session_from_session(base);
|
||||||
|
|
||||||
|
release_control(session);
|
||||||
|
|
||||||
|
wl_event_source_remove(session->event);
|
||||||
|
sd_bus_unref(session->bus);
|
||||||
|
free(session->id);
|
||||||
|
free(session->path);
|
||||||
|
free(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int session_removed(sd_bus_message *msg, void *userdata,
|
||||||
|
sd_bus_error *ret_error) {
|
||||||
|
wlr_log(WLR_INFO, "SessionRemoved signal received");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wlr_device *find_device(struct wlr_session *session,
|
||||||
|
dev_t devnum) {
|
||||||
|
struct wlr_device *dev;
|
||||||
|
|
||||||
|
wl_list_for_each(dev, &session->devices, link) {
|
||||||
|
if (dev->dev == devnum) {
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wlr_log(WLR_ERROR, "Tried to use dev_t %lu not opened by session",
|
||||||
|
(unsigned long)devnum);
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pause_device(sd_bus_message *msg, void *userdata,
|
||||||
|
sd_bus_error *ret_error) {
|
||||||
|
struct logind_session *session = userdata;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
uint32_t major, minor;
|
||||||
|
const char *type;
|
||||||
|
ret = sd_bus_message_read(msg, "uus", &major, &minor, &type);
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to parse D-Bus response for PauseDevice: %s",
|
||||||
|
strerror(-ret));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (major == DRM_MAJOR) {
|
||||||
|
assert(session->has_drm);
|
||||||
|
session->base.active = false;
|
||||||
|
wlr_signal_emit_safe(&session->base.session_signal, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(type, "pause") == 0) {
|
||||||
|
ret = sd_bus_call_method(session->bus, "org.freedesktop.login1",
|
||||||
|
session->path, "org.freedesktop.login1.Session", "PauseDeviceComplete",
|
||||||
|
ret_error, &msg, "uu", major, minor);
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to send PauseDeviceComplete signal: %s",
|
||||||
|
strerror(-ret));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int resume_device(sd_bus_message *msg, void *userdata,
|
||||||
|
sd_bus_error *ret_error) {
|
||||||
|
struct logind_session *session = userdata;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
uint32_t major, minor;
|
||||||
|
ret = sd_bus_message_read(msg, "uuh", &major, &minor, &fd);
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to parse D-Bus response for ResumeDevice: %s",
|
||||||
|
strerror(-ret));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (major == DRM_MAJOR) {
|
||||||
|
struct wlr_device *dev = find_device(&session->base, makedev(major, minor));
|
||||||
|
|
||||||
|
close(dev->fd);
|
||||||
|
if (fcntl(fd, F_DUPFD_CLOEXEC, dev->fd) < 0) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Failed to duplicate file descriptor");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!session->base.active) {
|
||||||
|
session->base.active = true;
|
||||||
|
wlr_signal_emit_safe(&session->base.session_signal, session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int properties_changed(sd_bus_message *msg, void *userdata,
|
||||||
|
sd_bus_error *ret_error) {
|
||||||
|
struct logind_session *session = userdata;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
// if we have a drm fd we don't depend on this
|
||||||
|
if (session->has_drm) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropertiesChanged arg 1: interface
|
||||||
|
const char *interface;
|
||||||
|
ret = sd_bus_message_read_basic(msg, 's', &interface); // skip path
|
||||||
|
if (ret < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(interface, "org.freedesktop.login1.Session") != 0) {
|
||||||
|
// not interesting for us; ignore
|
||||||
|
wlr_log(WLR_DEBUG, "ignoring PropertiesChanged from %s", interface);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropertiesChanged arg 2: changed properties with values
|
||||||
|
ret = sd_bus_message_enter_container(msg, 'a', "{sv}");
|
||||||
|
if (ret < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *s;
|
||||||
|
while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) {
|
||||||
|
ret = sd_bus_message_read_basic(msg, 's', &s);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(s, "Active") == 0) {
|
||||||
|
int ret;
|
||||||
|
ret = sd_bus_message_enter_container(msg, 'v', "b");
|
||||||
|
if (ret < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool active;
|
||||||
|
ret = sd_bus_message_read_basic(msg, 'b', &active);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session->base.active != active) {
|
||||||
|
session->base.active = active;
|
||||||
|
wlr_signal_emit_safe(&session->base.session_signal, session);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
sd_bus_message_skip(msg, "{sv}");
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sd_bus_message_exit_container(msg);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sd_bus_message_exit_container(msg);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PropertiesChanged arg 3: changed properties without values
|
||||||
|
sd_bus_message_enter_container(msg, 'a', "s");
|
||||||
|
while ((ret = sd_bus_message_read_basic(msg, 's', &s)) > 0) {
|
||||||
|
if (strcmp(s, "Active") == 0) {
|
||||||
|
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
bool active;
|
||||||
|
ret = sd_bus_get_property_trivial(session->bus,
|
||||||
|
"org.freedesktop.login1", session->path,
|
||||||
|
"org.freedesktop.login1.Session", "Active", &error,
|
||||||
|
'b', &active);
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to get 'Active' property: %s",
|
||||||
|
error.message);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (session->base.active != active) {
|
||||||
|
session->base.active = active;
|
||||||
|
wlr_signal_emit_safe(&session->base.session_signal, session);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
wlr_log(WLR_ERROR, "Failed to parse D-Bus PropertiesChanged: %s",
|
||||||
|
strerror(-ret));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool add_signal_matches(struct logind_session *session) {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
char str[256];
|
||||||
|
const char *fmt = "type='signal',"
|
||||||
|
"sender='org.freedesktop.login1',"
|
||||||
|
"interface='org.freedesktop.login1.%s',"
|
||||||
|
"member='%s',"
|
||||||
|
"path='%s'";
|
||||||
|
|
||||||
|
snprintf(str, sizeof(str), fmt, "Manager", "SessionRemoved",
|
||||||
|
"/org/freedesktop/login1");
|
||||||
|
ret = sd_bus_add_match(session->bus, NULL, str, session_removed, session);
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(str, sizeof(str), fmt, "Session", "PauseDevice", session->path);
|
||||||
|
ret = sd_bus_add_match(session->bus, NULL, str, pause_device, session);
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(str, sizeof(str), fmt, "Session", "ResumeDevice", session->path);
|
||||||
|
ret = sd_bus_add_match(session->bus, NULL, str, resume_device, session);
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sd_bus_match_signal(session->bus, NULL, "org.freedesktop.login1",
|
||||||
|
session->path, "org.freedesktop.DBus.Properties", "PropertiesChanged",
|
||||||
|
properties_changed, session);
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to add D-Bus match: %s", strerror(-ret));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dbus_event(int fd, uint32_t mask, void *data) {
|
||||||
|
sd_bus *bus = data;
|
||||||
|
while (sd_bus_process(bus, NULL) > 0) {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool contains_str(const char *needle, const char **haystack) {
|
||||||
|
for (int i = 0; haystack[i]; i++) {
|
||||||
|
if (strcmp(haystack[i], needle) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool get_greeter_session(char **session_id) {
|
||||||
|
char *class = NULL;
|
||||||
|
char **user_sessions = NULL;
|
||||||
|
int user_session_count = sd_uid_get_sessions(getuid(), 1, &user_sessions);
|
||||||
|
|
||||||
|
if (user_session_count < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to get sessions");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user_session_count == 0) {
|
||||||
|
wlr_log(WLR_ERROR, "User has no sessions");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < user_session_count; ++i) {
|
||||||
|
int ret = sd_session_get_class(user_sessions[i], &class);
|
||||||
|
if (ret < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(class, "greeter") == 0) {
|
||||||
|
*session_id = strdup(user_sessions[i]);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
free(class);
|
||||||
|
for (int i = 0; i < user_session_count; ++i) {
|
||||||
|
free(user_sessions[i]);
|
||||||
|
}
|
||||||
|
free(user_sessions);
|
||||||
|
|
||||||
|
return *session_id != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool get_display_session(char **session_id) {
|
||||||
|
assert(session_id != NULL);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
// If there's a session active for the current process then just use that
|
||||||
|
ret = sd_pid_get_session(getpid(), session_id);
|
||||||
|
if (ret == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *type = NULL;
|
||||||
|
char *state = NULL;
|
||||||
|
|
||||||
|
// Find any active sessions for the user if the process isn't part of an
|
||||||
|
// active session itself
|
||||||
|
ret = sd_uid_get_display(getuid(), session_id);
|
||||||
|
if (ret < 0 && ret != -ENODATA) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to get display: %s", strerror(-ret));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != 0 && !get_greeter_session(session_id)) {
|
||||||
|
wlr_log(WLR_ERROR, "Couldn't find an active session or a greeter session");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(*session_id != NULL);
|
||||||
|
|
||||||
|
// Check that the available session is graphical
|
||||||
|
ret = sd_session_get_type(*session_id, &type);
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Couldn't get a type for session '%s': %s",
|
||||||
|
*session_id, strerror(-ret));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *graphical_session_types[] = {"wayland", "x11", "mir", NULL};
|
||||||
|
if (!contains_str(type, graphical_session_types)) {
|
||||||
|
wlr_log(WLR_ERROR, "Session '%s' isn't a graphical session (type: '%s')",
|
||||||
|
*session_id, type);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the session is active
|
||||||
|
ret = sd_session_get_state(*session_id, &state);
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Couldn't get state for session '%s': %s",
|
||||||
|
*session_id, strerror(-ret));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *active_states[] = {"active", "online", NULL};
|
||||||
|
if (!contains_str(state, active_states)) {
|
||||||
|
wlr_log(WLR_ERROR, "Session '%s' is not active", *session_id);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(type);
|
||||||
|
free(state);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error:
|
||||||
|
free(type);
|
||||||
|
free(state);
|
||||||
|
free(*session_id);
|
||||||
|
*session_id = NULL;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wlr_session *logind_session_create(struct wl_display *disp) {
|
||||||
|
int ret;
|
||||||
|
struct logind_session *session = calloc(1, sizeof(*session));
|
||||||
|
if (!session) {
|
||||||
|
wlr_log(WLR_ERROR, "Allocation failed: %s", strerror(errno));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!get_display_session(&session->id)) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *seat;
|
||||||
|
ret = sd_session_get_seat(session->id, &seat);
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to get seat id: %s", strerror(-ret));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
snprintf(session->base.seat, sizeof(session->base.seat), "%s", seat);
|
||||||
|
|
||||||
|
if (strcmp(seat, "seat0") == 0) {
|
||||||
|
ret = sd_session_get_vt(session->id, &session->base.vtnr);
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Session not running in virtual terminal");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(seat);
|
||||||
|
|
||||||
|
ret = sd_bus_default_system(&session->bus);
|
||||||
|
if (ret < 0) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to open D-Bus connection: %s", strerror(-ret));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!find_session_path(session)) {
|
||||||
|
sd_bus_unref(session->bus);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!add_signal_matches(session)) {
|
||||||
|
goto error_bus;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wl_event_loop *event_loop = wl_display_get_event_loop(disp);
|
||||||
|
session->event = wl_event_loop_add_fd(event_loop, sd_bus_get_fd(session->bus),
|
||||||
|
WL_EVENT_READABLE, dbus_event, session->bus);
|
||||||
|
|
||||||
|
if (!session_activate(session)) {
|
||||||
|
goto error_bus;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!take_control(session)) {
|
||||||
|
goto error_bus;
|
||||||
|
}
|
||||||
|
|
||||||
|
wlr_log(WLR_INFO, "Successfully loaded logind session");
|
||||||
|
|
||||||
|
session->base.impl = &session_logind;
|
||||||
|
return &session->base;
|
||||||
|
|
||||||
|
error_bus:
|
||||||
|
sd_bus_unref(session->bus);
|
||||||
|
free(session->path);
|
||||||
|
|
||||||
|
error:
|
||||||
|
free(session->id);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct session_impl session_logind = {
|
||||||
|
.create = logind_session_create,
|
||||||
|
.destroy = logind_session_destroy,
|
||||||
|
.open = logind_take_device,
|
||||||
|
.close = logind_release_device,
|
||||||
|
.change_vt = logind_change_vt,
|
||||||
|
};
|
|
@ -1,7 +0,0 @@
|
||||||
libseat = dependency('libseat',
|
|
||||||
version: '>=0.2.0',
|
|
||||||
fallback: ['seatd', 'libseat'],
|
|
||||||
default_options: ['server=disabled', 'man-pages=disabled'],
|
|
||||||
)
|
|
||||||
wlr_files += files('session.c')
|
|
||||||
wlr_deps += libseat
|
|
|
@ -1,174 +1,32 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <libudev.h>
|
#include <libudev.h>
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <wayland-server.h>
|
||||||
#include <time.h>
|
|
||||||
#include <wayland-server-core.h>
|
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
|
#include <wlr/backend/session/interface.h>
|
||||||
#include <wlr/config.h>
|
#include <wlr/config.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <xf86drm.h>
|
#include <xf86drm.h>
|
||||||
#include <xf86drmMode.h>
|
#include <xf86drmMode.h>
|
||||||
#include "backend/session/session.h"
|
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
|
|
||||||
#include <libseat.h>
|
extern const struct session_impl session_logind;
|
||||||
|
extern const struct session_impl session_direct;
|
||||||
|
|
||||||
#define WAIT_GPU_TIMEOUT 10000 // ms
|
static const struct session_impl *impls[] = {
|
||||||
|
#if WLR_HAS_SYSTEMD || WLR_HAS_ELOGIND
|
||||||
static void handle_enable_seat(struct libseat *seat, void *data) {
|
&session_logind,
|
||||||
struct wlr_session *session = data;
|
#endif
|
||||||
session->active = true;
|
&session_direct,
|
||||||
wlr_signal_emit_safe(&session->events.active, NULL);
|
NULL,
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_disable_seat(struct libseat *seat, void *data) {
|
|
||||||
struct wlr_session *session = data;
|
|
||||||
session->active = false;
|
|
||||||
wlr_signal_emit_safe(&session->events.active, NULL);
|
|
||||||
libseat_disable_seat(session->seat_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int libseat_event(int fd, uint32_t mask, void *data) {
|
|
||||||
struct wlr_session *session = data;
|
|
||||||
if (libseat_dispatch(session->seat_handle, 0) == -1) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Failed to dispatch libseat");
|
|
||||||
wl_display_terminate(session->display);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct libseat_seat_listener seat_listener = {
|
|
||||||
.enable_seat = handle_enable_seat,
|
|
||||||
.disable_seat = handle_disable_seat,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static enum wlr_log_importance libseat_log_level_to_wlr(
|
static int udev_event(int fd, uint32_t mask, void *data) {
|
||||||
enum libseat_log_level level) {
|
|
||||||
switch (level) {
|
|
||||||
case LIBSEAT_LOG_LEVEL_ERROR:
|
|
||||||
return WLR_ERROR;
|
|
||||||
case LIBSEAT_LOG_LEVEL_INFO:
|
|
||||||
return WLR_INFO;
|
|
||||||
default:
|
|
||||||
return WLR_DEBUG;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void log_libseat(enum libseat_log_level level,
|
|
||||||
const char *fmt, va_list args) {
|
|
||||||
enum wlr_log_importance importance = libseat_log_level_to_wlr(level);
|
|
||||||
|
|
||||||
static char wlr_fmt[1024];
|
|
||||||
snprintf(wlr_fmt, sizeof(wlr_fmt), "[libseat] %s", fmt);
|
|
||||||
|
|
||||||
_wlr_vlog(importance, wlr_fmt, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int libseat_session_init(struct wlr_session *session, struct wl_display *disp) {
|
|
||||||
libseat_set_log_handler(log_libseat);
|
|
||||||
libseat_set_log_level(LIBSEAT_LOG_LEVEL_INFO);
|
|
||||||
|
|
||||||
// libseat will take care of updating the logind state if necessary
|
|
||||||
setenv("XDG_SESSION_TYPE", "wayland", 1);
|
|
||||||
|
|
||||||
session->seat_handle = libseat_open_seat(&seat_listener, session);
|
|
||||||
if (session->seat_handle == NULL) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Unable to create seat");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *seat_name = libseat_seat_name(session->seat_handle);
|
|
||||||
if (seat_name == NULL) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Unable to get seat info");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
snprintf(session->seat, sizeof(session->seat), "%s", seat_name);
|
|
||||||
|
|
||||||
struct wl_event_loop *event_loop = wl_display_get_event_loop(disp);
|
|
||||||
session->libseat_event = wl_event_loop_add_fd(event_loop, libseat_get_fd(session->seat_handle),
|
|
||||||
WL_EVENT_READABLE, libseat_event, session);
|
|
||||||
if (session->libseat_event == NULL) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to create libseat event source");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We may have received enable_seat immediately after the open_seat result,
|
|
||||||
// so, dispatch once without timeout to speed up activation.
|
|
||||||
if (libseat_dispatch(session->seat_handle, 0) == -1) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "libseat dispatch failed");
|
|
||||||
goto error_dispatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_log(WLR_INFO, "Successfully loaded libseat session");
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
error_dispatch:
|
|
||||||
wl_event_source_remove(session->libseat_event);
|
|
||||||
session->libseat_event = NULL;
|
|
||||||
error:
|
|
||||||
libseat_close_seat(session->seat_handle);
|
|
||||||
session->seat_handle = NULL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void libseat_session_finish(struct wlr_session *session) {
|
|
||||||
libseat_close_seat(session->seat_handle);
|
|
||||||
wl_event_source_remove(session->libseat_event);
|
|
||||||
session->seat_handle = NULL;
|
|
||||||
session->libseat_event = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_drm_card(const char *sysname) {
|
|
||||||
const char prefix[] = DRM_PRIMARY_MINOR_NAME;
|
|
||||||
if (strncmp(sysname, prefix, strlen(prefix)) != 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (size_t i = strlen(prefix); sysname[i] != '\0'; i++) {
|
|
||||||
if (sysname[i] < '0' || sysname[i] > '9') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void read_udev_change_event(struct wlr_device_change_event *event,
|
|
||||||
struct udev_device *udev_dev) {
|
|
||||||
const char *hotplug = udev_device_get_property_value(udev_dev, "HOTPLUG");
|
|
||||||
if (hotplug != NULL && strcmp(hotplug, "1") == 0) {
|
|
||||||
event->type = WLR_DEVICE_HOTPLUG;
|
|
||||||
struct wlr_device_hotplug_event *hotplug = &event->hotplug;
|
|
||||||
|
|
||||||
const char *connector =
|
|
||||||
udev_device_get_property_value(udev_dev, "CONNECTOR");
|
|
||||||
if (connector != NULL) {
|
|
||||||
hotplug->connector_id = strtoul(connector, NULL, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *prop =
|
|
||||||
udev_device_get_property_value(udev_dev, "PROPERTY");
|
|
||||||
if (prop != NULL) {
|
|
||||||
hotplug->prop_id = strtoul(prop, NULL, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *lease = udev_device_get_property_value(udev_dev, "LEASE");
|
|
||||||
if (lease != NULL && strcmp(lease, "1") == 0) {
|
|
||||||
event->type = WLR_DEVICE_LEASE;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int handle_udev_event(int fd, uint32_t mask, void *data) {
|
|
||||||
struct wlr_session *session = data;
|
struct wlr_session *session = data;
|
||||||
|
|
||||||
struct udev_device *udev_dev = udev_monitor_receive_device(session->mon);
|
struct udev_device *udev_dev = udev_monitor_receive_device(session->mon);
|
||||||
|
@ -176,48 +34,21 @@ static int handle_udev_event(int fd, uint32_t mask, void *data) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *sysname = udev_device_get_sysname(udev_dev);
|
|
||||||
const char *devnode = udev_device_get_devnode(udev_dev);
|
|
||||||
const char *action = udev_device_get_action(udev_dev);
|
const char *action = udev_device_get_action(udev_dev);
|
||||||
wlr_log(WLR_DEBUG, "udev event for %s (%s)", sysname, action);
|
|
||||||
|
|
||||||
if (!is_drm_card(sysname) || !action || !devnode) {
|
wlr_log(WLR_DEBUG, "udev event for %s (%s)",
|
||||||
|
udev_device_get_sysname(udev_dev), action);
|
||||||
|
|
||||||
|
if (!action || strcmp(action, "change") != 0) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *seat = udev_device_get_property_value(udev_dev, "ID_SEAT");
|
dev_t devnum = udev_device_get_devnum(udev_dev);
|
||||||
if (!seat) {
|
struct wlr_device *dev;
|
||||||
seat = "seat0";
|
|
||||||
}
|
|
||||||
if (session->seat[0] != '\0' && strcmp(session->seat, seat) != 0) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(action, "add") == 0) {
|
wl_list_for_each(dev, &session->devices, link) {
|
||||||
wlr_log(WLR_DEBUG, "DRM device %s added", sysname);
|
if (dev->dev == devnum) {
|
||||||
struct wlr_session_add_event event = {
|
wlr_signal_emit_safe(&dev->signal, session);
|
||||||
.path = devnode,
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&session->events.add_drm_card, &event);
|
|
||||||
} else if (strcmp(action, "change") == 0 || strcmp(action, "remove") == 0) {
|
|
||||||
dev_t devnum = udev_device_get_devnum(udev_dev);
|
|
||||||
struct wlr_device *dev;
|
|
||||||
wl_list_for_each(dev, &session->devices, link) {
|
|
||||||
if (dev->dev != devnum) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(action, "change") == 0) {
|
|
||||||
wlr_log(WLR_DEBUG, "DRM device %s changed", sysname);
|
|
||||||
struct wlr_device_change_event event = {0};
|
|
||||||
read_udev_change_event(&event, udev_dev);
|
|
||||||
wlr_signal_emit_safe(&dev->events.change, &event);
|
|
||||||
} else if (strcmp(action, "remove") == 0) {
|
|
||||||
wlr_log(WLR_DEBUG, "DRM device %s removed", sysname);
|
|
||||||
wlr_signal_emit_safe(&dev->events.remove, NULL);
|
|
||||||
} else {
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,22 +65,38 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_session *wlr_session_create(struct wl_display *disp) {
|
struct wlr_session *wlr_session_create(struct wl_display *disp) {
|
||||||
struct wlr_session *session = calloc(1, sizeof(*session));
|
struct wlr_session *session = NULL;
|
||||||
|
|
||||||
|
const char *env_wlr_session = getenv("WLR_SESSION");
|
||||||
|
if (env_wlr_session) {
|
||||||
|
if (!strcmp(env_wlr_session, "logind") || !strcmp(env_wlr_session, "systemd")) {
|
||||||
|
#if WLR_HAS_SYSTEMD || WLR_HAS_ELOGIND
|
||||||
|
session = session_logind.create(disp);
|
||||||
|
#else
|
||||||
|
wlr_log(WLR_ERROR, "wlroots is not compiled with logind support");
|
||||||
|
#endif
|
||||||
|
} else if (!strcmp(env_wlr_session, "direct")) {
|
||||||
|
session = session_direct.create(disp);
|
||||||
|
} else {
|
||||||
|
wlr_log(WLR_ERROR, "WLR_SESSION has an invalid value: %s", env_wlr_session);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const struct session_impl **iter;
|
||||||
|
for (iter = impls; !session && *iter; ++iter) {
|
||||||
|
session = (*iter)->create(disp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
wlr_log(WLR_ERROR, "Failed to load session backend");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
wl_signal_init(&session->events.active);
|
session->active = true;
|
||||||
wl_signal_init(&session->events.add_drm_card);
|
wl_signal_init(&session->session_signal);
|
||||||
wl_signal_init(&session->events.destroy);
|
wl_signal_init(&session->events.destroy);
|
||||||
wl_list_init(&session->devices);
|
wl_list_init(&session->devices);
|
||||||
|
|
||||||
if (libseat_session_init(session, disp) == -1) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to load session backend");
|
|
||||||
goto error_open;
|
|
||||||
}
|
|
||||||
|
|
||||||
session->udev = udev_new();
|
session->udev = udev_new();
|
||||||
if (!session->udev) {
|
if (!session->udev) {
|
||||||
wlr_log_errno(WLR_ERROR, "Failed to create udev context");
|
wlr_log_errno(WLR_ERROR, "Failed to create udev context");
|
||||||
|
@ -269,14 +116,12 @@ struct wlr_session *wlr_session_create(struct wl_display *disp) {
|
||||||
int fd = udev_monitor_get_fd(session->mon);
|
int fd = udev_monitor_get_fd(session->mon);
|
||||||
|
|
||||||
session->udev_event = wl_event_loop_add_fd(event_loop, fd,
|
session->udev_event = wl_event_loop_add_fd(event_loop, fd,
|
||||||
WL_EVENT_READABLE, handle_udev_event, session);
|
WL_EVENT_READABLE, udev_event, session);
|
||||||
if (!session->udev_event) {
|
if (!session->udev_event) {
|
||||||
wlr_log_errno(WLR_ERROR, "Failed to create udev event source");
|
wlr_log_errno(WLR_ERROR, "Failed to create udev event source");
|
||||||
goto error_mon;
|
goto error_mon;
|
||||||
}
|
}
|
||||||
|
|
||||||
session->display = disp;
|
|
||||||
|
|
||||||
session->display_destroy.notify = handle_display_destroy;
|
session->display_destroy.notify = handle_display_destroy;
|
||||||
wl_display_add_destroy_listener(disp, &session->display_destroy);
|
wl_display_add_destroy_listener(disp, &session->display_destroy);
|
||||||
|
|
||||||
|
@ -287,9 +132,7 @@ error_mon:
|
||||||
error_udev:
|
error_udev:
|
||||||
udev_unref(session->udev);
|
udev_unref(session->udev);
|
||||||
error_session:
|
error_session:
|
||||||
libseat_session_finish(session);
|
session->impl->destroy(session);
|
||||||
error_open:
|
|
||||||
free(session);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,22 +148,13 @@ void wlr_session_destroy(struct wlr_session *session) {
|
||||||
udev_monitor_unref(session->mon);
|
udev_monitor_unref(session->mon);
|
||||||
udev_unref(session->udev);
|
udev_unref(session->udev);
|
||||||
|
|
||||||
struct wlr_device *dev, *tmp_dev;
|
session->impl->destroy(session);
|
||||||
wl_list_for_each_safe(dev, tmp_dev, &session->devices, link) {
|
|
||||||
wlr_session_close_file(session, dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
libseat_session_finish(session);
|
|
||||||
free(session);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_device *wlr_session_open_file(struct wlr_session *session,
|
int wlr_session_open_file(struct wlr_session *session, const char *path) {
|
||||||
const char *path) {
|
int fd = session->impl->open(session, path);
|
||||||
int fd;
|
if (fd < 0) {
|
||||||
int device_id = libseat_open_device(session->seat_handle, path, &fd);
|
return fd;
|
||||||
if (device_id == -1) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Failed to open device: '%s'", path);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_device *dev = malloc(sizeof(*dev));
|
struct wlr_device *dev = malloc(sizeof(*dev));
|
||||||
|
@ -337,65 +171,86 @@ struct wlr_device *wlr_session_open_file(struct wlr_session *session,
|
||||||
|
|
||||||
dev->fd = fd;
|
dev->fd = fd;
|
||||||
dev->dev = st.st_rdev;
|
dev->dev = st.st_rdev;
|
||||||
dev->device_id = device_id;
|
wl_signal_init(&dev->signal);
|
||||||
wl_signal_init(&dev->events.change);
|
|
||||||
wl_signal_init(&dev->events.remove);
|
|
||||||
wl_list_insert(&session->devices, &dev->link);
|
wl_list_insert(&session->devices, &dev->link);
|
||||||
|
|
||||||
return dev;
|
return fd;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
libseat_close_device(session->seat_handle, device_id);
|
|
||||||
free(dev);
|
free(dev);
|
||||||
close(fd);
|
return fd;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void wlr_session_close_file(struct wlr_session *session,
|
static struct wlr_device *find_device(struct wlr_session *session, int fd) {
|
||||||
struct wlr_device *dev) {
|
struct wlr_device *dev;
|
||||||
if (libseat_close_device(session->seat_handle, dev->device_id) == -1) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Failed to close device %d", dev->device_id);
|
wl_list_for_each(dev, &session->devices, link) {
|
||||||
|
if (dev->fd == fd) {
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
close(dev->fd);
|
|
||||||
|
wlr_log(WLR_ERROR, "Tried to use fd %d not opened by session", fd);
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wlr_session_close_file(struct wlr_session *session, int fd) {
|
||||||
|
struct wlr_device *dev = find_device(session, fd);
|
||||||
|
|
||||||
|
session->impl->close(session, fd);
|
||||||
wl_list_remove(&dev->link);
|
wl_list_remove(&dev->link);
|
||||||
free(dev);
|
free(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wlr_session_signal_add(struct wlr_session *session, int fd,
|
||||||
|
struct wl_listener *listener) {
|
||||||
|
struct wlr_device *dev = find_device(session, fd);
|
||||||
|
|
||||||
|
wl_signal_add(&dev->signal, listener);
|
||||||
|
}
|
||||||
|
|
||||||
bool wlr_session_change_vt(struct wlr_session *session, unsigned vt) {
|
bool wlr_session_change_vt(struct wlr_session *session, unsigned vt) {
|
||||||
if (!session) {
|
if (!session) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return libseat_switch_session(session->seat_handle, vt) == 0;
|
|
||||||
|
return session->impl->change_vt(session, vt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tests if 'path' is KMS compatible by trying to open it. Returns the opened
|
/* Tests if 'path' is KMS compatible by trying to open it.
|
||||||
* device on success. */
|
* It leaves the open device in *fd_out it it succeeds.
|
||||||
struct wlr_device *session_open_if_kms(struct wlr_session *restrict session,
|
*/
|
||||||
const char *restrict path) {
|
static int open_if_kms(struct wlr_session *restrict session, const char *restrict path) {
|
||||||
|
int fd;
|
||||||
|
|
||||||
if (!path) {
|
if (!path) {
|
||||||
return NULL;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_device *dev = wlr_session_open_file(session, path);
|
fd = wlr_session_open_file(session, path);
|
||||||
if (!dev) {
|
if (fd < 0) {
|
||||||
return NULL;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!drmIsKMS(dev->fd)) {
|
drmModeRes *res = drmModeGetResources(fd);
|
||||||
wlr_log(WLR_DEBUG, "Ignoring '%s': not a KMS device", path);
|
if (!res) {
|
||||||
wlr_session_close_file(session, dev);
|
goto out_fd;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return dev;
|
drmModeFreeResources(res);
|
||||||
|
return fd;
|
||||||
|
|
||||||
|
out_fd:
|
||||||
|
wlr_session_close_file(session, fd);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t explicit_find_gpus(struct wlr_session *session,
|
static size_t explicit_find_gpus(struct wlr_session *session,
|
||||||
size_t ret_len, struct wlr_device *ret[static ret_len], const char *str) {
|
size_t ret_len, int ret[static ret_len], const char *str) {
|
||||||
char *gpus = strdup(str);
|
char *gpus = strdup(str);
|
||||||
if (!gpus) {
|
if (!gpus) {
|
||||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
@ -406,8 +261,8 @@ static ssize_t explicit_find_gpus(struct wlr_session *session,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret[i] = session_open_if_kms(session, ptr);
|
ret[i] = open_if_kms(session, ptr);
|
||||||
if (!ret[i]) {
|
if (ret[i] < 0) {
|
||||||
wlr_log(WLR_ERROR, "Unable to open %s as DRM device", ptr);
|
wlr_log(WLR_ERROR, "Unable to open %s as DRM device", ptr);
|
||||||
} else {
|
} else {
|
||||||
++i;
|
++i;
|
||||||
|
@ -418,92 +273,30 @@ static ssize_t explicit_find_gpus(struct wlr_session *session,
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct udev_enumerate *enumerate_drm_cards(struct udev *udev) {
|
|
||||||
struct udev_enumerate *en = udev_enumerate_new(udev);
|
|
||||||
if (!en) {
|
|
||||||
wlr_log(WLR_ERROR, "udev_enumerate_new failed");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
udev_enumerate_add_match_subsystem(en, "drm");
|
|
||||||
udev_enumerate_add_match_sysname(en, DRM_PRIMARY_MINOR_NAME "[0-9]*");
|
|
||||||
|
|
||||||
if (udev_enumerate_scan_devices(en) != 0) {
|
|
||||||
wlr_log(WLR_ERROR, "udev_enumerate_scan_devices failed");
|
|
||||||
udev_enumerate_unref(en);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return en;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t get_current_time_ms(void) {
|
|
||||||
struct timespec ts = {0};
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
||||||
return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct find_gpus_add_handler {
|
|
||||||
bool added;
|
|
||||||
struct wl_listener listener;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void find_gpus_handle_add(struct wl_listener *listener, void *data) {
|
|
||||||
struct find_gpus_add_handler *handler =
|
|
||||||
wl_container_of(listener, handler, listener);
|
|
||||||
handler->added = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tries to find the primary GPU by checking for the "boot_vga" attribute.
|
/* Tries to find the primary GPU by checking for the "boot_vga" attribute.
|
||||||
* If it's not found, it returns the first valid GPU it finds.
|
* If it's not found, it returns the first valid GPU it finds.
|
||||||
*/
|
*/
|
||||||
ssize_t wlr_session_find_gpus(struct wlr_session *session,
|
size_t wlr_session_find_gpus(struct wlr_session *session,
|
||||||
size_t ret_len, struct wlr_device **ret) {
|
size_t ret_len, int *ret) {
|
||||||
const char *explicit = getenv("WLR_DRM_DEVICES");
|
const char *explicit = getenv("WLR_DRM_DEVICES");
|
||||||
if (explicit) {
|
if (explicit) {
|
||||||
return explicit_find_gpus(session, ret_len, ret, explicit);
|
return explicit_find_gpus(session, ret_len, ret, explicit);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct udev_enumerate *en = enumerate_drm_cards(session->udev);
|
#ifdef __FreeBSD__
|
||||||
|
// XXX: libudev-devd does not return any GPUs (yet?)
|
||||||
|
return explicit_find_gpus(session, ret_len, ret, "/dev/drm/0");
|
||||||
|
#else
|
||||||
|
|
||||||
|
struct udev_enumerate *en = udev_enumerate_new(session->udev);
|
||||||
if (!en) {
|
if (!en) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to create udev enumeration");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (udev_enumerate_get_list_entry(en) == NULL) {
|
udev_enumerate_add_match_subsystem(en, "drm");
|
||||||
udev_enumerate_unref(en);
|
udev_enumerate_add_match_sysname(en, "card[0-9]*");
|
||||||
wlr_log(WLR_INFO, "Waiting for a DRM card device");
|
udev_enumerate_scan_devices(en);
|
||||||
|
|
||||||
struct find_gpus_add_handler handler = {0};
|
|
||||||
handler.listener.notify = find_gpus_handle_add;
|
|
||||||
wl_signal_add(&session->events.add_drm_card, &handler.listener);
|
|
||||||
|
|
||||||
uint64_t started_at = get_current_time_ms();
|
|
||||||
uint64_t timeout = WAIT_GPU_TIMEOUT;
|
|
||||||
struct wl_event_loop *event_loop =
|
|
||||||
wl_display_get_event_loop(session->display);
|
|
||||||
while (!handler.added) {
|
|
||||||
int ret = wl_event_loop_dispatch(event_loop, (int)timeout);
|
|
||||||
if (ret < 0) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Failed to wait for DRM card device: "
|
|
||||||
"wl_event_loop_dispatch failed");
|
|
||||||
udev_enumerate_unref(en);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t now = get_current_time_ms();
|
|
||||||
if (now >= started_at + WAIT_GPU_TIMEOUT) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
timeout = started_at + WAIT_GPU_TIMEOUT - now;
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_list_remove(&handler.listener.link);
|
|
||||||
|
|
||||||
en = enumerate_drm_cards(session->udev);
|
|
||||||
if (!en) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct udev_list_entry *entry;
|
struct udev_list_entry *entry;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
@ -541,18 +334,17 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_device *wlr_dev =
|
int fd = open_if_kms(session, udev_device_get_devnode(dev));
|
||||||
session_open_if_kms(session, udev_device_get_devnode(dev));
|
if (fd < 0) {
|
||||||
if (!wlr_dev) {
|
|
||||||
udev_device_unref(dev);
|
udev_device_unref(dev);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
udev_device_unref(dev);
|
udev_device_unref(dev);
|
||||||
|
|
||||||
ret[i] = wlr_dev;
|
ret[i] = fd;
|
||||||
if (is_boot_vga) {
|
if (is_boot_vga) {
|
||||||
struct wlr_device *tmp = ret[0];
|
int tmp = ret[0];
|
||||||
ret[0] = ret[i];
|
ret[0] = ret[i];
|
||||||
ret[i] = tmp;
|
ret[i] = tmp;
|
||||||
}
|
}
|
||||||
|
@ -563,4 +355,5 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session,
|
||||||
udev_enumerate_unref(en);
|
udev_enumerate_unref(en);
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,51 +1,24 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <fcntl.h>
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <drm_fourcc.h>
|
#include <wlr/config.h>
|
||||||
#include <wayland-server-core.h>
|
|
||||||
#include <xf86drm.h>
|
#include <EGL/egl.h>
|
||||||
|
#include <EGL/eglext.h>
|
||||||
|
#include <wayland-server.h>
|
||||||
|
|
||||||
#include <wlr/backend/interface.h>
|
#include <wlr/backend/interface.h>
|
||||||
#include <wlr/interfaces/wlr_input_device.h>
|
#include <wlr/interfaces/wlr_input_device.h>
|
||||||
#include <wlr/interfaces/wlr_output.h>
|
#include <wlr/interfaces/wlr_output.h>
|
||||||
|
#include <wlr/render/egl.h>
|
||||||
|
#include <wlr/render/gles2.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
|
||||||
#include "backend/wayland.h"
|
#include "backend/wayland.h"
|
||||||
#include "render/drm_format_set.h"
|
|
||||||
#include "render/pixel_format.h"
|
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
|
|
||||||
#include "drm-client-protocol.h"
|
|
||||||
#include "linux-dmabuf-unstable-v1-client-protocol.h"
|
|
||||||
#include "pointer-gestures-unstable-v1-client-protocol.h"
|
|
||||||
#include "presentation-time-client-protocol.h"
|
|
||||||
#include "xdg-activation-v1-client-protocol.h"
|
|
||||||
#include "xdg-decoration-unstable-v1-client-protocol.h"
|
|
||||||
#include "xdg-shell-client-protocol.h"
|
#include "xdg-shell-client-protocol.h"
|
||||||
#include "tablet-unstable-v2-client-protocol.h"
|
|
||||||
#include "relative-pointer-unstable-v1-client-protocol.h"
|
|
||||||
|
|
||||||
struct wlr_wl_linux_dmabuf_feedback_v1 {
|
|
||||||
struct wlr_wl_backend *backend;
|
|
||||||
dev_t main_device_id;
|
|
||||||
struct wlr_wl_linux_dmabuf_v1_table_entry *format_table;
|
|
||||||
size_t format_table_size;
|
|
||||||
|
|
||||||
dev_t tranche_target_device_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wlr_wl_linux_dmabuf_v1_table_entry {
|
|
||||||
uint32_t format;
|
|
||||||
uint32_t pad; /* unused */
|
|
||||||
uint64_t modifier;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend) {
|
struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend) {
|
||||||
assert(wlr_backend_is_wl(backend));
|
assert(wlr_backend_is_wl(backend));
|
||||||
|
@ -56,31 +29,24 @@ static int dispatch_events(int fd, uint32_t mask, void *data) {
|
||||||
struct wlr_wl_backend *wl = data;
|
struct wlr_wl_backend *wl = data;
|
||||||
|
|
||||||
if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) {
|
if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) {
|
||||||
if (mask & WL_EVENT_ERROR) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to read from remote Wayland display");
|
|
||||||
}
|
|
||||||
wl_display_terminate(wl->local_display);
|
wl_display_terminate(wl->local_display);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
if (mask & WL_EVENT_READABLE) {
|
if (mask & WL_EVENT_READABLE) {
|
||||||
count = wl_display_dispatch(wl->remote_display);
|
return wl_display_dispatch(wl->remote_display);
|
||||||
}
|
}
|
||||||
if (mask & WL_EVENT_WRITABLE) {
|
if (mask & WL_EVENT_WRITABLE) {
|
||||||
wl_display_flush(wl->remote_display);
|
wl_display_flush(wl->remote_display);
|
||||||
}
|
|
||||||
if (mask == 0) {
|
|
||||||
count = wl_display_dispatch_pending(wl->remote_display);
|
|
||||||
wl_display_flush(wl->remote_display);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count < 0) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to dispatch remote Wayland display");
|
|
||||||
wl_display_terminate(wl->local_display);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return count;
|
if (mask == 0) {
|
||||||
|
int count = wl_display_dispatch_pending(wl->remote_display);
|
||||||
|
wl_display_flush(wl->remote_display);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xdg_wm_base_handle_ping(void *data,
|
static void xdg_wm_base_handle_ping(void *data,
|
||||||
|
@ -92,302 +58,26 @@ static const struct xdg_wm_base_listener xdg_wm_base_listener = {
|
||||||
xdg_wm_base_handle_ping,
|
xdg_wm_base_handle_ping,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void presentation_handle_clock_id(void *data,
|
|
||||||
struct wp_presentation *presentation, uint32_t clock) {
|
|
||||||
struct wlr_wl_backend *wl = data;
|
|
||||||
wl->presentation_clock = clock;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wp_presentation_listener presentation_listener = {
|
|
||||||
.clock_id = presentation_handle_clock_id,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void linux_dmabuf_v1_handle_format(void *data,
|
|
||||||
struct zwp_linux_dmabuf_v1 *linux_dmabuf_v1, uint32_t format) {
|
|
||||||
// Note, this event is deprecated
|
|
||||||
struct wlr_wl_backend *wl = data;
|
|
||||||
|
|
||||||
wlr_drm_format_set_add(&wl->linux_dmabuf_v1_formats, format,
|
|
||||||
DRM_FORMAT_MOD_INVALID);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void linux_dmabuf_v1_handle_modifier(void *data,
|
|
||||||
struct zwp_linux_dmabuf_v1 *linux_dmabuf_v1, uint32_t format,
|
|
||||||
uint32_t modifier_hi, uint32_t modifier_lo) {
|
|
||||||
struct wlr_wl_backend *wl = data;
|
|
||||||
|
|
||||||
uint64_t modifier = ((uint64_t)modifier_hi << 32) | modifier_lo;
|
|
||||||
wlr_drm_format_set_add(&wl->linux_dmabuf_v1_formats, format, modifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_v1_listener = {
|
|
||||||
.format = linux_dmabuf_v1_handle_format,
|
|
||||||
.modifier = linux_dmabuf_v1_handle_modifier,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void linux_dmabuf_feedback_v1_handle_done(void *data,
|
|
||||||
struct zwp_linux_dmabuf_feedback_v1 *feedback) {
|
|
||||||
// This space is intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void linux_dmabuf_feedback_v1_handle_format_table(void *data,
|
|
||||||
struct zwp_linux_dmabuf_feedback_v1 *feedback, int fd, uint32_t size) {
|
|
||||||
struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data;
|
|
||||||
|
|
||||||
feedback_data->format_table = NULL;
|
|
||||||
|
|
||||||
void *table_data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
||||||
if (table_data == MAP_FAILED) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "failed to mmap DMA-BUF format table");
|
|
||||||
} else {
|
|
||||||
feedback_data->format_table = table_data;
|
|
||||||
feedback_data->format_table_size = size;
|
|
||||||
}
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void linux_dmabuf_feedback_v1_handle_main_device(void *data,
|
|
||||||
struct zwp_linux_dmabuf_feedback_v1 *feedback,
|
|
||||||
struct wl_array *dev_id_arr) {
|
|
||||||
struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data;
|
|
||||||
|
|
||||||
dev_t dev_id;
|
|
||||||
assert(dev_id_arr->size == sizeof(dev_id));
|
|
||||||
memcpy(&dev_id, dev_id_arr->data, sizeof(dev_id));
|
|
||||||
|
|
||||||
feedback_data->main_device_id = dev_id;
|
|
||||||
|
|
||||||
drmDevice *device = NULL;
|
|
||||||
if (drmGetDeviceFromDevId(dev_id, 0, &device) != 0) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "drmGetDeviceFromDevId failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *name = NULL;
|
|
||||||
if (device->available_nodes & (1 << DRM_NODE_RENDER)) {
|
|
||||||
name = device->nodes[DRM_NODE_RENDER];
|
|
||||||
} else {
|
|
||||||
// Likely a split display/render setup. Pick the primary node and hope
|
|
||||||
// Mesa will open the right render node under-the-hood.
|
|
||||||
assert(device->available_nodes & (1 << DRM_NODE_PRIMARY));
|
|
||||||
name = device->nodes[DRM_NODE_PRIMARY];
|
|
||||||
wlr_log(WLR_DEBUG, "DRM device %s has no render node, "
|
|
||||||
"falling back to primary node", name);
|
|
||||||
}
|
|
||||||
|
|
||||||
feedback_data->backend->drm_render_name = strdup(name);
|
|
||||||
|
|
||||||
drmFreeDevice(&device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void linux_dmabuf_feedback_v1_handle_tranche_done(void *data,
|
|
||||||
struct zwp_linux_dmabuf_feedback_v1 *feedback) {
|
|
||||||
struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data;
|
|
||||||
feedback_data->tranche_target_device_id = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void linux_dmabuf_feedback_v1_handle_tranche_target_device(void *data,
|
|
||||||
struct zwp_linux_dmabuf_feedback_v1 *feedback,
|
|
||||||
struct wl_array *dev_id_arr) {
|
|
||||||
struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data;
|
|
||||||
|
|
||||||
dev_t dev_id;
|
|
||||||
assert(dev_id_arr->size == sizeof(dev_id));
|
|
||||||
memcpy(&dev_id, dev_id_arr->data, sizeof(dev_id));
|
|
||||||
|
|
||||||
feedback_data->tranche_target_device_id = dev_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void linux_dmabuf_feedback_v1_handle_tranche_formats(void *data,
|
|
||||||
struct zwp_linux_dmabuf_feedback_v1 *feedback,
|
|
||||||
struct wl_array *indices_arr) {
|
|
||||||
struct wlr_wl_linux_dmabuf_feedback_v1 *feedback_data = data;
|
|
||||||
|
|
||||||
if (feedback_data->format_table == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (feedback_data->tranche_target_device_id != feedback_data->main_device_id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t table_cap = feedback_data->format_table_size /
|
|
||||||
sizeof(struct wlr_wl_linux_dmabuf_v1_table_entry);
|
|
||||||
uint16_t *index_ptr;
|
|
||||||
wl_array_for_each(index_ptr, indices_arr) {
|
|
||||||
assert(*index_ptr < table_cap);
|
|
||||||
const struct wlr_wl_linux_dmabuf_v1_table_entry *entry =
|
|
||||||
&feedback_data->format_table[*index_ptr];
|
|
||||||
wlr_drm_format_set_add(&feedback_data->backend->linux_dmabuf_v1_formats,
|
|
||||||
entry->format, entry->modifier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void linux_dmabuf_feedback_v1_handle_tranche_flags(void *data,
|
|
||||||
struct zwp_linux_dmabuf_feedback_v1 *feedback, uint32_t flags) {
|
|
||||||
// TODO: handle SCANOUT flag
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_linux_dmabuf_feedback_v1_listener
|
|
||||||
linux_dmabuf_feedback_v1_listener = {
|
|
||||||
.done = linux_dmabuf_feedback_v1_handle_done,
|
|
||||||
.format_table = linux_dmabuf_feedback_v1_handle_format_table,
|
|
||||||
.main_device = linux_dmabuf_feedback_v1_handle_main_device,
|
|
||||||
.tranche_done = linux_dmabuf_feedback_v1_handle_tranche_done,
|
|
||||||
.tranche_target_device = linux_dmabuf_feedback_v1_handle_tranche_target_device,
|
|
||||||
.tranche_formats = linux_dmabuf_feedback_v1_handle_tranche_formats,
|
|
||||||
.tranche_flags = linux_dmabuf_feedback_v1_handle_tranche_flags,
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool device_has_name(const drmDevice *device, const char *name) {
|
|
||||||
for (size_t i = 0; i < DRM_NODE_MAX; i++) {
|
|
||||||
if (!(device->available_nodes & (1 << i))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (strcmp(device->nodes[i], name) == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *get_render_name(const char *name) {
|
|
||||||
uint32_t flags = 0;
|
|
||||||
int devices_len = drmGetDevices2(flags, NULL, 0);
|
|
||||||
if (devices_len < 0) {
|
|
||||||
wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
drmDevice **devices = calloc(devices_len, sizeof(drmDevice *));
|
|
||||||
if (devices == NULL) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
devices_len = drmGetDevices2(flags, devices, devices_len);
|
|
||||||
if (devices_len < 0) {
|
|
||||||
free(devices);
|
|
||||||
wlr_log(WLR_ERROR, "drmGetDevices2 failed: %s", strerror(-devices_len));
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const drmDevice *match = NULL;
|
|
||||||
for (int i = 0; i < devices_len; i++) {
|
|
||||||
if (device_has_name(devices[i], name)) {
|
|
||||||
match = devices[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char *render_name = NULL;
|
|
||||||
if (match == NULL) {
|
|
||||||
wlr_log(WLR_ERROR, "Cannot find DRM device %s", name);
|
|
||||||
} else if (!(match->available_nodes & (1 << DRM_NODE_RENDER))) {
|
|
||||||
// Likely a split display/render setup. Pick the primary node and hope
|
|
||||||
// Mesa will open the right render node under-the-hood.
|
|
||||||
wlr_log(WLR_DEBUG, "DRM device %s has no render node, "
|
|
||||||
"falling back to primary node", name);
|
|
||||||
assert(match->available_nodes & (1 << DRM_NODE_PRIMARY));
|
|
||||||
render_name = strdup(match->nodes[DRM_NODE_PRIMARY]);
|
|
||||||
} else {
|
|
||||||
render_name = strdup(match->nodes[DRM_NODE_RENDER]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < devices_len; i++) {
|
|
||||||
drmFreeDevice(&devices[i]);
|
|
||||||
}
|
|
||||||
free(devices);
|
|
||||||
|
|
||||||
return render_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void legacy_drm_handle_device(void *data, struct wl_drm *drm,
|
|
||||||
const char *name) {
|
|
||||||
struct wlr_wl_backend *wl = data;
|
|
||||||
wl->drm_render_name = get_render_name(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void legacy_drm_handle_format(void *data, struct wl_drm *drm,
|
|
||||||
uint32_t format) {
|
|
||||||
// This space is intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void legacy_drm_handle_authenticated(void *data, struct wl_drm *drm) {
|
|
||||||
// This space is intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void legacy_drm_handle_capabilities(void *data, struct wl_drm *drm,
|
|
||||||
uint32_t caps) {
|
|
||||||
// This space is intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_drm_listener legacy_drm_listener = {
|
|
||||||
.device = legacy_drm_handle_device,
|
|
||||||
.format = legacy_drm_handle_format,
|
|
||||||
.authenticated = legacy_drm_handle_authenticated,
|
|
||||||
.capabilities = legacy_drm_handle_capabilities,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void shm_handle_format(void *data, struct wl_shm *shm,
|
|
||||||
uint32_t shm_format) {
|
|
||||||
struct wlr_wl_backend *wl = data;
|
|
||||||
uint32_t drm_format = convert_wl_shm_format_to_drm(shm_format);
|
|
||||||
wlr_drm_format_set_add(&wl->shm_formats, drm_format, DRM_FORMAT_MOD_INVALID);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_shm_listener shm_listener = {
|
|
||||||
.format = shm_handle_format,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void registry_global(void *data, struct wl_registry *registry,
|
static void registry_global(void *data, struct wl_registry *registry,
|
||||||
uint32_t name, const char *iface, uint32_t version) {
|
uint32_t name, const char *iface, uint32_t version) {
|
||||||
struct wlr_wl_backend *wl = data;
|
struct wlr_wl_backend *wl = data;
|
||||||
|
|
||||||
wlr_log(WLR_DEBUG, "Remote wayland global: %s v%" PRIu32, iface, version);
|
wlr_log(WLR_DEBUG, "Remote wayland global: %s v%d", iface, version);
|
||||||
|
|
||||||
if (strcmp(iface, wl_compositor_interface.name) == 0) {
|
if (strcmp(iface, wl_compositor_interface.name) == 0) {
|
||||||
wl->compositor = wl_registry_bind(registry, name,
|
wl->compositor = wl_registry_bind(registry, name,
|
||||||
&wl_compositor_interface, 4);
|
&wl_compositor_interface, 4);
|
||||||
} else if (strcmp(iface, wl_seat_interface.name) == 0) {
|
} else if (strcmp(iface, wl_seat_interface.name) == 0) {
|
||||||
struct wl_seat *wl_seat = wl_registry_bind(registry, name,
|
wl->seat = wl_registry_bind(registry, name,
|
||||||
&wl_seat_interface, 5);
|
&wl_seat_interface, 5);
|
||||||
if (!create_wl_seat(wl_seat, wl)) {
|
wl_seat_add_listener(wl->seat, &seat_listener, wl);
|
||||||
wl_seat_destroy(wl_seat);
|
} else if (strcmp(iface, wl_shm_interface.name) == 0) {
|
||||||
}
|
wl->shm = wl_registry_bind(registry, name,
|
||||||
|
&wl_shm_interface, 1);
|
||||||
} else if (strcmp(iface, xdg_wm_base_interface.name) == 0) {
|
} else if (strcmp(iface, xdg_wm_base_interface.name) == 0) {
|
||||||
wl->xdg_wm_base = wl_registry_bind(registry, name,
|
wl->xdg_wm_base = wl_registry_bind(registry, name,
|
||||||
&xdg_wm_base_interface, 1);
|
&xdg_wm_base_interface, 1);
|
||||||
xdg_wm_base_add_listener(wl->xdg_wm_base, &xdg_wm_base_listener, NULL);
|
xdg_wm_base_add_listener(wl->xdg_wm_base, &xdg_wm_base_listener, NULL);
|
||||||
} else if (strcmp(iface, zxdg_decoration_manager_v1_interface.name) == 0) {
|
|
||||||
wl->zxdg_decoration_manager_v1 = wl_registry_bind(registry, name,
|
|
||||||
&zxdg_decoration_manager_v1_interface, 1);
|
|
||||||
} else if (strcmp(iface, zwp_pointer_gestures_v1_interface.name) == 0) {
|
|
||||||
wl->zwp_pointer_gestures_v1 = wl_registry_bind(registry, name,
|
|
||||||
&zwp_pointer_gestures_v1_interface, version < 3 ? version : 3);
|
|
||||||
} else if (strcmp(iface, wp_presentation_interface.name) == 0) {
|
|
||||||
wl->presentation = wl_registry_bind(registry, name,
|
|
||||||
&wp_presentation_interface, 1);
|
|
||||||
wp_presentation_add_listener(wl->presentation,
|
|
||||||
&presentation_listener, wl);
|
|
||||||
} else if (strcmp(iface, zwp_tablet_manager_v2_interface.name) == 0) {
|
|
||||||
wl->tablet_manager = wl_registry_bind(registry, name,
|
|
||||||
&zwp_tablet_manager_v2_interface, 1);
|
|
||||||
} else if (strcmp(iface, zwp_linux_dmabuf_v1_interface.name) == 0 &&
|
|
||||||
version >= 3) {
|
|
||||||
wl->zwp_linux_dmabuf_v1 = wl_registry_bind(registry, name,
|
|
||||||
&zwp_linux_dmabuf_v1_interface, version >= 4 ? 4 : version);
|
|
||||||
zwp_linux_dmabuf_v1_add_listener(wl->zwp_linux_dmabuf_v1,
|
|
||||||
&linux_dmabuf_v1_listener, wl);
|
|
||||||
} else if (strcmp(iface, zwp_relative_pointer_manager_v1_interface.name) == 0) {
|
|
||||||
wl->zwp_relative_pointer_manager_v1 = wl_registry_bind(registry, name,
|
|
||||||
&zwp_relative_pointer_manager_v1_interface, 1);
|
|
||||||
} else if (strcmp(iface, wl_drm_interface.name) == 0) {
|
|
||||||
wl->legacy_drm = wl_registry_bind(registry, name, &wl_drm_interface, 1);
|
|
||||||
wl_drm_add_listener(wl->legacy_drm, &legacy_drm_listener, wl);
|
|
||||||
} else if (strcmp(iface, wl_shm_interface.name) == 0) {
|
|
||||||
wl->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
|
|
||||||
wl_shm_add_listener(wl->shm, &shm_listener, wl);
|
|
||||||
} else if (strcmp(iface, xdg_activation_v1_interface.name) == 0) {
|
|
||||||
wl->activation_v1 = wl_registry_bind(registry, name,
|
|
||||||
&xdg_activation_v1_interface, 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,19 +98,12 @@ static const struct wl_registry_listener registry_listener = {
|
||||||
*/
|
*/
|
||||||
static bool backend_start(struct wlr_backend *backend) {
|
static bool backend_start(struct wlr_backend *backend) {
|
||||||
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
|
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
|
||||||
wlr_log(WLR_INFO, "Starting Wayland backend");
|
wlr_log(WLR_INFO, "Initializating wayland backend");
|
||||||
|
|
||||||
wl->started = true;
|
wl->started = true;
|
||||||
|
|
||||||
struct wlr_wl_seat *seat;
|
if (wl->keyboard) {
|
||||||
wl_list_for_each(seat, &wl->seats, link) {
|
create_wl_keyboard(wl->keyboard, wl);
|
||||||
if (seat->keyboard) {
|
|
||||||
create_wl_keyboard(seat);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wl->tablet_manager) {
|
|
||||||
wl_add_tablet_seat(wl->tablet_manager, seat);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < wl->requested_outputs; ++i) {
|
for (size_t i = 0; i < wl->requested_outputs; ++i) {
|
||||||
|
@ -442,78 +125,47 @@ static void backend_destroy(struct wlr_backend *backend) {
|
||||||
wlr_output_destroy(&output->wlr_output);
|
wlr_output_destroy(&output->wlr_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_wl_input_device *input_device, *tmp_input_device;
|
struct wlr_input_device *input_device, *tmp_input_device;
|
||||||
wl_list_for_each_safe(input_device, tmp_input_device, &wl->devices, link) {
|
wl_list_for_each_safe(input_device, tmp_input_device, &wl->devices, link) {
|
||||||
wlr_input_device_destroy(&input_device->wlr_input_device);
|
wlr_input_device_destroy(input_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_wl_buffer *buffer, *tmp_buffer;
|
wlr_signal_emit_safe(&wl->backend.events.destroy, &wl->backend);
|
||||||
wl_list_for_each_safe(buffer, tmp_buffer, &wl->buffers, link) {
|
|
||||||
destroy_wl_buffer(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_backend_finish(backend);
|
|
||||||
|
|
||||||
wl_list_remove(&wl->local_display_destroy.link);
|
wl_list_remove(&wl->local_display_destroy.link);
|
||||||
|
|
||||||
|
free(wl->seat_name);
|
||||||
|
|
||||||
wl_event_source_remove(wl->remote_display_src);
|
wl_event_source_remove(wl->remote_display_src);
|
||||||
|
|
||||||
close(wl->drm_fd);
|
wlr_renderer_destroy(wl->renderer);
|
||||||
|
wlr_egl_finish(&wl->egl);
|
||||||
|
|
||||||
wlr_drm_format_set_finish(&wl->shm_formats);
|
if (wl->pointer) {
|
||||||
wlr_drm_format_set_finish(&wl->linux_dmabuf_v1_formats);
|
wl_pointer_destroy(wl->pointer);
|
||||||
|
|
||||||
destroy_wl_seats(wl);
|
|
||||||
if (wl->zxdg_decoration_manager_v1) {
|
|
||||||
zxdg_decoration_manager_v1_destroy(wl->zxdg_decoration_manager_v1);
|
|
||||||
}
|
}
|
||||||
if (wl->zwp_pointer_gestures_v1) {
|
if (wl->seat) {
|
||||||
zwp_pointer_gestures_v1_destroy(wl->zwp_pointer_gestures_v1);
|
wl_seat_destroy(wl->seat);
|
||||||
}
|
|
||||||
if (wl->presentation) {
|
|
||||||
wp_presentation_destroy(wl->presentation);
|
|
||||||
}
|
|
||||||
if (wl->zwp_linux_dmabuf_v1) {
|
|
||||||
zwp_linux_dmabuf_v1_destroy(wl->zwp_linux_dmabuf_v1);
|
|
||||||
}
|
}
|
||||||
if (wl->shm) {
|
if (wl->shm) {
|
||||||
wl_shm_destroy(wl->shm);
|
wl_shm_destroy(wl->shm);
|
||||||
}
|
}
|
||||||
if (wl->zwp_relative_pointer_manager_v1) {
|
|
||||||
zwp_relative_pointer_manager_v1_destroy(wl->zwp_relative_pointer_manager_v1);
|
|
||||||
}
|
|
||||||
free(wl->drm_render_name);
|
|
||||||
free(wl->activation_token);
|
|
||||||
xdg_wm_base_destroy(wl->xdg_wm_base);
|
xdg_wm_base_destroy(wl->xdg_wm_base);
|
||||||
wl_compositor_destroy(wl->compositor);
|
wl_compositor_destroy(wl->compositor);
|
||||||
wl_registry_destroy(wl->registry);
|
wl_registry_destroy(wl->registry);
|
||||||
wl_display_flush(wl->remote_display);
|
|
||||||
wl_display_disconnect(wl->remote_display);
|
wl_display_disconnect(wl->remote_display);
|
||||||
free(wl);
|
free(wl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static clockid_t backend_get_presentation_clock(struct wlr_backend *backend) {
|
static struct wlr_renderer *backend_get_renderer(struct wlr_backend *backend) {
|
||||||
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
|
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
|
||||||
return wl->presentation_clock;
|
return wl->renderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int backend_get_drm_fd(struct wlr_backend *backend) {
|
static struct wlr_backend_impl backend_impl = {
|
||||||
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
|
|
||||||
return wl->drm_fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t get_buffer_caps(struct wlr_backend *backend) {
|
|
||||||
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
|
|
||||||
return (wl->zwp_linux_dmabuf_v1 ? WLR_BUFFER_CAP_DMABUF : 0)
|
|
||||||
| (wl->shm ? WLR_BUFFER_CAP_SHM : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wlr_backend_impl backend_impl = {
|
|
||||||
.start = backend_start,
|
.start = backend_start,
|
||||||
.destroy = backend_destroy,
|
.destroy = backend_destroy,
|
||||||
.get_presentation_clock = backend_get_presentation_clock,
|
.get_renderer = backend_get_renderer,
|
||||||
.get_drm_fd = backend_get_drm_fd,
|
|
||||||
.get_buffer_caps = get_buffer_caps,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool wlr_backend_is_wl(struct wlr_backend *b) {
|
bool wlr_backend_is_wl(struct wlr_backend *b) {
|
||||||
|
@ -527,7 +179,7 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
|
struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
|
||||||
const char *remote) {
|
const char *remote, wlr_renderer_create_func_t create_renderer_func) {
|
||||||
wlr_log(WLR_INFO, "Creating wayland backend");
|
wlr_log(WLR_INFO, "Creating wayland backend");
|
||||||
|
|
||||||
struct wlr_wl_backend *wl = calloc(1, sizeof(*wl));
|
struct wlr_wl_backend *wl = calloc(1, sizeof(*wl));
|
||||||
|
@ -541,9 +193,6 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
|
||||||
wl->local_display = display;
|
wl->local_display = display;
|
||||||
wl_list_init(&wl->devices);
|
wl_list_init(&wl->devices);
|
||||||
wl_list_init(&wl->outputs);
|
wl_list_init(&wl->outputs);
|
||||||
wl_list_init(&wl->seats);
|
|
||||||
wl_list_init(&wl->buffers);
|
|
||||||
wl->presentation_clock = CLOCK_MONOTONIC;
|
|
||||||
|
|
||||||
wl->remote_display = wl_display_connect(remote);
|
wl->remote_display = wl_display_connect(remote);
|
||||||
if (!wl->remote_display) {
|
if (!wl->remote_display) {
|
||||||
|
@ -556,9 +205,10 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
|
||||||
wlr_log_errno(WLR_ERROR, "Could not obtain reference to remote registry");
|
wlr_log_errno(WLR_ERROR, "Could not obtain reference to remote registry");
|
||||||
goto error_display;
|
goto error_display;
|
||||||
}
|
}
|
||||||
wl_registry_add_listener(wl->registry, ®istry_listener, wl);
|
|
||||||
|
|
||||||
wl_display_roundtrip(wl->remote_display); // get globals
|
wl_registry_add_listener(wl->registry, ®istry_listener, wl);
|
||||||
|
wl_display_dispatch(wl->remote_display);
|
||||||
|
wl_display_roundtrip(wl->remote_display);
|
||||||
|
|
||||||
if (!wl->compositor) {
|
if (!wl->compositor) {
|
||||||
wlr_log(WLR_ERROR,
|
wlr_log(WLR_ERROR,
|
||||||
|
@ -571,38 +221,10 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
|
||||||
goto error_registry;
|
goto error_registry;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct zwp_linux_dmabuf_feedback_v1 *linux_dmabuf_feedback_v1 = NULL;
|
|
||||||
struct wlr_wl_linux_dmabuf_feedback_v1 feedback_data = { .backend = wl };
|
|
||||||
if (wl->zwp_linux_dmabuf_v1 != NULL &&
|
|
||||||
zwp_linux_dmabuf_v1_get_version(wl->zwp_linux_dmabuf_v1) >=
|
|
||||||
ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION) {
|
|
||||||
linux_dmabuf_feedback_v1 =
|
|
||||||
zwp_linux_dmabuf_v1_get_default_feedback(wl->zwp_linux_dmabuf_v1);
|
|
||||||
if (linux_dmabuf_feedback_v1 == NULL) {
|
|
||||||
wlr_log(WLR_ERROR, "Allocation failed");
|
|
||||||
goto error_registry;
|
|
||||||
}
|
|
||||||
zwp_linux_dmabuf_feedback_v1_add_listener(linux_dmabuf_feedback_v1,
|
|
||||||
&linux_dmabuf_feedback_v1_listener, &feedback_data);
|
|
||||||
|
|
||||||
if (wl->legacy_drm != NULL) {
|
|
||||||
wl_drm_destroy(wl->legacy_drm);
|
|
||||||
wl->legacy_drm = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_display_roundtrip(wl->remote_display); // get linux-dmabuf formats
|
|
||||||
|
|
||||||
if (feedback_data.format_table != NULL) {
|
|
||||||
munmap(feedback_data.format_table, feedback_data.format_table_size);
|
|
||||||
}
|
|
||||||
if (linux_dmabuf_feedback_v1 != NULL) {
|
|
||||||
zwp_linux_dmabuf_feedback_v1_destroy(linux_dmabuf_feedback_v1);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_event_loop *loop = wl_display_get_event_loop(wl->local_display);
|
struct wl_event_loop *loop = wl_display_get_event_loop(wl->local_display);
|
||||||
int fd = wl_display_get_fd(wl->remote_display);
|
int fd = wl_display_get_fd(wl->remote_display);
|
||||||
wl->remote_display_src = wl_event_loop_add_fd(loop, fd, WL_EVENT_READABLE,
|
int events = WL_EVENT_READABLE | WL_EVENT_ERROR | WL_EVENT_HANGUP;
|
||||||
|
wl->remote_display_src = wl_event_loop_add_fd(loop, fd, events,
|
||||||
dispatch_events, wl);
|
dispatch_events, wl);
|
||||||
if (!wl->remote_display_src) {
|
if (!wl->remote_display_src) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create event source");
|
wlr_log(WLR_ERROR, "Failed to create event source");
|
||||||
|
@ -610,33 +232,35 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
|
||||||
}
|
}
|
||||||
wl_event_source_check(wl->remote_display_src);
|
wl_event_source_check(wl->remote_display_src);
|
||||||
|
|
||||||
if (wl->drm_render_name != NULL) {
|
static EGLint config_attribs[] = {
|
||||||
wlr_log(WLR_DEBUG, "Opening DRM render node %s", wl->drm_render_name);
|
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||||
wl->drm_fd = open(wl->drm_render_name, O_RDWR | O_NONBLOCK | O_CLOEXEC);
|
EGL_RED_SIZE, 1,
|
||||||
if (wl->drm_fd < 0) {
|
EGL_GREEN_SIZE, 1,
|
||||||
wlr_log_errno(WLR_ERROR, "Failed to open DRM render node %s",
|
EGL_BLUE_SIZE, 1,
|
||||||
wl->drm_render_name);
|
EGL_ALPHA_SIZE, 1,
|
||||||
goto error_remote_display_src;
|
EGL_NONE,
|
||||||
}
|
};
|
||||||
} else {
|
|
||||||
wl->drm_fd = -1;
|
if (!create_renderer_func) {
|
||||||
|
create_renderer_func = wlr_renderer_autocreate;
|
||||||
|
}
|
||||||
|
|
||||||
|
wl->renderer = create_renderer_func(&wl->egl, EGL_PLATFORM_WAYLAND_EXT,
|
||||||
|
wl->remote_display, config_attribs, WL_SHM_FORMAT_ARGB8888);
|
||||||
|
|
||||||
|
if (!wl->renderer) {
|
||||||
|
wlr_log(WLR_ERROR, "Could not create renderer");
|
||||||
|
goto error_event;
|
||||||
}
|
}
|
||||||
|
|
||||||
wl->local_display_destroy.notify = handle_display_destroy;
|
wl->local_display_destroy.notify = handle_display_destroy;
|
||||||
wl_display_add_destroy_listener(display, &wl->local_display_destroy);
|
wl_display_add_destroy_listener(display, &wl->local_display_destroy);
|
||||||
|
|
||||||
const char *token = getenv("XDG_ACTIVATION_TOKEN");
|
|
||||||
if (token != NULL) {
|
|
||||||
wl->activation_token = strdup(token);
|
|
||||||
unsetenv("XDG_ACTIVATION_TOKEN");
|
|
||||||
}
|
|
||||||
|
|
||||||
return &wl->backend;
|
return &wl->backend;
|
||||||
|
|
||||||
error_remote_display_src:
|
error_event:
|
||||||
wl_event_source_remove(wl->remote_display_src);
|
wl_event_source_remove(wl->remote_display_src);
|
||||||
error_registry:
|
error_registry:
|
||||||
free(wl->drm_render_name);
|
|
||||||
if (wl->compositor) {
|
if (wl->compositor) {
|
||||||
wl_compositor_destroy(wl->compositor);
|
wl_compositor_destroy(wl->compositor);
|
||||||
}
|
}
|
||||||
|
@ -647,12 +271,6 @@ error_registry:
|
||||||
error_display:
|
error_display:
|
||||||
wl_display_disconnect(wl->remote_display);
|
wl_display_disconnect(wl->remote_display);
|
||||||
error_wl:
|
error_wl:
|
||||||
wlr_backend_finish(&wl->backend);
|
|
||||||
free(wl);
|
free(wl);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wl_display *wlr_wl_backend_get_remote_display(struct wlr_backend *backend) {
|
|
||||||
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
|
|
||||||
return wl->remote_display;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
wayland_client = dependency('wayland-client',
|
|
||||||
fallback: ['wayland', 'wayland_client_dep'],
|
|
||||||
default_options: wayland_project_options,
|
|
||||||
)
|
|
||||||
wlr_deps += wayland_client
|
|
||||||
|
|
||||||
wlr_files += files(
|
|
||||||
'backend.c',
|
|
||||||
'output.c',
|
|
||||||
'seat.c',
|
|
||||||
'tablet_v2.c',
|
|
||||||
)
|
|
||||||
|
|
||||||
client_protos = [
|
|
||||||
'drm',
|
|
||||||
'linux-dmabuf-unstable-v1',
|
|
||||||
'pointer-gestures-unstable-v1',
|
|
||||||
'presentation-time',
|
|
||||||
'relative-pointer-unstable-v1',
|
|
||||||
'tablet-unstable-v2',
|
|
||||||
'xdg-activation-v1',
|
|
||||||
'xdg-decoration-unstable-v1',
|
|
||||||
'xdg-shell',
|
|
||||||
]
|
|
||||||
|
|
||||||
foreach proto : client_protos
|
|
||||||
wlr_files += protocols_client_header[proto]
|
|
||||||
endforeach
|
|
|
@ -7,7 +7,6 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <drm_fourcc.h>
|
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
|
|
||||||
#include <wlr/interfaces/wlr_output.h>
|
#include <wlr/interfaces/wlr_output.h>
|
||||||
|
@ -16,28 +15,17 @@
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
|
||||||
#include "backend/wayland.h"
|
#include "backend/wayland.h"
|
||||||
#include "render/pixel_format.h"
|
|
||||||
#include "render/swapchain.h"
|
|
||||||
#include "render/wlr_renderer.h"
|
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
|
|
||||||
#include "linux-dmabuf-unstable-v1-client-protocol.h"
|
|
||||||
#include "presentation-time-client-protocol.h"
|
|
||||||
#include "xdg-activation-v1-client-protocol.h"
|
|
||||||
#include "xdg-decoration-unstable-v1-client-protocol.h"
|
|
||||||
#include "xdg-shell-client-protocol.h"
|
#include "xdg-shell-client-protocol.h"
|
||||||
|
|
||||||
static const uint32_t SUPPORTED_OUTPUT_STATE =
|
|
||||||
WLR_OUTPUT_STATE_BACKEND_OPTIONAL |
|
|
||||||
WLR_OUTPUT_STATE_BUFFER |
|
|
||||||
WLR_OUTPUT_STATE_MODE;
|
|
||||||
|
|
||||||
static struct wlr_wl_output *get_wl_output_from_output(
|
static struct wlr_wl_output *get_wl_output_from_output(
|
||||||
struct wlr_output *wlr_output) {
|
struct wlr_output *wlr_output) {
|
||||||
assert(wlr_output_is_wl(wlr_output));
|
assert(wlr_output_is_wl(wlr_output));
|
||||||
return (struct wlr_wl_output *)wlr_output;
|
return (struct wlr_wl_output *)wlr_output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct wl_callback_listener frame_listener;
|
||||||
|
|
||||||
static void surface_frame_callback(void *data, struct wl_callback *cb,
|
static void surface_frame_callback(void *data, struct wl_callback *cb,
|
||||||
uint32_t time) {
|
uint32_t time) {
|
||||||
struct wlr_wl_output *output = data;
|
struct wlr_wl_output *output = data;
|
||||||
|
@ -48,319 +36,78 @@ static void surface_frame_callback(void *data, struct wl_callback *cb,
|
||||||
wlr_output_send_frame(&output->wlr_output);
|
wlr_output_send_frame(&output->wlr_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct wl_callback_listener frame_listener = {
|
static struct wl_callback_listener frame_listener = {
|
||||||
.done = surface_frame_callback
|
.done = surface_frame_callback
|
||||||
};
|
};
|
||||||
|
|
||||||
static void presentation_feedback_destroy(
|
|
||||||
struct wlr_wl_presentation_feedback *feedback) {
|
|
||||||
wl_list_remove(&feedback->link);
|
|
||||||
wp_presentation_feedback_destroy(feedback->feedback);
|
|
||||||
free(feedback);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void presentation_feedback_handle_sync_output(void *data,
|
|
||||||
struct wp_presentation_feedback *feedback, struct wl_output *output) {
|
|
||||||
// This space is intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void presentation_feedback_handle_presented(void *data,
|
|
||||||
struct wp_presentation_feedback *wp_feedback, uint32_t tv_sec_hi,
|
|
||||||
uint32_t tv_sec_lo, uint32_t tv_nsec, uint32_t refresh_ns,
|
|
||||||
uint32_t seq_hi, uint32_t seq_lo, uint32_t flags) {
|
|
||||||
struct wlr_wl_presentation_feedback *feedback = data;
|
|
||||||
|
|
||||||
struct timespec t = {
|
|
||||||
.tv_sec = ((uint64_t)tv_sec_hi << 32) | tv_sec_lo,
|
|
||||||
.tv_nsec = tv_nsec,
|
|
||||||
};
|
|
||||||
struct wlr_output_event_present event = {
|
|
||||||
.commit_seq = feedback->commit_seq,
|
|
||||||
.presented = true,
|
|
||||||
.when = &t,
|
|
||||||
.seq = ((uint64_t)seq_hi << 32) | seq_lo,
|
|
||||||
.refresh = refresh_ns,
|
|
||||||
.flags = flags,
|
|
||||||
};
|
|
||||||
wlr_output_send_present(&feedback->output->wlr_output, &event);
|
|
||||||
|
|
||||||
presentation_feedback_destroy(feedback);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void presentation_feedback_handle_discarded(void *data,
|
|
||||||
struct wp_presentation_feedback *wp_feedback) {
|
|
||||||
struct wlr_wl_presentation_feedback *feedback = data;
|
|
||||||
|
|
||||||
struct wlr_output_event_present event = {
|
|
||||||
.commit_seq = feedback->commit_seq,
|
|
||||||
.presented = false,
|
|
||||||
};
|
|
||||||
wlr_output_send_present(&feedback->output->wlr_output, &event);
|
|
||||||
|
|
||||||
presentation_feedback_destroy(feedback);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wp_presentation_feedback_listener
|
|
||||||
presentation_feedback_listener = {
|
|
||||||
.sync_output = presentation_feedback_handle_sync_output,
|
|
||||||
.presented = presentation_feedback_handle_presented,
|
|
||||||
.discarded = presentation_feedback_handle_discarded,
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool output_set_custom_mode(struct wlr_output *wlr_output,
|
static bool output_set_custom_mode(struct wlr_output *wlr_output,
|
||||||
int32_t width, int32_t height, int32_t refresh) {
|
int32_t width, int32_t height, int32_t refresh) {
|
||||||
wlr_output_update_custom_mode(wlr_output, width, height, 0);
|
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
|
||||||
|
wlr_egl_swap_buffers(&output->backend->egl, output->egl_surface, NULL);
|
||||||
|
wl_egl_window_resize(output->egl_window, width, height, 0, 0);
|
||||||
|
wlr_output_update_custom_mode(&output->wlr_output, width, height, 0);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroy_wl_buffer(struct wlr_wl_buffer *buffer) {
|
static bool output_make_current(struct wlr_output *wlr_output,
|
||||||
if (buffer == NULL) {
|
int *buffer_age) {
|
||||||
return;
|
struct wlr_wl_output *output =
|
||||||
}
|
get_wl_output_from_output(wlr_output);
|
||||||
wl_list_remove(&buffer->buffer_destroy.link);
|
return wlr_egl_make_current(&output->backend->egl, output->egl_surface,
|
||||||
wl_list_remove(&buffer->link);
|
buffer_age);
|
||||||
wl_buffer_destroy(buffer->wl_buffer);
|
|
||||||
free(buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer) {
|
static bool output_swap_buffers(struct wlr_output *wlr_output,
|
||||||
struct wlr_wl_buffer *buffer = data;
|
pixman_region32_t *damage) {
|
||||||
buffer->released = true;
|
|
||||||
wlr_buffer_unlock(buffer->buffer); // might free buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_buffer_listener buffer_listener = {
|
|
||||||
.release = buffer_handle_release,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void buffer_handle_buffer_destroy(struct wl_listener *listener,
|
|
||||||
void *data) {
|
|
||||||
struct wlr_wl_buffer *buffer =
|
|
||||||
wl_container_of(listener, buffer, buffer_destroy);
|
|
||||||
destroy_wl_buffer(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool test_buffer(struct wlr_wl_backend *wl,
|
|
||||||
struct wlr_buffer *wlr_buffer) {
|
|
||||||
struct wlr_dmabuf_attributes dmabuf;
|
|
||||||
struct wlr_shm_attributes shm;
|
|
||||||
if (wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) {
|
|
||||||
return wlr_drm_format_set_has(&wl->linux_dmabuf_v1_formats,
|
|
||||||
dmabuf.format, dmabuf.modifier);
|
|
||||||
} else if (wlr_buffer_get_shm(wlr_buffer, &shm)) {
|
|
||||||
return wlr_drm_format_set_has(&wl->shm_formats, shm.format,
|
|
||||||
DRM_FORMAT_MOD_INVALID);
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wl_buffer *import_dmabuf(struct wlr_wl_backend *wl,
|
|
||||||
struct wlr_dmabuf_attributes *dmabuf) {
|
|
||||||
uint32_t modifier_hi = dmabuf->modifier >> 32;
|
|
||||||
uint32_t modifier_lo = (uint32_t)dmabuf->modifier;
|
|
||||||
struct zwp_linux_buffer_params_v1 *params =
|
|
||||||
zwp_linux_dmabuf_v1_create_params(wl->zwp_linux_dmabuf_v1);
|
|
||||||
for (int i = 0; i < dmabuf->n_planes; i++) {
|
|
||||||
zwp_linux_buffer_params_v1_add(params, dmabuf->fd[i], i,
|
|
||||||
dmabuf->offset[i], dmabuf->stride[i], modifier_hi, modifier_lo);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_buffer *wl_buffer = zwp_linux_buffer_params_v1_create_immed(
|
|
||||||
params, dmabuf->width, dmabuf->height, dmabuf->format, 0);
|
|
||||||
// TODO: handle create() errors
|
|
||||||
return wl_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wl_buffer *import_shm(struct wlr_wl_backend *wl,
|
|
||||||
struct wlr_shm_attributes *shm) {
|
|
||||||
enum wl_shm_format wl_shm_format = convert_drm_format_to_wl_shm(shm->format);
|
|
||||||
uint32_t size = shm->stride * shm->height;
|
|
||||||
struct wl_shm_pool *pool = wl_shm_create_pool(wl->shm, shm->fd, size);
|
|
||||||
if (pool == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
struct wl_buffer *wl_buffer = wl_shm_pool_create_buffer(pool, shm->offset,
|
|
||||||
shm->width, shm->height, shm->stride, wl_shm_format);
|
|
||||||
wl_shm_pool_destroy(pool);
|
|
||||||
return wl_buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wlr_wl_buffer *create_wl_buffer(struct wlr_wl_backend *wl,
|
|
||||||
struct wlr_buffer *wlr_buffer) {
|
|
||||||
if (!test_buffer(wl, wlr_buffer)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_dmabuf_attributes dmabuf;
|
|
||||||
struct wlr_shm_attributes shm;
|
|
||||||
struct wl_buffer *wl_buffer;
|
|
||||||
if (wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf)) {
|
|
||||||
wl_buffer = import_dmabuf(wl, &dmabuf);
|
|
||||||
} else if (wlr_buffer_get_shm(wlr_buffer, &shm)) {
|
|
||||||
wl_buffer = import_shm(wl, &shm);
|
|
||||||
} else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (wl_buffer == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_wl_buffer *buffer = calloc(1, sizeof(struct wlr_wl_buffer));
|
|
||||||
if (buffer == NULL) {
|
|
||||||
wl_buffer_destroy(wl_buffer);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
buffer->wl_buffer = wl_buffer;
|
|
||||||
buffer->buffer = wlr_buffer_lock(wlr_buffer);
|
|
||||||
wl_list_insert(&wl->buffers, &buffer->link);
|
|
||||||
|
|
||||||
wl_buffer_add_listener(wl_buffer, &buffer_listener, buffer);
|
|
||||||
|
|
||||||
buffer->buffer_destroy.notify = buffer_handle_buffer_destroy;
|
|
||||||
wl_signal_add(&wlr_buffer->events.destroy, &buffer->buffer_destroy);
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wlr_wl_buffer *get_or_create_wl_buffer(struct wlr_wl_backend *wl,
|
|
||||||
struct wlr_buffer *wlr_buffer) {
|
|
||||||
struct wlr_wl_buffer *buffer;
|
|
||||||
wl_list_for_each(buffer, &wl->buffers, link) {
|
|
||||||
// We can only re-use a wlr_wl_buffer if the parent compositor has
|
|
||||||
// released it, because wl_buffer.release is per-wl_buffer, not per
|
|
||||||
// wl_surface.commit.
|
|
||||||
if (buffer->buffer == wlr_buffer && buffer->released) {
|
|
||||||
buffer->released = false;
|
|
||||||
wlr_buffer_lock(buffer->buffer);
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return create_wl_buffer(wl, wlr_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool output_test(struct wlr_output *wlr_output) {
|
|
||||||
struct wlr_wl_output *output =
|
struct wlr_wl_output *output =
|
||||||
get_wl_output_from_output(wlr_output);
|
get_wl_output_from_output(wlr_output);
|
||||||
|
|
||||||
uint32_t unsupported =
|
if (output->frame_callback != NULL) {
|
||||||
wlr_output->pending.committed & ~SUPPORTED_OUTPUT_STATE;
|
wlr_log(WLR_ERROR, "Skipping buffer swap");
|
||||||
if (unsupported != 0) {
|
|
||||||
wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32,
|
|
||||||
unsupported);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
|
output->frame_callback = wl_surface_frame(output->surface);
|
||||||
assert(wlr_output->pending.mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM);
|
wl_callback_add_listener(output->frame_callback, &frame_listener, output);
|
||||||
}
|
|
||||||
|
|
||||||
if ((wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) &&
|
if (!wlr_egl_swap_buffers(&output->backend->egl,
|
||||||
!test_buffer(output->backend, wlr_output->pending.buffer)) {
|
output->egl_surface, damage)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: if available, use the presentation-time protocol
|
||||||
|
wlr_output_send_present(wlr_output, NULL);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool output_commit(struct wlr_output *wlr_output) {
|
static void output_transform(struct wlr_output *wlr_output,
|
||||||
struct wlr_wl_output *output =
|
enum wl_output_transform transform) {
|
||||||
get_wl_output_from_output(wlr_output);
|
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
|
||||||
|
output->wlr_output.transform = transform;
|
||||||
if (!output_test(wlr_output)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
|
|
||||||
if (!output_set_custom_mode(wlr_output,
|
|
||||||
wlr_output->pending.custom_mode.width,
|
|
||||||
wlr_output->pending.custom_mode.height,
|
|
||||||
wlr_output->pending.custom_mode.refresh)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) {
|
|
||||||
struct wp_presentation_feedback *wp_feedback = NULL;
|
|
||||||
if (output->backend->presentation != NULL) {
|
|
||||||
wp_feedback = wp_presentation_feedback(output->backend->presentation,
|
|
||||||
output->surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
pixman_region32_t *damage = NULL;
|
|
||||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_DAMAGE) {
|
|
||||||
damage = &wlr_output->pending.damage;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (output->frame_callback != NULL) {
|
|
||||||
wlr_log(WLR_ERROR, "Skipping buffer swap");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
output->frame_callback = wl_surface_frame(output->surface);
|
|
||||||
wl_callback_add_listener(output->frame_callback, &frame_listener, output);
|
|
||||||
|
|
||||||
struct wlr_buffer *wlr_buffer = wlr_output->pending.buffer;
|
|
||||||
struct wlr_wl_buffer *buffer =
|
|
||||||
get_or_create_wl_buffer(output->backend, wlr_buffer);
|
|
||||||
if (buffer == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_surface_attach(output->surface, buffer->wl_buffer, 0, 0);
|
|
||||||
|
|
||||||
if (damage == NULL) {
|
|
||||||
wl_surface_damage_buffer(output->surface,
|
|
||||||
0, 0, INT32_MAX, INT32_MAX);
|
|
||||||
} else {
|
|
||||||
int rects_len;
|
|
||||||
pixman_box32_t *rects =
|
|
||||||
pixman_region32_rectangles(damage, &rects_len);
|
|
||||||
for (int i = 0; i < rects_len; i++) {
|
|
||||||
pixman_box32_t *r = &rects[i];
|
|
||||||
wl_surface_damage_buffer(output->surface, r->x1, r->y1,
|
|
||||||
r->x2 - r->x1, r->y2 - r->y1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_surface_commit(output->surface);
|
|
||||||
|
|
||||||
if (wp_feedback != NULL) {
|
|
||||||
struct wlr_wl_presentation_feedback *feedback =
|
|
||||||
calloc(1, sizeof(*feedback));
|
|
||||||
if (feedback == NULL) {
|
|
||||||
wp_presentation_feedback_destroy(wp_feedback);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
feedback->output = output;
|
|
||||||
feedback->feedback = wp_feedback;
|
|
||||||
feedback->commit_seq = output->wlr_output.commit_seq + 1;
|
|
||||||
wl_list_insert(&output->presentation_feedbacks, &feedback->link);
|
|
||||||
|
|
||||||
wp_presentation_feedback_add_listener(wp_feedback,
|
|
||||||
&presentation_feedback_listener, feedback);
|
|
||||||
} else {
|
|
||||||
struct wlr_output_event_present present_event = {
|
|
||||||
.commit_seq = wlr_output->commit_seq + 1,
|
|
||||||
.presented = true,
|
|
||||||
};
|
|
||||||
wlr_output_send_present(wlr_output, &present_event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_display_flush(output->backend->remote_display);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool output_set_cursor(struct wlr_output *wlr_output,
|
static bool output_set_cursor(struct wlr_output *wlr_output,
|
||||||
struct wlr_buffer *wlr_buffer, int hotspot_x, int hotspot_y) {
|
struct wlr_texture *texture, int32_t scale,
|
||||||
|
enum wl_output_transform transform,
|
||||||
|
int32_t hotspot_x, int32_t hotspot_y, bool update_texture) {
|
||||||
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
|
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
|
||||||
struct wlr_wl_backend *backend = output->backend;
|
struct wlr_wl_backend *backend = output->backend;
|
||||||
|
|
||||||
output->cursor.hotspot_x = hotspot_x;
|
struct wlr_box hotspot = { .x = hotspot_x, .y = hotspot_y };
|
||||||
output->cursor.hotspot_y = hotspot_y;
|
wlr_box_transform(&hotspot, &hotspot,
|
||||||
|
wlr_output_transform_invert(wlr_output->transform),
|
||||||
|
output->cursor.width, output->cursor.height);
|
||||||
|
|
||||||
|
// TODO: use output->wlr_output.transform to transform pixels and hotpot
|
||||||
|
output->cursor.hotspot_x = hotspot.x;
|
||||||
|
output->cursor.hotspot_y = hotspot.y;
|
||||||
|
|
||||||
|
if (!update_texture) {
|
||||||
|
// Update hotspot without changing cursor image
|
||||||
|
update_wl_output_cursor(output);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (output->cursor.surface == NULL) {
|
if (output->cursor.surface == NULL) {
|
||||||
output->cursor.surface =
|
output->cursor.surface =
|
||||||
|
@ -368,37 +115,53 @@ static bool output_set_cursor(struct wlr_output *wlr_output,
|
||||||
}
|
}
|
||||||
struct wl_surface *surface = output->cursor.surface;
|
struct wl_surface *surface = output->cursor.surface;
|
||||||
|
|
||||||
if (wlr_buffer != NULL) {
|
if (texture != NULL) {
|
||||||
struct wlr_wl_buffer *buffer =
|
int width, height;
|
||||||
get_or_create_wl_buffer(output->backend, wlr_buffer);
|
wlr_texture_get_size(texture, &width, &height);
|
||||||
if (buffer == NULL) {
|
width = width * wlr_output->scale / scale;
|
||||||
return false;
|
height = height * wlr_output->scale / scale;
|
||||||
}
|
|
||||||
|
|
||||||
wl_surface_attach(surface, buffer->wl_buffer, 0, 0);
|
output->cursor.width = width;
|
||||||
wl_surface_damage_buffer(surface, 0, 0, INT32_MAX, INT32_MAX);
|
output->cursor.height = height;
|
||||||
wl_surface_commit(surface);
|
|
||||||
|
if (output->cursor.egl_window == NULL) {
|
||||||
|
output->cursor.egl_window =
|
||||||
|
wl_egl_window_create(surface, width, height);
|
||||||
|
}
|
||||||
|
wl_egl_window_resize(output->cursor.egl_window, width, height, 0, 0);
|
||||||
|
|
||||||
|
EGLSurface egl_surface =
|
||||||
|
wlr_egl_create_surface(&backend->egl, output->cursor.egl_window);
|
||||||
|
|
||||||
|
wlr_egl_make_current(&backend->egl, egl_surface, NULL);
|
||||||
|
|
||||||
|
struct wlr_box cursor_box = {
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
};
|
||||||
|
|
||||||
|
float projection[9];
|
||||||
|
wlr_matrix_projection(projection, width, height, wlr_output->transform);
|
||||||
|
|
||||||
|
float matrix[9];
|
||||||
|
wlr_matrix_project_box(matrix, &cursor_box, transform, 0, projection);
|
||||||
|
|
||||||
|
wlr_renderer_begin(backend->renderer, width, height);
|
||||||
|
wlr_renderer_clear(backend->renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 });
|
||||||
|
wlr_render_texture_with_matrix(backend->renderer, texture, matrix, 1.0);
|
||||||
|
wlr_renderer_end(backend->renderer);
|
||||||
|
|
||||||
|
wlr_egl_swap_buffers(&backend->egl, egl_surface, NULL);
|
||||||
|
wlr_egl_destroy_surface(&backend->egl, egl_surface);
|
||||||
} else {
|
} else {
|
||||||
wl_surface_attach(surface, NULL, 0, 0);
|
wl_surface_attach(surface, NULL, 0, 0);
|
||||||
wl_surface_commit(surface);
|
wl_surface_commit(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
update_wl_output_cursor(output);
|
update_wl_output_cursor(output);
|
||||||
wl_display_flush(backend->remote_display);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct wlr_drm_format_set *output_get_formats(
|
|
||||||
struct wlr_output *wlr_output, uint32_t buffer_caps) {
|
|
||||||
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
|
|
||||||
if (buffer_caps & WLR_BUFFER_CAP_DMABUF) {
|
|
||||||
return &output->backend->linux_dmabuf_v1_formats;
|
|
||||||
} else if (buffer_caps & WLR_BUFFER_CAP_SHM) {
|
|
||||||
return &output->backend->shm_formats;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void output_destroy(struct wlr_output *wlr_output) {
|
static void output_destroy(struct wlr_output *wlr_output) {
|
||||||
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
|
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
|
||||||
if (output == NULL) {
|
if (output == NULL) {
|
||||||
|
@ -407,6 +170,9 @@ static void output_destroy(struct wlr_output *wlr_output) {
|
||||||
|
|
||||||
wl_list_remove(&output->link);
|
wl_list_remove(&output->link);
|
||||||
|
|
||||||
|
if (output->cursor.egl_window != NULL) {
|
||||||
|
wl_egl_window_destroy(output->cursor.egl_window);
|
||||||
|
}
|
||||||
if (output->cursor.surface) {
|
if (output->cursor.surface) {
|
||||||
wl_surface_destroy(output->cursor.surface);
|
wl_surface_destroy(output->cursor.surface);
|
||||||
}
|
}
|
||||||
|
@ -415,28 +181,17 @@ static void output_destroy(struct wlr_output *wlr_output) {
|
||||||
wl_callback_destroy(output->frame_callback);
|
wl_callback_destroy(output->frame_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_wl_presentation_feedback *feedback, *feedback_tmp;
|
wlr_egl_destroy_surface(&output->backend->egl, output->egl_surface);
|
||||||
wl_list_for_each_safe(feedback, feedback_tmp,
|
wl_egl_window_destroy(output->egl_window);
|
||||||
&output->presentation_feedbacks, link) {
|
|
||||||
presentation_feedback_destroy(feedback);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (output->zxdg_toplevel_decoration_v1) {
|
|
||||||
zxdg_toplevel_decoration_v1_destroy(output->zxdg_toplevel_decoration_v1);
|
|
||||||
}
|
|
||||||
xdg_toplevel_destroy(output->xdg_toplevel);
|
xdg_toplevel_destroy(output->xdg_toplevel);
|
||||||
xdg_surface_destroy(output->xdg_surface);
|
xdg_surface_destroy(output->xdg_surface);
|
||||||
wl_surface_destroy(output->surface);
|
wl_surface_destroy(output->surface);
|
||||||
wl_display_flush(output->backend->remote_display);
|
|
||||||
free(output);
|
free(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_wl_output_cursor(struct wlr_wl_output *output) {
|
void update_wl_output_cursor(struct wlr_wl_output *output) {
|
||||||
struct wlr_wl_pointer *pointer = output->cursor.pointer;
|
if (output->backend->pointer && output->enter_serial) {
|
||||||
if (pointer) {
|
wl_pointer_set_cursor(output->backend->pointer, output->enter_serial,
|
||||||
assert(pointer->output == output);
|
|
||||||
assert(output->enter_serial);
|
|
||||||
wl_pointer_set_cursor(pointer->wl_pointer, output->enter_serial,
|
|
||||||
output->cursor.surface, output->cursor.hotspot_x,
|
output->cursor.surface, output->cursor.hotspot_x,
|
||||||
output->cursor.hotspot_y);
|
output->cursor.hotspot_y);
|
||||||
}
|
}
|
||||||
|
@ -447,14 +202,29 @@ static bool output_move_cursor(struct wlr_output *_output, int x, int y) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool output_schedule_frame(struct wlr_output *wlr_output) {
|
||||||
|
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
|
||||||
|
|
||||||
|
if (output->frame_callback != NULL) {
|
||||||
|
wlr_log(WLR_ERROR, "Skipping frame scheduling");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
output->frame_callback = wl_surface_frame(output->surface);
|
||||||
|
wl_callback_add_listener(output->frame_callback, &frame_listener, output);
|
||||||
|
wl_surface_commit(output->surface);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static const struct wlr_output_impl output_impl = {
|
static const struct wlr_output_impl output_impl = {
|
||||||
|
.set_custom_mode = output_set_custom_mode,
|
||||||
|
.transform = output_transform,
|
||||||
.destroy = output_destroy,
|
.destroy = output_destroy,
|
||||||
.test = output_test,
|
.make_current = output_make_current,
|
||||||
.commit = output_commit,
|
.swap_buffers = output_swap_buffers,
|
||||||
.set_cursor = output_set_cursor,
|
.set_cursor = output_set_cursor,
|
||||||
.move_cursor = output_move_cursor,
|
.move_cursor = output_move_cursor,
|
||||||
.get_cursor_formats = output_get_formats,
|
.schedule_frame = output_schedule_frame,
|
||||||
.get_primary_formats = output_get_formats,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool wlr_output_is_wl(struct wlr_output *wlr_output) {
|
bool wlr_output_is_wl(struct wlr_output *wlr_output) {
|
||||||
|
@ -471,7 +241,7 @@ static void xdg_surface_handle_configure(void *data,
|
||||||
// nothing else?
|
// nothing else?
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct xdg_surface_listener xdg_surface_listener = {
|
static struct xdg_surface_listener xdg_surface_listener = {
|
||||||
.configure = xdg_surface_handle_configure,
|
.configure = xdg_surface_handle_configure,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -493,10 +263,10 @@ static void xdg_toplevel_handle_close(void *data,
|
||||||
struct wlr_wl_output *output = data;
|
struct wlr_wl_output *output = data;
|
||||||
assert(output && output->xdg_toplevel == xdg_toplevel);
|
assert(output && output->xdg_toplevel == xdg_toplevel);
|
||||||
|
|
||||||
wlr_output_destroy(&output->wlr_output);
|
wlr_output_destroy((struct wlr_output *)output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
static struct xdg_toplevel_listener xdg_toplevel_listener = {
|
||||||
.configure = xdg_toplevel_handle_configure,
|
.configure = xdg_toplevel_handle_configure,
|
||||||
.close = xdg_toplevel_handle_close,
|
.close = xdg_toplevel_handle_close,
|
||||||
};
|
};
|
||||||
|
@ -520,18 +290,10 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) {
|
||||||
wlr_output_update_custom_mode(wlr_output, 1280, 720, 0);
|
wlr_output_update_custom_mode(wlr_output, 1280, 720, 0);
|
||||||
strncpy(wlr_output->make, "wayland", sizeof(wlr_output->make));
|
strncpy(wlr_output->make, "wayland", sizeof(wlr_output->make));
|
||||||
strncpy(wlr_output->model, "wayland", sizeof(wlr_output->model));
|
strncpy(wlr_output->model, "wayland", sizeof(wlr_output->model));
|
||||||
|
snprintf(wlr_output->name, sizeof(wlr_output->name), "WL-%d",
|
||||||
char name[64];
|
wl_list_length(&backend->outputs) + 1);
|
||||||
snprintf(name, sizeof(name), "WL-%zu", ++backend->last_output_num);
|
|
||||||
wlr_output_set_name(wlr_output, name);
|
|
||||||
|
|
||||||
char description[128];
|
|
||||||
snprintf(description, sizeof(description),
|
|
||||||
"Wayland output %zu", backend->last_output_num);
|
|
||||||
wlr_output_set_description(wlr_output, description);
|
|
||||||
|
|
||||||
output->backend = backend;
|
output->backend = backend;
|
||||||
wl_list_init(&output->presentation_feedbacks);
|
|
||||||
|
|
||||||
output->surface = wl_compositor_create_surface(backend->compositor);
|
output->surface = wl_compositor_create_surface(backend->compositor);
|
||||||
if (!output->surface) {
|
if (!output->surface) {
|
||||||
|
@ -552,18 +314,6 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) {
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (backend->zxdg_decoration_manager_v1) {
|
|
||||||
output->zxdg_toplevel_decoration_v1 =
|
|
||||||
zxdg_decoration_manager_v1_get_toplevel_decoration(
|
|
||||||
backend->zxdg_decoration_manager_v1, output->xdg_toplevel);
|
|
||||||
if (!output->zxdg_toplevel_decoration_v1) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Could not get xdg toplevel decoration");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
zxdg_toplevel_decoration_v1_set_mode(output->zxdg_toplevel_decoration_v1,
|
|
||||||
ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_wl_output_set_title(wlr_output, NULL);
|
wlr_wl_output_set_title(wlr_output, NULL);
|
||||||
|
|
||||||
xdg_toplevel_set_app_id(output->xdg_toplevel, "wlroots");
|
xdg_toplevel_set_app_id(output->xdg_toplevel, "wlroots");
|
||||||
|
@ -573,29 +323,40 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) {
|
||||||
&xdg_toplevel_listener, output);
|
&xdg_toplevel_listener, output);
|
||||||
wl_surface_commit(output->surface);
|
wl_surface_commit(output->surface);
|
||||||
|
|
||||||
|
output->egl_window = wl_egl_window_create(output->surface,
|
||||||
|
wlr_output->width, wlr_output->height);
|
||||||
|
output->egl_surface = wlr_egl_create_surface(&backend->egl,
|
||||||
|
output->egl_window);
|
||||||
|
|
||||||
wl_display_roundtrip(output->backend->remote_display);
|
wl_display_roundtrip(output->backend->remote_display);
|
||||||
|
|
||||||
|
// start rendering loop per callbacks by rendering first frame
|
||||||
|
if (!wlr_egl_make_current(&output->backend->egl, output->egl_surface,
|
||||||
|
NULL)) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
wlr_renderer_begin(backend->renderer, wlr_output->width, wlr_output->height);
|
||||||
|
wlr_renderer_clear(backend->renderer, (float[]){ 1.0, 1.0, 1.0, 1.0 });
|
||||||
|
wlr_renderer_end(backend->renderer);
|
||||||
|
|
||||||
|
output->frame_callback = wl_surface_frame(output->surface);
|
||||||
|
wl_callback_add_listener(output->frame_callback, &frame_listener, output);
|
||||||
|
|
||||||
|
if (!wlr_egl_swap_buffers(&output->backend->egl, output->egl_surface,
|
||||||
|
NULL)) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
wl_list_insert(&backend->outputs, &output->link);
|
wl_list_insert(&backend->outputs, &output->link);
|
||||||
wlr_output_update_enabled(wlr_output, true);
|
wlr_output_update_enabled(wlr_output, true);
|
||||||
|
|
||||||
wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output);
|
wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output);
|
||||||
|
|
||||||
struct wlr_wl_seat *seat;
|
if (backend->pointer != NULL) {
|
||||||
wl_list_for_each(seat, &backend->seats, link) {
|
create_wl_pointer(backend->pointer, output);
|
||||||
if (seat->pointer) {
|
|
||||||
create_wl_pointer(seat, output);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: let the compositor do this bit
|
|
||||||
if (backend->activation_v1 && backend->activation_token) {
|
|
||||||
xdg_activation_v1_activate(backend->activation_v1,
|
|
||||||
backend->activation_token, output->surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the rendering loop by requesting the compositor to render a frame
|
|
||||||
wlr_output_schedule_frame(wlr_output);
|
|
||||||
|
|
||||||
return wlr_output;
|
return wlr_output;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
@ -615,10 +376,4 @@ void wlr_wl_output_set_title(struct wlr_output *output, const char *title) {
|
||||||
}
|
}
|
||||||
|
|
||||||
xdg_toplevel_set_title(wl_output->xdg_toplevel, title);
|
xdg_toplevel_set_title(wl_output->xdg_toplevel, title);
|
||||||
wl_display_flush(wl_output->backend->remote_display);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_surface *wlr_wl_output_get_surface(struct wlr_output *output) {
|
|
||||||
struct wlr_wl_output *wl_output = get_wl_output_from_output(output);
|
|
||||||
return wl_output->surface;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,935 +0,0 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <wayland-client.h>
|
|
||||||
|
|
||||||
#include <wlr/interfaces/wlr_input_device.h>
|
|
||||||
#include <wlr/interfaces/wlr_keyboard.h>
|
|
||||||
#include <wlr/interfaces/wlr_output.h>
|
|
||||||
#include <wlr/interfaces/wlr_pointer.h>
|
|
||||||
#include <wlr/interfaces/wlr_touch.h>
|
|
||||||
#include <wlr/util/log.h>
|
|
||||||
|
|
||||||
#include "pointer-gestures-unstable-v1-client-protocol.h"
|
|
||||||
#include "relative-pointer-unstable-v1-client-protocol.h"
|
|
||||||
#include "backend/wayland.h"
|
|
||||||
#include "util/signal.h"
|
|
||||||
#include "util/time.h"
|
|
||||||
|
|
||||||
static struct wlr_wl_pointer *output_get_pointer(
|
|
||||||
struct wlr_wl_output *output,
|
|
||||||
const struct wl_pointer *wl_pointer) {
|
|
||||||
struct wlr_wl_input_device *dev;
|
|
||||||
wl_list_for_each(dev, &output->backend->devices, link) {
|
|
||||||
if (dev->wlr_input_device.type != WLR_INPUT_DEVICE_POINTER) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
struct wlr_wl_pointer *pointer =
|
|
||||||
pointer_get_wl(dev->wlr_input_device.pointer);
|
|
||||||
if (pointer->output == output && pointer->wl_pointer == wl_pointer) {
|
|
||||||
return pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t serial, struct wl_surface *surface, wl_fixed_t sx,
|
|
||||||
wl_fixed_t sy) {
|
|
||||||
struct wlr_wl_seat *seat = data;
|
|
||||||
if (surface == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_wl_output *output = wl_surface_get_user_data(surface);
|
|
||||||
assert(output);
|
|
||||||
struct wlr_wl_pointer *pointer = output_get_pointer(output, wl_pointer);
|
|
||||||
seat->active_pointer = pointer;
|
|
||||||
|
|
||||||
// Manage cursor icon/rendering on output
|
|
||||||
struct wlr_wl_pointer *current_pointer = output->cursor.pointer;
|
|
||||||
if (current_pointer && current_pointer != pointer) {
|
|
||||||
wlr_log(WLR_INFO, "Ignoring seat %s pointer cursor in favor of seat %s",
|
|
||||||
seat->name, current_pointer->input_device->seat->name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
output->enter_serial = serial;
|
|
||||||
output->cursor.pointer = pointer;
|
|
||||||
update_wl_output_cursor(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t serial, struct wl_surface *surface) {
|
|
||||||
struct wlr_wl_seat *seat = data;
|
|
||||||
if (surface == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_wl_output *output = wl_surface_get_user_data(surface);
|
|
||||||
assert(output);
|
|
||||||
|
|
||||||
if (seat->active_pointer != NULL &&
|
|
||||||
seat->active_pointer->output == output) {
|
|
||||||
seat->active_pointer = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (output->cursor.pointer == seat->active_pointer) {
|
|
||||||
output->enter_serial = 0;
|
|
||||||
output->cursor.pointer = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t time, wl_fixed_t sx, wl_fixed_t sy) {
|
|
||||||
struct wlr_wl_seat *seat = data;
|
|
||||||
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
|
||||||
if (pointer == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_output *wlr_output = &pointer->output->wlr_output;
|
|
||||||
struct wlr_event_pointer_motion_absolute event = {
|
|
||||||
.device = &pointer->input_device->wlr_input_device,
|
|
||||||
.time_msec = time,
|
|
||||||
.x = wl_fixed_to_double(sx) / wlr_output->width,
|
|
||||||
.y = wl_fixed_to_double(sy) / wlr_output->height,
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&pointer->wlr_pointer.events.motion_absolute, &event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
|
|
||||||
struct wlr_wl_seat *seat = data;
|
|
||||||
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
|
||||||
if (pointer == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_event_pointer_button event = {
|
|
||||||
.device = &pointer->input_device->wlr_input_device,
|
|
||||||
.button = button,
|
|
||||||
.state = state,
|
|
||||||
.time_msec = time,
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&pointer->wlr_pointer.events.button, &event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t time, uint32_t axis, wl_fixed_t value) {
|
|
||||||
struct wlr_wl_seat *seat = data;
|
|
||||||
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
|
||||||
if (pointer == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_event_pointer_axis event = {
|
|
||||||
.device = &pointer->input_device->wlr_input_device,
|
|
||||||
.delta = wl_fixed_to_double(value),
|
|
||||||
.delta_discrete = pointer->axis_discrete,
|
|
||||||
.orientation = axis,
|
|
||||||
.time_msec = time,
|
|
||||||
.source = pointer->axis_source,
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&pointer->wlr_pointer.events.axis, &event);
|
|
||||||
|
|
||||||
pointer->axis_discrete = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) {
|
|
||||||
struct wlr_wl_seat *seat = data;
|
|
||||||
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
|
||||||
if (pointer == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_signal_emit_safe(&pointer->wlr_pointer.events.frame,
|
|
||||||
&pointer->wlr_pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis_source(void *data,
|
|
||||||
struct wl_pointer *wl_pointer, uint32_t axis_source) {
|
|
||||||
struct wlr_wl_seat *seat = data;
|
|
||||||
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
|
||||||
if (pointer == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pointer->axis_source = axis_source;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t time, uint32_t axis) {
|
|
||||||
struct wlr_wl_seat *seat = data;
|
|
||||||
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
|
||||||
if (pointer == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_event_pointer_axis event = {
|
|
||||||
.device = &pointer->input_device->wlr_input_device,
|
|
||||||
.delta = 0,
|
|
||||||
.delta_discrete = 0,
|
|
||||||
.orientation = axis,
|
|
||||||
.time_msec = time,
|
|
||||||
.source = pointer->axis_source,
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&pointer->wlr_pointer.events.axis, &event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis_discrete(void *data,
|
|
||||||
struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) {
|
|
||||||
struct wlr_wl_seat *seat = data;
|
|
||||||
struct wlr_wl_pointer *pointer = seat->active_pointer;
|
|
||||||
if (pointer == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pointer->axis_discrete = discrete;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_pointer_listener pointer_listener = {
|
|
||||||
.enter = pointer_handle_enter,
|
|
||||||
.leave = pointer_handle_leave,
|
|
||||||
.motion = pointer_handle_motion,
|
|
||||||
.button = pointer_handle_button,
|
|
||||||
.axis = pointer_handle_axis,
|
|
||||||
.frame = pointer_handle_frame,
|
|
||||||
.axis_source = pointer_handle_axis_source,
|
|
||||||
.axis_stop = pointer_handle_axis_stop,
|
|
||||||
.axis_discrete = pointer_handle_axis_discrete,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
uint32_t format, int32_t fd, uint32_t size) {
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
|
|
||||||
struct wlr_input_device *dev = data;
|
|
||||||
|
|
||||||
uint32_t time = get_current_time_msec();
|
|
||||||
|
|
||||||
uint32_t *keycode_ptr;
|
|
||||||
wl_array_for_each(keycode_ptr, keys) {
|
|
||||||
struct wlr_event_keyboard_key event = {
|
|
||||||
.keycode = *keycode_ptr,
|
|
||||||
.state = WL_KEYBOARD_KEY_STATE_PRESSED,
|
|
||||||
.time_msec = time,
|
|
||||||
.update_state = false,
|
|
||||||
};
|
|
||||||
wlr_keyboard_notify_key(dev->keyboard, &event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
uint32_t serial, struct wl_surface *surface) {
|
|
||||||
struct wlr_input_device *dev = data;
|
|
||||||
|
|
||||||
uint32_t time = get_current_time_msec();
|
|
||||||
|
|
||||||
size_t num_keycodes = dev->keyboard->num_keycodes;
|
|
||||||
uint32_t pressed[num_keycodes + 1];
|
|
||||||
memcpy(pressed, dev->keyboard->keycodes,
|
|
||||||
num_keycodes * sizeof(uint32_t));
|
|
||||||
|
|
||||||
for (size_t i = 0; i < num_keycodes; ++i) {
|
|
||||||
uint32_t keycode = pressed[i];
|
|
||||||
|
|
||||||
struct wlr_event_keyboard_key event = {
|
|
||||||
.keycode = keycode,
|
|
||||||
.state = WL_KEYBOARD_KEY_STATE_RELEASED,
|
|
||||||
.time_msec = time,
|
|
||||||
.update_state = false,
|
|
||||||
};
|
|
||||||
wlr_keyboard_notify_key(dev->keyboard, &event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
|
|
||||||
struct wlr_input_device *dev = data;
|
|
||||||
assert(dev && dev->keyboard);
|
|
||||||
|
|
||||||
struct wlr_event_keyboard_key wlr_event = {
|
|
||||||
.keycode = key,
|
|
||||||
.state = state,
|
|
||||||
.time_msec = time,
|
|
||||||
.update_state = false,
|
|
||||||
};
|
|
||||||
wlr_keyboard_notify_key(dev->keyboard, &wlr_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard,
|
|
||||||
uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
|
|
||||||
uint32_t mods_locked, uint32_t group) {
|
|
||||||
struct wlr_input_device *dev = data;
|
|
||||||
assert(dev && dev->keyboard);
|
|
||||||
wlr_keyboard_notify_modifiers(dev->keyboard, mods_depressed, mods_latched,
|
|
||||||
mods_locked, group);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void keyboard_handle_repeat_info(void *data,
|
|
||||||
struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) {
|
|
||||||
// This space is intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_keyboard_listener keyboard_listener = {
|
|
||||||
.keymap = keyboard_handle_keymap,
|
|
||||||
.enter = keyboard_handle_enter,
|
|
||||||
.leave = keyboard_handle_leave,
|
|
||||||
.key = keyboard_handle_key,
|
|
||||||
.modifiers = keyboard_handle_modifiers,
|
|
||||||
.repeat_info = keyboard_handle_repeat_info
|
|
||||||
};
|
|
||||||
|
|
||||||
static void touch_coordinates_to_absolute(struct wlr_wl_input_device *device,
|
|
||||||
wl_fixed_t x, wl_fixed_t y, double *sx, double *sy) {
|
|
||||||
// TODO: each output needs its own touch
|
|
||||||
struct wlr_wl_output *output, *tmp;
|
|
||||||
wl_list_for_each_safe(output, tmp, &device->backend->outputs, link) {
|
|
||||||
*sx = wl_fixed_to_double(x) / output->wlr_output.width;
|
|
||||||
*sy = wl_fixed_to_double(y) / output->wlr_output.height;
|
|
||||||
return; // Choose the first output in the list
|
|
||||||
}
|
|
||||||
|
|
||||||
*sx = *sy = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void touch_handle_down(void *data, struct wl_touch *wl_touch,
|
|
||||||
uint32_t serial, uint32_t time, struct wl_surface *surface,
|
|
||||||
int32_t id, wl_fixed_t x, wl_fixed_t y) {
|
|
||||||
struct wlr_wl_input_device *device = data;
|
|
||||||
assert(device && device->wlr_input_device.touch);
|
|
||||||
|
|
||||||
double sx, sy;
|
|
||||||
touch_coordinates_to_absolute(device, x, y, &sx, &sy);
|
|
||||||
struct wlr_event_touch_down event = {
|
|
||||||
.device = &device->wlr_input_device,
|
|
||||||
.time_msec = time,
|
|
||||||
.touch_id = id,
|
|
||||||
.x = sx,
|
|
||||||
.y = sy
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&device->wlr_input_device.touch->events.down, &event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void touch_handle_up(void *data, struct wl_touch *wl_touch,
|
|
||||||
uint32_t serial, uint32_t time, int32_t id) {
|
|
||||||
struct wlr_wl_input_device *device = data;
|
|
||||||
assert(device && device->wlr_input_device.touch);
|
|
||||||
|
|
||||||
struct wlr_event_touch_up event = {
|
|
||||||
.device = &device->wlr_input_device,
|
|
||||||
.time_msec = time,
|
|
||||||
.touch_id = id,
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&device->wlr_input_device.touch->events.up, &event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void touch_handle_motion(void *data, struct wl_touch *wl_touch,
|
|
||||||
uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y) {
|
|
||||||
struct wlr_wl_input_device *device = data;
|
|
||||||
assert(device && device->wlr_input_device.touch);
|
|
||||||
|
|
||||||
double sx, sy;
|
|
||||||
touch_coordinates_to_absolute(device, x, y, &sx, &sy);
|
|
||||||
struct wlr_event_touch_motion event = {
|
|
||||||
.device = &device->wlr_input_device,
|
|
||||||
.time_msec = time,
|
|
||||||
.touch_id = id,
|
|
||||||
.x = sx,
|
|
||||||
.y = sy
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&device->wlr_input_device.touch->events.motion, &event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void touch_handle_frame(void *data, struct wl_touch *wl_touch) {
|
|
||||||
struct wlr_wl_input_device *device = data;
|
|
||||||
assert(device && device->wlr_input_device.touch);
|
|
||||||
|
|
||||||
wlr_signal_emit_safe(&device->wlr_input_device.touch->events.frame, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void touch_handle_cancel(void *data, struct wl_touch *wl_touch) {
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
static void touch_handle_shape(void *data, struct wl_touch *wl_touch,
|
|
||||||
int32_t id, wl_fixed_t major, wl_fixed_t minor) {
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
static void touch_handle_orientation(void *data, struct wl_touch *wl_touch,
|
|
||||||
int32_t id, wl_fixed_t orientation) {
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_touch_listener touch_listener = {
|
|
||||||
.down = touch_handle_down,
|
|
||||||
.up = touch_handle_up,
|
|
||||||
.motion = touch_handle_motion,
|
|
||||||
.frame = touch_handle_frame,
|
|
||||||
.cancel = touch_handle_cancel,
|
|
||||||
.shape = touch_handle_shape,
|
|
||||||
.orientation = touch_handle_orientation,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct wlr_wl_input_device *get_wl_input_device_from_input_device(
|
|
||||||
struct wlr_input_device *wlr_dev) {
|
|
||||||
assert(wlr_input_device_is_wl(wlr_dev));
|
|
||||||
return (struct wlr_wl_input_device *)wlr_dev;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool create_wl_seat(struct wl_seat *wl_seat, struct wlr_wl_backend *wl) {
|
|
||||||
struct wlr_wl_seat *seat = calloc(1, sizeof(struct wlr_wl_seat));
|
|
||||||
if (!seat) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
seat->wl_seat = wl_seat;
|
|
||||||
seat->backend = wl;
|
|
||||||
wl_list_insert(&wl->seats, &seat->link);
|
|
||||||
wl_seat_add_listener(wl_seat, &seat_listener, seat);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void destroy_wl_seats(struct wlr_wl_backend *wl) {
|
|
||||||
struct wlr_wl_seat *seat, *tmp_seat;
|
|
||||||
wl_list_for_each_safe(seat, tmp_seat, &wl->seats, link) {
|
|
||||||
if (seat->touch) {
|
|
||||||
wl_touch_destroy(seat->touch);
|
|
||||||
}
|
|
||||||
if (seat->pointer) {
|
|
||||||
wl_pointer_destroy(seat->pointer);
|
|
||||||
}
|
|
||||||
if (seat->keyboard && !wl->started) {
|
|
||||||
// early termination will not be handled by input_device_destroy
|
|
||||||
wl_keyboard_destroy(seat->keyboard);
|
|
||||||
}
|
|
||||||
free(seat->name);
|
|
||||||
assert(seat->wl_seat);
|
|
||||||
wl_seat_destroy(seat->wl_seat);
|
|
||||||
|
|
||||||
wl_list_remove(&seat->link);
|
|
||||||
free(seat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wlr_wl_seat *input_device_get_seat(struct wlr_input_device *wlr_dev) {
|
|
||||||
struct wlr_wl_input_device *dev =
|
|
||||||
get_wl_input_device_from_input_device(wlr_dev);
|
|
||||||
assert(dev->seat);
|
|
||||||
return dev->seat;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void input_device_destroy(struct wlr_input_device *wlr_dev) {
|
|
||||||
struct wlr_wl_input_device *dev =
|
|
||||||
get_wl_input_device_from_input_device(wlr_dev);
|
|
||||||
if (dev->wlr_input_device.type == WLR_INPUT_DEVICE_KEYBOARD) {
|
|
||||||
struct wlr_wl_seat *seat = input_device_get_seat(wlr_dev);
|
|
||||||
wl_keyboard_release(seat->keyboard);
|
|
||||||
seat->keyboard = NULL;
|
|
||||||
}
|
|
||||||
// We can't destroy pointer here because we might have multiple devices
|
|
||||||
// exposing it to compositor.
|
|
||||||
wl_list_remove(&dev->link);
|
|
||||||
free(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wlr_input_device_impl input_device_impl = {
|
|
||||||
.destroy = input_device_destroy,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool wlr_input_device_is_wl(struct wlr_input_device *dev) {
|
|
||||||
return dev->impl == &input_device_impl;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_wl_input_device *create_wl_input_device(
|
|
||||||
struct wlr_wl_seat *seat, enum wlr_input_device_type type) {
|
|
||||||
struct wlr_wl_input_device *dev =
|
|
||||||
calloc(1, sizeof(struct wlr_wl_input_device));
|
|
||||||
if (dev == NULL) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
dev->backend = seat->backend;
|
|
||||||
dev->seat = seat;
|
|
||||||
|
|
||||||
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
|
|
||||||
|
|
||||||
unsigned int vendor = 0, product = 0;
|
|
||||||
|
|
||||||
const char *type_name = "unknown";
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case WLR_INPUT_DEVICE_KEYBOARD:
|
|
||||||
type_name = "keyboard";
|
|
||||||
break;
|
|
||||||
case WLR_INPUT_DEVICE_POINTER:
|
|
||||||
type_name = "pointer";
|
|
||||||
break;
|
|
||||||
case WLR_INPUT_DEVICE_TOUCH:
|
|
||||||
type_name = "touch";
|
|
||||||
break;
|
|
||||||
case WLR_INPUT_DEVICE_TABLET_TOOL:
|
|
||||||
type_name = "tablet-tool";
|
|
||||||
break;
|
|
||||||
case WLR_INPUT_DEVICE_TABLET_PAD:
|
|
||||||
type_name = "tablet-pad";
|
|
||||||
break;
|
|
||||||
case WLR_INPUT_DEVICE_SWITCH:
|
|
||||||
type_name = "switch";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t name_size = 8 + strlen(type_name) + strlen(seat->name) + 1;
|
|
||||||
char name[name_size];
|
|
||||||
(void) snprintf(name, name_size, "wayland-%s-%s", type_name, seat->name);
|
|
||||||
|
|
||||||
wlr_input_device_init(wlr_dev, type, &input_device_impl, name, vendor,
|
|
||||||
product);
|
|
||||||
wl_list_insert(&seat->backend->devices, &dev->link);
|
|
||||||
return dev;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wlr_pointer_impl pointer_impl;
|
|
||||||
|
|
||||||
struct wlr_wl_pointer *pointer_get_wl(struct wlr_pointer *wlr_pointer) {
|
|
||||||
assert(wlr_pointer->impl == &pointer_impl);
|
|
||||||
return (struct wlr_wl_pointer *)wlr_pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_destroy(struct wlr_pointer *wlr_pointer) {
|
|
||||||
struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_pointer);
|
|
||||||
|
|
||||||
if (pointer->output->cursor.pointer == pointer) {
|
|
||||||
pointer->output->cursor.pointer = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_wl_seat *seat = pointer->input_device->seat;
|
|
||||||
if (seat->active_pointer == pointer) {
|
|
||||||
seat->active_pointer = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// pointer->wl_pointer belongs to the wlr_wl_seat
|
|
||||||
|
|
||||||
if (pointer->gesture_swipe != NULL) {
|
|
||||||
zwp_pointer_gesture_swipe_v1_destroy(pointer->gesture_swipe);
|
|
||||||
}
|
|
||||||
if (pointer->gesture_pinch != NULL) {
|
|
||||||
zwp_pointer_gesture_pinch_v1_destroy(pointer->gesture_pinch);
|
|
||||||
}
|
|
||||||
if (pointer->gesture_hold != NULL) {
|
|
||||||
zwp_pointer_gesture_hold_v1_destroy(pointer->gesture_hold);
|
|
||||||
}
|
|
||||||
if (pointer->relative_pointer != NULL) {
|
|
||||||
zwp_relative_pointer_v1_destroy(pointer->relative_pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_list_remove(&pointer->output_destroy.link);
|
|
||||||
free(pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wlr_pointer_impl pointer_impl = {
|
|
||||||
.destroy = pointer_destroy,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void gesture_swipe_begin(void *data,
|
|
||||||
struct zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1,
|
|
||||||
uint32_t serial, uint32_t time,
|
|
||||||
struct wl_surface *surface, uint32_t fingers) {
|
|
||||||
struct wlr_wl_input_device *input_device = (struct wlr_wl_input_device *)data;
|
|
||||||
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
|
|
||||||
struct wlr_event_pointer_swipe_begin wlr_event = {
|
|
||||||
.device = wlr_dev,
|
|
||||||
.time_msec = time,
|
|
||||||
.fingers = fingers,
|
|
||||||
};
|
|
||||||
input_device->fingers = fingers;
|
|
||||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.swipe_begin, &wlr_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gesture_swipe_update(void *data,
|
|
||||||
struct zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1,
|
|
||||||
uint32_t time, wl_fixed_t dx, wl_fixed_t dy) {
|
|
||||||
struct wlr_wl_input_device *input_device = (struct wlr_wl_input_device *)data;
|
|
||||||
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
|
|
||||||
struct wlr_event_pointer_swipe_update wlr_event = {
|
|
||||||
.device = wlr_dev,
|
|
||||||
.time_msec = time,
|
|
||||||
.fingers = input_device->fingers,
|
|
||||||
.dx = wl_fixed_to_double(dx),
|
|
||||||
.dy = wl_fixed_to_double(dy),
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.swipe_update, &wlr_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gesture_swipe_end(void *data,
|
|
||||||
struct zwp_pointer_gesture_swipe_v1 *zwp_pointer_gesture_swipe_v1,
|
|
||||||
uint32_t serial, uint32_t time, int32_t cancelled) {
|
|
||||||
struct wlr_wl_input_device *input_device = (struct wlr_wl_input_device *)data;
|
|
||||||
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
|
|
||||||
struct wlr_event_pointer_swipe_end wlr_event = {
|
|
||||||
.device = wlr_dev,
|
|
||||||
.time_msec = time,
|
|
||||||
.cancelled = cancelled,
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.swipe_end, &wlr_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_pointer_gesture_swipe_v1_listener gesture_swipe_impl = {
|
|
||||||
.begin = gesture_swipe_begin,
|
|
||||||
.update = gesture_swipe_update,
|
|
||||||
.end = gesture_swipe_end,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void gesture_pinch_begin(void *data,
|
|
||||||
struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1,
|
|
||||||
uint32_t serial, uint32_t time,
|
|
||||||
struct wl_surface *surface, uint32_t fingers) {
|
|
||||||
struct wlr_wl_input_device *input_device = (struct wlr_wl_input_device *)data;
|
|
||||||
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
|
|
||||||
struct wlr_event_pointer_pinch_begin wlr_event = {
|
|
||||||
.device = wlr_dev,
|
|
||||||
.time_msec = time,
|
|
||||||
.fingers = fingers,
|
|
||||||
};
|
|
||||||
input_device->fingers = fingers;
|
|
||||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_begin, &wlr_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gesture_pinch_update(void *data,
|
|
||||||
struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1,
|
|
||||||
uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation) {
|
|
||||||
struct wlr_wl_input_device *input_device = (struct wlr_wl_input_device *)data;
|
|
||||||
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
|
|
||||||
struct wlr_event_pointer_pinch_update wlr_event = {
|
|
||||||
.device = wlr_dev,
|
|
||||||
.time_msec = time,
|
|
||||||
.fingers = input_device->fingers,
|
|
||||||
.dx = wl_fixed_to_double(dx),
|
|
||||||
.dy = wl_fixed_to_double(dy),
|
|
||||||
.scale = wl_fixed_to_double(scale),
|
|
||||||
.rotation = wl_fixed_to_double(rotation),
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_update, &wlr_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gesture_pinch_end(void *data,
|
|
||||||
struct zwp_pointer_gesture_pinch_v1 *zwp_pointer_gesture_pinch_v1,
|
|
||||||
uint32_t serial, uint32_t time, int32_t cancelled) {
|
|
||||||
struct wlr_wl_input_device *input_device = (struct wlr_wl_input_device *)data;
|
|
||||||
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
|
|
||||||
struct wlr_event_pointer_pinch_end wlr_event = {
|
|
||||||
.device = wlr_dev,
|
|
||||||
.time_msec = time,
|
|
||||||
.cancelled = cancelled,
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.pinch_end, &wlr_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_pointer_gesture_pinch_v1_listener gesture_pinch_impl = {
|
|
||||||
.begin = gesture_pinch_begin,
|
|
||||||
.update = gesture_pinch_update,
|
|
||||||
.end = gesture_pinch_end,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void gesture_hold_begin(void *data,
|
|
||||||
struct zwp_pointer_gesture_hold_v1 *zwp_pointer_gesture_hold_v1,
|
|
||||||
uint32_t serial, uint32_t time,
|
|
||||||
struct wl_surface *surface, uint32_t fingers) {
|
|
||||||
struct wlr_wl_input_device *input_device = (struct wlr_wl_input_device *)data;
|
|
||||||
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
|
|
||||||
struct wlr_event_pointer_hold_begin wlr_event = {
|
|
||||||
.device = wlr_dev,
|
|
||||||
.time_msec = time,
|
|
||||||
.fingers = fingers,
|
|
||||||
};
|
|
||||||
input_device->fingers = fingers;
|
|
||||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.hold_begin, &wlr_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void gesture_hold_end(void *data,
|
|
||||||
struct zwp_pointer_gesture_hold_v1 *zwp_pointer_gesture_hold_v1,
|
|
||||||
uint32_t serial, uint32_t time, int32_t cancelled) {
|
|
||||||
struct wlr_wl_input_device *input_device = (struct wlr_wl_input_device *)data;
|
|
||||||
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
|
|
||||||
struct wlr_event_pointer_hold_end wlr_event = {
|
|
||||||
.device = wlr_dev,
|
|
||||||
.time_msec = time,
|
|
||||||
.cancelled = cancelled,
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.hold_end, &wlr_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_pointer_gesture_hold_v1_listener gesture_hold_impl = {
|
|
||||||
.begin = gesture_hold_begin,
|
|
||||||
.end = gesture_hold_end,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void relative_pointer_handle_relative_motion(void *data,
|
|
||||||
struct zwp_relative_pointer_v1 *relative_pointer, uint32_t utime_hi,
|
|
||||||
uint32_t utime_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel,
|
|
||||||
wl_fixed_t dy_unaccel) {
|
|
||||||
struct wlr_wl_input_device *input_device = data;
|
|
||||||
struct wlr_input_device *wlr_dev = &input_device->wlr_input_device;
|
|
||||||
if (pointer_get_wl(wlr_dev->pointer) != input_device->seat->active_pointer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t time_usec = (uint64_t)utime_hi << 32 | utime_lo;
|
|
||||||
|
|
||||||
struct wlr_event_pointer_motion wlr_event = {
|
|
||||||
.device = wlr_dev,
|
|
||||||
.time_msec = (uint32_t)(time_usec / 1000),
|
|
||||||
.delta_x = wl_fixed_to_double(dx),
|
|
||||||
.delta_y = wl_fixed_to_double(dy),
|
|
||||||
.unaccel_dx = wl_fixed_to_double(dx_unaccel),
|
|
||||||
.unaccel_dy = wl_fixed_to_double(dy_unaccel),
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&wlr_dev->pointer->events.motion, &wlr_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
|
|
||||||
.relative_motion = relative_pointer_handle_relative_motion,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static void pointer_handle_output_destroy(struct wl_listener *listener,
|
|
||||||
void *data) {
|
|
||||||
struct wlr_wl_pointer *pointer =
|
|
||||||
wl_container_of(listener, pointer, output_destroy);
|
|
||||||
wlr_input_device_destroy(&pointer->input_device->wlr_input_device);
|
|
||||||
}
|
|
||||||
|
|
||||||
void create_wl_pointer(struct wlr_wl_seat *seat, struct wlr_wl_output *output) {
|
|
||||||
assert(seat->pointer);
|
|
||||||
struct wl_pointer *wl_pointer = seat->pointer;
|
|
||||||
struct wlr_wl_backend *backend = output->backend;
|
|
||||||
|
|
||||||
if (output_get_pointer(output, wl_pointer)) {
|
|
||||||
wlr_log(WLR_DEBUG,
|
|
||||||
"Pointer for seat %s and output %s already exists (ignoring)",
|
|
||||||
seat->name, output->wlr_output.name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_wl_pointer *pointer = calloc(1, sizeof(struct wlr_wl_pointer));
|
|
||||||
if (pointer == NULL) {
|
|
||||||
wlr_log(WLR_ERROR, "Allocation failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pointer->wl_pointer = wl_pointer;
|
|
||||||
pointer->output = output; // we need output to map absolute coordinates onto
|
|
||||||
|
|
||||||
struct wlr_wl_input_device *dev =
|
|
||||||
create_wl_input_device(seat, WLR_INPUT_DEVICE_POINTER);
|
|
||||||
if (dev == NULL) {
|
|
||||||
free(pointer);
|
|
||||||
wlr_log(WLR_ERROR, "Allocation failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pointer->input_device = dev;
|
|
||||||
|
|
||||||
wl_signal_add(&output->wlr_output.events.destroy, &pointer->output_destroy);
|
|
||||||
pointer->output_destroy.notify = pointer_handle_output_destroy;
|
|
||||||
|
|
||||||
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
|
|
||||||
wlr_dev->pointer = &pointer->wlr_pointer;
|
|
||||||
wlr_dev->output_name = strdup(output->wlr_output.name);
|
|
||||||
wlr_pointer_init(wlr_dev->pointer, &pointer_impl);
|
|
||||||
|
|
||||||
if (backend->zwp_pointer_gestures_v1) {
|
|
||||||
uint32_t version = zwp_pointer_gestures_v1_get_version(
|
|
||||||
backend->zwp_pointer_gestures_v1);
|
|
||||||
|
|
||||||
pointer->gesture_swipe = zwp_pointer_gestures_v1_get_swipe_gesture(
|
|
||||||
backend->zwp_pointer_gestures_v1, wl_pointer);
|
|
||||||
zwp_pointer_gesture_swipe_v1_add_listener(pointer->gesture_swipe, &gesture_swipe_impl, dev);
|
|
||||||
pointer->gesture_pinch = zwp_pointer_gestures_v1_get_pinch_gesture(
|
|
||||||
backend->zwp_pointer_gestures_v1, wl_pointer);
|
|
||||||
zwp_pointer_gesture_pinch_v1_add_listener(pointer->gesture_pinch, &gesture_pinch_impl, dev);
|
|
||||||
|
|
||||||
if (version >= ZWP_POINTER_GESTURES_V1_GET_HOLD_GESTURE) {
|
|
||||||
pointer->gesture_hold = zwp_pointer_gestures_v1_get_hold_gesture(
|
|
||||||
backend->zwp_pointer_gestures_v1, wl_pointer);
|
|
||||||
zwp_pointer_gesture_hold_v1_add_listener(pointer->gesture_hold, &gesture_hold_impl, dev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backend->zwp_relative_pointer_manager_v1) {
|
|
||||||
pointer->relative_pointer =
|
|
||||||
zwp_relative_pointer_manager_v1_get_relative_pointer(
|
|
||||||
backend->zwp_relative_pointer_manager_v1, wl_pointer);
|
|
||||||
zwp_relative_pointer_v1_add_listener(pointer->relative_pointer,
|
|
||||||
&relative_pointer_listener, dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_pointer_add_listener(wl_pointer, &pointer_listener, seat);
|
|
||||||
wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
void create_wl_keyboard(struct wlr_wl_seat *seat) {
|
|
||||||
assert(seat->keyboard);
|
|
||||||
struct wl_keyboard *wl_keyboard = seat->keyboard;
|
|
||||||
struct wlr_wl_input_device *dev =
|
|
||||||
create_wl_input_device(seat, WLR_INPUT_DEVICE_KEYBOARD);
|
|
||||||
if (!dev) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
|
|
||||||
|
|
||||||
wlr_dev->keyboard = calloc(1, sizeof(*wlr_dev->keyboard));
|
|
||||||
if (!wlr_dev->keyboard) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
|
||||||
wlr_input_device_destroy(wlr_dev);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wlr_keyboard_init(wlr_dev->keyboard, NULL);
|
|
||||||
|
|
||||||
wl_keyboard_add_listener(wl_keyboard, &keyboard_listener, wlr_dev);
|
|
||||||
wlr_signal_emit_safe(&seat->backend->backend.events.new_input, wlr_dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
void create_wl_touch(struct wlr_wl_seat *seat) {
|
|
||||||
assert(seat->touch);
|
|
||||||
struct wl_touch *wl_touch = seat->touch;
|
|
||||||
struct wlr_wl_input_device *dev =
|
|
||||||
create_wl_input_device(seat, WLR_INPUT_DEVICE_TOUCH);
|
|
||||||
if (!dev) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
|
|
||||||
|
|
||||||
wlr_dev->touch = calloc(1, sizeof(*wlr_dev->touch));
|
|
||||||
if (!wlr_dev->touch) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
|
||||||
wlr_input_device_destroy(wlr_dev);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wlr_touch_init(wlr_dev->touch, NULL);
|
|
||||||
|
|
||||||
wl_touch_add_listener(wl_touch, &touch_listener, dev);
|
|
||||||
wlr_signal_emit_safe(&seat->backend->backend.events.new_input, wlr_dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
|
|
||||||
enum wl_seat_capability caps) {
|
|
||||||
struct wlr_wl_seat *seat = data;
|
|
||||||
struct wlr_wl_backend *backend = seat->backend;
|
|
||||||
|
|
||||||
if ((caps & WL_SEAT_CAPABILITY_POINTER) && seat->pointer == NULL) {
|
|
||||||
wlr_log(WLR_DEBUG, "seat %p offered pointer", (void *)wl_seat);
|
|
||||||
|
|
||||||
struct wl_pointer *wl_pointer = wl_seat_get_pointer(wl_seat);
|
|
||||||
seat->pointer = wl_pointer;
|
|
||||||
|
|
||||||
struct wlr_wl_output *output;
|
|
||||||
wl_list_for_each(output, &backend->outputs, link) {
|
|
||||||
create_wl_pointer(seat, output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!(caps & WL_SEAT_CAPABILITY_POINTER) && seat->pointer != NULL) {
|
|
||||||
wlr_log(WLR_DEBUG, "seat %p dropped pointer", (void *)wl_seat);
|
|
||||||
|
|
||||||
struct wl_pointer *wl_pointer = seat->pointer;
|
|
||||||
|
|
||||||
struct wlr_wl_input_device *device, *tmp;
|
|
||||||
wl_list_for_each_safe(device, tmp, &backend->devices, link) {
|
|
||||||
if (device->wlr_input_device.type != WLR_INPUT_DEVICE_POINTER) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
struct wlr_wl_pointer *pointer =
|
|
||||||
pointer_get_wl(device->wlr_input_device.pointer);
|
|
||||||
if (pointer->wl_pointer != wl_pointer) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
wlr_log(WLR_DEBUG, "dropping pointer %s",
|
|
||||||
pointer->input_device->wlr_input_device.name);
|
|
||||||
struct wlr_wl_output *output = pointer->output;
|
|
||||||
wlr_input_device_destroy(&device->wlr_input_device);
|
|
||||||
assert(seat->active_pointer != pointer);
|
|
||||||
assert(output->cursor.pointer != pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_pointer_release(seat->pointer);
|
|
||||||
seat->pointer = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && seat->keyboard == NULL) {
|
|
||||||
wlr_log(WLR_DEBUG, "seat %p offered keyboard", (void *)wl_seat);
|
|
||||||
|
|
||||||
struct wl_keyboard *wl_keyboard = wl_seat_get_keyboard(wl_seat);
|
|
||||||
seat->keyboard = wl_keyboard;
|
|
||||||
|
|
||||||
if (backend->started) {
|
|
||||||
create_wl_keyboard(seat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && seat->keyboard != NULL) {
|
|
||||||
wlr_log(WLR_DEBUG, "seat %p dropped keyboard", (void *)wl_seat);
|
|
||||||
|
|
||||||
struct wlr_wl_input_device *device, *tmp;
|
|
||||||
wl_list_for_each_safe(device, tmp, &backend->devices, link) {
|
|
||||||
if (device->wlr_input_device.type != WLR_INPUT_DEVICE_KEYBOARD) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (device->seat != seat) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
wlr_input_device_destroy(&device->wlr_input_device);
|
|
||||||
}
|
|
||||||
assert(seat->keyboard == NULL); // free'ed by input_device_destroy
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((caps & WL_SEAT_CAPABILITY_TOUCH) && seat->touch == NULL) {
|
|
||||||
wlr_log(WLR_DEBUG, "seat %p offered touch", (void *)wl_seat);
|
|
||||||
|
|
||||||
seat->touch = wl_seat_get_touch(wl_seat);
|
|
||||||
if (backend->started) {
|
|
||||||
create_wl_touch(seat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && seat->touch != NULL) {
|
|
||||||
wlr_log(WLR_DEBUG, "seat %p dropped touch", (void *)wl_seat);
|
|
||||||
|
|
||||||
struct wlr_wl_input_device *device, *tmp;
|
|
||||||
wl_list_for_each_safe(device, tmp, &backend->devices, link) {
|
|
||||||
if (device->wlr_input_device.type == WLR_INPUT_DEVICE_TOUCH) {
|
|
||||||
wlr_input_device_destroy(&device->wlr_input_device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_touch_release(seat->touch);
|
|
||||||
seat->touch = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void seat_handle_name(void *data, struct wl_seat *wl_seat,
|
|
||||||
const char *name) {
|
|
||||||
struct wlr_wl_seat *seat = data;
|
|
||||||
free(seat->name);
|
|
||||||
seat->name = strdup(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct wl_seat_listener seat_listener = {
|
|
||||||
.capabilities = seat_handle_capabilities,
|
|
||||||
.name = seat_handle_name,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wl_seat *wlr_wl_input_device_get_seat(struct wlr_input_device *wlr_dev) {
|
|
||||||
return input_device_get_seat(wlr_dev)->wl_seat;
|
|
||||||
}
|
|
|
@ -1,938 +0,0 @@
|
||||||
#ifndef _POSIX_C_SOURCE
|
|
||||||
#define _POSIX_C_SOURCE 200809L
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <wayland-util.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <wlr/interfaces/wlr_tablet_pad.h>
|
|
||||||
#include <wlr/interfaces/wlr_tablet_tool.h>
|
|
||||||
#include <wlr/types/wlr_input_device.h>
|
|
||||||
#include <wlr/interfaces/wlr_input_device.h>
|
|
||||||
|
|
||||||
#include "util/signal.h"
|
|
||||||
#include "util/time.h"
|
|
||||||
#include "wlr/util/log.h"
|
|
||||||
#include "tablet-unstable-v2-client-protocol.h"
|
|
||||||
|
|
||||||
#include "backend/wayland.h"
|
|
||||||
|
|
||||||
struct wlr_wl_tablet_seat {
|
|
||||||
struct zwp_tablet_seat_v2 *tablet_seat;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wlr_wl_tablet_tool {
|
|
||||||
/* static */
|
|
||||||
struct zwp_tablet_tool_v2 *tool;
|
|
||||||
struct wlr_tablet_tool wlr_tool;
|
|
||||||
|
|
||||||
/* semi-static */
|
|
||||||
struct wlr_wl_output *output;
|
|
||||||
struct wlr_wl_input_device *tablet;
|
|
||||||
double pre_x, pre_y;
|
|
||||||
|
|
||||||
/* per frame */
|
|
||||||
double x, y;
|
|
||||||
|
|
||||||
double pressure;
|
|
||||||
double distance;
|
|
||||||
double tilt_x, tilt_y;
|
|
||||||
double rotation;
|
|
||||||
double slider;
|
|
||||||
double wheel_delta;
|
|
||||||
|
|
||||||
bool is_in;
|
|
||||||
bool is_out;
|
|
||||||
|
|
||||||
bool is_up;
|
|
||||||
bool is_down;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wlr_wl_tablet_pad_ring {
|
|
||||||
struct wl_list link; // wlr_wl_tablet_pad_group::rings
|
|
||||||
/* static */
|
|
||||||
struct zwp_tablet_pad_ring_v2 *ring;
|
|
||||||
struct wlr_wl_tablet_pad_group *group;
|
|
||||||
size_t index;
|
|
||||||
|
|
||||||
/* per frame */
|
|
||||||
enum wlr_tablet_pad_ring_source source;
|
|
||||||
double angle;
|
|
||||||
bool stopped;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wlr_wl_tablet_pad_strip {
|
|
||||||
struct wl_list link; // wlr_wl_tablet_pad_group::strips
|
|
||||||
struct zwp_tablet_pad_strip_v2 *strip;
|
|
||||||
struct wlr_wl_tablet_pad_group *group;
|
|
||||||
size_t index;
|
|
||||||
|
|
||||||
enum wlr_tablet_pad_strip_source source;
|
|
||||||
double position;
|
|
||||||
bool stopped;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wlr_wl_tablet_pad_group {
|
|
||||||
struct zwp_tablet_pad_group_v2 *pad_group;
|
|
||||||
struct wlr_tablet_pad *pad;
|
|
||||||
unsigned int mode;
|
|
||||||
|
|
||||||
struct wlr_tablet_pad_group group;
|
|
||||||
|
|
||||||
struct wl_list rings; // wlr_wl_tablet_pad_ring::link
|
|
||||||
struct wl_list strips; // wlr_wl_tablet_pad_strips::link
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_tablet_pad_ring_source(void *data,
|
|
||||||
struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2,
|
|
||||||
uint32_t source) {
|
|
||||||
struct wlr_wl_tablet_pad_ring *ring = data;
|
|
||||||
ring->source = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_pad_ring_angle(void *data,
|
|
||||||
struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2,
|
|
||||||
wl_fixed_t degrees) {
|
|
||||||
struct wlr_wl_tablet_pad_ring *ring = data;
|
|
||||||
ring->angle = wl_fixed_to_double(degrees);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_pad_ring_stop(void *data,
|
|
||||||
struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2) {
|
|
||||||
struct wlr_wl_tablet_pad_ring *ring = data;
|
|
||||||
ring->stopped = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_pad_ring_frame(void *data,
|
|
||||||
struct zwp_tablet_pad_ring_v2 *zwp_tablet_pad_ring_v2,
|
|
||||||
uint32_t time) {
|
|
||||||
struct wlr_wl_tablet_pad_ring *ring = data;
|
|
||||||
|
|
||||||
struct wlr_event_tablet_pad_ring evt = {
|
|
||||||
.time_msec = time,
|
|
||||||
.source = ring->source,
|
|
||||||
.ring = ring->index,
|
|
||||||
.position = ring->angle,
|
|
||||||
.mode = ring->group->mode,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (ring->angle >= 0) {
|
|
||||||
wlr_signal_emit_safe(&ring->group->pad->events.ring, &evt);
|
|
||||||
}
|
|
||||||
if (ring->stopped) {
|
|
||||||
evt.position = -1;
|
|
||||||
wlr_signal_emit_safe(&ring->group->pad->events.ring, &evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
ring->angle = -1;
|
|
||||||
ring->stopped = false;
|
|
||||||
ring->source = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_tablet_pad_ring_v2_listener tablet_pad_ring_listener = {
|
|
||||||
.source = handle_tablet_pad_ring_source,
|
|
||||||
.angle = handle_tablet_pad_ring_angle,
|
|
||||||
.stop = handle_tablet_pad_ring_stop,
|
|
||||||
.frame = handle_tablet_pad_ring_frame,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_tablet_pad_strip_source(void *data,
|
|
||||||
struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2,
|
|
||||||
uint32_t source) {
|
|
||||||
struct wlr_wl_tablet_pad_strip *strip = data;
|
|
||||||
strip->source = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_pad_strip_position(void *data,
|
|
||||||
struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2,
|
|
||||||
uint32_t position) {
|
|
||||||
struct wlr_wl_tablet_pad_strip *strip = data;
|
|
||||||
strip->position = (double) position / 65536.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_pad_strip_stop(void *data,
|
|
||||||
struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2) {
|
|
||||||
struct wlr_wl_tablet_pad_strip *strip = data;
|
|
||||||
strip->stopped = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_pad_strip_frame(void *data,
|
|
||||||
struct zwp_tablet_pad_strip_v2 *zwp_tablet_pad_strip_v2,
|
|
||||||
uint32_t time) {
|
|
||||||
struct wlr_wl_tablet_pad_strip *strip = data;
|
|
||||||
|
|
||||||
struct wlr_event_tablet_pad_strip evt = {
|
|
||||||
.time_msec = time,
|
|
||||||
.source = strip->source,
|
|
||||||
.strip = strip->index,
|
|
||||||
.position = strip->position,
|
|
||||||
.mode = strip->group->mode,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (strip->position >= 0) {
|
|
||||||
wlr_signal_emit_safe(&strip->group->pad->events.strip, &evt);
|
|
||||||
}
|
|
||||||
if (strip->stopped) {
|
|
||||||
evt.position = -1;
|
|
||||||
wlr_signal_emit_safe(&strip->group->pad->events.strip, &evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
strip->position = -1;
|
|
||||||
strip->stopped = false;
|
|
||||||
strip->source = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_tablet_pad_strip_v2_listener tablet_pad_strip_listener = {
|
|
||||||
.source = handle_tablet_pad_strip_source,
|
|
||||||
.position = handle_tablet_pad_strip_position,
|
|
||||||
.stop = handle_tablet_pad_strip_stop,
|
|
||||||
.frame = handle_tablet_pad_strip_frame,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_tablet_pad_group_buttons(void *data,
|
|
||||||
struct zwp_tablet_pad_group_v2 *pad_group,
|
|
||||||
struct wl_array *buttons) {
|
|
||||||
struct wlr_wl_tablet_pad_group *group = data;
|
|
||||||
|
|
||||||
free(group->group.buttons);
|
|
||||||
group->group.buttons = calloc(1, buttons->size);
|
|
||||||
if (!group->group.buttons) {
|
|
||||||
// FIXME: Add actual error handling
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
group->group.button_count = buttons->size / sizeof(int);
|
|
||||||
memcpy(group->group.buttons, buttons->data, buttons->size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_pad_group_modes(void *data,
|
|
||||||
struct zwp_tablet_pad_group_v2 *pad_group, uint32_t modes) {
|
|
||||||
struct wlr_wl_tablet_pad_group *group = data;
|
|
||||||
|
|
||||||
group->group.mode_count = modes;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_pad_group_ring(void *data,
|
|
||||||
struct zwp_tablet_pad_group_v2 *pad_group,
|
|
||||||
struct zwp_tablet_pad_ring_v2 *ring) {
|
|
||||||
struct wlr_wl_tablet_pad_group *group = data;
|
|
||||||
struct wlr_wl_tablet_pad_ring *tablet_ring =
|
|
||||||
calloc(1, sizeof(struct wlr_wl_tablet_pad_ring));
|
|
||||||
if (!tablet_ring) {
|
|
||||||
zwp_tablet_pad_ring_v2_destroy(ring);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
tablet_ring->index = group->pad->ring_count++;
|
|
||||||
tablet_ring->group = group;
|
|
||||||
zwp_tablet_pad_ring_v2_add_listener(ring, &tablet_pad_ring_listener,
|
|
||||||
tablet_ring);
|
|
||||||
|
|
||||||
group->group.rings = realloc(group->group.rings,
|
|
||||||
++group->group.ring_count * sizeof(unsigned int));
|
|
||||||
group->group.rings[group->group.ring_count - 1] =
|
|
||||||
tablet_ring->index;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_pad_group_strip(void *data,
|
|
||||||
struct zwp_tablet_pad_group_v2 *pad_group,
|
|
||||||
struct zwp_tablet_pad_strip_v2 *strip) {
|
|
||||||
struct wlr_wl_tablet_pad_group *group = data;
|
|
||||||
struct wlr_wl_tablet_pad_strip *tablet_strip =
|
|
||||||
calloc(1, sizeof(struct wlr_wl_tablet_pad_strip));
|
|
||||||
if (!tablet_strip) {
|
|
||||||
zwp_tablet_pad_strip_v2_destroy(strip);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
tablet_strip->index = group->pad->strip_count++;
|
|
||||||
tablet_strip->group = group;
|
|
||||||
zwp_tablet_pad_strip_v2_add_listener(strip, &tablet_pad_strip_listener,
|
|
||||||
tablet_strip);
|
|
||||||
|
|
||||||
group->group.strips = realloc(group->group.strips,
|
|
||||||
++group->group.strip_count * sizeof(unsigned int));
|
|
||||||
group->group.strips[group->group.strip_count - 1] =
|
|
||||||
tablet_strip->index;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_pad_group_done(void *data,
|
|
||||||
struct zwp_tablet_pad_group_v2 *pad_group) {
|
|
||||||
/* Empty for now */
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_pad_group_mode_switch(void *data,
|
|
||||||
struct zwp_tablet_pad_group_v2 *pad_group,
|
|
||||||
uint32_t time, uint32_t serial, uint32_t mode) {
|
|
||||||
struct wlr_wl_tablet_pad_group *group = data;
|
|
||||||
group->mode = mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This isn't in the listener, but keep the naming scheme around since the
|
|
||||||
* other removed functions work like this, and pad sub-resources are just a bit
|
|
||||||
* special */
|
|
||||||
static void handle_tablet_pad_group_removed(
|
|
||||||
struct wlr_wl_tablet_pad_group *group) {
|
|
||||||
|
|
||||||
/* No need to remove the ::link on strips rings as long as we do *not*
|
|
||||||
* wl_list_remove on the wl_groups ring/strip attributes here */
|
|
||||||
struct wlr_wl_tablet_pad_ring *ring, *tmp_ring;
|
|
||||||
wl_list_for_each_safe(ring, tmp_ring, &group->rings, link) {
|
|
||||||
zwp_tablet_pad_ring_v2_destroy(ring->ring);
|
|
||||||
free(ring);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_wl_tablet_pad_strip *strip, *tmp_strip;
|
|
||||||
wl_list_for_each_safe(strip, tmp_strip, &group->strips, link) {
|
|
||||||
zwp_tablet_pad_strip_v2_destroy(strip->strip);
|
|
||||||
free(strip);
|
|
||||||
}
|
|
||||||
|
|
||||||
zwp_tablet_pad_group_v2_destroy(group->pad_group);
|
|
||||||
|
|
||||||
/* While I'm pretty sure we have control over this as well, it's
|
|
||||||
* outside the scope of a single function, so better be safe than
|
|
||||||
* sorry */
|
|
||||||
wl_list_remove(&group->group.link);
|
|
||||||
free(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_tablet_pad_group_v2_listener tablet_pad_group_listener = {
|
|
||||||
.buttons = handle_tablet_pad_group_buttons,
|
|
||||||
.modes = handle_tablet_pad_group_modes,
|
|
||||||
.ring = handle_tablet_pad_group_ring,
|
|
||||||
.strip = handle_tablet_pad_group_strip,
|
|
||||||
.done = handle_tablet_pad_group_done,
|
|
||||||
.mode_switch = handle_tablet_pad_group_mode_switch,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_tablet_pad_group(void *data,
|
|
||||||
struct zwp_tablet_pad_v2 *zwp_tablet_pad,
|
|
||||||
struct zwp_tablet_pad_group_v2 *pad_group) {
|
|
||||||
struct wlr_wl_input_device *dev = data;
|
|
||||||
struct wlr_tablet_pad *pad = dev->wlr_input_device.tablet_pad;
|
|
||||||
|
|
||||||
struct wlr_wl_tablet_pad_group *group =
|
|
||||||
calloc(1, sizeof(struct wlr_wl_tablet_pad_group));
|
|
||||||
if (!group) {
|
|
||||||
zwp_tablet_pad_group_v2_destroy(pad_group);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
group->pad_group = pad_group;
|
|
||||||
group->pad = pad;
|
|
||||||
|
|
||||||
wl_list_init(&group->rings);
|
|
||||||
wl_list_init(&group->strips);
|
|
||||||
|
|
||||||
zwp_tablet_pad_group_v2_add_listener(pad_group,
|
|
||||||
&tablet_pad_group_listener, group);
|
|
||||||
|
|
||||||
wl_list_insert(&pad->groups, &group->group.link);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_pad_path(void *data,
|
|
||||||
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
|
|
||||||
const char *path) {
|
|
||||||
struct wlr_wl_input_device *dev = data;
|
|
||||||
struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad;
|
|
||||||
|
|
||||||
char **dst = wl_array_add(&tablet_pad->paths, sizeof(char *));
|
|
||||||
*dst = strdup(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_pad_buttons(void *data,
|
|
||||||
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
|
|
||||||
uint32_t buttons) {
|
|
||||||
struct wlr_wl_input_device *dev = data;
|
|
||||||
struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad;
|
|
||||||
|
|
||||||
tablet_pad->button_count = buttons;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_pad_button(void *data,
|
|
||||||
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
|
|
||||||
uint32_t time, uint32_t button, uint32_t state) {
|
|
||||||
struct wlr_wl_input_device *dev = data;
|
|
||||||
struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad;
|
|
||||||
|
|
||||||
struct wlr_event_tablet_pad_button evt = {
|
|
||||||
.time_msec = time,
|
|
||||||
.button = button,
|
|
||||||
.state = state,
|
|
||||||
.mode = 0,
|
|
||||||
.group = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
wlr_signal_emit_safe(&tablet_pad->events.button, &evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_pad_done(void *data,
|
|
||||||
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2) {
|
|
||||||
struct wlr_wl_input_device *dev = data;
|
|
||||||
|
|
||||||
wlr_signal_emit_safe(&dev->backend->backend.events.new_input,
|
|
||||||
&dev->wlr_input_device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_pad_enter(void *data,
|
|
||||||
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
|
|
||||||
uint32_t serial, struct zwp_tablet_v2 *tablet_p,
|
|
||||||
struct wl_surface *surface) {
|
|
||||||
struct wlr_wl_input_device *dev = data;
|
|
||||||
struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad;
|
|
||||||
struct wlr_wl_input_device *tab_dev = zwp_tablet_v2_get_user_data(tablet_p);
|
|
||||||
struct wlr_input_device *tablet = &tab_dev->wlr_input_device;
|
|
||||||
wlr_log(WLR_DEBUG, "Tablet: %p\n", tablet);
|
|
||||||
|
|
||||||
wlr_signal_emit_safe(&tablet_pad->events.attach_tablet, tablet);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_pad_leave(void *data,
|
|
||||||
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
|
|
||||||
uint32_t serial, struct wl_surface *surface) {
|
|
||||||
/* Empty. Probably staying that way, unless we want to create/destroy
|
|
||||||
* tablet on enter/leave events (ehh) */
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_pad_removed(void *data,
|
|
||||||
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2) {
|
|
||||||
struct wlr_wl_input_device *dev = data;
|
|
||||||
struct wlr_tablet_pad *tablet_pad = dev->wlr_input_device.tablet_pad;
|
|
||||||
|
|
||||||
/* This doesn't free anything, but emits the destroy signal */
|
|
||||||
wlr_input_device_destroy(&dev->wlr_input_device);
|
|
||||||
/* This is a bit ugly, but we need to remove it from our list */
|
|
||||||
wl_list_remove(&dev->link);
|
|
||||||
|
|
||||||
struct wlr_wl_tablet_pad_group *group, *it;
|
|
||||||
wl_list_for_each_safe(group, it, &tablet_pad->groups, group.link) {
|
|
||||||
handle_tablet_pad_group_removed(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This frees */
|
|
||||||
wlr_tablet_pad_destroy(tablet_pad);
|
|
||||||
zwp_tablet_pad_v2_destroy(dev->resource);
|
|
||||||
free(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_tablet_pad_v2_listener tablet_pad_listener = {
|
|
||||||
.group = handle_tablet_pad_group,
|
|
||||||
.path = handle_tablet_pad_path,
|
|
||||||
.buttons = handle_tablet_pad_buttons,
|
|
||||||
.button = handle_tablet_pad_button,
|
|
||||||
.done = handle_tablet_pad_done,
|
|
||||||
.enter = handle_tablet_pad_enter,
|
|
||||||
.leave = handle_tablet_pad_leave,
|
|
||||||
.removed = handle_tablet_pad_removed,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_pad_added(void *data,
|
|
||||||
struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2,
|
|
||||||
struct zwp_tablet_pad_v2 *id) {
|
|
||||||
wlr_log(WLR_DEBUG, "New tablet pad");
|
|
||||||
struct wlr_wl_seat *seat = data;
|
|
||||||
struct wlr_wl_input_device *dev = create_wl_input_device(
|
|
||||||
seat, WLR_INPUT_DEVICE_TABLET_PAD);
|
|
||||||
if (!dev) {
|
|
||||||
/* This leaks a couple of server-sent resource ids. iirc this
|
|
||||||
* shouldn't ever be a problem, but it isn't exactly nice
|
|
||||||
* either. */
|
|
||||||
zwp_tablet_pad_v2_destroy(id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev->resource = id;
|
|
||||||
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
|
|
||||||
wlr_dev->tablet_pad = calloc(1, sizeof(*wlr_dev->tablet_pad));
|
|
||||||
|
|
||||||
if (!wlr_dev->tablet_pad) {
|
|
||||||
/* This leaks a couple of server-sent resource ids. iirc this
|
|
||||||
* shouldn't ever be a problem, but it isn't exactly nice
|
|
||||||
* either. */
|
|
||||||
free(dev);
|
|
||||||
zwp_tablet_pad_v2_destroy(id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wlr_tablet_pad_init(wlr_dev->tablet_pad, NULL);
|
|
||||||
zwp_tablet_pad_v2_add_listener(id, &tablet_pad_listener, dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_tool_done(void *data,
|
|
||||||
struct zwp_tablet_tool_v2 *id) {
|
|
||||||
/* empty */
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum wlr_tablet_tool_type tablet_type_to_wlr_type(enum zwp_tablet_tool_v2_type type) {
|
|
||||||
switch (type) {
|
|
||||||
case ZWP_TABLET_TOOL_V2_TYPE_PEN:
|
|
||||||
return WLR_TABLET_TOOL_TYPE_PEN;
|
|
||||||
case ZWP_TABLET_TOOL_V2_TYPE_ERASER:
|
|
||||||
return WLR_TABLET_TOOL_TYPE_ERASER;
|
|
||||||
case ZWP_TABLET_TOOL_V2_TYPE_BRUSH:
|
|
||||||
return WLR_TABLET_TOOL_TYPE_BRUSH;
|
|
||||||
case ZWP_TABLET_TOOL_V2_TYPE_PENCIL:
|
|
||||||
return WLR_TABLET_TOOL_TYPE_PENCIL;
|
|
||||||
case ZWP_TABLET_TOOL_V2_TYPE_AIRBRUSH:
|
|
||||||
return WLR_TABLET_TOOL_TYPE_AIRBRUSH;
|
|
||||||
case ZWP_TABLET_TOOL_V2_TYPE_MOUSE:
|
|
||||||
return WLR_TABLET_TOOL_TYPE_MOUSE;
|
|
||||||
case ZWP_TABLET_TOOL_V2_TYPE_LENS:
|
|
||||||
return WLR_TABLET_TOOL_TYPE_LENS;
|
|
||||||
case ZWP_TABLET_TOOL_V2_TYPE_FINGER:
|
|
||||||
// unused, see:
|
|
||||||
// https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/18
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
abort(); // unreachable
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_tool_type(void *data,
|
|
||||||
struct zwp_tablet_tool_v2 *id,
|
|
||||||
uint32_t tool_type) {
|
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
|
||||||
|
|
||||||
tool->wlr_tool.type = tablet_type_to_wlr_type(tool_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_tool_serial(void *data,
|
|
||||||
struct zwp_tablet_tool_v2 *id,
|
|
||||||
uint32_t high, uint32_t low) {
|
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
|
||||||
|
|
||||||
tool->wlr_tool.hardware_serial =
|
|
||||||
((uint64_t) high) << 32 | (uint64_t) low;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_tool_id_wacom(void *data,
|
|
||||||
struct zwp_tablet_tool_v2 *id,
|
|
||||||
uint32_t high, uint32_t low) {
|
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
|
||||||
|
|
||||||
tool->wlr_tool.hardware_wacom =
|
|
||||||
((uint64_t) high) << 32 | (uint64_t) low;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_tool_capability(void *data,
|
|
||||||
struct zwp_tablet_tool_v2 *id,
|
|
||||||
uint32_t capability) {
|
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
|
||||||
|
|
||||||
enum zwp_tablet_tool_v2_capability cap = capability;
|
|
||||||
|
|
||||||
switch (cap) {
|
|
||||||
case ZWP_TABLET_TOOL_V2_CAPABILITY_TILT:
|
|
||||||
tool->wlr_tool.tilt = true;
|
|
||||||
break;
|
|
||||||
case ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE:
|
|
||||||
tool->wlr_tool.pressure = true;
|
|
||||||
break;
|
|
||||||
case ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE:
|
|
||||||
tool->wlr_tool.distance = true;
|
|
||||||
break;
|
|
||||||
case ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION:
|
|
||||||
tool->wlr_tool.rotation = true;
|
|
||||||
break;
|
|
||||||
case ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER:
|
|
||||||
tool->wlr_tool.slider = true;
|
|
||||||
break;
|
|
||||||
case ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL:
|
|
||||||
tool->wlr_tool.wheel = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_tool_proximity_in(void *data,
|
|
||||||
struct zwp_tablet_tool_v2 *id, uint32_t serial,
|
|
||||||
struct zwp_tablet_v2 *tablet_id,
|
|
||||||
struct wl_surface *surface) {
|
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
|
||||||
tool->is_in = true;
|
|
||||||
tool->tablet = zwp_tablet_v2_get_user_data(tablet_id);
|
|
||||||
tool->output = wl_surface_get_user_data(surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_tool_proximity_out(void *data,
|
|
||||||
struct zwp_tablet_tool_v2 *id) {
|
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
|
||||||
tool->is_out = true;
|
|
||||||
tool->output = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_tool_down(void *data,
|
|
||||||
struct zwp_tablet_tool_v2 *id,
|
|
||||||
unsigned int serial) {
|
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
|
||||||
tool->is_down = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_tool_up(void *data,
|
|
||||||
struct zwp_tablet_tool_v2 *id) {
|
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
|
||||||
tool->is_up = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_tool_motion(void *data,
|
|
||||||
struct zwp_tablet_tool_v2 *id,
|
|
||||||
wl_fixed_t x, wl_fixed_t y) {
|
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
|
||||||
struct wlr_wl_output *output = tool->output;
|
|
||||||
assert(output);
|
|
||||||
|
|
||||||
tool->x = wl_fixed_to_double(x) / output->wlr_output.width;
|
|
||||||
tool->y = wl_fixed_to_double(y) / output->wlr_output.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_tool_pressure(void *data,
|
|
||||||
struct zwp_tablet_tool_v2 *id,
|
|
||||||
uint32_t pressure) {
|
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
|
||||||
tool->pressure = (double) pressure / 65535.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_tool_distance(void *data,
|
|
||||||
struct zwp_tablet_tool_v2 *id,
|
|
||||||
uint32_t distance) {
|
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
|
||||||
tool->distance = (double) distance / 65535.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_tool_tilt(void *data,
|
|
||||||
struct zwp_tablet_tool_v2 *id,
|
|
||||||
wl_fixed_t x, wl_fixed_t y) {
|
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
|
||||||
tool->tilt_x = wl_fixed_to_double(x);
|
|
||||||
tool->tilt_y = wl_fixed_to_double(y);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_tool_rotation(void *data,
|
|
||||||
struct zwp_tablet_tool_v2 *id,
|
|
||||||
wl_fixed_t rotation) {
|
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
|
||||||
tool->rotation = wl_fixed_to_double(rotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_tool_slider(void *data,
|
|
||||||
struct zwp_tablet_tool_v2 *id,
|
|
||||||
int slider) {
|
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
|
||||||
tool->slider = (double) slider / 65535.0;;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: This looks wrong :/
|
|
||||||
static void handle_tablet_tool_wheel(void *data,
|
|
||||||
struct zwp_tablet_tool_v2 *id,
|
|
||||||
wl_fixed_t degree, int clicks) {
|
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
|
||||||
tool->wheel_delta = wl_fixed_to_double(degree);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_tool_button(void *data,
|
|
||||||
struct zwp_tablet_tool_v2 *id,
|
|
||||||
uint32_t serial, uint32_t button, uint32_t state) {
|
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
|
||||||
struct wlr_tablet *tablet = tool->tablet->wlr_input_device.tablet;
|
|
||||||
|
|
||||||
struct wlr_event_tablet_tool_button evt = {
|
|
||||||
.device = &tool->tablet->wlr_input_device,
|
|
||||||
.tool = &tool->wlr_tool,
|
|
||||||
.time_msec = get_current_time_msec(),
|
|
||||||
.button = button,
|
|
||||||
.state = state == ZWP_TABLET_TOOL_V2_BUTTON_STATE_RELEASED ?
|
|
||||||
WLR_BUTTON_RELEASED : WLR_BUTTON_PRESSED,
|
|
||||||
};
|
|
||||||
|
|
||||||
wlr_signal_emit_safe(&tablet->events.button, &evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void clear_tablet_tool_values(struct wlr_wl_tablet_tool *tool) {
|
|
||||||
tool->is_out = tool->is_in = false;
|
|
||||||
tool->is_up = tool->is_down = false;
|
|
||||||
tool->x = tool->y = NAN;
|
|
||||||
tool->pressure = NAN;
|
|
||||||
tool->distance = NAN;
|
|
||||||
tool->tilt_x = tool->tilt_y = NAN;
|
|
||||||
tool->rotation = NAN;
|
|
||||||
tool->slider = NAN;
|
|
||||||
tool->wheel_delta = NAN;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_tool_frame(void *data,
|
|
||||||
struct zwp_tablet_tool_v2 *id,
|
|
||||||
uint32_t time) {
|
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
|
||||||
if (tool->is_out && tool->is_in) {
|
|
||||||
/* we got a tablet tool coming in and out of proximity before
|
|
||||||
* we could process it. Just ignore anything it did */
|
|
||||||
goto clear_values;
|
|
||||||
}
|
|
||||||
struct wlr_tablet *tablet = tool->tablet->wlr_input_device.tablet;
|
|
||||||
|
|
||||||
if (tool->is_in) {
|
|
||||||
struct wlr_event_tablet_tool_proximity evt = {
|
|
||||||
.device = &tool->tablet->wlr_input_device,
|
|
||||||
.tool = &tool->wlr_tool,
|
|
||||||
.time_msec = time,
|
|
||||||
.x = tool->x,
|
|
||||||
.y = tool->y,
|
|
||||||
.state = WLR_TABLET_TOOL_PROXIMITY_IN,
|
|
||||||
};
|
|
||||||
|
|
||||||
wlr_signal_emit_safe(&tablet->events.proximity, &evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
struct wlr_event_tablet_tool_axis evt = {
|
|
||||||
.device = &tool->tablet->wlr_input_device,
|
|
||||||
.tool = &tool->wlr_tool,
|
|
||||||
.time_msec = time,
|
|
||||||
.updated_axes = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!isnan(tool->x) && !tool->is_in) {
|
|
||||||
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_X;
|
|
||||||
evt.x = tool->x;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isnan(tool->y) && !tool->is_in) {
|
|
||||||
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_Y;
|
|
||||||
evt.y = tool->y;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isnan(tool->pressure)) {
|
|
||||||
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_PRESSURE;
|
|
||||||
evt.pressure = tool->pressure;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isnan(tool->distance)) {
|
|
||||||
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_DISTANCE;
|
|
||||||
evt.distance = tool->distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isnan(tool->tilt_x)) {
|
|
||||||
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_TILT_X;
|
|
||||||
evt.tilt_x = tool->tilt_x;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isnan(tool->tilt_y)) {
|
|
||||||
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_TILT_Y;
|
|
||||||
evt.tilt_y = tool->tilt_y;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isnan(tool->rotation)) {
|
|
||||||
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_ROTATION;
|
|
||||||
evt.rotation = tool->rotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isnan(tool->slider)) {
|
|
||||||
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_SLIDER;
|
|
||||||
evt.slider = tool->slider;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isnan(tool->wheel_delta)) {
|
|
||||||
evt.updated_axes |= WLR_TABLET_TOOL_AXIS_WHEEL;
|
|
||||||
evt.wheel_delta = tool->wheel_delta;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (evt.updated_axes) {
|
|
||||||
wlr_signal_emit_safe(&tablet->events.axis, &evt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This will always send down then up if we got both.
|
|
||||||
* Maybe we should send them right away, in case we get up then both in
|
|
||||||
* series?
|
|
||||||
* Downside: Here we have the frame time, if we sent right away, we
|
|
||||||
* need to generate the time */
|
|
||||||
if (tool->is_down) {
|
|
||||||
struct wlr_event_tablet_tool_tip evt = {
|
|
||||||
.device = &tool->tablet->wlr_input_device,
|
|
||||||
.tool = &tool->wlr_tool,
|
|
||||||
.time_msec = time,
|
|
||||||
.x = tool->x,
|
|
||||||
.y = tool->y,
|
|
||||||
.state = WLR_TABLET_TOOL_TIP_DOWN,
|
|
||||||
};
|
|
||||||
|
|
||||||
wlr_signal_emit_safe(&tablet->events.tip, &evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tool->is_up) {
|
|
||||||
struct wlr_event_tablet_tool_tip evt = {
|
|
||||||
.device = &tool->tablet->wlr_input_device,
|
|
||||||
.tool = &tool->wlr_tool,
|
|
||||||
.time_msec = time,
|
|
||||||
.x = tool->x,
|
|
||||||
.y = tool->y,
|
|
||||||
.state = WLR_TABLET_TOOL_TIP_UP,
|
|
||||||
};
|
|
||||||
|
|
||||||
wlr_signal_emit_safe(&tablet->events.tip, &evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tool->is_out) {
|
|
||||||
struct wlr_event_tablet_tool_proximity evt = {
|
|
||||||
.device = &tool->tablet->wlr_input_device,
|
|
||||||
.tool = &tool->wlr_tool,
|
|
||||||
.time_msec = time,
|
|
||||||
.x = tool->x,
|
|
||||||
.y = tool->y,
|
|
||||||
.state = WLR_TABLET_TOOL_PROXIMITY_OUT,
|
|
||||||
};
|
|
||||||
|
|
||||||
wlr_signal_emit_safe(&tablet->events.proximity, &evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
clear_values:
|
|
||||||
clear_tablet_tool_values(tool);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_tool_removed(void *data,
|
|
||||||
struct zwp_tablet_tool_v2 *id) {
|
|
||||||
struct wlr_wl_tablet_tool *tool = data;
|
|
||||||
|
|
||||||
zwp_tablet_tool_v2_destroy(tool->tool);
|
|
||||||
wlr_signal_emit_safe(&tool->wlr_tool.events.destroy, &tool->wlr_tool);
|
|
||||||
free(tool);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = {
|
|
||||||
.removed = handle_tablet_tool_removed,
|
|
||||||
.done = handle_tablet_tool_done,
|
|
||||||
.type = handle_tablet_tool_type,
|
|
||||||
.hardware_serial = handle_tablet_tool_serial,
|
|
||||||
.hardware_id_wacom = handle_tablet_tool_id_wacom,
|
|
||||||
.capability = handle_tablet_tool_capability,
|
|
||||||
|
|
||||||
.proximity_in = handle_tablet_tool_proximity_in,
|
|
||||||
.proximity_out = handle_tablet_tool_proximity_out,
|
|
||||||
.down = handle_tablet_tool_down,
|
|
||||||
.up = handle_tablet_tool_up,
|
|
||||||
|
|
||||||
.motion = handle_tablet_tool_motion,
|
|
||||||
.pressure = handle_tablet_tool_pressure,
|
|
||||||
.distance = handle_tablet_tool_distance,
|
|
||||||
.tilt = handle_tablet_tool_tilt,
|
|
||||||
.rotation = handle_tablet_tool_rotation,
|
|
||||||
.slider = handle_tablet_tool_slider,
|
|
||||||
.wheel = handle_tablet_tool_wheel,
|
|
||||||
.button = handle_tablet_tool_button,
|
|
||||||
.frame = handle_tablet_tool_frame,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_tool_added(void *data,
|
|
||||||
struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2,
|
|
||||||
struct zwp_tablet_tool_v2 *id) {
|
|
||||||
wlr_log(WLR_DEBUG, "New tablet tool");
|
|
||||||
struct wlr_wl_tablet_tool *tool = calloc(1, sizeof(*tool));
|
|
||||||
if (!tool) {
|
|
||||||
zwp_tablet_tool_v2_destroy(id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
tool->tool = id;
|
|
||||||
clear_tablet_tool_values(tool);
|
|
||||||
wl_signal_init(&tool->wlr_tool.events.destroy);
|
|
||||||
zwp_tablet_tool_v2_add_listener(id, &tablet_tool_listener, tool);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_name(void *data, struct zwp_tablet_v2 *zwp_tablet_v2,
|
|
||||||
const char *name) {
|
|
||||||
struct wlr_wl_input_device *dev = data;
|
|
||||||
struct wlr_tablet *tablet = dev->wlr_input_device.tablet;
|
|
||||||
|
|
||||||
free(tablet->name);
|
|
||||||
tablet->name = strdup(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_id(void *data, struct zwp_tablet_v2 *zwp_tablet_v2,
|
|
||||||
uint32_t vid, uint32_t pid) {
|
|
||||||
struct wlr_wl_input_device *dev = data;
|
|
||||||
dev->wlr_input_device.vendor = vid;
|
|
||||||
dev->wlr_input_device.product = pid;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_path(void *data, struct zwp_tablet_v2 *zwp_tablet_v2,
|
|
||||||
const char *path) {
|
|
||||||
struct wlr_wl_input_device *dev = data;
|
|
||||||
struct wlr_tablet *tablet = dev->wlr_input_device.tablet;
|
|
||||||
|
|
||||||
char **dst = wl_array_add(&tablet->paths, sizeof(char *));
|
|
||||||
*dst = strdup(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_done(void *data, struct zwp_tablet_v2 *zwp_tablet_v2) {
|
|
||||||
struct wlr_wl_input_device *dev = data;
|
|
||||||
|
|
||||||
wlr_signal_emit_safe(&dev->backend->backend.events.new_input,
|
|
||||||
&dev->wlr_input_device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_tablet_removed(void *data,
|
|
||||||
struct zwp_tablet_v2 *zwp_tablet_v2) {
|
|
||||||
struct wlr_wl_input_device *dev = data;
|
|
||||||
|
|
||||||
/* This doesn't free anything, but emits the destroy signal */
|
|
||||||
wlr_input_device_destroy(&dev->wlr_input_device);
|
|
||||||
/* This is a bit ugly, but we need to remove it from our list */
|
|
||||||
wl_list_remove(&dev->link);
|
|
||||||
|
|
||||||
zwp_tablet_v2_destroy(dev->resource);
|
|
||||||
free(dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_tablet_v2_listener tablet_listener = {
|
|
||||||
.name = handle_tablet_name,
|
|
||||||
.id = handle_tablet_id,
|
|
||||||
.path = handle_tablet_path,
|
|
||||||
.done = handle_tablet_done,
|
|
||||||
.removed = handle_tablet_removed,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_tab_added(void *data,
|
|
||||||
struct zwp_tablet_seat_v2 *zwp_tablet_seat_v2,
|
|
||||||
struct zwp_tablet_v2 *id) {
|
|
||||||
wlr_log(WLR_DEBUG, "New tablet");
|
|
||||||
struct wlr_wl_seat *seat = data;
|
|
||||||
struct wlr_wl_input_device *dev = create_wl_input_device(
|
|
||||||
seat, WLR_INPUT_DEVICE_TABLET_TOOL);
|
|
||||||
|
|
||||||
if (!dev) {
|
|
||||||
zwp_tablet_v2_destroy(id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
dev->resource = id;
|
|
||||||
|
|
||||||
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
|
|
||||||
wlr_dev->tablet = calloc(1, sizeof(*wlr_dev->tablet));
|
|
||||||
|
|
||||||
if (!wlr_dev->tablet) {
|
|
||||||
zwp_tablet_v2_destroy(id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
zwp_tablet_v2_set_user_data(id, wlr_dev->tablet);
|
|
||||||
wlr_tablet_init(wlr_dev->tablet, NULL);
|
|
||||||
zwp_tablet_v2_add_listener(id, &tablet_listener, dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = {
|
|
||||||
.tablet_added = handle_tab_added,
|
|
||||||
.tool_added = handle_tool_added,
|
|
||||||
.pad_added = handle_pad_added,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wlr_wl_tablet_seat *wl_add_tablet_seat(
|
|
||||||
struct zwp_tablet_manager_v2 *manager,
|
|
||||||
struct wlr_wl_seat *seat) {
|
|
||||||
struct wlr_wl_tablet_seat *ret =
|
|
||||||
calloc(1, sizeof(struct wlr_wl_tablet_seat));
|
|
||||||
|
|
||||||
if (!(ret->tablet_seat =
|
|
||||||
zwp_tablet_manager_v2_get_tablet_seat(manager, seat->wl_seat))) {
|
|
||||||
free(ret);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
zwp_tablet_seat_v2_add_listener(ret->tablet_seat,
|
|
||||||
&tablet_seat_listener, seat);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
|
@ -0,0 +1,455 @@
|
||||||
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <wayland-client.h>
|
||||||
|
|
||||||
|
#include <wlr/interfaces/wlr_input_device.h>
|
||||||
|
#include <wlr/interfaces/wlr_keyboard.h>
|
||||||
|
#include <wlr/interfaces/wlr_output.h>
|
||||||
|
#include <wlr/interfaces/wlr_pointer.h>
|
||||||
|
#include <wlr/interfaces/wlr_touch.h>
|
||||||
|
#include <wlr/util/log.h>
|
||||||
|
|
||||||
|
#include "backend/wayland.h"
|
||||||
|
#include "util/signal.h"
|
||||||
|
|
||||||
|
static struct wlr_wl_pointer *output_get_pointer(struct wlr_wl_output *output) {
|
||||||
|
struct wlr_input_device *wlr_dev;
|
||||||
|
wl_list_for_each(wlr_dev, &output->backend->devices, link) {
|
||||||
|
if (wlr_dev->type != WLR_INPUT_DEVICE_POINTER) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_dev->pointer);
|
||||||
|
if (pointer->output == output) {
|
||||||
|
return pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
|
||||||
|
uint32_t serial, struct wl_surface *surface, wl_fixed_t sx,
|
||||||
|
wl_fixed_t sy) {
|
||||||
|
struct wlr_wl_backend *backend = data;
|
||||||
|
if (surface == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_wl_output *output = wl_surface_get_user_data(surface);
|
||||||
|
assert(output);
|
||||||
|
struct wlr_wl_pointer *pointer = output_get_pointer(output);
|
||||||
|
|
||||||
|
output->enter_serial = serial;
|
||||||
|
backend->current_pointer = pointer;
|
||||||
|
update_wl_output_cursor(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
|
||||||
|
uint32_t serial, struct wl_surface *surface) {
|
||||||
|
struct wlr_wl_backend *backend = data;
|
||||||
|
if (surface == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_wl_output *output = wl_surface_get_user_data(surface);
|
||||||
|
assert(output);
|
||||||
|
output->enter_serial = 0;
|
||||||
|
|
||||||
|
if (backend->current_pointer == NULL ||
|
||||||
|
backend->current_pointer->output != output) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
backend->current_pointer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
|
||||||
|
uint32_t time, wl_fixed_t sx, wl_fixed_t sy) {
|
||||||
|
struct wlr_wl_backend *backend = data;
|
||||||
|
struct wlr_wl_pointer *pointer = backend->current_pointer;
|
||||||
|
if (pointer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_output *wlr_output = &pointer->output->wlr_output;
|
||||||
|
struct wlr_event_pointer_motion_absolute event = {
|
||||||
|
.device = &pointer->input_device->wlr_input_device,
|
||||||
|
.time_msec = time,
|
||||||
|
.x = wl_fixed_to_double(sx) / wlr_output->width,
|
||||||
|
.y = wl_fixed_to_double(sy) / wlr_output->height,
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&pointer->wlr_pointer.events.motion_absolute, &event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
|
||||||
|
uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
|
||||||
|
struct wlr_wl_backend *backend = data;
|
||||||
|
struct wlr_wl_pointer *pointer = backend->current_pointer;
|
||||||
|
if (pointer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_event_pointer_button event = {
|
||||||
|
.device = &pointer->input_device->wlr_input_device,
|
||||||
|
.button = button,
|
||||||
|
.state = state,
|
||||||
|
.time_msec = time,
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&pointer->wlr_pointer.events.button, &event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
|
||||||
|
uint32_t time, uint32_t axis, wl_fixed_t value) {
|
||||||
|
struct wlr_wl_backend *backend = data;
|
||||||
|
struct wlr_wl_pointer *pointer = backend->current_pointer;
|
||||||
|
if (pointer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_event_pointer_axis event = {
|
||||||
|
.device = &pointer->input_device->wlr_input_device,
|
||||||
|
.delta = wl_fixed_to_double(value),
|
||||||
|
.delta_discrete = pointer->axis_discrete,
|
||||||
|
.orientation = axis,
|
||||||
|
.time_msec = time,
|
||||||
|
.source = pointer->axis_source,
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&pointer->wlr_pointer.events.axis, &event);
|
||||||
|
|
||||||
|
pointer->axis_discrete = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) {
|
||||||
|
struct wlr_wl_backend *backend = data;
|
||||||
|
struct wlr_wl_pointer *pointer = backend->current_pointer;
|
||||||
|
if (pointer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wlr_signal_emit_safe(&pointer->wlr_pointer.events.frame,
|
||||||
|
&pointer->wlr_pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_axis_source(void *data,
|
||||||
|
struct wl_pointer *wl_pointer, uint32_t axis_source) {
|
||||||
|
struct wlr_wl_backend *backend = data;
|
||||||
|
struct wlr_wl_pointer *pointer = backend->current_pointer;
|
||||||
|
if (pointer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer->axis_source = axis_source;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_axis_stop(void *data, struct wl_pointer *wl_pointer,
|
||||||
|
uint32_t time, uint32_t axis) {
|
||||||
|
struct wlr_wl_backend *backend = data;
|
||||||
|
struct wlr_wl_pointer *pointer = backend->current_pointer;
|
||||||
|
if (pointer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_event_pointer_axis event = {
|
||||||
|
.device = &pointer->input_device->wlr_input_device,
|
||||||
|
.delta = 0,
|
||||||
|
.delta_discrete = 0,
|
||||||
|
.orientation = axis,
|
||||||
|
.time_msec = time,
|
||||||
|
.source = pointer->axis_source,
|
||||||
|
};
|
||||||
|
wlr_signal_emit_safe(&pointer->wlr_pointer.events.axis, &event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_handle_axis_discrete(void *data,
|
||||||
|
struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) {
|
||||||
|
struct wlr_wl_backend *backend = data;
|
||||||
|
struct wlr_wl_pointer *pointer = backend->current_pointer;
|
||||||
|
if (pointer == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer->axis_discrete = discrete;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_pointer_listener pointer_listener = {
|
||||||
|
.enter = pointer_handle_enter,
|
||||||
|
.leave = pointer_handle_leave,
|
||||||
|
.motion = pointer_handle_motion,
|
||||||
|
.button = pointer_handle_button,
|
||||||
|
.axis = pointer_handle_axis,
|
||||||
|
.frame = pointer_handle_frame,
|
||||||
|
.axis_source = pointer_handle_axis_source,
|
||||||
|
.axis_stop = pointer_handle_axis_stop,
|
||||||
|
.axis_discrete = pointer_handle_axis_discrete,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
|
uint32_t format, int32_t fd, uint32_t size) {
|
||||||
|
// TODO: set keymap
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t get_current_time_msec() {
|
||||||
|
struct timespec now;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
return now.tv_nsec / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_handle_enter(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
|
uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
|
||||||
|
struct wlr_input_device *dev = data;
|
||||||
|
|
||||||
|
uint32_t time = get_current_time_msec();
|
||||||
|
|
||||||
|
uint32_t *keycode_ptr;
|
||||||
|
wl_array_for_each(keycode_ptr, keys) {
|
||||||
|
struct wlr_event_keyboard_key event = {
|
||||||
|
.keycode = *keycode_ptr,
|
||||||
|
.state = WLR_KEY_PRESSED,
|
||||||
|
.time_msec = time,
|
||||||
|
.update_state = false,
|
||||||
|
};
|
||||||
|
wlr_keyboard_notify_key(dev->keyboard, &event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
|
uint32_t serial, struct wl_surface *surface) {
|
||||||
|
struct wlr_input_device *dev = data;
|
||||||
|
|
||||||
|
uint32_t time = get_current_time_msec();
|
||||||
|
|
||||||
|
uint32_t pressed[dev->keyboard->num_keycodes + 1];
|
||||||
|
memcpy(pressed, dev->keyboard->keycodes,
|
||||||
|
dev->keyboard->num_keycodes * sizeof(uint32_t));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof(pressed)/sizeof(pressed[0]); ++i) {
|
||||||
|
uint32_t keycode = pressed[i];
|
||||||
|
|
||||||
|
struct wlr_event_keyboard_key event = {
|
||||||
|
.keycode = keycode,
|
||||||
|
.state = WLR_KEY_RELEASED,
|
||||||
|
.time_msec = time,
|
||||||
|
.update_state = false,
|
||||||
|
};
|
||||||
|
wlr_keyboard_notify_key(dev->keyboard, &event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
|
uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
|
||||||
|
struct wlr_input_device *dev = data;
|
||||||
|
assert(dev && dev->keyboard);
|
||||||
|
|
||||||
|
struct wlr_event_keyboard_key wlr_event = {
|
||||||
|
.keycode = key,
|
||||||
|
.state = state,
|
||||||
|
.time_msec = time,
|
||||||
|
.update_state = false,
|
||||||
|
};
|
||||||
|
wlr_keyboard_notify_key(dev->keyboard, &wlr_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
|
uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
|
||||||
|
uint32_t mods_locked, uint32_t group) {
|
||||||
|
struct wlr_input_device *dev = data;
|
||||||
|
assert(dev && dev->keyboard);
|
||||||
|
wlr_keyboard_notify_modifiers(dev->keyboard, mods_depressed, mods_latched,
|
||||||
|
mods_locked, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyboard_handle_repeat_info(void *data,
|
||||||
|
struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay) {
|
||||||
|
// This space is intentionally left blank
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wl_keyboard_listener keyboard_listener = {
|
||||||
|
.keymap = keyboard_handle_keymap,
|
||||||
|
.enter = keyboard_handle_enter,
|
||||||
|
.leave = keyboard_handle_leave,
|
||||||
|
.key = keyboard_handle_key,
|
||||||
|
.modifiers = keyboard_handle_modifiers,
|
||||||
|
.repeat_info = keyboard_handle_repeat_info
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct wlr_wl_input_device *get_wl_input_device_from_input_device(
|
||||||
|
struct wlr_input_device *wlr_dev) {
|
||||||
|
assert(wlr_input_device_is_wl(wlr_dev));
|
||||||
|
return (struct wlr_wl_input_device *)wlr_dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void input_device_destroy(struct wlr_input_device *wlr_dev) {
|
||||||
|
struct wlr_wl_input_device *dev =
|
||||||
|
get_wl_input_device_from_input_device(wlr_dev);
|
||||||
|
if (dev->resource) {
|
||||||
|
wl_proxy_destroy(dev->resource);
|
||||||
|
}
|
||||||
|
wl_list_remove(&dev->wlr_input_device.link);
|
||||||
|
free(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wlr_input_device_impl input_device_impl = {
|
||||||
|
.destroy = input_device_destroy,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool wlr_input_device_is_wl(struct wlr_input_device *dev) {
|
||||||
|
return dev->impl == &input_device_impl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wlr_wl_input_device *create_wl_input_device(
|
||||||
|
struct wlr_wl_backend *backend, enum wlr_input_device_type type) {
|
||||||
|
struct wlr_wl_input_device *dev =
|
||||||
|
calloc(1, sizeof(struct wlr_wl_input_device));
|
||||||
|
if (dev == NULL) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
dev->backend = backend;
|
||||||
|
|
||||||
|
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
|
||||||
|
|
||||||
|
unsigned int vendor = 0, product = 0;
|
||||||
|
const char *name = "wayland";
|
||||||
|
wlr_input_device_init(wlr_dev, type, &input_device_impl, name, vendor,
|
||||||
|
product);
|
||||||
|
wl_list_insert(&backend->devices, &wlr_dev->link);
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wlr_pointer_impl pointer_impl;
|
||||||
|
|
||||||
|
struct wlr_wl_pointer *pointer_get_wl(struct wlr_pointer *wlr_pointer) {
|
||||||
|
assert(wlr_pointer->impl == &pointer_impl);
|
||||||
|
return (struct wlr_wl_pointer *)wlr_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pointer_destroy(struct wlr_pointer *wlr_pointer) {
|
||||||
|
struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_pointer);
|
||||||
|
wl_list_remove(&pointer->output_destroy.link);
|
||||||
|
free(pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wlr_pointer_impl pointer_impl = {
|
||||||
|
.destroy = pointer_destroy,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void pointer_handle_output_destroy(struct wl_listener *listener,
|
||||||
|
void *data) {
|
||||||
|
struct wlr_wl_pointer *pointer =
|
||||||
|
wl_container_of(listener, pointer, output_destroy);
|
||||||
|
wlr_input_device_destroy(&pointer->input_device->wlr_input_device);
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *output) {
|
||||||
|
struct wlr_wl_backend *backend = output->backend;
|
||||||
|
|
||||||
|
struct wlr_input_device *wlr_dev;
|
||||||
|
wl_list_for_each(wlr_dev, &output->backend->devices, link) {
|
||||||
|
if (wlr_dev->type != WLR_INPUT_DEVICE_POINTER) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
struct wlr_wl_pointer *pointer = pointer_get_wl(wlr_dev->pointer);
|
||||||
|
if (pointer->output == output) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_wl_pointer *pointer = calloc(1, sizeof(struct wlr_wl_pointer));
|
||||||
|
if (pointer == NULL) {
|
||||||
|
wlr_log(WLR_ERROR, "Allocation failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pointer->wl_pointer = wl_pointer;
|
||||||
|
pointer->output = output;
|
||||||
|
|
||||||
|
wl_signal_add(&output->wlr_output.events.destroy, &pointer->output_destroy);
|
||||||
|
pointer->output_destroy.notify = pointer_handle_output_destroy;
|
||||||
|
|
||||||
|
struct wlr_wl_input_device *dev =
|
||||||
|
create_wl_input_device(backend, WLR_INPUT_DEVICE_POINTER);
|
||||||
|
if (dev == NULL) {
|
||||||
|
free(pointer);
|
||||||
|
wlr_log(WLR_ERROR, "Allocation failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pointer->input_device = dev;
|
||||||
|
|
||||||
|
wlr_dev = &dev->wlr_input_device;
|
||||||
|
wlr_dev->pointer = &pointer->wlr_pointer;
|
||||||
|
wlr_dev->output_name = strdup(output->wlr_output.name);
|
||||||
|
wlr_pointer_init(wlr_dev->pointer, &pointer_impl);
|
||||||
|
|
||||||
|
wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void create_wl_keyboard(struct wl_keyboard *wl_keyboard, struct wlr_wl_backend *wl) {
|
||||||
|
struct wlr_wl_input_device *dev =
|
||||||
|
create_wl_input_device(wl, WLR_INPUT_DEVICE_KEYBOARD);
|
||||||
|
if (!dev) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_input_device *wlr_dev = &dev->wlr_input_device;
|
||||||
|
|
||||||
|
wlr_dev->keyboard = calloc(1, sizeof(*wlr_dev->keyboard));
|
||||||
|
if (!wlr_dev->keyboard) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
||||||
|
free(dev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wlr_keyboard_init(wlr_dev->keyboard, NULL);
|
||||||
|
|
||||||
|
wl_keyboard_add_listener(wl_keyboard, &keyboard_listener, wlr_dev);
|
||||||
|
dev->resource = wl_keyboard;
|
||||||
|
wlr_signal_emit_safe(&wl->backend.events.new_input, wlr_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
|
||||||
|
enum wl_seat_capability caps) {
|
||||||
|
struct wlr_wl_backend *backend = data;
|
||||||
|
assert(backend->seat == wl_seat);
|
||||||
|
|
||||||
|
if ((caps & WL_SEAT_CAPABILITY_POINTER)) {
|
||||||
|
wlr_log(WLR_DEBUG, "seat %p offered pointer", (void*) wl_seat);
|
||||||
|
|
||||||
|
struct wl_pointer *wl_pointer = wl_seat_get_pointer(wl_seat);
|
||||||
|
backend->pointer = wl_pointer;
|
||||||
|
|
||||||
|
struct wlr_wl_output *output;
|
||||||
|
wl_list_for_each(output, &backend->outputs, link) {
|
||||||
|
create_wl_pointer(wl_pointer, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_pointer_add_listener(wl_pointer, &pointer_listener, backend);
|
||||||
|
}
|
||||||
|
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD)) {
|
||||||
|
wlr_log(WLR_DEBUG, "seat %p offered keyboard", (void*) wl_seat);
|
||||||
|
|
||||||
|
struct wl_keyboard *wl_keyboard = wl_seat_get_keyboard(wl_seat);
|
||||||
|
backend->keyboard = wl_keyboard;
|
||||||
|
|
||||||
|
if (backend->started) {
|
||||||
|
create_wl_keyboard(wl_keyboard, backend);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void seat_handle_name(void *data, struct wl_seat *wl_seat,
|
||||||
|
const char *name) {
|
||||||
|
struct wlr_wl_backend *backend = data;
|
||||||
|
assert(backend->seat == wl_seat);
|
||||||
|
// Do we need to check if seatName was previously set for name change?
|
||||||
|
free(backend->seat_name);
|
||||||
|
backend->seat_name = strdup(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct wl_seat_listener seat_listener = {
|
||||||
|
.capabilities = seat_handle_capabilities,
|
||||||
|
.name = seat_handle_name,
|
||||||
|
};
|
|
@ -1,24 +1,17 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200112L
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <fcntl.h>
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
|
||||||
#include <xf86drm.h>
|
|
||||||
|
|
||||||
#include <wlr/config.h>
|
#include <wlr/config.h>
|
||||||
|
|
||||||
#include <drm_fourcc.h>
|
#include <X11/Xlib-xcb.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server.h>
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
#include <xcb/dri3.h>
|
|
||||||
#include <xcb/present.h>
|
|
||||||
#include <xcb/render.h>
|
|
||||||
#include <xcb/shm.h>
|
|
||||||
#include <xcb/xcb_renderutil.h>
|
|
||||||
#include <xcb/xfixes.h>
|
#include <xcb/xfixes.h>
|
||||||
#include <xcb/xinput.h>
|
#include <xcb/xinput.h>
|
||||||
|
|
||||||
|
@ -27,27 +20,13 @@
|
||||||
#include <wlr/interfaces/wlr_input_device.h>
|
#include <wlr/interfaces/wlr_input_device.h>
|
||||||
#include <wlr/interfaces/wlr_keyboard.h>
|
#include <wlr/interfaces/wlr_keyboard.h>
|
||||||
#include <wlr/interfaces/wlr_pointer.h>
|
#include <wlr/interfaces/wlr_pointer.h>
|
||||||
|
#include <wlr/render/egl.h>
|
||||||
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
|
||||||
#include "backend/x11.h"
|
#include "backend/x11.h"
|
||||||
#include "render/drm_format_set.h"
|
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
|
|
||||||
// See dri2_format_for_depth in mesa
|
|
||||||
const struct wlr_x11_format formats[] = {
|
|
||||||
{ .drm = DRM_FORMAT_XRGB8888, .depth = 24, .bpp = 32 },
|
|
||||||
{ .drm = DRM_FORMAT_ARGB8888, .depth = 32, .bpp = 32 },
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct wlr_x11_format *x11_format_from_depth(uint8_t depth) {
|
|
||||||
for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) {
|
|
||||||
if (formats[i].depth == depth) {
|
|
||||||
return &formats[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_x11_output *get_x11_output_from_window_id(
|
struct wlr_x11_output *get_x11_output_from_window_id(
|
||||||
struct wlr_x11_backend *x11, xcb_window_t window) {
|
struct wlr_x11_backend *x11, xcb_window_t window) {
|
||||||
struct wlr_x11_output *output;
|
struct wlr_x11_output *output;
|
||||||
|
@ -59,10 +38,6 @@ struct wlr_x11_output *get_x11_output_from_window_id(
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_x11_error(struct wlr_x11_backend *x11, xcb_value_error_t *ev);
|
|
||||||
static void handle_x11_unknown_event(struct wlr_x11_backend *x11,
|
|
||||||
xcb_generic_event_t *ev);
|
|
||||||
|
|
||||||
static void handle_x11_event(struct wlr_x11_backend *x11,
|
static void handle_x11_event(struct wlr_x11_backend *x11,
|
||||||
xcb_generic_event_t *event) {
|
xcb_generic_event_t *event) {
|
||||||
switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) {
|
switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) {
|
||||||
|
@ -71,10 +46,7 @@ static void handle_x11_event(struct wlr_x11_backend *x11,
|
||||||
struct wlr_x11_output *output =
|
struct wlr_x11_output *output =
|
||||||
get_x11_output_from_window_id(x11, ev->window);
|
get_x11_output_from_window_id(x11, ev->window);
|
||||||
if (output != NULL) {
|
if (output != NULL) {
|
||||||
pixman_region32_union_rect(
|
wlr_output_update_needs_swap(&output->wlr_output);
|
||||||
&output->exposed, &output->exposed,
|
|
||||||
ev->x, ev->y, ev->width, ev->height);
|
|
||||||
wlr_output_update_needs_frame(&output->wlr_output);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -96,9 +68,6 @@ static void handle_x11_event(struct wlr_x11_backend *x11,
|
||||||
if (output != NULL) {
|
if (output != NULL) {
|
||||||
wlr_output_destroy(&output->wlr_output);
|
wlr_output_destroy(&output->wlr_output);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
wlr_log(WLR_DEBUG, "Unhandled client message %"PRIu32,
|
|
||||||
ev->data.data32[0]);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -106,24 +75,8 @@ static void handle_x11_event(struct wlr_x11_backend *x11,
|
||||||
xcb_ge_generic_event_t *ev = (xcb_ge_generic_event_t *)event;
|
xcb_ge_generic_event_t *ev = (xcb_ge_generic_event_t *)event;
|
||||||
if (ev->extension == x11->xinput_opcode) {
|
if (ev->extension == x11->xinput_opcode) {
|
||||||
handle_x11_xinput_event(x11, ev);
|
handle_x11_xinput_event(x11, ev);
|
||||||
} else if (ev->extension == x11->present_opcode) {
|
|
||||||
handle_x11_present_event(x11, ev);
|
|
||||||
} else {
|
|
||||||
handle_x11_unknown_event(x11, event);
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case 0: {
|
|
||||||
xcb_value_error_t *ev = (xcb_value_error_t *)event;
|
|
||||||
handle_x11_error(x11, ev);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case XCB_UNMAP_NOTIFY:
|
|
||||||
case XCB_MAP_NOTIFY:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
handle_x11_unknown_event(x11, event);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,9 +84,6 @@ static int x11_event(int fd, uint32_t mask, void *data) {
|
||||||
struct wlr_x11_backend *x11 = data;
|
struct wlr_x11_backend *x11 = data;
|
||||||
|
|
||||||
if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) {
|
if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) {
|
||||||
if (mask & WL_EVENT_ERROR) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to read from X11 server");
|
|
||||||
}
|
|
||||||
wl_display_terminate(x11->wl_display);
|
wl_display_terminate(x11->wl_display);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -144,12 +94,6 @@ static int x11_event(int fd, uint32_t mask, void *data) {
|
||||||
free(e);
|
free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ret = xcb_connection_has_error(x11->xcb);
|
|
||||||
if (ret != 0) {
|
|
||||||
wlr_log(WLR_ERROR, "X11 connection error (%d)", ret);
|
|
||||||
wl_display_terminate(x11->wl_display);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,8 +107,6 @@ static bool backend_start(struct wlr_backend *backend) {
|
||||||
struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend);
|
struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend);
|
||||||
x11->started = true;
|
x11->started = true;
|
||||||
|
|
||||||
wlr_log(WLR_INFO, "Starting X11 backend");
|
|
||||||
|
|
||||||
wlr_signal_emit_safe(&x11->backend.events.new_input, &x11->keyboard_dev);
|
wlr_signal_emit_safe(&x11->backend.events.new_input, &x11->keyboard_dev);
|
||||||
|
|
||||||
for (size_t i = 0; i < x11->requested_outputs; ++i) {
|
for (size_t i = 0; i < x11->requested_outputs; ++i) {
|
||||||
|
@ -188,43 +130,32 @@ static void backend_destroy(struct wlr_backend *backend) {
|
||||||
|
|
||||||
wlr_input_device_destroy(&x11->keyboard_dev);
|
wlr_input_device_destroy(&x11->keyboard_dev);
|
||||||
|
|
||||||
wlr_backend_finish(backend);
|
wlr_signal_emit_safe(&backend->events.destroy, backend);
|
||||||
|
|
||||||
if (x11->event_source) {
|
if (x11->event_source) {
|
||||||
wl_event_source_remove(x11->event_source);
|
wl_event_source_remove(x11->event_source);
|
||||||
}
|
}
|
||||||
wl_list_remove(&x11->display_destroy.link);
|
wl_list_remove(&x11->display_destroy.link);
|
||||||
|
|
||||||
wlr_drm_format_set_finish(&x11->primary_dri3_formats);
|
wlr_renderer_destroy(x11->renderer);
|
||||||
wlr_drm_format_set_finish(&x11->primary_shm_formats);
|
wlr_egl_finish(&x11->egl);
|
||||||
wlr_drm_format_set_finish(&x11->dri3_formats);
|
|
||||||
wlr_drm_format_set_finish(&x11->shm_formats);
|
|
||||||
|
|
||||||
#if HAS_XCB_ERRORS
|
if (x11->xlib_conn) {
|
||||||
xcb_errors_context_free(x11->errors_context);
|
XCloseDisplay(x11->xlib_conn);
|
||||||
#endif
|
}
|
||||||
|
|
||||||
close(x11->drm_fd);
|
|
||||||
xcb_disconnect(x11->xcb);
|
|
||||||
free(x11);
|
free(x11);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int backend_get_drm_fd(struct wlr_backend *backend) {
|
static struct wlr_renderer *backend_get_renderer(
|
||||||
|
struct wlr_backend *backend) {
|
||||||
struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend);
|
struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend);
|
||||||
return x11->drm_fd;
|
return x11->renderer;
|
||||||
}
|
|
||||||
|
|
||||||
static uint32_t get_buffer_caps(struct wlr_backend *backend) {
|
|
||||||
struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend);
|
|
||||||
return (x11->have_dri3 ? WLR_BUFFER_CAP_DMABUF : 0)
|
|
||||||
| (x11->have_shm ? WLR_BUFFER_CAP_SHM : 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct wlr_backend_impl backend_impl = {
|
static const struct wlr_backend_impl backend_impl = {
|
||||||
.start = backend_start,
|
.start = backend_start,
|
||||||
.destroy = backend_destroy,
|
.destroy = backend_destroy,
|
||||||
.get_drm_fd = backend_get_drm_fd,
|
.get_renderer = backend_get_renderer,
|
||||||
.get_buffer_caps = get_buffer_caps,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool wlr_backend_is_x11(struct wlr_backend *backend) {
|
bool wlr_backend_is_x11(struct wlr_backend *backend) {
|
||||||
|
@ -237,165 +168,9 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
||||||
backend_destroy(&x11->backend);
|
backend_destroy(&x11->backend);
|
||||||
}
|
}
|
||||||
|
|
||||||
static xcb_depth_t *get_depth(xcb_screen_t *screen, uint8_t depth) {
|
|
||||||
xcb_depth_iterator_t iter = xcb_screen_allowed_depths_iterator(screen);
|
|
||||||
while (iter.rem > 0) {
|
|
||||||
if (iter.data->depth == depth) {
|
|
||||||
return iter.data;
|
|
||||||
}
|
|
||||||
xcb_depth_next(&iter);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static xcb_visualid_t pick_visualid(xcb_depth_t *depth) {
|
|
||||||
xcb_visualtype_t *visuals = xcb_depth_visuals(depth);
|
|
||||||
for (int i = 0; i < xcb_depth_visuals_length(depth); i++) {
|
|
||||||
if (visuals[i]._class == XCB_VISUAL_CLASS_TRUE_COLOR) {
|
|
||||||
return visuals[i].visual_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int query_dri3_drm_fd(struct wlr_x11_backend *x11) {
|
|
||||||
xcb_dri3_open_cookie_t open_cookie =
|
|
||||||
xcb_dri3_open(x11->xcb, x11->screen->root, 0);
|
|
||||||
xcb_dri3_open_reply_t *open_reply =
|
|
||||||
xcb_dri3_open_reply(x11->xcb, open_cookie, NULL);
|
|
||||||
if (open_reply == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int *open_fds = xcb_dri3_open_reply_fds(x11->xcb, open_reply);
|
|
||||||
if (open_fds == NULL) {
|
|
||||||
free(open_reply);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(open_reply->nfd == 1);
|
|
||||||
int drm_fd = open_fds[0];
|
|
||||||
|
|
||||||
free(open_reply);
|
|
||||||
|
|
||||||
int flags = fcntl(drm_fd, F_GETFD);
|
|
||||||
if (flags < 0) {
|
|
||||||
close(drm_fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (fcntl(drm_fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
|
|
||||||
close(drm_fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (drmGetNodeTypeFromFd(drm_fd) != DRM_NODE_RENDER) {
|
|
||||||
char *render_name = drmGetRenderDeviceNameFromFd(drm_fd);
|
|
||||||
if (render_name == NULL) {
|
|
||||||
close(drm_fd);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
close(drm_fd);
|
|
||||||
drm_fd = open(render_name, O_RDWR | O_CLOEXEC);
|
|
||||||
if (drm_fd < 0) {
|
|
||||||
free(render_name);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(render_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return drm_fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool query_dri3_modifiers(struct wlr_x11_backend *x11,
|
|
||||||
const struct wlr_x11_format *format) {
|
|
||||||
if (x11->dri3_major_version == 1 && x11->dri3_minor_version < 2) {
|
|
||||||
return true; // GetSupportedModifiers requires DRI3 1.2
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query the root window's supported modifiers, because we only care about
|
|
||||||
// screen_modifiers for now
|
|
||||||
xcb_dri3_get_supported_modifiers_cookie_t modifiers_cookie =
|
|
||||||
xcb_dri3_get_supported_modifiers(x11->xcb, x11->screen->root,
|
|
||||||
format->depth, format->bpp);
|
|
||||||
xcb_dri3_get_supported_modifiers_reply_t *modifiers_reply =
|
|
||||||
xcb_dri3_get_supported_modifiers_reply(x11->xcb, modifiers_cookie,
|
|
||||||
NULL);
|
|
||||||
if (!modifiers_reply) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to get DMA-BUF modifiers supported by "
|
|
||||||
"the X11 server for the format 0x%"PRIX32, format->drm);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If modifiers aren't supported, DRI3 will return an empty list
|
|
||||||
const uint64_t *modifiers =
|
|
||||||
xcb_dri3_get_supported_modifiers_screen_modifiers(modifiers_reply);
|
|
||||||
int modifiers_len =
|
|
||||||
xcb_dri3_get_supported_modifiers_screen_modifiers_length(modifiers_reply);
|
|
||||||
for (int i = 0; i < modifiers_len; i++) {
|
|
||||||
wlr_drm_format_set_add(&x11->dri3_formats, format->drm, modifiers[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(modifiers_reply);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool query_formats(struct wlr_x11_backend *x11) {
|
|
||||||
xcb_depth_iterator_t iter = xcb_screen_allowed_depths_iterator(x11->screen);
|
|
||||||
while (iter.rem > 0) {
|
|
||||||
uint8_t depth = iter.data->depth;
|
|
||||||
|
|
||||||
const struct wlr_x11_format *format = x11_format_from_depth(depth);
|
|
||||||
if (format != NULL) {
|
|
||||||
if (x11->have_shm) {
|
|
||||||
wlr_drm_format_set_add(&x11->shm_formats, format->drm,
|
|
||||||
DRM_FORMAT_MOD_INVALID);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x11->have_dri3) {
|
|
||||||
// X11 always supports implicit modifiers
|
|
||||||
wlr_drm_format_set_add(&x11->dri3_formats, format->drm,
|
|
||||||
DRM_FORMAT_MOD_INVALID);
|
|
||||||
if (!query_dri3_modifiers(x11, format)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xcb_depth_next(&iter);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void x11_get_argb32(struct wlr_x11_backend *x11) {
|
|
||||||
xcb_render_query_pict_formats_cookie_t cookie =
|
|
||||||
xcb_render_query_pict_formats(x11->xcb);
|
|
||||||
xcb_render_query_pict_formats_reply_t *reply =
|
|
||||||
xcb_render_query_pict_formats_reply(x11->xcb, cookie, NULL);
|
|
||||||
if (!reply) {
|
|
||||||
wlr_log(WLR_ERROR, "Did not get any reply from xcb_render_query_pict_formats");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
xcb_render_pictforminfo_t *format =
|
|
||||||
xcb_render_util_find_standard_format(reply, XCB_PICT_STANDARD_ARGB_32);
|
|
||||||
|
|
||||||
if (format == NULL) {
|
|
||||||
wlr_log(WLR_DEBUG, "No ARGB_32 render format");
|
|
||||||
free(reply);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
x11->argb32 = format->id;
|
|
||||||
free(reply);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
||||||
const char *x11_display) {
|
const char *x11_display,
|
||||||
wlr_log(WLR_INFO, "Creating X11 backend");
|
wlr_renderer_create_func_t create_renderer_func) {
|
||||||
|
|
||||||
struct wlr_x11_backend *x11 = calloc(1, sizeof(*x11));
|
struct wlr_x11_backend *x11 = calloc(1, sizeof(*x11));
|
||||||
if (!x11) {
|
if (!x11) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -405,12 +180,20 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
||||||
x11->wl_display = display;
|
x11->wl_display = display;
|
||||||
wl_list_init(&x11->outputs);
|
wl_list_init(&x11->outputs);
|
||||||
|
|
||||||
x11->xcb = xcb_connect(x11_display, NULL);
|
x11->xlib_conn = XOpenDisplay(x11_display);
|
||||||
if (!x11->xcb || xcb_connection_has_error(x11->xcb)) {
|
if (!x11->xlib_conn) {
|
||||||
wlr_log(WLR_ERROR, "Failed to open xcb connection");
|
wlr_log(WLR_ERROR, "Failed to open X connection");
|
||||||
goto error_x11;
|
goto error_x11;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
x11->xcb = XGetXCBConnection(x11->xlib_conn);
|
||||||
|
if (!x11->xcb || xcb_connection_has_error(x11->xcb)) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to open xcb connection");
|
||||||
|
goto error_display;
|
||||||
|
}
|
||||||
|
|
||||||
|
XSetEventQueueOwner(x11->xlib_conn, XCBOwnsEventQueue);
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
const char *name;
|
const char *name;
|
||||||
xcb_intern_atom_cookie_t cookie;
|
xcb_intern_atom_cookie_t cookie;
|
||||||
|
@ -420,7 +203,6 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
||||||
{ .name = "WM_DELETE_WINDOW", .atom = &x11->atoms.wm_delete_window },
|
{ .name = "WM_DELETE_WINDOW", .atom = &x11->atoms.wm_delete_window },
|
||||||
{ .name = "_NET_WM_NAME", .atom = &x11->atoms.net_wm_name },
|
{ .name = "_NET_WM_NAME", .atom = &x11->atoms.net_wm_name },
|
||||||
{ .name = "UTF8_STRING", .atom = &x11->atoms.utf8_string },
|
{ .name = "UTF8_STRING", .atom = &x11->atoms.utf8_string },
|
||||||
{ .name = "_VARIABLE_REFRESH", .atom = &x11->atoms.variable_refresh },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for (size_t i = 0; i < sizeof(atom) / sizeof(atom[0]); ++i) {
|
for (size_t i = 0; i < sizeof(atom) / sizeof(atom[0]); ++i) {
|
||||||
|
@ -442,80 +224,6 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
||||||
|
|
||||||
const xcb_query_extension_reply_t *ext;
|
const xcb_query_extension_reply_t *ext;
|
||||||
|
|
||||||
// DRI3 extension
|
|
||||||
|
|
||||||
ext = xcb_get_extension_data(x11->xcb, &xcb_dri3_id);
|
|
||||||
if (ext && ext->present) {
|
|
||||||
xcb_dri3_query_version_cookie_t dri3_cookie =
|
|
||||||
xcb_dri3_query_version(x11->xcb, 1, 2);
|
|
||||||
xcb_dri3_query_version_reply_t *dri3_reply =
|
|
||||||
xcb_dri3_query_version_reply(x11->xcb, dri3_cookie, NULL);
|
|
||||||
if (dri3_reply && dri3_reply->major_version >= 1) {
|
|
||||||
x11->have_dri3 = true;
|
|
||||||
x11->dri3_major_version = dri3_reply->major_version;
|
|
||||||
x11->dri3_minor_version = dri3_reply->minor_version;
|
|
||||||
} else {
|
|
||||||
wlr_log(WLR_INFO, "X11 does not support required DRI3 version "
|
|
||||||
"(has %"PRIu32".%"PRIu32", want 1.0)",
|
|
||||||
dri3_reply->major_version, dri3_reply->minor_version);
|
|
||||||
}
|
|
||||||
free(dri3_reply);
|
|
||||||
} else {
|
|
||||||
wlr_log(WLR_INFO, "X11 does not support DRI3 extension");
|
|
||||||
}
|
|
||||||
|
|
||||||
// SHM extension
|
|
||||||
|
|
||||||
ext = xcb_get_extension_data(x11->xcb, &xcb_shm_id);
|
|
||||||
if (ext && ext->present) {
|
|
||||||
xcb_shm_query_version_cookie_t shm_cookie =
|
|
||||||
xcb_shm_query_version(x11->xcb);
|
|
||||||
xcb_shm_query_version_reply_t *shm_reply =
|
|
||||||
xcb_shm_query_version_reply(x11->xcb, shm_cookie, NULL);
|
|
||||||
if (shm_reply) {
|
|
||||||
if (shm_reply->major_version >= 1 || shm_reply->minor_version >= 2) {
|
|
||||||
if (shm_reply->shared_pixmaps) {
|
|
||||||
x11->have_shm = true;
|
|
||||||
} else {
|
|
||||||
wlr_log(WLR_INFO, "X11 does not support shared pixmaps");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
wlr_log(WLR_INFO, "X11 does not support required SHM version "
|
|
||||||
"(has %"PRIu32".%"PRIu32", want 1.2)",
|
|
||||||
shm_reply->major_version, shm_reply->minor_version);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
wlr_log(WLR_INFO, "X11 does not support required SHM version");
|
|
||||||
}
|
|
||||||
free(shm_reply);
|
|
||||||
} else {
|
|
||||||
wlr_log(WLR_INFO, "X11 does not support SHM extension");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Present extension
|
|
||||||
|
|
||||||
ext = xcb_get_extension_data(x11->xcb, &xcb_present_id);
|
|
||||||
if (!ext || !ext->present) {
|
|
||||||
wlr_log(WLR_ERROR, "X11 does not support Present extension");
|
|
||||||
goto error_display;
|
|
||||||
}
|
|
||||||
x11->present_opcode = ext->major_opcode;
|
|
||||||
|
|
||||||
xcb_present_query_version_cookie_t present_cookie =
|
|
||||||
xcb_present_query_version(x11->xcb, 1, 2);
|
|
||||||
xcb_present_query_version_reply_t *present_reply =
|
|
||||||
xcb_present_query_version_reply(x11->xcb, present_cookie, NULL);
|
|
||||||
if (!present_reply || present_reply->major_version < 1) {
|
|
||||||
wlr_log(WLR_ERROR, "X11 does not support required Present version "
|
|
||||||
"(has %"PRIu32".%"PRIu32", want 1.0)",
|
|
||||||
present_reply->major_version, present_reply->minor_version);
|
|
||||||
free(present_reply);
|
|
||||||
goto error_display;
|
|
||||||
}
|
|
||||||
free(present_reply);
|
|
||||||
|
|
||||||
// Xfixes extension
|
|
||||||
|
|
||||||
ext = xcb_get_extension_data(x11->xcb, &xcb_xfixes_id);
|
ext = xcb_get_extension_data(x11->xcb, &xcb_xfixes_id);
|
||||||
if (!ext || !ext->present) {
|
if (!ext || !ext->present) {
|
||||||
wlr_log(WLR_ERROR, "X11 does not support Xfixes extension");
|
wlr_log(WLR_ERROR, "X11 does not support Xfixes extension");
|
||||||
|
@ -526,17 +234,14 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
||||||
xcb_xfixes_query_version(x11->xcb, 4, 0);
|
xcb_xfixes_query_version(x11->xcb, 4, 0);
|
||||||
xcb_xfixes_query_version_reply_t *fixes_reply =
|
xcb_xfixes_query_version_reply_t *fixes_reply =
|
||||||
xcb_xfixes_query_version_reply(x11->xcb, fixes_cookie, NULL);
|
xcb_xfixes_query_version_reply(x11->xcb, fixes_cookie, NULL);
|
||||||
|
|
||||||
if (!fixes_reply || fixes_reply->major_version < 4) {
|
if (!fixes_reply || fixes_reply->major_version < 4) {
|
||||||
wlr_log(WLR_ERROR, "X11 does not support required Xfixes version "
|
wlr_log(WLR_ERROR, "X11 does not support required Xfixes version");
|
||||||
"(has %"PRIu32".%"PRIu32", want 4.0)",
|
|
||||||
fixes_reply->major_version, fixes_reply->minor_version);
|
|
||||||
free(fixes_reply);
|
free(fixes_reply);
|
||||||
goto error_display;
|
goto error_display;
|
||||||
}
|
}
|
||||||
free(fixes_reply);
|
free(fixes_reply);
|
||||||
|
|
||||||
// Xinput extension
|
|
||||||
|
|
||||||
ext = xcb_get_extension_data(x11->xcb, &xcb_input_id);
|
ext = xcb_get_extension_data(x11->xcb, &xcb_input_id);
|
||||||
if (!ext || !ext->present) {
|
if (!ext || !ext->present) {
|
||||||
wlr_log(WLR_ERROR, "X11 does not support Xinput extension");
|
wlr_log(WLR_ERROR, "X11 does not support Xinput extension");
|
||||||
|
@ -548,10 +253,9 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
||||||
xcb_input_xi_query_version(x11->xcb, 2, 0);
|
xcb_input_xi_query_version(x11->xcb, 2, 0);
|
||||||
xcb_input_xi_query_version_reply_t *xi_reply =
|
xcb_input_xi_query_version_reply_t *xi_reply =
|
||||||
xcb_input_xi_query_version_reply(x11->xcb, xi_cookie, NULL);
|
xcb_input_xi_query_version_reply(x11->xcb, xi_cookie, NULL);
|
||||||
|
|
||||||
if (!xi_reply || xi_reply->major_version < 2) {
|
if (!xi_reply || xi_reply->major_version < 2) {
|
||||||
wlr_log(WLR_ERROR, "X11 does not support required Xinput version "
|
wlr_log(WLR_ERROR, "X11 does not support required Xinput version");
|
||||||
"(has %"PRIu32".%"PRIu32", want 2.0)",
|
|
||||||
xi_reply->major_version, xi_reply->minor_version);
|
|
||||||
free(xi_reply);
|
free(xi_reply);
|
||||||
goto error_display;
|
goto error_display;
|
||||||
}
|
}
|
||||||
|
@ -568,76 +272,28 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
||||||
wl_event_source_check(x11->event_source);
|
wl_event_source_check(x11->event_source);
|
||||||
|
|
||||||
x11->screen = xcb_setup_roots_iterator(xcb_get_setup(x11->xcb)).data;
|
x11->screen = xcb_setup_roots_iterator(xcb_get_setup(x11->xcb)).data;
|
||||||
if (!x11->screen) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to get X11 screen");
|
if (!create_renderer_func) {
|
||||||
|
create_renderer_func = wlr_renderer_autocreate;
|
||||||
|
}
|
||||||
|
|
||||||
|
static EGLint config_attribs[] = {
|
||||||
|
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||||
|
EGL_RED_SIZE, 1,
|
||||||
|
EGL_GREEN_SIZE, 1,
|
||||||
|
EGL_BLUE_SIZE, 1,
|
||||||
|
EGL_ALPHA_SIZE, 0,
|
||||||
|
EGL_NONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
x11->renderer = create_renderer_func(&x11->egl, EGL_PLATFORM_X11_KHR,
|
||||||
|
x11->xlib_conn, config_attribs, x11->screen->root_visual);
|
||||||
|
|
||||||
|
if (x11->renderer == NULL) {
|
||||||
|
wlr_log(WLR_ERROR, "Failed to create renderer");
|
||||||
goto error_event;
|
goto error_event;
|
||||||
}
|
}
|
||||||
|
|
||||||
x11->depth = get_depth(x11->screen, 24);
|
|
||||||
if (!x11->depth) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to get 24-bit depth for X11 screen");
|
|
||||||
goto error_event;
|
|
||||||
}
|
|
||||||
|
|
||||||
x11->visualid = pick_visualid(x11->depth);
|
|
||||||
if (!x11->visualid) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to pick X11 visual");
|
|
||||||
goto error_event;
|
|
||||||
}
|
|
||||||
|
|
||||||
x11->x11_format = x11_format_from_depth(x11->depth->depth);
|
|
||||||
if (!x11->x11_format) {
|
|
||||||
wlr_log(WLR_ERROR, "Unsupported depth %"PRIu8, x11->depth->depth);
|
|
||||||
goto error_event;
|
|
||||||
}
|
|
||||||
|
|
||||||
x11->colormap = xcb_generate_id(x11->xcb);
|
|
||||||
xcb_create_colormap(x11->xcb, XCB_COLORMAP_ALLOC_NONE, x11->colormap,
|
|
||||||
x11->screen->root, x11->visualid);
|
|
||||||
|
|
||||||
if (!query_formats(x11)) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to query supported DRM formats");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
x11->drm_fd = -1;
|
|
||||||
if (x11->have_dri3) {
|
|
||||||
// DRI3 may return a render node (Xwayland) or an authenticated primary
|
|
||||||
// node (plain Glamor).
|
|
||||||
x11->drm_fd = query_dri3_drm_fd(x11);
|
|
||||||
if (x11->drm_fd < 0) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to query DRI3 DRM FD");
|
|
||||||
goto error_event;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Windows can only display buffers with the depth they were created with
|
|
||||||
// TODO: look into changing the window's depth at runtime
|
|
||||||
const struct wlr_drm_format *dri3_format =
|
|
||||||
wlr_drm_format_set_get(&x11->dri3_formats, x11->x11_format->drm);
|
|
||||||
if (x11->have_dri3 && dri3_format != NULL) {
|
|
||||||
wlr_drm_format_set_add(&x11->primary_dri3_formats,
|
|
||||||
dri3_format->format, DRM_FORMAT_MOD_INVALID);
|
|
||||||
for (size_t i = 0; i < dri3_format->len; i++) {
|
|
||||||
wlr_drm_format_set_add(&x11->primary_dri3_formats,
|
|
||||||
dri3_format->format, dri3_format->modifiers[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct wlr_drm_format *shm_format =
|
|
||||||
wlr_drm_format_set_get(&x11->shm_formats, x11->x11_format->drm);
|
|
||||||
if (x11->have_shm && shm_format != NULL) {
|
|
||||||
wlr_drm_format_set_add(&x11->primary_shm_formats,
|
|
||||||
shm_format->format, DRM_FORMAT_MOD_INVALID);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if HAS_XCB_ERRORS
|
|
||||||
if (xcb_errors_context_new(x11->xcb, &x11->errors_context) != 0) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to create error context");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
wlr_input_device_init(&x11->keyboard_dev, WLR_INPUT_DEVICE_KEYBOARD,
|
wlr_input_device_init(&x11->keyboard_dev, WLR_INPUT_DEVICE_KEYBOARD,
|
||||||
&input_device_impl, "X11 keyboard", 0, 0);
|
&input_device_impl, "X11 keyboard", 0, 0);
|
||||||
wlr_keyboard_init(&x11->keyboard, &keyboard_impl);
|
wlr_keyboard_init(&x11->keyboard, &keyboard_impl);
|
||||||
|
@ -646,87 +302,13 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
|
||||||
x11->display_destroy.notify = handle_display_destroy;
|
x11->display_destroy.notify = handle_display_destroy;
|
||||||
wl_display_add_destroy_listener(display, &x11->display_destroy);
|
wl_display_add_destroy_listener(display, &x11->display_destroy);
|
||||||
|
|
||||||
// Create an empty pixmap to be used as the cursor. The
|
|
||||||
// default GC foreground is 0, and that is what it will be
|
|
||||||
// filled with.
|
|
||||||
xcb_pixmap_t blank = xcb_generate_id(x11->xcb);
|
|
||||||
xcb_create_pixmap(x11->xcb, 1, blank, x11->screen->root, 1, 1);
|
|
||||||
xcb_gcontext_t gc = xcb_generate_id(x11->xcb);
|
|
||||||
xcb_create_gc(x11->xcb, gc, blank, 0, NULL);
|
|
||||||
xcb_rectangle_t rect = { .x = 0, .y = 0, .width = 1, .height = 1 };
|
|
||||||
xcb_poly_fill_rectangle(x11->xcb, blank, gc, 1, &rect);
|
|
||||||
|
|
||||||
x11->transparent_cursor = xcb_generate_id(x11->xcb);
|
|
||||||
xcb_create_cursor(x11->xcb, x11->transparent_cursor, blank, blank,
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0);
|
|
||||||
|
|
||||||
xcb_free_gc(x11->xcb, gc);
|
|
||||||
xcb_free_pixmap(x11->xcb, blank);
|
|
||||||
|
|
||||||
x11_get_argb32(x11);
|
|
||||||
|
|
||||||
return &x11->backend;
|
return &x11->backend;
|
||||||
|
|
||||||
error_event:
|
error_event:
|
||||||
wl_event_source_remove(x11->event_source);
|
wl_event_source_remove(x11->event_source);
|
||||||
error_display:
|
error_display:
|
||||||
xcb_disconnect(x11->xcb);
|
XCloseDisplay(x11->xlib_conn);
|
||||||
error_x11:
|
error_x11:
|
||||||
free(x11);
|
free(x11);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_x11_error(struct wlr_x11_backend *x11, xcb_value_error_t *ev) {
|
|
||||||
#if HAS_XCB_ERRORS
|
|
||||||
const char *major_name = xcb_errors_get_name_for_major_code(
|
|
||||||
x11->errors_context, ev->major_opcode);
|
|
||||||
if (!major_name) {
|
|
||||||
wlr_log(WLR_DEBUG, "X11 error happened, but could not get major name");
|
|
||||||
goto log_raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *minor_name = xcb_errors_get_name_for_minor_code(
|
|
||||||
x11->errors_context, ev->major_opcode, ev->minor_opcode);
|
|
||||||
|
|
||||||
const char *extension;
|
|
||||||
const char *error_name = xcb_errors_get_name_for_error(x11->errors_context,
|
|
||||||
ev->error_code, &extension);
|
|
||||||
if (!error_name) {
|
|
||||||
wlr_log(WLR_DEBUG, "X11 error happened, but could not get error name");
|
|
||||||
goto log_raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_log(WLR_ERROR, "X11 error: op %s (%s), code %s (%s), "
|
|
||||||
"sequence %"PRIu16", value %"PRIu32,
|
|
||||||
major_name, minor_name ? minor_name : "no minor",
|
|
||||||
error_name, extension ? extension : "no extension",
|
|
||||||
ev->sequence, ev->bad_value);
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
log_raw:
|
|
||||||
#endif
|
|
||||||
|
|
||||||
wlr_log(WLR_ERROR, "X11 error: op %"PRIu8":%"PRIu16", code %"PRIu8", "
|
|
||||||
"sequence %"PRIu16", value %"PRIu32,
|
|
||||||
ev->major_opcode, ev->minor_opcode, ev->error_code,
|
|
||||||
ev->sequence, ev->bad_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_x11_unknown_event(struct wlr_x11_backend *x11,
|
|
||||||
xcb_generic_event_t *ev) {
|
|
||||||
#if HAS_XCB_ERRORS
|
|
||||||
const char *extension;
|
|
||||||
const char *event_name = xcb_errors_get_name_for_xcb_event(
|
|
||||||
x11->errors_context, ev, &extension);
|
|
||||||
if (!event_name) {
|
|
||||||
wlr_log(WLR_DEBUG, "No name for unhandled event: %u",
|
|
||||||
ev->response_type);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_log(WLR_DEBUG, "Unhandled X11 event: %s (%u)", event_name, ev->response_type);
|
|
||||||
#else
|
|
||||||
wlr_log(WLR_DEBUG, "Unhandled X11 event: %u", ev->response_type);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
|
|
||||||
#include <linux/input-event-codes.h>
|
#include <linux/input-event-codes.h>
|
||||||
|
|
||||||
#include <wayland-server-protocol.h>
|
|
||||||
|
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
#include <xcb/xfixes.h>
|
#include <xcb/xfixes.h>
|
||||||
#include <xcb/xinput.h>
|
#include <xcb/xinput.h>
|
||||||
|
@ -13,14 +11,13 @@
|
||||||
#include <wlr/interfaces/wlr_input_device.h>
|
#include <wlr/interfaces/wlr_input_device.h>
|
||||||
#include <wlr/interfaces/wlr_keyboard.h>
|
#include <wlr/interfaces/wlr_keyboard.h>
|
||||||
#include <wlr/interfaces/wlr_pointer.h>
|
#include <wlr/interfaces/wlr_pointer.h>
|
||||||
#include <wlr/interfaces/wlr_touch.h>
|
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
|
||||||
#include "backend/x11.h"
|
#include "backend/x11.h"
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
|
|
||||||
static void send_key_event(struct wlr_x11_backend *x11, uint32_t key,
|
static void send_key_event(struct wlr_x11_backend *x11, uint32_t key,
|
||||||
enum wl_keyboard_key_state st, xcb_timestamp_t time) {
|
enum wlr_key_state st, xcb_timestamp_t time) {
|
||||||
struct wlr_event_keyboard_key ev = {
|
struct wlr_event_keyboard_key ev = {
|
||||||
.time_msec = time,
|
.time_msec = time,
|
||||||
.keycode = key,
|
.keycode = key,
|
||||||
|
@ -39,7 +36,6 @@ static void send_button_event(struct wlr_x11_output *output, uint32_t key,
|
||||||
.state = st,
|
.state = st,
|
||||||
};
|
};
|
||||||
wlr_signal_emit_safe(&output->pointer.events.button, &ev);
|
wlr_signal_emit_safe(&output->pointer.events.button, &ev);
|
||||||
wlr_signal_emit_safe(&output->pointer.events.frame, &output->pointer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void send_axis_event(struct wlr_x11_output *output, int32_t delta,
|
static void send_axis_event(struct wlr_x11_output *output, int32_t delta,
|
||||||
|
@ -69,54 +65,6 @@ static void send_pointer_position_event(struct wlr_x11_output *output,
|
||||||
wlr_signal_emit_safe(&output->pointer.events.frame, &output->pointer);
|
wlr_signal_emit_safe(&output->pointer.events.frame, &output->pointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void send_touch_down_event(struct wlr_x11_output *output,
|
|
||||||
int16_t x, int16_t y, int32_t touch_id, xcb_timestamp_t time) {
|
|
||||||
struct wlr_event_touch_down ev = {
|
|
||||||
.device = &output->touch_dev,
|
|
||||||
.time_msec = time,
|
|
||||||
.x = (double)x / output->wlr_output.width,
|
|
||||||
.y = (double)y / output->wlr_output.height,
|
|
||||||
.touch_id = touch_id,
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&output->touch.events.down, &ev);
|
|
||||||
wlr_signal_emit_safe(&output->touch.events.frame, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void send_touch_motion_event(struct wlr_x11_output *output,
|
|
||||||
int16_t x, int16_t y, int32_t touch_id, xcb_timestamp_t time) {
|
|
||||||
struct wlr_event_touch_motion ev = {
|
|
||||||
.device = &output->touch_dev,
|
|
||||||
.time_msec = time,
|
|
||||||
.x = (double)x / output->wlr_output.width,
|
|
||||||
.y = (double)y / output->wlr_output.height,
|
|
||||||
.touch_id = touch_id,
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&output->touch.events.motion, &ev);
|
|
||||||
wlr_signal_emit_safe(&output->touch.events.frame, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void send_touch_up_event(struct wlr_x11_output *output,
|
|
||||||
int32_t touch_id, xcb_timestamp_t time) {
|
|
||||||
struct wlr_event_touch_up ev = {
|
|
||||||
.device = &output->touch_dev,
|
|
||||||
.time_msec = time,
|
|
||||||
.touch_id = touch_id,
|
|
||||||
};
|
|
||||||
wlr_signal_emit_safe(&output->touch.events.up, &ev);
|
|
||||||
wlr_signal_emit_safe(&output->touch.events.frame, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wlr_x11_touchpoint *get_touchpoint_from_x11_touch_id(
|
|
||||||
struct wlr_x11_output *output, uint32_t id) {
|
|
||||||
struct wlr_x11_touchpoint *touchpoint;
|
|
||||||
wl_list_for_each(touchpoint, &output->touchpoints, link) {
|
|
||||||
if (touchpoint->x11_id == id) {
|
|
||||||
return touchpoint;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_x11_xinput_event(struct wlr_x11_backend *x11,
|
void handle_x11_xinput_event(struct wlr_x11_backend *x11,
|
||||||
xcb_ge_generic_event_t *event) {
|
xcb_ge_generic_event_t *event) {
|
||||||
struct wlr_x11_output *output;
|
struct wlr_x11_output *output;
|
||||||
|
@ -126,13 +74,9 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11,
|
||||||
xcb_input_key_press_event_t *ev =
|
xcb_input_key_press_event_t *ev =
|
||||||
(xcb_input_key_press_event_t *)event;
|
(xcb_input_key_press_event_t *)event;
|
||||||
|
|
||||||
if (ev->flags & XCB_INPUT_KEY_EVENT_FLAGS_KEY_REPEAT) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base,
|
wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base,
|
||||||
ev->mods.latched, ev->mods.locked, ev->mods.effective);
|
ev->mods.latched, ev->mods.locked, ev->mods.effective);
|
||||||
send_key_event(x11, ev->detail - 8, WL_KEYBOARD_KEY_STATE_PRESSED, ev->time);
|
send_key_event(x11, ev->detail - 8, WLR_KEY_PRESSED, ev->time);
|
||||||
x11->time = ev->time;
|
x11->time = ev->time;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -142,7 +86,7 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11,
|
||||||
|
|
||||||
wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base,
|
wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base,
|
||||||
ev->mods.latched, ev->mods.locked, ev->mods.effective);
|
ev->mods.latched, ev->mods.locked, ev->mods.effective);
|
||||||
send_key_event(x11, ev->detail - 8, WL_KEYBOARD_KEY_STATE_RELEASED, ev->time);
|
send_key_event(x11, ev->detail - 8, WLR_KEY_RELEASED, ev->time);
|
||||||
x11->time = ev->time;
|
x11->time = ev->time;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -219,68 +163,34 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11,
|
||||||
x11->time = ev->time;
|
x11->time = ev->time;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case XCB_INPUT_TOUCH_BEGIN: {
|
case XCB_INPUT_ENTER: {
|
||||||
xcb_input_touch_begin_event_t *ev = (xcb_input_touch_begin_event_t *)event;
|
xcb_input_enter_event_t *ev = (xcb_input_enter_event_t *)event;
|
||||||
|
|
||||||
output = get_x11_output_from_window_id(x11, ev->event);
|
output = get_x11_output_from_window_id(x11, ev->event);
|
||||||
if (!output) {
|
if (!output) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t id = 0;
|
if (!output->cursor_hidden) {
|
||||||
if (!wl_list_empty(&output->touchpoints)) {
|
xcb_xfixes_hide_cursor(x11->xcb, output->win);
|
||||||
struct wlr_x11_touchpoint *last_touchpoint = wl_container_of(
|
xcb_flush(x11->xcb);
|
||||||
output->touchpoints.next, last_touchpoint, link);
|
output->cursor_hidden = true;
|
||||||
id = last_touchpoint->wayland_id + 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_x11_touchpoint *touchpoint = calloc(1, sizeof(struct wlr_x11_touchpoint));
|
|
||||||
touchpoint->x11_id = ev->detail;
|
|
||||||
touchpoint->wayland_id = id;
|
|
||||||
wl_list_init(&touchpoint->link);
|
|
||||||
wl_list_insert(&output->touchpoints, &touchpoint->link);
|
|
||||||
|
|
||||||
send_touch_down_event(output, ev->event_x >> 16,
|
|
||||||
ev->event_y >> 16, touchpoint->wayland_id, ev->time);
|
|
||||||
x11->time = ev->time;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case XCB_INPUT_TOUCH_END: {
|
case XCB_INPUT_LEAVE: {
|
||||||
xcb_input_touch_end_event_t *ev = (xcb_input_touch_end_event_t *)event;
|
xcb_input_leave_event_t *ev = (xcb_input_leave_event_t *)event;
|
||||||
|
|
||||||
output = get_x11_output_from_window_id(x11, ev->event);
|
output = get_x11_output_from_window_id(x11, ev->event);
|
||||||
if (!output) {
|
if (!output) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wlr_x11_touchpoint *touchpoint = get_touchpoint_from_x11_touch_id(output, ev->detail);
|
if (output->cursor_hidden) {
|
||||||
if (!touchpoint) {
|
xcb_xfixes_show_cursor(x11->xcb, output->win);
|
||||||
return;
|
xcb_flush(x11->xcb);
|
||||||
|
output->cursor_hidden = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
send_touch_up_event(output, touchpoint->wayland_id, ev->time);
|
|
||||||
x11->time = ev->time;
|
|
||||||
|
|
||||||
wl_list_remove(&touchpoint->link);
|
|
||||||
free(touchpoint);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case XCB_INPUT_TOUCH_UPDATE: {
|
|
||||||
xcb_input_touch_update_event_t *ev = (xcb_input_touch_update_event_t *)event;
|
|
||||||
|
|
||||||
output = get_x11_output_from_window_id(x11, ev->event);
|
|
||||||
if (!output) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_x11_touchpoint *touchpoint = get_touchpoint_from_x11_touch_id(output, ev->detail);
|
|
||||||
if (!touchpoint) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
send_touch_motion_event(output, ev->event_x >> 16,
|
|
||||||
ev->event_y >> 16, touchpoint->wayland_id, ev->time);
|
|
||||||
x11->time = ev->time;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -310,14 +220,6 @@ const struct wlr_pointer_impl pointer_impl = {
|
||||||
.destroy = pointer_destroy,
|
.destroy = pointer_destroy,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void touch_destroy(struct wlr_touch *wlr_touch) {
|
|
||||||
// Don't free the touch, it's on the stack
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct wlr_touch_impl touch_impl = {
|
|
||||||
.destroy = touch_destroy,
|
|
||||||
};
|
|
||||||
|
|
||||||
void update_x11_pointer_position(struct wlr_x11_output *output,
|
void update_x11_pointer_position(struct wlr_x11_output *output,
|
||||||
xcb_timestamp_t time) {
|
xcb_timestamp_t time) {
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
|
|
@ -1,25 +1,13 @@
|
||||||
x11_libs = []
|
x11_libs = []
|
||||||
x11_required = [
|
x11_required = [
|
||||||
|
'x11-xcb',
|
||||||
'xcb',
|
'xcb',
|
||||||
'xcb-dri3',
|
|
||||||
'xcb-present',
|
|
||||||
'xcb-render',
|
|
||||||
'xcb-renderutil',
|
|
||||||
'xcb-shm',
|
|
||||||
'xcb-xfixes',
|
|
||||||
'xcb-xinput',
|
'xcb-xinput',
|
||||||
|
'xcb-xfixes',
|
||||||
]
|
]
|
||||||
|
|
||||||
msg = ['Required for X11 backend support.']
|
|
||||||
if 'x11' in backends
|
|
||||||
msg += 'Install "@0@" or disable the X11 backend.'
|
|
||||||
endif
|
|
||||||
|
|
||||||
foreach lib : x11_required
|
foreach lib : x11_required
|
||||||
dep = dependency(lib,
|
dep = dependency(lib, required: get_option('x11-backend'))
|
||||||
required: 'x11' in backends,
|
|
||||||
not_found_message: '\n'.join(msg).format(lib),
|
|
||||||
)
|
|
||||||
if not dep.found()
|
if not dep.found()
|
||||||
subdir_done()
|
subdir_done()
|
||||||
endif
|
endif
|
||||||
|
@ -27,10 +15,21 @@ foreach lib : x11_required
|
||||||
x11_libs += dep
|
x11_libs += dep
|
||||||
endforeach
|
endforeach
|
||||||
|
|
||||||
wlr_files += files(
|
lib_wlr_backend_x11 = static_library(
|
||||||
'backend.c',
|
'wlr_backend_x11',
|
||||||
'input_device.c',
|
files(
|
||||||
'output.c',
|
'backend.c',
|
||||||
|
'input_device.c',
|
||||||
|
'output.c',
|
||||||
|
),
|
||||||
|
include_directories: wlr_inc,
|
||||||
|
dependencies: [
|
||||||
|
wayland_server,
|
||||||
|
pixman,
|
||||||
|
xkbcommon,
|
||||||
|
x11_libs,
|
||||||
|
],
|
||||||
)
|
)
|
||||||
wlr_deps += x11_libs
|
|
||||||
features += { 'x11-backend': true }
|
backend_parts += lib_wlr_backend_x11
|
||||||
|
conf_data.set10('WLR_HAS_X11_BACKEND', true)
|
||||||
|
|
|
@ -3,31 +3,23 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <fcntl.h>
|
|
||||||
|
|
||||||
#include <drm_fourcc.h>
|
|
||||||
#include <xcb/dri3.h>
|
|
||||||
#include <xcb/present.h>
|
|
||||||
#include <xcb/render.h>
|
|
||||||
#include <xcb/shm.h>
|
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
#include <xcb/xinput.h>
|
#include <xcb/xinput.h>
|
||||||
|
|
||||||
#include <wlr/interfaces/wlr_output.h>
|
#include <wlr/interfaces/wlr_output.h>
|
||||||
#include <wlr/interfaces/wlr_pointer.h>
|
#include <wlr/interfaces/wlr_pointer.h>
|
||||||
#include <wlr/interfaces/wlr_touch.h>
|
|
||||||
#include <wlr/render/wlr_renderer.h>
|
|
||||||
#include <wlr/types/wlr_matrix.h>
|
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
|
||||||
#include "backend/x11.h"
|
#include "backend/x11.h"
|
||||||
#include "util/signal.h"
|
#include "util/signal.h"
|
||||||
#include "util/time.h"
|
|
||||||
|
|
||||||
static const uint32_t SUPPORTED_OUTPUT_STATE =
|
static int signal_frame(void *data) {
|
||||||
WLR_OUTPUT_STATE_BACKEND_OPTIONAL |
|
struct wlr_x11_output *output = data;
|
||||||
WLR_OUTPUT_STATE_BUFFER |
|
wlr_output_send_frame(&output->wlr_output);
|
||||||
WLR_OUTPUT_STATE_MODE;
|
wl_event_source_timer_update(output->frame_timer, output->frame_delay);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void parse_xcb_setup(struct wlr_output *output,
|
static void parse_xcb_setup(struct wlr_output *output,
|
||||||
xcb_connection_t *xcb) {
|
xcb_connection_t *xcb) {
|
||||||
|
@ -47,11 +39,26 @@ static struct wlr_x11_output *get_x11_output_from_output(
|
||||||
return (struct wlr_x11_output *)wlr_output;
|
return (struct wlr_x11_output *)wlr_output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void output_set_refresh(struct wlr_output *wlr_output, int32_t refresh) {
|
||||||
|
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
||||||
|
|
||||||
|
if (refresh <= 0) {
|
||||||
|
refresh = X11_DEFAULT_REFRESH;
|
||||||
|
}
|
||||||
|
|
||||||
|
wlr_output_update_custom_mode(&output->wlr_output, wlr_output->width,
|
||||||
|
wlr_output->height, refresh);
|
||||||
|
|
||||||
|
output->frame_delay = 1000000 / refresh;
|
||||||
|
}
|
||||||
|
|
||||||
static bool output_set_custom_mode(struct wlr_output *wlr_output,
|
static bool output_set_custom_mode(struct wlr_output *wlr_output,
|
||||||
int32_t width, int32_t height, int32_t refresh) {
|
int32_t width, int32_t height, int32_t refresh) {
|
||||||
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
|
||||||
|
output_set_refresh(&output->wlr_output, refresh);
|
||||||
|
|
||||||
const uint32_t values[] = { width, height };
|
const uint32_t values[] = { width, height };
|
||||||
xcb_void_cookie_t cookie = xcb_configure_window_checked(
|
xcb_void_cookie_t cookie = xcb_configure_window_checked(
|
||||||
x11->xcb, output->win,
|
x11->xcb, output->win,
|
||||||
|
@ -68,427 +75,53 @@ static bool output_set_custom_mode(struct wlr_output *wlr_output,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void destroy_x11_buffer(struct wlr_x11_buffer *buffer);
|
static void output_transform(struct wlr_output *wlr_output,
|
||||||
|
enum wl_output_transform transform) {
|
||||||
|
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
||||||
|
output->wlr_output.transform = transform;
|
||||||
|
}
|
||||||
|
|
||||||
static void output_destroy(struct wlr_output *wlr_output) {
|
static void output_destroy(struct wlr_output *wlr_output) {
|
||||||
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
|
||||||
pixman_region32_fini(&output->exposed);
|
|
||||||
|
|
||||||
wlr_input_device_destroy(&output->pointer_dev);
|
wlr_input_device_destroy(&output->pointer_dev);
|
||||||
wlr_input_device_destroy(&output->touch_dev);
|
|
||||||
|
|
||||||
struct wlr_x11_buffer *buffer, *buffer_tmp;
|
|
||||||
wl_list_for_each_safe(buffer, buffer_tmp, &output->buffers, link) {
|
|
||||||
destroy_x11_buffer(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_list_remove(&output->link);
|
wl_list_remove(&output->link);
|
||||||
|
wl_event_source_remove(output->frame_timer);
|
||||||
if (output->cursor.pic != XCB_NONE) {
|
wlr_egl_destroy_surface(&x11->egl, output->surf);
|
||||||
xcb_render_free_picture(x11->xcb, output->cursor.pic);
|
|
||||||
}
|
|
||||||
|
|
||||||
// A zero event mask deletes the event context
|
|
||||||
xcb_present_select_input(x11->xcb, output->present_event_id, output->win, 0);
|
|
||||||
xcb_destroy_window(x11->xcb, output->win);
|
xcb_destroy_window(x11->xcb, output->win);
|
||||||
xcb_flush(x11->xcb);
|
xcb_flush(x11->xcb);
|
||||||
free(output);
|
free(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool output_test(struct wlr_output *wlr_output) {
|
static bool output_make_current(struct wlr_output *wlr_output,
|
||||||
uint32_t unsupported =
|
int *buffer_age) {
|
||||||
wlr_output->pending.committed & ~SUPPORTED_OUTPUT_STATE;
|
|
||||||
if (unsupported != 0) {
|
|
||||||
wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32,
|
|
||||||
unsupported);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
|
|
||||||
assert(wlr_output->pending.mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void destroy_x11_buffer(struct wlr_x11_buffer *buffer) {
|
|
||||||
if (!buffer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wl_list_remove(&buffer->buffer_destroy.link);
|
|
||||||
wl_list_remove(&buffer->link);
|
|
||||||
xcb_free_pixmap(buffer->x11->xcb, buffer->pixmap);
|
|
||||||
free(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void buffer_handle_buffer_destroy(struct wl_listener *listener,
|
|
||||||
void *data) {
|
|
||||||
struct wlr_x11_buffer *buffer =
|
|
||||||
wl_container_of(listener, buffer, buffer_destroy);
|
|
||||||
destroy_x11_buffer(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static xcb_pixmap_t import_dmabuf(struct wlr_x11_output *output,
|
|
||||||
struct wlr_dmabuf_attributes *dmabuf) {
|
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
|
||||||
|
|
||||||
if (dmabuf->format != x11->x11_format->drm) {
|
|
||||||
// The pixmap's depth must match the window's depth, otherwise Present
|
|
||||||
// will throw a Match error
|
|
||||||
return XCB_PIXMAP_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// xcb closes the FDs after sending them, so we need to dup them here
|
|
||||||
struct wlr_dmabuf_attributes dup_attrs = {0};
|
|
||||||
if (!wlr_dmabuf_attributes_copy(&dup_attrs, dmabuf)) {
|
|
||||||
return XCB_PIXMAP_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct wlr_x11_format *x11_fmt = x11->x11_format;
|
|
||||||
xcb_pixmap_t pixmap = xcb_generate_id(x11->xcb);
|
|
||||||
|
|
||||||
if (x11->dri3_major_version > 1 || x11->dri3_minor_version >= 2) {
|
|
||||||
if (dmabuf->n_planes > 4) {
|
|
||||||
wlr_dmabuf_attributes_finish(&dup_attrs);
|
|
||||||
return XCB_PIXMAP_NONE;
|
|
||||||
}
|
|
||||||
xcb_dri3_pixmap_from_buffers(x11->xcb, pixmap, output->win,
|
|
||||||
dmabuf->n_planes, dmabuf->width, dmabuf->height, dmabuf->stride[0],
|
|
||||||
dmabuf->offset[0], dmabuf->stride[1], dmabuf->offset[1],
|
|
||||||
dmabuf->stride[2], dmabuf->offset[2], dmabuf->stride[3],
|
|
||||||
dmabuf->offset[3], x11_fmt->depth, x11_fmt->bpp, dmabuf->modifier,
|
|
||||||
dup_attrs.fd);
|
|
||||||
} else {
|
|
||||||
// PixmapFromBuffers requires DRI3 1.2
|
|
||||||
if (dmabuf->n_planes != 1
|
|
||||||
|| dmabuf->modifier != DRM_FORMAT_MOD_INVALID) {
|
|
||||||
wlr_dmabuf_attributes_finish(&dup_attrs);
|
|
||||||
return XCB_PIXMAP_NONE;
|
|
||||||
}
|
|
||||||
xcb_dri3_pixmap_from_buffer(x11->xcb, pixmap, output->win,
|
|
||||||
dmabuf->height * dmabuf->stride[0], dmabuf->width, dmabuf->height,
|
|
||||||
dmabuf->stride[0], x11_fmt->depth, x11_fmt->bpp, dup_attrs.fd[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return pixmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
static xcb_pixmap_t import_shm(struct wlr_x11_output *output,
|
|
||||||
struct wlr_shm_attributes *shm) {
|
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
|
||||||
|
|
||||||
if (shm->format != x11->x11_format->drm) {
|
|
||||||
// The pixmap's depth must match the window's depth, otherwise Present
|
|
||||||
// will throw a Match error
|
|
||||||
return XCB_PIXMAP_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// xcb closes the FD after sending it
|
|
||||||
int fd = fcntl(shm->fd, F_DUPFD_CLOEXEC, 0);
|
|
||||||
if (fd < 0) {
|
|
||||||
wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed");
|
|
||||||
return XCB_PIXMAP_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
xcb_shm_seg_t seg = xcb_generate_id(x11->xcb);
|
|
||||||
xcb_shm_attach_fd(x11->xcb, seg, fd, false);
|
|
||||||
|
|
||||||
xcb_pixmap_t pixmap = xcb_generate_id(x11->xcb);
|
|
||||||
xcb_shm_create_pixmap(x11->xcb, pixmap, output->win, shm->width,
|
|
||||||
shm->height, x11->x11_format->depth, seg, shm->offset);
|
|
||||||
|
|
||||||
xcb_shm_detach(x11->xcb, seg);
|
|
||||||
|
|
||||||
return pixmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wlr_x11_buffer *create_x11_buffer(struct wlr_x11_output *output,
|
|
||||||
struct wlr_buffer *wlr_buffer) {
|
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
|
||||||
xcb_pixmap_t pixmap = XCB_PIXMAP_NONE;
|
|
||||||
|
|
||||||
struct wlr_dmabuf_attributes dmabuf_attrs;
|
|
||||||
struct wlr_shm_attributes shm_attrs;
|
|
||||||
if (wlr_buffer_get_dmabuf(wlr_buffer, &dmabuf_attrs)) {
|
|
||||||
pixmap = import_dmabuf(output, &dmabuf_attrs);
|
|
||||||
} else if (wlr_buffer_get_shm(wlr_buffer, &shm_attrs)) {
|
|
||||||
pixmap = import_shm(output, &shm_attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pixmap == XCB_PIXMAP_NONE) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_x11_buffer *buffer = calloc(1, sizeof(struct wlr_x11_buffer));
|
|
||||||
if (!buffer) {
|
|
||||||
xcb_free_pixmap(x11->xcb, pixmap);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
buffer->buffer = wlr_buffer_lock(wlr_buffer);
|
|
||||||
buffer->pixmap = pixmap;
|
|
||||||
buffer->x11 = x11;
|
|
||||||
wl_list_insert(&output->buffers, &buffer->link);
|
|
||||||
|
|
||||||
buffer->buffer_destroy.notify = buffer_handle_buffer_destroy;
|
|
||||||
wl_signal_add(&wlr_buffer->events.destroy, &buffer->buffer_destroy);
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct wlr_x11_buffer *get_or_create_x11_buffer(
|
|
||||||
struct wlr_x11_output *output, struct wlr_buffer *wlr_buffer) {
|
|
||||||
struct wlr_x11_buffer *buffer;
|
|
||||||
wl_list_for_each(buffer, &output->buffers, link) {
|
|
||||||
if (buffer->buffer == wlr_buffer) {
|
|
||||||
wlr_buffer_lock(buffer->buffer);
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return create_x11_buffer(output, wlr_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool output_commit_buffer(struct wlr_x11_output *output) {
|
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
|
||||||
|
|
||||||
struct wlr_buffer *buffer = output->wlr_output.pending.buffer;
|
|
||||||
struct wlr_x11_buffer *x11_buffer =
|
|
||||||
get_or_create_x11_buffer(output, buffer);
|
|
||||||
if (!x11_buffer) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
xcb_xfixes_region_t region = XCB_NONE;
|
|
||||||
if (output->wlr_output.pending.committed & WLR_OUTPUT_STATE_DAMAGE) {
|
|
||||||
pixman_region32_union(&output->exposed, &output->exposed, &output->wlr_output.pending.damage);
|
|
||||||
|
|
||||||
int rects_len = 0;
|
|
||||||
pixman_box32_t *rects = pixman_region32_rectangles(&output->exposed, &rects_len);
|
|
||||||
|
|
||||||
xcb_rectangle_t *xcb_rects = calloc(rects_len, sizeof(xcb_rectangle_t));
|
|
||||||
if (!xcb_rects) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < rects_len; i++) {
|
|
||||||
pixman_box32_t *box = &rects[i];
|
|
||||||
xcb_rects[i] = (struct xcb_rectangle_t){
|
|
||||||
.x = box->x1,
|
|
||||||
.y = box->y1,
|
|
||||||
.width = box->x2 - box->x1,
|
|
||||||
.height = box->y2 - box->y1,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
region = xcb_generate_id(x11->xcb);
|
|
||||||
xcb_xfixes_create_region(x11->xcb, region, rects_len, xcb_rects);
|
|
||||||
|
|
||||||
free(xcb_rects);
|
|
||||||
}
|
|
||||||
|
|
||||||
pixman_region32_clear(&output->exposed);
|
|
||||||
|
|
||||||
uint32_t serial = output->wlr_output.commit_seq;
|
|
||||||
uint32_t options = 0;
|
|
||||||
uint64_t target_msc = output->last_msc ? output->last_msc + 1 : 0;
|
|
||||||
xcb_present_pixmap(x11->xcb, output->win, x11_buffer->pixmap, serial,
|
|
||||||
0, region, 0, 0, XCB_NONE, XCB_NONE, XCB_NONE, options, target_msc,
|
|
||||||
0, 0, 0, NULL);
|
|
||||||
|
|
||||||
if (region != XCB_NONE) {
|
|
||||||
xcb_xfixes_destroy_region(x11->xcb, region);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
error:
|
|
||||||
destroy_x11_buffer(x11_buffer);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool output_commit(struct wlr_output *wlr_output) {
|
|
||||||
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
|
||||||
if (!output_test(wlr_output)) {
|
return wlr_egl_make_current(&x11->egl, output->surf, buffer_age);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool output_swap_buffers(struct wlr_output *wlr_output,
|
||||||
|
pixman_region32_t *damage) {
|
||||||
|
struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output;
|
||||||
|
struct wlr_x11_backend *x11 = output->x11;
|
||||||
|
|
||||||
|
if (!wlr_egl_swap_buffers(&x11->egl, output->surf, damage)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
|
wlr_output_send_present(wlr_output, NULL);
|
||||||
if (!output_set_custom_mode(wlr_output,
|
|
||||||
wlr_output->pending.custom_mode.width,
|
|
||||||
wlr_output->pending.custom_mode.height,
|
|
||||||
wlr_output->pending.custom_mode.refresh)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED &&
|
|
||||||
x11->atoms.variable_refresh != XCB_ATOM_NONE) {
|
|
||||||
if (wlr_output->pending.adaptive_sync_enabled) {
|
|
||||||
uint32_t enabled = 1;
|
|
||||||
xcb_change_property(x11->xcb, XCB_PROP_MODE_REPLACE, output->win,
|
|
||||||
x11->atoms.variable_refresh, XCB_ATOM_CARDINAL, 32, 1,
|
|
||||||
&enabled);
|
|
||||||
wlr_output->adaptive_sync_status = WLR_OUTPUT_ADAPTIVE_SYNC_UNKNOWN;
|
|
||||||
} else {
|
|
||||||
xcb_delete_property(x11->xcb, output->win,
|
|
||||||
x11->atoms.variable_refresh);
|
|
||||||
wlr_output->adaptive_sync_status = WLR_OUTPUT_ADAPTIVE_SYNC_DISABLED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) {
|
|
||||||
if (!output_commit_buffer(output)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
xcb_flush(x11->xcb);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_x11_output_cursor(struct wlr_x11_output *output,
|
|
||||||
int32_t hotspot_x, int32_t hotspot_y) {
|
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
|
||||||
|
|
||||||
xcb_cursor_t cursor = x11->transparent_cursor;
|
|
||||||
|
|
||||||
if (output->cursor.pic != XCB_NONE) {
|
|
||||||
cursor = xcb_generate_id(x11->xcb);
|
|
||||||
xcb_render_create_cursor(x11->xcb, cursor, output->cursor.pic,
|
|
||||||
hotspot_x, hotspot_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t values[] = {cursor};
|
|
||||||
xcb_change_window_attributes(x11->xcb, output->win,
|
|
||||||
XCB_CW_CURSOR, values);
|
|
||||||
xcb_flush(x11->xcb);
|
|
||||||
|
|
||||||
if (cursor != x11->transparent_cursor) {
|
|
||||||
xcb_free_cursor(x11->xcb, cursor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool output_cursor_to_picture(struct wlr_x11_output *output,
|
|
||||||
struct wlr_buffer *buffer) {
|
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
|
||||||
struct wlr_renderer *renderer = output->wlr_output.renderer;
|
|
||||||
|
|
||||||
if (output->cursor.pic != XCB_NONE) {
|
|
||||||
xcb_render_free_picture(x11->xcb, output->cursor.pic);
|
|
||||||
}
|
|
||||||
output->cursor.pic = XCB_NONE;
|
|
||||||
|
|
||||||
if (buffer == NULL) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int depth = 32;
|
|
||||||
int stride = buffer->width * 4;
|
|
||||||
|
|
||||||
uint8_t *data = malloc(buffer->height * stride);
|
|
||||||
if (data == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wlr_renderer_begin_with_buffer(renderer, buffer)) {
|
|
||||||
free(data);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool result = wlr_renderer_read_pixels(
|
|
||||||
renderer, DRM_FORMAT_ARGB8888, NULL,
|
|
||||||
stride, buffer->width, buffer->height, 0, 0, 0, 0,
|
|
||||||
data);
|
|
||||||
|
|
||||||
wlr_renderer_end(renderer);
|
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
free(data);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
xcb_pixmap_t pix = xcb_generate_id(x11->xcb);
|
|
||||||
xcb_create_pixmap(x11->xcb, depth, pix, output->win,
|
|
||||||
buffer->width, buffer->height);
|
|
||||||
|
|
||||||
output->cursor.pic = xcb_generate_id(x11->xcb);
|
|
||||||
xcb_render_create_picture(x11->xcb, output->cursor.pic,
|
|
||||||
pix, x11->argb32, 0, 0);
|
|
||||||
|
|
||||||
xcb_gcontext_t gc = xcb_generate_id(x11->xcb);
|
|
||||||
xcb_create_gc(x11->xcb, gc, pix, 0, NULL);
|
|
||||||
|
|
||||||
xcb_put_image(x11->xcb, XCB_IMAGE_FORMAT_Z_PIXMAP,
|
|
||||||
pix, gc, buffer->width, buffer->height, 0, 0, 0, depth,
|
|
||||||
stride * buffer->height * sizeof(uint8_t), data);
|
|
||||||
free(data);
|
|
||||||
xcb_free_gc(x11->xcb, gc);
|
|
||||||
xcb_free_pixmap(x11->xcb, pix);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool output_set_cursor(struct wlr_output *wlr_output,
|
|
||||||
struct wlr_buffer *buffer, int32_t hotspot_x, int32_t hotspot_y) {
|
|
||||||
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
|
||||||
|
|
||||||
if (x11->argb32 == XCB_NONE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer != NULL) {
|
|
||||||
if (hotspot_x < 0) {
|
|
||||||
hotspot_x = 0;
|
|
||||||
}
|
|
||||||
if (hotspot_x > buffer->width) {
|
|
||||||
hotspot_x = buffer->width;
|
|
||||||
}
|
|
||||||
if (hotspot_y < 0) {
|
|
||||||
hotspot_y = 0;
|
|
||||||
}
|
|
||||||
if (hotspot_y > buffer->height) {
|
|
||||||
hotspot_y = buffer->height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool success = output_cursor_to_picture(output, buffer);
|
|
||||||
|
|
||||||
update_x11_output_cursor(output, hotspot_x, hotspot_y);
|
|
||||||
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool output_move_cursor(struct wlr_output *_output, int x, int y) {
|
|
||||||
// TODO: only return true if x == current x and y == current y
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wlr_drm_format_set *output_get_primary_formats(
|
|
||||||
struct wlr_output *wlr_output, uint32_t buffer_caps) {
|
|
||||||
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
|
|
||||||
struct wlr_x11_backend *x11 = output->x11;
|
|
||||||
|
|
||||||
if (x11->have_dri3 && (buffer_caps & WLR_BUFFER_CAP_DMABUF)) {
|
|
||||||
return &output->x11->primary_dri3_formats;
|
|
||||||
} else if (x11->have_shm && (buffer_caps & WLR_BUFFER_CAP_SHM)) {
|
|
||||||
return &output->x11->primary_shm_formats;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wlr_output_impl output_impl = {
|
static const struct wlr_output_impl output_impl = {
|
||||||
|
.set_custom_mode = output_set_custom_mode,
|
||||||
|
.transform = output_transform,
|
||||||
.destroy = output_destroy,
|
.destroy = output_destroy,
|
||||||
.test = output_test,
|
.make_current = output_make_current,
|
||||||
.commit = output_commit,
|
.swap_buffers = output_swap_buffers,
|
||||||
.set_cursor = output_set_cursor,
|
|
||||||
.move_cursor = output_move_cursor,
|
|
||||||
.get_primary_formats = output_get_primary_formats,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
|
struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
|
||||||
|
@ -504,39 +137,27 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
output->x11 = x11;
|
output->x11 = x11;
|
||||||
wl_list_init(&output->buffers);
|
|
||||||
pixman_region32_init(&output->exposed);
|
|
||||||
|
|
||||||
struct wlr_output *wlr_output = &output->wlr_output;
|
struct wlr_output *wlr_output = &output->wlr_output;
|
||||||
wlr_output_init(wlr_output, &x11->backend, &output_impl, x11->wl_display);
|
wlr_output_init(wlr_output, &x11->backend, &output_impl, x11->wl_display);
|
||||||
|
|
||||||
wlr_output_update_custom_mode(wlr_output, 1024, 768, 0);
|
wlr_output->width = 1024;
|
||||||
|
wlr_output->height = 768;
|
||||||
|
|
||||||
char name[64];
|
output_set_refresh(&output->wlr_output, 0);
|
||||||
snprintf(name, sizeof(name), "X11-%zu", ++x11->last_output_num);
|
|
||||||
wlr_output_set_name(wlr_output, name);
|
|
||||||
|
|
||||||
|
snprintf(wlr_output->name, sizeof(wlr_output->name), "X11-%d",
|
||||||
|
wl_list_length(&x11->outputs) + 1);
|
||||||
parse_xcb_setup(wlr_output, x11->xcb);
|
parse_xcb_setup(wlr_output, x11->xcb);
|
||||||
|
|
||||||
char description[128];
|
uint32_t mask = XCB_CW_EVENT_MASK;
|
||||||
snprintf(description, sizeof(description),
|
|
||||||
"X11 output %zu", x11->last_output_num);
|
|
||||||
wlr_output_set_description(wlr_output, description);
|
|
||||||
|
|
||||||
// The X11 protocol requires us to set a colormap and border pixel if the
|
|
||||||
// depth doesn't match the root window's
|
|
||||||
uint32_t mask = XCB_CW_BORDER_PIXEL | XCB_CW_EVENT_MASK |
|
|
||||||
XCB_CW_COLORMAP | XCB_CW_CURSOR;
|
|
||||||
uint32_t values[] = {
|
uint32_t values[] = {
|
||||||
0,
|
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY
|
||||||
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY,
|
|
||||||
x11->colormap,
|
|
||||||
x11->transparent_cursor,
|
|
||||||
};
|
};
|
||||||
output->win = xcb_generate_id(x11->xcb);
|
output->win = xcb_generate_id(x11->xcb);
|
||||||
xcb_create_window(x11->xcb, x11->depth->depth, output->win,
|
xcb_create_window(x11->xcb, XCB_COPY_FROM_PARENT, output->win,
|
||||||
x11->screen->root, 0, 0, wlr_output->width, wlr_output->height, 0,
|
x11->screen->root, 0, 0, wlr_output->width, wlr_output->height, 1,
|
||||||
XCB_WINDOW_CLASS_INPUT_OUTPUT, x11->visualid, mask, values);
|
XCB_WINDOW_CLASS_INPUT_OUTPUT, x11->screen->root_visual, mask, values);
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
xcb_input_event_mask_t head;
|
xcb_input_event_mask_t head;
|
||||||
|
@ -548,17 +169,17 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
|
||||||
XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS |
|
XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS |
|
||||||
XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE |
|
XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE |
|
||||||
XCB_INPUT_XI_EVENT_MASK_MOTION |
|
XCB_INPUT_XI_EVENT_MASK_MOTION |
|
||||||
XCB_INPUT_XI_EVENT_MASK_TOUCH_BEGIN |
|
XCB_INPUT_XI_EVENT_MASK_ENTER |
|
||||||
XCB_INPUT_XI_EVENT_MASK_TOUCH_END |
|
XCB_INPUT_XI_EVENT_MASK_LEAVE,
|
||||||
XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE,
|
|
||||||
};
|
};
|
||||||
xcb_input_xi_select_events(x11->xcb, output->win, 1, &xinput_mask.head);
|
xcb_input_xi_select_events(x11->xcb, output->win, 1, &xinput_mask.head);
|
||||||
|
|
||||||
uint32_t present_mask = XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY |
|
output->surf = wlr_egl_create_surface(&x11->egl, &output->win);
|
||||||
XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY;
|
if (!output->surf) {
|
||||||
output->present_event_id = xcb_generate_id(x11->xcb);
|
wlr_log(WLR_ERROR, "Failed to create EGL surface");
|
||||||
xcb_present_select_input(x11->xcb, output->present_event_id, output->win,
|
free(output);
|
||||||
present_mask);
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
xcb_change_property(x11->xcb, XCB_PROP_MODE_REPLACE, output->win,
|
xcb_change_property(x11->xcb, XCB_PROP_MODE_REPLACE, output->win,
|
||||||
x11->atoms.wm_protocols, XCB_ATOM_ATOM, 32, 1,
|
x11->atoms.wm_protocols, XCB_ATOM_ATOM, 32, 1,
|
||||||
|
@ -569,8 +190,12 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
|
||||||
xcb_map_window(x11->xcb, output->win);
|
xcb_map_window(x11->xcb, output->win);
|
||||||
xcb_flush(x11->xcb);
|
xcb_flush(x11->xcb);
|
||||||
|
|
||||||
|
struct wl_event_loop *ev = wl_display_get_event_loop(x11->wl_display);
|
||||||
|
output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output);
|
||||||
|
|
||||||
wl_list_insert(&x11->outputs, &output->link);
|
wl_list_insert(&x11->outputs, &output->link);
|
||||||
|
|
||||||
|
wl_event_source_timer_update(output->frame_timer, output->frame_delay);
|
||||||
wlr_output_update_enabled(wlr_output, true);
|
wlr_output_update_enabled(wlr_output, true);
|
||||||
|
|
||||||
wlr_input_device_init(&output->pointer_dev, WLR_INPUT_DEVICE_POINTER,
|
wlr_input_device_init(&output->pointer_dev, WLR_INPUT_DEVICE_POINTER,
|
||||||
|
@ -579,19 +204,8 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
|
||||||
output->pointer_dev.pointer = &output->pointer;
|
output->pointer_dev.pointer = &output->pointer;
|
||||||
output->pointer_dev.output_name = strdup(wlr_output->name);
|
output->pointer_dev.output_name = strdup(wlr_output->name);
|
||||||
|
|
||||||
wlr_input_device_init(&output->touch_dev, WLR_INPUT_DEVICE_TOUCH,
|
|
||||||
&input_device_impl, "X11 touch", 0, 0);
|
|
||||||
wlr_touch_init(&output->touch, &touch_impl);
|
|
||||||
output->touch_dev.touch = &output->touch;
|
|
||||||
output->touch_dev.output_name = strdup(wlr_output->name);
|
|
||||||
wl_list_init(&output->touchpoints);
|
|
||||||
|
|
||||||
wlr_signal_emit_safe(&x11->backend.events.new_output, wlr_output);
|
wlr_signal_emit_safe(&x11->backend.events.new_output, wlr_output);
|
||||||
wlr_signal_emit_safe(&x11->backend.events.new_input, &output->pointer_dev);
|
wlr_signal_emit_safe(&x11->backend.events.new_input, &output->pointer_dev);
|
||||||
wlr_signal_emit_safe(&x11->backend.events.new_input, &output->touch_dev);
|
|
||||||
|
|
||||||
// Start the rendering loop by requesting the compositor to render a frame
|
|
||||||
wlr_output_schedule_frame(wlr_output);
|
|
||||||
|
|
||||||
return wlr_output;
|
return wlr_output;
|
||||||
}
|
}
|
||||||
|
@ -599,18 +213,17 @@ struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) {
|
||||||
void handle_x11_configure_notify(struct wlr_x11_output *output,
|
void handle_x11_configure_notify(struct wlr_x11_output *output,
|
||||||
xcb_configure_notify_event_t *ev) {
|
xcb_configure_notify_event_t *ev) {
|
||||||
// ignore events that set an invalid size:
|
// ignore events that set an invalid size:
|
||||||
if (ev->width == 0 || ev->height == 0) {
|
if (ev->width > 0 && ev->height > 0) {
|
||||||
|
wlr_output_update_custom_mode(&output->wlr_output, ev->width,
|
||||||
|
ev->height, output->wlr_output.refresh);
|
||||||
|
|
||||||
|
// Move the pointer to its new location
|
||||||
|
update_x11_pointer_position(output, output->x11->time);
|
||||||
|
} else {
|
||||||
wlr_log(WLR_DEBUG,
|
wlr_log(WLR_DEBUG,
|
||||||
"Ignoring X11 configure event for height=%d, width=%d",
|
"Ignoring X11 configure event for height=%d, width=%d",
|
||||||
ev->width, ev->height);
|
ev->width, ev->height);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_output_update_custom_mode(&output->wlr_output, ev->width,
|
|
||||||
ev->height, 0);
|
|
||||||
|
|
||||||
// Move the pointer to its new location
|
|
||||||
update_x11_pointer_position(output, output->x11->time);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool wlr_output_is_x11(struct wlr_output *wlr_output) {
|
bool wlr_output_is_x11(struct wlr_output *wlr_output) {
|
||||||
|
@ -632,76 +245,3 @@ void wlr_x11_output_set_title(struct wlr_output *output, const char *title) {
|
||||||
x11_output->x11->atoms.net_wm_name, x11_output->x11->atoms.utf8_string, 8,
|
x11_output->x11->atoms.net_wm_name, x11_output->x11->atoms.utf8_string, 8,
|
||||||
strlen(title), title);
|
strlen(title), title);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct wlr_x11_buffer *get_x11_buffer(struct wlr_x11_output *output,
|
|
||||||
xcb_pixmap_t pixmap) {
|
|
||||||
struct wlr_x11_buffer *buffer;
|
|
||||||
wl_list_for_each(buffer, &output->buffers, link) {
|
|
||||||
if (buffer->pixmap == pixmap) {
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_x11_present_event(struct wlr_x11_backend *x11,
|
|
||||||
xcb_ge_generic_event_t *event) {
|
|
||||||
struct wlr_x11_output *output;
|
|
||||||
|
|
||||||
switch (event->event_type) {
|
|
||||||
case XCB_PRESENT_EVENT_IDLE_NOTIFY:;
|
|
||||||
xcb_present_idle_notify_event_t *idle_notify =
|
|
||||||
(xcb_present_idle_notify_event_t *)event;
|
|
||||||
|
|
||||||
output = get_x11_output_from_window_id(x11, idle_notify->window);
|
|
||||||
if (!output) {
|
|
||||||
wlr_log(WLR_DEBUG, "Got PresentIdleNotify event for unknown window");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_x11_buffer *buffer =
|
|
||||||
get_x11_buffer(output, idle_notify->pixmap);
|
|
||||||
if (!buffer) {
|
|
||||||
wlr_log(WLR_DEBUG, "Got PresentIdleNotify event for unknown buffer");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_buffer_unlock(buffer->buffer); // may destroy buffer
|
|
||||||
break;
|
|
||||||
case XCB_PRESENT_COMPLETE_NOTIFY:;
|
|
||||||
xcb_present_complete_notify_event_t *complete_notify =
|
|
||||||
(xcb_present_complete_notify_event_t *)event;
|
|
||||||
|
|
||||||
output = get_x11_output_from_window_id(x11, complete_notify->window);
|
|
||||||
if (!output) {
|
|
||||||
wlr_log(WLR_DEBUG, "Got PresentCompleteNotify event for unknown window");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
output->last_msc = complete_notify->msc;
|
|
||||||
|
|
||||||
struct timespec t;
|
|
||||||
timespec_from_nsec(&t, complete_notify->ust * 1000);
|
|
||||||
|
|
||||||
uint32_t flags = 0;
|
|
||||||
if (complete_notify->mode == XCB_PRESENT_COMPLETE_MODE_FLIP) {
|
|
||||||
flags |= WLR_OUTPUT_PRESENT_ZERO_COPY;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool presented = complete_notify->mode != XCB_PRESENT_COMPLETE_MODE_SKIP;
|
|
||||||
struct wlr_output_event_present present_event = {
|
|
||||||
.output = &output->wlr_output,
|
|
||||||
.commit_seq = complete_notify->serial,
|
|
||||||
.presented = presented,
|
|
||||||
.when = &t,
|
|
||||||
.seq = complete_notify->msc,
|
|
||||||
.flags = flags,
|
|
||||||
};
|
|
||||||
wlr_output_send_present(&output->wlr_output, &present_event);
|
|
||||||
|
|
||||||
wlr_output_send_frame(&output->wlr_output);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
wlr_log(WLR_DEBUG, "Unhandled Present event %"PRIu16, event->event_type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,54 +1,34 @@
|
||||||
wlroots reads these environment variables
|
wlroots reads these environment variables
|
||||||
|
|
||||||
# wlroots specific
|
wlroots specific
|
||||||
|
----------------
|
||||||
* *WLR_BACKENDS*: comma-separated list of backends to use (available backends:
|
|
||||||
libinput, drm, wayland, x11, headless)
|
|
||||||
* *WLR_NO_HARDWARE_CURSORS*: set to 1 to use software cursors instead of
|
|
||||||
hardware cursors
|
|
||||||
* *WLR_XWAYLAND*: specifies the path to an Xwayland binary to be used (instead
|
|
||||||
of following shell search semantics for "Xwayland")
|
|
||||||
* *WLR_RENDERER*: forces the creation of a specified renderer (available
|
|
||||||
renderers: gles2, pixman, vulkan)
|
|
||||||
* *WLR_RENDER_DRM_DEVICE*: specifies the DRM node to use for
|
|
||||||
hardware-accelerated renderers.
|
|
||||||
|
|
||||||
## DRM backend
|
|
||||||
|
|
||||||
* *WLR_DRM_DEVICES*: specifies the DRM devices (as a colon separated list)
|
* *WLR_DRM_DEVICES*: specifies the DRM devices (as a colon separated list)
|
||||||
instead of auto probing them. The first existing device in this list is
|
instead of auto probing them. The first existing device in this list is
|
||||||
considered the primary DRM device.
|
considered the primary DRM device.
|
||||||
* *WLR_DRM_NO_ATOMIC*: set to 1 to use legacy DRM interface instead of atomic
|
* *WLR_DRM_NO_ATOMIC*: set to 1 to use legacy DRM interface instead of atomic
|
||||||
mode setting
|
mode setting
|
||||||
* *WLR_DRM_NO_MODIFIERS*: set to 1 to always allocate planes without modifiers,
|
* *WLR_DRM_NO_ATOMIC_GAMMA*: set to 1 to use legacy DRM interface for gamma
|
||||||
this can fix certain modeset failures because of bandwidth restrictions.
|
control instead of the atomic interface
|
||||||
|
* *WLR_LIBINPUT_NO_DEVICES*: set to 1 to not fail without any input devices
|
||||||
## Headless backend
|
* *WLR_BACKENDS*: comma-separated list of backends to use (available backends:
|
||||||
|
wayland, x11, headless, noop)
|
||||||
|
* *WLR_WL_OUTPUTS*: when using the wayland backend specifies the number of outputs
|
||||||
|
* *WLR_X11_OUTPUTS*: when using the X11 backend specifies the number of outputs
|
||||||
* *WLR_HEADLESS_OUTPUTS*: when using the headless backend specifies the number
|
* *WLR_HEADLESS_OUTPUTS*: when using the headless backend specifies the number
|
||||||
of outputs
|
of outputs
|
||||||
|
* *WLR_NO_HARDWARE_CURSORS*: set to 1 to use software cursors instead of
|
||||||
|
hardware cursors
|
||||||
|
* *WLR_SESSION*: specifies the wlr\_session to be used (available sessions:
|
||||||
|
logind/systemd, direct)
|
||||||
|
|
||||||
## libinput backend
|
rootston specific
|
||||||
|
------------------
|
||||||
|
* *XKB_DEFAULT_RULES*, *XKB_DEFAULT_MODEL*, *XKB_DEFAULT_LAYOUT*,
|
||||||
|
*XKB_DEFAULT_VARIANT*, *XKB_DEFAULT_OPTIONS*: xkb setup
|
||||||
|
|
||||||
* *WLR_LIBINPUT_NO_DEVICES*: set to 1 to not fail without any input devices
|
generic
|
||||||
|
-------
|
||||||
## Wayland backend
|
* *DISPLAY*: if set probe X11 backend in *wlr_backend_autocreate*
|
||||||
|
* *WAYLAND_DISPLAY*, *_WAYLAND_DISPLAY*, *WAYLAND_SOCKET*: if set probe Wayland
|
||||||
* *WLR_WL_OUTPUTS*: when using the wayland backend specifies the number of outputs
|
backend in *wlr_backend_autocreate*
|
||||||
|
|
||||||
## X11 backend
|
|
||||||
|
|
||||||
* *WLR_X11_OUTPUTS*: when using the X11 backend specifies the number of outputs
|
|
||||||
|
|
||||||
## gles2 renderer
|
|
||||||
|
|
||||||
* *WLR_RENDERER_ALLOW_SOFTWARE*: allows the gles2 renderer to use software
|
|
||||||
rendering
|
|
||||||
|
|
||||||
# Generic
|
|
||||||
|
|
||||||
* *DISPLAY*: if set probe X11 backend in `wlr_backend_autocreate`
|
|
||||||
* *WAYLAND_DISPLAY*, *WAYLAND_SOCKET*: if set probe Wayland backend in
|
|
||||||
`wlr_backend_autocreate`
|
|
||||||
* *XCURSOR_PATH*: directory where xcursors are located
|
* *XCURSOR_PATH*: directory where xcursors are located
|
||||||
* *XDG_SESSION_ID*: if set, session ID used by the logind session
|
|
||||||
|
|
|
@ -205,11 +205,10 @@ static void remove_output(struct wayland_output *out) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct wayland_output *find_output(struct capture_context *ctx,
|
static struct wayland_output *find_output(struct capture_context *ctx,
|
||||||
struct wl_output *out, int id) {
|
struct wl_output *out, uint32_t id) {
|
||||||
struct wayland_output *output, *tmp;
|
struct wayland_output *output, *tmp;
|
||||||
wl_list_for_each_safe(output, tmp, &ctx->output_list, link) {
|
wl_list_for_each_safe(output, tmp, &ctx->output_list, link) {
|
||||||
if (output->output == out || (id >= 0 && output->id == (uint32_t)id)
|
if ((output->output == out) || (output->id == id)) {
|
||||||
|| id == -1) {
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -462,7 +461,7 @@ static void register_cb(struct capture_context *ctx) {
|
||||||
&frame_listener, ctx);
|
&frame_listener, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *vid_encode_thread(void *arg) {
|
void *vid_encode_thread(void *arg) {
|
||||||
int err = 0;
|
int err = 0;
|
||||||
struct capture_context *ctx = arg;
|
struct capture_context *ctx = arg;
|
||||||
|
|
||||||
|
@ -490,27 +489,26 @@ static void *vid_encode_thread(void *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
AVPacket *pkt = av_packet_alloc();
|
AVPacket pkt;
|
||||||
int ret = avcodec_receive_packet(ctx->avctx, pkt);
|
av_init_packet(&pkt);
|
||||||
|
|
||||||
|
int ret = avcodec_receive_packet(ctx->avctx, &pkt);
|
||||||
if (ret == AVERROR(EAGAIN)) {
|
if (ret == AVERROR(EAGAIN)) {
|
||||||
av_packet_free(&pkt);
|
|
||||||
break;
|
break;
|
||||||
} else if (ret == AVERROR_EOF) {
|
} else if (ret == AVERROR_EOF) {
|
||||||
av_log(ctx, AV_LOG_INFO, "Encoder flushed!\n");
|
av_log(ctx, AV_LOG_INFO, "Encoder flushed!\n");
|
||||||
av_packet_free(&pkt);
|
|
||||||
goto end;
|
goto end;
|
||||||
} else if (ret) {
|
} else if (ret) {
|
||||||
av_log(ctx, AV_LOG_ERROR, "Error encoding: %s!\n",
|
av_log(ctx, AV_LOG_ERROR, "Error encoding: %s!\n",
|
||||||
av_err2str(ret));
|
av_err2str(ret));
|
||||||
av_packet_free(&pkt);
|
|
||||||
err = ret;
|
err = ret;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
pkt->stream_index = 0;
|
pkt.stream_index = 0;
|
||||||
err = av_interleaved_write_frame(ctx->avf, pkt);
|
err = av_interleaved_write_frame(ctx->avf, &pkt);
|
||||||
|
|
||||||
av_packet_free(&pkt);
|
av_packet_unref(&pkt);
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
av_log(ctx, AV_LOG_ERROR, "Writing packet fail: %s!\n",
|
av_log(ctx, AV_LOG_ERROR, "Writing packet fail: %s!\n",
|
||||||
|
@ -688,7 +686,7 @@ static int init_encoding(struct capture_context *ctx) {
|
||||||
|
|
||||||
struct capture_context *q_ctx = NULL;
|
struct capture_context *q_ctx = NULL;
|
||||||
|
|
||||||
static void on_quit_signal(int signo) {
|
void on_quit_signal(int signo) {
|
||||||
printf("\r");
|
printf("\r");
|
||||||
av_log(q_ctx, AV_LOG_WARNING, "Quitting!\n");
|
av_log(q_ctx, AV_LOG_WARNING, "Quitting!\n");
|
||||||
q_ctx->quit = true;
|
q_ctx->quit = true;
|
||||||
|
@ -754,11 +752,8 @@ static int init(struct capture_context *ctx) {
|
||||||
ctx->registry = wl_display_get_registry(ctx->display);
|
ctx->registry = wl_display_get_registry(ctx->display);
|
||||||
wl_registry_add_listener(ctx->registry, ®istry_listener, ctx);
|
wl_registry_add_listener(ctx->registry, ®istry_listener, ctx);
|
||||||
|
|
||||||
// First roundtrip to fetch globals
|
|
||||||
wl_display_roundtrip(ctx->display);
|
|
||||||
|
|
||||||
// Second roundtrip to fetch wl_output information
|
|
||||||
wl_display_roundtrip(ctx->display);
|
wl_display_roundtrip(ctx->display);
|
||||||
|
wl_display_dispatch(ctx->display);
|
||||||
|
|
||||||
if (!ctx->export_manager) {
|
if (!ctx->export_manager) {
|
||||||
av_log(ctx, AV_LOG_ERROR, "Compositor doesn't support %s!\n",
|
av_log(ctx, AV_LOG_ERROR, "Compositor doesn't support %s!\n",
|
||||||
|
@ -771,77 +766,16 @@ static int init(struct capture_context *ctx) {
|
||||||
|
|
||||||
static void uninit(struct capture_context *ctx);
|
static void uninit(struct capture_context *ctx);
|
||||||
|
|
||||||
static const char usage[] = "usage: dmabuf-capture [options...] <destination file path>\n"
|
|
||||||
" -o <output ID>\n"
|
|
||||||
" -t <hardware device type>\n"
|
|
||||||
" -d <device path>\n"
|
|
||||||
" -e <encoder>\n"
|
|
||||||
" -f <pixel format>\n"
|
|
||||||
" -r <bitrate in Mbps>\n"
|
|
||||||
"\n"
|
|
||||||
"Example:\n"
|
|
||||||
" dmabuf-capture -o 32 -t vaapi -d /dev/dri/renderD129 \\\n"
|
|
||||||
" -e libx264 -f nv12 -r 12 recording.mkv\n";
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
struct capture_context ctx = {
|
int err;
|
||||||
.hardware_device = "/dev/dri/renderD128",
|
struct capture_context ctx = { 0 };
|
||||||
.encoder_name = "libx264",
|
ctx.class = &((AVClass) {
|
||||||
.out_bitrate = 12,
|
|
||||||
};
|
|
||||||
int output_id = -1;
|
|
||||||
const char *hw_device_type = "vaapi";
|
|
||||||
const char *software_format = "nv12";
|
|
||||||
int opt;
|
|
||||||
while ((opt = getopt(argc, argv, "ho:t:d:e:f:r:")) != -1) {
|
|
||||||
char *end;
|
|
||||||
switch (opt) {
|
|
||||||
case 'o':
|
|
||||||
output_id = strtol(optarg, &end, 10);
|
|
||||||
if (optarg[0] == '\0' || end[0] != '\0') {
|
|
||||||
fprintf(stderr, "Output ID is not an integer\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
hw_device_type = optarg;
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
ctx.hardware_device = optarg;
|
|
||||||
break;
|
|
||||||
case 'e':
|
|
||||||
ctx.encoder_name = optarg;
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
software_format = optarg;
|
|
||||||
break;
|
|
||||||
case 'r':
|
|
||||||
ctx.out_bitrate = strtof(optarg, &end);
|
|
||||||
if (optarg[0] == '\0' || end[0] != '\0') {
|
|
||||||
fprintf(stderr, "Bitrate is not a floating-pointer number\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "%s", usage);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (optind >= argc) {
|
|
||||||
fprintf(stderr, "Missing destination file argument\n");
|
|
||||||
fprintf(stderr, "%s", usage);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
ctx.out_filename = argv[optind];
|
|
||||||
|
|
||||||
ctx.class = &((AVClass){
|
|
||||||
.class_name = "dmabuf-capture",
|
.class_name = "dmabuf-capture",
|
||||||
.item_name = av_default_item_name,
|
.item_name = av_default_item_name,
|
||||||
.version = LIBAVUTIL_VERSION_INT,
|
.version = LIBAVUTIL_VERSION_INT,
|
||||||
});
|
});
|
||||||
|
|
||||||
int err = init(&ctx);
|
err = init(&ctx);
|
||||||
if (err) {
|
if (err) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
@ -852,16 +786,31 @@ int main(int argc, char *argv[]) {
|
||||||
o->make, o->model, o->id);
|
o->make, o->model, o->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
o = find_output(&ctx, NULL, output_id);
|
if (argc != 8) {
|
||||||
|
printf("Invalid number of arguments! Usage and example:\n"
|
||||||
|
"./dmabuf-capture <source id> <hardware device type> <device> "
|
||||||
|
"<encoder name> <pixel format> <bitrate in Mbps> <file path>\n"
|
||||||
|
"./dmabuf-capture 0 vaapi /dev/dri/renderD129 libx264 nv12 12 "
|
||||||
|
"dmabuf_recording_01.mkv\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int o_id = strtol(argv[1], NULL, 10);
|
||||||
|
o = find_output(&ctx, NULL, o_id);
|
||||||
if (!o) {
|
if (!o) {
|
||||||
printf("Unable to find output with ID %d\n", output_id);
|
printf("Unable to find output with ID %i!\n", o_id);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.target_output = o->output;
|
ctx.target_output = o->output;
|
||||||
ctx.with_cursor = true;
|
ctx.with_cursor = true;
|
||||||
ctx.hw_device_type = av_hwdevice_find_type_by_name(hw_device_type);
|
ctx.hw_device_type = av_hwdevice_find_type_by_name(argv[2]);
|
||||||
ctx.software_format = av_get_pix_fmt(software_format);
|
ctx.hardware_device = argv[3];
|
||||||
|
|
||||||
|
ctx.encoder_name = argv[4];
|
||||||
|
ctx.software_format = av_get_pix_fmt(argv[5]);
|
||||||
|
ctx.out_bitrate = strtof(argv[6], NULL);
|
||||||
|
ctx.out_filename = argv[7];
|
||||||
|
|
||||||
av_dict_set(&ctx.encoder_opts, "preset", "veryfast", 0);
|
av_dict_set(&ctx.encoder_opts, "preset", "veryfast", 0);
|
||||||
|
|
||||||
|
|
|
@ -1,122 +0,0 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
|
|
||||||
#include <EGL/egl.h>
|
|
||||||
#include <EGL/eglext.h>
|
|
||||||
|
|
||||||
#include "egl_common.h"
|
|
||||||
|
|
||||||
EGLDisplay egl_display;
|
|
||||||
EGLConfig egl_config;
|
|
||||||
EGLContext egl_context;
|
|
||||||
|
|
||||||
PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT;
|
|
||||||
PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC eglCreatePlatformWindowSurfaceEXT;
|
|
||||||
|
|
||||||
const EGLint config_attribs[] = {
|
|
||||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
|
||||||
EGL_RED_SIZE, 1,
|
|
||||||
EGL_GREEN_SIZE, 1,
|
|
||||||
EGL_BLUE_SIZE, 1,
|
|
||||||
EGL_ALPHA_SIZE, 1,
|
|
||||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
|
||||||
EGL_NONE,
|
|
||||||
};
|
|
||||||
|
|
||||||
const EGLint context_attribs[] = {
|
|
||||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
|
||||||
EGL_NONE,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool egl_init(struct wl_display *display) {
|
|
||||||
const char *client_exts_str =
|
|
||||||
eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
|
|
||||||
if (client_exts_str == NULL) {
|
|
||||||
if (eglGetError() == EGL_BAD_DISPLAY) {
|
|
||||||
fprintf(stderr, "EGL_EXT_client_extensions not supported\n");
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Failed to query EGL client extensions\n");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strstr(client_exts_str, "EGL_EXT_platform_base")) {
|
|
||||||
fprintf(stderr, "EGL_EXT_platform_base not supported\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strstr(client_exts_str, "EGL_EXT_platform_wayland")) {
|
|
||||||
fprintf(stderr, "EGL_EXT_platform_wayland not supported\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
eglGetPlatformDisplayEXT =
|
|
||||||
(void *)eglGetProcAddress("eglGetPlatformDisplayEXT");
|
|
||||||
if (eglGetPlatformDisplayEXT == NULL) {
|
|
||||||
fprintf(stderr, "Failed to get eglGetPlatformDisplayEXT\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
eglCreatePlatformWindowSurfaceEXT =
|
|
||||||
(void *)eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT");
|
|
||||||
if (eglCreatePlatformWindowSurfaceEXT == NULL) {
|
|
||||||
fprintf(stderr, "Failed to get eglCreatePlatformWindowSurfaceEXT\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
egl_display =
|
|
||||||
eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT,
|
|
||||||
display, NULL);
|
|
||||||
if (egl_display == EGL_NO_DISPLAY) {
|
|
||||||
fprintf(stderr, "Failed to create EGL display\n");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eglInitialize(egl_display, NULL, NULL) == EGL_FALSE) {
|
|
||||||
fprintf(stderr, "Failed to initialize EGL\n");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
EGLint matched = 0;
|
|
||||||
if (!eglChooseConfig(egl_display, config_attribs,
|
|
||||||
&egl_config, 1, &matched)) {
|
|
||||||
fprintf(stderr, "eglChooseConfig failed\n");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (matched == 0) {
|
|
||||||
fprintf(stderr, "Failed to match an EGL config\n");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
egl_context =
|
|
||||||
eglCreateContext(egl_display, egl_config,
|
|
||||||
EGL_NO_CONTEXT, context_attribs);
|
|
||||||
if (egl_context == EGL_NO_CONTEXT) {
|
|
||||||
fprintf(stderr, "Failed to create EGL context\n");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
error:
|
|
||||||
eglMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE,
|
|
||||||
EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
||||||
if (egl_display) {
|
|
||||||
eglTerminate(egl_display);
|
|
||||||
}
|
|
||||||
eglReleaseThread();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void egl_finish(void) {
|
|
||||||
eglMakeCurrent(egl_display, EGL_NO_SURFACE,
|
|
||||||
EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
||||||
eglDestroyContext(egl_display, egl_context);
|
|
||||||
eglTerminate(egl_display);
|
|
||||||
eglReleaseThread();
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
#ifndef _EGL_COMMON_H
|
|
||||||
#define _EGL_COMMON_H
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
|
|
||||||
#include <EGL/egl.h>
|
|
||||||
#include <EGL/eglext.h>
|
|
||||||
|
|
||||||
extern EGLDisplay egl_display;
|
|
||||||
extern EGLConfig egl_config;
|
|
||||||
extern EGLContext egl_context;
|
|
||||||
|
|
||||||
extern PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC eglCreatePlatformWindowSurfaceEXT;
|
|
||||||
|
|
||||||
bool egl_init(struct wl_display *display);
|
|
||||||
|
|
||||||
void egl_finish(void);
|
|
|
@ -7,48 +7,39 @@
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
|
#include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
#define WLR_FOREIGN_TOPLEVEL_MANAGEMENT_VERSION 3
|
/**
|
||||||
|
* Usage:
|
||||||
|
* 1. foreign-toplevel
|
||||||
static void print_help(void) {
|
* Prints a list of opened toplevels
|
||||||
static const char usage[] =
|
* 2. foreign-toplevel -f <id>
|
||||||
"Usage: foreign-toplevel [OPTIONS] ...\n"
|
* Focus the toplevel with the given id
|
||||||
"Manage and view information about toplevel windows.\n"
|
* 3. foreign-toplevel -a <id>
|
||||||
"\n"
|
* Maximize the toplevel with the given id
|
||||||
" -f <id> focus\n"
|
* 4. foreign-toplevel -u <id>
|
||||||
" -s <id> fullscreen\n"
|
* Unmaximize the toplevel with the given id
|
||||||
" -o <output_id> select output for fullscreen toplevel to appear on. Use this\n"
|
* 5. foreign-toplevel -i <id>
|
||||||
" option with -s. View available outputs with wayland-info.\n"
|
* Minimize the toplevel with the given id
|
||||||
" -S <id> unfullscreen\n"
|
* 6. foreign-toplevel -r <id>
|
||||||
" -a <id> maximize\n"
|
* Restore(unminimize) the toplevel with the given id
|
||||||
" -u <id> unmaximize\n"
|
* 7. foreign-toplevel -c <id>
|
||||||
" -i <id> minimize\n"
|
* Close the toplevel with the given id
|
||||||
" -r <id> restore(unminimize)\n"
|
* 8. foreign-toplevel -m
|
||||||
" -c <id> close\n"
|
* Continuously print changes to the list of opened toplevels.
|
||||||
" -m continuously print changes to the list of opened toplevels\n"
|
* Can be used together with some of the previous options.
|
||||||
" Can be used together with some of the previous options.\n"
|
*/
|
||||||
" -h print help message and quit\n";
|
|
||||||
fprintf(stderr, "%s", usage);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum toplevel_state_field {
|
enum toplevel_state_field {
|
||||||
TOPLEVEL_STATE_MAXIMIZED = (1 << 0),
|
TOPLEVEL_STATE_MAXIMIZED = 1,
|
||||||
TOPLEVEL_STATE_MINIMIZED = (1 << 1),
|
TOPLEVEL_STATE_MINIMIZED = 2,
|
||||||
TOPLEVEL_STATE_ACTIVATED = (1 << 2),
|
TOPLEVEL_STATE_ACTIVATED = 4,
|
||||||
TOPLEVEL_STATE_FULLSCREEN = (1 << 3),
|
TOPLEVEL_STATE_INVALID = 8,
|
||||||
TOPLEVEL_STATE_INVALID = (1 << 4),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint32_t no_parent = (uint32_t)-1;
|
|
||||||
static struct wl_output *pref_output = NULL;
|
|
||||||
static uint32_t pref_output_id = UINT32_MAX;
|
|
||||||
|
|
||||||
struct toplevel_state {
|
struct toplevel_state {
|
||||||
char *title;
|
char *title;
|
||||||
char *app_id;
|
char *app_id;
|
||||||
|
|
||||||
uint32_t state;
|
uint32_t state;
|
||||||
uint32_t parent_id;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void copy_state(struct toplevel_state *current,
|
static void copy_state(struct toplevel_state *current,
|
||||||
|
@ -73,8 +64,6 @@ static void copy_state(struct toplevel_state *current,
|
||||||
current->state = pending->state;
|
current->state = pending->state;
|
||||||
}
|
}
|
||||||
|
|
||||||
current->parent_id = pending->parent_id;
|
|
||||||
|
|
||||||
pending->state = TOPLEVEL_STATE_INVALID;
|
pending->state = TOPLEVEL_STATE_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,12 +81,6 @@ static void print_toplevel(struct toplevel_v1 *toplevel, bool print_endl) {
|
||||||
toplevel->current.title ?: "(nil)",
|
toplevel->current.title ?: "(nil)",
|
||||||
toplevel->current.app_id ?: "(nil)");
|
toplevel->current.app_id ?: "(nil)");
|
||||||
|
|
||||||
if (toplevel->current.parent_id != no_parent) {
|
|
||||||
printf(" parent=%u", toplevel->current.parent_id);
|
|
||||||
} else {
|
|
||||||
printf(" no parent");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (print_endl) {
|
if (print_endl) {
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
@ -119,20 +102,12 @@ static void print_toplevel_state(struct toplevel_v1 *toplevel, bool print_endl)
|
||||||
} else {
|
} else {
|
||||||
printf(" inactive");
|
printf(" inactive");
|
||||||
}
|
}
|
||||||
if (toplevel->current.state & TOPLEVEL_STATE_FULLSCREEN) {
|
|
||||||
printf(" fullscreen");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (print_endl) {
|
if (print_endl) {
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void finish_toplevel_state(struct toplevel_state *state) {
|
|
||||||
free(state->title);
|
|
||||||
free(state->app_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void toplevel_handle_title(void *data,
|
static void toplevel_handle_title(void *data,
|
||||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel,
|
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel,
|
||||||
const char *title) {
|
const char *title) {
|
||||||
|
@ -177,8 +152,6 @@ static uint32_t array_to_state(struct wl_array *array) {
|
||||||
state |= TOPLEVEL_STATE_MINIMIZED;
|
state |= TOPLEVEL_STATE_MINIMIZED;
|
||||||
if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED)
|
if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED)
|
||||||
state |= TOPLEVEL_STATE_ACTIVATED;
|
state |= TOPLEVEL_STATE_ACTIVATED;
|
||||||
if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN)
|
|
||||||
state |= TOPLEVEL_STATE_FULLSCREEN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
|
@ -191,28 +164,6 @@ static void toplevel_handle_state(void *data,
|
||||||
toplevel->pending.state = array_to_state(state);
|
toplevel->pending.state = array_to_state(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager = NULL;
|
|
||||||
static struct wl_list toplevel_list;
|
|
||||||
|
|
||||||
static void toplevel_handle_parent(void *data,
|
|
||||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel,
|
|
||||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_parent) {
|
|
||||||
struct toplevel_v1 *toplevel = data;
|
|
||||||
toplevel->pending.parent_id = no_parent;
|
|
||||||
if (zwlr_parent) {
|
|
||||||
struct toplevel_v1 *toplevel_tmp;
|
|
||||||
wl_list_for_each(toplevel_tmp, &toplevel_list, link) {
|
|
||||||
if (toplevel_tmp->zwlr_toplevel == zwlr_parent) {
|
|
||||||
toplevel->pending.parent_id = toplevel_tmp->id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (toplevel->pending.parent_id == no_parent) {
|
|
||||||
fprintf(stderr, "Cannot find parent toplevel!\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void toplevel_handle_done(void *data,
|
static void toplevel_handle_done(void *data,
|
||||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) {
|
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) {
|
||||||
struct toplevel_v1 *toplevel = data;
|
struct toplevel_v1 *toplevel = data;
|
||||||
|
@ -233,9 +184,6 @@ static void toplevel_handle_closed(void *data,
|
||||||
printf(" closed\n");
|
printf(" closed\n");
|
||||||
|
|
||||||
zwlr_foreign_toplevel_handle_v1_destroy(zwlr_toplevel);
|
zwlr_foreign_toplevel_handle_v1_destroy(zwlr_toplevel);
|
||||||
finish_toplevel_state(&toplevel->current);
|
|
||||||
finish_toplevel_state(&toplevel->pending);
|
|
||||||
free(toplevel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_impl = {
|
static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_impl = {
|
||||||
|
@ -246,9 +194,11 @@ static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_impl = {
|
||||||
.state = toplevel_handle_state,
|
.state = toplevel_handle_state,
|
||||||
.done = toplevel_handle_done,
|
.done = toplevel_handle_done,
|
||||||
.closed = toplevel_handle_closed,
|
.closed = toplevel_handle_closed,
|
||||||
.parent = toplevel_handle_parent
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager = NULL;
|
||||||
|
static struct wl_list toplevel_list;
|
||||||
|
|
||||||
static void toplevel_manager_handle_toplevel(void *data,
|
static void toplevel_manager_handle_toplevel(void *data,
|
||||||
struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager,
|
struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager,
|
||||||
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) {
|
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) {
|
||||||
|
@ -260,8 +210,6 @@ static void toplevel_manager_handle_toplevel(void *data,
|
||||||
|
|
||||||
toplevel->id = global_id++;
|
toplevel->id = global_id++;
|
||||||
toplevel->zwlr_toplevel = zwlr_toplevel;
|
toplevel->zwlr_toplevel = zwlr_toplevel;
|
||||||
toplevel->current.parent_id = no_parent;
|
|
||||||
toplevel->pending.parent_id = no_parent;
|
|
||||||
wl_list_insert(&toplevel_list, &toplevel->link);
|
wl_list_insert(&toplevel_list, &toplevel->link);
|
||||||
|
|
||||||
zwlr_foreign_toplevel_handle_v1_add_listener(zwlr_toplevel, &toplevel_impl,
|
zwlr_foreign_toplevel_handle_v1_add_listener(zwlr_toplevel, &toplevel_impl,
|
||||||
|
@ -282,16 +230,13 @@ struct wl_seat *seat = NULL;
|
||||||
static void handle_global(void *data, struct wl_registry *registry,
|
static void handle_global(void *data, struct wl_registry *registry,
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
uint32_t name, const char *interface, uint32_t version) {
|
||||||
if (strcmp(interface, wl_output_interface.name) == 0) {
|
if (strcmp(interface, wl_output_interface.name) == 0) {
|
||||||
if (name == pref_output_id) {
|
struct wl_output *output = wl_registry_bind(registry, name,
|
||||||
pref_output = wl_registry_bind(registry, name,
|
&wl_output_interface, version);
|
||||||
&wl_output_interface, version);
|
wl_output_set_user_data(output, (void*)(size_t)name); // assign some ID to the output
|
||||||
|
|
||||||
}
|
|
||||||
} else if (strcmp(interface,
|
} else if (strcmp(interface,
|
||||||
zwlr_foreign_toplevel_manager_v1_interface.name) == 0) {
|
zwlr_foreign_toplevel_manager_v1_interface.name) == 0) {
|
||||||
toplevel_manager = wl_registry_bind(registry, name,
|
toplevel_manager = wl_registry_bind(registry, name,
|
||||||
&zwlr_foreign_toplevel_manager_v1_interface,
|
&zwlr_foreign_toplevel_manager_v1_interface, 1);
|
||||||
WLR_FOREIGN_TOPLEVEL_MANAGEMENT_VERSION);
|
|
||||||
|
|
||||||
wl_list_init(&toplevel_list);
|
wl_list_init(&toplevel_list);
|
||||||
zwlr_foreign_toplevel_manager_v1_add_listener(toplevel_manager,
|
zwlr_foreign_toplevel_manager_v1_add_listener(toplevel_manager,
|
||||||
|
@ -333,11 +278,11 @@ int main(int argc, char **argv) {
|
||||||
int focus_id = -1, close_id = -1;
|
int focus_id = -1, close_id = -1;
|
||||||
int maximize_id = -1, unmaximize_id = -1;
|
int maximize_id = -1, unmaximize_id = -1;
|
||||||
int minimize_id = -1, restore_id = -1;
|
int minimize_id = -1, restore_id = -1;
|
||||||
int fullscreen_id = -1, unfullscreen_id = -1;
|
|
||||||
int one_shot = 1;
|
int one_shot = 1;
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "f:a:u:i:r:c:s:S:mo:h")) != -1) {
|
// TODO maybe print usage with -h?
|
||||||
|
while ((c = getopt(argc, argv, "f:a:u:i:r:c:m")) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'f':
|
case 'f':
|
||||||
focus_id = atoi(optarg);
|
focus_id = atoi(optarg);
|
||||||
|
@ -357,26 +302,9 @@ int main(int argc, char **argv) {
|
||||||
case 'c':
|
case 'c':
|
||||||
close_id = atoi(optarg);
|
close_id = atoi(optarg);
|
||||||
break;
|
break;
|
||||||
case 's':
|
|
||||||
fullscreen_id = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'S':
|
|
||||||
unfullscreen_id = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case 'm':
|
case 'm':
|
||||||
one_shot = 0;
|
one_shot = 0;
|
||||||
break;
|
break;
|
||||||
case 'o':
|
|
||||||
pref_output_id = atoi(optarg);
|
|
||||||
break;
|
|
||||||
case '?':
|
|
||||||
print_help();
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
print_help();
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,6 +316,7 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
|
wl_display_dispatch(display);
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
if (toplevel_manager == NULL) {
|
if (toplevel_manager == NULL) {
|
||||||
|
@ -413,16 +342,6 @@ int main(int argc, char **argv) {
|
||||||
if ((toplevel = toplevel_by_id_or_bail(restore_id))) {
|
if ((toplevel = toplevel_by_id_or_bail(restore_id))) {
|
||||||
zwlr_foreign_toplevel_handle_v1_unset_minimized(toplevel->zwlr_toplevel);
|
zwlr_foreign_toplevel_handle_v1_unset_minimized(toplevel->zwlr_toplevel);
|
||||||
}
|
}
|
||||||
if ((toplevel = toplevel_by_id_or_bail(fullscreen_id))) {
|
|
||||||
if (pref_output_id != UINT32_MAX && pref_output == NULL) {
|
|
||||||
fprintf(stderr, "Could not find output %i\n", pref_output_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
zwlr_foreign_toplevel_handle_v1_set_fullscreen(toplevel->zwlr_toplevel, pref_output);
|
|
||||||
}
|
|
||||||
if ((toplevel = toplevel_by_id_or_bail(unfullscreen_id))) {
|
|
||||||
zwlr_foreign_toplevel_handle_v1_unset_fullscreen(toplevel->zwlr_toplevel);
|
|
||||||
}
|
|
||||||
if ((toplevel = toplevel_by_id_or_bail(close_id))) {
|
if ((toplevel = toplevel_by_id_or_bail(close_id))) {
|
||||||
zwlr_foreign_toplevel_handle_v1_close(toplevel->zwlr_toplevel);
|
zwlr_foreign_toplevel_handle_v1_close(toplevel->zwlr_toplevel);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,17 +5,16 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
#include <wlr/render/allocator.h>
|
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/types/wlr_compositor.h>
|
#include <wlr/types/wlr_compositor.h>
|
||||||
#include <wlr/types/wlr_fullscreen_shell_v1.h>
|
#include <wlr/types/wlr_fullscreen_shell_v1.h>
|
||||||
|
#include <wlr/types/wlr_linux_dmabuf_v1.h>
|
||||||
#include <wlr/types/wlr_matrix.h>
|
#include <wlr/types/wlr_matrix.h>
|
||||||
#include <wlr/types/wlr_output_layout.h>
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
#include <wlr/types/wlr_output.h>
|
#include <wlr/types/wlr_output.h>
|
||||||
#include <wlr/types/wlr_surface.h>
|
#include <wlr/types/wlr_surface.h>
|
||||||
#include <wlr/util/box.h>
|
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,7 +25,6 @@ struct fullscreen_server {
|
||||||
struct wl_display *wl_display;
|
struct wl_display *wl_display;
|
||||||
struct wlr_backend *backend;
|
struct wlr_backend *backend;
|
||||||
struct wlr_renderer *renderer;
|
struct wlr_renderer *renderer;
|
||||||
struct wlr_allocator *allocator;
|
|
||||||
|
|
||||||
struct wlr_fullscreen_shell_v1 *fullscreen_shell;
|
struct wlr_fullscreen_shell_v1 *fullscreen_shell;
|
||||||
struct wl_listener present_surface;
|
struct wl_listener present_surface;
|
||||||
|
@ -49,6 +47,7 @@ struct fullscreen_output {
|
||||||
struct render_data {
|
struct render_data {
|
||||||
struct wlr_output *output;
|
struct wlr_output *output;
|
||||||
struct wlr_renderer *renderer;
|
struct wlr_renderer *renderer;
|
||||||
|
struct tinywl_view *view;
|
||||||
struct timespec *when;
|
struct timespec *when;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -90,7 +89,7 @@ static void output_handle_frame(struct wl_listener *listener, void *data) {
|
||||||
int width, height;
|
int width, height;
|
||||||
wlr_output_effective_resolution(output->wlr_output, &width, &height);
|
wlr_output_effective_resolution(output->wlr_output, &width, &height);
|
||||||
|
|
||||||
if (!wlr_output_attach_render(output->wlr_output, NULL)) {
|
if (!wlr_output_make_current(output->wlr_output, NULL)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +108,7 @@ static void output_handle_frame(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_renderer_end(renderer);
|
wlr_renderer_end(renderer);
|
||||||
wlr_output_commit(output->wlr_output);
|
wlr_output_swap_buffers(output->wlr_output, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void output_set_surface(struct fullscreen_output *output,
|
static void output_set_surface(struct fullscreen_output *output,
|
||||||
|
@ -148,7 +147,11 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) {
|
||||||
wl_container_of(listener, server, new_output);
|
wl_container_of(listener, server, new_output);
|
||||||
struct wlr_output *wlr_output = data;
|
struct wlr_output *wlr_output = data;
|
||||||
|
|
||||||
wlr_output_init_render(wlr_output, server->allocator, server->renderer);
|
if (!wl_list_empty(&wlr_output->modes)) {
|
||||||
|
struct wlr_output_mode *mode =
|
||||||
|
wl_container_of(wlr_output->modes.prev, mode, link);
|
||||||
|
wlr_output_set_mode(wlr_output, mode);
|
||||||
|
}
|
||||||
|
|
||||||
struct fullscreen_output *output =
|
struct fullscreen_output *output =
|
||||||
calloc(1, sizeof(struct fullscreen_output));
|
calloc(1, sizeof(struct fullscreen_output));
|
||||||
|
@ -160,13 +163,6 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) {
|
||||||
|
|
||||||
wlr_output_layout_add_auto(server->output_layout, wlr_output);
|
wlr_output_layout_add_auto(server->output_layout, wlr_output);
|
||||||
wlr_output_create_global(wlr_output);
|
wlr_output_create_global(wlr_output);
|
||||||
|
|
||||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
|
|
||||||
if (mode != NULL) {
|
|
||||||
wlr_output_set_mode(wlr_output, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_output_commit(wlr_output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void server_handle_present_surface(struct wl_listener *listener,
|
static void server_handle_present_surface(struct wl_listener *listener,
|
||||||
|
@ -206,13 +202,12 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
struct fullscreen_server server = {0};
|
struct fullscreen_server server = {0};
|
||||||
server.wl_display = wl_display_create();
|
server.wl_display = wl_display_create();
|
||||||
server.backend = wlr_backend_autocreate(server.wl_display);
|
server.backend = wlr_backend_autocreate(server.wl_display, NULL);
|
||||||
server.renderer = wlr_renderer_autocreate(server.backend);
|
server.renderer = wlr_backend_get_renderer(server.backend);
|
||||||
wlr_renderer_init_wl_display(server.renderer, server.wl_display);
|
wlr_renderer_init_wl_display(server.renderer, server.wl_display);
|
||||||
server.allocator = wlr_allocator_autocreate(server.backend,
|
|
||||||
server.renderer);
|
|
||||||
|
|
||||||
wlr_compositor_create(server.wl_display, server.renderer);
|
wlr_compositor_create(server.wl_display, server.renderer);
|
||||||
|
wlr_linux_dmabuf_v1_create(server.wl_display, server.renderer);
|
||||||
|
|
||||||
server.output_layout = wlr_output_layout_create();
|
server.output_layout = wlr_output_layout_create();
|
||||||
|
|
||||||
|
|
|
@ -162,6 +162,7 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
|
wl_display_dispatch(display);
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
if (gamma_control_manager == NULL) {
|
if (gamma_control_manager == NULL) {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <wayland-egl.h>
|
#include <wayland-egl.h>
|
||||||
#include "egl_common.h"
|
#include <wlr/render/egl.h>
|
||||||
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
#include "idle-inhibit-unstable-v1-client-protocol.h"
|
||||||
#include "xdg-shell-client-protocol.h"
|
#include "xdg-shell-client-protocol.h"
|
||||||
|
|
||||||
|
@ -27,11 +27,12 @@ static struct xdg_wm_base *wm_base = NULL;
|
||||||
static struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager = NULL;
|
static struct zwp_idle_inhibit_manager_v1 *idle_inhibit_manager = NULL;
|
||||||
static struct zwp_idle_inhibitor_v1 *idle_inhibitor = NULL;
|
static struct zwp_idle_inhibitor_v1 *idle_inhibitor = NULL;
|
||||||
|
|
||||||
|
struct wlr_egl egl;
|
||||||
struct wl_egl_window *egl_window;
|
struct wl_egl_window *egl_window;
|
||||||
struct wlr_egl_surface *egl_surface;
|
struct wlr_egl_surface *egl_surface;
|
||||||
|
|
||||||
static void draw(void) {
|
static void draw(void) {
|
||||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
|
||||||
|
|
||||||
float color[] = {1.0, 1.0, 0.0, 1.0};
|
float color[] = {1.0, 1.0, 0.0, 1.0};
|
||||||
if (idle_inhibitor) {
|
if (idle_inhibitor) {
|
||||||
|
@ -42,7 +43,7 @@ static void draw(void) {
|
||||||
glClearColor(color[0], color[1], color[2], 1.0);
|
glClearColor(color[0], color[1], color[2], 1.0);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
eglSwapBuffers(egl_display, egl_surface);
|
eglSwapBuffers(egl.display, egl_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
|
static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
|
||||||
|
@ -176,6 +177,7 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
|
wl_display_dispatch(display);
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
if (compositor == NULL) {
|
if (compositor == NULL) {
|
||||||
|
@ -191,7 +193,8 @@ int main(int argc, char **argv) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
egl_init(display);
|
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
|
||||||
|
WL_SHM_FORMAT_ARGB8888);
|
||||||
|
|
||||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
||||||
struct xdg_surface *xdg_surface =
|
struct xdg_surface *xdg_surface =
|
||||||
|
@ -212,8 +215,7 @@ int main(int argc, char **argv) {
|
||||||
wl_surface_commit(surface);
|
wl_surface_commit(surface);
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(surface, width, height);
|
egl_window = wl_egl_window_create(surface, width, height);
|
||||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
egl_surface = wlr_egl_create_surface(&egl, egl_window);
|
||||||
egl_display, egl_config, egl_window, NULL);
|
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener = {
|
||||||
.resumed = handle_resume,
|
.resumed = handle_resume,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int parse_args(int argc, char *argv[]) {
|
int parse_args(int argc, char *argv[]) {
|
||||||
int c;
|
int c;
|
||||||
while ((c = getopt(argc, argv, "c:hs:t:")) != -1) {
|
while ((c = getopt(argc, argv, "c:hs:t:")) != -1) {
|
||||||
switch(c)
|
switch(c)
|
||||||
|
@ -81,7 +81,7 @@ static int parse_args(int argc, char *argv[]) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *simulate_activity(void *data) {
|
void *simulate_activity(void *data) {
|
||||||
sleep(simulate_activity_timeout);
|
sleep(simulate_activity_timeout);
|
||||||
fprintf(stdout, "simulate user activity\n");
|
fprintf(stdout, "simulate user activity\n");
|
||||||
struct thread_args *arg = data;
|
struct thread_args *arg = data;
|
||||||
|
@ -90,7 +90,7 @@ static void *simulate_activity(void *data) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *close_program(void *data) {
|
void *close_program(void *data) {
|
||||||
sleep(close_timeout);
|
sleep(close_timeout);
|
||||||
struct thread_args *arg = data;
|
struct thread_args *arg = data;
|
||||||
org_kde_kwin_idle_timeout_release(arg->timer);
|
org_kde_kwin_idle_timeout_release(arg->timer);
|
||||||
|
@ -100,7 +100,7 @@ static void *close_program(void *data) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *main_loop(void *data) {
|
void *main_loop(void *data) {
|
||||||
struct wl_display *display = data;
|
struct wl_display *display = data;
|
||||||
while (wl_display_dispatch(display) != -1) {
|
while (wl_display_dispatch(display) != -1) {
|
||||||
;
|
;
|
||||||
|
@ -125,8 +125,9 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
|
wl_display_dispatch(display);
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
wl_registry_destroy(registry);
|
free(registry);
|
||||||
|
|
||||||
if (idle_manager == NULL) {
|
if (idle_manager == NULL) {
|
||||||
fprintf(stderr, "display doesn't support idle protocol\n");
|
fprintf(stderr, "display doesn't support idle protocol\n");
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <wayland-egl.h>
|
#include <wayland-egl.h>
|
||||||
#include "egl_common.h"
|
#include <wlr/render/egl.h>
|
||||||
#include "wlr-input-inhibitor-unstable-v1-client-protocol.h"
|
#include "wlr-input-inhibitor-unstable-v1-client-protocol.h"
|
||||||
#include "xdg-shell-client-protocol.h"
|
#include "xdg-shell-client-protocol.h"
|
||||||
|
|
||||||
|
@ -18,11 +18,12 @@ static struct xdg_wm_base *wm_base = NULL;
|
||||||
static struct zwlr_input_inhibit_manager_v1 *input_inhibit_manager = NULL;
|
static struct zwlr_input_inhibit_manager_v1 *input_inhibit_manager = NULL;
|
||||||
static struct zwlr_input_inhibitor_v1 *input_inhibitor = NULL;
|
static struct zwlr_input_inhibitor_v1 *input_inhibitor = NULL;
|
||||||
|
|
||||||
|
struct wlr_egl egl;
|
||||||
struct wl_egl_window *egl_window;
|
struct wl_egl_window *egl_window;
|
||||||
struct wlr_egl_surface *egl_surface;
|
struct wlr_egl_surface *egl_surface;
|
||||||
|
|
||||||
static void render_frame(void) {
|
static void render_frame(void) {
|
||||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
|
||||||
|
|
||||||
glViewport(0, 0, width, height);
|
glViewport(0, 0, width, height);
|
||||||
if (keys) {
|
if (keys) {
|
||||||
|
@ -32,7 +33,7 @@ static void render_frame(void) {
|
||||||
}
|
}
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
eglSwapBuffers(egl_display, egl_surface);
|
eglSwapBuffers(egl.display, egl_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xdg_surface_handle_configure(void *data,
|
static void xdg_surface_handle_configure(void *data,
|
||||||
|
@ -149,6 +150,7 @@ int main(int argc, char **argv) {
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
assert(registry);
|
assert(registry);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
|
wl_display_dispatch(display);
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
assert(compositor && seat && wm_base && input_inhibit_manager);
|
assert(compositor && seat && wm_base && input_inhibit_manager);
|
||||||
|
|
||||||
|
@ -156,7 +158,8 @@ int main(int argc, char **argv) {
|
||||||
input_inhibit_manager);
|
input_inhibit_manager);
|
||||||
assert(input_inhibitor);
|
assert(input_inhibitor);
|
||||||
|
|
||||||
egl_init(display);
|
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
|
||||||
|
WL_SHM_FORMAT_ARGB8888);
|
||||||
|
|
||||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
||||||
assert(surface);
|
assert(surface);
|
||||||
|
@ -172,8 +175,7 @@ int main(int argc, char **argv) {
|
||||||
wl_surface_commit(surface);
|
wl_surface_commit(surface);
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(surface, width, height);
|
egl_window = wl_egl_window_create(surface, width, height);
|
||||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
egl_surface = wlr_egl_create_surface(&egl, egl_window);
|
||||||
egl_display, egl_config, egl_window, NULL);
|
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
|
|
@ -1,216 +0,0 @@
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
#include <xkbcommon/xkbcommon.h>
|
|
||||||
#include "input-method-unstable-v2-client-protocol.h"
|
|
||||||
|
|
||||||
static struct wl_display *display = NULL;
|
|
||||||
static struct wl_seat *seat = NULL;
|
|
||||||
static struct zwp_input_method_manager_v2 *input_method_manager = NULL;
|
|
||||||
static struct zwp_input_method_v2 *input_method = NULL;
|
|
||||||
static struct zwp_input_method_keyboard_grab_v2 *kb_grab = NULL;
|
|
||||||
|
|
||||||
static bool active = false;
|
|
||||||
static bool pending_active = false;
|
|
||||||
|
|
||||||
static struct xkb_context *xkb_context = NULL;
|
|
||||||
static struct xkb_keymap *keymap = NULL;
|
|
||||||
static struct xkb_state *xkb_state = NULL;
|
|
||||||
|
|
||||||
static void handle_key(void *data,
|
|
||||||
struct zwp_input_method_keyboard_grab_v2 *im_keyboard_grab,
|
|
||||||
uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
|
|
||||||
printf("handle_key %u %u %u %u\n", serial, time, key, state);
|
|
||||||
xkb_keysym_t keysym = xkb_state_key_get_one_sym(xkb_state, key + 8);
|
|
||||||
char keysym_name[64];
|
|
||||||
xkb_keysym_get_name(keysym, keysym_name, sizeof(keysym_name));
|
|
||||||
printf("xkb translated to %s\n", keysym_name);
|
|
||||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
|
||||||
if (keysym == XKB_KEY_KP_Enter || keysym == XKB_KEY_Return) {
|
|
||||||
printf("Stopping grab\n");
|
|
||||||
zwp_input_method_keyboard_grab_v2_release(kb_grab);
|
|
||||||
kb_grab = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_modifiers(void *data,
|
|
||||||
struct zwp_input_method_keyboard_grab_v2 *im_keyboard_grab,
|
|
||||||
uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
|
|
||||||
uint32_t mods_locked, uint32_t group) {
|
|
||||||
printf("handle_modifiers %u %u %u %u %u\n", serial, mods_depressed,
|
|
||||||
mods_latched, mods_locked, group);
|
|
||||||
xkb_state_update_mask(xkb_state, mods_depressed, mods_latched,
|
|
||||||
mods_locked, 0, 0, group);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_keymap(void *data,
|
|
||||||
struct zwp_input_method_keyboard_grab_v2 *im_keyboard_grab,
|
|
||||||
uint32_t format, int32_t fd, uint32_t size) {
|
|
||||||
printf("handle_keymap\n");
|
|
||||||
char *keymap_string = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
|
|
||||||
xkb_keymap_unref(keymap);
|
|
||||||
keymap = xkb_keymap_new_from_string(xkb_context, keymap_string,
|
|
||||||
XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
|
|
||||||
munmap(keymap_string, size);
|
|
||||||
close(fd);
|
|
||||||
xkb_state_unref(xkb_state);
|
|
||||||
xkb_state = xkb_state_new(keymap);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_repeat_info(void *data,
|
|
||||||
struct zwp_input_method_keyboard_grab_v2 *im_keyboard_grab,
|
|
||||||
int32_t rate, int32_t delay) {
|
|
||||||
printf("handle_repeat_info %d %d", rate, delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static const struct zwp_input_method_keyboard_grab_v2_listener grab_listener = {
|
|
||||||
.key = handle_key,
|
|
||||||
.modifiers = handle_modifiers,
|
|
||||||
.keymap = handle_keymap,
|
|
||||||
.repeat_info = handle_repeat_info,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_activate(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2) {
|
|
||||||
pending_active = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_deactivate(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2) {
|
|
||||||
pending_active = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_unavailable(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2) {
|
|
||||||
printf("IM unavailable\n");
|
|
||||||
zwp_input_method_v2_destroy(zwp_input_method_v2);
|
|
||||||
input_method = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void im_activate(void *data,
|
|
||||||
struct zwp_input_method_v2 *id) {
|
|
||||||
kb_grab = zwp_input_method_v2_grab_keyboard(input_method);
|
|
||||||
if (kb_grab == NULL) {
|
|
||||||
fprintf(stderr, "Failed to grab\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
zwp_input_method_keyboard_grab_v2_add_listener(kb_grab, &grab_listener,
|
|
||||||
NULL);
|
|
||||||
printf("Started grab, press enter to stop grab\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void im_deactivate(void *data,
|
|
||||||
struct zwp_input_method_v2 *context) {
|
|
||||||
if (kb_grab != NULL) {
|
|
||||||
zwp_input_method_keyboard_grab_v2_release(kb_grab);
|
|
||||||
kb_grab = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_done(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2) {
|
|
||||||
bool prev_active = active;
|
|
||||||
if (active != pending_active) {
|
|
||||||
printf("Now %s\n", pending_active ? "active" : "inactive");
|
|
||||||
}
|
|
||||||
active = pending_active;
|
|
||||||
if (active && !prev_active) {
|
|
||||||
im_activate(data, zwp_input_method_v2);
|
|
||||||
} else if (!active && prev_active) {
|
|
||||||
im_deactivate(data, zwp_input_method_v2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_surrounding_text(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2,
|
|
||||||
const char *text, uint32_t cursor, uint32_t anchor) {
|
|
||||||
// not for this test
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_text_change_cause(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2,
|
|
||||||
uint32_t cause) {
|
|
||||||
// not for this test
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_content_type(void *data,
|
|
||||||
struct zwp_input_method_v2 *zwp_input_method_v2,
|
|
||||||
uint32_t hint, uint32_t purpose) {
|
|
||||||
// not for this test
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_input_method_v2_listener im_listener = {
|
|
||||||
.activate = handle_activate,
|
|
||||||
.deactivate = handle_deactivate,
|
|
||||||
.surrounding_text = handle_surrounding_text,
|
|
||||||
.text_change_cause = handle_text_change_cause,
|
|
||||||
.content_type = handle_content_type,
|
|
||||||
.done = handle_done,
|
|
||||||
.unavailable = handle_unavailable,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
if (strcmp(interface, zwp_input_method_manager_v2_interface.name) == 0) {
|
|
||||||
input_method_manager = wl_registry_bind(registry, name,
|
|
||||||
&zwp_input_method_manager_v2_interface, 1);
|
|
||||||
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
|
||||||
seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_global_remove(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name) {
|
|
||||||
// who cares
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
.global = handle_global,
|
|
||||||
.global_remove = handle_global_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
|
||||||
if (xkb_context == NULL) {
|
|
||||||
fprintf(stderr, "Failed to create xkb context\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
display = wl_display_connect(NULL);
|
|
||||||
if (display == NULL) {
|
|
||||||
fprintf(stderr, "Failed to create display\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
if (input_method_manager == NULL) {
|
|
||||||
fprintf(stderr, "input-method not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (seat == NULL) {
|
|
||||||
fprintf(stderr, "seat not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
input_method = zwp_input_method_manager_v2_get_input_method(
|
|
||||||
input_method_manager, seat);
|
|
||||||
zwp_input_method_v2_add_listener(input_method, &im_listener, NULL);
|
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
while (wl_display_dispatch(display) != -1) {
|
|
||||||
// This space is intentionally left blank
|
|
||||||
};
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
|
@ -176,7 +176,7 @@ static void timer_arm(unsigned seconds) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_updates(void) {
|
static void do_updates() {
|
||||||
printf("Update %d\n", update_stage);
|
printf("Update %d\n", update_stage);
|
||||||
switch (update_stage) {
|
switch (update_stage) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -240,7 +240,7 @@ static void do_updates(void) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_timer(void) {
|
static void handle_timer() {
|
||||||
printf("Timer dispatched at %d\n", update_stage);
|
printf("Timer dispatched at %d\n", update_stage);
|
||||||
do_updates();
|
do_updates();
|
||||||
}
|
}
|
||||||
|
@ -324,6 +324,7 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
|
wl_display_dispatch(display);
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
if (compositor == NULL) {
|
if (compositor == NULL) {
|
||||||
|
|
|
@ -1,261 +0,0 @@
|
||||||
#include <GLES2/gl2.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
#include <wayland-egl.h>
|
|
||||||
#include "egl_common.h"
|
|
||||||
#include "keyboard-shortcuts-inhibit-unstable-v1-client-protocol.h"
|
|
||||||
#include "xdg-shell-client-protocol.h"
|
|
||||||
|
|
||||||
#include <linux/input-event-codes.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Usage: keyboard-shortcuts-inhibit
|
|
||||||
* Creates a xdg-toplevel using the keyboard-shortcuts-inhibit protocol.
|
|
||||||
* It will be solid green, when it has an keyboard shortcuts inhibitor, and
|
|
||||||
* solid yellow if it does not.
|
|
||||||
* Left click with a pointer will toggle this state. (Touch is not supported
|
|
||||||
* for now).
|
|
||||||
* The compositor (de-)activating the inhibitor will also toggle state.
|
|
||||||
* With a compositor supporting the protocol, compositor shortcuts will be
|
|
||||||
* suspended while the inhibitor is active and the window has focus.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int width = 500, height = 300;
|
|
||||||
|
|
||||||
static struct wl_compositor *compositor = NULL;
|
|
||||||
static struct wl_seat *seat = NULL;
|
|
||||||
static struct xdg_wm_base *wm_base = NULL;
|
|
||||||
static struct zwp_keyboard_shortcuts_inhibit_manager_v1 *
|
|
||||||
keyboard_shortcuts_inhibit_manager = NULL;
|
|
||||||
static struct zwp_keyboard_shortcuts_inhibitor_v1 *
|
|
||||||
keyboard_shortcuts_inhibitor = NULL;
|
|
||||||
static bool active = false;
|
|
||||||
|
|
||||||
struct wl_egl_window *egl_window;
|
|
||||||
struct wlr_egl_surface *egl_surface;
|
|
||||||
|
|
||||||
static void draw(void) {
|
|
||||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
|
||||||
|
|
||||||
float color[] = {1.0, 1.0, 0.0, 1.0};
|
|
||||||
if (keyboard_shortcuts_inhibitor) {
|
|
||||||
color[0] = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
glViewport(0, 0, width, height);
|
|
||||||
glClearColor(color[0], color[1], color[2], 1.0);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
eglSwapBuffers(egl_display, egl_surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void keyboard_shortcuts_inhibit_handle_active(void *data,
|
|
||||||
struct zwp_keyboard_shortcuts_inhibitor_v1 *
|
|
||||||
zwp_keyboard_shortcuts_inhibitor_v1) {
|
|
||||||
active = 1;
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void keyboard_shortcuts_inhibit_handle_inactive(void *data,
|
|
||||||
struct zwp_keyboard_shortcuts_inhibitor_v1 *
|
|
||||||
zwp_keyboard_shortcuts_inhibitor_v1) {
|
|
||||||
active = 0;
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_keyboard_shortcuts_inhibitor_v1_listener
|
|
||||||
keyboard_shortcuts_inhibitor_listener = {
|
|
||||||
.active = keyboard_shortcuts_inhibit_handle_active,
|
|
||||||
.inactive = keyboard_shortcuts_inhibit_handle_inactive,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
|
|
||||||
uint32_t time, uint32_t button, uint32_t state_w) {
|
|
||||||
struct wl_surface *surface = data;
|
|
||||||
|
|
||||||
if (button == BTN_LEFT && state_w == WL_POINTER_BUTTON_STATE_PRESSED) {
|
|
||||||
if (keyboard_shortcuts_inhibitor) {
|
|
||||||
zwp_keyboard_shortcuts_inhibitor_v1_destroy(
|
|
||||||
keyboard_shortcuts_inhibitor);
|
|
||||||
keyboard_shortcuts_inhibitor = NULL;
|
|
||||||
active = false;
|
|
||||||
} else {
|
|
||||||
keyboard_shortcuts_inhibitor =
|
|
||||||
zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(
|
|
||||||
keyboard_shortcuts_inhibit_manager, surface, seat);
|
|
||||||
zwp_keyboard_shortcuts_inhibitor_v1_add_listener(
|
|
||||||
keyboard_shortcuts_inhibitor,
|
|
||||||
&keyboard_shortcuts_inhibitor_listener, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t serial, struct wl_surface *surface,
|
|
||||||
wl_fixed_t surface_x, wl_fixed_t surface_y) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t serial, struct wl_surface *surface) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
|
|
||||||
uint32_t time, uint32_t axis, wl_fixed_t value) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis_source(void *data,
|
|
||||||
struct wl_pointer *wl_pointer, uint32_t axis_source) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis_stop(void *data,
|
|
||||||
struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pointer_handle_axis_discrete(void *data,
|
|
||||||
struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_pointer_listener pointer_listener = {
|
|
||||||
.enter = pointer_handle_enter,
|
|
||||||
.leave = pointer_handle_leave,
|
|
||||||
.motion = pointer_handle_motion,
|
|
||||||
.button = pointer_handle_button,
|
|
||||||
.axis = pointer_handle_axis,
|
|
||||||
.frame = pointer_handle_frame,
|
|
||||||
.axis_source = pointer_handle_axis_source,
|
|
||||||
.axis_stop = pointer_handle_axis_stop,
|
|
||||||
.axis_discrete = pointer_handle_axis_discrete,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void xdg_surface_handle_configure(void *data,
|
|
||||||
struct xdg_surface *xdg_surface, uint32_t serial) {
|
|
||||||
xdg_surface_ack_configure(xdg_surface, serial);
|
|
||||||
wl_egl_window_resize(egl_window, width, height, 0, 0);
|
|
||||||
draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_surface_listener xdg_surface_listener = {
|
|
||||||
.configure = xdg_surface_handle_configure,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void xdg_toplevel_handle_configure(void *data,
|
|
||||||
struct xdg_toplevel *xdg_toplevel, int32_t w, int32_t h,
|
|
||||||
struct wl_array *states) {
|
|
||||||
width = w;
|
|
||||||
height = h;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xdg_toplevel_handle_close(void *data,
|
|
||||||
struct xdg_toplevel *xdg_toplevel) {
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
|
||||||
.configure = xdg_toplevel_handle_configure,
|
|
||||||
.close = xdg_toplevel_handle_close,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
if (strcmp(interface, "wl_compositor") == 0) {
|
|
||||||
compositor = wl_registry_bind(registry, name,
|
|
||||||
&wl_compositor_interface, 1);
|
|
||||||
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
|
||||||
wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
|
|
||||||
} else if (strcmp(interface, zwp_keyboard_shortcuts_inhibit_manager_v1_interface.name) == 0) {
|
|
||||||
keyboard_shortcuts_inhibit_manager = wl_registry_bind(registry, name,
|
|
||||||
&zwp_keyboard_shortcuts_inhibit_manager_v1_interface, 1);
|
|
||||||
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
|
||||||
seat = wl_registry_bind(registry, name, &wl_seat_interface, version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_global_remove(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name) {
|
|
||||||
// who cares
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
.global = handle_global,
|
|
||||||
.global_remove = handle_global_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
|
||||||
struct wl_display *display = wl_display_connect(NULL);
|
|
||||||
if (display == NULL) {
|
|
||||||
fprintf(stderr, "Failed to create display\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
if (compositor == NULL) {
|
|
||||||
fprintf(stderr, "wl-compositor not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (wm_base == NULL) {
|
|
||||||
fprintf(stderr, "xdg-shell not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (keyboard_shortcuts_inhibit_manager == NULL) {
|
|
||||||
fprintf(stderr, "keyboard-shortcuts-inhibit not available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
egl_init(display);
|
|
||||||
|
|
||||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
|
||||||
struct xdg_surface *xdg_surface =
|
|
||||||
xdg_wm_base_get_xdg_surface(wm_base, surface);
|
|
||||||
struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface);
|
|
||||||
|
|
||||||
struct wl_pointer *pointer = wl_seat_get_pointer(seat);
|
|
||||||
wl_pointer_add_listener(pointer, &pointer_listener, surface);
|
|
||||||
|
|
||||||
xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL);
|
|
||||||
xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL);
|
|
||||||
|
|
||||||
wl_surface_commit(surface);
|
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(surface, width, height);
|
|
||||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
|
||||||
egl_display, egl_config, egl_window, NULL);
|
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
keyboard_shortcuts_inhibitor =
|
|
||||||
zwp_keyboard_shortcuts_inhibit_manager_v1_inhibit_shortcuts(
|
|
||||||
keyboard_shortcuts_inhibit_manager, surface, seat);
|
|
||||||
zwp_keyboard_shortcuts_inhibitor_v1_add_listener(
|
|
||||||
keyboard_shortcuts_inhibitor,
|
|
||||||
&keyboard_shortcuts_inhibitor_listener, NULL);
|
|
||||||
|
|
||||||
draw();
|
|
||||||
|
|
||||||
while (wl_display_dispatch(display) != -1) {
|
|
||||||
// This space intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
|
@ -11,7 +11,8 @@
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <wayland-cursor.h>
|
#include <wayland-cursor.h>
|
||||||
#include <wayland-egl.h>
|
#include <wayland-egl.h>
|
||||||
#include "egl_common.h"
|
#include <wlr/render/egl.h>
|
||||||
|
#include <wlr/util/log.h>
|
||||||
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
#include "wlr-layer-shell-unstable-v1-client-protocol.h"
|
||||||
#include "xdg-shell-client-protocol.h"
|
#include "xdg-shell-client-protocol.h"
|
||||||
|
|
||||||
|
@ -28,6 +29,7 @@ struct zwlr_layer_surface_v1 *layer_surface;
|
||||||
static struct wl_output *wl_output;
|
static struct wl_output *wl_output;
|
||||||
|
|
||||||
struct wl_surface *wl_surface;
|
struct wl_surface *wl_surface;
|
||||||
|
struct wlr_egl egl;
|
||||||
struct wl_egl_window *egl_window;
|
struct wl_egl_window *egl_window;
|
||||||
struct wlr_egl_surface *egl_surface;
|
struct wlr_egl_surface *egl_surface;
|
||||||
struct wl_callback *frame_callback;
|
struct wl_callback *frame_callback;
|
||||||
|
@ -48,8 +50,7 @@ static int32_t margin_top = 0;
|
||||||
static double alpha = 1.0;
|
static double alpha = 1.0;
|
||||||
static bool run_display = true;
|
static bool run_display = true;
|
||||||
static bool animate = false;
|
static bool animate = false;
|
||||||
static enum zwlr_layer_surface_v1_keyboard_interactivity keyboard_interactive =
|
static bool keyboard_interactive = false;
|
||||||
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
|
|
||||||
static double frame = 0;
|
static double frame = 0;
|
||||||
static int cur_x = -1, cur_y = -1;
|
static int cur_x = -1, cur_y = -1;
|
||||||
static int buttons = 0;
|
static int buttons = 0;
|
||||||
|
@ -92,7 +93,7 @@ static struct wl_callback_listener popup_frame_listener = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static void draw(void) {
|
static void draw(void) {
|
||||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
|
|
||||||
|
@ -141,7 +142,7 @@ static void draw(void) {
|
||||||
frame_callback = wl_surface_frame(wl_surface);
|
frame_callback = wl_surface_frame(wl_surface);
|
||||||
wl_callback_add_listener(frame_callback, &frame_listener, NULL);
|
wl_callback_add_listener(frame_callback, &frame_listener, NULL);
|
||||||
|
|
||||||
eglSwapBuffers(egl_display, egl_surface);
|
eglSwapBuffers(egl.display, egl_surface);
|
||||||
|
|
||||||
demo.last_frame = ts;
|
demo.last_frame = ts;
|
||||||
}
|
}
|
||||||
|
@ -149,7 +150,7 @@ static void draw(void) {
|
||||||
static void draw_popup(void) {
|
static void draw_popup(void) {
|
||||||
static float alpha_mod = -0.01;
|
static float alpha_mod = -0.01;
|
||||||
|
|
||||||
eglMakeCurrent(egl_display, popup_egl_surface, popup_egl_surface, egl_context);
|
eglMakeCurrent(egl.display, popup_egl_surface, popup_egl_surface, egl.context);
|
||||||
glViewport(0, 0, popup_width, popup_height);
|
glViewport(0, 0, popup_width, popup_height);
|
||||||
glClearColor(popup_red, 0.5f, 0.5f, popup_alpha);
|
glClearColor(popup_red, 0.5f, 0.5f, popup_alpha);
|
||||||
popup_alpha += alpha_mod;
|
popup_alpha += alpha_mod;
|
||||||
|
@ -161,7 +162,7 @@ static void draw_popup(void) {
|
||||||
popup_frame_callback = wl_surface_frame(popup_wl_surface);
|
popup_frame_callback = wl_surface_frame(popup_wl_surface);
|
||||||
assert(popup_frame_callback);
|
assert(popup_frame_callback);
|
||||||
wl_callback_add_listener(popup_frame_callback, &popup_frame_listener, NULL);
|
wl_callback_add_listener(popup_frame_callback, &popup_frame_listener, NULL);
|
||||||
eglSwapBuffers(egl_display, popup_egl_surface);
|
eglSwapBuffers(egl.display, popup_egl_surface);
|
||||||
wl_surface_commit(popup_wl_surface);
|
wl_surface_commit(popup_wl_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,7 +177,8 @@ static const struct xdg_surface_listener xdg_surface_listener = {
|
||||||
|
|
||||||
static void xdg_popup_configure(void *data, struct xdg_popup *xdg_popup,
|
static void xdg_popup_configure(void *data, struct xdg_popup *xdg_popup,
|
||||||
int32_t x, int32_t y, int32_t width, int32_t height) {
|
int32_t x, int32_t y, int32_t width, int32_t height) {
|
||||||
fprintf(stderr, "Popup configured %dx%d@%d,%d\n", width, height, x, y);
|
wlr_log(WLR_DEBUG, "Popup configured %dx%d@%d,%d",
|
||||||
|
width, height, x, y);
|
||||||
popup_width = width;
|
popup_width = width;
|
||||||
popup_height = height;
|
popup_height = height;
|
||||||
if (popup_egl_window) {
|
if (popup_egl_window) {
|
||||||
|
@ -185,7 +187,7 @@ static void xdg_popup_configure(void *data, struct xdg_popup *xdg_popup,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void popup_destroy(void) {
|
static void popup_destroy(void) {
|
||||||
eglDestroySurface(egl_display, popup_egl_surface);
|
wlr_egl_destroy_surface(&egl, popup_egl_surface);
|
||||||
wl_egl_window_destroy(popup_egl_window);
|
wl_egl_window_destroy(popup_egl_window);
|
||||||
xdg_popup_destroy(popup);
|
xdg_popup_destroy(popup);
|
||||||
wl_surface_destroy(popup_wl_surface);
|
wl_surface_destroy(popup_wl_surface);
|
||||||
|
@ -195,7 +197,7 @@ static void popup_destroy(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xdg_popup_done(void *data, struct xdg_popup *xdg_popup) {
|
static void xdg_popup_done(void *data, struct xdg_popup *xdg_popup) {
|
||||||
fprintf(stderr, "Popup done\n");
|
wlr_log(WLR_DEBUG, "Popup done");
|
||||||
popup_destroy();
|
popup_destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,9 +241,8 @@ static void create_popup(uint32_t serial) {
|
||||||
popup_wl_surface = surface;
|
popup_wl_surface = surface;
|
||||||
popup_egl_window = wl_egl_window_create(surface, popup_width, popup_height);
|
popup_egl_window = wl_egl_window_create(surface, popup_width, popup_height);
|
||||||
assert(popup_egl_window);
|
assert(popup_egl_window);
|
||||||
popup_egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
popup_egl_surface = wlr_egl_create_surface(&egl, popup_egl_window);
|
||||||
egl_display, egl_config, popup_egl_window, NULL);
|
assert(popup_egl_surface);
|
||||||
assert(popup_egl_surface != EGL_NO_SURFACE);
|
|
||||||
draw_popup();
|
draw_popup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +259,7 @@ static void layer_surface_configure(void *data,
|
||||||
|
|
||||||
static void layer_surface_closed(void *data,
|
static void layer_surface_closed(void *data,
|
||||||
struct zwlr_layer_surface_v1 *surface) {
|
struct zwlr_layer_surface_v1 *surface) {
|
||||||
eglDestroySurface(egl_display, egl_surface);
|
wlr_egl_destroy_surface(&egl, egl_surface);
|
||||||
wl_egl_window_destroy(egl_window);
|
wl_egl_window_destroy(egl_window);
|
||||||
zwlr_layer_surface_v1_destroy(surface);
|
zwlr_layer_surface_v1_destroy(surface);
|
||||||
wl_surface_destroy(wl_surface);
|
wl_surface_destroy(wl_surface);
|
||||||
|
@ -375,17 +376,17 @@ static void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
|
|
||||||
static void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard,
|
static void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
|
uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
|
||||||
fprintf(stderr, "Keyboard enter\n");
|
wlr_log(WLR_DEBUG, "Keyboard enter");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard,
|
static void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
uint32_t serial, struct wl_surface *surface) {
|
uint32_t serial, struct wl_surface *surface) {
|
||||||
fprintf(stderr, "Keyboard leave\n");
|
wlr_log(WLR_DEBUG, "Keyboard leave");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard,
|
static void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
|
uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
|
||||||
fprintf(stderr, "Key event: %d %d\n", key, state);
|
wlr_log(WLR_DEBUG, "Key event: %d %d", key, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
|
static void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
|
||||||
|
@ -452,8 +453,8 @@ static void handle_global(void *data, struct wl_registry *registry,
|
||||||
&wl_seat_interface, 1);
|
&wl_seat_interface, 1);
|
||||||
wl_seat_add_listener(seat, &seat_listener, NULL);
|
wl_seat_add_listener(seat, &seat_listener, NULL);
|
||||||
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
||||||
layer_shell = wl_registry_bind(registry, name,
|
layer_shell = wl_registry_bind(
|
||||||
&zwlr_layer_shell_v1_interface, version < 4 ? version : 4);
|
registry, name, &zwlr_layer_shell_v1_interface, 1);
|
||||||
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
||||||
xdg_wm_base = wl_registry_bind(
|
xdg_wm_base = wl_registry_bind(
|
||||||
registry, name, &xdg_wm_base_interface, 1);
|
registry, name, &xdg_wm_base_interface, 1);
|
||||||
|
@ -471,12 +472,13 @@ static const struct wl_registry_listener registry_listener = {
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
|
wlr_log_init(WLR_DEBUG, NULL);
|
||||||
char *namespace = "wlroots";
|
char *namespace = "wlroots";
|
||||||
int exclusive_zone = 0;
|
int exclusive_zone = 0;
|
||||||
int32_t margin_right = 0, margin_bottom = 0, margin_left = 0;
|
int32_t margin_right = 0, margin_bottom = 0, margin_left = 0;
|
||||||
bool found;
|
bool found;
|
||||||
int c;
|
int c;
|
||||||
while ((c = getopt(argc, argv, "k:nw:h:o:l:a:x:m:t:")) != -1) {
|
while ((c = getopt(argc, argv, "knw:h:o:l:a:x:m:t:")) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'o':
|
case 'o':
|
||||||
output = atoi(optarg);
|
output = atoi(optarg);
|
||||||
|
@ -556,29 +558,9 @@ int main(int argc, char **argv) {
|
||||||
case 'n':
|
case 'n':
|
||||||
animate = true;
|
animate = true;
|
||||||
break;
|
break;
|
||||||
case 'k': {
|
case 'k':
|
||||||
const struct {
|
keyboard_interactive = true;
|
||||||
const char *name;
|
|
||||||
enum zwlr_layer_surface_v1_keyboard_interactivity value;
|
|
||||||
} kb_int[] = {
|
|
||||||
{ "none", ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE },
|
|
||||||
{ "exclusive", ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE },
|
|
||||||
{ "on_demand", ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND }
|
|
||||||
};
|
|
||||||
found = false;
|
|
||||||
for (size_t i = 0; i < sizeof(kb_int) / sizeof(kb_int[0]); ++i) {
|
|
||||||
if (strcmp(optarg, kb_int[i].name) == 0) {
|
|
||||||
keyboard_interactive = kb_int[i].value;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
fprintf(stderr, "invalid keyboard interactivity setting %s\n", optarg);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -628,7 +610,9 @@ int main(int argc, char **argv) {
|
||||||
cursor_surface = wl_compositor_create_surface(compositor);
|
cursor_surface = wl_compositor_create_surface(compositor);
|
||||||
assert(cursor_surface);
|
assert(cursor_surface);
|
||||||
|
|
||||||
egl_init(display);
|
EGLint attribs[] = { EGL_ALPHA_SIZE, 8, EGL_NONE };
|
||||||
|
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display,
|
||||||
|
attribs, WL_SHM_FORMAT_ARGB8888);
|
||||||
|
|
||||||
wl_surface = wl_compositor_create_surface(compositor);
|
wl_surface = wl_compositor_create_surface(compositor);
|
||||||
assert(wl_surface);
|
assert(wl_surface);
|
||||||
|
@ -650,9 +634,8 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(wl_surface, width, height);
|
egl_window = wl_egl_window_create(wl_surface, width, height);
|
||||||
assert(egl_window);
|
assert(egl_window);
|
||||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
egl_surface = wlr_egl_create_surface(&egl, egl_window);
|
||||||
egl_display, egl_config, egl_window, NULL);
|
assert(egl_surface);
|
||||||
assert(egl_surface != EGL_NO_SURFACE);
|
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
draw();
|
draw();
|
||||||
|
|
|
@ -1,127 +1,89 @@
|
||||||
threads = dependency('threads')
|
threads = dependency('threads')
|
||||||
wayland_egl = dependency('wayland-egl')
|
|
||||||
wayland_cursor = dependency('wayland-cursor')
|
wayland_cursor = dependency('wayland-cursor')
|
||||||
wayland_client = dependency('wayland-client')
|
libpng = dependency('libpng', required: false)
|
||||||
libpng = dependency('libpng', required: false, disabler: true)
|
|
||||||
egl = dependency('egl', required: false, disabler: true)
|
|
||||||
glesv2 = dependency('glesv2', required: false, disabler: true)
|
|
||||||
# These versions correspond to ffmpeg 4.0
|
# These versions correspond to ffmpeg 4.0
|
||||||
libavutil = dependency('libavutil', version: '>=56.14.100', required: false, disabler: true)
|
libavutil = dependency('libavutil', version: '>=56.14.100', required: false)
|
||||||
libavcodec = dependency('libavcodec', version: '>=58.18.100', required: false, disabler: true)
|
libavcodec = dependency('libavcodec', version: '>=58.18.100', required: false)
|
||||||
libavformat = dependency('libavformat', version: '>=58.12.100', required: false, disabler: true)
|
libavformat = dependency('libavformat', version: '>=58.12.100', required: false)
|
||||||
# Only needed for drm_fourcc.h
|
|
||||||
libdrm = dependency('libdrm').partial_dependency(compile_args: true, includes: true)
|
|
||||||
|
|
||||||
# epoll is a separate library in FreeBSD
|
# epoll is a separate library in FreeBSD
|
||||||
if host_machine.system() == 'freebsd'
|
if host_machine.system() == 'freebsd'
|
||||||
libepoll = dependency('epoll-shim')
|
libepoll = [dependency('epoll-shim')]
|
||||||
else
|
else
|
||||||
libepoll = dependency('', required: false)
|
libepoll = []
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# Small hack until https://github.com/mesonbuild/meson/pull/3386/ is merged
|
||||||
|
foreach dep : ['libpng', 'libavutil', 'libavcodec', 'libavformat']
|
||||||
|
if not get_variable(dep).found()
|
||||||
|
set_variable(dep, disabler())
|
||||||
|
endif
|
||||||
|
endforeach
|
||||||
|
|
||||||
if not cc.has_header('libavutil/hwcontext_drm.h', dependencies: libavutil)
|
if not cc.has_header('libavutil/hwcontext_drm.h', dependencies: libavutil)
|
||||||
libavutil = disabler()
|
libavutil = disabler()
|
||||||
endif
|
endif
|
||||||
|
|
||||||
compositors = {
|
examples = {
|
||||||
'simple': {
|
'simple': {
|
||||||
'src': 'simple.c',
|
'src': 'simple.c',
|
||||||
|
'dep': [wlroots],
|
||||||
},
|
},
|
||||||
'pointer': {
|
'pointer': {
|
||||||
'src': 'pointer.c',
|
'src': 'pointer.c',
|
||||||
|
'dep': [wlroots],
|
||||||
},
|
},
|
||||||
'touch': {
|
'touch': {
|
||||||
'src': ['touch.c', 'cat.c'],
|
'src': ['touch.c', 'cat.c'],
|
||||||
|
'dep': [wlroots],
|
||||||
},
|
},
|
||||||
'tablet': {
|
'tablet': {
|
||||||
'src': 'tablet.c',
|
'src': 'tablet.c',
|
||||||
|
'dep': [wlroots],
|
||||||
},
|
},
|
||||||
'rotation': {
|
'rotation': {
|
||||||
'src': ['rotation.c', 'cat.c'],
|
'src': ['rotation.c', 'cat.c'],
|
||||||
|
'dep': [wlroots],
|
||||||
},
|
},
|
||||||
'multi-pointer': {
|
'multi-pointer': {
|
||||||
'src': 'multi-pointer.c',
|
'src': 'multi-pointer.c',
|
||||||
|
'dep': [wlroots],
|
||||||
},
|
},
|
||||||
'output-layout': {
|
'output-layout': {
|
||||||
'src': ['output-layout.c', 'cat.c'],
|
'src': ['output-layout.c', 'cat.c'],
|
||||||
|
'dep': [wlroots],
|
||||||
},
|
},
|
||||||
'fullscreen-shell': {
|
'screenshot': {
|
||||||
'src': 'fullscreen-shell.c',
|
'src': 'screenshot.c',
|
||||||
'proto': ['fullscreen-shell-unstable-v1'],
|
'dep': [wayland_client, wlr_protos, rt],
|
||||||
},
|
},
|
||||||
'quads': {
|
|
||||||
'src': 'quads.c',
|
|
||||||
},
|
|
||||||
'scene-graph': {
|
|
||||||
'src': 'scene-graph.c',
|
|
||||||
'proto': ['xdg-shell'],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
clients = {
|
|
||||||
'idle': {
|
'idle': {
|
||||||
'src': 'idle.c',
|
'src': 'idle.c',
|
||||||
'dep': threads,
|
'dep': [wayland_client, wlr_protos, threads],
|
||||||
'proto': ['kde-idle'],
|
|
||||||
},
|
},
|
||||||
'idle-inhibit': {
|
'idle-inhibit': {
|
||||||
'src': ['idle-inhibit.c', 'egl_common.c'],
|
'src': 'idle-inhibit.c',
|
||||||
'dep': [wayland_egl, egl, glesv2],
|
'dep': [wayland_client, wlr_protos, wlroots],
|
||||||
'proto': [
|
|
||||||
'idle-inhibit-unstable-v1',
|
|
||||||
'xdg-shell',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
'keyboard-shortcuts-inhibit': {
|
|
||||||
'src': ['keyboard-shortcuts-inhibit.c', 'egl_common.c'],
|
|
||||||
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
|
|
||||||
'proto': [
|
|
||||||
'keyboard-shortcuts-inhibit-unstable-v1',
|
|
||||||
'xdg-shell',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
'layer-shell': {
|
'layer-shell': {
|
||||||
'src': ['layer-shell.c', 'egl_common.c'],
|
'src': 'layer-shell.c',
|
||||||
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
|
'dep': [wayland_client, wayland_cursor, wlr_protos, wlroots],
|
||||||
'proto': [
|
|
||||||
'wlr-layer-shell-unstable-v1',
|
|
||||||
'xdg-shell',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
'input-inhibitor': {
|
'input-inhibitor': {
|
||||||
'src': ['input-inhibitor.c', 'egl_common.c'],
|
'src': 'input-inhibitor.c',
|
||||||
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
|
'dep': [wayland_client, wayland_cursor, wlr_protos, wlroots],
|
||||||
'proto': [
|
|
||||||
'wlr-input-inhibitor-unstable-v1',
|
|
||||||
'xdg-shell',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
'gamma-control': {
|
'gamma-control': {
|
||||||
'src': 'gamma-control.c',
|
'src': 'gamma-control.c',
|
||||||
'dep': [wayland_cursor, math],
|
'dep': [wayland_client, wayland_cursor, wlr_protos, math],
|
||||||
'proto': ['wlr-gamma-control-unstable-v1'],
|
|
||||||
},
|
|
||||||
'output-power-management': {
|
|
||||||
'src': 'output-power-management.c',
|
|
||||||
'dep': [wayland_client],
|
|
||||||
'proto': ['wlr-output-power-management-unstable-v1'],
|
|
||||||
},
|
},
|
||||||
'pointer-constraints': {
|
'pointer-constraints': {
|
||||||
'src': ['pointer-constraints.c', 'egl_common.c'],
|
'src': 'pointer-constraints.c',
|
||||||
'dep': [wayland_egl, egl, glesv2],
|
'dep': [wayland_client, wlr_protos, wlroots],
|
||||||
'proto': [
|
|
||||||
'pointer-constraints-unstable-v1',
|
|
||||||
'xdg-shell',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
'relative-pointer': {
|
'relative-pointer': {
|
||||||
'src': ['relative-pointer-unstable-v1.c', 'egl_common.c'],
|
'src': 'relative-pointer-unstable-v1.c',
|
||||||
'dep': [wayland_egl, egl, glesv2],
|
'dep': [wayland_client, wlr_protos, wlroots],
|
||||||
'proto': [
|
|
||||||
'pointer-constraints-unstable-v1',
|
|
||||||
'relative-pointer-unstable-v1',
|
|
||||||
'xdg-shell',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
'dmabuf-capture': {
|
'dmabuf-capture': {
|
||||||
'src': 'dmabuf-capture.c',
|
'src': 'dmabuf-capture.c',
|
||||||
|
@ -129,92 +91,51 @@ clients = {
|
||||||
libavcodec,
|
libavcodec,
|
||||||
libavformat,
|
libavformat,
|
||||||
libavutil,
|
libavutil,
|
||||||
drm,
|
drm.partial_dependency(compile_args: true), # <drm_fourcc.h>
|
||||||
threads,
|
threads,
|
||||||
|
wayland_client,
|
||||||
|
wlr_protos,
|
||||||
],
|
],
|
||||||
'proto': ['wlr-export-dmabuf-unstable-v1'],
|
|
||||||
},
|
},
|
||||||
'screencopy': {
|
'screencopy': {
|
||||||
'src': 'screencopy.c',
|
'src': 'screencopy.c',
|
||||||
'dep': [libpng, rt],
|
'dep': [libpng, wayland_client, wlr_protos, rt],
|
||||||
'proto': ['wlr-screencopy-unstable-v1'],
|
|
||||||
},
|
|
||||||
'screencopy-dmabuf': {
|
|
||||||
'src': 'screencopy-dmabuf.c',
|
|
||||||
'dep': [libpng, rt, gbm, drm],
|
|
||||||
'proto': [
|
|
||||||
'wlr-screencopy-unstable-v1',
|
|
||||||
'linux-dmabuf-unstable-v1',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
'toplevel-decoration': {
|
'toplevel-decoration': {
|
||||||
'src': ['toplevel-decoration.c', 'egl_common.c'],
|
'src': 'toplevel-decoration.c',
|
||||||
'dep': [wayland_egl, egl, glesv2],
|
'dep': [wayland_client, wlr_protos, wlroots],
|
||||||
'proto': [
|
|
||||||
'xdg-decoration-unstable-v1',
|
|
||||||
'xdg-shell',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
'input-method': {
|
'input-method': {
|
||||||
'src': 'input-method.c',
|
'src': 'input-method.c',
|
||||||
'dep': [wayland_egl, libepoll],
|
'dep': [wayland_client, wlr_protos] + libepoll,
|
||||||
'proto': [
|
|
||||||
'input-method-unstable-v2',
|
|
||||||
'text-input-unstable-v3',
|
|
||||||
'xdg-shell',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
'text-input': {
|
'text-input': {
|
||||||
'src': ['text-input.c', 'egl_common.c'],
|
'src': 'text-input.c',
|
||||||
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
|
'dep': [wayland_cursor, wayland_client, wlr_protos, wlroots],
|
||||||
'proto': [
|
|
||||||
'text-input-unstable-v3',
|
|
||||||
'xdg-shell',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
'foreign-toplevel': {
|
'foreign-toplevel': {
|
||||||
'src': 'foreign-toplevel.c',
|
'src': 'foreign-toplevel.c',
|
||||||
'proto': ['wlr-foreign-toplevel-management-unstable-v1'],
|
'dep': [wayland_client, wlr_protos, wlroots],
|
||||||
},
|
},
|
||||||
'virtual-pointer': {
|
'fullscreen-shell': {
|
||||||
'src': 'virtual-pointer.c',
|
'src': 'fullscreen-shell.c',
|
||||||
'proto': ['wlr-virtual-pointer-unstable-v1'],
|
'dep': [wlr_protos, wlroots],
|
||||||
},
|
|
||||||
'input-method-keyboard-grab': {
|
|
||||||
'src': 'input-method-keyboard-grab.c',
|
|
||||||
'dep': xkbcommon,
|
|
||||||
'proto': [
|
|
||||||
'input-method-unstable-v2',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach name, info : compositors
|
foreach name, info : examples
|
||||||
extra_src = []
|
all_dep_found = true
|
||||||
foreach p : info.get('proto', [])
|
foreach d : info.get('dep')
|
||||||
extra_src += protocols_server_header[p]
|
all_dep_found = all_dep_found and d.found()
|
||||||
endforeach
|
endforeach
|
||||||
|
if all_dep_found
|
||||||
executable(
|
executable(
|
||||||
name,
|
name,
|
||||||
[info.get('src'), extra_src],
|
info.get('src'),
|
||||||
dependencies: [wlroots, libdrm],
|
dependencies: info.get('dep'),
|
||||||
include_directories: [wlr_inc, proto_inc],
|
build_by_default: get_option('examples'),
|
||||||
build_by_default: get_option('examples'),
|
)
|
||||||
)
|
else
|
||||||
endforeach
|
warning('Dependencies not satisfied for ' + name)
|
||||||
|
endif
|
||||||
foreach name, info : clients
|
|
||||||
extra_src = []
|
|
||||||
foreach p : info.get('proto')
|
|
||||||
extra_src += protocols_code[p]
|
|
||||||
extra_src += protocols_client_header[p]
|
|
||||||
endforeach
|
|
||||||
|
|
||||||
executable(
|
|
||||||
name,
|
|
||||||
[info.get('src'), extra_src],
|
|
||||||
dependencies: [wayland_client, info.get('dep', [])],
|
|
||||||
build_by_default: get_option('examples'),
|
|
||||||
)
|
|
||||||
endforeach
|
endforeach
|
||||||
|
|
|
@ -1,21 +1,23 @@
|
||||||
#define _POSIX_C_SOURCE 200112L
|
#define _POSIX_C_SOURCE 200112L
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <GLES2/gl2.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-protocol.h>
|
||||||
|
#include <wayland-server.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/render/allocator.h>
|
#include <wlr/render/gles2.h>
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/types/wlr_cursor.h>
|
#include <wlr/types/wlr_cursor.h>
|
||||||
#include <wlr/types/wlr_keyboard.h>
|
#include <wlr/types/wlr_keyboard.h>
|
||||||
|
#include <wlr/types/wlr_list.h>
|
||||||
#include <wlr/types/wlr_matrix.h>
|
#include <wlr/types/wlr_matrix.h>
|
||||||
#include <wlr/types/wlr_output_layout.h>
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
#include <wlr/types/wlr_pointer.h>
|
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <wlr/xcursor.h>
|
#include <wlr/xcursor.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
@ -23,8 +25,6 @@
|
||||||
struct sample_state {
|
struct sample_state {
|
||||||
struct wl_display *display;
|
struct wl_display *display;
|
||||||
struct wlr_xcursor *xcursor;
|
struct wlr_xcursor *xcursor;
|
||||||
struct wlr_renderer *renderer;
|
|
||||||
struct wlr_allocator *allocator;
|
|
||||||
float default_color[4];
|
float default_color[4];
|
||||||
float clear_color[4];
|
float clear_color[4];
|
||||||
struct wlr_output_layout *layout;
|
struct wlr_output_layout *layout;
|
||||||
|
@ -69,10 +69,10 @@ struct sample_keyboard {
|
||||||
struct wl_listener destroy;
|
struct wl_listener destroy;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void configure_cursor(struct wlr_cursor *cursor, struct wlr_input_device *device,
|
void configure_cursor(struct wlr_cursor *cursor, struct wlr_input_device *device,
|
||||||
struct sample_state *sample) {
|
struct sample_state *sample) {
|
||||||
struct sample_output *output;
|
struct sample_output *output;
|
||||||
wlr_log(WLR_INFO, "Configuring cursor %p for device %p", cursor, device);
|
wlr_log(WLR_ERROR, "Configuring cursor %p for device %p", cursor, device);
|
||||||
|
|
||||||
// reset mappings
|
// reset mappings
|
||||||
wlr_cursor_map_to_output(cursor, NULL);
|
wlr_cursor_map_to_output(cursor, NULL);
|
||||||
|
@ -89,21 +89,19 @@ static void configure_cursor(struct wlr_cursor *cursor, struct wlr_input_device
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void output_frame_notify(struct wl_listener *listener, void *data) {
|
void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_output *output = wl_container_of(listener, output, frame);
|
struct sample_output *output = wl_container_of(listener, output, frame);
|
||||||
struct sample_state *sample = output->sample;
|
struct sample_state *sample = output->sample;
|
||||||
struct wlr_output *wlr_output = output->output;
|
struct wlr_output *wlr_output = output->output;
|
||||||
struct wlr_renderer *renderer = sample->renderer;
|
|
||||||
|
|
||||||
wlr_output_attach_render(wlr_output, NULL);
|
wlr_output_make_current(wlr_output, NULL);
|
||||||
|
|
||||||
wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
|
glClearColor(sample->clear_color[0], sample->clear_color[1],
|
||||||
|
sample->clear_color[2], sample->clear_color[3]);
|
||||||
wlr_renderer_clear(renderer, sample->clear_color);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
wlr_output_render_software_cursors(wlr_output, NULL);
|
wlr_output_render_software_cursors(wlr_output, NULL);
|
||||||
wlr_renderer_end(renderer);
|
wlr_output_swap_buffers(wlr_output, NULL, NULL);
|
||||||
wlr_output_commit(wlr_output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_cursor_motion(struct wl_listener *listener, void *data) {
|
static void handle_cursor_motion(struct wl_listener *listener, void *data) {
|
||||||
|
@ -130,7 +128,27 @@ static void cursor_destroy(struct sample_cursor *cursor) {
|
||||||
free(cursor);
|
free(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void output_remove_notify(struct wl_listener *listener, void *data) {
|
void input_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
|
struct wlr_input_device *device = data;
|
||||||
|
struct sample_cursor *sample_cursor = wl_container_of(listener, sample_cursor, destroy);
|
||||||
|
struct sample_state *sample = sample_cursor->sample;
|
||||||
|
struct sample_cursor *cursor;
|
||||||
|
wl_list_for_each(cursor, &sample->cursors, link) {
|
||||||
|
if (cursor->device == device) {
|
||||||
|
cursor_destroy(cursor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct sample_pointer *pointer;
|
||||||
|
wl_list_for_each(pointer, &sample->pointers, link) {
|
||||||
|
if (pointer->device == device) {
|
||||||
|
free(pointer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
||||||
struct sample_state *sample = sample_output->sample;
|
struct sample_state *sample = sample_output->sample;
|
||||||
wl_list_remove(&sample_output->frame.link);
|
wl_list_remove(&sample_output->frame.link);
|
||||||
|
@ -144,13 +162,14 @@ static void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void new_output_notify(struct wl_listener *listener, void *data) {
|
void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_output *output = data;
|
struct wlr_output *output = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
||||||
|
|
||||||
wlr_output_init_render(output, sample->allocator, sample->renderer);
|
|
||||||
|
|
||||||
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
|
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
|
||||||
|
if (!wl_list_empty(&output->modes)) {
|
||||||
|
struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link);
|
||||||
|
wlr_output_set_mode(output, mode);
|
||||||
|
}
|
||||||
sample_output->output = output;
|
sample_output->output = output;
|
||||||
sample_output->sample = sample;
|
sample_output->sample = sample;
|
||||||
wl_signal_add(&output->events.frame, &sample_output->frame);
|
wl_signal_add(&output->events.frame, &sample_output->frame);
|
||||||
|
@ -160,6 +179,7 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
|
|
||||||
wlr_output_layout_add_auto(sample->layout, output);
|
wlr_output_layout_add_auto(sample->layout, output);
|
||||||
|
|
||||||
|
|
||||||
struct sample_cursor *cursor;
|
struct sample_cursor *cursor;
|
||||||
wl_list_for_each(cursor, &sample->cursors, link) {
|
wl_list_for_each(cursor, &sample->cursors, link) {
|
||||||
configure_cursor(cursor->cursor, cursor->device, sample);
|
configure_cursor(cursor->cursor, cursor->device, sample);
|
||||||
|
@ -172,16 +192,9 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
cursor->cursor->y);
|
cursor->cursor->y);
|
||||||
}
|
}
|
||||||
wl_list_insert(&sample->outputs, &sample_output->link);
|
wl_list_insert(&sample->outputs, &sample_output->link);
|
||||||
|
|
||||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
|
|
||||||
if (mode != NULL) {
|
|
||||||
wlr_output_set_mode(output, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_output_commit(output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
||||||
struct sample_state *sample = keyboard->sample;
|
struct sample_state *sample = keyboard->sample;
|
||||||
struct wlr_event_keyboard_key *event = data;
|
struct wlr_event_keyboard_key *event = data;
|
||||||
|
@ -197,14 +210,14 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
||||||
wl_list_remove(&keyboard->destroy.link);
|
wl_list_remove(&keyboard->destroy.link);
|
||||||
wl_list_remove(&keyboard->key.link);
|
wl_list_remove(&keyboard->key.link);
|
||||||
free(keyboard);
|
free(keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void new_input_notify(struct wl_listener *listener, void *data) {
|
void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_input_device *device = data;
|
struct wlr_input_device *device = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
||||||
switch (device->type) {
|
switch (device->type) {
|
||||||
|
@ -216,12 +229,18 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
keyboard->destroy.notify = keyboard_destroy_notify;
|
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||||
keyboard->key.notify = keyboard_key_notify;
|
keyboard->key.notify = keyboard_key_notify;
|
||||||
|
struct xkb_rule_names rules = { 0 };
|
||||||
|
rules.rules = getenv("XKB_DEFAULT_RULES");
|
||||||
|
rules.model = getenv("XKB_DEFAULT_MODEL");
|
||||||
|
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
|
||||||
|
rules.variant = getenv("XKB_DEFAULT_VARIANT");
|
||||||
|
rules.options = getenv("XKB_DEFAULT_OPTIONS");
|
||||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
|
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
|
||||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
if (!keymap) {
|
if (!keymap) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||||
|
@ -271,14 +290,10 @@ int main(int argc, char *argv[]) {
|
||||||
.clear_color = { 0.25f, 0.25f, 0.25f, 1 },
|
.clear_color = { 0.25f, 0.25f, 0.25f, 1 },
|
||||||
.display = display,
|
.display = display,
|
||||||
};
|
};
|
||||||
struct wlr_backend *wlr = wlr_backend_autocreate(display);
|
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
|
||||||
if (!wlr) {
|
if (!wlr) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.renderer = wlr_renderer_autocreate(wlr);
|
|
||||||
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
|
|
||||||
|
|
||||||
wl_list_init(&state.cursors);
|
wl_list_init(&state.cursors);
|
||||||
wl_list_init(&state.pointers);
|
wl_list_init(&state.pointers);
|
||||||
wl_list_init(&state.outputs);
|
wl_list_init(&state.outputs);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#define _POSIX_C_SOURCE 200112L
|
#define _POSIX_C_SOURCE 200112L
|
||||||
#include <drm_fourcc.h>
|
#include <GLES2/gl2.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -8,17 +8,16 @@
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-protocol.h>
|
||||||
|
#include <wayland-server.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/render/allocator.h>
|
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/types/wlr_keyboard.h>
|
#include <wlr/types/wlr_keyboard.h>
|
||||||
#include <wlr/types/wlr_matrix.h>
|
#include <wlr/types/wlr_matrix.h>
|
||||||
#include <wlr/types/wlr_input_device.h>
|
#include <wlr/types/wlr_input_device.h>
|
||||||
#include <wlr/types/wlr_output_layout.h>
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
#include <wlr/types/wlr_output.h>
|
#include <wlr/types/wlr_output.h>
|
||||||
#include <wlr/util/box.h>
|
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
#include "cat.h"
|
#include "cat.h"
|
||||||
|
@ -28,7 +27,6 @@ struct sample_state {
|
||||||
struct wl_listener new_output;
|
struct wl_listener new_output;
|
||||||
struct wl_listener new_input;
|
struct wl_listener new_input;
|
||||||
struct wlr_renderer *renderer;
|
struct wlr_renderer *renderer;
|
||||||
struct wlr_allocator *allocator;
|
|
||||||
struct wlr_texture *cat_texture;
|
struct wlr_texture *cat_texture;
|
||||||
struct wlr_output_layout *layout;
|
struct wlr_output_layout *layout;
|
||||||
float x_offs, y_offs;
|
float x_offs, y_offs;
|
||||||
|
@ -109,7 +107,7 @@ static void animate_cat(struct sample_state *sample,
|
||||||
sample->ts_last = ts;
|
sample->ts_last = ts;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void output_frame_notify(struct wl_listener *listener, void *data) {
|
void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_output *output = wl_container_of(listener, output, frame);
|
struct sample_output *output = wl_container_of(listener, output, frame);
|
||||||
struct sample_state *sample = output->sample;
|
struct sample_state *sample = output->sample;
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
|
@ -117,7 +115,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
|
|
||||||
struct wlr_output *wlr_output = output->output;
|
struct wlr_output *wlr_output = output->output;
|
||||||
|
|
||||||
wlr_output_attach_render(wlr_output, NULL);
|
wlr_output_make_current(wlr_output, NULL);
|
||||||
wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height);
|
wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height);
|
||||||
wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1});
|
wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1});
|
||||||
|
|
||||||
|
@ -139,7 +137,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_renderer_end(sample->renderer);
|
wlr_renderer_end(sample->renderer);
|
||||||
wlr_output_commit(wlr_output);
|
wlr_output_swap_buffers(wlr_output, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_velocities(struct sample_state *sample,
|
static void update_velocities(struct sample_state *sample,
|
||||||
|
@ -148,7 +146,7 @@ static void update_velocities(struct sample_state *sample,
|
||||||
sample->y_vel += y_diff;
|
sample->y_vel += y_diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void output_remove_notify(struct wl_listener *listener, void *data) {
|
void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
||||||
struct sample_state *sample = sample_output->sample;
|
struct sample_state *sample = sample_output->sample;
|
||||||
wlr_output_layout_remove(sample->layout, sample_output->output);
|
wlr_output_layout_remove(sample->layout, sample_output->output);
|
||||||
|
@ -157,13 +155,14 @@ static void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
free(sample_output);
|
free(sample_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void new_output_notify(struct wl_listener *listener, void *data) {
|
void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_output *output = data;
|
struct wlr_output *output = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
||||||
|
|
||||||
wlr_output_init_render(output, sample->allocator, sample->renderer);
|
|
||||||
|
|
||||||
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
|
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
|
||||||
|
if (!wl_list_empty(&output->modes)) {
|
||||||
|
struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link);
|
||||||
|
wlr_output_set_mode(output, mode);
|
||||||
|
}
|
||||||
wlr_output_layout_add_auto(sample->layout, output);
|
wlr_output_layout_add_auto(sample->layout, output);
|
||||||
sample_output->output = output;
|
sample_output->output = output;
|
||||||
sample_output->sample = sample;
|
sample_output->sample = sample;
|
||||||
|
@ -171,16 +170,9 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
sample_output->frame.notify = output_frame_notify;
|
sample_output->frame.notify = output_frame_notify;
|
||||||
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
||||||
sample_output->destroy.notify = output_remove_notify;
|
sample_output->destroy.notify = output_remove_notify;
|
||||||
|
|
||||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
|
|
||||||
if (mode != NULL) {
|
|
||||||
wlr_output_set_mode(output, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_output_commit(output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
||||||
struct sample_state *sample = keyboard->sample;
|
struct sample_state *sample = keyboard->sample;
|
||||||
struct wlr_event_keyboard_key *event = data;
|
struct wlr_event_keyboard_key *event = data;
|
||||||
|
@ -197,7 +189,7 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
// and make this change in pixels/sec^2
|
// and make this change in pixels/sec^2
|
||||||
// Also, key repeat
|
// Also, key repeat
|
||||||
int delta = 75;
|
int delta = 75;
|
||||||
if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
if (event->state == WLR_KEY_PRESSED) {
|
||||||
switch (sym) {
|
switch (sym) {
|
||||||
case XKB_KEY_Left:
|
case XKB_KEY_Left:
|
||||||
update_velocities(sample, -delta, 0);
|
update_velocities(sample, -delta, 0);
|
||||||
|
@ -216,14 +208,14 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
||||||
wl_list_remove(&keyboard->destroy.link);
|
wl_list_remove(&keyboard->destroy.link);
|
||||||
wl_list_remove(&keyboard->key.link);
|
wl_list_remove(&keyboard->key.link);
|
||||||
free(keyboard);
|
free(keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void new_input_notify(struct wl_listener *listener, void *data) {
|
void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_input_device *device = data;
|
struct wlr_input_device *device = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
||||||
switch (device->type) {
|
switch (device->type) {
|
||||||
|
@ -235,12 +227,18 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
keyboard->destroy.notify = keyboard_destroy_notify;
|
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||||
keyboard->key.notify = keyboard_key_notify;
|
keyboard->key.notify = keyboard_key_notify;
|
||||||
|
struct xkb_rule_names rules = { 0 };
|
||||||
|
rules.rules = getenv("XKB_DEFAULT_RULES");
|
||||||
|
rules.model = getenv("XKB_DEFAULT_MODEL");
|
||||||
|
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
|
||||||
|
rules.variant = getenv("XKB_DEFAULT_VARIANT");
|
||||||
|
rules.options = getenv("XKB_DEFAULT_OPTIONS");
|
||||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
|
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
|
||||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
if (!keymap) {
|
if (!keymap) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||||
|
@ -268,7 +266,7 @@ int main(int argc, char *argv[]) {
|
||||||
state.layout = wlr_output_layout_create();
|
state.layout = wlr_output_layout_create();
|
||||||
clock_gettime(CLOCK_MONOTONIC, &state.ts_last);
|
clock_gettime(CLOCK_MONOTONIC, &state.ts_last);
|
||||||
|
|
||||||
struct wlr_backend *wlr = wlr_backend_autocreate(display);
|
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
|
||||||
if (!wlr) {
|
if (!wlr) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
@ -278,13 +276,11 @@ int main(int argc, char *argv[]) {
|
||||||
wl_signal_add(&wlr->events.new_input, &state.new_input);
|
wl_signal_add(&wlr->events.new_input, &state.new_input);
|
||||||
state.new_input.notify = new_input_notify;
|
state.new_input.notify = new_input_notify;
|
||||||
|
|
||||||
state.renderer = wlr_renderer_autocreate(wlr);
|
state.renderer = wlr_backend_get_renderer(wlr);
|
||||||
state.cat_texture = wlr_texture_from_pixels(state.renderer,
|
state.cat_texture = wlr_texture_from_pixels(state.renderer,
|
||||||
DRM_FORMAT_ABGR8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
|
WL_SHM_FORMAT_ABGR8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
|
||||||
cat_tex.pixel_data);
|
cat_tex.pixel_data);
|
||||||
|
|
||||||
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
|
|
||||||
|
|
||||||
if (!wlr_backend_start(wlr)) {
|
if (!wlr_backend_start(wlr)) {
|
||||||
wlr_log(WLR_ERROR, "Failed to start backend");
|
wlr_log(WLR_ERROR, "Failed to start backend");
|
||||||
wlr_backend_destroy(wlr);
|
wlr_backend_destroy(wlr);
|
||||||
|
|
|
@ -1,144 +0,0 @@
|
||||||
#define _POSIX_C_SOURCE 200809L
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <wayland-client-protocol.h>
|
|
||||||
#include <wayland-client.h>
|
|
||||||
#include "wlr-output-power-management-unstable-v1-client-protocol.h"
|
|
||||||
|
|
||||||
struct output {
|
|
||||||
struct wl_output *wl_output;
|
|
||||||
struct zwlr_output_power_v1 *output_power;
|
|
||||||
struct wl_list link;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct wl_list outputs;
|
|
||||||
static struct zwlr_output_power_manager_v1 *output_power_manager = NULL;
|
|
||||||
|
|
||||||
static void output_power_handle_mode(void *data,
|
|
||||||
struct zwlr_output_power_v1 *output_power,
|
|
||||||
enum zwlr_output_power_v1_mode mode) {
|
|
||||||
struct output *output = data;
|
|
||||||
|
|
||||||
switch (mode) {
|
|
||||||
case ZWLR_OUTPUT_POWER_V1_MODE_OFF:
|
|
||||||
printf("Output %p disabled\n", output);
|
|
||||||
break;
|
|
||||||
case ZWLR_OUTPUT_POWER_V1_MODE_ON:
|
|
||||||
printf("Output %p enabled\n", output);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void output_power_handle_failed(void *data,
|
|
||||||
struct zwlr_output_power_v1 *output_power) {
|
|
||||||
fprintf(stderr, "failed to set output power mode\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwlr_output_power_v1_listener output_power_listener = {
|
|
||||||
.mode = output_power_handle_mode,
|
|
||||||
.failed = output_power_handle_failed,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void registry_handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
if (strcmp(interface, wl_output_interface.name) == 0) {
|
|
||||||
struct output *output = calloc(1, sizeof(struct output));
|
|
||||||
output->wl_output = wl_registry_bind(registry, name,
|
|
||||||
&wl_output_interface, 1);
|
|
||||||
wl_list_insert(&outputs, &output->link);
|
|
||||||
} else if (strcmp(interface,
|
|
||||||
zwlr_output_power_manager_v1_interface.name) == 0) {
|
|
||||||
output_power_manager = wl_registry_bind(registry, name,
|
|
||||||
&zwlr_output_power_manager_v1_interface, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void registry_handle_global_remove(void *data,
|
|
||||||
struct wl_registry *registry, uint32_t name) {
|
|
||||||
// Who cares?
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
.global = registry_handle_global,
|
|
||||||
.global_remove = registry_handle_global_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char usage[] = "usage: output-power-management [options...]\n"
|
|
||||||
" -h: show this help message\n"
|
|
||||||
" -e: turn outputs on\n"
|
|
||||||
" -d: turn outputs off\n"
|
|
||||||
" -w: continuously watch for power mode changes\n";
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
wl_list_init(&outputs);
|
|
||||||
|
|
||||||
int opt;
|
|
||||||
enum zwlr_output_power_v1_mode mode =
|
|
||||||
ZWLR_OUTPUT_POWER_V1_MODE_ON;
|
|
||||||
bool watch_mode = false;
|
|
||||||
while ((opt = getopt(argc, argv, "edhw")) != -1) {
|
|
||||||
switch (opt) {
|
|
||||||
case 'e':
|
|
||||||
mode = ZWLR_OUTPUT_POWER_V1_MODE_ON;
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
mode = ZWLR_OUTPUT_POWER_V1_MODE_OFF;
|
|
||||||
break;
|
|
||||||
case 'w':
|
|
||||||
watch_mode = true;
|
|
||||||
break;
|
|
||||||
case 'h':
|
|
||||||
default:
|
|
||||||
fprintf(stderr, usage);
|
|
||||||
return opt == 'h' ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_display *display = wl_display_connect(NULL);
|
|
||||||
if (display == NULL) {
|
|
||||||
fprintf(stderr, "failed to create display\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
if (output_power_manager == NULL) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"compositor doesn't support wlr-output-power-management-unstable-v1\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct output *output;
|
|
||||||
wl_list_for_each(output, &outputs, link) {
|
|
||||||
output->output_power = zwlr_output_power_manager_v1_get_output_power(
|
|
||||||
output_power_manager, output->wl_output);
|
|
||||||
zwlr_output_power_v1_add_listener(output->output_power,
|
|
||||||
&output_power_listener, output);
|
|
||||||
}
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
wl_list_for_each(output, &outputs, link) {
|
|
||||||
zwlr_output_power_v1_set_mode(output->output_power,
|
|
||||||
mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!watch_mode) {
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (wl_display_dispatch(display) != -1) {
|
|
||||||
// nothing to see here, please move along
|
|
||||||
}
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <wayland-egl.h>
|
#include <wayland-egl.h>
|
||||||
#include "egl_common.h"
|
#include <wlr/render/egl.h>
|
||||||
#include "xdg-shell-client-protocol.h"
|
#include "xdg-shell-client-protocol.h"
|
||||||
#include "pointer-constraints-unstable-v1-client-protocol.h"
|
#include "pointer-constraints-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ static struct wl_seat *seat = NULL;
|
||||||
static struct xdg_wm_base *wm_base = NULL;
|
static struct xdg_wm_base *wm_base = NULL;
|
||||||
static struct zwp_pointer_constraints_v1 *pointer_constraints = NULL;
|
static struct zwp_pointer_constraints_v1 *pointer_constraints = NULL;
|
||||||
|
|
||||||
|
struct wlr_egl egl;
|
||||||
struct wl_egl_window *egl_window;
|
struct wl_egl_window *egl_window;
|
||||||
struct wlr_egl_surface *egl_surface;
|
struct wlr_egl_surface *egl_surface;
|
||||||
struct zwp_locked_pointer_v1* locked_pointer;
|
struct zwp_locked_pointer_v1* locked_pointer;
|
||||||
|
@ -31,7 +32,7 @@ enum {
|
||||||
struct wl_region *regions[3];
|
struct wl_region *regions[3];
|
||||||
|
|
||||||
static void draw(void) {
|
static void draw(void) {
|
||||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
|
||||||
|
|
||||||
float color[] = {1.0, 1.0, 0.0, 1.0};
|
float color[] = {1.0, 1.0, 0.0, 1.0};
|
||||||
|
|
||||||
|
@ -39,7 +40,7 @@ static void draw(void) {
|
||||||
glClearColor(color[0], color[1], color[2], 1.0);
|
glClearColor(color[0], color[1], color[2], 1.0);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
eglSwapBuffers(egl_display, egl_surface);
|
eglSwapBuffers(egl.display, egl_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pointer_handle_button(void *data, struct wl_pointer *pointer,
|
static void pointer_handle_button(void *data, struct wl_pointer *pointer,
|
||||||
|
@ -197,6 +198,7 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
|
wl_display_dispatch(display);
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
struct wl_region *disjoint_region = wl_compositor_create_region(compositor);
|
struct wl_region *disjoint_region = wl_compositor_create_region(compositor);
|
||||||
|
@ -210,7 +212,8 @@ int main(int argc, char **argv) {
|
||||||
wl_region_add(joint_region, 256, 256, 256, 256);
|
wl_region_add(joint_region, 256, 256, 256, 256);
|
||||||
regions[REGION_TYPE_JOINT] = joint_region;
|
regions[REGION_TYPE_JOINT] = joint_region;
|
||||||
|
|
||||||
egl_init(display);
|
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
|
||||||
|
WL_SHM_FORMAT_ARGB8888);
|
||||||
|
|
||||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
||||||
struct xdg_surface *xdg_surface =
|
struct xdg_surface *xdg_surface =
|
||||||
|
@ -239,8 +242,7 @@ int main(int argc, char **argv) {
|
||||||
wl_surface_commit(surface);
|
wl_surface_commit(surface);
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(surface, width, height);
|
egl_window = wl_egl_window_create(surface, width, height);
|
||||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
egl_surface = wlr_egl_create_surface(&egl, egl_window);
|
||||||
egl_display, egl_config, egl_window, NULL);
|
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
|
|
@ -6,18 +6,17 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-protocol.h>
|
||||||
|
#include <wayland-server.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/render/allocator.h>
|
#include <wlr/render/gles2.h>
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/types/wlr_cursor.h>
|
#include <wlr/types/wlr_cursor.h>
|
||||||
#include <wlr/types/wlr_keyboard.h>
|
#include <wlr/types/wlr_keyboard.h>
|
||||||
|
#include <wlr/types/wlr_list.h>
|
||||||
#include <wlr/types/wlr_matrix.h>
|
#include <wlr/types/wlr_matrix.h>
|
||||||
#include <wlr/types/wlr_output_layout.h>
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
#include <wlr/types/wlr_pointer.h>
|
|
||||||
#include <wlr/types/wlr_tablet_tool.h>
|
|
||||||
#include <wlr/types/wlr_touch.h>
|
|
||||||
#include <wlr/types/wlr_xcursor_manager.h>
|
#include <wlr/types/wlr_xcursor_manager.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
@ -25,8 +24,6 @@
|
||||||
struct sample_state {
|
struct sample_state {
|
||||||
struct wl_display *display;
|
struct wl_display *display;
|
||||||
struct compositor_state *compositor;
|
struct compositor_state *compositor;
|
||||||
struct wlr_renderer *renderer;
|
|
||||||
struct wlr_allocator *allocator;
|
|
||||||
struct wlr_xcursor_manager *xcursor_manager;
|
struct wlr_xcursor_manager *xcursor_manager;
|
||||||
struct wlr_cursor *cursor;
|
struct wlr_cursor *cursor;
|
||||||
double cur_x, cur_y;
|
double cur_x, cur_y;
|
||||||
|
@ -94,19 +91,19 @@ static void warp_to_touch(struct sample_state *state,
|
||||||
wlr_cursor_warp_absolute(state->cursor, dev, x, y);
|
wlr_cursor_warp_absolute(state->cursor, dev, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void output_frame_notify(struct wl_listener *listener, void *data) {
|
void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_output *sample_output = wl_container_of(listener, sample_output, frame);
|
struct sample_output *sample_output = wl_container_of(listener, sample_output, frame);
|
||||||
struct sample_state *state = sample_output->state;
|
struct sample_state *state = sample_output->state;
|
||||||
struct wlr_output *wlr_output = sample_output->output;
|
struct wlr_output *wlr_output = sample_output->output;
|
||||||
struct wlr_renderer *renderer = state->renderer;
|
struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend);
|
||||||
assert(renderer);
|
assert(renderer);
|
||||||
|
|
||||||
wlr_output_attach_render(wlr_output, NULL);
|
wlr_output_make_current(wlr_output, NULL);
|
||||||
wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
|
wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
|
||||||
wlr_renderer_clear(renderer, state->clear_color);
|
wlr_renderer_clear(renderer, state->clear_color);
|
||||||
wlr_output_render_software_cursors(wlr_output, NULL);
|
wlr_output_render_software_cursors(wlr_output, NULL);
|
||||||
|
wlr_output_swap_buffers(wlr_output, NULL, NULL);
|
||||||
wlr_renderer_end(renderer);
|
wlr_renderer_end(renderer);
|
||||||
wlr_output_commit(wlr_output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_cursor_motion(struct wl_listener *listener, void *data) {
|
static void handle_cursor_motion(struct wl_listener *listener, void *data) {
|
||||||
|
@ -225,7 +222,7 @@ static void handle_tablet_tool_axis(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
||||||
struct sample_state *sample = keyboard->state;
|
struct sample_state *sample = keyboard->state;
|
||||||
struct wlr_event_keyboard_key *event = data;
|
struct wlr_event_keyboard_key *event = data;
|
||||||
|
@ -241,7 +238,7 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void output_remove_notify(struct wl_listener *listener, void *data) {
|
void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
||||||
struct sample_state *sample = sample_output->state;
|
struct sample_state *sample = sample_output->state;
|
||||||
wlr_output_layout_remove(sample->layout, sample_output->output);
|
wlr_output_layout_remove(sample->layout, sample_output->output);
|
||||||
|
@ -250,13 +247,14 @@ static void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
free(sample_output);
|
free(sample_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void new_output_notify(struct wl_listener *listener, void *data) {
|
void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_output *output = data;
|
struct wlr_output *output = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
||||||
|
|
||||||
wlr_output_init_render(output, sample->allocator, sample->renderer);
|
|
||||||
|
|
||||||
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
|
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
|
||||||
|
if (!wl_list_empty(&output->modes)) {
|
||||||
|
struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link);
|
||||||
|
wlr_output_set_mode(output, mode);
|
||||||
|
}
|
||||||
sample_output->output = output;
|
sample_output->output = output;
|
||||||
sample_output->state = sample;
|
sample_output->state = sample;
|
||||||
wl_signal_add(&output->events.frame, &sample_output->frame);
|
wl_signal_add(&output->events.frame, &sample_output->frame);
|
||||||
|
@ -268,24 +266,17 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
wlr_xcursor_manager_load(sample->xcursor_manager, output->scale);
|
wlr_xcursor_manager_load(sample->xcursor_manager, output->scale);
|
||||||
wlr_xcursor_manager_set_cursor_image(sample->xcursor_manager, "left_ptr",
|
wlr_xcursor_manager_set_cursor_image(sample->xcursor_manager, "left_ptr",
|
||||||
sample->cursor);
|
sample->cursor);
|
||||||
|
|
||||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
|
|
||||||
if (mode != NULL) {
|
|
||||||
wlr_output_set_mode(output, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_output_commit(output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
||||||
wl_list_remove(&keyboard->destroy.link);
|
wl_list_remove(&keyboard->destroy.link);
|
||||||
wl_list_remove(&keyboard->key.link);
|
wl_list_remove(&keyboard->key.link);
|
||||||
free(keyboard);
|
free(keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void new_input_notify(struct wl_listener *listener, void *data) {
|
void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_input_device *device = data;
|
struct wlr_input_device *device = data;
|
||||||
struct sample_state *state = wl_container_of(listener, state, new_input);
|
struct sample_state *state = wl_container_of(listener, state, new_input);
|
||||||
switch (device->type) {
|
switch (device->type) {
|
||||||
|
@ -303,12 +294,18 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
keyboard->destroy.notify = keyboard_destroy_notify;
|
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||||
keyboard->key.notify = keyboard_key_notify;
|
keyboard->key.notify = keyboard_key_notify;
|
||||||
|
struct xkb_rule_names rules = { 0 };
|
||||||
|
rules.rules = getenv("XKB_DEFAULT_RULES");
|
||||||
|
rules.model = getenv("XKB_DEFAULT_MODEL");
|
||||||
|
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
|
||||||
|
rules.variant = getenv("XKB_DEFAULT_VARIANT");
|
||||||
|
rules.options = getenv("XKB_DEFAULT_OPTIONS");
|
||||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
|
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
|
||||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
if (!keymap) {
|
if (!keymap) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||||
|
@ -333,14 +330,10 @@ int main(int argc, char *argv[]) {
|
||||||
.display = display
|
.display = display
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_backend *wlr = wlr_backend_autocreate(display);
|
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
|
||||||
if (!wlr) {
|
if (!wlr) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.renderer = wlr_renderer_autocreate(wlr);
|
|
||||||
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
|
|
||||||
|
|
||||||
state.cursor = wlr_cursor_create();
|
state.cursor = wlr_cursor_create();
|
||||||
state.layout = wlr_output_layout_create();
|
state.layout = wlr_output_layout_create();
|
||||||
wlr_cursor_attach_output_layout(state.cursor, state.layout);
|
wlr_cursor_attach_output_layout(state.cursor, state.layout);
|
||||||
|
|
220
examples/quads.c
220
examples/quads.c
|
@ -1,220 +0,0 @@
|
||||||
#define _XOPEN_SOURCE 600
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <strings.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <wayland-server-core.h>
|
|
||||||
#include <wlr/backend.h>
|
|
||||||
#include <wlr/render/allocator.h>
|
|
||||||
#include <wlr/render/wlr_renderer.h>
|
|
||||||
#include <wlr/types/wlr_keyboard.h>
|
|
||||||
#include <wlr/types/wlr_output.h>
|
|
||||||
#include <wlr/types/wlr_input_device.h>
|
|
||||||
#include <wlr/types/wlr_matrix.h>
|
|
||||||
#include <wlr/util/box.h>
|
|
||||||
#include <wlr/util/log.h>
|
|
||||||
#include <xkbcommon/xkbcommon.h>
|
|
||||||
|
|
||||||
struct sample_state {
|
|
||||||
struct wl_display *display;
|
|
||||||
struct wl_listener new_output;
|
|
||||||
struct wl_listener new_input;
|
|
||||||
struct timespec last_frame;
|
|
||||||
struct wlr_renderer *renderer;
|
|
||||||
struct wlr_allocator *allocator;
|
|
||||||
struct wl_list outputs;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sample_output {
|
|
||||||
struct sample_state *sample;
|
|
||||||
struct wlr_output *output;
|
|
||||||
struct wl_listener frame;
|
|
||||||
struct wl_listener destroy;
|
|
||||||
struct wl_list link;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sample_keyboard {
|
|
||||||
struct sample_state *sample;
|
|
||||||
struct wlr_input_device *device;
|
|
||||||
struct wl_listener key;
|
|
||||||
struct wl_listener destroy;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void output_frame_notify(struct wl_listener *listener, void *data) {
|
|
||||||
struct sample_output *sample_output = wl_container_of(listener, sample_output, frame);
|
|
||||||
struct sample_state *sample = sample_output->sample;
|
|
||||||
struct wlr_output *wlr_output = sample_output->output;
|
|
||||||
struct timespec now;
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
||||||
|
|
||||||
int32_t width, height;
|
|
||||||
wlr_output_effective_resolution(wlr_output, &width, &height);
|
|
||||||
|
|
||||||
wlr_output_attach_render(wlr_output, NULL);
|
|
||||||
wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height);
|
|
||||||
wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1});
|
|
||||||
|
|
||||||
static float rotation = 0.f;
|
|
||||||
for (int y = -128; y < height; y += 128) {
|
|
||||||
for (int x = -128; x < width; x += 128) {
|
|
||||||
struct wlr_box box = {
|
|
||||||
.x = x,
|
|
||||||
.y = y,
|
|
||||||
.width = 128,
|
|
||||||
.height = 128,
|
|
||||||
};
|
|
||||||
|
|
||||||
float color[] = {
|
|
||||||
255.f / x,
|
|
||||||
255.f / y,
|
|
||||||
255.f / (x + y),
|
|
||||||
1.f
|
|
||||||
};
|
|
||||||
|
|
||||||
float matrix[9];
|
|
||||||
wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL,
|
|
||||||
rotation, wlr_output->transform_matrix);
|
|
||||||
|
|
||||||
wlr_render_quad_with_matrix(sample->renderer, color, matrix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_renderer_end(sample->renderer);
|
|
||||||
wlr_output_commit(wlr_output);
|
|
||||||
|
|
||||||
//TODO rotate with a delta time
|
|
||||||
rotation += 0.05;
|
|
||||||
if (rotation > 2 * M_PI) {
|
|
||||||
rotation = 0.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
sample->last_frame = now;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void output_remove_notify(struct wl_listener *listener, void *data) {
|
|
||||||
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
|
||||||
wl_list_remove(&sample_output->frame.link);
|
|
||||||
wl_list_remove(&sample_output->destroy.link);
|
|
||||||
free(sample_output);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void new_output_notify(struct wl_listener *listener, void *data) {
|
|
||||||
struct wlr_output *output = data;
|
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
|
||||||
|
|
||||||
wlr_output_init_render(output, sample->allocator, sample->renderer);
|
|
||||||
|
|
||||||
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
|
|
||||||
|
|
||||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
|
|
||||||
if (mode != NULL) {
|
|
||||||
wlr_output_set_mode(output, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
sample_output->output = output;
|
|
||||||
sample_output->sample = sample;
|
|
||||||
wl_signal_add(&output->events.frame, &sample_output->frame);
|
|
||||||
sample_output->frame.notify = output_frame_notify;
|
|
||||||
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
|
||||||
sample_output->destroy.notify = output_remove_notify;
|
|
||||||
wl_list_insert(&sample->outputs, &sample_output->link);
|
|
||||||
|
|
||||||
wlr_output_commit(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
|
||||||
struct sample_state *sample = keyboard->sample;
|
|
||||||
struct wlr_event_keyboard_key *event = data;
|
|
||||||
uint32_t keycode = event->keycode + 8;
|
|
||||||
const xkb_keysym_t *syms;
|
|
||||||
int nsyms = xkb_state_key_get_syms(keyboard->device->keyboard->xkb_state,
|
|
||||||
keycode, &syms);
|
|
||||||
for (int i = 0; i < nsyms; i++) {
|
|
||||||
xkb_keysym_t sym = syms[i];
|
|
||||||
if (sym == XKB_KEY_Escape) {
|
|
||||||
wl_display_terminate(sample->display);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
|
||||||
wl_list_remove(&keyboard->destroy.link);
|
|
||||||
wl_list_remove(&keyboard->key.link);
|
|
||||||
free(keyboard);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void new_input_notify(struct wl_listener *listener, void *data) {
|
|
||||||
struct wlr_input_device *device = data;
|
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
|
||||||
switch (device->type) {
|
|
||||||
case WLR_INPUT_DEVICE_KEYBOARD:;
|
|
||||||
struct sample_keyboard *keyboard = calloc(1, sizeof(struct sample_keyboard));
|
|
||||||
keyboard->device = device;
|
|
||||||
keyboard->sample = sample;
|
|
||||||
wl_signal_add(&device->events.destroy, &keyboard->destroy);
|
|
||||||
keyboard->destroy.notify = keyboard_destroy_notify;
|
|
||||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
|
||||||
keyboard->key.notify = keyboard_key_notify;
|
|
||||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
|
||||||
if (!context) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
struct xkb_keymap *keymap = xkb_map_new_from_names(context, NULL,
|
|
||||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
|
||||||
if (!keymap) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
wlr_keyboard_set_keymap(device->keyboard, keymap);
|
|
||||||
xkb_keymap_unref(keymap);
|
|
||||||
xkb_context_unref(context);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
wlr_log_init(WLR_DEBUG, NULL);
|
|
||||||
struct wl_display *display = wl_display_create();
|
|
||||||
struct sample_state state = {
|
|
||||||
.display = display,
|
|
||||||
};
|
|
||||||
wl_list_init(&state.outputs);
|
|
||||||
|
|
||||||
struct wlr_backend *wlr = wlr_backend_autocreate(display);
|
|
||||||
if (!wlr) {
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_signal_add(&wlr->events.new_output, &state.new_output);
|
|
||||||
state.new_output.notify = new_output_notify;
|
|
||||||
wl_signal_add(&wlr->events.new_input, &state.new_input);
|
|
||||||
state.new_input.notify = new_input_notify;
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
|
|
||||||
|
|
||||||
state.renderer = wlr_renderer_autocreate(wlr);
|
|
||||||
if (!state.renderer) {
|
|
||||||
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
|
||||||
wlr_backend_destroy(wlr);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
|
|
||||||
|
|
||||||
if (!wlr_backend_start(wlr)) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to start backend");
|
|
||||||
wlr_backend_destroy(wlr);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
wl_display_run(display);
|
|
||||||
|
|
||||||
wl_display_destroy(display);
|
|
||||||
}
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include <GLES2/gl2.h>
|
#include <GLES2/gl2.h>
|
||||||
#include <linux/input-event-codes.h>
|
#include <linux/input-event-codes.h>
|
||||||
#include <wayland-egl.h>
|
#include <wayland-egl.h>
|
||||||
#include "egl_common.h"
|
#include <wlr/render/egl.h>
|
||||||
#include "pointer-constraints-unstable-v1-client-protocol.h"
|
#include "pointer-constraints-unstable-v1-client-protocol.h"
|
||||||
#include "relative-pointer-unstable-v1-client-protocol.h"
|
#include "relative-pointer-unstable-v1-client-protocol.h"
|
||||||
#include "xdg-shell-client-protocol.h"
|
#include "xdg-shell-client-protocol.h"
|
||||||
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct egl_info {
|
struct egl_info {
|
||||||
|
struct wlr_egl *egl;
|
||||||
struct wl_egl_window *egl_window;
|
struct wl_egl_window *egl_window;
|
||||||
struct wlr_egl_surface *egl_surface;
|
struct wlr_egl_surface *egl_surface;
|
||||||
uint32_t width;
|
uint32_t width;
|
||||||
|
@ -57,8 +58,8 @@ static struct wl_callback_listener surface_callback_listener = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static void draw_init(struct egl_info *e) {
|
static void draw_init(struct egl_info *e) {
|
||||||
eglMakeCurrent(egl_display, e->egl_surface,
|
eglMakeCurrent(e->egl->display, e->egl_surface,
|
||||||
e->egl_surface, egl_context);
|
e->egl_surface, e->egl->context);
|
||||||
glViewport(0, 0, e->width, e->height);
|
glViewport(0, 0, e->width, e->height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +88,7 @@ static void draw_end(struct egl_info *e) {
|
||||||
e->frame_callback = wl_surface_frame(e->surface);
|
e->frame_callback = wl_surface_frame(e->surface);
|
||||||
wl_callback_add_listener(e->frame_callback, &surface_callback_listener, e);
|
wl_callback_add_listener(e->frame_callback, &surface_callback_listener, e);
|
||||||
|
|
||||||
eglSwapBuffers(egl_display, e->egl_surface);
|
eglSwapBuffers(e->egl->display, e->egl_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -169,7 +170,8 @@ static void xdg_toplevel_handle_configure(void *data,
|
||||||
|
|
||||||
static void xdg_toplevel_handle_close(void *data,
|
static void xdg_toplevel_handle_close(void *data,
|
||||||
struct xdg_toplevel *xdg_toplevel) {
|
struct xdg_toplevel *xdg_toplevel) {
|
||||||
egl_finish();
|
struct egl_info *e = data;
|
||||||
|
wlr_egl_finish(e->egl);
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,6 +412,7 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
|
wl_display_dispatch(display);
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
/* Check that all the global interfaces were captured */
|
/* Check that all the global interfaces were captured */
|
||||||
|
@ -438,9 +441,11 @@ int main(int argc, char **argv) {
|
||||||
/* Initialize EGL context */
|
/* Initialize EGL context */
|
||||||
|
|
||||||
struct egl_info *e = calloc(1, sizeof(struct egl_info));
|
struct egl_info *e = calloc(1, sizeof(struct egl_info));
|
||||||
|
e->egl = calloc(1, sizeof(struct wlr_egl));
|
||||||
e->width = e->height = 512;
|
e->width = e->height = 512;
|
||||||
|
|
||||||
egl_init(display);
|
wlr_egl_init(e->egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
|
||||||
|
WL_SHM_FORMAT_ARGB8888);
|
||||||
|
|
||||||
/* Create the surface and xdg_toplevels, and set listeners */
|
/* Create the surface and xdg_toplevels, and set listeners */
|
||||||
|
|
||||||
|
@ -457,8 +462,7 @@ int main(int argc, char **argv) {
|
||||||
wl_surface_commit(surface);
|
wl_surface_commit(surface);
|
||||||
|
|
||||||
e->egl_window = wl_egl_window_create(surface, e->width, e->height);
|
e->egl_window = wl_egl_window_create(surface, e->width, e->height);
|
||||||
e->egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
e->egl_surface = wlr_egl_create_surface(e->egl, e->egl_window);
|
||||||
egl_display, egl_config, e->egl_window, NULL);
|
|
||||||
e->surface = surface;
|
e->surface = surface;
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#define _POSIX_C_SOURCE 200112L
|
#define _POSIX_C_SOURCE 200112L
|
||||||
#include <drm_fourcc.h>
|
#include <GLES2/gl2.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -8,12 +8,14 @@
|
||||||
#include <strings.h>
|
#include <strings.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-protocol.h>
|
||||||
|
#include <wayland-server.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
#include <wlr/render/allocator.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/types/wlr_keyboard.h>
|
#include <wlr/types/wlr_keyboard.h>
|
||||||
#include <wlr/types/wlr_output.h>
|
#include <wlr/types/wlr_output.h>
|
||||||
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
#include <wlr/types/wlr_input_device.h>
|
#include <wlr/types/wlr_input_device.h>
|
||||||
#include <wlr/types/wlr_matrix.h>
|
#include <wlr/types/wlr_matrix.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
@ -26,7 +28,6 @@ struct sample_state {
|
||||||
struct wl_listener new_input;
|
struct wl_listener new_input;
|
||||||
struct timespec last_frame;
|
struct timespec last_frame;
|
||||||
struct wlr_renderer *renderer;
|
struct wlr_renderer *renderer;
|
||||||
struct wlr_allocator *allocator;
|
|
||||||
struct wlr_texture *cat_texture;
|
struct wlr_texture *cat_texture;
|
||||||
struct wl_list outputs;
|
struct wl_list outputs;
|
||||||
enum wl_output_transform transform;
|
enum wl_output_transform transform;
|
||||||
|
@ -59,7 +60,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
int32_t width, height;
|
int32_t width, height;
|
||||||
wlr_output_effective_resolution(wlr_output, &width, &height);
|
wlr_output_effective_resolution(wlr_output, &width, &height);
|
||||||
|
|
||||||
wlr_output_attach_render(wlr_output, NULL);
|
wlr_output_make_current(wlr_output, NULL);
|
||||||
wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height);
|
wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height);
|
||||||
wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1});
|
wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1});
|
||||||
|
|
||||||
|
@ -71,7 +72,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_renderer_end(sample->renderer);
|
wlr_renderer_end(sample->renderer);
|
||||||
wlr_output_commit(wlr_output);
|
wlr_output_swap_buffers(wlr_output, NULL, NULL);
|
||||||
|
|
||||||
long ms = (now.tv_sec - sample->last_frame.tv_sec) * 1000 +
|
long ms = (now.tv_sec - sample->last_frame.tv_sec) * 1000 +
|
||||||
(now.tv_nsec - sample->last_frame.tv_nsec) / 1000000;
|
(now.tv_nsec - sample->last_frame.tv_nsec) / 1000000;
|
||||||
|
@ -97,20 +98,21 @@ static void update_velocities(struct sample_state *sample,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void output_remove_notify(struct wl_listener *listener, void *data) {
|
void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
||||||
wl_list_remove(&sample_output->frame.link);
|
wl_list_remove(&sample_output->frame.link);
|
||||||
wl_list_remove(&sample_output->destroy.link);
|
wl_list_remove(&sample_output->destroy.link);
|
||||||
free(sample_output);
|
free(sample_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void new_output_notify(struct wl_listener *listener, void *data) {
|
void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_output *output = data;
|
struct wlr_output *output = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
||||||
|
|
||||||
wlr_output_init_render(output, sample->allocator, sample->renderer);
|
|
||||||
|
|
||||||
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
|
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
|
||||||
|
if (!wl_list_empty(&output->modes)) {
|
||||||
|
struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link);
|
||||||
|
wlr_output_set_mode(output, mode);
|
||||||
|
}
|
||||||
sample_output->x_offs = sample_output->y_offs = 0;
|
sample_output->x_offs = sample_output->y_offs = 0;
|
||||||
sample_output->x_vel = sample_output->y_vel = 128;
|
sample_output->x_vel = sample_output->y_vel = 128;
|
||||||
|
|
||||||
|
@ -122,16 +124,9 @@ static void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
||||||
sample_output->destroy.notify = output_remove_notify;
|
sample_output->destroy.notify = output_remove_notify;
|
||||||
wl_list_insert(&sample->outputs, &sample_output->link);
|
wl_list_insert(&sample->outputs, &sample_output->link);
|
||||||
|
|
||||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
|
|
||||||
if (mode != NULL) {
|
|
||||||
wlr_output_set_mode(output, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_output_commit(output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
||||||
struct sample_state *sample = keyboard->sample;
|
struct sample_state *sample = keyboard->sample;
|
||||||
struct wlr_event_keyboard_key *event = data;
|
struct wlr_event_keyboard_key *event = data;
|
||||||
|
@ -144,7 +139,7 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
if (sym == XKB_KEY_Escape) {
|
if (sym == XKB_KEY_Escape) {
|
||||||
wl_display_terminate(sample->display);
|
wl_display_terminate(sample->display);
|
||||||
}
|
}
|
||||||
if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
if (event->state == WLR_KEY_PRESSED) {
|
||||||
switch (sym) {
|
switch (sym) {
|
||||||
case XKB_KEY_Left:
|
case XKB_KEY_Left:
|
||||||
update_velocities(sample, -16, 0);
|
update_velocities(sample, -16, 0);
|
||||||
|
@ -163,14 +158,14 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
||||||
wl_list_remove(&keyboard->destroy.link);
|
wl_list_remove(&keyboard->destroy.link);
|
||||||
wl_list_remove(&keyboard->key.link);
|
wl_list_remove(&keyboard->key.link);
|
||||||
free(keyboard);
|
free(keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void new_input_notify(struct wl_listener *listener, void *data) {
|
void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_input_device *device = data;
|
struct wlr_input_device *device = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
||||||
switch (device->type) {
|
switch (device->type) {
|
||||||
|
@ -182,12 +177,18 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
keyboard->destroy.notify = keyboard_destroy_notify;
|
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||||
keyboard->key.notify = keyboard_key_notify;
|
keyboard->key.notify = keyboard_key_notify;
|
||||||
|
struct xkb_rule_names rules = { 0 };
|
||||||
|
rules.rules = getenv("XKB_DEFAULT_RULES");
|
||||||
|
rules.model = getenv("XKB_DEFAULT_MODEL");
|
||||||
|
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
|
||||||
|
rules.variant = getenv("XKB_DEFAULT_VARIANT");
|
||||||
|
rules.options = getenv("XKB_DEFAULT_OPTIONS");
|
||||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
|
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
|
||||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
if (!keymap) {
|
if (!keymap) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||||
|
@ -228,7 +229,7 @@ int main(int argc, char *argv[]) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return 1;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
wlr_log_init(WLR_DEBUG, NULL);
|
wlr_log_init(WLR_DEBUG, NULL);
|
||||||
|
@ -239,7 +240,7 @@ int main(int argc, char *argv[]) {
|
||||||
};
|
};
|
||||||
wl_list_init(&state.outputs);
|
wl_list_init(&state.outputs);
|
||||||
|
|
||||||
struct wlr_backend *wlr = wlr_backend_autocreate(display);
|
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
|
||||||
if (!wlr) {
|
if (!wlr) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
@ -250,22 +251,20 @@ int main(int argc, char *argv[]) {
|
||||||
state.new_input.notify = new_input_notify;
|
state.new_input.notify = new_input_notify;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
|
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
|
||||||
|
|
||||||
state.renderer = wlr_renderer_autocreate(wlr);
|
state.renderer = wlr_backend_get_renderer(wlr);
|
||||||
if (!state.renderer) {
|
if (!state.renderer) {
|
||||||
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
||||||
wlr_backend_destroy(wlr);
|
wlr_backend_destroy(wlr);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
state.cat_texture = wlr_texture_from_pixels(state.renderer,
|
state.cat_texture = wlr_texture_from_pixels(state.renderer,
|
||||||
DRM_FORMAT_ABGR8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
|
WL_SHM_FORMAT_ABGR8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
|
||||||
cat_tex.pixel_data);
|
cat_tex.pixel_data);
|
||||||
if (!state.cat_texture) {
|
if (!state.cat_texture) {
|
||||||
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
|
|
||||||
|
|
||||||
if (!wlr_backend_start(wlr)) {
|
if (!wlr_backend_start(wlr)) {
|
||||||
wlr_log(WLR_ERROR, "Failed to start backend");
|
wlr_log(WLR_ERROR, "Failed to start backend");
|
||||||
wlr_backend_destroy(wlr);
|
wlr_backend_destroy(wlr);
|
||||||
|
|
|
@ -1,214 +0,0 @@
|
||||||
#define _POSIX_C_SOURCE 200112L
|
|
||||||
#include <assert.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <wayland-server-core.h>
|
|
||||||
#include <wlr/backend.h>
|
|
||||||
#include <wlr/render/allocator.h>
|
|
||||||
#include <wlr/render/wlr_renderer.h>
|
|
||||||
#include <wlr/types/wlr_compositor.h>
|
|
||||||
#include <wlr/types/wlr_output.h>
|
|
||||||
#include <wlr/types/wlr_scene.h>
|
|
||||||
#include <wlr/types/wlr_surface.h>
|
|
||||||
#include <wlr/types/wlr_xdg_shell.h>
|
|
||||||
#include <wlr/util/log.h>
|
|
||||||
|
|
||||||
/* Simple compositor making use of the scene-graph API. Input is unimplemented.
|
|
||||||
*
|
|
||||||
* New surfaces are stacked on top of the existing ones as they appear. */
|
|
||||||
|
|
||||||
static const int border_width = 3;
|
|
||||||
|
|
||||||
struct server {
|
|
||||||
struct wl_display *display;
|
|
||||||
struct wlr_backend *backend;
|
|
||||||
struct wlr_renderer *renderer;
|
|
||||||
struct wlr_allocator *allocator;
|
|
||||||
struct wlr_scene *scene;
|
|
||||||
|
|
||||||
struct wl_list outputs;
|
|
||||||
struct wl_list surfaces;
|
|
||||||
|
|
||||||
struct wl_listener new_output;
|
|
||||||
struct wl_listener new_surface;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct surface {
|
|
||||||
struct wlr_surface *wlr;
|
|
||||||
struct wlr_scene_surface *scene_surface;
|
|
||||||
struct wlr_scene_rect *border;
|
|
||||||
struct wl_list link;
|
|
||||||
|
|
||||||
struct wl_listener commit;
|
|
||||||
struct wl_listener destroy;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct output {
|
|
||||||
struct wl_list link;
|
|
||||||
struct server *server;
|
|
||||||
struct wlr_output *wlr;
|
|
||||||
struct wlr_scene_output *scene_output;
|
|
||||||
|
|
||||||
struct wl_listener frame;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void output_handle_frame(struct wl_listener *listener, void *data) {
|
|
||||||
struct output *output = wl_container_of(listener, output, frame);
|
|
||||||
|
|
||||||
if (!wlr_scene_output_commit(output->scene_output)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct timespec now;
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
||||||
|
|
||||||
struct surface *surface;
|
|
||||||
wl_list_for_each(surface, &output->server->surfaces, link) {
|
|
||||||
wlr_surface_send_frame_done(surface->wlr, &now);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void server_handle_new_output(struct wl_listener *listener, void *data) {
|
|
||||||
struct server *server = wl_container_of(listener, server, new_output);
|
|
||||||
struct wlr_output *wlr_output = data;
|
|
||||||
|
|
||||||
wlr_output_init_render(wlr_output, server->allocator, server->renderer);
|
|
||||||
|
|
||||||
struct output *output =
|
|
||||||
calloc(1, sizeof(struct output));
|
|
||||||
output->wlr = wlr_output;
|
|
||||||
output->server = server;
|
|
||||||
output->frame.notify = output_handle_frame;
|
|
||||||
wl_signal_add(&wlr_output->events.frame, &output->frame);
|
|
||||||
wl_list_insert(&server->outputs, &output->link);
|
|
||||||
|
|
||||||
output->scene_output = wlr_scene_output_create(server->scene, wlr_output);
|
|
||||||
|
|
||||||
if (!wl_list_empty(&wlr_output->modes)) {
|
|
||||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output);
|
|
||||||
wlr_output_set_mode(wlr_output, mode);
|
|
||||||
wlr_output_commit(wlr_output);
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_output_create_global(wlr_output);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void surface_handle_commit(struct wl_listener *listener, void *data) {
|
|
||||||
struct surface *surface = wl_container_of(listener, surface, commit);
|
|
||||||
wlr_scene_rect_set_size(surface->border,
|
|
||||||
surface->wlr->current.width + 2 * border_width,
|
|
||||||
surface->wlr->current.height + 2 * border_width);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void surface_handle_destroy(struct wl_listener *listener, void *data) {
|
|
||||||
struct surface *surface = wl_container_of(listener, surface, destroy);
|
|
||||||
wlr_scene_node_destroy(&surface->scene_surface->node);
|
|
||||||
wlr_scene_node_destroy(&surface->border->node);
|
|
||||||
wl_list_remove(&surface->destroy.link);
|
|
||||||
wl_list_remove(&surface->link);
|
|
||||||
free(surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void server_handle_new_surface(struct wl_listener *listener,
|
|
||||||
void *data) {
|
|
||||||
struct server *server = wl_container_of(listener, server, new_surface);
|
|
||||||
struct wlr_surface *wlr_surface = data;
|
|
||||||
|
|
||||||
int pos = 50 * wl_list_length(&server->surfaces);
|
|
||||||
|
|
||||||
struct surface *surface = calloc(1, sizeof(struct surface));
|
|
||||||
surface->wlr = wlr_surface;
|
|
||||||
surface->commit.notify = surface_handle_commit;
|
|
||||||
wl_signal_add(&wlr_surface->events.commit, &surface->commit);
|
|
||||||
surface->destroy.notify = surface_handle_destroy;
|
|
||||||
wl_signal_add(&wlr_surface->events.destroy, &surface->destroy);
|
|
||||||
|
|
||||||
/* Border dimensions will be set in surface.commit handler */
|
|
||||||
surface->border = wlr_scene_rect_create(&server->scene->node,
|
|
||||||
0, 0, (float[4]){ 0.5f, 0.5f, 0.5f, 1 });
|
|
||||||
wlr_scene_node_set_position(&surface->border->node, pos, pos);
|
|
||||||
|
|
||||||
surface->scene_surface =
|
|
||||||
wlr_scene_surface_create(&server->scene->node, wlr_surface);
|
|
||||||
wl_list_insert(server->surfaces.prev, &surface->link);
|
|
||||||
|
|
||||||
wlr_scene_node_set_position(&surface->scene_surface->node,
|
|
||||||
pos + border_width, pos + border_width);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
wlr_log_init(WLR_DEBUG, NULL);
|
|
||||||
|
|
||||||
char *startup_cmd = NULL;
|
|
||||||
|
|
||||||
int c;
|
|
||||||
while ((c = getopt(argc, argv, "s:")) != -1) {
|
|
||||||
switch (c) {
|
|
||||||
case 's':
|
|
||||||
startup_cmd = optarg;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printf("usage: %s [-s startup-command]\n", argv[0]);
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (optind < argc) {
|
|
||||||
printf("usage: %s [-s startup-command]\n", argv[0]);
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct server server = {0};
|
|
||||||
server.display = wl_display_create();
|
|
||||||
server.backend = wlr_backend_autocreate(server.display);
|
|
||||||
server.scene = wlr_scene_create();
|
|
||||||
|
|
||||||
server.renderer = wlr_renderer_autocreate(server.backend);
|
|
||||||
wlr_renderer_init_wl_display(server.renderer, server.display);
|
|
||||||
|
|
||||||
server.allocator = wlr_allocator_autocreate(server.backend,
|
|
||||||
server.renderer);
|
|
||||||
|
|
||||||
struct wlr_compositor *compositor =
|
|
||||||
wlr_compositor_create(server.display, server.renderer);
|
|
||||||
|
|
||||||
wlr_xdg_shell_create(server.display);
|
|
||||||
|
|
||||||
wl_list_init(&server.outputs);
|
|
||||||
wl_list_init(&server.surfaces);
|
|
||||||
|
|
||||||
server.new_output.notify = server_handle_new_output;
|
|
||||||
wl_signal_add(&server.backend->events.new_output, &server.new_output);
|
|
||||||
|
|
||||||
server.new_surface.notify = server_handle_new_surface;
|
|
||||||
wl_signal_add(&compositor->events.new_surface, &server.new_surface);
|
|
||||||
|
|
||||||
const char *socket = wl_display_add_socket_auto(server.display);
|
|
||||||
if (!socket) {
|
|
||||||
wl_display_destroy(server.display);
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wlr_backend_start(server.backend)) {
|
|
||||||
wl_display_destroy(server.display);
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
setenv("WAYLAND_DISPLAY", socket, true);
|
|
||||||
if (startup_cmd != NULL) {
|
|
||||||
if (fork() == 0) {
|
|
||||||
execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s",
|
|
||||||
socket);
|
|
||||||
wl_display_run(server.display);
|
|
||||||
|
|
||||||
wl_display_destroy_clients(server.display);
|
|
||||||
wl_display_destroy(server.display);
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
|
@ -1,361 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2008 Kristian Høgsberg
|
|
||||||
* Copyright © 2020 Andri Yngvason
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
|
||||||
* to deal in the Software without restriction, including without limitation
|
|
||||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
* and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
* Software is furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice (including the next
|
|
||||||
* paragraph) shall be included in all copies or substantial portions of the
|
|
||||||
* Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
* DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200112L
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <png.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
#include <sys/param.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <gbm.h>
|
|
||||||
#include <xf86drm.h>
|
|
||||||
#include <drm_fourcc.h>
|
|
||||||
#include <wayland-client-protocol.h>
|
|
||||||
#include "wlr-screencopy-unstable-v1-client-protocol.h"
|
|
||||||
#include "linux-dmabuf-unstable-v1-client-protocol.h"
|
|
||||||
|
|
||||||
struct format {
|
|
||||||
uint32_t format;
|
|
||||||
bool is_bgr;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int drm_fd = -1;
|
|
||||||
static struct gbm_device *gbm_device = NULL;
|
|
||||||
|
|
||||||
static struct zwp_linux_dmabuf_v1 *dmabuf = NULL;
|
|
||||||
static struct zwlr_screencopy_manager_v1 *screencopy_manager = NULL;
|
|
||||||
static struct wl_output *output = NULL;
|
|
||||||
|
|
||||||
static struct {
|
|
||||||
struct gbm_bo *bo;
|
|
||||||
struct wl_buffer *wl_buffer;
|
|
||||||
int width, height;
|
|
||||||
uint32_t format;
|
|
||||||
bool y_invert;
|
|
||||||
} buffer;
|
|
||||||
|
|
||||||
bool buffer_copy_done = false;
|
|
||||||
static bool have_linux_dmabuf = false;
|
|
||||||
|
|
||||||
// wl_shm_format describes little-endian formats, libpng uses big-endian
|
|
||||||
// formats (so Wayland's ABGR is libpng's RGBA).
|
|
||||||
static const struct format formats[] = {
|
|
||||||
{DRM_FORMAT_XRGB8888, true},
|
|
||||||
{DRM_FORMAT_ARGB8888, true},
|
|
||||||
{DRM_FORMAT_XBGR8888, false},
|
|
||||||
{DRM_FORMAT_ABGR8888, false},
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool find_render_node(char *node, size_t maxlen) {
|
|
||||||
bool r = false;
|
|
||||||
drmDevice *devices[64];
|
|
||||||
|
|
||||||
int n = drmGetDevices2(0, devices, sizeof(devices) / sizeof(devices[0]));
|
|
||||||
for (int i = 0; i < n; ++i) {
|
|
||||||
drmDevice *dev = devices[i];
|
|
||||||
if (!(dev->available_nodes & (1 << DRM_NODE_RENDER))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
strncpy(node, dev->nodes[DRM_NODE_RENDER], maxlen - 1);
|
|
||||||
node[maxlen - 1] = '\0';
|
|
||||||
r = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
drmFreeDevices(devices, n);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dmabuf_created(void *data,
|
|
||||||
struct zwp_linux_buffer_params_v1 *params,
|
|
||||||
struct wl_buffer *wl_buffer) {
|
|
||||||
buffer.wl_buffer = wl_buffer;
|
|
||||||
|
|
||||||
zwlr_screencopy_frame_v1_copy(data, buffer.wl_buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dmabuf_failed(void *data,
|
|
||||||
struct zwp_linux_buffer_params_v1 *params) {
|
|
||||||
fprintf(stderr, "Failed to create dmabuf\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_linux_buffer_params_v1_listener params_listener = {
|
|
||||||
.created = dmabuf_created,
|
|
||||||
.failed = dmabuf_failed,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void frame_handle_buffer(void *data,
|
|
||||||
struct zwlr_screencopy_frame_v1 *frame, uint32_t wl_format,
|
|
||||||
uint32_t width, uint32_t height, uint32_t stride) {
|
|
||||||
// Not implemented
|
|
||||||
}
|
|
||||||
|
|
||||||
static void frame_handle_linux_dmabuf(void *data,
|
|
||||||
struct zwlr_screencopy_frame_v1 *frame, uint32_t fourcc,
|
|
||||||
uint32_t width, uint32_t height) {
|
|
||||||
buffer.width = width;
|
|
||||||
buffer.height = height;
|
|
||||||
buffer.format = fourcc;
|
|
||||||
have_linux_dmabuf = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void frame_handle_buffer_done(void *data,
|
|
||||||
struct zwlr_screencopy_frame_v1 *frame) {
|
|
||||||
assert(!buffer.bo);
|
|
||||||
|
|
||||||
if (!have_linux_dmabuf) {
|
|
||||||
fprintf(stderr, "linux-dmabuf is not supported\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.bo = gbm_bo_create(gbm_device, buffer.width, buffer.height,
|
|
||||||
buffer.format,
|
|
||||||
GBM_BO_USE_LINEAR | GBM_BO_USE_RENDERING);
|
|
||||||
if (buffer.bo == NULL) {
|
|
||||||
fprintf(stderr, "failed to create GBM buffer object\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct zwp_linux_buffer_params_v1 *params;
|
|
||||||
params = zwp_linux_dmabuf_v1_create_params(dmabuf);
|
|
||||||
assert(params);
|
|
||||||
|
|
||||||
int fd = gbm_bo_get_fd(buffer.bo);
|
|
||||||
uint32_t off = gbm_bo_get_offset(buffer.bo, 0);
|
|
||||||
uint32_t bo_stride = gbm_bo_get_stride(buffer.bo);
|
|
||||||
uint64_t mod = gbm_bo_get_modifier(buffer.bo);
|
|
||||||
zwp_linux_buffer_params_v1_add(params, fd, 0, off, bo_stride, mod >> 32,
|
|
||||||
mod & 0xffffffff);
|
|
||||||
|
|
||||||
zwp_linux_buffer_params_v1_add_listener(params, ¶ms_listener, frame);
|
|
||||||
|
|
||||||
zwp_linux_buffer_params_v1_create(params, buffer.width, buffer.height,
|
|
||||||
buffer.format, /* flags */ 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void frame_handle_flags(void *data,
|
|
||||||
struct zwlr_screencopy_frame_v1 *frame, uint32_t flags) {
|
|
||||||
buffer.y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void frame_handle_ready(void *data,
|
|
||||||
struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi,
|
|
||||||
uint32_t tv_sec_lo, uint32_t tv_nsec) {
|
|
||||||
buffer_copy_done = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void frame_handle_failed(void *data,
|
|
||||||
struct zwlr_screencopy_frame_v1 *frame) {
|
|
||||||
fprintf(stderr, "failed to copy frame\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwlr_screencopy_frame_v1_listener frame_listener = {
|
|
||||||
.buffer = frame_handle_buffer,
|
|
||||||
.linux_dmabuf = frame_handle_linux_dmabuf,
|
|
||||||
.buffer_done = frame_handle_buffer_done,
|
|
||||||
.flags = frame_handle_flags,
|
|
||||||
.ready = frame_handle_ready,
|
|
||||||
.failed = frame_handle_failed,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void dmabuf_format(void *data,
|
|
||||||
struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, uint32_t format) {
|
|
||||||
// deprecated
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dmabuf_modifier(void *data,
|
|
||||||
struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, uint32_t format,
|
|
||||||
uint32_t modifier_hi, uint32_t modifier_lo) {
|
|
||||||
// TODO?
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = {
|
|
||||||
.format = dmabuf_format,
|
|
||||||
.modifier = dmabuf_modifier,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
if (strcmp(interface, wl_output_interface.name) == 0 && output == NULL) {
|
|
||||||
output = wl_registry_bind(registry, name, &wl_output_interface, 1);
|
|
||||||
} else if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0) {
|
|
||||||
dmabuf = wl_registry_bind(registry, name,
|
|
||||||
&zwp_linux_dmabuf_v1_interface, 3);
|
|
||||||
zwp_linux_dmabuf_v1_add_listener(dmabuf, &dmabuf_listener, data);
|
|
||||||
} else if (strcmp(interface,
|
|
||||||
zwlr_screencopy_manager_v1_interface.name) == 0) {
|
|
||||||
screencopy_manager = wl_registry_bind(registry, name,
|
|
||||||
&zwlr_screencopy_manager_v1_interface, 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_global_remove(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name) {
|
|
||||||
// Who cares?
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
.global = handle_global,
|
|
||||||
.global_remove = handle_global_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void write_image(char *filename, uint32_t format, int width,
|
|
||||||
int height, int stride, bool y_invert, png_bytep data) {
|
|
||||||
const struct format *fmt = NULL;
|
|
||||||
for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); ++i) {
|
|
||||||
if (formats[i].format == format) {
|
|
||||||
fmt = &formats[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fmt == NULL) {
|
|
||||||
fprintf(stderr, "unsupported format %"PRIu32"\n", format);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE *f = fopen(filename, "wb");
|
|
||||||
if (f == NULL) {
|
|
||||||
fprintf(stderr, "failed to open output file\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
png_structp png =
|
|
||||||
png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
|
||||||
png_infop info = png_create_info_struct(png);
|
|
||||||
|
|
||||||
png_init_io(png, f);
|
|
||||||
|
|
||||||
png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGBA,
|
|
||||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
|
||||||
PNG_FILTER_TYPE_DEFAULT);
|
|
||||||
|
|
||||||
if (fmt->is_bgr) {
|
|
||||||
png_set_bgr(png);
|
|
||||||
}
|
|
||||||
|
|
||||||
png_write_info(png, info);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < (size_t)height; ++i) {
|
|
||||||
png_bytep row;
|
|
||||||
if (y_invert) {
|
|
||||||
row = data + (height - i - 1) * stride;
|
|
||||||
} else {
|
|
||||||
row = data + i * stride;
|
|
||||||
}
|
|
||||||
png_write_row(png, row);
|
|
||||||
}
|
|
||||||
|
|
||||||
png_write_end(png, NULL);
|
|
||||||
|
|
||||||
png_destroy_write_struct(&png, &info);
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
char render_node[256];
|
|
||||||
if (!find_render_node(render_node, sizeof(render_node))) {
|
|
||||||
fprintf(stderr, "Failed to find a DRM render node\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Using render node: %s\n", render_node);
|
|
||||||
|
|
||||||
drm_fd = open(render_node, O_RDWR);
|
|
||||||
if (drm_fd < 0) {
|
|
||||||
perror("Failed to open drm render node");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
gbm_device = gbm_create_device(drm_fd);
|
|
||||||
if (!gbm_device) {
|
|
||||||
fprintf(stderr, "Failed to create gbm device\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_display *display = wl_display_connect(NULL);
|
|
||||||
if (display == NULL) {
|
|
||||||
perror("failed to create display");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
if (dmabuf == NULL) {
|
|
||||||
fprintf(stderr, "compositor is missing linux-dmabuf-unstable-v1\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (screencopy_manager == NULL) {
|
|
||||||
fprintf(stderr, "compositor doesn't support wlr-screencopy-unstable-v1\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
if (output == NULL) {
|
|
||||||
fprintf(stderr, "no output available\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct zwlr_screencopy_frame_v1 *frame =
|
|
||||||
zwlr_screencopy_manager_v1_capture_output(screencopy_manager, 0, output);
|
|
||||||
zwlr_screencopy_frame_v1_add_listener(frame, &frame_listener, NULL);
|
|
||||||
|
|
||||||
while (!buffer_copy_done && wl_display_dispatch(display) != -1) {
|
|
||||||
// This space is intentionally left blank
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t stride = 0;
|
|
||||||
void *map_data = NULL;
|
|
||||||
void *data = gbm_bo_map(buffer.bo, 0, 0, buffer.width, buffer.height,
|
|
||||||
GBM_BO_TRANSFER_READ, &stride, &map_data);
|
|
||||||
if (!data) {
|
|
||||||
perror("Failed to map gbm bo");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
write_image("wayland-screenshot.png", buffer.format, buffer.width,
|
|
||||||
buffer.height, stride, buffer.y_invert, data);
|
|
||||||
|
|
||||||
gbm_bo_unmap(buffer.bo, map_data);
|
|
||||||
gbm_bo_destroy(buffer.bo);
|
|
||||||
wl_buffer_destroy(buffer.wl_buffer);
|
|
||||||
|
|
||||||
gbm_device_destroy(gbm_device);
|
|
||||||
close(drm_fd);
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
|
@ -32,10 +32,8 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <wayland-client-protocol.h>
|
#include <wayland-client-protocol.h>
|
||||||
#include "wlr-screencopy-unstable-v1-client-protocol.h"
|
#include "wlr-screencopy-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
|
@ -71,7 +69,7 @@ static struct wl_buffer *create_shm_buffer(enum wl_shm_format fmt,
|
||||||
int size = stride * height;
|
int size = stride * height;
|
||||||
|
|
||||||
const char shm_name[] = "/wlroots-screencopy";
|
const char shm_name[] = "/wlroots-screencopy";
|
||||||
int fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
|
int fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, 0);
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
fprintf(stderr, "shm_open failed\n");
|
fprintf(stderr, "shm_open failed\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -90,7 +88,7 @@ static struct wl_buffer *create_shm_buffer(enum wl_shm_format fmt,
|
||||||
|
|
||||||
void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
if (data == MAP_FAILED) {
|
if (data == MAP_FAILED) {
|
||||||
perror("mmap failed");
|
fprintf(stderr, "mmap failed: %m\n");
|
||||||
close(fd);
|
close(fd);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -112,9 +110,6 @@ static void frame_handle_buffer(void *data,
|
||||||
buffer.width = width;
|
buffer.width = width;
|
||||||
buffer.height = height;
|
buffer.height = height;
|
||||||
buffer.stride = stride;
|
buffer.stride = stride;
|
||||||
|
|
||||||
// Make sure the buffer is not allocated
|
|
||||||
assert(!buffer.wl_buffer);
|
|
||||||
buffer.wl_buffer =
|
buffer.wl_buffer =
|
||||||
create_shm_buffer(format, width, height, stride, &buffer.data);
|
create_shm_buffer(format, width, height, stride, &buffer.data);
|
||||||
if (buffer.wl_buffer == NULL) {
|
if (buffer.wl_buffer == NULL) {
|
||||||
|
@ -228,12 +223,13 @@ static void write_image(char *filename, enum wl_shm_format wl_fmt, int width,
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
struct wl_display * display = wl_display_connect(NULL);
|
struct wl_display * display = wl_display_connect(NULL);
|
||||||
if (display == NULL) {
|
if (display == NULL) {
|
||||||
perror("failed to create display");
|
fprintf(stderr, "failed to create display: %m\n");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
|
wl_display_dispatch(display);
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
if (shm == NULL) {
|
if (shm == NULL) {
|
||||||
|
@ -260,7 +256,6 @@ int main(int argc, char *argv[]) {
|
||||||
write_image("wayland-screenshot.png", buffer.format, buffer.width,
|
write_image("wayland-screenshot.png", buffer.format, buffer.width,
|
||||||
buffer.height, buffer.stride, buffer.y_invert, buffer.data);
|
buffer.height, buffer.stride, buffer.y_invert, buffer.data);
|
||||||
wl_buffer_destroy(buffer.wl_buffer);
|
wl_buffer_destroy(buffer.wl_buffer);
|
||||||
munmap(buffer.data, buffer.stride * buffer.height);
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,237 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2008 Kristian Høgsberg
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
* copy of this software and associated documentation files (the "Software"),
|
||||||
|
* to deal in the Software without restriction, including without limitation
|
||||||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice (including the next
|
||||||
|
* paragraph) shall be included in all copies or substantial portions of the
|
||||||
|
* Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
* DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define _POSIX_C_SOURCE 200112L
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <wayland-client.h>
|
||||||
|
#include "screenshooter-client-protocol.h"
|
||||||
|
|
||||||
|
static struct wl_shm *shm = NULL;
|
||||||
|
static struct orbital_screenshooter *screenshooter = NULL;
|
||||||
|
static struct wl_list output_list;
|
||||||
|
static bool buffer_copy_done;
|
||||||
|
|
||||||
|
struct screenshooter_output {
|
||||||
|
struct wl_output *output;
|
||||||
|
int width, height;
|
||||||
|
struct wl_list link;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void output_handle_geometry(void *data, struct wl_output *wl_output,
|
||||||
|
int x, int y, int physical_width, int physical_height, int subpixel,
|
||||||
|
const char *make, const char *model, int transform) {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_handle_mode(void *data, struct wl_output *wl_output,
|
||||||
|
uint32_t flags, int width, int height, int refresh) {
|
||||||
|
struct screenshooter_output *output = wl_output_get_user_data(wl_output);
|
||||||
|
|
||||||
|
if (wl_output == output->output && (flags & WL_OUTPUT_MODE_CURRENT)) {
|
||||||
|
output->width = width;
|
||||||
|
output->height = height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void output_handle_done(void *data, struct wl_output *wl_output) {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_output_listener output_listener = {
|
||||||
|
.geometry = output_handle_geometry,
|
||||||
|
.mode = output_handle_mode,
|
||||||
|
.done = output_handle_done,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void screenshot_done(void *data, struct orbital_screenshot *screenshot) {
|
||||||
|
buffer_copy_done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct orbital_screenshot_listener screenshot_listener = {
|
||||||
|
.done = screenshot_done,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void handle_global(void *data, struct wl_registry *registry,
|
||||||
|
uint32_t name, const char *interface, uint32_t version) {
|
||||||
|
static struct screenshooter_output *output;
|
||||||
|
|
||||||
|
if (strcmp(interface, "wl_output") == 0) {
|
||||||
|
output = calloc(1, sizeof(*output));
|
||||||
|
output->output = wl_registry_bind(registry, name, &wl_output_interface,
|
||||||
|
1);
|
||||||
|
wl_list_insert(&output_list, &output->link);
|
||||||
|
wl_output_add_listener(output->output, &output_listener, output);
|
||||||
|
} else if (strcmp(interface, "wl_shm") == 0) {
|
||||||
|
shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
|
||||||
|
} else if (strcmp(interface, "orbital_screenshooter") == 0) {
|
||||||
|
screenshooter = wl_registry_bind(registry, name,
|
||||||
|
&orbital_screenshooter_interface, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_global_remove(void *data, struct wl_registry *registry,
|
||||||
|
uint32_t name) {
|
||||||
|
// Who cares?
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wl_registry_listener registry_listener = {
|
||||||
|
.global = handle_global,
|
||||||
|
.global_remove = handle_global_remove,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct wl_buffer *create_shm_buffer(int width, int height,
|
||||||
|
void **data_out) {
|
||||||
|
int stride = width * 4;
|
||||||
|
int size = stride * height;
|
||||||
|
|
||||||
|
const char shm_name[] = "/wlroots-screenshot";
|
||||||
|
int fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, 0);
|
||||||
|
if (fd < 0) {
|
||||||
|
fprintf(stderr, "shm_open failed\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
shm_unlink(shm_name);
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
while ((ret = ftruncate(fd, size)) == EINTR) {
|
||||||
|
// No-op
|
||||||
|
}
|
||||||
|
if (ret < 0) {
|
||||||
|
close(fd);
|
||||||
|
fprintf(stderr, "ftruncate failed\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
|
if (data == MAP_FAILED) {
|
||||||
|
fprintf(stderr, "mmap failed: %m\n");
|
||||||
|
close(fd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
|
||||||
|
close(fd);
|
||||||
|
struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, width, height,
|
||||||
|
stride, WL_SHM_FORMAT_XRGB8888);
|
||||||
|
wl_shm_pool_destroy(pool);
|
||||||
|
|
||||||
|
*data_out = data;
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_image(const char *filename, int width, int height,
|
||||||
|
void *data) {
|
||||||
|
char size[10 + 1 + 10 + 2 + 1]; // int32_t are max 10 digits
|
||||||
|
sprintf(size, "%dx%d+0", width, height);
|
||||||
|
|
||||||
|
int fd[2];
|
||||||
|
if (pipe(fd) != 0) {
|
||||||
|
fprintf(stderr, "cannot create pipe: %s\n", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t child = fork();
|
||||||
|
if (child < 0) {
|
||||||
|
fprintf(stderr, "fork() failed\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
} else if (child != 0) {
|
||||||
|
close(fd[0]);
|
||||||
|
if (write(fd[1], data, 4 * width * height) < 0) {
|
||||||
|
fprintf(stderr, "write() failed: %s\n", strerror(errno));
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
close(fd[1]);
|
||||||
|
waitpid(child, NULL, 0);
|
||||||
|
} else {
|
||||||
|
close(fd[1]);
|
||||||
|
if (dup2(fd[0], 0) != 0) {
|
||||||
|
fprintf(stderr, "cannot dup the pipe\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
close(fd[0]);
|
||||||
|
// We requested WL_SHM_FORMAT_XRGB8888 in little endian, so that's BGRA
|
||||||
|
// in big endian.
|
||||||
|
execlp("convert", "convert", "-depth", "8", "-size", size, "bgra:-",
|
||||||
|
"-alpha", "opaque", filename, NULL);
|
||||||
|
fprintf(stderr, "cannot execute convert\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
struct wl_display * display = wl_display_connect(NULL);
|
||||||
|
if (display == NULL) {
|
||||||
|
fprintf(stderr, "failed to create display: %m\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
wl_list_init(&output_list);
|
||||||
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
|
wl_display_dispatch(display);
|
||||||
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
if (screenshooter == NULL) {
|
||||||
|
fprintf(stderr, "display doesn't support screenshooter\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
struct screenshooter_output *output;
|
||||||
|
wl_list_for_each(output, &output_list, link) {
|
||||||
|
void *data = NULL;
|
||||||
|
struct wl_buffer *buffer =
|
||||||
|
create_shm_buffer(output->width, output->height, &data);
|
||||||
|
if (buffer == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
struct orbital_screenshot *screenshot = orbital_screenshooter_shoot(
|
||||||
|
screenshooter, output->output, buffer);
|
||||||
|
orbital_screenshot_add_listener(screenshot, &screenshot_listener,
|
||||||
|
screenshot);
|
||||||
|
buffer_copy_done = false;
|
||||||
|
while (!buffer_copy_done) {
|
||||||
|
wl_display_roundtrip(display);
|
||||||
|
}
|
||||||
|
|
||||||
|
char filename[24 + 10]; // int32_t are max 10 digits
|
||||||
|
snprintf(filename, sizeof(filename), "wayland-screenshot-%d.png", i);
|
||||||
|
|
||||||
|
write_image(filename, output->width, output->height, data);
|
||||||
|
wl_buffer_destroy(buffer);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
|
@ -1,17 +1,15 @@
|
||||||
#define _POSIX_C_SOURCE 200112L
|
#define _POSIX_C_SOURCE 200112L
|
||||||
|
#include <GLES2/gl2.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/render/allocator.h>
|
|
||||||
#include <wlr/render/wlr_renderer.h>
|
|
||||||
#include <wlr/types/wlr_output.h>
|
#include <wlr/types/wlr_output.h>
|
||||||
#include <wlr/types/wlr_input_device.h>
|
#include <wlr/types/wlr_input_device.h>
|
||||||
#include <wlr/types/wlr_keyboard.h>
|
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
|
||||||
|
@ -19,10 +17,8 @@ struct sample_state {
|
||||||
struct wl_display *display;
|
struct wl_display *display;
|
||||||
struct wl_listener new_output;
|
struct wl_listener new_output;
|
||||||
struct wl_listener new_input;
|
struct wl_listener new_input;
|
||||||
struct wlr_renderer *renderer;
|
|
||||||
struct wlr_allocator *allocator;
|
|
||||||
struct timespec last_frame;
|
struct timespec last_frame;
|
||||||
float color[4];
|
float color[3];
|
||||||
int dec;
|
int dec;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -40,12 +36,10 @@ struct sample_keyboard {
|
||||||
struct wl_listener destroy;
|
struct wl_listener destroy;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void output_frame_notify(struct wl_listener *listener, void *data) {
|
void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_output *sample_output =
|
struct sample_output *sample_output =
|
||||||
wl_container_of(listener, sample_output, frame);
|
wl_container_of(listener, sample_output, frame);
|
||||||
struct sample_state *sample = sample_output->sample;
|
struct sample_state *sample = sample_output->sample;
|
||||||
struct wlr_output *wlr_output = sample_output->output;
|
|
||||||
|
|
||||||
struct timespec now;
|
struct timespec now;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
|
||||||
|
@ -62,18 +56,16 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
sample->dec = inc;
|
sample->dec = inc;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_output_attach_render(wlr_output, NULL);
|
wlr_output_make_current(sample_output->output, NULL);
|
||||||
|
|
||||||
struct wlr_renderer *renderer = sample->renderer;
|
glClearColor(sample->color[0], sample->color[1], sample->color[2], 1.0);
|
||||||
wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
wlr_renderer_clear(renderer, sample->color);
|
|
||||||
wlr_renderer_end(renderer);
|
|
||||||
|
|
||||||
wlr_output_commit(wlr_output);
|
wlr_output_swap_buffers(sample_output->output, NULL, NULL);
|
||||||
sample->last_frame = now;
|
sample->last_frame = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void output_remove_notify(struct wl_listener *listener, void *data) {
|
void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_output *sample_output =
|
struct sample_output *sample_output =
|
||||||
wl_container_of(listener, sample_output, destroy);
|
wl_container_of(listener, sample_output, destroy);
|
||||||
wlr_log(WLR_DEBUG, "Output removed");
|
wlr_log(WLR_DEBUG, "Output removed");
|
||||||
|
@ -82,31 +74,26 @@ static void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
free(sample_output);
|
free(sample_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void new_output_notify(struct wl_listener *listener, void *data) {
|
void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_output *output = data;
|
struct wlr_output *output = data;
|
||||||
struct sample_state *sample =
|
struct sample_state *sample =
|
||||||
wl_container_of(listener, sample, new_output);
|
wl_container_of(listener, sample, new_output);
|
||||||
|
|
||||||
wlr_output_init_render(output, sample->allocator, sample->renderer);
|
|
||||||
|
|
||||||
struct sample_output *sample_output =
|
struct sample_output *sample_output =
|
||||||
calloc(1, sizeof(struct sample_output));
|
calloc(1, sizeof(struct sample_output));
|
||||||
|
if (!wl_list_empty(&output->modes)) {
|
||||||
|
struct wlr_output_mode *mode =
|
||||||
|
wl_container_of(output->modes.prev, mode, link);
|
||||||
|
wlr_output_set_mode(output, mode);
|
||||||
|
}
|
||||||
sample_output->output = output;
|
sample_output->output = output;
|
||||||
sample_output->sample = sample;
|
sample_output->sample = sample;
|
||||||
wl_signal_add(&output->events.frame, &sample_output->frame);
|
wl_signal_add(&output->events.frame, &sample_output->frame);
|
||||||
sample_output->frame.notify = output_frame_notify;
|
sample_output->frame.notify = output_frame_notify;
|
||||||
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
||||||
sample_output->destroy.notify = output_remove_notify;
|
sample_output->destroy.notify = output_remove_notify;
|
||||||
|
|
||||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
|
|
||||||
if (mode != NULL) {
|
|
||||||
wlr_output_set_mode(output, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_output_commit(sample_output->output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
||||||
struct sample_state *sample = keyboard->sample;
|
struct sample_state *sample = keyboard->sample;
|
||||||
struct wlr_event_keyboard_key *event = data;
|
struct wlr_event_keyboard_key *event = data;
|
||||||
|
@ -122,7 +109,7 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard =
|
struct sample_keyboard *keyboard =
|
||||||
wl_container_of(listener, keyboard, destroy);
|
wl_container_of(listener, keyboard, destroy);
|
||||||
wl_list_remove(&keyboard->destroy.link);
|
wl_list_remove(&keyboard->destroy.link);
|
||||||
|
@ -130,7 +117,7 @@ static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
||||||
free(keyboard);
|
free(keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void new_input_notify(struct wl_listener *listener, void *data) {
|
void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_input_device *device = data;
|
struct wlr_input_device *device = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
||||||
switch (device->type) {
|
switch (device->type) {
|
||||||
|
@ -143,12 +130,18 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
keyboard->destroy.notify = keyboard_destroy_notify;
|
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||||
keyboard->key.notify = keyboard_key_notify;
|
keyboard->key.notify = keyboard_key_notify;
|
||||||
|
struct xkb_rule_names rules = { 0 };
|
||||||
|
rules.rules = getenv("XKB_DEFAULT_RULES");
|
||||||
|
rules.model = getenv("XKB_DEFAULT_MODEL");
|
||||||
|
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
|
||||||
|
rules.variant = getenv("XKB_DEFAULT_VARIANT");
|
||||||
|
rules.options = getenv("XKB_DEFAULT_OPTIONS");
|
||||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
|
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
|
||||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
if (!keymap) {
|
if (!keymap) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||||
|
@ -167,19 +160,15 @@ int main(void) {
|
||||||
wlr_log_init(WLR_DEBUG, NULL);
|
wlr_log_init(WLR_DEBUG, NULL);
|
||||||
struct wl_display *display = wl_display_create();
|
struct wl_display *display = wl_display_create();
|
||||||
struct sample_state state = {
|
struct sample_state state = {
|
||||||
.color = { 1.0, 0.0, 0.0, 1.0 },
|
.color = { 1.0, 0.0, 0.0 },
|
||||||
.dec = 0,
|
.dec = 0,
|
||||||
.last_frame = { 0 },
|
.last_frame = { 0 },
|
||||||
.display = display
|
.display = display
|
||||||
};
|
};
|
||||||
struct wlr_backend *backend = wlr_backend_autocreate(display);
|
struct wlr_backend *backend = wlr_backend_autocreate(display, NULL);
|
||||||
if (!backend) {
|
if (!backend) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.renderer = wlr_renderer_autocreate(backend);
|
|
||||||
state.allocator = wlr_allocator_autocreate(backend, state.renderer);
|
|
||||||
|
|
||||||
wl_signal_add(&backend->events.new_output, &state.new_output);
|
wl_signal_add(&backend->events.new_output, &state.new_output);
|
||||||
state.new_output.notify = new_output_notify;
|
state.new_output.notify = new_output_notify;
|
||||||
wl_signal_add(&backend->events.new_input, &state.new_input);
|
wl_signal_add(&backend->events.new_input, &state.new_input);
|
||||||
|
|
|
@ -1,29 +1,28 @@
|
||||||
#define _XOPEN_SOURCE 600
|
#define _XOPEN_SOURCE 600
|
||||||
|
#include <GLES2/gl2.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-protocol.h>
|
||||||
|
#include <wayland-server.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/render/allocator.h>
|
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
|
#include <wlr/types/wlr_box.h>
|
||||||
#include <wlr/types/wlr_matrix.h>
|
#include <wlr/types/wlr_matrix.h>
|
||||||
#include <wlr/types/wlr_output.h>
|
#include <wlr/types/wlr_output.h>
|
||||||
#include <wlr/types/wlr_input_device.h>
|
#include <wlr/types/wlr_input_device.h>
|
||||||
#include <wlr/types/wlr_keyboard.h>
|
|
||||||
#include <wlr/types/wlr_tablet_pad.h>
|
#include <wlr/types/wlr_tablet_pad.h>
|
||||||
#include <wlr/types/wlr_tablet_tool.h>
|
#include <wlr/types/wlr_tablet_tool.h>
|
||||||
#include <wlr/util/box.h>
|
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
|
|
||||||
struct sample_state {
|
struct sample_state {
|
||||||
struct wl_display *display;
|
struct wl_display *display;
|
||||||
struct wlr_renderer *renderer;
|
struct wlr_renderer *renderer;
|
||||||
struct wlr_allocator *allocator;
|
|
||||||
bool proximity, tap, button;
|
bool proximity, tap, button;
|
||||||
double distance;
|
double distance;
|
||||||
double pressure;
|
double pressure;
|
||||||
|
@ -87,7 +86,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
int32_t width, height;
|
int32_t width, height;
|
||||||
wlr_output_effective_resolution(wlr_output, &width, &height);
|
wlr_output_effective_resolution(wlr_output, &width, &height);
|
||||||
|
|
||||||
wlr_output_attach_render(wlr_output, NULL);
|
wlr_output_make_current(wlr_output, NULL);
|
||||||
wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height);
|
wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height);
|
||||||
wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1});
|
wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1});
|
||||||
|
|
||||||
|
@ -130,7 +129,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_renderer_end(sample->renderer);
|
wlr_renderer_end(sample->renderer);
|
||||||
wlr_output_commit(wlr_output);
|
wlr_output_swap_buffers(wlr_output, NULL, NULL);
|
||||||
sample->last_frame = now;
|
sample->last_frame = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,36 +228,30 @@ static void tablet_pad_destroy_notify(struct wl_listener *listener, void *data)
|
||||||
free(pstate);
|
free(pstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void output_remove_notify(struct wl_listener *listener, void *data) {
|
void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
||||||
wl_list_remove(&sample_output->frame.link);
|
wl_list_remove(&sample_output->frame.link);
|
||||||
wl_list_remove(&sample_output->destroy.link);
|
wl_list_remove(&sample_output->destroy.link);
|
||||||
free(sample_output);
|
free(sample_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void new_output_notify(struct wl_listener *listener, void *data) {
|
void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_output *output = data;
|
struct wlr_output *output = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
||||||
|
|
||||||
wlr_output_init_render(output, sample->allocator, sample->renderer);
|
|
||||||
|
|
||||||
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
|
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
|
||||||
|
if (!wl_list_empty(&output->modes)) {
|
||||||
|
struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link);
|
||||||
|
wlr_output_set_mode(output, mode);
|
||||||
|
}
|
||||||
sample_output->output = output;
|
sample_output->output = output;
|
||||||
sample_output->sample = sample;
|
sample_output->sample = sample;
|
||||||
wl_signal_add(&output->events.frame, &sample_output->frame);
|
wl_signal_add(&output->events.frame, &sample_output->frame);
|
||||||
sample_output->frame.notify = output_frame_notify;
|
sample_output->frame.notify = output_frame_notify;
|
||||||
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
||||||
sample_output->destroy.notify = output_remove_notify;
|
sample_output->destroy.notify = output_remove_notify;
|
||||||
|
|
||||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
|
|
||||||
if (mode != NULL) {
|
|
||||||
wlr_output_set_mode(output, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_output_commit(output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
||||||
struct sample_state *sample = keyboard->sample;
|
struct sample_state *sample = keyboard->sample;
|
||||||
struct wlr_event_keyboard_key *event = data;
|
struct wlr_event_keyboard_key *event = data;
|
||||||
|
@ -274,14 +267,14 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
||||||
wl_list_remove(&keyboard->destroy.link);
|
wl_list_remove(&keyboard->destroy.link);
|
||||||
wl_list_remove(&keyboard->key.link);
|
wl_list_remove(&keyboard->key.link);
|
||||||
free(keyboard);
|
free(keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void new_input_notify(struct wl_listener *listener, void *data) {
|
void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_input_device *device = data;
|
struct wlr_input_device *device = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
||||||
switch (device->type) {
|
switch (device->type) {
|
||||||
|
@ -293,12 +286,18 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
keyboard->destroy.notify = keyboard_destroy_notify;
|
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||||
keyboard->key.notify = keyboard_key_notify;
|
keyboard->key.notify = keyboard_key_notify;
|
||||||
|
struct xkb_rule_names rules = { 0 };
|
||||||
|
rules.rules = getenv("XKB_DEFAULT_RULES");
|
||||||
|
rules.model = getenv("XKB_DEFAULT_MODEL");
|
||||||
|
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
|
||||||
|
rules.variant = getenv("XKB_DEFAULT_VARIANT");
|
||||||
|
rules.options = getenv("XKB_DEFAULT_OPTIONS");
|
||||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
|
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
|
||||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
if (!keymap) {
|
if (!keymap) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||||
|
@ -355,7 +354,7 @@ int main(int argc, char *argv[]) {
|
||||||
};
|
};
|
||||||
wl_list_init(&state.tablet_pads);
|
wl_list_init(&state.tablet_pads);
|
||||||
wl_list_init(&state.tablet_tools);
|
wl_list_init(&state.tablet_tools);
|
||||||
struct wlr_backend *wlr = wlr_backend_autocreate(display);
|
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
|
||||||
if (!wlr) {
|
if (!wlr) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
@ -366,14 +365,11 @@ int main(int argc, char *argv[]) {
|
||||||
state.new_input.notify = new_input_notify;
|
state.new_input.notify = new_input_notify;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
|
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
|
||||||
|
|
||||||
state.renderer = wlr_renderer_autocreate(wlr);
|
state.renderer = wlr_backend_get_renderer(wlr);
|
||||||
if (!state.renderer) {
|
if (!state.renderer) {
|
||||||
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
|
|
||||||
|
|
||||||
if (!wlr_backend_start(wlr)) {
|
if (!wlr_backend_start(wlr)) {
|
||||||
wlr_log(WLR_ERROR, "Failed to start backend");
|
wlr_log(WLR_ERROR, "Failed to start backend");
|
||||||
wlr_backend_destroy(wlr);
|
wlr_backend_destroy(wlr);
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <wayland-egl.h>
|
#include <wayland-egl.h>
|
||||||
#include "egl_common.h"
|
#include <wlr/render/egl.h>
|
||||||
#include "text-input-unstable-v3-client-protocol.h"
|
#include "text-input-unstable-v3-client-protocol.h"
|
||||||
#include "xdg-shell-client-protocol.h"
|
#include "xdg-shell-client-protocol.h"
|
||||||
|
|
||||||
|
@ -63,11 +63,12 @@ static struct xdg_wm_base *wm_base = NULL;
|
||||||
static struct zwp_text_input_manager_v3 *text_input_manager = NULL;
|
static struct zwp_text_input_manager_v3 *text_input_manager = NULL;
|
||||||
static struct zwp_text_input_v3 *text_input = NULL;
|
static struct zwp_text_input_v3 *text_input = NULL;
|
||||||
|
|
||||||
|
struct wlr_egl egl;
|
||||||
struct wl_egl_window *egl_window;
|
struct wl_egl_window *egl_window;
|
||||||
struct wlr_egl_surface *egl_surface;
|
struct wlr_egl_surface *egl_surface;
|
||||||
|
|
||||||
static void draw(void) {
|
static void draw(void) {
|
||||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
|
||||||
|
|
||||||
float color[] = {1.0, 1.0, 0.0, 1.0};
|
float color[] = {1.0, 1.0, 0.0, 1.0};
|
||||||
color[0] = enabled * 1.0;
|
color[0] = enabled * 1.0;
|
||||||
|
@ -77,7 +78,7 @@ static void draw(void) {
|
||||||
glClearColor(color[0], color[1], color[2], 1.0);
|
glClearColor(color[0], color[1], color[2], 1.0);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
eglSwapBuffers(egl_display, egl_surface);
|
eglSwapBuffers(egl.display, egl_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t utf8_strlen(char *str) {
|
static size_t utf8_strlen(char *str) {
|
||||||
|
@ -101,7 +102,7 @@ static size_t utf8_offset(char *utf8_str, size_t byte_offset) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: would be nicer to have this text display inside the window
|
// TODO: would be nicer to have this text display inside the window
|
||||||
static void show_status(void) {
|
static void show_status() {
|
||||||
printf("State %d:", serial);
|
printf("State %d:", serial);
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
printf(" disabled");
|
printf(" disabled");
|
||||||
|
@ -136,7 +137,8 @@ static void show_status(void) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((unsigned)current.preedit.cursor_begin > strlen(preedit_text)) {
|
if ((unsigned)current.preedit.cursor_begin > strlen(preedit_text)
|
||||||
|
|| (unsigned)current.preedit.cursor_begin > strlen(preedit_text)) {
|
||||||
printf("Cursor out of bounds\n");
|
printf("Cursor out of bounds\n");
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
@ -343,6 +345,7 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
|
wl_display_dispatch(display);
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
if (compositor == NULL) {
|
if (compositor == NULL) {
|
||||||
|
@ -362,7 +365,9 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
zwp_text_input_v3_add_listener(text_input, &text_input_listener, NULL);
|
zwp_text_input_v3_add_listener(text_input, &text_input_listener, NULL);
|
||||||
|
|
||||||
egl_init(display);
|
|
||||||
|
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
|
||||||
|
WL_SHM_FORMAT_ARGB8888);
|
||||||
|
|
||||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
||||||
struct xdg_surface *xdg_surface =
|
struct xdg_surface *xdg_surface =
|
||||||
|
@ -375,8 +380,7 @@ int main(int argc, char **argv) {
|
||||||
wl_surface_commit(surface);
|
wl_surface_commit(surface);
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(surface, width, height);
|
egl_window = wl_egl_window_create(surface, width, height);
|
||||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
egl_surface = wlr_egl_create_surface(&egl, egl_window);
|
||||||
egl_display, egl_config, egl_window, NULL);
|
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <wayland-egl.h>
|
#include <wayland-egl.h>
|
||||||
#include "egl_common.h"
|
#include <wlr/render/egl.h>
|
||||||
#include "xdg-shell-client-protocol.h"
|
#include "xdg-shell-client-protocol.h"
|
||||||
#include "xdg-decoration-unstable-v1-client-protocol.h"
|
#include "xdg-decoration-unstable-v1-client-protocol.h"
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ static struct wl_compositor *compositor = NULL;
|
||||||
static struct xdg_wm_base *wm_base = NULL;
|
static struct xdg_wm_base *wm_base = NULL;
|
||||||
static struct zxdg_decoration_manager_v1 *decoration_manager = NULL;
|
static struct zxdg_decoration_manager_v1 *decoration_manager = NULL;
|
||||||
|
|
||||||
|
struct wlr_egl egl;
|
||||||
struct wl_egl_window *egl_window;
|
struct wl_egl_window *egl_window;
|
||||||
struct wlr_egl_surface *egl_surface;
|
struct wlr_egl_surface *egl_surface;
|
||||||
|
|
||||||
|
@ -52,7 +53,7 @@ static void request_preferred_mode(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void draw(void) {
|
static void draw(void) {
|
||||||
eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
|
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
|
||||||
|
|
||||||
float color[] = {1.0, 1.0, 0.0, 1.0};
|
float color[] = {1.0, 1.0, 0.0, 1.0};
|
||||||
if (current_mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE) {
|
if (current_mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE) {
|
||||||
|
@ -63,7 +64,7 @@ static void draw(void) {
|
||||||
glClearColor(color[0], color[1], color[2], 1.0);
|
glClearColor(color[0], color[1], color[2], 1.0);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
eglSwapBuffers(egl_display, egl_surface);
|
eglSwapBuffers(egl.display, egl_surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xdg_surface_handle_configure(void *data,
|
static void xdg_surface_handle_configure(void *data,
|
||||||
|
@ -202,6 +203,7 @@ int main(int argc, char **argv) {
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
struct wl_registry *registry = wl_display_get_registry(display);
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
||||||
|
wl_display_dispatch(display);
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
if (compositor == NULL) {
|
if (compositor == NULL) {
|
||||||
|
@ -217,7 +219,8 @@ int main(int argc, char **argv) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
egl_init(display);
|
wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL,
|
||||||
|
WL_SHM_FORMAT_ARGB8888);
|
||||||
|
|
||||||
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
struct wl_surface *surface = wl_compositor_create_surface(compositor);
|
||||||
struct xdg_surface *xdg_surface =
|
struct xdg_surface *xdg_surface =
|
||||||
|
@ -236,8 +239,7 @@ int main(int argc, char **argv) {
|
||||||
wl_surface_commit(surface);
|
wl_surface_commit(surface);
|
||||||
|
|
||||||
egl_window = wl_egl_window_create(surface, width, height);
|
egl_window = wl_egl_window_create(surface, width, height);
|
||||||
egl_surface = eglCreatePlatformWindowSurfaceEXT(
|
egl_surface = wlr_egl_create_surface(&egl, egl_window);
|
||||||
egl_display, egl_config, egl_window, NULL);
|
|
||||||
|
|
||||||
wl_display_roundtrip(display);
|
wl_display_roundtrip(display);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
#define _POSIX_C_SOURCE 200112L
|
#define _POSIX_C_SOURCE 200112L
|
||||||
#include <drm_fourcc.h>
|
#include <GLES2/gl2.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -7,16 +7,15 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server-protocol.h>
|
||||||
|
#include <wayland-server.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/render/allocator.h>
|
|
||||||
#include <wlr/render/wlr_renderer.h>
|
|
||||||
#include <wlr/types/wlr_output.h>
|
#include <wlr/types/wlr_output.h>
|
||||||
|
#include <wlr/render/wlr_renderer.h>
|
||||||
|
#include <wlr/types/wlr_list.h>
|
||||||
#include <wlr/types/wlr_input_device.h>
|
#include <wlr/types/wlr_input_device.h>
|
||||||
#include <wlr/types/wlr_keyboard.h>
|
|
||||||
#include <wlr/types/wlr_matrix.h>
|
#include <wlr/types/wlr_matrix.h>
|
||||||
#include <wlr/types/wlr_touch.h>
|
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <xkbcommon/xkbcommon.h>
|
#include <xkbcommon/xkbcommon.h>
|
||||||
#include "cat.h"
|
#include "cat.h"
|
||||||
|
@ -24,7 +23,6 @@
|
||||||
struct sample_state {
|
struct sample_state {
|
||||||
struct wl_display *display;
|
struct wl_display *display;
|
||||||
struct wlr_renderer *renderer;
|
struct wlr_renderer *renderer;
|
||||||
struct wlr_allocator *allocator;
|
|
||||||
struct wlr_texture *cat_texture;
|
struct wlr_texture *cat_texture;
|
||||||
struct wl_list touch_points;
|
struct wl_list touch_points;
|
||||||
struct timespec last_frame;
|
struct timespec last_frame;
|
||||||
|
@ -75,20 +73,23 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
|
||||||
int32_t width, height;
|
int32_t width, height;
|
||||||
wlr_output_effective_resolution(wlr_output, &width, &height);
|
wlr_output_effective_resolution(wlr_output, &width, &height);
|
||||||
|
|
||||||
wlr_output_attach_render(wlr_output, NULL);
|
wlr_output_make_current(wlr_output, NULL);
|
||||||
wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height);
|
wlr_renderer_begin(sample->renderer, wlr_output->width, wlr_output->height);
|
||||||
wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1});
|
wlr_renderer_clear(sample->renderer, (float[]){0.25f, 0.25f, 0.25f, 1});
|
||||||
|
|
||||||
|
int tex_width, tex_height;
|
||||||
|
wlr_texture_get_size(sample->cat_texture, &tex_width, &tex_height);
|
||||||
|
|
||||||
struct touch_point *p;
|
struct touch_point *p;
|
||||||
wl_list_for_each(p, &sample->touch_points, link) {
|
wl_list_for_each(p, &sample->touch_points, link) {
|
||||||
int x = (int)(p->x * width) - sample->cat_texture->width / 2;
|
int x = (int)(p->x * width) - tex_width / 2;
|
||||||
int y = (int)(p->y * height) - sample->cat_texture->height / 2;
|
int y = (int)(p->y * height) - tex_height / 2;
|
||||||
wlr_render_texture(sample->renderer, sample->cat_texture,
|
wlr_render_texture(sample->renderer, sample->cat_texture,
|
||||||
wlr_output->transform_matrix, x, y, 1.0f);
|
wlr_output->transform_matrix, x, y, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_renderer_end(sample->renderer);
|
wlr_renderer_end(sample->renderer);
|
||||||
wlr_output_commit(wlr_output);
|
wlr_output_swap_buffers(wlr_output, NULL, NULL);
|
||||||
sample->last_frame = now;
|
sample->last_frame = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,36 +141,30 @@ static void touch_destroy_notify(struct wl_listener *listener, void *data) {
|
||||||
free(tstate);
|
free(tstate);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void output_remove_notify(struct wl_listener *listener, void *data) {
|
void output_remove_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
struct sample_output *sample_output = wl_container_of(listener, sample_output, destroy);
|
||||||
wl_list_remove(&sample_output->frame.link);
|
wl_list_remove(&sample_output->frame.link);
|
||||||
wl_list_remove(&sample_output->destroy.link);
|
wl_list_remove(&sample_output->destroy.link);
|
||||||
free(sample_output);
|
free(sample_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void new_output_notify(struct wl_listener *listener, void *data) {
|
void new_output_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_output *output = data;
|
struct wlr_output *output = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
struct sample_state *sample = wl_container_of(listener, sample, new_output);
|
||||||
|
|
||||||
wlr_output_init_render(output, sample->allocator, sample->renderer);
|
|
||||||
|
|
||||||
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
|
struct sample_output *sample_output = calloc(1, sizeof(struct sample_output));
|
||||||
|
if (!wl_list_empty(&output->modes)) {
|
||||||
|
struct wlr_output_mode *mode = wl_container_of(output->modes.prev, mode, link);
|
||||||
|
wlr_output_set_mode(output, mode);
|
||||||
|
}
|
||||||
sample_output->output = output;
|
sample_output->output = output;
|
||||||
sample_output->sample = sample;
|
sample_output->sample = sample;
|
||||||
wl_signal_add(&output->events.frame, &sample_output->frame);
|
wl_signal_add(&output->events.frame, &sample_output->frame);
|
||||||
sample_output->frame.notify = output_frame_notify;
|
sample_output->frame.notify = output_frame_notify;
|
||||||
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
wl_signal_add(&output->events.destroy, &sample_output->destroy);
|
||||||
sample_output->destroy.notify = output_remove_notify;
|
sample_output->destroy.notify = output_remove_notify;
|
||||||
|
|
||||||
struct wlr_output_mode *mode = wlr_output_preferred_mode(output);
|
|
||||||
if (mode != NULL) {
|
|
||||||
wlr_output_set_mode(output, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_output_commit(output);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, key);
|
||||||
struct sample_state *sample = keyboard->sample;
|
struct sample_state *sample = keyboard->sample;
|
||||||
struct wlr_event_keyboard_key *event = data;
|
struct wlr_event_keyboard_key *event = data;
|
||||||
|
@ -185,14 +180,14 @@ static void keyboard_key_notify(struct wl_listener *listener, void *data) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
|
||||||
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
struct sample_keyboard *keyboard = wl_container_of(listener, keyboard, destroy);
|
||||||
wl_list_remove(&keyboard->destroy.link);
|
wl_list_remove(&keyboard->destroy.link);
|
||||||
wl_list_remove(&keyboard->key.link);
|
wl_list_remove(&keyboard->key.link);
|
||||||
free(keyboard);
|
free(keyboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void new_input_notify(struct wl_listener *listener, void *data) {
|
void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
struct wlr_input_device *device = data;
|
struct wlr_input_device *device = data;
|
||||||
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
struct sample_state *sample = wl_container_of(listener, sample, new_input);
|
||||||
switch (device->type) {
|
switch (device->type) {
|
||||||
|
@ -204,12 +199,18 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
|
||||||
keyboard->destroy.notify = keyboard_destroy_notify;
|
keyboard->destroy.notify = keyboard_destroy_notify;
|
||||||
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
|
||||||
keyboard->key.notify = keyboard_key_notify;
|
keyboard->key.notify = keyboard_key_notify;
|
||||||
|
struct xkb_rule_names rules = { 0 };
|
||||||
|
rules.rules = getenv("XKB_DEFAULT_RULES");
|
||||||
|
rules.model = getenv("XKB_DEFAULT_MODEL");
|
||||||
|
rules.layout = getenv("XKB_DEFAULT_LAYOUT");
|
||||||
|
rules.variant = getenv("XKB_DEFAULT_VARIANT");
|
||||||
|
rules.options = getenv("XKB_DEFAULT_OPTIONS");
|
||||||
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
wlr_log(WLR_ERROR, "Failed to create XKB context");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL,
|
struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules,
|
||||||
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
XKB_KEYMAP_COMPILE_NO_FLAGS);
|
||||||
if (!keymap) {
|
if (!keymap) {
|
||||||
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
|
||||||
|
@ -248,7 +249,7 @@ int main(int argc, char *argv[]) {
|
||||||
wl_list_init(&state.touch_points);
|
wl_list_init(&state.touch_points);
|
||||||
wl_list_init(&state.touch);
|
wl_list_init(&state.touch);
|
||||||
|
|
||||||
struct wlr_backend *wlr = wlr_backend_autocreate(display);
|
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
|
||||||
if (!wlr) {
|
if (!wlr) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
@ -259,21 +260,20 @@ int main(int argc, char *argv[]) {
|
||||||
state.new_input.notify = new_input_notify;
|
state.new_input.notify = new_input_notify;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
|
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
|
||||||
|
|
||||||
state.renderer = wlr_renderer_autocreate(wlr);
|
|
||||||
|
state.renderer = wlr_backend_get_renderer(wlr);
|
||||||
if (!state.renderer) {
|
if (!state.renderer) {
|
||||||
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
state.cat_texture = wlr_texture_from_pixels(state.renderer,
|
state.cat_texture = wlr_texture_from_pixels(state.renderer,
|
||||||
DRM_FORMAT_ARGB8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
|
WL_SHM_FORMAT_ARGB8888, cat_tex.width * 4, cat_tex.width, cat_tex.height,
|
||||||
cat_tex.pixel_data);
|
cat_tex.pixel_data);
|
||||||
if (!state.cat_texture) {
|
if (!state.cat_texture) {
|
||||||
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
|
|
||||||
|
|
||||||
if (!wlr_backend_start(wlr)) {
|
if (!wlr_backend_start(wlr)) {
|
||||||
wlr_log(WLR_ERROR, "Failed to start backend");
|
wlr_log(WLR_ERROR, "Failed to start backend");
|
||||||
wlr_backend_destroy(wlr);
|
wlr_backend_destroy(wlr);
|
||||||
|
|
|
@ -1,138 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2019 Josef Gajdusek
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
||||||
* copy of this software and associated documentation files (the "Software"),
|
|
||||||
* to deal in the Software without restriction, including without limitation
|
|
||||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
||||||
* and/or sell copies of the Software, and to permit persons to whom the
|
|
||||||
* Software is furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice (including the next
|
|
||||||
* paragraph) shall be included in all copies or substantial portions of the
|
|
||||||
* Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
* DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200112L
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <wayland-client-protocol.h>
|
|
||||||
#include "wlr-virtual-pointer-unstable-v1-client-protocol.h"
|
|
||||||
|
|
||||||
static struct wl_seat *seat = NULL;
|
|
||||||
static struct zwlr_virtual_pointer_manager_v1 *pointer_manager = NULL;
|
|
||||||
|
|
||||||
static void handle_global(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name, const char *interface, uint32_t version) {
|
|
||||||
if (strcmp(interface, wl_seat_interface.name) == 0) {
|
|
||||||
seat = wl_registry_bind(registry, name,
|
|
||||||
&wl_seat_interface, version);
|
|
||||||
} else if (strcmp(interface,
|
|
||||||
zwlr_virtual_pointer_manager_v1_interface.name) == 0) {
|
|
||||||
pointer_manager = wl_registry_bind(registry, name,
|
|
||||||
&zwlr_virtual_pointer_manager_v1_interface, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_global_remove(void *data, struct wl_registry *registry,
|
|
||||||
uint32_t name) {
|
|
||||||
// Who cares?
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct wl_registry_listener registry_listener = {
|
|
||||||
.global = handle_global,
|
|
||||||
.global_remove = handle_global_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
if (argc < 2) {
|
|
||||||
fprintf(stderr, "Usage: ./virtual-pointer <subcommand>\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
struct wl_display * display = wl_display_connect(NULL);
|
|
||||||
if (display == NULL) {
|
|
||||||
perror("failed to create display");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wl_registry *registry = wl_display_get_registry(display);
|
|
||||||
wl_registry_add_listener(registry, ®istry_listener, NULL);
|
|
||||||
wl_display_roundtrip(display);
|
|
||||||
|
|
||||||
if (pointer_manager == NULL) {
|
|
||||||
fprintf(stderr, "compositor does not support wlr-virtual-pointer-unstable-v1\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct zwlr_virtual_pointer_v1 *pointer =
|
|
||||||
zwlr_virtual_pointer_manager_v1_create_virtual_pointer(
|
|
||||||
pointer_manager, seat);
|
|
||||||
|
|
||||||
const char *cmd = argv[1];
|
|
||||||
if (strcmp(cmd, "motion") == 0) {
|
|
||||||
if (argc < 4) {
|
|
||||||
fprintf(stderr, "Usage: ./virtual-pointer motion <dx> <dy>\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
wl_fixed_t dx = wl_fixed_from_double(atof(argv[2]));
|
|
||||||
wl_fixed_t dy = wl_fixed_from_double(atof(argv[3]));
|
|
||||||
zwlr_virtual_pointer_v1_motion(pointer, 0, dx, dy);
|
|
||||||
} else if (strcmp(cmd, "absolute") == 0) {
|
|
||||||
if (argc < 6) {
|
|
||||||
fprintf(stderr, "Usage: ./virtual-pointer absolute <x> <y> <x_extent> <y_extent>\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
uint32_t x = atoi(argv[2]);
|
|
||||||
uint32_t y = atoi(argv[3]);
|
|
||||||
uint32_t x_extent = atoi(argv[4]);
|
|
||||||
uint32_t y_extent = atoi(argv[5]);
|
|
||||||
zwlr_virtual_pointer_v1_motion_absolute(pointer, 0, x, y, x_extent, y_extent);
|
|
||||||
} else if (strcmp(cmd, "button") == 0) {
|
|
||||||
if (argc < 4) {
|
|
||||||
fprintf(stderr, "Usage: ./virtual-pointer button <button> press|release\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
uint32_t button = atoi(argv[2]);
|
|
||||||
bool press = !!strcmp(argv[3], "release");
|
|
||||||
zwlr_virtual_pointer_v1_button(pointer, 0, button, press);
|
|
||||||
} else if (strcmp(cmd, "axis") == 0) {
|
|
||||||
if (argc < 4) {
|
|
||||||
fprintf(stderr, "Usage: ./virtual-pointer axis <axis> <value>\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
uint32_t axis = atoi(argv[2]);
|
|
||||||
wl_fixed_t value = wl_fixed_from_double(atof(argv[3]));
|
|
||||||
zwlr_virtual_pointer_v1_axis(pointer, 0, axis, value);
|
|
||||||
zwlr_virtual_pointer_v1_axis_stop(pointer, 0, axis);
|
|
||||||
} else if (strcmp(cmd, "axis_discrete") == 0) {
|
|
||||||
if (argc < 5) {
|
|
||||||
fprintf(stderr, "Usage: ./virtual-pointer axis <axis> <value> <value_discrete>\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
uint32_t axis = atoi(argv[2]);
|
|
||||||
wl_fixed_t value = wl_fixed_from_double(atof(argv[3]));
|
|
||||||
uint32_t discrete = atoi(argv[4]);
|
|
||||||
zwlr_virtual_pointer_v1_axis_discrete(pointer, 0, axis, value, discrete);
|
|
||||||
zwlr_virtual_pointer_v1_axis_stop(pointer, 0, axis);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Invalid subcommand\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
zwlr_virtual_pointer_v1_frame(pointer);
|
|
||||||
zwlr_virtual_pointer_v1_destroy(pointer);
|
|
||||||
|
|
||||||
wl_display_flush(display);
|
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Generates a simple GL/EGL extension function loader.
|
||||||
|
#
|
||||||
|
# The input is a .txt file, with each function to load on its own line.
|
||||||
|
# If a line starts with a -, it is optional, and will not cause the loader
|
||||||
|
# to fail if it can't load the function. You'll need to check if that function
|
||||||
|
# is NULL before using it.
|
||||||
|
|
||||||
|
if [ $# -ne 2 ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
SPEC=$1
|
||||||
|
OUTDIR=$2
|
||||||
|
|
||||||
|
BASE=$(basename "$SPEC" .txt)
|
||||||
|
INCLUDE_GUARD=$(printf %s_%s_H "$OUTDIR" "$BASE" | tr -c [:alnum:] _ | tr [:lower:] [:upper:])
|
||||||
|
|
||||||
|
DECL=""
|
||||||
|
DEFN=""
|
||||||
|
LOADER=""
|
||||||
|
|
||||||
|
DECL_FMT='extern %s %s;'
|
||||||
|
DEFN_FMT='%s %s;'
|
||||||
|
LOADER_FMT='%s = (%s)eglGetProcAddress("%s");'
|
||||||
|
CHECK_FMT='if (!%s) {
|
||||||
|
wlr_log(WLR_ERROR, "Unable to load %s");
|
||||||
|
return false;
|
||||||
|
}'
|
||||||
|
|
||||||
|
while read -r COMMAND; do
|
||||||
|
OPTIONAL=0
|
||||||
|
FUNC_PTR_FMT='PFN%sPROC'
|
||||||
|
|
||||||
|
case $COMMAND in
|
||||||
|
-*)
|
||||||
|
OPTIONAL=1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case $COMMAND in
|
||||||
|
*WL)
|
||||||
|
FUNC_PTR_FMT='PFN%s'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
COMMAND=${COMMAND#-}
|
||||||
|
FUNC_PTR=$(printf "$FUNC_PTR_FMT" "$COMMAND" | tr [:lower:] [:upper:])
|
||||||
|
|
||||||
|
DECL="$DECL$(printf "\n$DECL_FMT" "$FUNC_PTR" "$COMMAND")"
|
||||||
|
DEFN="$DEFN$(printf "\n$DEFN_FMT" "$FUNC_PTR" "$COMMAND")"
|
||||||
|
LOADER="$LOADER$(printf "\n$LOADER_FMT" "$COMMAND" "$FUNC_PTR" "$COMMAND")"
|
||||||
|
|
||||||
|
if [ $OPTIONAL -eq 0 ]; then
|
||||||
|
LOADER="$LOADER$(printf "\n$CHECK_FMT" "$COMMAND" "$COMMAND")"
|
||||||
|
fi
|
||||||
|
done < "$SPEC"
|
||||||
|
|
||||||
|
cat > "$OUTDIR/$BASE.h" << EOF
|
||||||
|
#ifndef $INCLUDE_GUARD
|
||||||
|
#define $INCLUDE_GUARD
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <EGL/egl.h>
|
||||||
|
#include <EGL/eglext.h>
|
||||||
|
#include <GLES2/gl2.h>
|
||||||
|
#include <GLES2/gl2ext.h>
|
||||||
|
|
||||||
|
bool load_$BASE(void);
|
||||||
|
$DECL
|
||||||
|
|
||||||
|
#endif
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat > "$OUTDIR/$BASE.c" << EOF
|
||||||
|
#include <wlr/util/log.h>
|
||||||
|
#include "$BASE.h"
|
||||||
|
$DEFN
|
||||||
|
|
||||||
|
bool load_$BASE(void) {
|
||||||
|
static bool done = false;
|
||||||
|
if (done) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
$LOADER
|
||||||
|
|
||||||
|
done = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
EOF
|
|
@ -1,13 +0,0 @@
|
||||||
#ifndef BACKEND_WLR_BACKEND_H
|
|
||||||
#define BACKEND_WLR_BACKEND_H
|
|
||||||
|
|
||||||
#include <wlr/backend.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the supported buffer capabilities.
|
|
||||||
*
|
|
||||||
* This functions returns a bitfield of supported wlr_buffer_cap.
|
|
||||||
*/
|
|
||||||
uint32_t backend_get_buffer_caps(struct wlr_backend *backend);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,13 +0,0 @@
|
||||||
#ifndef BACKEND_DRM_CVT_H
|
|
||||||
#define BACKEND_DRM_CVT_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <xf86drmMode.h>
|
|
||||||
|
|
||||||
void generate_cvt_mode(drmModeModeInfo *mode, int hdisplay, int vdisplay,
|
|
||||||
float vrefresh, bool reduced, bool interlaced);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,54 +1,67 @@
|
||||||
#ifndef BACKEND_DRM_DRM_H
|
#ifndef BACKEND_DRM_DRM_H
|
||||||
#define BACKEND_DRM_DRM_H
|
#define BACKEND_DRM_DRM_H
|
||||||
|
|
||||||
|
#include <EGL/egl.h>
|
||||||
|
#include <gbm.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server.h>
|
||||||
#include <wayland-util.h>
|
#include <wayland-util.h>
|
||||||
#include <wlr/backend/drm.h>
|
#include <wlr/backend/drm.h>
|
||||||
#include <wlr/backend/session.h>
|
#include <wlr/backend/session.h>
|
||||||
#include <wlr/render/drm_format_set.h>
|
#include <wlr/render/egl.h>
|
||||||
#include <xf86drmMode.h>
|
#include <xf86drmMode.h>
|
||||||
#include "backend/drm/iface.h"
|
#include "iface.h"
|
||||||
#include "backend/drm/properties.h"
|
#include "properties.h"
|
||||||
#include "backend/drm/renderer.h"
|
#include "renderer.h"
|
||||||
|
|
||||||
struct wlr_drm_plane {
|
struct wlr_drm_plane {
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
|
|
||||||
/* Only initialized on multi-GPU setups */
|
uint32_t possible_crtcs;
|
||||||
|
|
||||||
|
struct wlr_drm_surface surf;
|
||||||
struct wlr_drm_surface mgpu_surf;
|
struct wlr_drm_surface mgpu_surf;
|
||||||
|
|
||||||
/* Buffer to be submitted to the kernel on the next page-flip */
|
uint32_t drm_format; // ARGB8888 or XRGB8888
|
||||||
struct wlr_drm_fb *pending_fb;
|
|
||||||
/* Buffer submitted to the kernel, will be presented on next vblank */
|
|
||||||
struct wlr_drm_fb *queued_fb;
|
|
||||||
/* Buffer currently displayed on screen */
|
|
||||||
struct wlr_drm_fb *current_fb;
|
|
||||||
|
|
||||||
struct wlr_drm_format_set formats;
|
// Only used by cursor
|
||||||
|
float matrix[9];
|
||||||
|
bool cursor_enabled;
|
||||||
|
int32_t cursor_hotspot_x, cursor_hotspot_y;
|
||||||
|
|
||||||
union wlr_drm_plane_props props;
|
union wlr_drm_plane_props props;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_drm_crtc {
|
struct wlr_drm_crtc {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
struct wlr_drm_lease *lease;
|
|
||||||
|
|
||||||
// Atomic modesetting only
|
// Atomic modesetting only
|
||||||
uint32_t mode_id;
|
uint32_t mode_id;
|
||||||
uint32_t gamma_lut;
|
uint32_t gamma_lut;
|
||||||
|
drmModeAtomicReq *atomic;
|
||||||
|
|
||||||
// Legacy only
|
// Legacy only
|
||||||
drmModeCrtc *legacy_crtc;
|
drmModeCrtc *legacy_crtc;
|
||||||
|
|
||||||
struct wlr_drm_plane *primary;
|
union {
|
||||||
struct wlr_drm_plane *cursor;
|
struct {
|
||||||
|
struct wlr_drm_plane *overlay;
|
||||||
|
struct wlr_drm_plane *primary;
|
||||||
|
struct wlr_drm_plane *cursor;
|
||||||
|
};
|
||||||
|
struct wlr_drm_plane *planes[3];
|
||||||
|
};
|
||||||
|
|
||||||
union wlr_drm_crtc_props props;
|
union wlr_drm_crtc_props props;
|
||||||
|
|
||||||
|
struct wl_list connectors;
|
||||||
|
|
||||||
|
uint16_t *gamma_table;
|
||||||
|
size_t gamma_table_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_drm_backend {
|
struct wlr_drm_backend {
|
||||||
|
@ -57,45 +70,54 @@ struct wlr_drm_backend {
|
||||||
struct wlr_drm_backend *parent;
|
struct wlr_drm_backend *parent;
|
||||||
const struct wlr_drm_interface *iface;
|
const struct wlr_drm_interface *iface;
|
||||||
clockid_t clock;
|
clockid_t clock;
|
||||||
bool addfb2_modifiers;
|
|
||||||
|
|
||||||
int fd;
|
int fd;
|
||||||
char *name;
|
|
||||||
struct wlr_device *dev;
|
|
||||||
|
|
||||||
size_t num_crtcs;
|
size_t num_crtcs;
|
||||||
struct wlr_drm_crtc *crtcs;
|
struct wlr_drm_crtc *crtcs;
|
||||||
|
size_t num_planes;
|
||||||
|
struct wlr_drm_plane *planes;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
size_t num_overlay_planes;
|
||||||
|
size_t num_primary_planes;
|
||||||
|
size_t num_cursor_planes;
|
||||||
|
};
|
||||||
|
size_t num_type_planes[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
struct wlr_drm_plane *overlay_planes;
|
||||||
|
struct wlr_drm_plane *primary_planes;
|
||||||
|
struct wlr_drm_plane *cursor_planes;
|
||||||
|
};
|
||||||
|
struct wlr_drm_plane *type_planes[3];
|
||||||
|
};
|
||||||
|
|
||||||
struct wl_display *display;
|
struct wl_display *display;
|
||||||
struct wl_event_source *drm_event;
|
struct wl_event_source *drm_event;
|
||||||
|
|
||||||
struct wl_listener display_destroy;
|
struct wl_listener display_destroy;
|
||||||
struct wl_listener session_destroy;
|
struct wl_listener session_signal;
|
||||||
struct wl_listener session_active;
|
struct wl_listener drm_invalidated;
|
||||||
struct wl_listener parent_destroy;
|
|
||||||
struct wl_listener dev_change;
|
|
||||||
struct wl_listener dev_remove;
|
|
||||||
|
|
||||||
struct wl_list fbs; // wlr_drm_fb.link
|
|
||||||
struct wl_list outputs;
|
struct wl_list outputs;
|
||||||
|
|
||||||
/* Only initialized on multi-GPU setups */
|
struct wlr_drm_renderer renderer;
|
||||||
struct wlr_drm_renderer mgpu_renderer;
|
|
||||||
|
|
||||||
struct wlr_session *session;
|
struct wlr_session *session;
|
||||||
|
|
||||||
uint64_t cursor_width, cursor_height;
|
|
||||||
|
|
||||||
struct wlr_drm_format_set mgpu_formats;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum wlr_drm_connector_status {
|
enum wlr_drm_connector_state {
|
||||||
// Connector is available but no output is plugged in
|
// Connector is available but no output is plugged in
|
||||||
WLR_DRM_CONN_DISCONNECTED,
|
WLR_DRM_CONN_DISCONNECTED,
|
||||||
// An output just has been plugged in and is waiting for a modeset
|
// An output just has been plugged in and is waiting for a modeset
|
||||||
WLR_DRM_CONN_NEEDS_MODESET,
|
WLR_DRM_CONN_NEEDS_MODESET,
|
||||||
WLR_DRM_CONN_CLEANUP,
|
WLR_DRM_CONN_CLEANUP,
|
||||||
WLR_DRM_CONN_CONNECTED,
|
WLR_DRM_CONN_CONNECTED,
|
||||||
|
// Connector disappeared, waiting for being destroyed on next page-flip
|
||||||
|
WLR_DRM_CONN_DISAPPEARED,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_drm_mode {
|
struct wlr_drm_mode {
|
||||||
|
@ -103,43 +125,27 @@ struct wlr_drm_mode {
|
||||||
drmModeModeInfo drm_mode;
|
drmModeModeInfo drm_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_drm_connector_state {
|
|
||||||
const struct wlr_output_state *base;
|
|
||||||
bool modeset;
|
|
||||||
bool active;
|
|
||||||
drmModeModeInfo mode;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wlr_drm_connector {
|
struct wlr_drm_connector {
|
||||||
struct wlr_output output; // only valid if status != DISCONNECTED
|
struct wlr_output output;
|
||||||
|
|
||||||
struct wlr_drm_backend *backend;
|
enum wlr_drm_connector_state state;
|
||||||
char name[24];
|
struct wlr_output_mode *desired_mode;
|
||||||
enum wlr_drm_connector_status status;
|
|
||||||
bool desired_enabled;
|
bool desired_enabled;
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
struct wlr_drm_lease *lease;
|
|
||||||
|
|
||||||
struct wlr_drm_crtc *crtc;
|
struct wlr_drm_crtc *crtc;
|
||||||
uint32_t possible_crtcs;
|
uint32_t possible_crtc;
|
||||||
|
|
||||||
union wlr_drm_connector_props props;
|
union wlr_drm_connector_props props;
|
||||||
|
|
||||||
bool cursor_enabled;
|
uint32_t width, height;
|
||||||
int cursor_x, cursor_y;
|
int32_t cursor_x, cursor_y;
|
||||||
int cursor_width, cursor_height;
|
|
||||||
int cursor_hotspot_x, cursor_hotspot_y;
|
|
||||||
|
|
||||||
|
drmModeCrtc *old_crtc;
|
||||||
|
|
||||||
|
bool pageflip_pending;
|
||||||
|
struct wl_event_source *retry_pageflip;
|
||||||
struct wl_list link;
|
struct wl_list link;
|
||||||
|
|
||||||
/* CRTC ID if a page-flip is pending, zero otherwise.
|
|
||||||
*
|
|
||||||
* We've asked for a state change in the kernel, and yet to receive a
|
|
||||||
* notification for its completion. Currently, the kernel only has a
|
|
||||||
* queue length of 1, and no way to modify your submissions after
|
|
||||||
* they're sent.
|
|
||||||
*/
|
|
||||||
uint32_t pending_page_flip_crtc;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_drm_backend *get_drm_backend_from_backend(
|
struct wlr_drm_backend *get_drm_backend_from_backend(
|
||||||
|
@ -147,24 +153,13 @@ struct wlr_drm_backend *get_drm_backend_from_backend(
|
||||||
bool check_drm_features(struct wlr_drm_backend *drm);
|
bool check_drm_features(struct wlr_drm_backend *drm);
|
||||||
bool init_drm_resources(struct wlr_drm_backend *drm);
|
bool init_drm_resources(struct wlr_drm_backend *drm);
|
||||||
void finish_drm_resources(struct wlr_drm_backend *drm);
|
void finish_drm_resources(struct wlr_drm_backend *drm);
|
||||||
void scan_drm_connectors(struct wlr_drm_backend *state,
|
void restore_drm_outputs(struct wlr_drm_backend *drm);
|
||||||
struct wlr_device_hotplug_event *event);
|
void scan_drm_connectors(struct wlr_drm_backend *state);
|
||||||
void scan_drm_leases(struct wlr_drm_backend *drm);
|
|
||||||
int handle_drm_event(int fd, uint32_t mask, void *data);
|
int handle_drm_event(int fd, uint32_t mask, void *data);
|
||||||
void destroy_drm_connector(struct wlr_drm_connector *conn);
|
bool enable_drm_connector(struct wlr_output *output, bool enable);
|
||||||
bool drm_connector_commit_state(struct wlr_drm_connector *conn,
|
bool set_drm_connector_gamma(struct wlr_output *output, size_t size,
|
||||||
const struct wlr_output_state *state);
|
const uint16_t *r, const uint16_t *g, const uint16_t *b);
|
||||||
bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn);
|
bool drm_connector_set_mode(struct wlr_output *output,
|
||||||
bool drm_connector_supports_vrr(struct wlr_drm_connector *conn);
|
struct wlr_output_mode *mode);
|
||||||
size_t drm_crtc_get_gamma_lut_size(struct wlr_drm_backend *drm,
|
|
||||||
struct wlr_drm_crtc *crtc);
|
|
||||||
void drm_lease_destroy(struct wlr_drm_lease *lease);
|
|
||||||
|
|
||||||
struct wlr_drm_fb *plane_get_next_fb(struct wlr_drm_plane *plane);
|
|
||||||
|
|
||||||
#define wlr_drm_conn_log(conn, verb, fmt, ...) \
|
|
||||||
wlr_log(verb, "connector %s: " fmt, conn->name, ##__VA_ARGS__)
|
|
||||||
#define wlr_drm_conn_log_errno(conn, verb, fmt, ...) \
|
|
||||||
wlr_log_errno(verb, "connector %s: " fmt, conn->name, ##__VA_ARGS__)
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#ifndef BACKEND_DRM_IFACE_H
|
#ifndef BACKEND_DRM_IFACE_H
|
||||||
#define BACKEND_DRM_IFACE_H
|
#define BACKEND_DRM_IFACE_H
|
||||||
|
|
||||||
|
#include <gbm.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <xf86drm.h>
|
#include <xf86drm.h>
|
||||||
|
@ -9,20 +10,32 @@
|
||||||
struct wlr_drm_backend;
|
struct wlr_drm_backend;
|
||||||
struct wlr_drm_connector;
|
struct wlr_drm_connector;
|
||||||
struct wlr_drm_crtc;
|
struct wlr_drm_crtc;
|
||||||
struct wlr_drm_connector_state;
|
|
||||||
|
|
||||||
// Used to provide atomic or legacy DRM functions
|
// Used to provide atomic or legacy DRM functions
|
||||||
struct wlr_drm_interface {
|
struct wlr_drm_interface {
|
||||||
// Commit all pending changes on a CRTC.
|
// Enable or disable DPMS for connector
|
||||||
bool (*crtc_commit)(struct wlr_drm_connector *conn,
|
bool (*conn_enable)(struct wlr_drm_backend *drm,
|
||||||
const struct wlr_drm_connector_state *state, uint32_t flags,
|
struct wlr_drm_connector *conn, bool enable);
|
||||||
bool test_only);
|
// Pageflip on crtc. If mode is non-NULL perform a full modeset using it.
|
||||||
|
bool (*crtc_pageflip)(struct wlr_drm_backend *drm,
|
||||||
|
struct wlr_drm_connector *conn, struct wlr_drm_crtc *crtc,
|
||||||
|
uint32_t fb_id, drmModeModeInfo *mode);
|
||||||
|
// Enable the cursor buffer on crtc. Set bo to NULL to disable
|
||||||
|
bool (*crtc_set_cursor)(struct wlr_drm_backend *drm,
|
||||||
|
struct wlr_drm_crtc *crtc, struct gbm_bo *bo);
|
||||||
|
// Move the cursor on crtc
|
||||||
|
bool (*crtc_move_cursor)(struct wlr_drm_backend *drm,
|
||||||
|
struct wlr_drm_crtc *crtc, int x, int y);
|
||||||
|
// Set the gamma lut on crtc
|
||||||
|
bool (*crtc_set_gamma)(struct wlr_drm_backend *drm,
|
||||||
|
struct wlr_drm_crtc *crtc, size_t size,
|
||||||
|
uint16_t *r, uint16_t *g, uint16_t *b);
|
||||||
|
// Get the gamma lut size of a crtc
|
||||||
|
size_t (*crtc_get_gamma_size)(struct wlr_drm_backend *drm,
|
||||||
|
struct wlr_drm_crtc *crtc);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const struct wlr_drm_interface atomic_iface;
|
extern const struct wlr_drm_interface atomic_iface;
|
||||||
extern const struct wlr_drm_interface legacy_iface;
|
extern const struct wlr_drm_interface legacy_iface;
|
||||||
|
|
||||||
bool drm_legacy_crtc_set_gamma(struct wlr_drm_backend *drm,
|
|
||||||
struct wlr_drm_crtc *crtc, size_t size, uint16_t *lut);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
#ifndef BACKEND_DRM_MONITOR_H
|
|
||||||
#define BACKEND_DRM_MONITOR_H
|
|
||||||
|
|
||||||
#include <wlr/backend/drm.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper to create new DRM sub-backends on GPU hotplug.
|
|
||||||
*/
|
|
||||||
struct wlr_drm_backend_monitor {
|
|
||||||
struct wlr_backend *multi;
|
|
||||||
struct wlr_backend *primary_drm;
|
|
||||||
struct wlr_session *session;
|
|
||||||
|
|
||||||
struct wl_listener multi_destroy;
|
|
||||||
struct wl_listener primary_drm_destroy;
|
|
||||||
struct wl_listener session_destroy;
|
|
||||||
struct wl_listener session_add_drm_card;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wlr_drm_backend_monitor *drm_backend_monitor_create(
|
|
||||||
struct wlr_backend *multi, struct wlr_backend *primary_drm,
|
|
||||||
struct wlr_session *session);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -16,10 +16,6 @@ union wlr_drm_connector_props {
|
||||||
uint32_t dpms;
|
uint32_t dpms;
|
||||||
uint32_t link_status; // not guaranteed to exist
|
uint32_t link_status; // not guaranteed to exist
|
||||||
uint32_t path;
|
uint32_t path;
|
||||||
uint32_t vrr_capable; // not guaranteed to exist
|
|
||||||
uint32_t subconnector; // not guaranteed to exist
|
|
||||||
uint32_t non_desktop;
|
|
||||||
uint32_t panel_orientation; // not guaranteed to exist
|
|
||||||
|
|
||||||
// atomic-modesetting only
|
// atomic-modesetting only
|
||||||
|
|
||||||
|
@ -31,14 +27,15 @@ union wlr_drm_connector_props {
|
||||||
union wlr_drm_crtc_props {
|
union wlr_drm_crtc_props {
|
||||||
struct {
|
struct {
|
||||||
// Neither of these are guaranteed to exist
|
// Neither of these are guaranteed to exist
|
||||||
uint32_t vrr_enabled;
|
uint32_t rotation;
|
||||||
uint32_t gamma_lut;
|
uint32_t scaling_mode;
|
||||||
uint32_t gamma_lut_size;
|
|
||||||
|
|
||||||
// atomic-modesetting only
|
// atomic-modesetting only
|
||||||
|
|
||||||
uint32_t active;
|
uint32_t active;
|
||||||
uint32_t mode_id;
|
uint32_t mode_id;
|
||||||
|
uint32_t gamma_lut;
|
||||||
|
uint32_t gamma_lut_size;
|
||||||
};
|
};
|
||||||
uint32_t props[6];
|
uint32_t props[6];
|
||||||
};
|
};
|
||||||
|
@ -47,7 +44,6 @@ union wlr_drm_plane_props {
|
||||||
struct {
|
struct {
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
uint32_t rotation; // Not guaranteed to exist
|
uint32_t rotation; // Not guaranteed to exist
|
||||||
uint32_t in_formats; // Not guaranteed to exist
|
|
||||||
|
|
||||||
// atomic-modesetting only
|
// atomic-modesetting only
|
||||||
|
|
||||||
|
@ -61,9 +57,8 @@ union wlr_drm_plane_props {
|
||||||
uint32_t crtc_h;
|
uint32_t crtc_h;
|
||||||
uint32_t fb_id;
|
uint32_t fb_id;
|
||||||
uint32_t crtc_id;
|
uint32_t crtc_id;
|
||||||
uint32_t fb_damage_clips;
|
|
||||||
};
|
};
|
||||||
uint32_t props[14];
|
uint32_t props[12];
|
||||||
};
|
};
|
||||||
|
|
||||||
bool get_drm_connector_props(int fd, uint32_t id,
|
bool get_drm_connector_props(int fd, uint32_t id,
|
||||||
|
@ -73,6 +68,5 @@ bool get_drm_plane_props(int fd, uint32_t id, union wlr_drm_plane_props *out);
|
||||||
|
|
||||||
bool get_drm_prop(int fd, uint32_t obj, uint32_t prop, uint64_t *ret);
|
bool get_drm_prop(int fd, uint32_t obj, uint32_t prop, uint64_t *ret);
|
||||||
void *get_drm_prop_blob(int fd, uint32_t obj, uint32_t prop, size_t *ret_len);
|
void *get_drm_prop_blob(int fd, uint32_t obj, uint32_t prop, size_t *ret_len);
|
||||||
char *get_drm_prop_enum(int fd, uint32_t obj, uint32_t prop);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef BACKEND_DRM_RENDERER_H
|
#ifndef BACKEND_DRM_RENDERER_H
|
||||||
#define BACKEND_DRM_RENDERER_H
|
#define BACKEND_DRM_RENDERER_H
|
||||||
|
|
||||||
|
#include <EGL/egl.h>
|
||||||
|
#include <gbm.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
|
@ -8,13 +10,15 @@
|
||||||
|
|
||||||
struct wlr_drm_backend;
|
struct wlr_drm_backend;
|
||||||
struct wlr_drm_plane;
|
struct wlr_drm_plane;
|
||||||
struct wlr_buffer;
|
|
||||||
|
|
||||||
struct wlr_drm_renderer {
|
struct wlr_drm_renderer {
|
||||||
struct wlr_drm_backend *backend;
|
int fd;
|
||||||
|
struct gbm_device *gbm;
|
||||||
|
struct wlr_egl egl;
|
||||||
|
|
||||||
|
uint32_t gbm_format;
|
||||||
|
|
||||||
struct wlr_renderer *wlr_rend;
|
struct wlr_renderer *wlr_rend;
|
||||||
struct wlr_allocator *allocator;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_drm_surface {
|
struct wlr_drm_surface {
|
||||||
|
@ -23,38 +27,33 @@ struct wlr_drm_surface {
|
||||||
uint32_t width;
|
uint32_t width;
|
||||||
uint32_t height;
|
uint32_t height;
|
||||||
|
|
||||||
struct wlr_swapchain *swapchain;
|
struct gbm_surface *gbm;
|
||||||
};
|
EGLSurface egl;
|
||||||
|
|
||||||
struct wlr_drm_fb {
|
struct gbm_bo *front;
|
||||||
struct wlr_buffer *wlr_buf;
|
struct gbm_bo *back;
|
||||||
struct wlr_addon addon;
|
|
||||||
struct wlr_drm_backend *backend;
|
|
||||||
struct wl_list link; // wlr_drm_backend.fbs
|
|
||||||
|
|
||||||
uint32_t id;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool init_drm_renderer(struct wlr_drm_backend *drm,
|
bool init_drm_renderer(struct wlr_drm_backend *drm,
|
||||||
struct wlr_drm_renderer *renderer);
|
struct wlr_drm_renderer *renderer, wlr_renderer_create_func_t create_render);
|
||||||
void finish_drm_renderer(struct wlr_drm_renderer *renderer);
|
void finish_drm_renderer(struct wlr_drm_renderer *renderer);
|
||||||
|
|
||||||
bool init_drm_surface(struct wlr_drm_surface *surf,
|
bool init_drm_surface(struct wlr_drm_surface *surf,
|
||||||
struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height,
|
struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height,
|
||||||
const struct wlr_drm_format *drm_format);
|
uint32_t format, uint32_t flags);
|
||||||
|
|
||||||
bool drm_fb_import(struct wlr_drm_fb **fb, struct wlr_drm_backend *drm,
|
bool init_drm_plane_surfaces(struct wlr_drm_plane *plane,
|
||||||
struct wlr_buffer *buf, const struct wlr_drm_format_set *formats);
|
struct wlr_drm_backend *drm, int32_t width, uint32_t height,
|
||||||
void drm_fb_destroy(struct wlr_drm_fb *fb);
|
uint32_t format);
|
||||||
|
|
||||||
void drm_fb_clear(struct wlr_drm_fb **fb);
|
void finish_drm_surface(struct wlr_drm_surface *surf);
|
||||||
void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old);
|
bool make_drm_surface_current(struct wlr_drm_surface *surf, int *buffer_age);
|
||||||
|
struct gbm_bo *swap_drm_surface_buffers(struct wlr_drm_surface *surf,
|
||||||
struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf,
|
pixman_region32_t *damage);
|
||||||
struct wlr_buffer *buffer);
|
struct gbm_bo *get_drm_surface_front(struct wlr_drm_surface *surf);
|
||||||
|
void post_drm_surface(struct wlr_drm_surface *surf);
|
||||||
struct wlr_drm_format *drm_plane_pick_render_format(
|
struct gbm_bo *copy_drm_surface_mgpu(struct wlr_drm_surface *dest,
|
||||||
struct wlr_drm_plane *plane, struct wlr_drm_renderer *renderer);
|
struct gbm_bo *src);
|
||||||
void drm_plane_finish_surface(struct wlr_drm_plane *plane);
|
bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -13,6 +13,8 @@ void parse_edid(struct wlr_output *restrict output, size_t len,
|
||||||
const uint8_t *data);
|
const uint8_t *data);
|
||||||
// Returns the string representation of a DRM output type
|
// Returns the string representation of a DRM output type
|
||||||
const char *conn_get_name(uint32_t type_id);
|
const char *conn_get_name(uint32_t type_id);
|
||||||
|
// Returns the DRM framebuffer id for a gbm_bo
|
||||||
|
uint32_t get_fb_for_bo(struct gbm_bo *bo, uint32_t drm_format);
|
||||||
|
|
||||||
// Part of match_obj
|
// Part of match_obj
|
||||||
enum {
|
enum {
|
||||||
|
|
|
@ -8,9 +8,10 @@
|
||||||
|
|
||||||
struct wlr_headless_backend {
|
struct wlr_headless_backend {
|
||||||
struct wlr_backend backend;
|
struct wlr_backend backend;
|
||||||
|
struct wlr_egl egl;
|
||||||
|
struct wlr_renderer *renderer;
|
||||||
struct wl_display *display;
|
struct wl_display *display;
|
||||||
struct wl_list outputs;
|
struct wl_list outputs;
|
||||||
size_t last_output_num;
|
|
||||||
struct wl_list input_devices;
|
struct wl_list input_devices;
|
||||||
struct wl_listener display_destroy;
|
struct wl_listener display_destroy;
|
||||||
bool started;
|
bool started;
|
||||||
|
@ -22,13 +23,14 @@ struct wlr_headless_output {
|
||||||
struct wlr_headless_backend *backend;
|
struct wlr_headless_backend *backend;
|
||||||
struct wl_list link;
|
struct wl_list link;
|
||||||
|
|
||||||
|
void *egl_surface;
|
||||||
struct wl_event_source *frame_timer;
|
struct wl_event_source *frame_timer;
|
||||||
int frame_delay; // ms
|
int frame_delay; // ms
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_headless_input_device {
|
struct wlr_headless_input_device {
|
||||||
struct wlr_input_device wlr_input_device;
|
struct wlr_input_device wlr_input_device;
|
||||||
struct wl_list link;
|
|
||||||
struct wlr_headless_backend *backend;
|
struct wlr_headless_backend *backend;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <wlr/backend/libinput.h>
|
#include <wlr/backend/libinput.h>
|
||||||
#include <wlr/interfaces/wlr_input_device.h>
|
#include <wlr/interfaces/wlr_input_device.h>
|
||||||
#include <wlr/types/wlr_input_device.h>
|
#include <wlr/types/wlr_input_device.h>
|
||||||
|
#include <wlr/types/wlr_list.h>
|
||||||
|
|
||||||
struct wlr_libinput_backend {
|
struct wlr_libinput_backend {
|
||||||
struct wlr_backend backend;
|
struct wlr_backend backend;
|
||||||
|
@ -18,15 +19,14 @@ struct wlr_libinput_backend {
|
||||||
struct wl_event_source *input_event;
|
struct wl_event_source *input_event;
|
||||||
|
|
||||||
struct wl_listener display_destroy;
|
struct wl_listener display_destroy;
|
||||||
struct wl_listener session_destroy;
|
|
||||||
struct wl_listener session_signal;
|
struct wl_listener session_signal;
|
||||||
|
|
||||||
struct wl_array wlr_device_lists; // struct wl_list *
|
struct wlr_list wlr_device_lists; // list of struct wl_list
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_libinput_input_device {
|
struct wlr_libinput_input_device {
|
||||||
struct wlr_input_device wlr_input_device;
|
struct wlr_input_device wlr_input_device;
|
||||||
struct wl_list link;
|
|
||||||
struct libinput_device *handle;
|
struct libinput_device *handle;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -66,10 +66,6 @@ void handle_pointer_pinch_update(struct libinput_event *event,
|
||||||
struct libinput_device *device);
|
struct libinput_device *device);
|
||||||
void handle_pointer_pinch_end(struct libinput_event *event,
|
void handle_pointer_pinch_end(struct libinput_event *event,
|
||||||
struct libinput_device *device);
|
struct libinput_device *device);
|
||||||
void handle_pointer_hold_begin(struct libinput_event *event,
|
|
||||||
struct libinput_device *device);
|
|
||||||
void handle_pointer_hold_end(struct libinput_event *event,
|
|
||||||
struct libinput_device *device);
|
|
||||||
|
|
||||||
struct wlr_switch *create_libinput_switch(
|
struct wlr_switch *create_libinput_switch(
|
||||||
struct libinput_device *device);
|
struct libinput_device *device);
|
||||||
|
@ -86,8 +82,6 @@ void handle_touch_motion(struct libinput_event *event,
|
||||||
struct libinput_device *device);
|
struct libinput_device *device);
|
||||||
void handle_touch_cancel(struct libinput_event *event,
|
void handle_touch_cancel(struct libinput_event *event,
|
||||||
struct libinput_device *device);
|
struct libinput_device *device);
|
||||||
void handle_touch_frame(struct libinput_event *event,
|
|
||||||
struct libinput_device *device);
|
|
||||||
|
|
||||||
struct wlr_tablet *create_libinput_tablet(
|
struct wlr_tablet *create_libinput_tablet(
|
||||||
struct libinput_device *device);
|
struct libinput_device *device);
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef BACKEND_NOOP_H
|
||||||
|
#define BACKEND_NOOP_H
|
||||||
|
|
||||||
|
#include <wlr/backend/noop.h>
|
||||||
|
#include <wlr/backend/interface.h>
|
||||||
|
|
||||||
|
struct wlr_noop_backend {
|
||||||
|
struct wlr_backend backend;
|
||||||
|
struct wl_display *display;
|
||||||
|
struct wl_list outputs;
|
||||||
|
bool started;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_noop_output {
|
||||||
|
struct wlr_output wlr_output;
|
||||||
|
|
||||||
|
struct wlr_noop_backend *backend;
|
||||||
|
struct wl_list link;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wlr_noop_backend *noop_backend_from_backend(
|
||||||
|
struct wlr_backend *wlr_backend);
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,12 @@
|
||||||
|
#ifndef BACKEND_SESSION_DIRECT_IPC_H
|
||||||
|
#define BACKEND_SESSION_DIRECT_IPC_H
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
int direct_ipc_open(int sock, const char *path);
|
||||||
|
void direct_ipc_setmaster(int sock, int fd);
|
||||||
|
void direct_ipc_dropmaster(int sock, int fd);
|
||||||
|
void direct_ipc_finish(int sock, pid_t pid);
|
||||||
|
int direct_ipc_init(pid_t *pid_out);
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,17 +0,0 @@
|
||||||
#ifndef BACKEND_SESSION_SESSION_H
|
|
||||||
#define BACKEND_SESSION_SESSION_H
|
|
||||||
|
|
||||||
struct wlr_session;
|
|
||||||
|
|
||||||
struct wlr_session *libseat_session_create(struct wl_display *disp);
|
|
||||||
void libseat_session_destroy(struct wlr_session *base);
|
|
||||||
int libseat_session_open_device(struct wlr_session *base, const char *path);
|
|
||||||
void libseat_session_close_device(struct wlr_session *base, int fd);
|
|
||||||
bool libseat_change_vt(struct wlr_session *base, unsigned vt);
|
|
||||||
|
|
||||||
void session_init(struct wlr_session *session);
|
|
||||||
|
|
||||||
struct wlr_device *session_open_if_kms(struct wlr_session *restrict session,
|
|
||||||
const char *restrict path);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -4,12 +4,14 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-egl.h>
|
||||||
|
#include <wayland-server.h>
|
||||||
|
#include <wayland-util.h>
|
||||||
|
|
||||||
#include <wlr/backend/wayland.h>
|
#include <wlr/backend/wayland.h>
|
||||||
|
#include <wlr/render/egl.h>
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/types/wlr_pointer.h>
|
#include <wlr/types/wlr_box.h>
|
||||||
#include <wlr/render/drm_format_set.h>
|
|
||||||
|
|
||||||
struct wlr_wl_backend {
|
struct wlr_wl_backend {
|
||||||
struct wlr_backend backend;
|
struct wlr_backend backend;
|
||||||
|
@ -19,48 +21,22 @@ struct wlr_wl_backend {
|
||||||
struct wl_display *local_display;
|
struct wl_display *local_display;
|
||||||
struct wl_list devices;
|
struct wl_list devices;
|
||||||
struct wl_list outputs;
|
struct wl_list outputs;
|
||||||
int drm_fd;
|
struct wlr_egl egl;
|
||||||
struct wl_list buffers; // wlr_wl_buffer.link
|
struct wlr_renderer *renderer;
|
||||||
size_t requested_outputs;
|
size_t requested_outputs;
|
||||||
size_t last_output_num;
|
|
||||||
struct wl_listener local_display_destroy;
|
struct wl_listener local_display_destroy;
|
||||||
char *activation_token;
|
|
||||||
|
|
||||||
/* remote state */
|
/* remote state */
|
||||||
struct wl_display *remote_display;
|
struct wl_display *remote_display;
|
||||||
struct wl_event_source *remote_display_src;
|
struct wl_event_source *remote_display_src;
|
||||||
struct wl_registry *registry;
|
struct wl_registry *registry;
|
||||||
struct wl_compositor *compositor;
|
struct wl_compositor *compositor;
|
||||||
struct xdg_wm_base *xdg_wm_base;
|
struct xdg_wm_base *xdg_wm_base;
|
||||||
struct zxdg_decoration_manager_v1 *zxdg_decoration_manager_v1;
|
|
||||||
struct zwp_pointer_gestures_v1 *zwp_pointer_gestures_v1;
|
|
||||||
struct wp_presentation *presentation;
|
|
||||||
struct wl_shm *shm;
|
struct wl_shm *shm;
|
||||||
struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1;
|
struct wl_seat *seat;
|
||||||
struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1;
|
struct wl_pointer *pointer;
|
||||||
struct wl_list seats; // wlr_wl_seat.link
|
struct wl_keyboard *keyboard;
|
||||||
struct zwp_tablet_manager_v2 *tablet_manager;
|
struct wlr_wl_pointer *current_pointer;
|
||||||
clockid_t presentation_clock;
|
char *seat_name;
|
||||||
struct wlr_drm_format_set shm_formats;
|
|
||||||
struct wlr_drm_format_set linux_dmabuf_v1_formats;
|
|
||||||
struct wl_drm *legacy_drm;
|
|
||||||
struct xdg_activation_v1 *activation_v1;
|
|
||||||
char *drm_render_name;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wlr_wl_buffer {
|
|
||||||
struct wlr_buffer *buffer;
|
|
||||||
struct wl_buffer *wl_buffer;
|
|
||||||
bool released;
|
|
||||||
struct wl_list link; // wlr_wl_backend.buffers
|
|
||||||
struct wl_listener buffer_destroy;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wlr_wl_presentation_feedback {
|
|
||||||
struct wlr_wl_output *output;
|
|
||||||
struct wl_list link;
|
|
||||||
struct wp_presentation_feedback *feedback;
|
|
||||||
uint32_t commit_seq;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_wl_output {
|
struct wlr_wl_output {
|
||||||
|
@ -73,25 +49,23 @@ struct wlr_wl_output {
|
||||||
struct wl_callback *frame_callback;
|
struct wl_callback *frame_callback;
|
||||||
struct xdg_surface *xdg_surface;
|
struct xdg_surface *xdg_surface;
|
||||||
struct xdg_toplevel *xdg_toplevel;
|
struct xdg_toplevel *xdg_toplevel;
|
||||||
struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1;
|
struct wl_egl_window *egl_window;
|
||||||
struct wl_list presentation_feedbacks;
|
EGLSurface egl_surface;
|
||||||
|
|
||||||
uint32_t enter_serial;
|
uint32_t enter_serial;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
struct wlr_wl_pointer *pointer;
|
|
||||||
struct wl_surface *surface;
|
struct wl_surface *surface;
|
||||||
|
struct wl_egl_window *egl_window;
|
||||||
int32_t hotspot_x, hotspot_y;
|
int32_t hotspot_x, hotspot_y;
|
||||||
|
int32_t width, height;
|
||||||
} cursor;
|
} cursor;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_wl_input_device {
|
struct wlr_wl_input_device {
|
||||||
struct wlr_input_device wlr_input_device;
|
struct wlr_input_device wlr_input_device;
|
||||||
struct wl_list link;
|
|
||||||
uint32_t fingers;
|
|
||||||
|
|
||||||
struct wlr_wl_backend *backend;
|
struct wlr_wl_backend *backend;
|
||||||
struct wlr_wl_seat *seat;
|
|
||||||
void *resource;
|
void *resource;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -100,10 +74,6 @@ struct wlr_wl_pointer {
|
||||||
|
|
||||||
struct wlr_wl_input_device *input_device;
|
struct wlr_wl_input_device *input_device;
|
||||||
struct wl_pointer *wl_pointer;
|
struct wl_pointer *wl_pointer;
|
||||||
struct zwp_pointer_gesture_swipe_v1 *gesture_swipe;
|
|
||||||
struct zwp_pointer_gesture_pinch_v1 *gesture_pinch;
|
|
||||||
struct zwp_pointer_gesture_hold_v1 *gesture_hold;
|
|
||||||
struct zwp_relative_pointer_v1 *relative_pointer;
|
|
||||||
enum wlr_axis_source axis_source;
|
enum wlr_axis_source axis_source;
|
||||||
int32_t axis_discrete;
|
int32_t axis_discrete;
|
||||||
struct wlr_wl_output *output;
|
struct wlr_wl_output *output;
|
||||||
|
@ -111,35 +81,12 @@ struct wlr_wl_pointer {
|
||||||
struct wl_listener output_destroy;
|
struct wl_listener output_destroy;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wlr_wl_seat {
|
|
||||||
struct wl_seat *wl_seat;
|
|
||||||
|
|
||||||
struct wl_list link; // wlr_wl_backend.seats
|
|
||||||
char *name;
|
|
||||||
struct wl_touch *touch;
|
|
||||||
struct wl_pointer *pointer;
|
|
||||||
struct wl_keyboard *keyboard;
|
|
||||||
|
|
||||||
struct wlr_wl_backend *backend;
|
|
||||||
struct wlr_wl_pointer *active_pointer;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend);
|
struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend);
|
||||||
void update_wl_output_cursor(struct wlr_wl_output *output);
|
void update_wl_output_cursor(struct wlr_wl_output *output);
|
||||||
struct wlr_wl_pointer *pointer_get_wl(struct wlr_pointer *wlr_pointer);
|
struct wlr_wl_pointer *pointer_get_wl(struct wlr_pointer *wlr_pointer);
|
||||||
void create_wl_pointer(struct wlr_wl_seat *seat, struct wlr_wl_output *output);
|
void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *output);
|
||||||
void create_wl_keyboard(struct wlr_wl_seat *seat);
|
void create_wl_keyboard(struct wl_keyboard *wl_keyboard, struct wlr_wl_backend *wl);
|
||||||
void create_wl_touch(struct wlr_wl_seat *seat);
|
|
||||||
struct wlr_wl_input_device *create_wl_input_device(
|
|
||||||
struct wlr_wl_seat *seat, enum wlr_input_device_type type);
|
|
||||||
bool create_wl_seat(struct wl_seat *wl_seat, struct wlr_wl_backend *wl);
|
|
||||||
void destroy_wl_seats(struct wlr_wl_backend *wl);
|
|
||||||
void destroy_wl_buffer(struct wlr_wl_buffer *buffer);
|
|
||||||
|
|
||||||
extern const struct wl_seat_listener seat_listener;
|
extern const struct wl_seat_listener seat_listener;
|
||||||
|
|
||||||
struct wlr_wl_tablet_seat *wl_add_tablet_seat(
|
|
||||||
struct zwp_tablet_manager_v2 *manager,
|
|
||||||
struct wlr_wl_seat *seat);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue