Compare commits

..

No commits in common. "dkondor-upstream-pr-2551" and "0.4.1" have entirely different histories.

438 changed files with 27815 additions and 39385 deletions

View File

@ -2,35 +2,21 @@ image: alpine/edge
packages:
- eudev-dev
- ffmpeg-dev
- glslang
- libcap-dev
- libinput-dev
- libxkbcommon-dev
- mesa-dev
- meson
- pixman-dev
- vulkan-headers
- vulkan-loader-dev
- wayland-dev
- wayland-protocols
- xcb-util-image-dev
- xcb-util-renderutil-dev
- xcb-util-wm-dev
- xwayland
- libseat-dev
sources:
- https://gitlab.freedesktop.org/wlroots/wlroots.git
- https://github.com/swaywm/wlroots
tasks:
- setup: |
cd wlroots
meson build --fatal-meson-warnings --default-library=both -Dauto_features=enabled -Dxcb-errors=disabled
meson build
- build: |
cd wlroots
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

View File

@ -2,6 +2,7 @@ image: archlinux
packages:
- clang
- ffmpeg
- libcap
- libinput
- libxkbcommon
- mesa
@ -9,37 +10,17 @@ packages:
- pixman
- wayland
- wayland-protocols
- xcb-util-errors
- xcb-util-image
- xcb-util-renderutil
- xcb-util-wm
- xorg-xwayland
- seatd
- vulkan-icd-loader
- vulkan-headers
- glslang
sources:
- https://gitlab.freedesktop.org/wlroots/wlroots.git
- https://github.com/swaywm/wlroots
tasks:
- setup: |
cd wlroots
CC=gcc meson build-gcc --fatal-meson-warnings --default-library=both -Dauto_features=enabled --prefix /usr -Db_sanitize=address,undefined
CC=clang meson build-clang --fatal-meson-warnings -Dauto_features=enabled
CC=gcc meson build-gcc
CC=clang meson build-clang
- gcc: |
cd wlroots/build-gcc
ninja
sudo ninja install
cd ../tinywl
CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" make
- clang: |
cd wlroots/build-clang
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 ]

View File

@ -1,38 +1,42 @@
image: freebsd/latest
packages:
- devel/evdev-proto
- devel/libepoll-shim
- devel/libudev-devd
- devel/meson # implies ninja
- devel/pkgconf
- graphics/glslang
- graphics/libdrm
- graphics/mesa-libs
- graphics/png
- graphics/vulkan-headers
- graphics/vulkan-loader
- graphics/wayland
- graphics/wayland-protocols
- multimedia/ffmpeg
- x11/libX11
- x11/libinput
- x11/libxcb
- x11/libxkbcommon
- x11/pixman
- x11/xcb-util-errors
- x11/xcb-util-renderutil
- x11/xcb-util-wm
- x11-servers/xwayland
- sysutils/seatd
- gmake
- devel/evdev-proto
- devel/libepoll-shim
- devel/libudev-devd
- devel/meson # implies ninja
- devel/pkgconf
- graphics/libdrm
- graphics/mesa-libs
- graphics/png
- graphics/wayland
- graphics/wayland-protocols
- multimedia/ffmpeg
- x11/libX11
- x11/libinput
- x11/libxcb
- x11/libxkbcommon
- x11/pixman
- x11/xcb-util-errors
- x11/xcb-util-wm
sources:
- https://gitlab.freedesktop.org/wlroots/wlroots.git
- https://github.com/swaywm/wlroots
tasks:
- wlroots: |
cd wlroots
meson build --fatal-meson-warnings -Dauto_features=enabled
ninja -C build
sudo ninja -C build install
- tinywl: |
cd wlroots/tinywl
gmake
- fixup_epoll: |
cat << 'EOF' | sudo tee /usr/local/libdata/pkgconfig/epoll-shim.pc
prefix=/usr/local
exec_prefix=\$\{\$prefix\}
libdir=${prefix}/lib
sharedlibdir=${prefix}/lib
includedir=${prefix}/include/libepoll-shim
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

1
.gitignore vendored
View File

@ -7,3 +7,4 @@ build/
build-*/
wayland-*-protocol.*
wlr-example.ini
rootston.ini

View File

@ -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

View File

@ -1,21 +1,22 @@
# Contributing to wlroots
Contributing just involves sending a merge request. You will probably be more
successful with your contribution if you visit [#sway-devel on Libera Chat]
upfront and discuss your plans.
Contributing just involves sending a pull request. You will probably be more
successful with your contribution if you visit
[#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
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
upstream. Try this:
1. Fork wlroots
2. `git clone git@gitlab.freedesktop.org:<username>/wlroots.git && cd wlroots`
3. `git remote add upstream https://gitlab.freedesktop.org/wlroots/wlroots.git`
2. `git clone https://github.com/username/wlroots && cd wlroots`
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
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`
3. Add and commit your changes
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
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
(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
of so we can adequately test them - then verify the test plan yourself before
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
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
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
blank line, and include optional details. In this you can give justification
for the change, [reference issues], or explain some of the subtler
details of your patch. This is important because when someone finds a line of
code they don't understand later, they can use the `git blame` command to find
out what the author was thinking when they wrote it. It's also easier to review
your merge requests if they're separated into logical commits that have good
commit messages and justify themselves in the extended commit description.
for the change, [reference Github
issues](https://help.github.com/articles/closing-issues-via-commit-messages/),
or explain some of the subtler details of your patch. This is important because
when someone finds a line of code they don't understand later, they can use the
`git blame` command to find out what the author was thinking when they wrote
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
description on GitLab is probably fair game for going into the extended commit
As a good rule of thumb, anything you might put into the pull request
description on Github is probably fair game for going into the extended commit
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
@ -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
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
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).
2. **Review** the code. Look for code style violations, naming convention
violations, buffer overflows, memory leaks, logic errors, non-portable code
(including GNU-isms), etc. For significant changes to the public API, loop in
a couple more people for discussion.
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.
## 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
wlroots is written in C with a style similar to the [kernel style], but with a
few notable differences.
wlroots is written in C with a style similar to the [kernel
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
not use GNU extensions.
@ -200,7 +163,7 @@ zeroed value and exit cleanly; this simplifies error handling a lot.
### Error Codes
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
@ -279,12 +242,14 @@ at least one struct for each interface in the protocol. For instance,
### Globals
Global interfaces generally have public constructors and destructors. Their
struct has a field holding the `wl_global` itself, a destroy signal and a
`wl_display` destroy listener. Example:
struct has a field holding the `wl_global` itself, a list of resources clients
created by binding to the global, a destroy signal and a `wl_display` destroy
listener. Example:
```c
struct wlr_compositor {
struct wl_global *global;
struct wl_list resources;
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
display destroy listener, destroy the `wl_global` and then destroy the struct.
The destructor can assume all clients and resources have been already
destroyed.
display destroy listener, destroy the `wl_global`, destroy all bound resources
and then destroy the struct.
### 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
Object structs should only be destroyed when their resource is destroyed, ie.
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
`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`.
The compositor should not destroy resources on its own.
### Inert resources
@ -399,11 +354,3 @@ static void subsurface_handle_surface_destroy(struct wl_listener *listener,
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

View File

@ -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
Pluggable, composable, unopinionated modules for building a [Wayland]
compositor; or about 60,000 lines of code you were going to write anyway.
Pluggable, composable, unopinionated modules for building a
[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
hardware, including KMS/DRM, libinput, Wayland, X11, and headless backends,
plus any custom backends you choose to write, which can all be created or
destroyed at runtime and used in concert with each other.
hardware, including KMS/DRM, libinput, Wayland, X11, and headless backends,
plus any custom backends you choose to write, which can all be created or
destroyed at runtime and used in concert with each other.
- wlroots provides unopinionated, mostly standalone implementations of many
Wayland interfaces, both from wayland.xml and various protocol extensions.
We also promote the standardization of portable extensions across
many compositors.
Wayland interfaces, both from wayland.xml and various protocol extensions.
We also promote the standardization of portable extensions across
many compositors.
- wlroots provides several powerful, standalone, and optional tools that
implement components common to many compositors, such as the arrangement of
outputs in physical space.
implement components common to many compositors, such as the arrangement of
outputs in physical space.
- wlroots provides an Xwayland abstraction that allows you to have excellent
Xwayland support without worrying about writing your own X11 window manager
on top of writing your compositor.
Xwayland support without worrying about writing your own X11 window manager
on top of writing your compositor.
- 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
needs demand custom rendering code.
avoid writing GL code directly, but which steps out of the way when your
needs demand custom rendering code.
wlroots implements a huge variety of Wayland compositor features and implements
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
to implement yourself.
Check out our [wiki] to get started with wlroots. Join our IRC channel:
[#sway-devel on Libera Chat].
Check out our [wiki](https://github.com/swaywm/wlroots/wiki/Getting-started) to
get started with wlroots.
wlroots is developed under the direction of the [sway] project. A variety of
[wrapper libraries] are available for using it with your favorite programming
language.
wlroots is developed under the direction of the
[sway](https://github.com/swaywm/sway) project. A variety of wrapper libraries
[are available](https://github.com/swaywm) for using it with your favorite
programming language.
## Building
@ -49,41 +45,39 @@ Install dependencies:
* meson
* wayland
* wayland-protocols
* EGL and GLESv2 (optional, for the GLES2 renderer)
* Vulkan loader, headers and glslang (optional, for the Vulkan renderer)
* EGL
* GLESv2
* libdrm
* GBM
* libinput (optional, for the libinput backend)
* libinput
* xkbcommon
* udev
* 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:
* xwayland (build-time only, optional at runtime)
* libxcb
* libxcb-render-util
* libxcb-wm
* libxcb-errors (optional, for improved error reporting)
* xcb
* xcb-composite
* xcb-xfixes
* xcb-xinput
* xcb-image
* xcb-render
* x11-xcb
* xcb-errors (optional, for improved error reporting)
* x11-icccm (optional, for improved Xwayland introspection)
Run these commands:
meson build/
ninja -C build/
meson build
ninja -C build
Install like so:
sudo ninja -C build/ install
sudo ninja -C build install
## Contributing
See [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
See [CONTRIBUTING.md](https://github.com/swaywm/wlroots/blob/master/CONTRIBUTING.md).

View File

@ -1,39 +1,27 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <errno.h>
#include <libinput.h>
#include <stdlib.h>
#include <string.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/interface.h>
#include <wlr/backend/libinput.h>
#include <wlr/backend/multi.h>
#include <wlr/backend/noop.h>
#include <wlr/backend/session.h>
#include <wlr/backend/wayland.h>
#include <wlr/config.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/util/log.h>
#include "backend/backend.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
#include <wlr/backend/x11.h>
#endif
#define WAIT_SESSION_TIMEOUT 10000 // ms
void wlr_backend_init(struct wlr_backend *backend,
const struct wlr_backend_impl *impl) {
assert(backend);
@ -43,10 +31,6 @@ void wlr_backend_init(struct wlr_backend *backend,
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) {
if (backend->impl->start) {
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) {
if (backend->impl->get_session) {
return backend->impl->get_session(backend);
@ -73,52 +64,6 @@ struct wlr_session *wlr_backend_get_session(struct wlr_backend *backend) {
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) {
if (backend->impl->get_presentation_clock) {
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;
}
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) {
const char *outputs_str = getenv(name);
if (outputs_str == NULL) {
@ -157,8 +87,9 @@ static size_t parse_outputs_env(const char *name) {
return outputs;
}
static struct wlr_backend *attempt_wl_backend(struct wl_display *display) {
struct wlr_backend *backend = wlr_wl_backend_create(display, NULL);
static struct wlr_backend *attempt_wl_backend(struct wl_display *display,
wlr_renderer_create_func_t create_renderer_func) {
struct wlr_backend *backend = wlr_wl_backend_create(display, NULL, create_renderer_func);
if (backend == NULL) {
return NULL;
}
@ -173,8 +104,8 @@ static struct wlr_backend *attempt_wl_backend(struct wl_display *display) {
#if WLR_HAS_X11_BACKEND
static struct wlr_backend *attempt_x11_backend(struct wl_display *display,
const char *x11_display) {
struct wlr_backend *backend = wlr_x11_backend_create(display, 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, create_renderer_func);
if (backend == NULL) {
return NULL;
}
@ -189,8 +120,8 @@ static struct wlr_backend *attempt_x11_backend(struct wl_display *display,
#endif
static struct wlr_backend *attempt_headless_backend(
struct wl_display *display) {
struct wlr_backend *backend = wlr_headless_backend_create(display);
struct wl_display *display, wlr_renderer_create_func_t create_renderer_func) {
struct wlr_backend *backend = wlr_headless_backend_create(display, create_renderer_func);
if (backend == NULL) {
return NULL;
}
@ -203,29 +134,33 @@ static struct wlr_backend *attempt_headless_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,
struct wlr_backend *backend, struct wlr_session *session) {
struct wlr_device *gpus[8];
ssize_t num_gpus = wlr_session_find_gpus(session, 8, gpus);
if (num_gpus < 0) {
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 *backend, struct wlr_session *session,
wlr_renderer_create_func_t create_renderer_func) {
int gpus[8];
size_t num_gpus = wlr_session_find_gpus(session, 8, gpus);
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,
gpus[i], primary_drm);
gpus[i], primary_drm, create_renderer_func);
if (!drm) {
wlr_log(WLR_ERROR, "Failed to create DRM backend");
wlr_log(WLR_ERROR, "Failed to open DRM device %d", gpus[i]);
continue;
}
@ -235,56 +170,46 @@ static struct wlr_backend *attempt_drm_backend(struct wl_display *display,
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;
}
#endif
static bool attempt_backend_by_name(struct wl_display *display,
struct wlr_multi_backend *multi, char *name) {
struct wlr_backend *backend = NULL;
static struct wlr_backend *attempt_backend_by_name(struct wl_display *display,
struct wlr_backend *backend, struct wlr_session **session,
const char *name, wlr_renderer_create_func_t create_renderer_func) {
if (strcmp(name, "wayland") == 0) {
backend = attempt_wl_backend(display);
return attempt_wl_backend(display, create_renderer_func);
#if WLR_HAS_X11_BACKEND
} else if (strcmp(name, "x11") == 0) {
backend = attempt_x11_backend(display, NULL);
return attempt_x11_backend(display, NULL, create_renderer_func);
#endif
} 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) {
// DRM and libinput need a session
if (multi->session == NULL) {
multi->session = session_create_and_wait(display);
if (multi->session == NULL) {
if (!*session) {
*session = wlr_session_create(display);
if (!*session) {
wlr_log(WLR_ERROR, "failed to start a session");
return false;
return NULL;
}
}
if (strcmp(name, "libinput") == 0) {
#if WLR_HAS_LIBINPUT_BACKEND
backend = wlr_libinput_backend_create(display, multi->session);
#endif
return wlr_libinput_backend_create(display, *session);
} else {
#if WLR_HAS_DRM_BACKEND
// attempt_drm_backend adds the multi drm backends itself
return attempt_drm_backend(display, &multi->backend,
multi->session) != NULL;
#endif
return attempt_drm_backend(display, backend, *session, create_renderer_func);
}
} 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_multi_backend *multi = (struct wlr_multi_backend *)backend;
if (!backend) {
@ -294,9 +219,6 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) {
char *names = getenv("WLR_BACKENDS");
if (names) {
wlr_log(WLR_INFO, "Loading user-specified backends due to WLR_BACKENDS: %s",
names);
names = strdup(names);
if (names == NULL) {
wlr_log(WLR_ERROR, "allocation failed");
@ -307,7 +229,17 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) {
char *saveptr;
char *name = strtok_r(names, ",", &saveptr);
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_session_destroy(multi->session);
wlr_backend_destroy(backend);
@ -322,39 +254,36 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) {
return backend;
}
if (getenv("WAYLAND_DISPLAY") || getenv("WAYLAND_SOCKET")) {
struct wlr_backend *wl_backend = attempt_wl_backend(display);
if (!wl_backend) {
goto error;
if (getenv("WAYLAND_DISPLAY") || getenv("_WAYLAND_DISPLAY") ||
getenv("WAYLAND_SOCKET")) {
struct wlr_backend *wl_backend = attempt_wl_backend(display,
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
const char *x11_display = getenv("DISPLAY");
if (x11_display) {
struct wlr_backend *x11_backend =
attempt_x11_backend(display, x11_display);
if (!x11_backend) {
goto error;
attempt_x11_backend(display, x11_display, create_renderer_func);
if (x11_backend) {
wlr_multi_backend_add(backend, x11_backend);
return backend;
}
wlr_multi_backend_add(backend, x11_backend);
return backend;
}
#endif
// Attempt DRM+libinput
multi->session = session_create_and_wait(display);
multi->session = wlr_session_create(display);
if (!multi->session) {
wlr_log(WLR_ERROR, "Failed to start a DRM session");
wlr_backend_destroy(backend);
return NULL;
}
#if WLR_HAS_LIBINPUT_BACKEND
struct wlr_backend *libinput = wlr_libinput_backend_create(display,
multi->session);
if (!libinput) {
@ -364,37 +293,16 @@ struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) {
return NULL;
}
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, multi->session);
struct wlr_backend *primary_drm = attempt_drm_backend(display, backend,
multi->session, create_renderer_func);
if (!primary_drm) {
wlr_log(WLR_ERROR, "Failed to open any DRM device");
wlr_backend_destroy(libinput);
wlr_session_destroy(multi->session);
wlr_backend_destroy(backend);
return NULL;
}
drm_backend_monitor_create(backend, primary_drm, multi->session);
return backend;
#endif
error:
wlr_backend_destroy(backend);
return NULL;
}

View File

@ -1,3 +1,4 @@
#include <gbm.h>
#include <stdlib.h>
#include <wlr/util/log.h>
#include <xf86drm.h>
@ -8,290 +9,266 @@
struct atomic {
drmModeAtomicReq *req;
int cursor;
bool failed;
};
static void atomic_begin(struct atomic *atom) {
memset(atom, 0, sizeof(*atom));
atom->req = drmModeAtomicAlloc();
if (!atom->req) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
atom->failed = true;
return;
static void atomic_begin(struct wlr_drm_crtc *crtc, struct atomic *atom) {
if (!crtc->atomic) {
crtc->atomic = drmModeAtomicAlloc();
if (!crtc->atomic) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
atom->failed = true;
return;
}
}
atom->req = crtc->atomic;
atom->cursor = drmModeAtomicGetCursor(atom->req);
atom->failed = false;
}
static bool atomic_commit(struct atomic *atom,
struct wlr_drm_connector *conn, uint32_t flags) {
struct wlr_drm_backend *drm = conn->backend;
static bool atomic_end(int drm_fd, struct atomic *atom) {
if (atom->failed) {
return false;
}
int ret = drmModeAtomicCommit(drm->fd, atom->req, flags, drm);
if (ret != 0) {
wlr_drm_conn_log_errno(conn,
(flags & DRM_MODE_ATOMIC_TEST_ONLY) ? WLR_DEBUG : WLR_ERROR,
"Atomic %s failed (%s)",
(flags & DRM_MODE_ATOMIC_TEST_ONLY) ? "test" : "commit",
(flags & DRM_MODE_ATOMIC_ALLOW_MODESET) ? "modeset" : "pageflip");
uint32_t flags = DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_NONBLOCK;
if (drmModeAtomicCommit(drm_fd, atom->req, flags, NULL)) {
wlr_log_errno(WLR_ERROR, "Atomic test failed");
drmModeAtomicSetCursor(atom->req, atom->cursor);
return false;
}
return true;
}
static void atomic_finish(struct atomic *atom) {
drmModeAtomicFree(atom->req);
static bool atomic_commit(int drm_fd, struct atomic *atom,
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) {
wlr_log_errno(WLR_ERROR, "Failed to add atomic DRM property");
atom->failed = true;
}
}
static bool create_mode_blob(struct wlr_drm_backend *drm,
struct wlr_drm_connector *conn,
const struct wlr_drm_connector_state *state, uint32_t *blob_id) {
if (!state->active) {
*blob_id = 0;
return true;
}
static void set_plane_props(struct atomic *atom, struct wlr_drm_plane *plane,
uint32_t crtc_id, uint32_t fb_id, bool set_crtc_xy) {
uint32_t id = plane->id;
const union wlr_drm_plane_props *props = &plane->props;
if (drmModeCreatePropertyBlob(drm->fd, &state->mode,
sizeof(drmModeModeInfo), blob_id)) {
wlr_log_errno(WLR_ERROR, "Unable to create mode property blob");
return false;
// 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)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,
size_t size, const uint16_t *lut, uint32_t *blob_id) {
if (size == 0) {
*blob_id = 0;
static bool atomic_crtc_pageflip(struct wlr_drm_backend *drm,
struct wlr_drm_connector *conn,
struct wlr_drm_crtc *crtc,
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;
}
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));
if (gamma == NULL) {
wlr_log(WLR_ERROR, "Failed to allocate gamma table");
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++) {
gamma[i].red = r[i];
gamma[i].green = g[i];
gamma[i].blue = b[i];
}
if (crtc->gamma_lut != 0) {
drmModeDestroyPropertyBlob(drm->fd, crtc->gamma_lut);
}
if (drmModeCreatePropertyBlob(drm->fd, gamma,
size * sizeof(struct drm_color_lut), blob_id) != 0) {
wlr_log_errno(WLR_ERROR, "Unable to create gamma LUT property blob");
size * sizeof(struct drm_color_lut), &crtc->gamma_lut)) {
free(gamma);
wlr_log_errno(WLR_ERROR, "Unable to create property blob");
return false;
}
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;
atomic_begin(&atom);
atomic_add(&atom, conn->id, conn->props.crtc_id, active ? crtc->id : 0);
if (modeset && active && 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, mode_id);
atomic_add(&atom, crtc->id, crtc->props.active, active);
if (active) {
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);
}
atomic_begin(crtc, &atom);
atomic_add(&atom, crtc->id, crtc->props.gamma_lut, crtc->gamma_lut);
return atomic_end(drm->fd, &atom);
}
static size_t atomic_crtc_get_gamma_size(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc) {
if (crtc->props.gamma_lut_size == 0) {
return legacy_iface.crtc_get_gamma_size(drm, crtc);
}
bool ok = atomic_commit(&atom, conn, flags);
atomic_finish(&atom);
if (ok && !test_only) {
commit_blob(drm, &crtc->mode_id, mode_id);
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);
uint64_t gamma_lut_size;
if (!get_drm_prop(drm->fd, crtc->id, crtc->props.gamma_lut_size,
&gamma_lut_size)) {
wlr_log(WLR_ERROR, "Unable to get gamma lut size");
return 0;
}
if (fb_damage_clips != 0 &&
drmModeDestroyPropertyBlob(drm->fd, fb_damage_clips) != 0) {
wlr_log_errno(WLR_ERROR, "Failed to destroy FB_DAMAGE_CLIPS property blob");
}
return ok;
return (size_t)gamma_lut_size;
}
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,
};

View File

@ -1,14 +1,15 @@
#include <assert.h>
#include <errno.h>
#include <drm_fourcc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wayland-server-core.h>
#include <wayland-server.h>
#include <wlr/backend/interface.h>
#include <wlr/backend/session.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/render/egl.h>
#include <wlr/types/wlr_list.h>
#include <wlr/util/log.h>
#include <xf86drm.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) {
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
scan_drm_connectors(drm, NULL);
scan_drm_connectors(drm);
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);
restore_drm_outputs(drm);
struct wlr_drm_connector *conn, *next;
wl_list_for_each_safe(conn, next, &drm->outputs, link) {
destroy_drm_connector(conn);
wlr_output_destroy(&conn->output);
}
wlr_backend_finish(backend);
struct wlr_drm_fb *fb, *fb_tmp;
wl_list_for_each_safe(fb, fb_tmp, &drm->fbs, link) {
drm_fb_destroy(fb);
}
wlr_signal_emit_safe(&backend->events.destroy, backend);
wl_list_remove(&drm->display_destroy.link);
wl_list_remove(&drm->session_destroy.link);
wl_list_remove(&drm->session_active.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);
}
wl_list_remove(&drm->session_signal.link);
wl_list_remove(&drm->drm_invalidated.link);
finish_drm_resources(drm);
free(drm->name);
wlr_session_close_file(drm->session, drm->dev);
finish_drm_renderer(&drm->renderer);
wlr_session_close_file(drm->session, drm->fd);
wl_event_source_remove(drm->drm_event);
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) {
struct wlr_drm_backend *drm = get_drm_backend_from_backend(backend);
return drm->clock;
}
static int backend_get_drm_fd(struct wlr_backend *backend) {
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 = {
static struct wlr_backend_impl backend_impl = {
.start = backend_start,
.destroy = backend_destroy,
.get_renderer = backend_get_renderer,
.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) {
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 =
wl_container_of(listener, drm, session_active);
struct wlr_session *session = drm->session;
wl_container_of(listener, drm, session_signal);
struct wlr_session *session = data;
if (session->active) {
wlr_log(WLR_INFO, "DRM fd resumed");
scan_drm_connectors(drm, NULL);
scan_drm_connectors(drm);
struct wlr_drm_connector *conn;
wl_list_for_each(conn, &drm->outputs, link) {
struct wlr_output_mode *mode = NULL;
uint32_t committed = WLR_OUTPUT_STATE_ENABLED;
if (conn->output.enabled && conn->output.current_mode != NULL) {
committed |= WLR_OUTPUT_STATE_MODE;
mode = conn->output.current_mode;
wl_list_for_each(conn, &drm->outputs, link){
if (conn->output.enabled) {
drm_connector_set_mode(&conn->output,
conn->output.current_mode);
} else {
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 {
wlr_log(WLR_INFO, "DRM fd paused");
}
}
static void handle_dev_change(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) {
static void drm_invalidated(struct wl_listener *listener, void *data) {
struct wlr_drm_backend *drm =
wl_container_of(listener, drm, session_destroy);
backend_destroy(&drm->backend);
wl_container_of(listener, drm, drm_invalidated);
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) {
@ -166,21 +141,16 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
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_session *session, struct wlr_device *dev,
struct wlr_backend *parent) {
assert(display && session && dev);
struct wlr_session *session, int gpu_fd, struct wlr_backend *parent,
wlr_renderer_create_func_t create_renderer_func) {
assert(display && session && gpu_fd >= 0);
assert(!parent || wlr_backend_is_drm(parent));
char *name = drmGetDeviceNameFromFd2(dev->fd);
drmVersion *version = drmGetVersion(dev->fd);
char *name = drmGetDeviceNameFromFd2(gpu_fd);
drmVersion *version = drmGetVersion(gpu_fd);
wlr_log(WLR_INFO, "Initializing DRM backend for %s (%s)", name, version->name);
free(name);
drmFreeVersion(version);
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);
drm->session = session;
wl_list_init(&drm->fbs);
wl_list_init(&drm->outputs);
drm->dev = dev;
drm->fd = dev->fd;
drm->name = name;
drm->fd = gpu_fd;
if (parent != NULL) {
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;
wl_signal_add(&dev->events.change, &drm->dev_change);
drm->dev_remove.notify = handle_dev_remove;
wl_signal_add(&dev->events.remove, &drm->dev_remove);
drm->drm_invalidated.notify = drm_invalidated;
wlr_session_signal_add(session, gpu_fd, &drm->drm_invalidated);
drm->display = 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,
WL_EVENT_READABLE, handle_drm_event, drm);
WL_EVENT_READABLE, handle_drm_event, NULL);
if (!drm->drm_event) {
wlr_log(WLR_ERROR, "Failed to create DRM event source");
goto error_fd;
}
drm->session_active.notify = handle_session_active;
wl_signal_add(&session->events.active, &drm->session_active);
drm->session_signal.notify = session_signal;
wl_signal_add(&session->session_signal, &drm->session_signal);
if (!check_drm_features(drm)) {
goto error_event;
@ -234,54 +192,21 @@ struct wlr_backend *wlr_drm_backend_create(struct wl_display *display,
goto error_event;
}
if (drm->parent) {
if (!init_drm_renderer(drm, &drm->mgpu_renderer)) {
wlr_log(WLR_ERROR, "Failed to initialize renderer");
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);
}
if (!init_drm_renderer(drm, &drm->renderer, create_renderer_func)) {
wlr_log(WLR_ERROR, "Failed to initialize renderer");
goto error_event;
}
drm->session_destroy.notify = handle_session_destroy;
wl_signal_add(&session->events.destroy, &drm->session_destroy);
drm->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &drm->display_destroy);
return &drm->backend;
error_mgpu_renderer:
finish_drm_renderer(&drm->mgpu_renderer);
error_resources:
finish_drm_resources(drm);
error_event:
wl_list_remove(&drm->session_active.link);
wl_list_remove(&drm->session_signal.link);
wl_event_source_remove(drm->drm_event);
error_fd:
wl_list_remove(&drm->dev_remove.link);
wl_list_remove(&drm->dev_change.link);
wl_list_remove(&drm->parent_destroy.link);
wlr_session_close_file(drm->session, dev);
wlr_session_close_file(drm->session, drm->fd);
free(drm);
return NULL;
}

View File

@ -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;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,4 @@
#include <assert.h>
#include <stdlib.h>
#include <gbm.h>
#include <wlr/util/log.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
@ -7,225 +6,78 @@
#include "backend/drm/iface.h"
#include "backend/drm/util.h"
static bool legacy_fb_props_match(struct wlr_drm_fb *fb1,
struct wlr_drm_fb *fb2) {
struct wlr_dmabuf_attributes dmabuf1 = {0}, dmabuf2 = {0};
if (!wlr_buffer_get_dmabuf(fb1->wlr_buf, &dmabuf1) ||
!wlr_buffer_get_dmabuf(fb2->wlr_buf, &dmabuf2)) {
return false;
}
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]) {
static bool legacy_crtc_pageflip(struct wlr_drm_backend *drm,
struct wlr_drm_connector *conn, struct wlr_drm_crtc *crtc,
uint32_t fb_id, drmModeModeInfo *mode) {
if (mode) {
if (drmModeSetCrtc(drm->fd, crtc->id, fb_id, 0, 0,
&conn->id, 1, mode)) {
wlr_log_errno(WLR_ERROR, "%s: Failed to set CRTC", conn->output.name);
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;
}
static bool legacy_crtc_test(struct wlr_drm_connector *conn,
const struct wlr_drm_connector_state *state) {
struct wlr_drm_crtc *crtc = conn->crtc;
if ((state->base->committed & WLR_OUTPUT_STATE_BUFFER) && !state->modeset) {
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_conn_enable(struct wlr_drm_backend *drm,
struct wlr_drm_connector *conn, bool enable) {
int ret = drmModeConnectorSetProperty(drm->fd, conn->id, conn->props.dpms,
enable ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF);
return ret >= 0;
}
static bool legacy_crtc_commit(struct wlr_drm_connector *conn,
const struct wlr_drm_connector_state *state,
uint32_t flags, bool test_only) {
if (!legacy_crtc_test(conn, state)) {
return false;
}
if (test_only) {
bool legacy_crtc_set_cursor(struct wlr_drm_backend *drm,
struct wlr_drm_crtc *crtc, struct gbm_bo *bo) {
if (!crtc || !crtc->cursor) {
return true;
}
struct wlr_drm_backend *drm = conn->backend;
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 (!bo) {
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 true;
}
if (flags & DRM_MODE_PAGE_FLIP_EVENT) {
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;
}
}
struct wlr_drm_plane *plane = crtc->cursor;
return true;
}
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);
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");
return false;
}
free(linear_lut);
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 = {
.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,
};

View File

@ -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 }

View File

@ -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;
}

View File

@ -1,4 +1,3 @@
#define _POSIX_C_SOURCE 200809L
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@ -20,44 +19,38 @@ struct prop_info {
static const struct prop_info connector_info[] = {
#define INDEX(name) (offsetof(union wlr_drm_connector_props, name) / sizeof(uint32_t))
{ "CRTC_ID", INDEX(crtc_id) },
{ "DPMS", INDEX(dpms) },
{ "EDID", INDEX(edid) },
{ "PATH", INDEX(path) },
{ "CRTC_ID", INDEX(crtc_id) },
{ "DPMS", INDEX(dpms) },
{ "EDID", INDEX(edid) },
{ "PATH", INDEX(path) },
{ "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
};
static const struct prop_info crtc_info[] = {
#define INDEX(name) (offsetof(union wlr_drm_crtc_props, name) / sizeof(uint32_t))
{ "ACTIVE", INDEX(active) },
{ "GAMMA_LUT", INDEX(gamma_lut) },
{ "ACTIVE", INDEX(active) },
{ "GAMMA_LUT", INDEX(gamma_lut) },
{ "GAMMA_LUT_SIZE", INDEX(gamma_lut_size) },
{ "MODE_ID", INDEX(mode_id) },
{ "VRR_ENABLED", INDEX(vrr_enabled) },
{ "MODE_ID", INDEX(mode_id) },
{ "rotation", INDEX(rotation) },
{ "scaling mode", INDEX(scaling_mode) },
#undef INDEX
};
static const struct prop_info plane_info[] = {
#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_W", INDEX(crtc_w) },
{ "CRTC_X", INDEX(crtc_x) },
{ "CRTC_Y", INDEX(crtc_y) },
{ "FB_DAMAGE_CLIPS", INDEX(fb_damage_clips) },
{ "FB_ID", INDEX(fb_id) },
{ "IN_FORMATS", INDEX(in_formats) },
{ "SRC_H", INDEX(src_h) },
{ "SRC_W", INDEX(src_w) },
{ "SRC_X", INDEX(src_x) },
{ "SRC_Y", INDEX(src_y) },
{ "rotation", INDEX(rotation) },
{ "type", INDEX(type) },
{ "CRTC_W", INDEX(crtc_w) },
{ "CRTC_X", INDEX(crtc_x) },
{ "CRTC_Y", INDEX(crtc_y) },
{ "FB_ID", INDEX(fb_id) },
{ "SRC_H", INDEX(src_h) },
{ "SRC_W", INDEX(src_w) },
{ "SRC_X", INDEX(src_x) },
{ "SRC_Y", INDEX(src_y) },
{ "type", INDEX(type) },
#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);
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;
}

View File

@ -1,42 +1,54 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <drm_fourcc.h>
#include <fcntl.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <gbm.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wayland-util.h>
#include <wlr/render/egl.h>
#include <wlr/render/gles2.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/util/log.h>
#include "backend/drm/drm.h"
#include "backend/drm/util.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"
#include "glapi.h"
bool init_drm_renderer(struct wlr_drm_backend *drm,
struct wlr_drm_renderer *renderer) {
renderer->backend = drm;
struct wlr_drm_renderer *renderer, wlr_renderer_create_func_t create_renderer_func) {
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) {
wlr_log(WLR_ERROR, "Failed to create renderer");
return false;
}
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;
wlr_log(WLR_ERROR, "Failed to create EGL/WLR renderer");
goto error_gbm;
}
renderer->fd = drm->fd;
return true;
error_gbm:
gbm_device_destroy(renderer->gbm);
return false;
}
void finish_drm_renderer(struct wlr_drm_renderer *renderer) {
@ -44,13 +56,14 @@ void finish_drm_renderer(struct wlr_drm_renderer *renderer) {
return;
}
wlr_allocator_destroy(renderer->allocator);
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,
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) {
return true;
}
@ -59,357 +72,189 @@ bool init_drm_surface(struct wlr_drm_surface *surf,
surf->width = width;
surf->height = height;
wlr_swapchain_destroy(surf->swapchain);
surf->swapchain = NULL;
if (surf->gbm) {
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,
drm_format);
if (surf->swapchain == NULL) {
wlr_log(WLR_ERROR, "Failed to create swapchain");
memset(surf, 0, sizeof(*surf));
surf->gbm = gbm_surface_create(renderer->gbm, width, height,
format, GBM_BO_USE_RENDERING | flags);
if (!surf->gbm) {
wlr_log_errno(WLR_ERROR, "Failed to create GBM surface");
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 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;
}

View File

@ -2,6 +2,7 @@
#include <drm_fourcc.h>
#include <drm_mode.h>
#include <drm.h>
#include <gbm.h>
#include <stdio.h>
#include <string.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', 'V', 'T'): return "Avtek (Electronics) Pty Ltd";
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', 'O'): return "Chi Mei Optoelectronics corp.";
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', 'I', 'T'): return "Visitech AS";
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('Y', 'M', 'H'): return "Yamaha Corporation";
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_VIRTUAL: return "Virtual";
case DRM_MODE_CONNECTOR_DSI: return "DSI";
#ifdef DRM_MODE_CONNECTOR_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
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) {
if (arr[i] == key) {
return true;

View File

@ -2,8 +2,11 @@
#include <stdlib.h>
#include <wlr/interfaces/wlr_input_device.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/render/egl.h>
#include <wlr/render/gles2.h>
#include <wlr/util/log.h>
#include "backend/headless.h"
#include "glapi.h"
#include "util/signal.h"
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;
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,
&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;
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_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);
}
static uint32_t get_buffer_caps(struct wlr_backend *wlr_backend) {
return WLR_BUFFER_CAP_DATA_PTR
| WLR_BUFFER_CAP_DMABUF
| WLR_BUFFER_CAP_SHM;
static struct wlr_renderer *backend_get_renderer(
struct wlr_backend *wlr_backend) {
struct wlr_headless_backend *backend =
headless_backend_from_backend(wlr_backend);
return backend->renderer;
}
static const struct wlr_backend_impl backend_impl = {
.start = backend_start,
.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) {
@ -78,7 +85,8 @@ static void handle_display_destroy(struct wl_listener *listener, void *data) {
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");
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");
return NULL;
}
wlr_backend_init(&backend->backend, &backend_impl);
backend->display = display;
wl_list_init(&backend->outputs);
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;
wl_display_add_destroy_listener(display, &backend->display_destroy);

View File

@ -11,16 +11,7 @@
#include "backend/headless.h"
#include "util/signal.h"
static void input_device_destroy(struct wlr_input_device *wlr_dev) {
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,
};
static const struct wlr_input_device_impl input_device_impl = { 0 };
bool wlr_input_device_is_headless(struct wlr_input_device *wlr_dev) {
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);
break;
case WLR_INPUT_DEVICE_SWITCH:
wlr_device->switch_device = calloc(1, sizeof(struct wlr_switch));
if (wlr_device->switch_device == NULL) {
wlr_device->lid_switch = calloc(1, sizeof(struct wlr_switch));
if (wlr_device->lid_switch == NULL) {
wlr_log(WLR_ERROR, "Unable to allocate wlr_switch");
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) {
wlr_signal_emit_safe(&backend->backend.events.new_input, wlr_device);

View File

@ -1,5 +0,0 @@
wlr_files += files(
'backend.c',
'input_device.c',
'output.c',
)

View File

@ -1,89 +1,95 @@
#include <assert.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <stdlib.h>
#include <stdio.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/util/log.h>
#include "backend/headless.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(
struct wlr_output *wlr_output) {
assert(wlr_output_is_headless(wlr_output));
return (struct wlr_headless_output *)wlr_output;
}
static bool output_set_custom_mode(struct wlr_headless_output *output,
int32_t width, int32_t height, int32_t refresh) {
static EGLSurface egl_create_surface(struct wlr_egl *egl, unsigned int width,
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) {
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;
wlr_output_update_custom_mode(&output->wlr_output, width, height, refresh);
return true;
}
static bool output_test(struct wlr_output *wlr_output) {
uint32_t unsupported =
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) {
static void output_transform(struct wlr_output *wlr_output,
enum wl_output_transform transform) {
struct wlr_headless_output *output =
headless_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(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_make_current(struct wlr_output *wlr_output, int *buffer_age) {
struct wlr_headless_output *output =
headless_output_from_output(wlr_output);
return wlr_egl_make_current(&output->backend->egl, output->egl_surface,
buffer_age);
}
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;
}
static void output_destroy(struct wlr_output *wlr_output) {
struct wlr_headless_output *output =
headless_output_from_output(wlr_output);
wl_list_remove(&output->link);
wl_event_source_remove(output->frame_timer);
wlr_egl_destroy_surface(&output->backend->egl, output->egl_surface);
free(output);
}
static const struct wlr_output_impl output_impl = {
.set_custom_mode = output_set_custom_mode,
.transform = output_transform,
.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) {
@ -113,18 +119,26 @@ struct wlr_output *wlr_headless_add_output(struct wlr_backend *wlr_backend,
backend->display);
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->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];
snprintf(name, sizeof(name), "HEADLESS-%zu", ++backend->last_output_num);
wlr_output_set_name(wlr_output, name);
if (!wlr_egl_make_current(&output->backend->egl, output->egl_surface,
NULL)) {
goto error;
}
char description[128];
snprintf(description, sizeof(description),
"Headless output %zu", backend->last_output_num);
wlr_output_set_description(wlr_output, description);
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);
struct wl_event_loop *ev = wl_display_get_event_loop(backend->display);
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;
error:
wlr_output_destroy(&output->wlr_output);
return NULL;
}

View File

@ -1,7 +1,6 @@
#include <assert.h>
#include <libinput.h>
#include <stdlib.h>
#include <stdio.h>
#include <wlr/backend/interface.h>
#include <wlr/backend/session.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,
int flags, void *_backend) {
struct wlr_libinput_backend *backend = _backend;
struct wlr_device *dev = wlr_session_open_file(backend->session, path);
if (dev == NULL) {
return -1;
}
return dev->fd;
return wlr_session_open_file(backend->session, path);
}
static void libinput_close_restricted(int fd, void *_backend) {
struct wlr_libinput_backend *backend = _backend;
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);
}
wlr_session_close_file(backend->session, fd);
}
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) {
struct wlr_libinput_backend *backend = _backend;
int ret = libinput_dispatch(backend->libinput_context);
if (ret != 0) {
wlr_log(WLR_ERROR, "Failed to dispatch libinput: %s", strerror(-ret));
wl_display_terminate(backend->display);
if (libinput_dispatch(backend->libinput_context) != 0) {
wlr_log(WLR_ERROR, "Failed to dispatch libinput");
// TODO: some kind of abort?
return 0;
}
struct libinput_event *event;
@ -61,30 +44,15 @@ static int handle_libinput_readable(int fd, uint32_t mask, void *_backend) {
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,
enum libinput_log_priority priority, const char *fmt, va_list args) {
enum wlr_log_importance importance = libinput_log_priority_to_wlr(priority);
static char wlr_fmt[1024];
snprintf(wlr_fmt, sizeof(wlr_fmt), "[libinput] %s", fmt);
_wlr_vlog(importance, wlr_fmt, args);
_wlr_vlog(WLR_ERROR, fmt, args);
}
static bool backend_start(struct wlr_backend *wlr_backend) {
struct wlr_libinput_backend *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, backend->session->udev);
@ -110,9 +78,9 @@ static bool backend_start(struct wlr_backend *wlr_backend) {
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);
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, "Set WLR_LIBINPUT_NO_DEVICES=1 to suppress this check");
return false;
@ -141,22 +109,21 @@ static void backend_destroy(struct wlr_backend *wlr_backend) {
struct wlr_libinput_backend *backend =
get_libinput_backend_from_backend(wlr_backend);
struct wl_list **wlr_devices_ptr;
wl_array_for_each(wlr_devices_ptr, &backend->wlr_device_lists) {
struct wlr_libinput_input_device *dev, *tmp;
wl_list_for_each_safe(dev, tmp, *wlr_devices_ptr, link) {
wlr_input_device_destroy(&dev->wlr_input_device);
for (size_t i = 0; i < backend->wlr_device_lists.length; i++) {
struct wl_list *wlr_devices = backend->wlr_device_lists.items[i];
struct wlr_input_device *wlr_dev, *next;
wl_list_for_each_safe(wlr_dev, next, wlr_devices, link) {
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->session_destroy.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) {
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) {
struct wlr_libinput_backend *backend =
wl_container_of(listener, backend, session_signal);
struct wlr_session *session = backend->session;
struct wlr_session *session = data;
if (!backend->libinput_context) {
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) {
struct wlr_libinput_backend *backend =
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);
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->display = display;
backend->session_signal.notify = session_signal;
wl_signal_add(&session->events.active, &backend->session_signal);
backend->session_destroy.notify = handle_session_destroy;
wl_signal_add(&session->events.destroy, &backend->session_destroy);
wl_signal_add(&session->session_signal, &backend->session_signal);
backend->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(display, &backend->display_destroy);
return &backend->backend;
error_backend:
free(backend);
return NULL;
}
struct libinput_device *wlr_libinput_get_device_handle(

View File

@ -7,10 +7,9 @@
#include <wlr/interfaces/wlr_input_device.h>
#include <wlr/util/log.h>
#include "backend/libinput.h"
#include "util/array.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) {
assert(wlr_input_device_is_libinput(wlr_dev));
return (struct wlr_libinput_input_device *)wlr_dev;
@ -23,10 +22,10 @@ struct wlr_input_device *get_appropriate_device(
if (!wlr_devices) {
return NULL;
}
struct wlr_libinput_input_device *dev;
struct wlr_input_device *dev;
wl_list_for_each(dev, wlr_devices, link) {
if (dev->wlr_input_device.type == desired_type) {
return &dev->wlr_input_device;
if (dev->type == desired_type) {
return dev;
}
}
return NULL;
@ -36,7 +35,7 @@ static void input_device_destroy(struct wlr_input_device *wlr_dev) {
struct wlr_libinput_input_device *dev =
get_libinput_device_from_device(wlr_dev);
libinput_device_unref(dev->handle);
wl_list_remove(&dev->link);
wl_list_remove(&dev->wlr_input_device.link);
free(dev);
}
@ -63,7 +62,7 @@ static struct wlr_input_device *allocate_device(
if (output_name != NULL) {
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;
libinput_device_ref(libinput_dev);
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) {
goto fail;
}
wlr_dev->switch_device = create_libinput_switch(libinput_dev);
if (!wlr_dev->switch_device) {
wlr_dev->lid_switch = create_libinput_switch(libinput_dev);
if (!wlr_dev->lid_switch) {
free(wlr_dev);
goto fail;
}
@ -184,13 +183,8 @@ static void handle_device_added(struct wlr_libinput_backend *backend,
}
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);
wlr_list_push(&backend->wlr_device_lists, wlr_devices);
} else {
free(wlr_devices);
}
@ -198,7 +192,7 @@ static void handle_device_added(struct wlr_libinput_backend *backend,
fail:
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) {
free(dev);
}
@ -215,20 +209,15 @@ static void handle_device_removed(struct wlr_libinput_backend *backend,
if (!wlr_devices) {
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) {
wlr_input_device_destroy(&dev->wlr_input_device);
wlr_input_device_destroy(dev);
}
size_t i = 0;
struct wl_list **ptr;
wl_array_for_each(ptr, &backend->wlr_device_lists) {
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 *));
for (size_t i = 0; i < backend->wlr_device_lists.length; i++) {
if (backend->wlr_device_lists.items[i] == wlr_devices) {
wlr_list_del(&backend->wlr_device_lists, i);
break;
}
i++;
}
free(wlr_devices);
}
@ -272,7 +261,7 @@ void handle_libinput_event(struct wlr_libinput_backend *backend,
handle_touch_cancel(event, libinput_dev);
break;
case LIBINPUT_EVENT_TOUCH_FRAME:
handle_touch_frame(event, libinput_dev);
// no-op (at least for now)
break;
case LIBINPUT_EVENT_TABLET_TOOL_AXIS:
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:
handle_pointer_pinch_end(event, libinput_dev);
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:
wlr_log(WLR_DEBUG, "Unknown libinput event %d", event_type);
break;

View File

@ -72,10 +72,10 @@ void handle_keyboard_key(struct libinput_event *event,
libinput_event_keyboard_get_key_state(kbevent);
switch (state) {
case LIBINPUT_KEY_STATE_RELEASED:
wlr_event.state = WL_KEYBOARD_KEY_STATE_RELEASED;
wlr_event.state = WLR_KEY_RELEASED;
break;
case LIBINPUT_KEY_STATE_PRESSED:
wlr_event.state = WL_KEYBOARD_KEY_STATE_PRESSED;
wlr_event.state = WLR_KEY_PRESSED;
break;
}
wlr_event.update_state = true;

View File

@ -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',
)

View File

@ -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);
}
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);
}

View File

@ -51,5 +51,5 @@ void handle_switch_toggle(struct libinput_event *event,
}
wlr_event.time_msec =
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);
}

View File

@ -75,7 +75,6 @@ struct wlr_tablet_pad *create_libinput_tablet_pad(
wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet_pad");
return NULL;
}
wlr_tablet_pad_init(wlr_tablet_pad, NULL);
wlr_tablet_pad->button_count =
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 =
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);
char **dst = wl_array_add(&wlr_tablet_pad->paths, sizeof(char *));
*dst = strdup(udev_device_get_syspath(udev));
wlr_list_push(&wlr_tablet_pad->paths, 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);
for (int i = 0; i < groups; ++i) {
add_pad_group_from_libinput(wlr_tablet_pad, libinput_dev, i);
}
wlr_tablet_pad_init(wlr_tablet_pad, NULL);
return wlr_tablet_pad;
}

View File

@ -11,10 +11,9 @@
#include <wlr/types/wlr_input_device.h>
#include <wlr/util/log.h>
#include "backend/libinput.h"
#include "util/array.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) {
return tablet->impl == &tablet_impl;
@ -30,9 +29,18 @@ struct wlr_libinput_tablet_tool {
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_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) {
@ -48,19 +56,22 @@ static void destroy_tablet(struct wlr_tablet *wlr_tablet) {
struct wlr_libinput_tablet *tablet =
wl_container_of(wlr_tablet, tablet, wlr_tablet);
struct wlr_libinput_tablet_tool **tool_ptr;
wl_array_for_each(tool_ptr, &tablet->tools) {
struct wlr_libinput_tablet_tool *tool = *tool_ptr;
struct tablet_tool_list_elem *pos;
struct tablet_tool_list_elem *tmp;
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) {
destroy_tool(tool);
}
}
wl_array_release(&tablet->tools);
free(tablet);
}
static const struct wlr_tablet_impl tablet_impl = {
static struct wlr_tablet_impl tablet_impl = {
.destroy = destroy_tablet,
};
@ -73,18 +84,15 @@ struct wlr_tablet *create_libinput_tablet(
wlr_log(WLR_ERROR, "Unable to allocate wlr_tablet_tool");
return NULL;
}
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);
char **dst = wl_array_add(&wlr_tablet->paths, sizeof(char *));
*dst = strdup(udev_device_get_syspath(udev));
wlr_list_push(&wlr_tablet->paths, strdup(udev_device_get_syspath(udev)));
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;
}
@ -105,10 +113,9 @@ static enum wlr_tablet_tool_type wlr_type_from_libinput_type(
return WLR_TABLET_TOOL_TYPE_MOUSE;
case LIBINPUT_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(
@ -152,9 +159,9 @@ static void ensure_tool_reference(struct wlr_libinput_tablet_tool *tool,
struct wlr_libinput_tablet *tablet =
wl_container_of(wlr_dev, tablet, wlr_tablet);
struct wlr_libinput_tablet_tool **tool_ptr;
wl_array_for_each(tool_ptr, &tablet->tools) {
if (*tool_ptr == tool) { // We already have a ref
struct tablet_tool_list_elem *pos;
wl_list_for_each(pos, &tablet->tools, link) {
if (pos->tool == tool) { // We already have a ref
// XXX: We *could* optimize the tool to the front of
// the list here, since we will probably get the next
// 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 =
wl_array_add(&tablet->tools, sizeof(tool));
if (!dst) {
struct tablet_tool_list_elem *new =
calloc(1, sizeof(struct tablet_tool_list_elem));
if (!new) {
wlr_log(WLR_ERROR, "Failed to allocate memory for tracking tablet tool");
return;
}
*dst = tool;
new->tool = tool;
wl_list_insert(&tablet->tools, &new->link);
++tool->pad_refs;
}
@ -256,9 +265,6 @@ void handle_tablet_tool_proximity(struct libinput_event *event,
wlr_event.device = wlr_dev;
wlr_event.time_msec =
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)) {
case LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_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));
struct wlr_libinput_tablet *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;
struct wlr_libinput_tablet_tool **tool_ptr;
wl_array_for_each(tool_ptr, &tablet->tools) {
if (*tool_ptr == tool) {
array_remove_at(&tablet->tools, i * sizeof(tool), sizeof(tool));
wl_list_for_each_safe(pos, tmp, &tablet->tools, link) {
if (pos->tool == tool) {
wl_list_remove(&pos->link);
free(pos);
break;
}
i++;
}
destroy_tool(tool);
@ -320,9 +326,6 @@ void handle_tablet_tool_tip(struct libinput_event *event,
wlr_event.tool = &tool->wlr_tool;
wlr_event.time_msec =
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)) {
case LIBINPUT_TABLET_TOOL_TIP_UP:
wlr_event.state = WLR_TABLET_TOOL_TIP_UP;

View File

@ -34,7 +34,7 @@ void handle_touch_down(struct libinput_event *event,
wlr_event.device = wlr_dev;
wlr_event.time_msec =
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.y = libinput_event_touch_get_y_transformed(tevent, 1);
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.time_msec =
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);
}
@ -72,7 +72,7 @@ void handle_touch_motion(struct libinput_event *event,
wlr_event.device = wlr_dev;
wlr_event.time_msec =
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.y = libinput_event_touch_get_y_transformed(tevent, 1);
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.time_msec =
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);
}
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);
}

View File

@ -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']
backends = get_option('backends')
if 'auto' in backends and get_option('auto_features').enabled()
backends = all_backends
elif 'auto' in backends and get_option('auto_features').disabled()
backends = []
backend_deps = [
drm,
egl,
gbm,
libinput,
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
foreach backend : all_backends
if backend in backends or 'auto' in backends
subdir(backend)
endif
endforeach
if logind.found()
backend_files += files('session/logind.c')
backend_deps += logind
endif
subdir('multi')
subdir('wayland')
subdir('headless')
subdir('x11')
subdir('session')
lib_wlr_backend = static_library(
'wlr_backend',
backend_files,
include_directories: wlr_inc,
link_whole: backend_parts,
dependencies: backend_deps,
)

View File

@ -5,9 +5,7 @@
#include <time.h>
#include <wlr/backend/interface.h>
#include <wlr/backend/session.h>
#include <wlr/types/wlr_buffer.h>
#include <wlr/util/log.h>
#include "backend/backend.h"
#include "backend/multi.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);
// Some backends may depend on other backends, ie. destroying a backend may
// also destroy other backends
while (!wl_list_empty(&backend->backends)) {
struct subbackend_state *sub =
wl_container_of(backend->backends.next, sub, link);
struct subbackend_state *sub, *next;
wl_list_for_each_safe(sub, next, &backend->backends, link) {
wlr_backend_destroy(sub->backend);
}
// 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);
}
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(
struct wlr_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;
}
static int multi_backend_get_drm_fd(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) {
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 = {
struct wlr_backend_impl backend_impl = {
.start = multi_backend_start,
.destroy = multi_backend_destroy,
.get_renderer = multi_backend_get_renderer,
.get_session = multi_backend_get_session,
.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) {
@ -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,
struct wlr_backend *backend) {
assert(_multi && backend);
assert(_multi != backend);
struct wlr_multi_backend *multi = multi_backend_from_backend(_multi);
if (multi_backend_get_subbackend(multi, backend)) {
@ -199,12 +169,21 @@ bool wlr_multi_backend_add(struct wlr_backend *_multi,
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));
if (sub == NULL) {
wlr_log(WLR_ERROR, "Could not add backend: allocation failed");
return false;
}
wl_list_insert(multi->backends.prev, &sub->link);
wl_list_insert(&multi->backends, &sub->link);
sub->backend = backend;
sub->container = &multi->backend;

View File

@ -1 +0,0 @@
wlr_files += files('backend.c')

68
backend/noop/backend.c Normal file
View File

@ -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;
}

75
backend/noop/output.c Normal file
View File

@ -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;
}

View File

@ -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,
};

View File

@ -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];
}

278
backend/session/direct.c Normal file
View File

@ -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,
};

683
backend/session/logind.c Normal file
View File

@ -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,
};

View File

@ -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

View File

@ -1,174 +1,32 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <libudev.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
#include <wayland-server-core.h>
#include <wayland-server.h>
#include <wlr/backend/session.h>
#include <wlr/backend/session/interface.h>
#include <wlr/config.h>
#include <wlr/util/log.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include "backend/session/session.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 void handle_enable_seat(struct libseat *seat, void *data) {
struct wlr_session *session = data;
session->active = true;
wlr_signal_emit_safe(&session->events.active, 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 const struct session_impl *impls[] = {
#if WLR_HAS_SYSTEMD || WLR_HAS_ELOGIND
&session_logind,
#endif
&session_direct,
NULL,
};
static enum wlr_log_importance libseat_log_level_to_wlr(
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) {
static int udev_event(int fd, uint32_t mask, void *data) {
struct wlr_session *session = data;
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;
}
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);
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;
}
const char *seat = udev_device_get_property_value(udev_dev, "ID_SEAT");
if (!seat) {
seat = "seat0";
}
if (session->seat[0] != '\0' && strcmp(session->seat, seat) != 0) {
goto out;
}
dev_t devnum = udev_device_get_devnum(udev_dev);
struct wlr_device *dev;
if (strcmp(action, "add") == 0) {
wlr_log(WLR_DEBUG, "DRM device %s added", sysname);
struct wlr_session_add_event event = {
.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);
}
wl_list_for_each(dev, &session->devices, link) {
if (dev->dev == devnum) {
wlr_signal_emit_safe(&dev->signal, session);
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 *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) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
wlr_log(WLR_ERROR, "Failed to load session backend");
return NULL;
}
wl_signal_init(&session->events.active);
wl_signal_init(&session->events.add_drm_card);
session->active = true;
wl_signal_init(&session->session_signal);
wl_signal_init(&session->events.destroy);
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();
if (!session->udev) {
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);
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) {
wlr_log_errno(WLR_ERROR, "Failed to create udev event source");
goto error_mon;
}
session->display = disp;
session->display_destroy.notify = handle_display_destroy;
wl_display_add_destroy_listener(disp, &session->display_destroy);
@ -287,9 +132,7 @@ error_mon:
error_udev:
udev_unref(session->udev);
error_session:
libseat_session_finish(session);
error_open:
free(session);
session->impl->destroy(session);
return NULL;
}
@ -305,22 +148,13 @@ void wlr_session_destroy(struct wlr_session *session) {
udev_monitor_unref(session->mon);
udev_unref(session->udev);
struct wlr_device *dev, *tmp_dev;
wl_list_for_each_safe(dev, tmp_dev, &session->devices, link) {
wlr_session_close_file(session, dev);
}
libseat_session_finish(session);
free(session);
session->impl->destroy(session);
}
struct wlr_device *wlr_session_open_file(struct wlr_session *session,
const char *path) {
int fd;
int device_id = libseat_open_device(session->seat_handle, path, &fd);
if (device_id == -1) {
wlr_log_errno(WLR_ERROR, "Failed to open device: '%s'", path);
return NULL;
int wlr_session_open_file(struct wlr_session *session, const char *path) {
int fd = session->impl->open(session, path);
if (fd < 0) {
return fd;
}
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->dev = st.st_rdev;
dev->device_id = device_id;
wl_signal_init(&dev->events.change);
wl_signal_init(&dev->events.remove);
wl_signal_init(&dev->signal);
wl_list_insert(&session->devices, &dev->link);
return dev;
return fd;
error:
libseat_close_device(session->seat_handle, device_id);
free(dev);
close(fd);
return NULL;
return fd;
}
void wlr_session_close_file(struct wlr_session *session,
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);
static struct wlr_device *find_device(struct wlr_session *session, int fd) {
struct wlr_device *dev;
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);
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) {
if (!session) {
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
* device on success. */
struct wlr_device *session_open_if_kms(struct wlr_session *restrict session,
const char *restrict path) {
/* Tests if 'path' is KMS compatible by trying to open it.
* It leaves the open device in *fd_out it it succeeds.
*/
static int open_if_kms(struct wlr_session *restrict session, const char *restrict path) {
int fd;
if (!path) {
return NULL;
return -1;
}
struct wlr_device *dev = wlr_session_open_file(session, path);
if (!dev) {
return NULL;
fd = wlr_session_open_file(session, path);
if (fd < 0) {
return -1;
}
if (!drmIsKMS(dev->fd)) {
wlr_log(WLR_DEBUG, "Ignoring '%s': not a KMS device", path);
wlr_session_close_file(session, dev);
return NULL;
drmModeRes *res = drmModeGetResources(fd);
if (!res) {
goto out_fd;
}
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,
size_t ret_len, struct wlr_device *ret[static ret_len], const char *str) {
static size_t explicit_find_gpus(struct wlr_session *session,
size_t ret_len, int ret[static ret_len], const char *str) {
char *gpus = strdup(str);
if (!gpus) {
wlr_log_errno(WLR_ERROR, "Allocation failed");
return -1;
return 0;
}
size_t i = 0;
@ -406,8 +261,8 @@ static ssize_t explicit_find_gpus(struct wlr_session *session,
break;
}
ret[i] = session_open_if_kms(session, ptr);
if (!ret[i]) {
ret[i] = open_if_kms(session, ptr);
if (ret[i] < 0) {
wlr_log(WLR_ERROR, "Unable to open %s as DRM device", ptr);
} else {
++i;
@ -418,92 +273,30 @@ static ssize_t explicit_find_gpus(struct wlr_session *session,
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.
* 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 ret_len, struct wlr_device **ret) {
size_t wlr_session_find_gpus(struct wlr_session *session,
size_t ret_len, int *ret) {
const char *explicit = getenv("WLR_DRM_DEVICES");
if (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) {
wlr_log(WLR_ERROR, "Failed to create udev enumeration");
return -1;
}
if (udev_enumerate_get_list_entry(en) == NULL) {
udev_enumerate_unref(en);
wlr_log(WLR_INFO, "Waiting for a DRM card device");
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;
}
}
udev_enumerate_add_match_subsystem(en, "drm");
udev_enumerate_add_match_sysname(en, "card[0-9]*");
udev_enumerate_scan_devices(en);
struct udev_list_entry *entry;
size_t i = 0;
@ -541,18 +334,17 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session,
}
}
struct wlr_device *wlr_dev =
session_open_if_kms(session, udev_device_get_devnode(dev));
if (!wlr_dev) {
int fd = open_if_kms(session, udev_device_get_devnode(dev));
if (fd < 0) {
udev_device_unref(dev);
continue;
}
udev_device_unref(dev);
ret[i] = wlr_dev;
ret[i] = fd;
if (is_boot_vga) {
struct wlr_device *tmp = ret[0];
int tmp = ret[0];
ret[0] = ret[i];
ret[i] = tmp;
}
@ -563,4 +355,5 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session,
udev_enumerate_unref(en);
return i;
#endif
}

View File

@ -1,51 +1,24 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
#include <fcntl.h>
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <drm_fourcc.h>
#include <wayland-server-core.h>
#include <xf86drm.h>
#include <wlr/config.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <wayland-server.h>
#include <wlr/backend/interface.h>
#include <wlr/interfaces/wlr_input_device.h>
#include <wlr/interfaces/wlr_output.h>
#include <wlr/render/egl.h>
#include <wlr/render/gles2.h>
#include <wlr/util/log.h>
#include "backend/wayland.h"
#include "render/drm_format_set.h"
#include "render/pixel_format.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 "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) {
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;
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);
return 0;
}
int count = 0;
if (mask & WL_EVENT_READABLE) {
count = wl_display_dispatch(wl->remote_display);
return wl_display_dispatch(wl->remote_display);
}
if (mask & WL_EVENT_WRITABLE) {
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 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,
@ -92,302 +58,26 @@ static const struct xdg_wm_base_listener xdg_wm_base_listener = {
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,
uint32_t name, const char *iface, uint32_t version) {
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) {
wl->compositor = wl_registry_bind(registry, name,
&wl_compositor_interface, 4);
} 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);
if (!create_wl_seat(wl_seat, wl)) {
wl_seat_destroy(wl_seat);
}
wl_seat_add_listener(wl->seat, &seat_listener, wl);
} 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) {
wl->xdg_wm_base = wl_registry_bind(registry, name,
&xdg_wm_base_interface, 1);
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) {
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;
struct wlr_wl_seat *seat;
wl_list_for_each(seat, &wl->seats, link) {
if (seat->keyboard) {
create_wl_keyboard(seat);
}
if (wl->tablet_manager) {
wl_add_tablet_seat(wl->tablet_manager, seat);
}
if (wl->keyboard) {
create_wl_keyboard(wl->keyboard, wl);
}
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);
}
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) {
wlr_input_device_destroy(&input_device->wlr_input_device);
wlr_input_device_destroy(input_device);
}
struct wlr_wl_buffer *buffer, *tmp_buffer;
wl_list_for_each_safe(buffer, tmp_buffer, &wl->buffers, link) {
destroy_wl_buffer(buffer);
}
wlr_backend_finish(backend);
wlr_signal_emit_safe(&wl->backend.events.destroy, &wl->backend);
wl_list_remove(&wl->local_display_destroy.link);
free(wl->seat_name);
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);
wlr_drm_format_set_finish(&wl->linux_dmabuf_v1_formats);
destroy_wl_seats(wl);
if (wl->zxdg_decoration_manager_v1) {
zxdg_decoration_manager_v1_destroy(wl->zxdg_decoration_manager_v1);
if (wl->pointer) {
wl_pointer_destroy(wl->pointer);
}
if (wl->zwp_pointer_gestures_v1) {
zwp_pointer_gestures_v1_destroy(wl->zwp_pointer_gestures_v1);
}
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->seat) {
wl_seat_destroy(wl->seat);
}
if (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);
wl_compositor_destroy(wl->compositor);
wl_registry_destroy(wl->registry);
wl_display_flush(wl->remote_display);
wl_display_disconnect(wl->remote_display);
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);
return wl->presentation_clock;
return wl->renderer;
}
static int backend_get_drm_fd(struct wlr_backend *backend) {
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 = {
static struct wlr_backend_impl backend_impl = {
.start = backend_start,
.destroy = backend_destroy,
.get_presentation_clock = backend_get_presentation_clock,
.get_drm_fd = backend_get_drm_fd,
.get_buffer_caps = get_buffer_caps,
.get_renderer = backend_get_renderer,
};
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,
const char *remote) {
const char *remote, wlr_renderer_create_func_t create_renderer_func) {
wlr_log(WLR_INFO, "Creating wayland backend");
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_list_init(&wl->devices);
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);
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");
goto error_display;
}
wl_registry_add_listener(wl->registry, &registry_listener, wl);
wl_display_roundtrip(wl->remote_display); // get globals
wl_registry_add_listener(wl->registry, &registry_listener, wl);
wl_display_dispatch(wl->remote_display);
wl_display_roundtrip(wl->remote_display);
if (!wl->compositor) {
wlr_log(WLR_ERROR,
@ -571,38 +221,10 @@ struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
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);
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);
if (!wl->remote_display_src) {
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);
if (wl->drm_render_name != NULL) {
wlr_log(WLR_DEBUG, "Opening DRM render node %s", wl->drm_render_name);
wl->drm_fd = open(wl->drm_render_name, O_RDWR | O_NONBLOCK | O_CLOEXEC);
if (wl->drm_fd < 0) {
wlr_log_errno(WLR_ERROR, "Failed to open DRM render node %s",
wl->drm_render_name);
goto error_remote_display_src;
}
} else {
wl->drm_fd = -1;
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, 1,
EGL_NONE,
};
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_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;
error_remote_display_src:
error_event:
wl_event_source_remove(wl->remote_display_src);
error_registry:
free(wl->drm_render_name);
if (wl->compositor) {
wl_compositor_destroy(wl->compositor);
}
@ -647,12 +271,6 @@ error_registry:
error_display:
wl_display_disconnect(wl->remote_display);
error_wl:
wlr_backend_finish(&wl->backend);
free(wl);
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;
}

View File

@ -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

View File

@ -7,7 +7,6 @@
#include <sys/types.h>
#include <unistd.h>
#include <drm_fourcc.h>
#include <wayland-client.h>
#include <wlr/interfaces/wlr_output.h>
@ -16,28 +15,17 @@
#include <wlr/util/log.h>
#include "backend/wayland.h"
#include "render/pixel_format.h"
#include "render/swapchain.h"
#include "render/wlr_renderer.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"
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(
struct wlr_output *wlr_output) {
assert(wlr_output_is_wl(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,
uint32_t time) {
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);
}
static const struct wl_callback_listener frame_listener = {
static struct wl_callback_listener frame_listener = {
.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,
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;
}
void destroy_wl_buffer(struct wlr_wl_buffer *buffer) {
if (buffer == NULL) {
return;
}
wl_list_remove(&buffer->buffer_destroy.link);
wl_list_remove(&buffer->link);
wl_buffer_destroy(buffer->wl_buffer);
free(buffer);
static bool output_make_current(struct wlr_output *wlr_output,
int *buffer_age) {
struct wlr_wl_output *output =
get_wl_output_from_output(wlr_output);
return wlr_egl_make_current(&output->backend->egl, output->egl_surface,
buffer_age);
}
static void buffer_handle_release(void *data, struct wl_buffer *wl_buffer) {
struct wlr_wl_buffer *buffer = data;
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) {
static bool output_swap_buffers(struct wlr_output *wlr_output,
pixman_region32_t *damage) {
struct wlr_wl_output *output =
get_wl_output_from_output(wlr_output);
uint32_t unsupported =
wlr_output->pending.committed & ~SUPPORTED_OUTPUT_STATE;
if (unsupported != 0) {
wlr_log(WLR_DEBUG, "Unsupported output state fields: 0x%"PRIx32,
unsupported);
if (output->frame_callback != NULL) {
wlr_log(WLR_ERROR, "Skipping buffer swap");
return false;
}
if (wlr_output->pending.committed & WLR_OUTPUT_STATE_MODE) {
assert(wlr_output->pending.mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM);
}
output->frame_callback = wl_surface_frame(output->surface);
wl_callback_add_listener(output->frame_callback, &frame_listener, output);
if ((wlr_output->pending.committed & WLR_OUTPUT_STATE_BUFFER) &&
!test_buffer(output->backend, wlr_output->pending.buffer)) {
if (!wlr_egl_swap_buffers(&output->backend->egl,
output->egl_surface, damage)) {
return false;
}
// TODO: if available, use the presentation-time protocol
wlr_output_send_present(wlr_output, NULL);
return true;
}
static bool output_commit(struct wlr_output *wlr_output) {
struct wlr_wl_output *output =
get_wl_output_from_output(wlr_output);
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 void output_transform(struct wlr_output *wlr_output,
enum wl_output_transform transform) {
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
output->wlr_output.transform = transform;
}
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_backend *backend = output->backend;
output->cursor.hotspot_x = hotspot_x;
output->cursor.hotspot_y = hotspot_y;
struct wlr_box hotspot = { .x = hotspot_x, .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) {
output->cursor.surface =
@ -368,37 +115,53 @@ static bool output_set_cursor(struct wlr_output *wlr_output,
}
struct wl_surface *surface = output->cursor.surface;
if (wlr_buffer != NULL) {
struct wlr_wl_buffer *buffer =
get_or_create_wl_buffer(output->backend, wlr_buffer);
if (buffer == NULL) {
return false;
}
if (texture != NULL) {
int width, height;
wlr_texture_get_size(texture, &width, &height);
width = width * wlr_output->scale / scale;
height = height * wlr_output->scale / scale;
wl_surface_attach(surface, buffer->wl_buffer, 0, 0);
wl_surface_damage_buffer(surface, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_commit(surface);
output->cursor.width = width;
output->cursor.height = height;
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 {
wl_surface_attach(surface, NULL, 0, 0);
wl_surface_commit(surface);
}
update_wl_output_cursor(output);
wl_display_flush(backend->remote_display);
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) {
struct wlr_wl_output *output = get_wl_output_from_output(wlr_output);
if (output == NULL) {
@ -407,6 +170,9 @@ static void output_destroy(struct wlr_output *wlr_output) {
wl_list_remove(&output->link);
if (output->cursor.egl_window != NULL) {
wl_egl_window_destroy(output->cursor.egl_window);
}
if (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);
}
struct wlr_wl_presentation_feedback *feedback, *feedback_tmp;
wl_list_for_each_safe(feedback, feedback_tmp,
&output->presentation_feedbacks, link) {
presentation_feedback_destroy(feedback);
}
if (output->zxdg_toplevel_decoration_v1) {
zxdg_toplevel_decoration_v1_destroy(output->zxdg_toplevel_decoration_v1);
}
wlr_egl_destroy_surface(&output->backend->egl, output->egl_surface);
wl_egl_window_destroy(output->egl_window);
xdg_toplevel_destroy(output->xdg_toplevel);
xdg_surface_destroy(output->xdg_surface);
wl_surface_destroy(output->surface);
wl_display_flush(output->backend->remote_display);
free(output);
}
void update_wl_output_cursor(struct wlr_wl_output *output) {
struct wlr_wl_pointer *pointer = output->cursor.pointer;
if (pointer) {
assert(pointer->output == output);
assert(output->enter_serial);
wl_pointer_set_cursor(pointer->wl_pointer, output->enter_serial,
if (output->backend->pointer && output->enter_serial) {
wl_pointer_set_cursor(output->backend->pointer, output->enter_serial,
output->cursor.surface, output->cursor.hotspot_x,
output->cursor.hotspot_y);
}
@ -447,14 +202,29 @@ static bool output_move_cursor(struct wlr_output *_output, int x, int y) {
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 = {
.set_custom_mode = output_set_custom_mode,
.transform = output_transform,
.destroy = output_destroy,
.test = output_test,
.commit = output_commit,
.make_current = output_make_current,
.swap_buffers = output_swap_buffers,
.set_cursor = output_set_cursor,
.move_cursor = output_move_cursor,
.get_cursor_formats = output_get_formats,
.get_primary_formats = output_get_formats,
.schedule_frame = output_schedule_frame,
};
bool wlr_output_is_wl(struct wlr_output *wlr_output) {
@ -471,7 +241,7 @@ static void xdg_surface_handle_configure(void *data,
// nothing else?
}
static const struct xdg_surface_listener xdg_surface_listener = {
static struct xdg_surface_listener xdg_surface_listener = {
.configure = xdg_surface_handle_configure,
};
@ -493,10 +263,10 @@ static void xdg_toplevel_handle_close(void *data,
struct wlr_wl_output *output = data;
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,
.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);
strncpy(wlr_output->make, "wayland", sizeof(wlr_output->make));
strncpy(wlr_output->model, "wayland", sizeof(wlr_output->model));
char name[64];
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);
snprintf(wlr_output->name, sizeof(wlr_output->name), "WL-%d",
wl_list_length(&backend->outputs) + 1);
output->backend = backend;
wl_list_init(&output->presentation_feedbacks);
output->surface = wl_compositor_create_surface(backend->compositor);
if (!output->surface) {
@ -552,18 +314,6 @@ struct wlr_output *wlr_wl_output_create(struct wlr_backend *wlr_backend) {
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);
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);
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);
// 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);
wlr_output_update_enabled(wlr_output, true);
wlr_signal_emit_safe(&backend->backend.events.new_output, wlr_output);
struct wlr_wl_seat *seat;
wl_list_for_each(seat, &backend->seats, link) {
if (seat->pointer) {
create_wl_pointer(seat, output);
}
if (backend->pointer != NULL) {
create_wl_pointer(backend->pointer, 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;
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);
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;
}

View File

@ -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;
}

View File

@ -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;
}

455
backend/wayland/wl_seat.c Normal file
View File

@ -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,
};

View File

@ -1,24 +1,17 @@
#define _POSIX_C_SOURCE 200809L
#define _POSIX_C_SOURCE 200112L
#include <assert.h>
#include <fcntl.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <xf86drm.h>
#include <wlr/config.h>
#include <drm_fourcc.h>
#include <wayland-server-core.h>
#include <X11/Xlib-xcb.h>
#include <wayland-server.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/xinput.h>
@ -27,27 +20,13 @@
#include <wlr/interfaces/wlr_input_device.h>
#include <wlr/interfaces/wlr_keyboard.h>
#include <wlr/interfaces/wlr_pointer.h>
#include <wlr/render/egl.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/util/log.h>
#include "backend/x11.h"
#include "render/drm_format_set.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_backend *x11, xcb_window_t window) {
struct wlr_x11_output *output;
@ -59,10 +38,6 @@ struct wlr_x11_output *get_x11_output_from_window_id(
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,
xcb_generic_event_t *event) {
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 =
get_x11_output_from_window_id(x11, ev->window);
if (output != NULL) {
pixman_region32_union_rect(
&output->exposed, &output->exposed,
ev->x, ev->y, ev->width, ev->height);
wlr_output_update_needs_frame(&output->wlr_output);
wlr_output_update_needs_swap(&output->wlr_output);
}
break;
}
@ -96,9 +68,6 @@ static void handle_x11_event(struct wlr_x11_backend *x11,
if (output != NULL) {
wlr_output_destroy(&output->wlr_output);
}
} else {
wlr_log(WLR_DEBUG, "Unhandled client message %"PRIu32,
ev->data.data32[0]);
}
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;
if (ev->extension == x11->xinput_opcode) {
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;
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);
return 0;
}
@ -144,12 +94,6 @@ static int x11_event(int fd, uint32_t mask, void *data) {
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;
}
@ -163,8 +107,6 @@ static bool backend_start(struct wlr_backend *backend) {
struct wlr_x11_backend *x11 = get_x11_backend_from_backend(backend);
x11->started = true;
wlr_log(WLR_INFO, "Starting X11 backend");
wlr_signal_emit_safe(&x11->backend.events.new_input, &x11->keyboard_dev);
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_backend_finish(backend);
wlr_signal_emit_safe(&backend->events.destroy, backend);
if (x11->event_source) {
wl_event_source_remove(x11->event_source);
}
wl_list_remove(&x11->display_destroy.link);
wlr_drm_format_set_finish(&x11->primary_dri3_formats);
wlr_drm_format_set_finish(&x11->primary_shm_formats);
wlr_drm_format_set_finish(&x11->dri3_formats);
wlr_drm_format_set_finish(&x11->shm_formats);
wlr_renderer_destroy(x11->renderer);
wlr_egl_finish(&x11->egl);
#if HAS_XCB_ERRORS
xcb_errors_context_free(x11->errors_context);
#endif
close(x11->drm_fd);
xcb_disconnect(x11->xcb);
if (x11->xlib_conn) {
XCloseDisplay(x11->xlib_conn);
}
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);
return x11->drm_fd;
}
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);
return x11->renderer;
}
static const struct wlr_backend_impl backend_impl = {
.start = backend_start,
.destroy = backend_destroy,
.get_drm_fd = backend_get_drm_fd,
.get_buffer_caps = get_buffer_caps,
.get_renderer = backend_get_renderer,
};
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);
}
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,
const char *x11_display) {
wlr_log(WLR_INFO, "Creating X11 backend");
const char *x11_display,
wlr_renderer_create_func_t create_renderer_func) {
struct wlr_x11_backend *x11 = calloc(1, sizeof(*x11));
if (!x11) {
return NULL;
@ -405,12 +180,20 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
x11->wl_display = display;
wl_list_init(&x11->outputs);
x11->xcb = xcb_connect(x11_display, NULL);
if (!x11->xcb || xcb_connection_has_error(x11->xcb)) {
wlr_log(WLR_ERROR, "Failed to open xcb connection");
x11->xlib_conn = XOpenDisplay(x11_display);
if (!x11->xlib_conn) {
wlr_log(WLR_ERROR, "Failed to open X connection");
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 {
const char *name;
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 = "_NET_WM_NAME", .atom = &x11->atoms.net_wm_name },
{ .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) {
@ -442,80 +224,6 @@ struct wlr_backend *wlr_x11_backend_create(struct wl_display *display,
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);
if (!ext || !ext->present) {
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_reply_t *fixes_reply =
xcb_xfixes_query_version_reply(x11->xcb, fixes_cookie, NULL);
if (!fixes_reply || fixes_reply->major_version < 4) {
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);
wlr_log(WLR_ERROR, "X11 does not support required Xfixes version");
free(fixes_reply);
goto error_display;
}
free(fixes_reply);
// Xinput extension
ext = xcb_get_extension_data(x11->xcb, &xcb_input_id);
if (!ext || !ext->present) {
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_reply_t *xi_reply =
xcb_input_xi_query_version_reply(x11->xcb, xi_cookie, NULL);
if (!xi_reply || xi_reply->major_version < 2) {
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);
wlr_log(WLR_ERROR, "X11 does not support required Xinput version");
free(xi_reply);
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);
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;
}
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,
&input_device_impl, "X11 keyboard", 0, 0);
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;
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;
error_event:
wl_event_source_remove(x11->event_source);
error_display:
xcb_disconnect(x11->xcb);
XCloseDisplay(x11->xlib_conn);
error_x11:
free(x11);
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
}

View File

@ -4,8 +4,6 @@
#include <linux/input-event-codes.h>
#include <wayland-server-protocol.h>
#include <xcb/xcb.h>
#include <xcb/xfixes.h>
#include <xcb/xinput.h>
@ -13,14 +11,13 @@
#include <wlr/interfaces/wlr_input_device.h>
#include <wlr/interfaces/wlr_keyboard.h>
#include <wlr/interfaces/wlr_pointer.h>
#include <wlr/interfaces/wlr_touch.h>
#include <wlr/util/log.h>
#include "backend/x11.h"
#include "util/signal.h"
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 = {
.time_msec = time,
.keycode = key,
@ -39,7 +36,6 @@ static void send_button_event(struct wlr_x11_output *output, uint32_t key,
.state = st,
};
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,
@ -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);
}
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,
xcb_ge_generic_event_t *event) {
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 *)event;
if (ev->flags & XCB_INPUT_KEY_EVENT_FLAGS_KEY_REPEAT) {
return;
}
wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base,
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;
break;
}
@ -142,7 +86,7 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11,
wlr_keyboard_notify_modifiers(&x11->keyboard, ev->mods.base,
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;
break;
}
@ -219,68 +163,34 @@ void handle_x11_xinput_event(struct wlr_x11_backend *x11,
x11->time = ev->time;
break;
}
case XCB_INPUT_TOUCH_BEGIN: {
xcb_input_touch_begin_event_t *ev = (xcb_input_touch_begin_event_t *)event;
case XCB_INPUT_ENTER: {
xcb_input_enter_event_t *ev = (xcb_input_enter_event_t *)event;
output = get_x11_output_from_window_id(x11, ev->event);
if (!output) {
return;
}
int32_t id = 0;
if (!wl_list_empty(&output->touchpoints)) {
struct wlr_x11_touchpoint *last_touchpoint = wl_container_of(
output->touchpoints.next, last_touchpoint, link);
id = last_touchpoint->wayland_id + 1;
if (!output->cursor_hidden) {
xcb_xfixes_hide_cursor(x11->xcb, output->win);
xcb_flush(x11->xcb);
output->cursor_hidden = true;
}
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;
}
case XCB_INPUT_TOUCH_END: {
xcb_input_touch_end_event_t *ev = (xcb_input_touch_end_event_t *)event;
case XCB_INPUT_LEAVE: {
xcb_input_leave_event_t *ev = (xcb_input_leave_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;
if (output->cursor_hidden) {
xcb_xfixes_show_cursor(x11->xcb, output->win);
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;
}
}
@ -310,14 +220,6 @@ const struct wlr_pointer_impl pointer_impl = {
.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,
xcb_timestamp_t time) {
struct wlr_x11_backend *x11 = output->x11;

View File

@ -1,25 +1,13 @@
x11_libs = []
x11_required = [
'x11-xcb',
'xcb',
'xcb-dri3',
'xcb-present',
'xcb-render',
'xcb-renderutil',
'xcb-shm',
'xcb-xfixes',
'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
dep = dependency(lib,
required: 'x11' in backends,
not_found_message: '\n'.join(msg).format(lib),
)
dep = dependency(lib, required: get_option('x11-backend'))
if not dep.found()
subdir_done()
endif
@ -27,10 +15,21 @@ foreach lib : x11_required
x11_libs += dep
endforeach
wlr_files += files(
'backend.c',
'input_device.c',
'output.c',
lib_wlr_backend_x11 = static_library(
'wlr_backend_x11',
files(
'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)

View File

@ -3,31 +3,23 @@
#include <assert.h>
#include <stdlib.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/xinput.h>
#include <wlr/interfaces/wlr_output.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 "backend/x11.h"
#include "util/signal.h"
#include "util/time.h"
static const uint32_t SUPPORTED_OUTPUT_STATE =
WLR_OUTPUT_STATE_BACKEND_OPTIONAL |
WLR_OUTPUT_STATE_BUFFER |
WLR_OUTPUT_STATE_MODE;
static int signal_frame(void *data) {
struct wlr_x11_output *output = data;
wlr_output_send_frame(&output->wlr_output);
wl_event_source_timer_update(output->frame_timer, output->frame_delay);
return 0;
}
static void parse_xcb_setup(struct wlr_output *output,
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;
}
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,
int32_t width, int32_t height, int32_t refresh) {
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
struct wlr_x11_backend *x11 = output->x11;
output_set_refresh(&output->wlr_output, refresh);
const uint32_t values[] = { width, height };
xcb_void_cookie_t cookie = xcb_configure_window_checked(
x11->xcb, output->win,
@ -68,427 +75,53 @@ static bool output_set_custom_mode(struct wlr_output *wlr_output,
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) {
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
struct wlr_x11_backend *x11 = output->x11;
pixman_region32_fini(&output->exposed);
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);
if (output->cursor.pic != XCB_NONE) {
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);
wl_event_source_remove(output->frame_timer);
wlr_egl_destroy_surface(&x11->egl, output->surf);
xcb_destroy_window(x11->xcb, output->win);
xcb_flush(x11->xcb);
free(output);
}
static bool output_test(struct wlr_output *wlr_output) {
uint32_t unsupported =
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) {
static bool output_make_current(struct wlr_output *wlr_output,
int *buffer_age) {
struct wlr_x11_output *output = get_x11_output_from_output(wlr_output);
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;
}
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_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);
wlr_output_send_present(wlr_output, NULL);
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 = {
.set_custom_mode = output_set_custom_mode,
.transform = output_transform,
.destroy = output_destroy,
.test = output_test,
.commit = output_commit,
.set_cursor = output_set_cursor,
.move_cursor = output_move_cursor,
.get_primary_formats = output_get_primary_formats,
.make_current = output_make_current,
.swap_buffers = output_swap_buffers,
};
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;
}
output->x11 = x11;
wl_list_init(&output->buffers);
pixman_region32_init(&output->exposed);
struct wlr_output *wlr_output = &output->wlr_output;
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];
snprintf(name, sizeof(name), "X11-%zu", ++x11->last_output_num);
wlr_output_set_name(wlr_output, name);
output_set_refresh(&output->wlr_output, 0);
snprintf(wlr_output->name, sizeof(wlr_output->name), "X11-%d",
wl_list_length(&x11->outputs) + 1);
parse_xcb_setup(wlr_output, x11->xcb);
char description[128];
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 mask = XCB_CW_EVENT_MASK;
uint32_t values[] = {
0,
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY,
x11->colormap,
x11->transparent_cursor,
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY
};
output->win = xcb_generate_id(x11->xcb);
xcb_create_window(x11->xcb, x11->depth->depth, output->win,
x11->screen->root, 0, 0, wlr_output->width, wlr_output->height, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT, x11->visualid, mask, values);
xcb_create_window(x11->xcb, XCB_COPY_FROM_PARENT, output->win,
x11->screen->root, 0, 0, wlr_output->width, wlr_output->height, 1,
XCB_WINDOW_CLASS_INPUT_OUTPUT, x11->screen->root_visual, mask, values);
struct {
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_RELEASE |
XCB_INPUT_XI_EVENT_MASK_MOTION |
XCB_INPUT_XI_EVENT_MASK_TOUCH_BEGIN |
XCB_INPUT_XI_EVENT_MASK_TOUCH_END |
XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE,
XCB_INPUT_XI_EVENT_MASK_ENTER |
XCB_INPUT_XI_EVENT_MASK_LEAVE,
};
xcb_input_xi_select_events(x11->xcb, output->win, 1, &xinput_mask.head);
uint32_t present_mask = XCB_PRESENT_EVENT_MASK_IDLE_NOTIFY |
XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY;
output->present_event_id = xcb_generate_id(x11->xcb);
xcb_present_select_input(x11->xcb, output->present_event_id, output->win,
present_mask);
output->surf = wlr_egl_create_surface(&x11->egl, &output->win);
if (!output->surf) {
wlr_log(WLR_ERROR, "Failed to create EGL surface");
free(output);
return NULL;
}
xcb_change_property(x11->xcb, XCB_PROP_MODE_REPLACE, output->win,
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_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_event_source_timer_update(output->frame_timer, output->frame_delay);
wlr_output_update_enabled(wlr_output, true);
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.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_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;
}
@ -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,
xcb_configure_notify_event_t *ev) {
// 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,
"Ignoring X11 configure event for height=%d, width=%d",
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) {
@ -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,
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);
}
}

View File

@ -1,54 +1,34 @@
wlroots reads these environment variables
# 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
wlroots specific
----------------
* *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
considered the primary DRM device.
* *WLR_DRM_NO_ATOMIC*: set to 1 to use legacy DRM interface instead of atomic
mode setting
* *WLR_DRM_NO_MODIFIERS*: set to 1 to always allocate planes without modifiers,
this can fix certain modeset failures because of bandwidth restrictions.
## Headless backend
* *WLR_DRM_NO_ATOMIC_GAMMA*: set to 1 to use legacy DRM interface for gamma
control instead of the atomic interface
* *WLR_LIBINPUT_NO_DEVICES*: set to 1 to not fail without any input devices
* *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
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
## Wayland backend
* *WLR_WL_OUTPUTS*: when using the wayland backend specifies the number of outputs
## 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`
generic
-------
* *DISPLAY*: if set probe X11 backend in *wlr_backend_autocreate*
* *WAYLAND_DISPLAY*, *_WAYLAND_DISPLAY*, *WAYLAND_SOCKET*: if set probe Wayland
backend in *wlr_backend_autocreate*
* *XCURSOR_PATH*: directory where xcursors are located
* *XDG_SESSION_ID*: if set, session ID used by the logind session

View File

@ -205,11 +205,10 @@ static void remove_output(struct wayland_output *out) {
}
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;
wl_list_for_each_safe(output, tmp, &ctx->output_list, link) {
if (output->output == out || (id >= 0 && output->id == (uint32_t)id)
|| id == -1) {
if ((output->output == out) || (output->id == id)) {
return output;
}
}
@ -462,7 +461,7 @@ static void register_cb(struct capture_context *ctx) {
&frame_listener, ctx);
}
static void *vid_encode_thread(void *arg) {
void *vid_encode_thread(void *arg) {
int err = 0;
struct capture_context *ctx = arg;
@ -490,27 +489,26 @@ static void *vid_encode_thread(void *arg) {
}
while (1) {
AVPacket *pkt = av_packet_alloc();
int ret = avcodec_receive_packet(ctx->avctx, pkt);
AVPacket pkt;
av_init_packet(&pkt);
int ret = avcodec_receive_packet(ctx->avctx, &pkt);
if (ret == AVERROR(EAGAIN)) {
av_packet_free(&pkt);
break;
} else if (ret == AVERROR_EOF) {
av_log(ctx, AV_LOG_INFO, "Encoder flushed!\n");
av_packet_free(&pkt);
goto end;
} else if (ret) {
av_log(ctx, AV_LOG_ERROR, "Error encoding: %s!\n",
av_err2str(ret));
av_packet_free(&pkt);
err = ret;
goto end;
}
pkt->stream_index = 0;
err = av_interleaved_write_frame(ctx->avf, pkt);
pkt.stream_index = 0;
err = av_interleaved_write_frame(ctx->avf, &pkt);
av_packet_free(&pkt);
av_packet_unref(&pkt);
if (err) {
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;
static void on_quit_signal(int signo) {
void on_quit_signal(int signo) {
printf("\r");
av_log(q_ctx, AV_LOG_WARNING, "Quitting!\n");
q_ctx->quit = true;
@ -754,11 +752,8 @@ static int init(struct capture_context *ctx) {
ctx->registry = wl_display_get_registry(ctx->display);
wl_registry_add_listener(ctx->registry, &registry_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_dispatch(ctx->display);
if (!ctx->export_manager) {
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 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[]) {
struct capture_context ctx = {
.hardware_device = "/dev/dri/renderD128",
.encoder_name = "libx264",
.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){
int err;
struct capture_context ctx = { 0 };
ctx.class = &((AVClass) {
.class_name = "dmabuf-capture",
.item_name = av_default_item_name,
.version = LIBAVUTIL_VERSION_INT,
});
int err = init(&ctx);
err = init(&ctx);
if (err) {
goto end;
}
@ -852,16 +786,31 @@ int main(int argc, char *argv[]) {
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) {
printf("Unable to find output with ID %d\n", output_id);
printf("Unable to find output with ID %i!\n", o_id);
return 1;
}
ctx.target_output = o->output;
ctx.with_cursor = true;
ctx.hw_device_type = av_hwdevice_find_type_by_name(hw_device_type);
ctx.software_format = av_get_pix_fmt(software_format);
ctx.hw_device_type = av_hwdevice_find_type_by_name(argv[2]);
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);

View File

@ -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();
}

View File

@ -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);

View File

@ -7,48 +7,39 @@
#include <wayland-client.h>
#include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h"
#define WLR_FOREIGN_TOPLEVEL_MANAGEMENT_VERSION 3
static void print_help(void) {
static const char usage[] =
"Usage: foreign-toplevel [OPTIONS] ...\n"
"Manage and view information about toplevel windows.\n"
"\n"
" -f <id> focus\n"
" -s <id> fullscreen\n"
" -o <output_id> select output for fullscreen toplevel to appear on. Use this\n"
" option with -s. View available outputs with wayland-info.\n"
" -S <id> unfullscreen\n"
" -a <id> maximize\n"
" -u <id> unmaximize\n"
" -i <id> minimize\n"
" -r <id> restore(unminimize)\n"
" -c <id> close\n"
" -m continuously print changes to the list of opened toplevels\n"
" Can be used together with some of the previous options.\n"
" -h print help message and quit\n";
fprintf(stderr, "%s", usage);
}
/**
* Usage:
* 1. foreign-toplevel
* Prints a list of opened toplevels
* 2. foreign-toplevel -f <id>
* Focus the toplevel with the given id
* 3. foreign-toplevel -a <id>
* Maximize the toplevel with the given id
* 4. foreign-toplevel -u <id>
* Unmaximize the toplevel with the given id
* 5. foreign-toplevel -i <id>
* Minimize the toplevel with the given id
* 6. foreign-toplevel -r <id>
* Restore(unminimize) the toplevel with the given id
* 7. foreign-toplevel -c <id>
* Close the toplevel with the given id
* 8. foreign-toplevel -m
* Continuously print changes to the list of opened toplevels.
* Can be used together with some of the previous options.
*/
enum toplevel_state_field {
TOPLEVEL_STATE_MAXIMIZED = (1 << 0),
TOPLEVEL_STATE_MINIMIZED = (1 << 1),
TOPLEVEL_STATE_ACTIVATED = (1 << 2),
TOPLEVEL_STATE_FULLSCREEN = (1 << 3),
TOPLEVEL_STATE_INVALID = (1 << 4),
TOPLEVEL_STATE_MAXIMIZED = 1,
TOPLEVEL_STATE_MINIMIZED = 2,
TOPLEVEL_STATE_ACTIVATED = 4,
TOPLEVEL_STATE_INVALID = 8,
};
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 {
char *title;
char *app_id;
uint32_t state;
uint32_t parent_id;
};
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->parent_id = pending->parent_id;
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.app_id ?: "(nil)");
if (toplevel->current.parent_id != no_parent) {
printf(" parent=%u", toplevel->current.parent_id);
} else {
printf(" no parent");
}
if (print_endl) {
printf("\n");
}
@ -119,20 +102,12 @@ static void print_toplevel_state(struct toplevel_v1 *toplevel, bool print_endl)
} else {
printf(" inactive");
}
if (toplevel->current.state & TOPLEVEL_STATE_FULLSCREEN) {
printf(" fullscreen");
}
if (print_endl) {
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,
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel,
const char *title) {
@ -177,8 +152,6 @@ static uint32_t array_to_state(struct wl_array *array) {
state |= TOPLEVEL_STATE_MINIMIZED;
if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED)
state |= TOPLEVEL_STATE_ACTIVATED;
if (*entry == ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN)
state |= TOPLEVEL_STATE_FULLSCREEN;
}
return state;
@ -191,28 +164,6 @@ static void toplevel_handle_state(void *data,
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,
struct zwlr_foreign_toplevel_handle_v1 *zwlr_toplevel) {
struct toplevel_v1 *toplevel = data;
@ -233,9 +184,6 @@ static void toplevel_handle_closed(void *data,
printf(" closed\n");
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 = {
@ -246,9 +194,11 @@ static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_impl = {
.state = toplevel_handle_state,
.done = toplevel_handle_done,
.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,
struct zwlr_foreign_toplevel_manager_v1 *toplevel_manager,
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->zwlr_toplevel = zwlr_toplevel;
toplevel->current.parent_id = no_parent;
toplevel->pending.parent_id = no_parent;
wl_list_insert(&toplevel_list, &toplevel->link);
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,
uint32_t name, const char *interface, uint32_t version) {
if (strcmp(interface, wl_output_interface.name) == 0) {
if (name == pref_output_id) {
pref_output = wl_registry_bind(registry, name,
&wl_output_interface, version);
}
struct wl_output *output = wl_registry_bind(registry, name,
&wl_output_interface, version);
wl_output_set_user_data(output, (void*)(size_t)name); // assign some ID to the output
} else if (strcmp(interface,
zwlr_foreign_toplevel_manager_v1_interface.name) == 0) {
toplevel_manager = wl_registry_bind(registry, name,
&zwlr_foreign_toplevel_manager_v1_interface,
WLR_FOREIGN_TOPLEVEL_MANAGEMENT_VERSION);
&zwlr_foreign_toplevel_manager_v1_interface, 1);
wl_list_init(&toplevel_list);
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 maximize_id = -1, unmaximize_id = -1;
int minimize_id = -1, restore_id = -1;
int fullscreen_id = -1, unfullscreen_id = -1;
int one_shot = 1;
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) {
case 'f':
focus_id = atoi(optarg);
@ -357,26 +302,9 @@ int main(int argc, char **argv) {
case 'c':
close_id = atoi(optarg);
break;
case 's':
fullscreen_id = atoi(optarg);
break;
case 'S':
unfullscreen_id = atoi(optarg);
break;
case 'm':
one_shot = 0;
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);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
if (toplevel_manager == NULL) {
@ -413,16 +342,6 @@ int main(int argc, char **argv) {
if ((toplevel = toplevel_by_id_or_bail(restore_id))) {
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))) {
zwlr_foreign_toplevel_handle_v1_close(toplevel->zwlr_toplevel);
}

View File

@ -5,17 +5,16 @@
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <wayland-server-core.h>
#include <wayland-server.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_fullscreen_shell_v1.h>
#include <wlr/types/wlr_linux_dmabuf_v1.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_surface.h>
#include <wlr/util/box.h>
#include <wlr/util/log.h>
/**
@ -26,7 +25,6 @@ struct fullscreen_server {
struct wl_display *wl_display;
struct wlr_backend *backend;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
struct wlr_fullscreen_shell_v1 *fullscreen_shell;
struct wl_listener present_surface;
@ -49,6 +47,7 @@ struct fullscreen_output {
struct render_data {
struct wlr_output *output;
struct wlr_renderer *renderer;
struct tinywl_view *view;
struct timespec *when;
};
@ -90,7 +89,7 @@ static void output_handle_frame(struct wl_listener *listener, void *data) {
int 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;
}
@ -109,7 +108,7 @@ static void output_handle_frame(struct wl_listener *listener, void *data) {
}
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,
@ -148,7 +147,11 @@ static void server_handle_new_output(struct wl_listener *listener, void *data) {
wl_container_of(listener, server, new_output);
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 =
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_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,
@ -206,13 +202,12 @@ int main(int argc, char *argv[]) {
struct fullscreen_server server = {0};
server.wl_display = wl_display_create();
server.backend = wlr_backend_autocreate(server.wl_display);
server.renderer = wlr_renderer_autocreate(server.backend);
server.backend = wlr_backend_autocreate(server.wl_display, NULL);
server.renderer = wlr_backend_get_renderer(server.backend);
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_linux_dmabuf_v1_create(server.wl_display, server.renderer);
server.output_layout = wlr_output_layout_create();

View File

@ -162,6 +162,7 @@ int main(int argc, char *argv[]) {
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
if (gamma_control_manager == NULL) {

View File

@ -4,7 +4,7 @@
#include <string.h>
#include <wayland-client.h>
#include <wayland-egl.h>
#include "egl_common.h"
#include <wlr/render/egl.h>
#include "idle-inhibit-unstable-v1-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_inhibitor_v1 *idle_inhibitor = NULL;
struct wlr_egl egl;
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);
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
float color[] = {1.0, 1.0, 0.0, 1.0};
if (idle_inhibitor) {
@ -42,7 +43,7 @@ static void draw(void) {
glClearColor(color[0], color[1], color[2], 1.0);
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,
@ -176,6 +177,7 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
if (compositor == NULL) {
@ -191,7 +193,8 @@ int main(int argc, char **argv) {
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 xdg_surface *xdg_surface =
@ -212,8 +215,7 @@ int main(int argc, char **argv) {
wl_surface_commit(surface);
egl_window = wl_egl_window_create(surface, width, height);
egl_surface = eglCreatePlatformWindowSurfaceEXT(
egl_display, egl_config, egl_window, NULL);
egl_surface = wlr_egl_create_surface(&egl, egl_window);
wl_display_roundtrip(display);

View File

@ -52,7 +52,7 @@ static const struct org_kde_kwin_idle_timeout_listener idle_timer_listener = {
.resumed = handle_resume,
};
static int parse_args(int argc, char *argv[]) {
int parse_args(int argc, char *argv[]) {
int c;
while ((c = getopt(argc, argv, "c:hs:t:")) != -1) {
switch(c)
@ -81,7 +81,7 @@ static int parse_args(int argc, char *argv[]) {
return 0;
}
static void *simulate_activity(void *data) {
void *simulate_activity(void *data) {
sleep(simulate_activity_timeout);
fprintf(stdout, "simulate user activity\n");
struct thread_args *arg = data;
@ -90,7 +90,7 @@ static void *simulate_activity(void *data) {
return NULL;
}
static void *close_program(void *data) {
void *close_program(void *data) {
sleep(close_timeout);
struct thread_args *arg = data;
org_kde_kwin_idle_timeout_release(arg->timer);
@ -100,7 +100,7 @@ static void *close_program(void *data) {
return NULL;
}
static void *main_loop(void *data) {
void *main_loop(void *data) {
struct wl_display *display = data;
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);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
wl_registry_destroy(registry);
free(registry);
if (idle_manager == NULL) {
fprintf(stderr, "display doesn't support idle protocol\n");

View File

@ -5,7 +5,7 @@
#include <string.h>
#include <wayland-client.h>
#include <wayland-egl.h>
#include "egl_common.h"
#include <wlr/render/egl.h>
#include "wlr-input-inhibitor-unstable-v1-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_inhibitor_v1 *input_inhibitor = NULL;
struct wlr_egl egl;
struct wl_egl_window *egl_window;
struct wlr_egl_surface *egl_surface;
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);
if (keys) {
@ -32,7 +33,7 @@ static void render_frame(void) {
}
glClear(GL_COLOR_BUFFER_BIT);
eglSwapBuffers(egl_display, egl_surface);
eglSwapBuffers(egl.display, egl_surface);
}
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);
assert(registry);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
assert(compositor && seat && wm_base && input_inhibit_manager);
@ -156,7 +158,8 @@ int main(int argc, char **argv) {
input_inhibit_manager);
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);
assert(surface);
@ -172,8 +175,7 @@ int main(int argc, char **argv) {
wl_surface_commit(surface);
egl_window = wl_egl_window_create(surface, width, height);
egl_surface = eglCreatePlatformWindowSurfaceEXT(
egl_display, egl_config, egl_window, NULL);
egl_surface = wlr_egl_create_surface(&egl, egl_window);
wl_display_roundtrip(display);

View File

@ -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, &registry_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;
}

View File

@ -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);
switch (update_stage) {
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);
do_updates();
}
@ -324,6 +324,7 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
if (compositor == NULL) {

View File

@ -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, &registry_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;
}

View File

@ -11,7 +11,8 @@
#include <wayland-client.h>
#include <wayland-cursor.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 "xdg-shell-client-protocol.h"
@ -28,6 +29,7 @@ struct zwlr_layer_surface_v1 *layer_surface;
static struct wl_output *wl_output;
struct wl_surface *wl_surface;
struct wlr_egl egl;
struct wl_egl_window *egl_window;
struct wlr_egl_surface *egl_surface;
struct wl_callback *frame_callback;
@ -48,8 +50,7 @@ static int32_t margin_top = 0;
static double alpha = 1.0;
static bool run_display = true;
static bool animate = false;
static enum zwlr_layer_surface_v1_keyboard_interactivity keyboard_interactive =
ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE;
static bool keyboard_interactive = false;
static double frame = 0;
static int cur_x = -1, cur_y = -1;
static int buttons = 0;
@ -92,7 +93,7 @@ static struct wl_callback_listener popup_frame_listener = {
};
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;
clock_gettime(CLOCK_MONOTONIC, &ts);
@ -141,7 +142,7 @@ static void draw(void) {
frame_callback = wl_surface_frame(wl_surface);
wl_callback_add_listener(frame_callback, &frame_listener, NULL);
eglSwapBuffers(egl_display, egl_surface);
eglSwapBuffers(egl.display, egl_surface);
demo.last_frame = ts;
}
@ -149,7 +150,7 @@ static void draw(void) {
static void draw_popup(void) {
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);
glClearColor(popup_red, 0.5f, 0.5f, popup_alpha);
popup_alpha += alpha_mod;
@ -161,7 +162,7 @@ static void draw_popup(void) {
popup_frame_callback = wl_surface_frame(popup_wl_surface);
assert(popup_frame_callback);
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);
}
@ -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,
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_height = height;
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) {
eglDestroySurface(egl_display, popup_egl_surface);
wlr_egl_destroy_surface(&egl, popup_egl_surface);
wl_egl_window_destroy(popup_egl_window);
xdg_popup_destroy(popup);
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) {
fprintf(stderr, "Popup done\n");
wlr_log(WLR_DEBUG, "Popup done");
popup_destroy();
}
@ -239,9 +241,8 @@ static void create_popup(uint32_t serial) {
popup_wl_surface = surface;
popup_egl_window = wl_egl_window_create(surface, popup_width, popup_height);
assert(popup_egl_window);
popup_egl_surface = eglCreatePlatformWindowSurfaceEXT(
egl_display, egl_config, popup_egl_window, NULL);
assert(popup_egl_surface != EGL_NO_SURFACE);
popup_egl_surface = wlr_egl_create_surface(&egl, popup_egl_window);
assert(popup_egl_surface);
draw_popup();
}
@ -258,7 +259,7 @@ static void layer_surface_configure(void *data,
static void layer_surface_closed(void *data,
struct zwlr_layer_surface_v1 *surface) {
eglDestroySurface(egl_display, egl_surface);
wlr_egl_destroy_surface(&egl, egl_surface);
wl_egl_window_destroy(egl_window);
zwlr_layer_surface_v1_destroy(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,
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,
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,
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,
@ -452,8 +453,8 @@ static void handle_global(void *data, struct wl_registry *registry,
&wl_seat_interface, 1);
wl_seat_add_listener(seat, &seat_listener, NULL);
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
layer_shell = wl_registry_bind(registry, name,
&zwlr_layer_shell_v1_interface, version < 4 ? version : 4);
layer_shell = wl_registry_bind(
registry, name, &zwlr_layer_shell_v1_interface, 1);
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
xdg_wm_base = wl_registry_bind(
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) {
wlr_log_init(WLR_DEBUG, NULL);
char *namespace = "wlroots";
int exclusive_zone = 0;
int32_t margin_right = 0, margin_bottom = 0, margin_left = 0;
bool found;
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) {
case 'o':
output = atoi(optarg);
@ -556,29 +558,9 @@ int main(int argc, char **argv) {
case 'n':
animate = true;
break;
case 'k': {
const struct {
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;
}
case 'k':
keyboard_interactive = true;
break;
}
default:
break;
}
@ -628,7 +610,9 @@ int main(int argc, char **argv) {
cursor_surface = wl_compositor_create_surface(compositor);
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);
assert(wl_surface);
@ -650,9 +634,8 @@ int main(int argc, char **argv) {
egl_window = wl_egl_window_create(wl_surface, width, height);
assert(egl_window);
egl_surface = eglCreatePlatformWindowSurfaceEXT(
egl_display, egl_config, egl_window, NULL);
assert(egl_surface != EGL_NO_SURFACE);
egl_surface = wlr_egl_create_surface(&egl, egl_window);
assert(egl_surface);
wl_display_roundtrip(display);
draw();

View File

@ -1,127 +1,89 @@
threads = dependency('threads')
wayland_egl = dependency('wayland-egl')
wayland_cursor = dependency('wayland-cursor')
wayland_client = dependency('wayland-client')
libpng = dependency('libpng', required: false, disabler: true)
egl = dependency('egl', required: false, disabler: true)
glesv2 = dependency('glesv2', required: false, disabler: true)
libpng = dependency('libpng', required: false)
# These versions correspond to ffmpeg 4.0
libavutil = dependency('libavutil', version: '>=56.14.100', required: false, disabler: true)
libavcodec = dependency('libavcodec', version: '>=58.18.100', required: false, disabler: true)
libavformat = dependency('libavformat', version: '>=58.12.100', required: false, disabler: true)
# Only needed for drm_fourcc.h
libdrm = dependency('libdrm').partial_dependency(compile_args: true, includes: true)
libavutil = dependency('libavutil', version: '>=56.14.100', required: false)
libavcodec = dependency('libavcodec', version: '>=58.18.100', required: false)
libavformat = dependency('libavformat', version: '>=58.12.100', required: false)
# epoll is a separate library in FreeBSD
if host_machine.system() == 'freebsd'
libepoll = dependency('epoll-shim')
libepoll = [dependency('epoll-shim')]
else
libepoll = dependency('', required: false)
libepoll = []
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)
libavutil = disabler()
endif
compositors = {
examples = {
'simple': {
'src': 'simple.c',
'dep': [wlroots],
},
'pointer': {
'src': 'pointer.c',
'dep': [wlroots],
},
'touch': {
'src': ['touch.c', 'cat.c'],
'dep': [wlroots],
},
'tablet': {
'src': 'tablet.c',
'dep': [wlroots],
},
'rotation': {
'src': ['rotation.c', 'cat.c'],
'dep': [wlroots],
},
'multi-pointer': {
'src': 'multi-pointer.c',
'dep': [wlroots],
},
'output-layout': {
'src': ['output-layout.c', 'cat.c'],
'dep': [wlroots],
},
'fullscreen-shell': {
'src': 'fullscreen-shell.c',
'proto': ['fullscreen-shell-unstable-v1'],
'screenshot': {
'src': 'screenshot.c',
'dep': [wayland_client, wlr_protos, rt],
},
'quads': {
'src': 'quads.c',
},
'scene-graph': {
'src': 'scene-graph.c',
'proto': ['xdg-shell'],
},
}
clients = {
'idle': {
'src': 'idle.c',
'dep': threads,
'proto': ['kde-idle'],
'dep': [wayland_client, wlr_protos, threads],
},
'idle-inhibit': {
'src': ['idle-inhibit.c', 'egl_common.c'],
'dep': [wayland_egl, egl, glesv2],
'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',
],
'src': 'idle-inhibit.c',
'dep': [wayland_client, wlr_protos, wlroots],
},
'layer-shell': {
'src': ['layer-shell.c', 'egl_common.c'],
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
'proto': [
'wlr-layer-shell-unstable-v1',
'xdg-shell',
],
'src': 'layer-shell.c',
'dep': [wayland_client, wayland_cursor, wlr_protos, wlroots],
},
'input-inhibitor': {
'src': ['input-inhibitor.c', 'egl_common.c'],
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
'proto': [
'wlr-input-inhibitor-unstable-v1',
'xdg-shell',
],
'src': 'input-inhibitor.c',
'dep': [wayland_client, wayland_cursor, wlr_protos, wlroots],
},
'gamma-control': {
'src': 'gamma-control.c',
'dep': [wayland_cursor, 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'],
'dep': [wayland_client, wayland_cursor, wlr_protos, math],
},
'pointer-constraints': {
'src': ['pointer-constraints.c', 'egl_common.c'],
'dep': [wayland_egl, egl, glesv2],
'proto': [
'pointer-constraints-unstable-v1',
'xdg-shell',
],
'src': 'pointer-constraints.c',
'dep': [wayland_client, wlr_protos, wlroots],
},
'relative-pointer': {
'src': ['relative-pointer-unstable-v1.c', 'egl_common.c'],
'dep': [wayland_egl, egl, glesv2],
'proto': [
'pointer-constraints-unstable-v1',
'relative-pointer-unstable-v1',
'xdg-shell',
],
'src': 'relative-pointer-unstable-v1.c',
'dep': [wayland_client, wlr_protos, wlroots],
},
'dmabuf-capture': {
'src': 'dmabuf-capture.c',
@ -129,92 +91,51 @@ clients = {
libavcodec,
libavformat,
libavutil,
drm,
drm.partial_dependency(compile_args: true), # <drm_fourcc.h>
threads,
wayland_client,
wlr_protos,
],
'proto': ['wlr-export-dmabuf-unstable-v1'],
},
'screencopy': {
'src': 'screencopy.c',
'dep': [libpng, 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',
],
'dep': [libpng, wayland_client, wlr_protos, rt],
},
'toplevel-decoration': {
'src': ['toplevel-decoration.c', 'egl_common.c'],
'dep': [wayland_egl, egl, glesv2],
'proto': [
'xdg-decoration-unstable-v1',
'xdg-shell',
],
'src': 'toplevel-decoration.c',
'dep': [wayland_client, wlr_protos, wlroots],
},
'input-method': {
'src': 'input-method.c',
'dep': [wayland_egl, libepoll],
'proto': [
'input-method-unstable-v2',
'text-input-unstable-v3',
'xdg-shell',
],
'dep': [wayland_client, wlr_protos] + libepoll,
},
'text-input': {
'src': ['text-input.c', 'egl_common.c'],
'dep': [wayland_egl, wayland_cursor, egl, glesv2],
'proto': [
'text-input-unstable-v3',
'xdg-shell',
],
'src': 'text-input.c',
'dep': [wayland_cursor, wayland_client, wlr_protos, wlroots],
},
'foreign-toplevel': {
'src': 'foreign-toplevel.c',
'proto': ['wlr-foreign-toplevel-management-unstable-v1'],
'dep': [wayland_client, wlr_protos, wlroots],
},
'virtual-pointer': {
'src': 'virtual-pointer.c',
'proto': ['wlr-virtual-pointer-unstable-v1'],
},
'input-method-keyboard-grab': {
'src': 'input-method-keyboard-grab.c',
'dep': xkbcommon,
'proto': [
'input-method-unstable-v2',
],
'fullscreen-shell': {
'src': 'fullscreen-shell.c',
'dep': [wlr_protos, wlroots],
},
}
foreach name, info : compositors
extra_src = []
foreach p : info.get('proto', [])
extra_src += protocols_server_header[p]
foreach name, info : examples
all_dep_found = true
foreach d : info.get('dep')
all_dep_found = all_dep_found and d.found()
endforeach
executable(
name,
[info.get('src'), extra_src],
dependencies: [wlroots, libdrm],
include_directories: [wlr_inc, proto_inc],
build_by_default: get_option('examples'),
)
endforeach
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'),
)
if all_dep_found
executable(
name,
info.get('src'),
dependencies: info.get('dep'),
build_by_default: get_option('examples'),
)
else
warning('Dependencies not satisfied for ' + name)
endif
endforeach

View File

@ -1,21 +1,23 @@
#define _POSIX_C_SOURCE 200112L
#include <assert.h>
#include <GLES2/gl2.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.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/session.h>
#include <wlr/render/allocator.h>
#include <wlr/render/gles2.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_list.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/util/log.h>
#include <wlr/xcursor.h>
#include <xkbcommon/xkbcommon.h>
@ -23,8 +25,6 @@
struct sample_state {
struct wl_display *display;
struct wlr_xcursor *xcursor;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
float default_color[4];
float clear_color[4];
struct wlr_output_layout *layout;
@ -69,10 +69,10 @@ struct sample_keyboard {
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_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
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_state *sample = output->sample;
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);
wlr_renderer_clear(renderer, sample->clear_color);
glClearColor(sample->clear_color[0], sample->clear_color[1],
sample->clear_color[2], sample->clear_color[3]);
glClear(GL_COLOR_BUFFER_BIT);
wlr_output_render_software_cursors(wlr_output, NULL);
wlr_renderer_end(renderer);
wlr_output_commit(wlr_output);
wlr_output_swap_buffers(wlr_output, NULL, NULL);
}
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);
}
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_state *sample = sample_output->sample;
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 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));
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->sample = sample;
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);
struct sample_cursor *cursor;
wl_list_for_each(cursor, &sample->cursors, link) {
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);
}
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_state *sample = keyboard->sample;
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);
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) {
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) {
@ -216,12 +229,18 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
keyboard->destroy.notify = keyboard_destroy_notify;
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
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);
if (!context) {
wlr_log(WLR_ERROR, "Failed to create XKB context");
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);
if (!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 },
.display = display,
};
struct wlr_backend *wlr = wlr_backend_autocreate(display);
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
if (!wlr) {
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.pointers);
wl_list_init(&state.outputs);

View File

@ -1,5 +1,5 @@
#define _POSIX_C_SOURCE 200112L
#include <drm_fourcc.h>
#include <GLES2/gl2.h>
#include <limits.h>
#include <math.h>
#include <stdio.h>
@ -8,17 +8,16 @@
#include <strings.h>
#include <time.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/session.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_output.h>
#include <wlr/util/box.h>
#include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h>
#include "cat.h"
@ -28,7 +27,6 @@ struct sample_state {
struct wl_listener new_output;
struct wl_listener new_input;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
struct wlr_texture *cat_texture;
struct wlr_output_layout *layout;
float x_offs, y_offs;
@ -109,7 +107,7 @@ static void animate_cat(struct sample_state *sample,
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_state *sample = output->sample;
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;
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_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_output_commit(wlr_output);
wlr_output_swap_buffers(wlr_output, NULL, NULL);
}
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;
}
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_state *sample = sample_output->sample;
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);
}
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 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));
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);
sample_output->output = output;
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;
wl_signal_add(&output->events.destroy, &sample_output->destroy);
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_state *sample = keyboard->sample;
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
// Also, key repeat
int delta = 75;
if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
if (event->state == WLR_KEY_PRESSED) {
switch (sym) {
case XKB_KEY_Left:
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);
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) {
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) {
@ -235,12 +227,18 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
keyboard->destroy.notify = keyboard_destroy_notify;
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
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);
if (!context) {
wlr_log(WLR_ERROR, "Failed to create XKB context");
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);
if (!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();
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) {
exit(1);
}
@ -278,13 +276,11 @@ int main(int argc, char *argv[]) {
wl_signal_add(&wlr->events.new_input, &state.new_input);
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,
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);
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);

View File

@ -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, &registry_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;
}

View File

@ -5,7 +5,7 @@
#include <string.h>
#include <wayland-client.h>
#include <wayland-egl.h>
#include "egl_common.h"
#include <wlr/render/egl.h>
#include "xdg-shell-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 zwp_pointer_constraints_v1 *pointer_constraints = NULL;
struct wlr_egl egl;
struct wl_egl_window *egl_window;
struct wlr_egl_surface *egl_surface;
struct zwp_locked_pointer_v1* locked_pointer;
@ -31,7 +32,7 @@ enum {
struct wl_region *regions[3];
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};
@ -39,7 +40,7 @@ static void draw(void) {
glClearColor(color[0], color[1], color[2], 1.0);
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,
@ -197,6 +198,7 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
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);
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 xdg_surface *xdg_surface =
@ -239,8 +242,7 @@ int main(int argc, char **argv) {
wl_surface_commit(surface);
egl_window = wl_egl_window_create(surface, width, height);
egl_surface = eglCreatePlatformWindowSurfaceEXT(
egl_display, egl_config, egl_window, NULL);
egl_surface = wlr_egl_create_surface(&egl, egl_window);
wl_display_roundtrip(display);

View File

@ -6,18 +6,17 @@
#include <string.h>
#include <time.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/session.h>
#include <wlr/render/allocator.h>
#include <wlr/render/gles2.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_list.h>
#include <wlr/types/wlr_matrix.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/util/log.h>
#include <xkbcommon/xkbcommon.h>
@ -25,8 +24,6 @@
struct sample_state {
struct wl_display *display;
struct compositor_state *compositor;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
struct wlr_xcursor_manager *xcursor_manager;
struct wlr_cursor *cursor;
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);
}
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_state *state = sample_output->state;
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);
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_clear(renderer, state->clear_color);
wlr_output_render_software_cursors(wlr_output, NULL);
wlr_output_swap_buffers(wlr_output, NULL, NULL);
wlr_renderer_end(renderer);
wlr_output_commit(wlr_output);
}
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_state *sample = keyboard->state;
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_state *sample = sample_output->state;
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);
}
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 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));
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->state = sample;
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_set_cursor_image(sample->xcursor_manager, "left_ptr",
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);
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) {
void new_input_notify(struct wl_listener *listener, void *data) {
struct wlr_input_device *device = data;
struct sample_state *state = wl_container_of(listener, state, new_input);
switch (device->type) {
@ -303,12 +294,18 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
keyboard->destroy.notify = keyboard_destroy_notify;
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
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);
if (!context) {
wlr_log(WLR_ERROR, "Failed to create XKB context");
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);
if (!keymap) {
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
@ -333,14 +330,10 @@ int main(int argc, char *argv[]) {
.display = display
};
struct wlr_backend *wlr = wlr_backend_autocreate(display);
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
if (!wlr) {
exit(1);
}
state.renderer = wlr_renderer_autocreate(wlr);
state.allocator = wlr_allocator_autocreate(wlr, state.renderer);
state.cursor = wlr_cursor_create();
state.layout = wlr_output_layout_create();
wlr_cursor_attach_output_layout(state.cursor, state.layout);

View File

@ -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);
}

View File

@ -4,7 +4,7 @@
#include <GLES2/gl2.h>
#include <linux/input-event-codes.h>
#include <wayland-egl.h>
#include "egl_common.h"
#include <wlr/render/egl.h>
#include "pointer-constraints-unstable-v1-client-protocol.h"
#include "relative-pointer-unstable-v1-client-protocol.h"
#include "xdg-shell-client-protocol.h"
@ -17,6 +17,7 @@
*/
struct egl_info {
struct wlr_egl *egl;
struct wl_egl_window *egl_window;
struct wlr_egl_surface *egl_surface;
uint32_t width;
@ -57,8 +58,8 @@ static struct wl_callback_listener surface_callback_listener = {
};
static void draw_init(struct egl_info *e) {
eglMakeCurrent(egl_display, e->egl_surface,
e->egl_surface, egl_context);
eglMakeCurrent(e->egl->display, e->egl_surface,
e->egl_surface, e->egl->context);
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);
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,
struct xdg_toplevel *xdg_toplevel) {
egl_finish();
struct egl_info *e = data;
wlr_egl_finish(e->egl);
exit(EXIT_SUCCESS);
}
@ -410,6 +412,7 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
/* Check that all the global interfaces were captured */
@ -438,9 +441,11 @@ int main(int argc, char **argv) {
/* Initialize EGL context */
struct egl_info *e = calloc(1, sizeof(struct egl_info));
e->egl = calloc(1, sizeof(struct wlr_egl));
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 */
@ -457,8 +462,7 @@ int main(int argc, char **argv) {
wl_surface_commit(surface);
e->egl_window = wl_egl_window_create(surface, e->width, e->height);
e->egl_surface = eglCreatePlatformWindowSurfaceEXT(
egl_display, egl_config, e->egl_window, NULL);
e->egl_surface = wlr_egl_create_surface(e->egl, e->egl_window);
e->surface = surface;
wl_display_roundtrip(display);

View File

@ -1,5 +1,5 @@
#define _POSIX_C_SOURCE 200112L
#include <drm_fourcc.h>
#include <GLES2/gl2.h>
#include <getopt.h>
#include <math.h>
#include <stdio.h>
@ -8,12 +8,14 @@
#include <strings.h>
#include <time.h>
#include <unistd.h>
#include <wayland-server-core.h>
#include <wayland-server-protocol.h>
#include <wayland-server.h>
#include <wlr/backend.h>
#include <wlr/render/allocator.h>
#include <wlr/backend/session.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_keyboard.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_matrix.h>
#include <wlr/util/log.h>
@ -26,7 +28,6 @@ struct sample_state {
struct wl_listener new_input;
struct timespec last_frame;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
struct wlr_texture *cat_texture;
struct wl_list outputs;
enum wl_output_transform transform;
@ -59,7 +60,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
int32_t 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_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_output_commit(wlr_output);
wlr_output_swap_buffers(wlr_output, NULL, NULL);
long ms = (now.tv_sec - sample->last_frame.tv_sec) * 1000 +
(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);
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) {
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));
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_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);
sample_output->destroy.notify = output_remove_notify;
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_state *sample = keyboard->sample;
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) {
wl_display_terminate(sample->display);
}
if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
if (event->state == WLR_KEY_PRESSED) {
switch (sym) {
case XKB_KEY_Left:
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);
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) {
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) {
@ -182,12 +177,18 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
keyboard->destroy.notify = keyboard_destroy_notify;
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
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);
if (!context) {
wlr_log(WLR_ERROR, "Failed to create XKB context");
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);
if (!keymap) {
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
@ -228,7 +229,7 @@ int main(int argc, char *argv[]) {
}
break;
default:
return 1;
break;
}
}
wlr_log_init(WLR_DEBUG, NULL);
@ -239,7 +240,7 @@ int main(int argc, char *argv[]) {
};
wl_list_init(&state.outputs);
struct wlr_backend *wlr = wlr_backend_autocreate(display);
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
if (!wlr) {
exit(1);
}
@ -250,22 +251,20 @@ int main(int argc, char *argv[]) {
state.new_input.notify = new_input_notify;
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
state.renderer = wlr_renderer_autocreate(wlr);
state.renderer = wlr_backend_get_renderer(wlr);
if (!state.renderer) {
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
wlr_backend_destroy(wlr);
exit(EXIT_FAILURE);
}
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);
if (!state.cat_texture) {
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
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);

View File

@ -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;
}

View File

@ -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, &params_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, &registry_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;
}

View File

@ -32,10 +32,8 @@
#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 <wayland-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;
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) {
fprintf(stderr, "shm_open failed\n");
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);
if (data == MAP_FAILED) {
perror("mmap failed");
fprintf(stderr, "mmap failed: %m\n");
close(fd);
return NULL;
}
@ -112,9 +110,6 @@ static void frame_handle_buffer(void *data,
buffer.width = width;
buffer.height = height;
buffer.stride = stride;
// Make sure the buffer is not allocated
assert(!buffer.wl_buffer);
buffer.wl_buffer =
create_shm_buffer(format, width, height, stride, &buffer.data);
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[]) {
struct wl_display * display = wl_display_connect(NULL);
if (display == NULL) {
perror("failed to create display");
fprintf(stderr, "failed to create display: %m\n");
return EXIT_FAILURE;
}
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
if (shm == NULL) {
@ -260,7 +256,6 @@ int main(int argc, char *argv[]) {
write_image("wayland-screenshot.png", buffer.format, buffer.width,
buffer.height, buffer.stride, buffer.y_invert, buffer.data);
wl_buffer_destroy(buffer.wl_buffer);
munmap(buffer.data, buffer.stride * buffer.height);
return EXIT_SUCCESS;
}

237
examples/screenshot.c Normal file
View File

@ -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, &registry_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;
}

View File

@ -1,17 +1,15 @@
#define _POSIX_C_SOURCE 200112L
#include <GLES2/gl2.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <wayland-server-core.h>
#include <wayland-server.h>
#include <wlr/backend.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_input_device.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h>
@ -19,10 +17,8 @@ struct sample_state {
struct wl_display *display;
struct wl_listener new_output;
struct wl_listener new_input;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
struct timespec last_frame;
float color[4];
float color[3];
int dec;
};
@ -40,12 +36,10 @@ struct sample_keyboard {
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 =
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);
@ -62,18 +56,16 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
sample->dec = inc;
}
wlr_output_attach_render(wlr_output, NULL);
wlr_output_make_current(sample_output->output, NULL);
struct wlr_renderer *renderer = sample->renderer;
wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
wlr_renderer_clear(renderer, sample->color);
wlr_renderer_end(renderer);
glClearColor(sample->color[0], sample->color[1], sample->color[2], 1.0);
glClear(GL_COLOR_BUFFER_BIT);
wlr_output_commit(wlr_output);
wlr_output_swap_buffers(sample_output->output, NULL, NULL);
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 =
wl_container_of(listener, sample_output, destroy);
wlr_log(WLR_DEBUG, "Output removed");
@ -82,31 +74,26 @@ static void output_remove_notify(struct wl_listener *listener, void *data) {
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 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));
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->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;
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_state *sample = keyboard->sample;
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 =
wl_container_of(listener, keyboard, destroy);
wl_list_remove(&keyboard->destroy.link);
@ -130,7 +117,7 @@ static void keyboard_destroy_notify(struct wl_listener *listener, void *data) {
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 sample_state *sample = wl_container_of(listener, sample, new_input);
switch (device->type) {
@ -143,12 +130,18 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
keyboard->destroy.notify = keyboard_destroy_notify;
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
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);
if (!context) {
wlr_log(WLR_ERROR, "Failed to create XKB context");
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);
if (!keymap) {
wlr_log(WLR_ERROR, "Failed to create XKB keymap");
@ -167,19 +160,15 @@ int main(void) {
wlr_log_init(WLR_DEBUG, NULL);
struct wl_display *display = wl_display_create();
struct sample_state state = {
.color = { 1.0, 0.0, 0.0, 1.0 },
.color = { 1.0, 0.0, 0.0 },
.dec = 0,
.last_frame = { 0 },
.display = display
};
struct wlr_backend *backend = wlr_backend_autocreate(display);
struct wlr_backend *backend = wlr_backend_autocreate(display, NULL);
if (!backend) {
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);
state.new_output.notify = new_output_notify;
wl_signal_add(&backend->events.new_input, &state.new_input);

View File

@ -1,29 +1,28 @@
#define _XOPEN_SOURCE 600
#include <GLES2/gl2.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.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/session.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_box.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_output.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_tool.h>
#include <wlr/util/box.h>
#include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h>
struct sample_state {
struct wl_display *display;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
bool proximity, tap, button;
double distance;
double pressure;
@ -87,7 +86,7 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
int32_t 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_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_output_commit(wlr_output);
wlr_output_swap_buffers(wlr_output, NULL, NULL);
sample->last_frame = now;
}
@ -229,36 +228,30 @@ static void tablet_pad_destroy_notify(struct wl_listener *listener, void *data)
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);
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) {
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));
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->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;
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_state *sample = keyboard->sample;
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);
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) {
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) {
@ -293,12 +286,18 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
keyboard->destroy.notify = keyboard_destroy_notify;
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
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);
if (!context) {
wlr_log(WLR_ERROR, "Failed to create XKB context");
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);
if (!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_tools);
struct wlr_backend *wlr = wlr_backend_autocreate(display);
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
if (!wlr) {
exit(1);
}
@ -366,14 +365,11 @@ int main(int argc, char *argv[]) {
state.new_input.notify = new_input_notify;
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
state.renderer = wlr_renderer_autocreate(wlr);
state.renderer = wlr_backend_get_renderer(wlr);
if (!state.renderer) {
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
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);

View File

@ -6,7 +6,7 @@
#include <unistd.h>
#include <wayland-client.h>
#include <wayland-egl.h>
#include "egl_common.h"
#include <wlr/render/egl.h>
#include "text-input-unstable-v3-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_v3 *text_input = NULL;
struct wlr_egl egl;
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);
eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context);
float color[] = {1.0, 1.0, 0.0, 1.0};
color[0] = enabled * 1.0;
@ -77,7 +78,7 @@ static void draw(void) {
glClearColor(color[0], color[1], color[2], 1.0);
glClear(GL_COLOR_BUFFER_BIT);
eglSwapBuffers(egl_display, egl_surface);
eglSwapBuffers(egl.display, egl_surface);
}
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
static void show_status(void) {
static void show_status() {
printf("State %d:", serial);
if (!enabled) {
printf(" disabled");
@ -136,7 +137,8 @@ static void show_status(void) {
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");
goto end;
}
@ -343,6 +345,7 @@ int main(int argc, char **argv) {
struct wl_registry *registry = wl_display_get_registry(display);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
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);
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 xdg_surface *xdg_surface =
@ -375,8 +380,7 @@ int main(int argc, char **argv) {
wl_surface_commit(surface);
egl_window = wl_egl_window_create(surface, width, height);
egl_surface = eglCreatePlatformWindowSurfaceEXT(
egl_display, egl_config, egl_window, NULL);
egl_surface = wlr_egl_create_surface(&egl, egl_window);
wl_display_roundtrip(display);

View File

@ -4,7 +4,7 @@
#include <string.h>
#include <wayland-client.h>
#include <wayland-egl.h>
#include "egl_common.h"
#include <wlr/render/egl.h>
#include "xdg-shell-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 zxdg_decoration_manager_v1 *decoration_manager = NULL;
struct wlr_egl egl;
struct wl_egl_window *egl_window;
struct wlr_egl_surface *egl_surface;
@ -52,7 +53,7 @@ static void request_preferred_mode(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};
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);
glClear(GL_COLOR_BUFFER_BIT);
eglSwapBuffers(egl_display, egl_surface);
eglSwapBuffers(egl.display, egl_surface);
}
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);
wl_registry_add_listener(registry, &registry_listener, NULL);
wl_display_dispatch(display);
wl_display_roundtrip(display);
if (compositor == NULL) {
@ -217,7 +219,8 @@ int main(int argc, char **argv) {
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 xdg_surface *xdg_surface =
@ -236,8 +239,7 @@ int main(int argc, char **argv) {
wl_surface_commit(surface);
egl_window = wl_egl_window_create(surface, width, height);
egl_surface = eglCreatePlatformWindowSurfaceEXT(
egl_display, egl_config, egl_window, NULL);
egl_surface = wlr_egl_create_surface(&egl, egl_window);
wl_display_roundtrip(display);

View File

@ -1,5 +1,5 @@
#define _POSIX_C_SOURCE 200112L
#include <drm_fourcc.h>
#include <GLES2/gl2.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
@ -7,16 +7,15 @@
#include <string.h>
#include <time.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/session.h>
#include <wlr/render/allocator.h>
#include <wlr/render/wlr_renderer.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_keyboard.h>
#include <wlr/types/wlr_matrix.h>
#include <wlr/types/wlr_touch.h>
#include <wlr/util/log.h>
#include <xkbcommon/xkbcommon.h>
#include "cat.h"
@ -24,7 +23,6 @@
struct sample_state {
struct wl_display *display;
struct wlr_renderer *renderer;
struct wlr_allocator *allocator;
struct wlr_texture *cat_texture;
struct wl_list touch_points;
struct timespec last_frame;
@ -75,20 +73,23 @@ static void output_frame_notify(struct wl_listener *listener, void *data) {
int32_t 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_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;
wl_list_for_each(p, &sample->touch_points, link) {
int x = (int)(p->x * width) - sample->cat_texture->width / 2;
int y = (int)(p->y * height) - sample->cat_texture->height / 2;
int x = (int)(p->x * width) - tex_width / 2;
int y = (int)(p->y * height) - tex_height / 2;
wlr_render_texture(sample->renderer, sample->cat_texture,
wlr_output->transform_matrix, x, y, 1.0f);
}
wlr_renderer_end(sample->renderer);
wlr_output_commit(wlr_output);
wlr_output_swap_buffers(wlr_output, NULL, NULL);
sample->last_frame = now;
}
@ -140,36 +141,30 @@ static void touch_destroy_notify(struct wl_listener *listener, void *data) {
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);
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) {
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));
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->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;
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_state *sample = keyboard->sample;
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);
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) {
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) {
@ -204,12 +199,18 @@ static void new_input_notify(struct wl_listener *listener, void *data) {
keyboard->destroy.notify = keyboard_destroy_notify;
wl_signal_add(&device->keyboard->events.key, &keyboard->key);
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);
if (!context) {
wlr_log(WLR_ERROR, "Failed to create XKB context");
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);
if (!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);
struct wlr_backend *wlr = wlr_backend_autocreate(display);
struct wlr_backend *wlr = wlr_backend_autocreate(display, NULL);
if (!wlr) {
exit(1);
}
@ -259,21 +260,20 @@ int main(int argc, char *argv[]) {
state.new_input.notify = new_input_notify;
clock_gettime(CLOCK_MONOTONIC, &state.last_frame);
state.renderer = wlr_renderer_autocreate(wlr);
state.renderer = wlr_backend_get_renderer(wlr);
if (!state.renderer) {
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
exit(EXIT_FAILURE);
}
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);
if (!state.cat_texture) {
wlr_log(WLR_ERROR, "Could not start compositor, OOM");
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);

View File

@ -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, &registry_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;
}

92
glgen.sh Executable file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,54 +1,67 @@
#ifndef BACKEND_DRM_DRM_H
#define BACKEND_DRM_DRM_H
#include <EGL/egl.h>
#include <gbm.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <time.h>
#include <wayland-server-core.h>
#include <wayland-server.h>
#include <wayland-util.h>
#include <wlr/backend/drm.h>
#include <wlr/backend/session.h>
#include <wlr/render/drm_format_set.h>
#include <wlr/render/egl.h>
#include <xf86drmMode.h>
#include "backend/drm/iface.h"
#include "backend/drm/properties.h"
#include "backend/drm/renderer.h"
#include "iface.h"
#include "properties.h"
#include "renderer.h"
struct wlr_drm_plane {
uint32_t type;
uint32_t id;
/* Only initialized on multi-GPU setups */
uint32_t possible_crtcs;
struct wlr_drm_surface surf;
struct wlr_drm_surface mgpu_surf;
/* Buffer to be submitted to the kernel on the next page-flip */
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;
uint32_t drm_format; // ARGB8888 or XRGB8888
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;
};
struct wlr_drm_crtc {
uint32_t id;
struct wlr_drm_lease *lease;
// Atomic modesetting only
uint32_t mode_id;
uint32_t gamma_lut;
drmModeAtomicReq *atomic;
// Legacy only
drmModeCrtc *legacy_crtc;
struct wlr_drm_plane *primary;
struct wlr_drm_plane *cursor;
union {
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;
struct wl_list connectors;
uint16_t *gamma_table;
size_t gamma_table_size;
};
struct wlr_drm_backend {
@ -57,45 +70,54 @@ struct wlr_drm_backend {
struct wlr_drm_backend *parent;
const struct wlr_drm_interface *iface;
clockid_t clock;
bool addfb2_modifiers;
int fd;
char *name;
struct wlr_device *dev;
size_t num_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_event_source *drm_event;
struct wl_listener display_destroy;
struct wl_listener session_destroy;
struct wl_listener session_active;
struct wl_listener parent_destroy;
struct wl_listener dev_change;
struct wl_listener dev_remove;
struct wl_listener session_signal;
struct wl_listener drm_invalidated;
struct wl_list fbs; // wlr_drm_fb.link
struct wl_list outputs;
/* Only initialized on multi-GPU setups */
struct wlr_drm_renderer mgpu_renderer;
struct wlr_drm_renderer renderer;
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
WLR_DRM_CONN_DISCONNECTED,
// An output just has been plugged in and is waiting for a modeset
WLR_DRM_CONN_NEEDS_MODESET,
WLR_DRM_CONN_CLEANUP,
WLR_DRM_CONN_CONNECTED,
// Connector disappeared, waiting for being destroyed on next page-flip
WLR_DRM_CONN_DISAPPEARED,
};
struct wlr_drm_mode {
@ -103,43 +125,27 @@ struct wlr_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_output output; // only valid if status != DISCONNECTED
struct wlr_output output;
struct wlr_drm_backend *backend;
char name[24];
enum wlr_drm_connector_status status;
enum wlr_drm_connector_state state;
struct wlr_output_mode *desired_mode;
bool desired_enabled;
uint32_t id;
struct wlr_drm_lease *lease;
struct wlr_drm_crtc *crtc;
uint32_t possible_crtcs;
uint32_t possible_crtc;
union wlr_drm_connector_props props;
bool cursor_enabled;
int cursor_x, cursor_y;
int cursor_width, cursor_height;
int cursor_hotspot_x, cursor_hotspot_y;
uint32_t width, height;
int32_t cursor_x, cursor_y;
drmModeCrtc *old_crtc;
bool pageflip_pending;
struct wl_event_source *retry_pageflip;
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(
@ -147,24 +153,13 @@ struct wlr_drm_backend *get_drm_backend_from_backend(
bool check_drm_features(struct wlr_drm_backend *drm);
bool init_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,
struct wlr_device_hotplug_event *event);
void scan_drm_leases(struct wlr_drm_backend *drm);
void restore_drm_outputs(struct wlr_drm_backend *drm);
void scan_drm_connectors(struct wlr_drm_backend *state);
int handle_drm_event(int fd, uint32_t mask, void *data);
void destroy_drm_connector(struct wlr_drm_connector *conn);
bool drm_connector_commit_state(struct wlr_drm_connector *conn,
const struct wlr_output_state *state);
bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn);
bool drm_connector_supports_vrr(struct wlr_drm_connector *conn);
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__)
bool enable_drm_connector(struct wlr_output *output, bool enable);
bool set_drm_connector_gamma(struct wlr_output *output, size_t size,
const uint16_t *r, const uint16_t *g, const uint16_t *b);
bool drm_connector_set_mode(struct wlr_output *output,
struct wlr_output_mode *mode);
#endif

View File

@ -1,6 +1,7 @@
#ifndef BACKEND_DRM_IFACE_H
#define BACKEND_DRM_IFACE_H
#include <gbm.h>
#include <stdbool.h>
#include <stdint.h>
#include <xf86drm.h>
@ -9,20 +10,32 @@
struct wlr_drm_backend;
struct wlr_drm_connector;
struct wlr_drm_crtc;
struct wlr_drm_connector_state;
// Used to provide atomic or legacy DRM functions
struct wlr_drm_interface {
// Commit all pending changes on a CRTC.
bool (*crtc_commit)(struct wlr_drm_connector *conn,
const struct wlr_drm_connector_state *state, uint32_t flags,
bool test_only);
// Enable or disable DPMS for connector
bool (*conn_enable)(struct wlr_drm_backend *drm,
struct wlr_drm_connector *conn, bool enable);
// 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 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

View File

@ -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

View File

@ -16,10 +16,6 @@ union wlr_drm_connector_props {
uint32_t dpms;
uint32_t link_status; // not guaranteed to exist
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
@ -31,14 +27,15 @@ union wlr_drm_connector_props {
union wlr_drm_crtc_props {
struct {
// Neither of these are guaranteed to exist
uint32_t vrr_enabled;
uint32_t gamma_lut;
uint32_t gamma_lut_size;
uint32_t rotation;
uint32_t scaling_mode;
// atomic-modesetting only
uint32_t active;
uint32_t mode_id;
uint32_t gamma_lut;
uint32_t gamma_lut_size;
};
uint32_t props[6];
};
@ -47,7 +44,6 @@ union wlr_drm_plane_props {
struct {
uint32_t type;
uint32_t rotation; // Not guaranteed to exist
uint32_t in_formats; // Not guaranteed to exist
// atomic-modesetting only
@ -61,9 +57,8 @@ union wlr_drm_plane_props {
uint32_t crtc_h;
uint32_t fb_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,
@ -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);
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

View File

@ -1,6 +1,8 @@
#ifndef BACKEND_DRM_RENDERER_H
#define BACKEND_DRM_RENDERER_H
#include <EGL/egl.h>
#include <gbm.h>
#include <stdbool.h>
#include <stdint.h>
#include <wlr/backend.h>
@ -8,13 +10,15 @@
struct wlr_drm_backend;
struct wlr_drm_plane;
struct wlr_buffer;
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_allocator *allocator;
};
struct wlr_drm_surface {
@ -23,38 +27,33 @@ struct wlr_drm_surface {
uint32_t width;
uint32_t height;
struct wlr_swapchain *swapchain;
};
struct gbm_surface *gbm;
EGLSurface egl;
struct wlr_drm_fb {
struct wlr_buffer *wlr_buf;
struct wlr_addon addon;
struct wlr_drm_backend *backend;
struct wl_list link; // wlr_drm_backend.fbs
uint32_t id;
struct gbm_bo *front;
struct gbm_bo *back;
};
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);
bool init_drm_surface(struct wlr_drm_surface *surf,
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,
struct wlr_buffer *buf, const struct wlr_drm_format_set *formats);
void drm_fb_destroy(struct wlr_drm_fb *fb);
bool init_drm_plane_surfaces(struct wlr_drm_plane *plane,
struct wlr_drm_backend *drm, int32_t width, uint32_t height,
uint32_t format);
void drm_fb_clear(struct wlr_drm_fb **fb);
void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old);
struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf,
struct wlr_buffer *buffer);
struct wlr_drm_format *drm_plane_pick_render_format(
struct wlr_drm_plane *plane, struct wlr_drm_renderer *renderer);
void drm_plane_finish_surface(struct wlr_drm_plane *plane);
void finish_drm_surface(struct wlr_drm_surface *surf);
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,
pixman_region32_t *damage);
struct gbm_bo *get_drm_surface_front(struct wlr_drm_surface *surf);
void post_drm_surface(struct wlr_drm_surface *surf);
struct gbm_bo *copy_drm_surface_mgpu(struct wlr_drm_surface *dest,
struct gbm_bo *src);
bool export_drm_bo(struct gbm_bo *bo, struct wlr_dmabuf_attributes *attribs);
#endif

View File

@ -13,6 +13,8 @@ void parse_edid(struct wlr_output *restrict output, size_t len,
const uint8_t *data);
// Returns the string representation of a DRM output type
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
enum {

View File

@ -8,9 +8,10 @@
struct wlr_headless_backend {
struct wlr_backend backend;
struct wlr_egl egl;
struct wlr_renderer *renderer;
struct wl_display *display;
struct wl_list outputs;
size_t last_output_num;
struct wl_list input_devices;
struct wl_listener display_destroy;
bool started;
@ -22,13 +23,14 @@ struct wlr_headless_output {
struct wlr_headless_backend *backend;
struct wl_list link;
void *egl_surface;
struct wl_event_source *frame_timer;
int frame_delay; // ms
};
struct wlr_headless_input_device {
struct wlr_input_device wlr_input_device;
struct wl_list link;
struct wlr_headless_backend *backend;
};

View File

@ -7,6 +7,7 @@
#include <wlr/backend/libinput.h>
#include <wlr/interfaces/wlr_input_device.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_list.h>
struct wlr_libinput_backend {
struct wlr_backend backend;
@ -18,15 +19,14 @@ struct wlr_libinput_backend {
struct wl_event_source *input_event;
struct wl_listener display_destroy;
struct wl_listener session_destroy;
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_input_device wlr_input_device;
struct wl_list link;
struct libinput_device *handle;
};
@ -66,10 +66,6 @@ void handle_pointer_pinch_update(struct libinput_event *event,
struct libinput_device *device);
void handle_pointer_pinch_end(struct libinput_event *event,
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 libinput_device *device);
@ -86,8 +82,6 @@ void handle_touch_motion(struct libinput_event *event,
struct libinput_device *device);
void handle_touch_cancel(struct libinput_event *event,
struct libinput_device *device);
void handle_touch_frame(struct libinput_event *event,
struct libinput_device *device);
struct wlr_tablet *create_libinput_tablet(
struct libinput_device *device);

24
include/backend/noop.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -4,12 +4,14 @@
#include <stdbool.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/render/egl.h>
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/render/drm_format_set.h>
#include <wlr/types/wlr_box.h>
struct wlr_wl_backend {
struct wlr_backend backend;
@ -19,48 +21,22 @@ struct wlr_wl_backend {
struct wl_display *local_display;
struct wl_list devices;
struct wl_list outputs;
int drm_fd;
struct wl_list buffers; // wlr_wl_buffer.link
struct wlr_egl egl;
struct wlr_renderer *renderer;
size_t requested_outputs;
size_t last_output_num;
struct wl_listener local_display_destroy;
char *activation_token;
/* remote state */
struct wl_display *remote_display;
struct wl_event_source *remote_display_src;
struct wl_registry *registry;
struct wl_compositor *compositor;
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 zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1;
struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager_v1;
struct wl_list seats; // wlr_wl_seat.link
struct zwp_tablet_manager_v2 *tablet_manager;
clockid_t presentation_clock;
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 wl_seat *seat;
struct wl_pointer *pointer;
struct wl_keyboard *keyboard;
struct wlr_wl_pointer *current_pointer;
char *seat_name;
};
struct wlr_wl_output {
@ -73,25 +49,23 @@ struct wlr_wl_output {
struct wl_callback *frame_callback;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
struct zxdg_toplevel_decoration_v1 *zxdg_toplevel_decoration_v1;
struct wl_list presentation_feedbacks;
struct wl_egl_window *egl_window;
EGLSurface egl_surface;
uint32_t enter_serial;
struct {
struct wlr_wl_pointer *pointer;
struct wl_surface *surface;
struct wl_egl_window *egl_window;
int32_t hotspot_x, hotspot_y;
int32_t width, height;
} cursor;
};
struct wlr_wl_input_device {
struct wlr_input_device wlr_input_device;
struct wl_list link;
uint32_t fingers;
struct wlr_wl_backend *backend;
struct wlr_wl_seat *seat;
void *resource;
};
@ -100,10 +74,6 @@ struct wlr_wl_pointer {
struct wlr_wl_input_device *input_device;
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;
int32_t axis_discrete;
struct wlr_wl_output *output;
@ -111,35 +81,12 @@ struct wlr_wl_pointer {
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);
void update_wl_output_cursor(struct wlr_wl_output *output);
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_keyboard(struct wlr_wl_seat *seat);
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);
void create_wl_pointer(struct wl_pointer *wl_pointer, struct wlr_wl_output *output);
void create_wl_keyboard(struct wl_keyboard *wl_keyboard, struct wlr_wl_backend *wl);
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

Some files were not shown because too many files have changed in this diff Show More