mirror of
https://github.com/cage-kiosk/cage.git
synced 2025-11-01 22:58:41 -04:00
Compare commits
No commits in common. "master" and "v0.1" have entirely different histories.
31 changed files with 1352 additions and 1848 deletions
28
.builds/alpine.yml
Normal file
28
.builds/alpine.yml
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
image: alpine/edge
|
||||||
|
packages:
|
||||||
|
- eudev-dev
|
||||||
|
- mesa-dev
|
||||||
|
- meson
|
||||||
|
- libinput-dev
|
||||||
|
- libxkbcommon-dev
|
||||||
|
- pixman-dev
|
||||||
|
- wayland-dev
|
||||||
|
- wayland-protocols
|
||||||
|
- xorg-server-xwayland
|
||||||
|
sources:
|
||||||
|
- https://github.com/swaywm/wlroots
|
||||||
|
- https://github.com/Hjdskes/cage
|
||||||
|
tasks:
|
||||||
|
# Install wlroots, which is required by Cage. Note that we compile a tagged
|
||||||
|
# version, instead of master, to avoid any breaking changes in wlroots.
|
||||||
|
- wlroots: |
|
||||||
|
cd wlroots
|
||||||
|
# This corresponds to the tag of 0.5.0
|
||||||
|
git checkout c9137cba691b57c3eaf3ff94f9bf8e623f66ccc5
|
||||||
|
meson --prefix=/usr build -Drootston=false -Dexamples=false
|
||||||
|
ninja -C build
|
||||||
|
sudo ninja -C build install
|
||||||
|
- build: |
|
||||||
|
cd cage
|
||||||
|
meson build -Dxwayland=true
|
||||||
|
ninja -C build
|
||||||
30
.builds/archlinux.yml
Normal file
30
.builds/archlinux.yml
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
image: archlinux
|
||||||
|
packages:
|
||||||
|
- clang
|
||||||
|
- meson
|
||||||
|
- libinput
|
||||||
|
- libxkbcommon
|
||||||
|
- wayland
|
||||||
|
- wayland-protocols
|
||||||
|
- xorg-server-xwayland
|
||||||
|
sources:
|
||||||
|
- https://github.com/swaywm/wlroots
|
||||||
|
- https://github.com/Hjdskes/cage
|
||||||
|
tasks:
|
||||||
|
# Install wlroots, which is required by Cage. Note that we compile a tagged
|
||||||
|
# version, instead of master, to avoid any breaking changes in wlroots.
|
||||||
|
- wlroots: |
|
||||||
|
cd wlroots
|
||||||
|
# This corresponds to the tag of 0.5.0
|
||||||
|
git checkout c9137cba691b57c3eaf3ff94f9bf8e623f66ccc5
|
||||||
|
meson --prefix=/usr build -Drootston=false -Dexamples=false
|
||||||
|
ninja -C build
|
||||||
|
sudo ninja -C build install
|
||||||
|
- build: |
|
||||||
|
cd cage
|
||||||
|
meson build -Dxwayland=true
|
||||||
|
ninja -C build
|
||||||
|
- scan-build: |
|
||||||
|
cd cage
|
||||||
|
CC=clang meson build -Dxwayland=true
|
||||||
|
CC=clang ninja -C build scan-build
|
||||||
45
.builds/freebsd.yml
Normal file
45
.builds/freebsd.yml
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
image: freebsd/latest
|
||||||
|
packages:
|
||||||
|
- devel/evdev-proto
|
||||||
|
- devel/meson
|
||||||
|
- devel/libepoll-shim
|
||||||
|
- devel/pkgconf
|
||||||
|
- graphics/mesa-libs
|
||||||
|
- graphics/wayland
|
||||||
|
- graphics/wayland-protocols
|
||||||
|
- x11/libinput
|
||||||
|
- x11/libxkbcommon
|
||||||
|
- x11/pixman
|
||||||
|
sources:
|
||||||
|
- https://github.com/swaywm/wlroots
|
||||||
|
- https://github.com/Hjdskes/cage
|
||||||
|
tasks:
|
||||||
|
# Taken from sway
|
||||||
|
- 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
|
||||||
|
# Install wlroots, which is required by Cage. Note that we compile a tagged
|
||||||
|
# version, instead of master, to avoid any breaking changes in wlroots.
|
||||||
|
- wlroots: |
|
||||||
|
cd wlroots
|
||||||
|
# This corresponds to the tag of 0.5.0
|
||||||
|
git checkout c9137cba691b57c3eaf3ff94f9bf8e623f66ccc5
|
||||||
|
meson --prefix=/usr/local build -Drootston=false -Dexamples=false
|
||||||
|
ninja -C build
|
||||||
|
sudo ninja -C build install
|
||||||
|
- build: |
|
||||||
|
cd cage
|
||||||
|
PKG_CONFIG_PATH=/usr/local/lib/pkgconfig meson build -Dxwayland=true
|
||||||
|
PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ninja -C build
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
AlignAfterOpenBracket: Align
|
|
||||||
AlignTrailingComments: false
|
|
||||||
AlwaysBreakAfterReturnType: TopLevelDefinitions
|
|
||||||
BreakBeforeBraces: Linux
|
|
||||||
ColumnLimit: 120
|
|
||||||
ContinuationIndentWidth: 8
|
|
||||||
ForEachMacros: [wl_list_for_each, wl_list_for_each_safe, wl_list_for_each_reverse]
|
|
||||||
IndentWidth: 8
|
|
||||||
ReflowComments: true
|
|
||||||
SortIncludes: true
|
|
||||||
SpaceAfterCStyleCast: true
|
|
||||||
TabWidth: 8
|
|
||||||
UseTab: Always
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
subprojects/**/*
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
charset = utf-8
|
|
||||||
end_of_line = lf
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
insert_final_newline = true
|
|
||||||
indent_style = tab
|
|
||||||
indent_size = 8
|
|
||||||
max_line_length = 120
|
|
||||||
81
.github/workflows/main.yml
vendored
81
.github/workflows/main.yml
vendored
|
|
@ -1,81 +0,0 @@
|
||||||
name: Continuous integration build
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
env:
|
|
||||||
WLROOTS_VERSION: 0.19
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
compile:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
CC: [ gcc, clang ]
|
|
||||||
OS: [ "alpine:edge", "archlinux:base-devel" ]
|
|
||||||
xwayland: [ enabled, disabled ]
|
|
||||||
container: ${{ matrix.OS }}
|
|
||||||
env:
|
|
||||||
CC: ${{ matrix.CC }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout Cage
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Install dependencies (Alpine)
|
|
||||||
if: "matrix.OS == 'alpine:edge'"
|
|
||||||
run: apk add build-base xcb-util-wm-dev libseat-dev clang git eudev-dev mesa-dev libdrm-dev libinput-dev libxkbcommon-dev pixman-dev wayland-dev meson wayland-protocols xwayland-dev scdoc-doc hwdata libdisplay-info-dev
|
|
||||||
|
|
||||||
- name: Install dependencies (Arch)
|
|
||||||
if: "matrix.OS == 'archlinux:base-devel'"
|
|
||||||
run: |
|
|
||||||
pacman-key --init
|
|
||||||
pacman -Syu --noconfirm xcb-util-wm seatd git clang meson libinput libdrm mesa libxkbcommon wayland wayland-protocols xorg-server-xwayland scdoc libdisplay-info
|
|
||||||
|
|
||||||
- name: Fetch wlroots as a subproject
|
|
||||||
run: git clone https://gitlab.freedesktop.org/wlroots/wlroots.git subprojects/wlroots -b $WLROOTS_VERSION
|
|
||||||
|
|
||||||
- name: Compile Cage (XWayland=${{ matrix.xwayland }})
|
|
||||||
run: |
|
|
||||||
meson --fatal-meson-warnings --wrap-mode=nodownload \
|
|
||||||
build-${{ matrix.CC }}-${{matrix.xwayland }} \
|
|
||||||
-Dwlroots:xwayland=${{ matrix.xwayland }}
|
|
||||||
ninja -C build-${{ matrix.CC }}-${{matrix.xwayland }}
|
|
||||||
|
|
||||||
format:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: "archlinux:base-devel"
|
|
||||||
steps:
|
|
||||||
- name: Checkout Cage
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
pacman-key --init
|
|
||||||
pacman -Syu --noconfirm xcb-util-wm seatd git clang meson libinput libdrm mesa libxkbcommon wayland wayland-protocols xorg-server-xwayland scdoc hwdata libdisplay-info
|
|
||||||
- name: Fetch wlroots as a subproject
|
|
||||||
run: git clone https://gitlab.freedesktop.org/wlroots/wlroots.git subprojects/wlroots -b $WLROOTS_VERSION
|
|
||||||
- name: Check for formatting changes
|
|
||||||
run: |
|
|
||||||
meson --wrap-mode=nodownload build-clang-format -Dwlroots:xwayland=enabled
|
|
||||||
ninja -C build-clang-format clang-format-check
|
|
||||||
|
|
||||||
scan-build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
container: "archlinux:base-devel"
|
|
||||||
env:
|
|
||||||
CC: clang
|
|
||||||
steps:
|
|
||||||
- name: Checkout Cage
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
pacman-key --init
|
|
||||||
pacman -Syu --noconfirm xcb-util-wm seatd git clang meson libinput libdrm mesa libxkbcommon wayland wayland-protocols xorg-server-xwayland scdoc hwdata libdisplay-info
|
|
||||||
- name: Fetch wlroots as a subproject
|
|
||||||
run: git clone https://gitlab.freedesktop.org/wlroots/wlroots.git subprojects/wlroots -b $WLROOTS_VERSION
|
|
||||||
- name: Run scan-build
|
|
||||||
run: |
|
|
||||||
meson --wrap-mode=nodownload build-scan-build -Dwlroots:xwayland=enabled
|
|
||||||
ninja -C build-scan-build scan-build
|
|
||||||
3
LICENSE
3
LICENSE
|
|
@ -1,5 +1,4 @@
|
||||||
Copyright (c) 2018-2020 Jente Hidskes
|
Copyright (c) 2018-2019 Jente Hidskes
|
||||||
Copyright (c) 2019 The Sway authors
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
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
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
|
|
||||||
61
README.md
61
README.md
|
|
@ -1,61 +1,52 @@
|
||||||
# Cage: a Wayland kiosk
|
# Cage: a Wayland kiosk [](https://builds.sr.ht/~hjdskes?)
|
||||||
|
|
||||||
<img src="https://www.hjdskes.nl/img/projects/cage/cage.svg" alt="Cage's logo" width="150px" align="right">
|
|
||||||
|
|
||||||
This is Cage, a Wayland kiosk. A kiosk runs a single, maximized
|
This is Cage, a Wayland kiosk. A kiosk runs a single, maximized
|
||||||
application.
|
application.
|
||||||
|
|
||||||
This README is only relevant for development resources and instructions. For a
|
This README is only relevant for development resources and
|
||||||
description of Cage and installation instructions for end-users, please see
|
instructions. For a description of Cage and installation instructions
|
||||||
[its project page](https://www.hjdskes.nl/projects/cage) and [the
|
for end-users, please see [its project
|
||||||
Wiki](https://github.com/cage-kiosk/cage/wiki/).
|
page](https://hjdskes.nl/projects/cage).
|
||||||
See [the man page](./cage.1.scd) for a list of possible environment variables and run options.
|
|
||||||
|
|
||||||
## Release signatures
|
|
||||||
|
|
||||||
Releases up to version 0.1.4 are signed with [6EBC43B1](http://keys.gnupg.net/pks/lookup?op=vindex&fingerprint=on&search=0x37C445296EBC43B1). Releases from 0.1.5 onwards are signed with
|
|
||||||
[E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)
|
|
||||||
All releases are published on [GitHub](https://github.com/cage-kiosk/cage/releases).
|
|
||||||
|
|
||||||
## Building and running Cage
|
## Building and running Cage
|
||||||
|
|
||||||
You can build Cage with the [meson](https://mesonbuild.com/) build system. It
|
You can build Cage with the [meson](https://mesonbuild.com/) build system. It
|
||||||
requires wayland, wlroots, and xkbcommon to be installed. Optionally, install
|
requires wayland, wlroots and xkbcommon to be installed. Note that Cage is
|
||||||
scdoc for manual pages. Cage is currently based on branch 0.18 of wlroots.
|
developed against the latest tag of wlroots, in order to not constantly chase
|
||||||
|
breaking changes as soon as they occur.
|
||||||
|
|
||||||
Simply execute the following steps to build Cage:
|
Simply execute the following steps to build Cage:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ meson setup build
|
$ meson build
|
||||||
$ meson compile -C build
|
$ ninja -C build
|
||||||
```
|
```
|
||||||
|
|
||||||
By default, this builds a debug build. To build a release build, use `meson
|
Cage comes with compile-time support for XWayland. To enable this,
|
||||||
setup build --buildtype=release`.
|
first make sure that your version of wlroots is compiled with this
|
||||||
|
option. Then, add `-Dxwayland=true` to the `meson` command above. Note
|
||||||
|
that you'll need to have the XWayland binary installed on your system
|
||||||
|
for this to work.
|
||||||
|
|
||||||
Cage comes with compile-time support for XWayland. To enable this, make sure
|
You can run Cage by running `./build/cage APPLICATION`. If you run it
|
||||||
that your version of wlroots is compiled with this option. Note that you'll
|
from within an existing X11 or Wayland session, it will open in a
|
||||||
need to have the XWayland binary installed on your system for this to work.
|
virtual output as a window in your existing session. If you run it at
|
||||||
|
a TTY, it'll run with the KMS+DRM backend. In debug mode (default
|
||||||
You can run Cage by running `./build/cage APPLICATION`. If you run it from
|
build type with Meson), press Alt+Esc to quit. To build a release
|
||||||
within an existing X11 or Wayland session, it will open in a virtual output as
|
build, use `meson build --buildtype=release`.
|
||||||
a window in your existing session. If you run it at a TTY, it'll run with the
|
|
||||||
KMS+DRM backend. In debug mode (default build type with Meson), press
|
|
||||||
<kbd>Alt</kbd>+<kbd>Esc</kbd> to quit. For more configuration options, see
|
|
||||||
[Configuration](https://github.com/cage-kiosk/cage/wiki/Configuration).
|
|
||||||
|
|
||||||
Cage is based on the annotated source of tinywl and rootston.
|
Cage is based on the annotated source of tinywl and rootston.
|
||||||
|
|
||||||
## Bugs
|
## Bugs
|
||||||
|
|
||||||
For any bug, please [create an
|
For any bug, please [create an
|
||||||
issue](https://github.com/cage-kiosk/cage/issues/new) on
|
issue](https://github.com/Hjdskes/cage/issues/new) on
|
||||||
[GitHub](https://github.com/cage-kiosk/cage).
|
[GitHub](https://github.com/Hjdskes/cage).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Please see
|
Please see
|
||||||
[LICENSE](https://github.com/cage-kiosk/cage/blob/master/LICENSE) on
|
[LICENSE](https://github.com/Hjdskes/cage/blob/master/LICENSE) on
|
||||||
[GitHub](https://github.com/cage-kiosk/cage).
|
[GitHub](https://github.com/Hjdskes/cage).
|
||||||
|
|
||||||
Copyright © 2018-2020 Jente Hidskes <dev@hjdskes.nl>
|
Copyright © 2018-2019 Jente Hidskes <hjdskes@gmail.com>
|
||||||
|
|
|
||||||
71
cage.1.scd
71
cage.1.scd
|
|
@ -1,71 +0,0 @@
|
||||||
cage(1)
|
|
||||||
|
|
||||||
# NAME
|
|
||||||
|
|
||||||
cage - a Wayland kiosk compositor
|
|
||||||
|
|
||||||
# SYNOPSIS
|
|
||||||
|
|
||||||
*cage* [options...] [--] [_application_...]
|
|
||||||
|
|
||||||
# DESCRIPTION
|
|
||||||
|
|
||||||
Cage runs a single, maximized application. Cage can run multiple applications,
|
|
||||||
but only a single one is visible at any point in time. User interaction and
|
|
||||||
activities outside the scope of the running application are prevented.
|
|
||||||
|
|
||||||
# OPTIONS
|
|
||||||
|
|
||||||
*-d*
|
|
||||||
Don't draw client side decorations when possible.
|
|
||||||
|
|
||||||
*-D*
|
|
||||||
Enable debug logging.
|
|
||||||
|
|
||||||
*-h*
|
|
||||||
Show the help message.
|
|
||||||
|
|
||||||
*-m* <mode>
|
|
||||||
Set the multi-monitor behavior. Supported modes are:
|
|
||||||
*last* Cage uses only the last connected monitor.
|
|
||||||
*extend* Cage extends the display across all connected monitors.
|
|
||||||
|
|
||||||
*-s*
|
|
||||||
Allow VT switching
|
|
||||||
|
|
||||||
*-v*
|
|
||||||
Show the version number and exit.
|
|
||||||
|
|
||||||
# ENVIRONMENT
|
|
||||||
|
|
||||||
_DISPLAY_
|
|
||||||
If compiled with Xwayland support, this will be set to the name of the
|
|
||||||
X display used for Xwayland. Otherwise, probe the X11 backend.
|
|
||||||
|
|
||||||
_WAYLAND_DISPLAY_
|
|
||||||
Specifies the name of the Wayland display that Cage is running on.
|
|
||||||
|
|
||||||
_XCURSOR_PATH_
|
|
||||||
Directory where cursors are located.
|
|
||||||
|
|
||||||
_XCURSOR_SIZE_
|
|
||||||
Specifies the configured cursor size.
|
|
||||||
|
|
||||||
_XCURSOR_THEME_
|
|
||||||
Specifies the configured cursor theme.
|
|
||||||
|
|
||||||
_XKB_DEFAULT_RULES_, _XKB_DEFAULT_MODEL_, _XKB_DEFAULT_LAYOUT_,
|
|
||||||
_XKB_DEFAULT_VARIANT_, _XKB_DEFAULT_OPTIONS_
|
|
||||||
Configures the xkb keyboard settings. See *xkeyboard-config*(7).
|
|
||||||
|
|
||||||
# SEE ALSO
|
|
||||||
|
|
||||||
*xkeyboard-config(7)*
|
|
||||||
|
|
||||||
# BUGS
|
|
||||||
|
|
||||||
Report bugs at https://github.com/cage-kiosk/cage
|
|
||||||
|
|
||||||
# AUTHORS
|
|
||||||
|
|
||||||
Jente Hidskes <dev@hjdskes.nl>
|
|
||||||
508
cage.c
508
cage.c
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Cage: A Wayland kiosk.
|
* Cage: A Wayland kiosk.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2018-2020 Jente Hidskes
|
* Copyright (C) 2018-2019 Jente Hidskes
|
||||||
*
|
*
|
||||||
* See the LICENSE file accompanying this file.
|
* See the LICENSE file accompanying this file.
|
||||||
*/
|
*/
|
||||||
|
|
@ -10,41 +10,25 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
#include <wlr/render/allocator.h>
|
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/types/wlr_compositor.h>
|
#include <wlr/types/wlr_compositor.h>
|
||||||
#include <wlr/types/wlr_data_device.h>
|
#include <wlr/types/wlr_data_device.h>
|
||||||
#include <wlr/types/wlr_export_dmabuf_v1.h>
|
#include <wlr/types/wlr_idle.h>
|
||||||
#include <wlr/types/wlr_gamma_control_v1.h>
|
|
||||||
#include <wlr/types/wlr_idle_inhibit_v1.h>
|
#include <wlr/types/wlr_idle_inhibit_v1.h>
|
||||||
#include <wlr/types/wlr_idle_notify_v1.h>
|
|
||||||
#include <wlr/types/wlr_output_layout.h>
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
#include <wlr/types/wlr_output_management_v1.h>
|
|
||||||
#include <wlr/types/wlr_presentation_time.h>
|
|
||||||
#include <wlr/types/wlr_primary_selection_v1.h>
|
|
||||||
#include <wlr/types/wlr_relative_pointer_v1.h>
|
|
||||||
#include <wlr/types/wlr_scene.h>
|
|
||||||
#include <wlr/types/wlr_screencopy_v1.h>
|
|
||||||
#include <wlr/types/wlr_server_decoration.h>
|
#include <wlr/types/wlr_server_decoration.h>
|
||||||
#include <wlr/types/wlr_single_pixel_buffer_v1.h>
|
|
||||||
#include <wlr/types/wlr_subcompositor.h>
|
|
||||||
#include <wlr/types/wlr_viewporter.h>
|
|
||||||
#include <wlr/types/wlr_virtual_keyboard_v1.h>
|
|
||||||
#include <wlr/types/wlr_virtual_pointer_v1.h>
|
|
||||||
#if CAGE_HAS_XWAYLAND
|
#if CAGE_HAS_XWAYLAND
|
||||||
#include <wlr/types/wlr_xcursor_manager.h>
|
#include <wlr/types/wlr_xcursor_manager.h>
|
||||||
#endif
|
#endif
|
||||||
#include <wlr/types/wlr_xdg_decoration_v1.h>
|
#include <wlr/types/wlr_xdg_decoration_v1.h>
|
||||||
#include <wlr/types/wlr_xdg_output_v1.h>
|
|
||||||
#include <wlr/types/wlr_xdg_shell.h>
|
#include <wlr/types/wlr_xdg_shell.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#if CAGE_HAS_XWAYLAND
|
#if CAGE_HAS_XWAYLAND
|
||||||
|
|
@ -61,178 +45,53 @@
|
||||||
#include "xwayland.h"
|
#include "xwayland.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void
|
|
||||||
server_terminate(struct cg_server *server)
|
|
||||||
{
|
|
||||||
// Workaround for https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/421
|
|
||||||
if (server->terminated) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wl_display_terminate(server->wl_display);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
handle_display_destroy(struct wl_listener *listener, void *data)
|
|
||||||
{
|
|
||||||
struct cg_server *server = wl_container_of(listener, server, display_destroy);
|
|
||||||
server->terminated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
sigchld_handler(int fd, uint32_t mask, void *data)
|
|
||||||
{
|
|
||||||
struct cg_server *server = data;
|
|
||||||
|
|
||||||
/* Close Cage's read pipe. */
|
|
||||||
close(fd);
|
|
||||||
|
|
||||||
if (mask & WL_EVENT_HANGUP) {
|
|
||||||
wlr_log(WLR_DEBUG, "Child process closed normally");
|
|
||||||
} else if (mask & WL_EVENT_ERROR) {
|
|
||||||
wlr_log(WLR_DEBUG, "Connection closed by server");
|
|
||||||
}
|
|
||||||
|
|
||||||
server->return_app_code = true;
|
|
||||||
server_terminate(server);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
set_cloexec(int fd)
|
spawn_primary_client(char *argv[], pid_t *pid_out)
|
||||||
{
|
{
|
||||||
int flags = fcntl(fd, F_GETFD);
|
|
||||||
|
|
||||||
if (flags == -1) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to set the CLOEXEC flag: fnctl failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
flags = flags | FD_CLOEXEC;
|
|
||||||
if (fcntl(fd, F_SETFD, flags) == -1) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to set the CLOEXEC flag: fnctl failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
spawn_primary_client(struct cg_server *server, char *argv[], pid_t *pid_out, struct wl_event_source **sigchld_source)
|
|
||||||
{
|
|
||||||
int fd[2];
|
|
||||||
if (pipe(fd) != 0) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to create pipe");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
sigset_t set;
|
sigset_t set;
|
||||||
sigemptyset(&set);
|
sigemptyset(&set);
|
||||||
sigprocmask(SIG_SETMASK, &set, NULL);
|
sigprocmask(SIG_SETMASK, &set, NULL);
|
||||||
/* Close read, we only need write in the primary client process. */
|
|
||||||
close(fd[0]);
|
|
||||||
execvp(argv[0], argv);
|
execvp(argv[0], argv);
|
||||||
/* execvp() returns only on failure */
|
|
||||||
wlr_log_errno(WLR_ERROR, "Failed to spawn client");
|
|
||||||
_exit(1);
|
_exit(1);
|
||||||
} else if (pid == -1) {
|
} else if (pid == -1) {
|
||||||
wlr_log_errno(WLR_ERROR, "Unable to fork");
|
wlr_log_errno(WLR_ERROR, "Unable to fork");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set this early so that if we fail, the client process will be cleaned up properly. */
|
|
||||||
*pid_out = pid;
|
*pid_out = pid;
|
||||||
|
|
||||||
if (!set_cloexec(fd[0]) || !set_cloexec(fd[1])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Close write, we only need read in Cage. */
|
|
||||||
close(fd[1]);
|
|
||||||
|
|
||||||
struct wl_event_loop *event_loop = wl_display_get_event_loop(server->wl_display);
|
|
||||||
uint32_t mask = WL_EVENT_HANGUP | WL_EVENT_ERROR;
|
|
||||||
*sigchld_source = wl_event_loop_add_fd(event_loop, fd[0], mask, sigchld_handler, server);
|
|
||||||
|
|
||||||
wlr_log(WLR_DEBUG, "Child process created with pid %d", pid);
|
wlr_log(WLR_DEBUG, "Child process created with pid %d", pid);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
cleanup_primary_client(pid_t pid)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
|
|
||||||
waitpid(pid, &status, 0);
|
|
||||||
|
|
||||||
if (WIFEXITED(status)) {
|
|
||||||
wlr_log(WLR_DEBUG, "Child exited normally with exit status %d", WEXITSTATUS(status));
|
|
||||||
return WEXITSTATUS(status);
|
|
||||||
} else if (WIFSIGNALED(status)) {
|
|
||||||
/* Mimic Bash and other shells for the exit status */
|
|
||||||
wlr_log(WLR_DEBUG, "Child was terminated by a signal (%d)", WTERMSIG(status));
|
|
||||||
return 128 + WTERMSIG(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
drop_permissions(void)
|
|
||||||
{
|
|
||||||
if (getuid() == 0 || getgid() == 0) {
|
|
||||||
wlr_log(WLR_INFO, "Running as root user, this is dangerous");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (getuid() != geteuid() || getgid() != getegid()) {
|
|
||||||
wlr_log(WLR_INFO, "setuid/setgid bit detected, dropping permissions");
|
|
||||||
// Set the gid and uid in the correct order.
|
|
||||||
if (setgid(getgid()) != 0 || setuid(getuid()) != 0) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to drop root, refusing to start");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (setgid(0) != -1 || setuid(0) != -1) {
|
|
||||||
wlr_log(WLR_ERROR,
|
|
||||||
"Unable to drop root (we shouldn't be able to restore it after setuid), refusing to start");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
handle_signal(int signal, void *data)
|
handle_signal(int signal, void *data)
|
||||||
{
|
{
|
||||||
struct cg_server *server = data;
|
struct wl_display *display = data;
|
||||||
|
|
||||||
switch (signal) {
|
switch (signal) {
|
||||||
case SIGINT:
|
case SIGINT:
|
||||||
/* Fallthrough */
|
/* Fallthrough */
|
||||||
case SIGTERM:
|
case SIGTERM:
|
||||||
server_terminate(server);
|
wl_display_terminate(display);
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
usage(FILE *file, const char *cage)
|
usage(FILE *file, const char *cage)
|
||||||
{
|
{
|
||||||
fprintf(file,
|
fprintf(file, "Usage: %s [OPTIONS] [--] APPLICATION\n"
|
||||||
"Usage: %s [OPTIONS] [--] [APPLICATION...]\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
" -d\t Don't draw client side decorations, when possible\n"
|
" -d\t Don't draw client side decorations, when possible\n"
|
||||||
" -D\t Enable debug logging\n"
|
" -r\t Rotate the output 90 degrees clockwise, specify up to three times\n"
|
||||||
|
#ifdef DEBUG
|
||||||
|
" -D\t Turn on damage tracking debugging\n"
|
||||||
|
#endif
|
||||||
" -h\t Display this help message\n"
|
" -h\t Display this help message\n"
|
||||||
" -m extend Extend the display across all connected outputs (default)\n"
|
|
||||||
" -m last Use only the last connected output\n"
|
|
||||||
" -s\t Allow VT switching\n"
|
|
||||||
" -v\t Show the version number and exit\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
" Use -- when you want to pass arguments to APPLICATION\n",
|
" Use -- when you want to pass arguments to APPLICATION\n",
|
||||||
cage);
|
cage);
|
||||||
|
|
@ -242,62 +101,71 @@ static bool
|
||||||
parse_args(struct cg_server *server, int argc, char *argv[])
|
parse_args(struct cg_server *server, int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
while ((c = getopt(argc, argv, "dDhm:sv")) != -1) {
|
#ifdef DEBUG
|
||||||
|
while ((c = getopt(argc, argv, "drDh")) != -1) {
|
||||||
|
#else
|
||||||
|
while ((c = getopt(argc, argv, "drh")) != -1) {
|
||||||
|
#endif
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'd':
|
case 'd':
|
||||||
server->xdg_decoration = true;
|
server->xdg_decoration = true;
|
||||||
break;
|
break;
|
||||||
case 'D':
|
case 'r':
|
||||||
server->log_level = WLR_DEBUG;
|
server->output_transform++;
|
||||||
|
if (server->output_transform > WL_OUTPUT_TRANSFORM_270) {
|
||||||
|
server->output_transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
#ifdef DEBUG
|
||||||
|
case 'D':
|
||||||
|
server->debug_damage_tracking = true;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
case 'h':
|
case 'h':
|
||||||
usage(stdout, argv[0]);
|
usage(stdout, argv[0]);
|
||||||
return false;
|
return false;
|
||||||
case 'm':
|
|
||||||
if (strcmp(optarg, "last") == 0) {
|
|
||||||
server->output_mode = CAGE_MULTI_OUTPUT_MODE_LAST;
|
|
||||||
} else if (strcmp(optarg, "extend") == 0) {
|
|
||||||
server->output_mode = CAGE_MULTI_OUTPUT_MODE_EXTEND;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
server->allow_vt_switch = true;
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
fprintf(stdout, "Cage version " CAGE_VERSION "\n");
|
|
||||||
exit(0);
|
|
||||||
default:
|
default:
|
||||||
usage(stderr, argv[0]);
|
usage(stderr, argv[0]);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (optind >= argc) {
|
||||||
|
usage(stderr, argv[0]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
struct cg_server server = {.log_level = WLR_INFO};
|
struct cg_server server = {0};
|
||||||
struct wl_event_source *sigchld_source = NULL;
|
struct wl_event_loop *event_loop = NULL;
|
||||||
pid_t pid = 0;
|
struct wl_event_source *sigint_source = NULL;
|
||||||
int ret = 0, app_ret = 0;
|
struct wl_event_source *sigterm_source = NULL;
|
||||||
|
struct wlr_renderer *renderer = NULL;
|
||||||
#ifdef DEBUG
|
struct wlr_compositor *compositor = NULL;
|
||||||
server.log_level = WLR_DEBUG;
|
struct wlr_data_device_manager *data_device_mgr = NULL;
|
||||||
|
struct wlr_server_decoration_manager *server_decoration_manager = NULL;
|
||||||
|
struct wlr_xdg_decoration_manager_v1 *xdg_decoration_manager = NULL;
|
||||||
|
struct wlr_xdg_shell *xdg_shell = NULL;
|
||||||
|
#if CAGE_HAS_XWAYLAND
|
||||||
|
struct wlr_xwayland *xwayland = NULL;
|
||||||
|
struct wlr_xcursor_manager *xcursor_manager = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
if (!parse_args(&server, argc, argv)) {
|
if (!parse_args(&server, argc, argv)) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_log_init(server.log_level, NULL);
|
#ifdef DEBUG
|
||||||
|
wlr_log_init(WLR_DEBUG, NULL);
|
||||||
/* Wayland requires XDG_RUNTIME_DIR to be set. */
|
#else
|
||||||
if (!getenv("XDG_RUNTIME_DIR")) {
|
wlr_log_init(WLR_ERROR, NULL);
|
||||||
wlr_log(WLR_ERROR, "XDG_RUNTIME_DIR is not set in the environment");
|
#endif
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
server.wl_display = wl_display_create();
|
server.wl_display = wl_display_create();
|
||||||
if (!server.wl_display) {
|
if (!server.wl_display) {
|
||||||
|
|
@ -305,101 +173,57 @@ main(int argc, char *argv[])
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
server.display_destroy.notify = handle_display_destroy;
|
event_loop = wl_display_get_event_loop(server.wl_display);
|
||||||
wl_display_add_destroy_listener(server.wl_display, &server.display_destroy);
|
sigint_source = wl_event_loop_add_signal(event_loop, SIGINT, handle_signal, &server.wl_display);
|
||||||
|
sigterm_source = wl_event_loop_add_signal(event_loop, SIGTERM, handle_signal, &server.wl_display);
|
||||||
|
|
||||||
struct wl_event_loop *event_loop = wl_display_get_event_loop(server.wl_display);
|
server.backend = wlr_backend_autocreate(server.wl_display, NULL);
|
||||||
struct wl_event_source *sigint_source = wl_event_loop_add_signal(event_loop, SIGINT, handle_signal, &server);
|
|
||||||
struct wl_event_source *sigterm_source = wl_event_loop_add_signal(event_loop, SIGTERM, handle_signal, &server);
|
|
||||||
|
|
||||||
server.backend = wlr_backend_autocreate(event_loop, &server.session);
|
|
||||||
if (!server.backend) {
|
if (!server.backend) {
|
||||||
wlr_log(WLR_ERROR, "Unable to create the wlroots backend");
|
wlr_log(WLR_ERROR, "Unable to create the wlroots backend");
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!drop_permissions()) {
|
renderer = wlr_backend_get_renderer(server.backend);
|
||||||
ret = 1;
|
wlr_renderer_init_wl_display(renderer, server.wl_display);
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
server.renderer = wlr_renderer_autocreate(server.backend);
|
|
||||||
if (!server.renderer) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to create the wlroots renderer");
|
|
||||||
ret = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
server.allocator = wlr_allocator_autocreate(server.backend, server.renderer);
|
|
||||||
if (!server.allocator) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to create the wlroots allocator");
|
|
||||||
ret = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_renderer_init_wl_display(server.renderer, server.wl_display);
|
|
||||||
|
|
||||||
wl_list_init(&server.views);
|
wl_list_init(&server.views);
|
||||||
wl_list_init(&server.outputs);
|
|
||||||
|
|
||||||
server.output_layout = wlr_output_layout_create(server.wl_display);
|
server.output_layout = wlr_output_layout_create();
|
||||||
if (!server.output_layout) {
|
if (!server.output_layout) {
|
||||||
wlr_log(WLR_ERROR, "Unable to create output layout");
|
wlr_log(WLR_ERROR, "Unable to create output layout");
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
server.output_layout_change.notify = handle_output_layout_change;
|
|
||||||
wl_signal_add(&server.output_layout->events.change, &server.output_layout_change);
|
|
||||||
|
|
||||||
server.scene = wlr_scene_create();
|
compositor = wlr_compositor_create(server.wl_display, renderer);
|
||||||
if (!server.scene) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to create scene");
|
|
||||||
ret = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
server.scene_output_layout = wlr_scene_attach_output_layout(server.scene, server.output_layout);
|
|
||||||
|
|
||||||
struct wlr_compositor *compositor = wlr_compositor_create(server.wl_display, 6, server.renderer);
|
|
||||||
if (!compositor) {
|
if (!compositor) {
|
||||||
wlr_log(WLR_ERROR, "Unable to create the wlroots compositor");
|
wlr_log(WLR_ERROR, "Unable to create the wlroots compositor");
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wlr_subcompositor_create(server.wl_display)) {
|
data_device_mgr = wlr_data_device_manager_create(server.wl_display);
|
||||||
wlr_log(WLR_ERROR, "Unable to create the wlroots subcompositor");
|
if (!data_device_mgr) {
|
||||||
ret = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wlr_data_device_manager_create(server.wl_display)) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to create the data device manager");
|
wlr_log(WLR_ERROR, "Unable to create the data device manager");
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wlr_primary_selection_v1_device_manager_create(server.wl_display)) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to create primary selection device manager");
|
|
||||||
ret = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Configure a listener to be notified when new outputs are
|
/* Configure a listener to be notified when new outputs are
|
||||||
* available on the backend. We use this only to detect the
|
* available on the backend. We use this only to detect the
|
||||||
* first output and ignore subsequent outputs. */
|
* first output and ignore subsequent outputs. */
|
||||||
server.new_output.notify = handle_new_output;
|
server.new_output.notify = handle_new_output;
|
||||||
wl_signal_add(&server.backend->events.new_output, &server.new_output);
|
wl_signal_add(&server.backend->events.new_output, &server.new_output);
|
||||||
|
|
||||||
server.seat = seat_create(&server, server.backend);
|
server.seat = seat_create(&server);
|
||||||
if (!server.seat) {
|
if (!server.seat) {
|
||||||
wlr_log(WLR_ERROR, "Unable to create the seat");
|
wlr_log(WLR_ERROR, "Unable to create the seat");
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
server.idle = wlr_idle_notifier_v1_create(server.wl_display);
|
server.idle = wlr_idle_create(server.wl_display);
|
||||||
if (!server.idle) {
|
if (!server.idle) {
|
||||||
wlr_log(WLR_ERROR, "Unable to create the idle tracker");
|
wlr_log(WLR_ERROR, "Unable to create the idle tracker");
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
|
@ -416,19 +240,16 @@ main(int argc, char *argv[])
|
||||||
wl_signal_add(&server.idle_inhibit_v1->events.new_inhibitor, &server.new_idle_inhibitor_v1);
|
wl_signal_add(&server.idle_inhibit_v1->events.new_inhibitor, &server.new_idle_inhibitor_v1);
|
||||||
wl_list_init(&server.inhibitors);
|
wl_list_init(&server.inhibitors);
|
||||||
|
|
||||||
struct wlr_xdg_shell *xdg_shell = wlr_xdg_shell_create(server.wl_display, 5);
|
xdg_shell = wlr_xdg_shell_create(server.wl_display);
|
||||||
if (!xdg_shell) {
|
if (!xdg_shell) {
|
||||||
wlr_log(WLR_ERROR, "Unable to create the XDG shell interface");
|
wlr_log(WLR_ERROR, "Unable to create the XDG shell interface");
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
server.new_xdg_toplevel.notify = handle_new_xdg_toplevel;
|
server.new_xdg_shell_surface.notify = handle_xdg_shell_surface_new;
|
||||||
wl_signal_add(&xdg_shell->events.new_toplevel, &server.new_xdg_toplevel);
|
wl_signal_add(&xdg_shell->events.new_surface, &server.new_xdg_shell_surface);
|
||||||
server.new_xdg_popup.notify = handle_new_xdg_popup;
|
|
||||||
wl_signal_add(&xdg_shell->events.new_popup, &server.new_xdg_popup);
|
|
||||||
|
|
||||||
struct wlr_xdg_decoration_manager_v1 *xdg_decoration_manager =
|
xdg_decoration_manager = wlr_xdg_decoration_manager_v1_create(server.wl_display);
|
||||||
wlr_xdg_decoration_manager_v1_create(server.wl_display);
|
|
||||||
if (!xdg_decoration_manager) {
|
if (!xdg_decoration_manager) {
|
||||||
wlr_log(WLR_ERROR, "Unable to create the XDG decoration manager");
|
wlr_log(WLR_ERROR, "Unable to create the XDG decoration manager");
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
|
@ -437,128 +258,51 @@ main(int argc, char *argv[])
|
||||||
wl_signal_add(&xdg_decoration_manager->events.new_toplevel_decoration, &server.xdg_toplevel_decoration);
|
wl_signal_add(&xdg_decoration_manager->events.new_toplevel_decoration, &server.xdg_toplevel_decoration);
|
||||||
server.xdg_toplevel_decoration.notify = handle_xdg_toplevel_decoration;
|
server.xdg_toplevel_decoration.notify = handle_xdg_toplevel_decoration;
|
||||||
|
|
||||||
struct wlr_server_decoration_manager *server_decoration_manager =
|
server_decoration_manager = wlr_server_decoration_manager_create(server.wl_display);
|
||||||
wlr_server_decoration_manager_create(server.wl_display);
|
|
||||||
if (!server_decoration_manager) {
|
if (!server_decoration_manager) {
|
||||||
wlr_log(WLR_ERROR, "Unable to create the server decoration manager");
|
wlr_log(WLR_ERROR, "Unable to create the server decoration manager");
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
wlr_server_decoration_manager_set_default_mode(
|
wlr_server_decoration_manager_set_default_mode(server_decoration_manager,
|
||||||
server_decoration_manager, server.xdg_decoration ? WLR_SERVER_DECORATION_MANAGER_MODE_SERVER
|
server.xdg_decoration ?
|
||||||
: WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT);
|
WLR_SERVER_DECORATION_MANAGER_MODE_SERVER :
|
||||||
|
WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT);
|
||||||
if (!wlr_viewporter_create(server.wl_display)) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to create the viewporter interface");
|
|
||||||
ret = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_presentation *presentation = wlr_presentation_create(server.wl_display, server.backend, 2);
|
|
||||||
if (!presentation) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to create the presentation interface");
|
|
||||||
ret = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wlr_export_dmabuf_manager_v1_create(server.wl_display)) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to create the export DMABUF manager");
|
|
||||||
ret = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wlr_screencopy_manager_v1_create(server.wl_display)) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to create the screencopy manager");
|
|
||||||
ret = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wlr_single_pixel_buffer_manager_v1_create(server.wl_display)) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to create the single pixel buffer manager");
|
|
||||||
ret = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wlr_xdg_output_manager_v1_create(server.wl_display, server.output_layout)) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to create the output manager");
|
|
||||||
ret = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
server.output_manager_v1 = wlr_output_manager_v1_create(server.wl_display);
|
|
||||||
if (!server.output_manager_v1) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to create the output manager");
|
|
||||||
ret = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
server.output_manager_apply.notify = handle_output_manager_apply;
|
|
||||||
wl_signal_add(&server.output_manager_v1->events.apply, &server.output_manager_apply);
|
|
||||||
server.output_manager_test.notify = handle_output_manager_test;
|
|
||||||
wl_signal_add(&server.output_manager_v1->events.test, &server.output_manager_test);
|
|
||||||
|
|
||||||
if (!wlr_gamma_control_manager_v1_create(server.wl_display)) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to create the gamma control manager");
|
|
||||||
ret = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard =
|
|
||||||
wlr_virtual_keyboard_manager_v1_create(server.wl_display);
|
|
||||||
if (!virtual_keyboard) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to create the virtual keyboard manager");
|
|
||||||
ret = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
wl_signal_add(&virtual_keyboard->events.new_virtual_keyboard, &server.new_virtual_keyboard);
|
|
||||||
|
|
||||||
struct wlr_virtual_pointer_manager_v1 *virtual_pointer =
|
|
||||||
wlr_virtual_pointer_manager_v1_create(server.wl_display);
|
|
||||||
if (!virtual_pointer) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to create the virtual pointer manager");
|
|
||||||
ret = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
wl_signal_add(&virtual_pointer->events.new_virtual_pointer, &server.new_virtual_pointer);
|
|
||||||
|
|
||||||
server.relative_pointer_manager = wlr_relative_pointer_manager_v1_create(server.wl_display);
|
|
||||||
if (!server.relative_pointer_manager) {
|
|
||||||
wlr_log(WLR_ERROR, "Unable to create the relative pointer manager");
|
|
||||||
ret = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if CAGE_HAS_XWAYLAND
|
#if CAGE_HAS_XWAYLAND
|
||||||
struct wlr_xcursor_manager *xcursor_manager = NULL;
|
xwayland = wlr_xwayland_create(server.wl_display, compositor, true);
|
||||||
struct wlr_xwayland *xwayland = wlr_xwayland_create(server.wl_display, compositor, true);
|
|
||||||
if (!xwayland) {
|
if (!xwayland) {
|
||||||
wlr_log(WLR_ERROR, "Cannot create XWayland server");
|
wlr_log(WLR_ERROR, "Cannot create XWayland server");
|
||||||
|
ret = 1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
server.new_xwayland_surface.notify = handle_xwayland_surface_new;
|
||||||
|
wl_signal_add(&xwayland->events.new_surface, &server.new_xwayland_surface);
|
||||||
|
|
||||||
|
xcursor_manager = wlr_xcursor_manager_create(DEFAULT_XCURSOR, XCURSOR_SIZE);
|
||||||
|
if (!xcursor_manager) {
|
||||||
|
wlr_log(WLR_ERROR, "Cannot create XWayland XCursor manager");
|
||||||
|
ret = 1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setenv("DISPLAY", xwayland->display_name, true) < 0) {
|
||||||
|
wlr_log_errno(WLR_ERROR, "Unable to set DISPLAY for XWayland.",
|
||||||
|
"Clients may not be able to connect");
|
||||||
} else {
|
} else {
|
||||||
server.new_xwayland_surface.notify = handle_xwayland_surface_new;
|
wlr_log(WLR_DEBUG, "XWayland is running on display %s", xwayland->display_name);
|
||||||
wl_signal_add(&xwayland->events.new_surface, &server.new_xwayland_surface);
|
}
|
||||||
|
|
||||||
xcursor_manager = wlr_xcursor_manager_create(DEFAULT_XCURSOR, XCURSOR_SIZE);
|
if (wlr_xcursor_manager_load(xcursor_manager, 1)) {
|
||||||
if (!xcursor_manager) {
|
wlr_log(WLR_ERROR, "Cannot load XWayland XCursor theme");
|
||||||
wlr_log(WLR_ERROR, "Cannot create XWayland XCursor manager");
|
}
|
||||||
ret = 1;
|
struct wlr_xcursor *xcursor =
|
||||||
goto end;
|
wlr_xcursor_manager_get_xcursor(xcursor_manager, DEFAULT_XCURSOR, 1);
|
||||||
}
|
if (xcursor) {
|
||||||
|
struct wlr_xcursor_image *image = xcursor->images[0];
|
||||||
if (setenv("DISPLAY", xwayland->display_name, true) < 0) {
|
wlr_xwayland_set_cursor(xwayland, image->buffer,
|
||||||
wlr_log_errno(WLR_ERROR,
|
image->width * 4, image->width, image->height,
|
||||||
"Unable to set DISPLAY for XWayland. Clients may not be able to connect");
|
image->hotspot_x, image->hotspot_y);
|
||||||
} else {
|
|
||||||
wlr_log(WLR_DEBUG, "XWayland is running on display %s", xwayland->display_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!wlr_xcursor_manager_load(xcursor_manager, 1)) {
|
|
||||||
wlr_log(WLR_ERROR, "Cannot load XWayland XCursor theme");
|
|
||||||
}
|
|
||||||
struct wlr_xcursor *xcursor = wlr_xcursor_manager_get_xcursor(xcursor_manager, DEFAULT_XCURSOR, 1);
|
|
||||||
if (xcursor) {
|
|
||||||
struct wlr_xcursor_image *image = xcursor->images[0];
|
|
||||||
wlr_xwayland_set_cursor(xwayland, image->buffer, image->width * 4, image->width, image->height,
|
|
||||||
image->hotspot_x, image->hotspot_y);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -566,7 +310,7 @@ main(int argc, char *argv[])
|
||||||
if (!socket) {
|
if (!socket) {
|
||||||
wlr_log_errno(WLR_ERROR, "Unable to open Wayland socket");
|
wlr_log_errno(WLR_ERROR, "Unable to open Wayland socket");
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wlr_backend_start(server.backend)) {
|
if (!wlr_backend_start(server.backend)) {
|
||||||
|
|
@ -576,62 +320,48 @@ main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setenv("WAYLAND_DISPLAY", socket, true) < 0) {
|
if (setenv("WAYLAND_DISPLAY", socket, true) < 0) {
|
||||||
wlr_log_errno(WLR_ERROR, "Unable to set WAYLAND_DISPLAY. Clients may not be able to connect");
|
wlr_log_errno(WLR_ERROR, "Unable to set WAYLAND_DISPLAY.",
|
||||||
|
"Clients may not be able to connect");
|
||||||
} else {
|
} else {
|
||||||
wlr_log(WLR_DEBUG, "Cage " CAGE_VERSION " is running on Wayland display %s", socket);
|
wlr_log(WLR_DEBUG, "Cage is running on Wayland display %s", socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CAGE_HAS_XWAYLAND
|
#if CAGE_HAS_XWAYLAND
|
||||||
if (xwayland) {
|
wlr_xwayland_set_seat(xwayland, server.seat->seat);
|
||||||
wlr_xwayland_set_seat(xwayland, server.seat->seat);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (optind < argc && !spawn_primary_client(&server, argv + optind, &pid, &sigchld_source)) {
|
pid_t pid;
|
||||||
|
if (!spawn_primary_client(argv + optind, &pid)) {
|
||||||
ret = 1;
|
ret = 1;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
seat_center_cursor(server.seat);
|
|
||||||
wl_display_run(server.wl_display);
|
wl_display_run(server.wl_display);
|
||||||
|
|
||||||
#if CAGE_HAS_XWAYLAND
|
#if CAGE_HAS_XWAYLAND
|
||||||
if (xwayland) {
|
|
||||||
wl_list_remove(&server.new_xwayland_surface.link);
|
|
||||||
}
|
|
||||||
wlr_xwayland_destroy(xwayland);
|
wlr_xwayland_destroy(xwayland);
|
||||||
wlr_xcursor_manager_destroy(xcursor_manager);
|
wlr_xcursor_manager_destroy(xcursor_manager);
|
||||||
#endif
|
#endif
|
||||||
wl_display_destroy_clients(server.wl_display);
|
wl_display_destroy_clients(server.wl_display);
|
||||||
|
|
||||||
wl_list_remove(&server.new_virtual_pointer.link);
|
waitpid(pid, NULL, 0);
|
||||||
wl_list_remove(&server.new_virtual_keyboard.link);
|
|
||||||
wl_list_remove(&server.output_manager_apply.link);
|
|
||||||
wl_list_remove(&server.output_manager_test.link);
|
|
||||||
wl_list_remove(&server.xdg_toplevel_decoration.link);
|
|
||||||
wl_list_remove(&server.new_xdg_toplevel.link);
|
|
||||||
wl_list_remove(&server.new_xdg_popup.link);
|
|
||||||
wl_list_remove(&server.new_idle_inhibitor_v1.link);
|
|
||||||
wl_list_remove(&server.new_output.link);
|
|
||||||
wl_list_remove(&server.output_layout_change.link);
|
|
||||||
|
|
||||||
end:
|
end:
|
||||||
if (pid != 0)
|
|
||||||
app_ret = cleanup_primary_client(pid);
|
|
||||||
if (!ret && server.return_app_code)
|
|
||||||
ret = app_ret;
|
|
||||||
|
|
||||||
wl_event_source_remove(sigint_source);
|
wl_event_source_remove(sigint_source);
|
||||||
wl_event_source_remove(sigterm_source);
|
wl_event_source_remove(sigterm_source);
|
||||||
if (sigchld_source) {
|
|
||||||
wl_event_source_remove(sigchld_source);
|
|
||||||
}
|
|
||||||
seat_destroy(server.seat);
|
seat_destroy(server.seat);
|
||||||
|
wlr_server_decoration_manager_destroy(server_decoration_manager);
|
||||||
|
wlr_xdg_decoration_manager_v1_destroy(xdg_decoration_manager);
|
||||||
|
wlr_xdg_shell_destroy(xdg_shell);
|
||||||
|
wlr_idle_inhibit_v1_destroy(server.idle_inhibit_v1);
|
||||||
|
if (server.idle) {
|
||||||
|
wlr_idle_destroy(server.idle);
|
||||||
|
}
|
||||||
|
wlr_data_device_manager_destroy(data_device_mgr);
|
||||||
|
wlr_compositor_destroy(compositor);
|
||||||
|
wlr_output_layout_destroy(server.output_layout);
|
||||||
/* This function is not null-safe, but we only ever get here
|
/* This function is not null-safe, but we only ever get here
|
||||||
with a proper wl_display. */
|
with a proper wl_display. */
|
||||||
wl_display_destroy(server.wl_display);
|
wl_display_destroy(server.wl_display);
|
||||||
wlr_scene_node_destroy(&server.scene->tree.node);
|
|
||||||
wlr_allocator_destroy(server.allocator);
|
|
||||||
wlr_renderer_destroy(server.renderer);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,4 @@
|
||||||
|
|
||||||
#mesondefine CAGE_HAS_XWAYLAND
|
#mesondefine CAGE_HAS_XWAYLAND
|
||||||
|
|
||||||
#mesondefine CAGE_VERSION
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
if [ "$#" -ne 1 ]; then
|
|
||||||
echo "usage: $0 <new-version>" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
new_version="$1"
|
|
||||||
|
|
||||||
if [ "$new_version" != "${new_version#v}" ]; then
|
|
||||||
echo "Error: The new version shouldn't be prefixed with a \"v\"." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
set -x
|
|
||||||
|
|
||||||
sed -i meson.build -e "s/^ version: '.*'/ version: '$new_version'/"
|
|
||||||
|
|
||||||
echo -n "Minimum wlroots version? "
|
|
||||||
read -r wlr_version_min
|
|
||||||
|
|
||||||
sed -i meson.build -e "s/'wlroots', version: '.*'/'wlroots', version: '>= $wlr_version_min'/"
|
|
||||||
|
|
||||||
git add meson.build
|
|
||||||
git commit -m "Update version to $new_version"
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
if [ "$#" -ne 1 ]; then
|
|
||||||
echo "usage: $0 <new-version>" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
new_version="$1"
|
|
||||||
|
|
||||||
if [ "$new_version" != "${new_version#v}" ]; then
|
|
||||||
echo "Error: The new version shouldn't be prefixed with a \"v\"." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
set -x
|
|
||||||
|
|
||||||
./increment_version "$new_version"
|
|
||||||
./tag-release "$new_version"
|
|
||||||
./sign-release
|
|
||||||
|
|
||||||
git push --tags
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -x
|
|
||||||
|
|
||||||
project="$(basename "$(pwd)")"
|
|
||||||
last=$(git describe --tags --abbrev=0)
|
|
||||||
|
|
||||||
prefix="$project-${last#v}"
|
|
||||||
archive="$prefix.tar.gz"
|
|
||||||
|
|
||||||
git archive --prefix="$prefix/" -o "$archive" "$last"
|
|
||||||
gpg --output "$archive".sig --detach-sig "$archive"
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -x
|
|
||||||
|
|
||||||
if [ "$#" -ne 1 ]; then
|
|
||||||
echo "usage: $0 <new-version>" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
last=$(git describe --tags --abbrev=0)
|
|
||||||
echo "Last release was $last"
|
|
||||||
|
|
||||||
next="v$1"
|
|
||||||
|
|
||||||
shortlog="$(git shortlog --no-merges "$last"..)"
|
|
||||||
|
|
||||||
printf "Shortlog: \n\n%s\n\nRelease $next? [y/N] " "$shortlog"
|
|
||||||
read -r answer
|
|
||||||
|
|
||||||
if [ "$answer" != "y" ]; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
project="$(basename "$(pwd)")"
|
|
||||||
|
|
||||||
(echo "$project $next"; echo ""; echo "$shortlog") | git tag "$next" -ase -F -
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
/*
|
/*
|
||||||
* Cage: A Wayland kiosk.
|
* Cage: A Wayland kiosk.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2018-2019 Jente Hidskes
|
* Copyright (C) 2018-2019 Jente Hidskes
|
||||||
*
|
*
|
||||||
* See the LICENSE file accompanying this file.
|
* See the LICENSE file accompanying this file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server.h>
|
||||||
|
#include <wlr/types/wlr_idle.h>
|
||||||
#include <wlr/types/wlr_idle_inhibit_v1.h>
|
#include <wlr/types/wlr_idle_inhibit_v1.h>
|
||||||
#include <wlr/types/wlr_idle_notify_v1.h>
|
|
||||||
|
|
||||||
#include "idle_inhibit_v1.h"
|
#include "idle_inhibit_v1.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
|
@ -32,7 +32,7 @@ idle_inhibit_v1_check_active(struct cg_server *server)
|
||||||
Hence, we simply check for any inhibitors and inhibit
|
Hence, we simply check for any inhibitors and inhibit
|
||||||
accordingly. */
|
accordingly. */
|
||||||
bool inhibited = !wl_list_empty(&server->inhibitors);
|
bool inhibited = !wl_list_empty(&server->inhibitors);
|
||||||
wlr_idle_notifier_v1_set_inhibited(server->idle, inhibited);
|
wlr_idle_set_enabled(server->idle, NULL, !inhibited);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef CG_IDLE_INHIBIT_H
|
#ifndef CG_IDLE_INHIBIT_H
|
||||||
#define CG_IDLE_INHIBIT_H
|
#define CG_IDLE_INHIBIT_H
|
||||||
|
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server.h>
|
||||||
|
|
||||||
void handle_idle_inhibitor_v1_new(struct wl_listener *listener, void *data);
|
void handle_idle_inhibitor_v1_new(struct wl_listener *listener, void *data);
|
||||||
|
|
||||||
|
|
|
||||||
74
meson.build
74
meson.build
|
|
@ -1,10 +1,9 @@
|
||||||
project('cage', 'c',
|
project('cage', 'c',
|
||||||
version: '0.2.1',
|
version: '0.0.1',
|
||||||
license: 'MIT',
|
license: 'MIT',
|
||||||
meson_version: '>=0.58.1',
|
|
||||||
default_options: [
|
default_options: [
|
||||||
'c_std=c11',
|
'c_std=c11',
|
||||||
'warning_level=2',
|
'warning_level=3',
|
||||||
'werror=true',
|
'werror=true',
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
@ -12,6 +11,7 @@ project('cage', 'c',
|
||||||
add_project_arguments(
|
add_project_arguments(
|
||||||
[
|
[
|
||||||
'-DWLR_USE_UNSTABLE',
|
'-DWLR_USE_UNSTABLE',
|
||||||
|
'-Wall',
|
||||||
'-Wundef',
|
'-Wundef',
|
||||||
'-Wno-unused-parameter',
|
'-Wno-unused-parameter',
|
||||||
],
|
],
|
||||||
|
|
@ -35,13 +35,14 @@ if is_freebsd
|
||||||
)
|
)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
wlroots = dependency('wlroots-0.19', fallback: ['wlroots', 'wlroots'])
|
wlroots = dependency('wlroots', version: '>= 0.5.0')
|
||||||
wayland_protos = dependency('wayland-protocols', version: '>=1.14')
|
wayland_protos = dependency('wayland-protocols', version: '>=1.14')
|
||||||
wayland_server = dependency('wayland-server')
|
wayland_server = dependency('wayland-server')
|
||||||
|
pixman = dependency('pixman-1')
|
||||||
xkbcommon = dependency('xkbcommon')
|
xkbcommon = dependency('xkbcommon')
|
||||||
math = cc.find_library('m')
|
math = cc.find_library('m')
|
||||||
|
|
||||||
wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
|
wl_protocol_dir = wayland_protos.get_pkgconfig_variable('pkgdatadir')
|
||||||
wayland_scanner = find_program('wayland-scanner')
|
wayland_scanner = find_program('wayland-scanner')
|
||||||
wayland_scanner_server = generator(
|
wayland_scanner_server = generator(
|
||||||
wayland_scanner,
|
wayland_scanner,
|
||||||
|
|
@ -64,51 +65,19 @@ server_protos = declare_dependency(
|
||||||
sources: server_protos_headers,
|
sources: server_protos_headers,
|
||||||
)
|
)
|
||||||
|
|
||||||
have_xwayland = wlroots.get_variable(pkgconfig: 'have_xwayland', internal: 'have_xwayland') == 'true'
|
if get_option('xwayland')
|
||||||
|
wlroots_has_xwayland = cc.get_define('WLR_HAS_XWAYLAND', prefix: '#include <wlr/config.h>', dependencies: wlroots) == '1'
|
||||||
version = '@0@'.format(meson.project_version())
|
if not wlroots_has_xwayland
|
||||||
git = find_program('git', native: true, required: false)
|
error('Cannot build Cage with XWayland support: wlroots has been built without it')
|
||||||
if git.found()
|
else
|
||||||
git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'], check: false)
|
have_xwayland = true
|
||||||
git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'], check: false)
|
|
||||||
if git_commit.returncode() == 0 and git_branch.returncode() == 0
|
|
||||||
version = '@0@-@1@ (branch \'@2@\')'.format(
|
|
||||||
meson.project_version(),
|
|
||||||
git_commit.stdout().strip(),
|
|
||||||
git_branch.stdout().strip(),
|
|
||||||
)
|
|
||||||
endif
|
endif
|
||||||
|
else
|
||||||
|
have_xwayland = false
|
||||||
endif
|
endif
|
||||||
|
|
||||||
conf_data = configuration_data()
|
conf_data = configuration_data()
|
||||||
conf_data.set10('CAGE_HAS_XWAYLAND', have_xwayland)
|
conf_data.set10('CAGE_HAS_XWAYLAND', have_xwayland)
|
||||||
conf_data.set_quoted('CAGE_VERSION', version)
|
|
||||||
|
|
||||||
scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages'))
|
|
||||||
if scdoc.found()
|
|
||||||
scdoc_prog = find_program(scdoc.get_variable('scdoc'), native: true)
|
|
||||||
sh = find_program('sh', native: true)
|
|
||||||
mandir = get_option('mandir')
|
|
||||||
man_files = [
|
|
||||||
'cage.1.scd'
|
|
||||||
]
|
|
||||||
foreach filename : man_files
|
|
||||||
topic = filename.split('.')[-3].split('/')[-1]
|
|
||||||
section = filename.split('.')[-2]
|
|
||||||
output = '@0@.@1@'.format(topic, section)
|
|
||||||
|
|
||||||
custom_target(
|
|
||||||
output,
|
|
||||||
input: filename,
|
|
||||||
output: output,
|
|
||||||
command: [
|
|
||||||
sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.full_path(), output)
|
|
||||||
],
|
|
||||||
install: true,
|
|
||||||
install_dir: '@0@/man@1@'.format(mandir, section)
|
|
||||||
)
|
|
||||||
endforeach
|
|
||||||
endif
|
|
||||||
|
|
||||||
cage_sources = [
|
cage_sources = [
|
||||||
'cage.c',
|
'cage.c',
|
||||||
|
|
@ -121,8 +90,8 @@ cage_sources = [
|
||||||
|
|
||||||
cage_headers = [
|
cage_headers = [
|
||||||
configure_file(input: 'config.h.in',
|
configure_file(input: 'config.h.in',
|
||||||
output: 'config.h',
|
output: 'config.h',
|
||||||
configuration: conf_data),
|
configuration: conf_data),
|
||||||
'idle_inhibit_v1.h',
|
'idle_inhibit_v1.h',
|
||||||
'output.h',
|
'output.h',
|
||||||
'seat.h',
|
'seat.h',
|
||||||
|
|
@ -144,16 +113,17 @@ executable(
|
||||||
wayland_server,
|
wayland_server,
|
||||||
wlroots,
|
wlroots,
|
||||||
xkbcommon,
|
xkbcommon,
|
||||||
|
pixman,
|
||||||
math,
|
math,
|
||||||
],
|
],
|
||||||
install: true,
|
install: true,
|
||||||
)
|
)
|
||||||
|
|
||||||
summary = [
|
summary = [
|
||||||
'',
|
'',
|
||||||
'Cage @0@'.format(version),
|
'Cage @0@'.format(meson.project_version()),
|
||||||
'',
|
'',
|
||||||
' xwayland: @0@'.format(have_xwayland),
|
' xwayland: @0@'.format(conf_data.get('CAGE_HAS_XWAYLAND', false)),
|
||||||
''
|
''
|
||||||
]
|
]
|
||||||
message('\n'.join(summary))
|
message('\n'.join(summary))
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
|
option('xwayland', type: 'boolean', value: 'false', description: 'Enable support for X11 applications')
|
||||||
|
|
|
||||||
643
output.c
643
output.c
|
|
@ -1,8 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Cage: A Wayland kiosk.
|
* Cage: A Wayland kiosk.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2018-2021 Jente Hidskes
|
* Copyright (C) 2018-2019 Jente Hidskes
|
||||||
* Copyright (C) 2019 The Sway authors
|
|
||||||
*
|
*
|
||||||
* See the LICENSE file accompanying this file.
|
* See the LICENSE file accompanying this file.
|
||||||
*/
|
*/
|
||||||
|
|
@ -10,224 +9,329 @@
|
||||||
#define _POSIX_C_SOURCE 200112L
|
#define _POSIX_C_SOURCE 200112L
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include <wlr/config.h>
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server.h>
|
||||||
#include <wlr/backend.h>
|
#include <wlr/backend.h>
|
||||||
#include <wlr/backend/wayland.h>
|
#include <wlr/backend/wayland.h>
|
||||||
#include <wlr/config.h>
|
|
||||||
#if WLR_HAS_X11_BACKEND
|
#if WLR_HAS_X11_BACKEND
|
||||||
#include <wlr/backend/x11.h>
|
#include <wlr/backend/x11.h>
|
||||||
#endif
|
#endif
|
||||||
#include <wlr/render/swapchain.h>
|
|
||||||
#include <wlr/render/wlr_renderer.h>
|
#include <wlr/render/wlr_renderer.h>
|
||||||
#include <wlr/types/wlr_compositor.h>
|
|
||||||
#include <wlr/types/wlr_data_device.h>
|
#include <wlr/types/wlr_data_device.h>
|
||||||
|
#include <wlr/types/wlr_matrix.h>
|
||||||
#include <wlr/types/wlr_output.h>
|
#include <wlr/types/wlr_output.h>
|
||||||
|
#include <wlr/types/wlr_output_damage.h>
|
||||||
#include <wlr/types/wlr_output_layout.h>
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
#include <wlr/types/wlr_output_management_v1.h>
|
#include <wlr/types/wlr_surface.h>
|
||||||
#include <wlr/types/wlr_output_swapchain_manager.h>
|
|
||||||
#include <wlr/types/wlr_scene.h>
|
|
||||||
#include <wlr/types/wlr_xdg_shell.h>
|
#include <wlr/types/wlr_xdg_shell.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
#include <wlr/util/region.h>
|
#include <wlr/util/region.h>
|
||||||
|
|
||||||
#include "output.h"
|
#include "output.h"
|
||||||
#include "seat.h"
|
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "view.h"
|
#include "view.h"
|
||||||
#if CAGE_HAS_XWAYLAND
|
|
||||||
#include "xwayland.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define OUTPUT_CONFIG_UPDATED \
|
|
||||||
(WLR_OUTPUT_STATE_ENABLED | WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_SCALE | WLR_OUTPUT_STATE_TRANSFORM | \
|
|
||||||
WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED)
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
update_output_manager_config(struct cg_server *server)
|
scissor_output(struct wlr_output *output, pixman_box32_t *rect)
|
||||||
{
|
{
|
||||||
struct wlr_output_configuration_v1 *config = wlr_output_configuration_v1_create();
|
struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend);
|
||||||
|
|
||||||
|
struct wlr_box box = {
|
||||||
|
.x = rect->x1,
|
||||||
|
.y = rect->y1,
|
||||||
|
.width = rect->x2 - rect->x1,
|
||||||
|
.height = rect->y2 - rect->y1,
|
||||||
|
};
|
||||||
|
|
||||||
|
int output_width, output_height;
|
||||||
|
wlr_output_transformed_resolution(output, &output_width, &output_height);
|
||||||
|
enum wl_output_transform transform = wlr_output_transform_invert(output->transform);
|
||||||
|
wlr_box_transform(&box, &box, transform, output_width, output_height);
|
||||||
|
|
||||||
|
wlr_renderer_scissor(renderer, &box);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
send_frame_done(struct wlr_surface *surface, int _unused, int _not_used, void *data)
|
||||||
|
{
|
||||||
|
struct timespec *now = data;
|
||||||
|
wlr_surface_send_frame_done(surface, now);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Used to move all of the data necessary to damage a surface. */
|
||||||
|
struct damage_data {
|
||||||
struct cg_output *output;
|
struct cg_output *output;
|
||||||
wl_list_for_each (output, &server->outputs, link) {
|
double x;
|
||||||
struct wlr_output *wlr_output = output->wlr_output;
|
double y;
|
||||||
struct wlr_output_configuration_head_v1 *config_head =
|
bool whole;
|
||||||
wlr_output_configuration_head_v1_create(config, wlr_output);
|
};
|
||||||
struct wlr_box output_box;
|
|
||||||
|
|
||||||
wlr_output_layout_get_box(server->output_layout, wlr_output, &output_box);
|
static void
|
||||||
if (!wlr_box_empty(&output_box)) {
|
damage_surface(struct wlr_surface *surface, int sx, int sy, void *data)
|
||||||
config_head->state.x = output_box.x;
|
{
|
||||||
config_head->state.y = output_box.y;
|
struct damage_data *ddata = data;
|
||||||
|
struct cg_output *output = ddata->output;
|
||||||
|
struct wlr_output *wlr_output = output->wlr_output;
|
||||||
|
|
||||||
|
if (!wlr_surface_has_buffer(surface)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double x = ddata->x + sx, y = ddata->y + sy;
|
||||||
|
wlr_output_layout_output_coords(output->server->output_layout, wlr_output, &x, &y);
|
||||||
|
|
||||||
|
struct wlr_box box = {
|
||||||
|
.x = x * wlr_output->scale,
|
||||||
|
.y = y * wlr_output->scale,
|
||||||
|
.width = surface->current.width * wlr_output->scale,
|
||||||
|
.height = surface->current.height * wlr_output->scale,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ddata->whole) {
|
||||||
|
wlr_output_damage_add_box(output->damage, &box);
|
||||||
|
} else if (pixman_region32_not_empty(&surface->buffer_damage)) {
|
||||||
|
pixman_region32_t damage;
|
||||||
|
pixman_region32_init(&damage);
|
||||||
|
wlr_surface_get_effective_damage(surface, &damage);
|
||||||
|
|
||||||
|
wlr_region_scale(&damage, &damage, wlr_output->scale);
|
||||||
|
if (ceil(wlr_output->scale) > surface->current.scale) {
|
||||||
|
/* When scaling up a surface it'll become
|
||||||
|
blurry, so we need to expand the damage
|
||||||
|
region. */
|
||||||
|
wlr_region_expand(&damage, &damage,
|
||||||
|
ceil(wlr_output->scale) - surface->current.scale);
|
||||||
}
|
}
|
||||||
|
pixman_region32_translate(&damage, box.x, box.y);
|
||||||
|
wlr_output_damage_add(output->damage, &damage);
|
||||||
|
pixman_region32_fini(&damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_output_manager_v1_set_configuration(server->output_manager_v1, config);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
/* Used to move all of the data necessary to render a surface from the
|
||||||
output_layout_add_auto(struct cg_output *output)
|
* top-level frame handler to the per-surface render function. */
|
||||||
{
|
struct render_data {
|
||||||
assert(output->scene_output != NULL);
|
struct wlr_output_layout *output_layout;
|
||||||
struct wlr_output_layout_output *layout_output =
|
struct wlr_output *output;
|
||||||
wlr_output_layout_add_auto(output->server->output_layout, output->wlr_output);
|
struct timespec *when;
|
||||||
wlr_scene_output_layout_add_output(output->server->scene_output_layout, layout_output, output->scene_output);
|
pixman_region32_t *damage;
|
||||||
}
|
double x, y;
|
||||||
|
};
|
||||||
static inline void
|
|
||||||
output_layout_add(struct cg_output *output, int32_t x, int32_t y)
|
|
||||||
{
|
|
||||||
assert(output->scene_output != NULL);
|
|
||||||
bool exists = wlr_output_layout_get(output->server->output_layout, output->wlr_output);
|
|
||||||
struct wlr_output_layout_output *layout_output =
|
|
||||||
wlr_output_layout_add(output->server->output_layout, output->wlr_output, x, y);
|
|
||||||
if (exists) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
wlr_scene_output_layout_add_output(output->server->scene_output_layout, layout_output, output->scene_output);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
output_layout_remove(struct cg_output *output)
|
|
||||||
{
|
|
||||||
wlr_output_layout_remove(output->server->output_layout, output->wlr_output);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
output_enable(struct cg_output *output)
|
render_surface(struct wlr_surface *surface, int sx, int sy, void *data)
|
||||||
{
|
{
|
||||||
struct wlr_output *wlr_output = output->wlr_output;
|
struct render_data *rdata = data;
|
||||||
|
struct wlr_output *output = rdata->output;
|
||||||
|
|
||||||
/* Outputs get enabled by the backend before firing the new_output event,
|
if (!wlr_surface_has_buffer(surface)) {
|
||||||
* so we can't do a check for already enabled outputs here unless we
|
|
||||||
* duplicate the enabled property in cg_output. */
|
|
||||||
wlr_log(WLR_DEBUG, "Enabling output %s", wlr_output->name);
|
|
||||||
|
|
||||||
struct wlr_output_state state = {0};
|
|
||||||
wlr_output_state_set_enabled(&state, true);
|
|
||||||
|
|
||||||
if (wlr_output_commit_state(wlr_output, &state)) {
|
|
||||||
output_layout_add_auto(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
update_output_manager_config(output->server);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
output_disable(struct cg_output *output)
|
|
||||||
{
|
|
||||||
struct wlr_output *wlr_output = output->wlr_output;
|
|
||||||
if (!wlr_output->enabled) {
|
|
||||||
wlr_log(WLR_DEBUG, "Not disabling already disabled output %s", wlr_output->name);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_log(WLR_DEBUG, "Disabling output %s", wlr_output->name);
|
struct wlr_texture *texture = wlr_surface_get_texture(surface);
|
||||||
struct wlr_output_state state = {0};
|
if (!texture) {
|
||||||
wlr_output_state_set_enabled(&state, false);
|
wlr_log(WLR_DEBUG, "Cannot obtain surface texture");
|
||||||
wlr_output_commit_state(wlr_output, &state);
|
|
||||||
output_layout_remove(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
handle_output_frame(struct wl_listener *listener, void *data)
|
|
||||||
{
|
|
||||||
struct cg_output *output = wl_container_of(listener, output, frame);
|
|
||||||
|
|
||||||
if (!output->wlr_output->enabled || !output->scene_output) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_scene_output_commit(output->scene_output, NULL);
|
double x = rdata->x + sx, y = rdata->y + sy;
|
||||||
|
wlr_output_layout_output_coords(rdata->output_layout, output, &x, &y);
|
||||||
|
|
||||||
struct timespec now = {0};
|
struct wlr_box box = {
|
||||||
|
.x = x * output->scale,
|
||||||
|
.y = y * output->scale,
|
||||||
|
.width = surface->current.width * output->scale,
|
||||||
|
.height = surface->current.height * output->scale,
|
||||||
|
};
|
||||||
|
|
||||||
|
pixman_region32_t damage;
|
||||||
|
pixman_region32_init(&damage);
|
||||||
|
pixman_region32_union_rect(&damage, &damage, box.x, box.y, box.width, box.height);
|
||||||
|
pixman_region32_intersect(&damage, &damage, rdata->damage);
|
||||||
|
if (!pixman_region32_not_empty(&damage)) {
|
||||||
|
goto damage_finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
float matrix[9];
|
||||||
|
enum wl_output_transform transform = wlr_output_transform_invert(surface->current.transform);
|
||||||
|
wlr_matrix_project_box(matrix, &box, transform, 0, output->transform_matrix);
|
||||||
|
|
||||||
|
int nrects;
|
||||||
|
pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
|
||||||
|
for (int i = 0; i < nrects; i++) {
|
||||||
|
scissor_output(output, &rects[i]);
|
||||||
|
wlr_render_texture_with_matrix(surface->renderer, texture, matrix, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
damage_finish:
|
||||||
|
pixman_region32_fini(&damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
drag_icons_for_each_surface(struct cg_server *server, wlr_surface_iterator_func_t iterator,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct render_data *rdata = data;
|
||||||
|
|
||||||
|
struct cg_drag_icon *drag_icon;
|
||||||
|
wl_list_for_each(drag_icon, &server->seat->drag_icons, link) {
|
||||||
|
if (!drag_icon->wlr_drag_icon->mapped) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rdata->x = drag_icon->x;
|
||||||
|
rdata->y = drag_icon->y;
|
||||||
|
wlr_surface_for_each_surface(drag_icon->wlr_drag_icon->surface,
|
||||||
|
iterator,
|
||||||
|
data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_output_damage_frame(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct cg_output *output = wl_container_of(listener, output, damage_frame);
|
||||||
|
struct wlr_renderer *renderer = wlr_backend_get_renderer(output->server->backend);
|
||||||
|
|
||||||
|
struct timespec now;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
wlr_scene_output_send_frame_done(output->scene_output, &now);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
bool needs_swap;
|
||||||
handle_output_commit(struct wl_listener *listener, void *data)
|
pixman_region32_t damage;
|
||||||
{
|
pixman_region32_init(&damage);
|
||||||
struct cg_output *output = wl_container_of(listener, output, commit);
|
if (!wlr_output_damage_make_current(output->damage, &needs_swap, &damage)) {
|
||||||
struct wlr_output_event_commit *event = data;
|
wlr_log(WLR_ERROR, "Cannot make damage output current");
|
||||||
|
goto damage_finish;
|
||||||
/* Notes:
|
|
||||||
* - output layout change will also be called if needed to position the views
|
|
||||||
* - always update output manager configuration even if the output is now disabled */
|
|
||||||
|
|
||||||
if (event->state->committed & OUTPUT_CONFIG_UPDATED) {
|
|
||||||
update_output_manager_config(output->server);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
if (!needs_swap) {
|
||||||
handle_output_request_state(struct wl_listener *listener, void *data)
|
wlr_log(WLR_DEBUG, "Output doesn't need swap and isn't damaged");
|
||||||
{
|
goto damage_finish;
|
||||||
struct cg_output *output = wl_container_of(listener, output, request_state);
|
|
||||||
struct wlr_output_event_request_state *event = data;
|
|
||||||
|
|
||||||
if (wlr_output_commit_state(output->wlr_output, event->state)) {
|
|
||||||
update_output_manager_config(output->server);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void
|
wlr_renderer_begin(renderer, output->wlr_output->width, output->wlr_output->height);
|
||||||
handle_output_layout_change(struct wl_listener *listener, void *data)
|
|
||||||
{
|
|
||||||
struct cg_server *server = wl_container_of(listener, server, output_layout_change);
|
|
||||||
|
|
||||||
view_position_all(server);
|
if (!pixman_region32_not_empty(&damage)) {
|
||||||
update_output_manager_config(server);
|
wlr_log(WLR_DEBUG, "Output isn't damaged but needs a buffer swap");
|
||||||
}
|
goto renderer_end;
|
||||||
|
|
||||||
static bool
|
|
||||||
is_nested_output(struct cg_output *output)
|
|
||||||
{
|
|
||||||
if (wlr_output_is_wl(output->wlr_output)) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
#if WLR_HAS_X11_BACKEND
|
|
||||||
if (wlr_output_is_x11(output->wlr_output)) {
|
#ifdef DEBUG
|
||||||
return true;
|
if (output->server->debug_damage_tracking) {
|
||||||
|
wlr_renderer_clear(renderer, (float[]){1, 0, 0, 1});
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return false;
|
|
||||||
|
float color[4] = {0.3, 0.3, 0.3, 1.0};
|
||||||
|
int nrects;
|
||||||
|
pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
|
||||||
|
for (int i = 0; i < nrects; i++) {
|
||||||
|
scissor_output(output->wlr_output, &rects[i]);
|
||||||
|
wlr_renderer_clear(renderer, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct render_data rdata = {
|
||||||
|
.output_layout = output->server->output_layout,
|
||||||
|
.output = output->wlr_output,
|
||||||
|
.when = &now,
|
||||||
|
.damage = &damage,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cg_view *view;
|
||||||
|
wl_list_for_each_reverse(view, &output->server->views, link) {
|
||||||
|
rdata.x = view->x;
|
||||||
|
rdata.y = view->y;
|
||||||
|
view_for_each_surface(view, render_surface, &rdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
drag_icons_for_each_surface(output->server, render_surface, &rdata);
|
||||||
|
|
||||||
|
renderer_end:
|
||||||
|
/* Draw software cursor in case hardware cursors aren't
|
||||||
|
available. This is a no-op when they are. */
|
||||||
|
wlr_output_render_software_cursors(output->wlr_output, &damage);
|
||||||
|
wlr_renderer_scissor(renderer, NULL);
|
||||||
|
wlr_renderer_end(renderer);
|
||||||
|
|
||||||
|
int output_width, output_height;
|
||||||
|
wlr_output_transformed_resolution(output->wlr_output, &output_width, &output_height);
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (output->server->debug_damage_tracking) {
|
||||||
|
pixman_region32_union_rect(&damage, &damage, 0, 0, output_width, output_height);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum wl_output_transform transform = wlr_output_transform_invert(output->wlr_output->transform);
|
||||||
|
wlr_region_transform(&damage, &damage, transform, output_width, output_height);
|
||||||
|
|
||||||
|
if (!wlr_output_damage_swap_buffers(output->damage, &now, &damage)) {
|
||||||
|
wlr_log(WLR_ERROR, "Could not swap buffers");
|
||||||
|
}
|
||||||
|
|
||||||
|
damage_finish:
|
||||||
|
pixman_region32_fini(&damage);
|
||||||
|
|
||||||
|
wl_list_for_each_reverse(view, &output->server->views, link) {
|
||||||
|
view_for_each_surface(view, send_frame_done, &now);
|
||||||
|
}
|
||||||
|
drag_icons_for_each_surface(output->server, send_frame_done, &now);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_output_transform(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct cg_output *output = wl_container_of(listener, output, transform);
|
||||||
|
|
||||||
|
struct cg_view *view;
|
||||||
|
wl_list_for_each(view, &output->server->views, link) {
|
||||||
|
view_position(view);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_output_mode(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct cg_output *output = wl_container_of(listener, output, mode);
|
||||||
|
|
||||||
|
struct cg_view *view;
|
||||||
|
wl_list_for_each(view, &output->server->views, link) {
|
||||||
|
view_position(view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
output_destroy(struct cg_output *output)
|
output_destroy(struct cg_output *output)
|
||||||
{
|
{
|
||||||
struct cg_server *server = output->server;
|
struct cg_server *server = output->server;
|
||||||
bool was_nested_output = is_nested_output(output);
|
|
||||||
|
|
||||||
output->wlr_output->data = NULL;
|
|
||||||
|
|
||||||
wl_list_remove(&output->destroy.link);
|
wl_list_remove(&output->destroy.link);
|
||||||
wl_list_remove(&output->commit.link);
|
wl_list_remove(&output->mode.link);
|
||||||
wl_list_remove(&output->request_state.link);
|
wl_list_remove(&output->transform.link);
|
||||||
wl_list_remove(&output->frame.link);
|
wl_list_remove(&output->damage_frame.link);
|
||||||
wl_list_remove(&output->link);
|
wl_list_remove(&output->damage_destroy.link);
|
||||||
|
|
||||||
output_layout_remove(output);
|
|
||||||
|
|
||||||
free(output);
|
free(output);
|
||||||
|
server->output = NULL;
|
||||||
|
|
||||||
if (wl_list_empty(&server->outputs) && was_nested_output) {
|
/* Since there is no use in continuing without our (single)
|
||||||
server_terminate(server);
|
* output, terminate. */
|
||||||
} else if (server->output_mode == CAGE_MULTI_OUTPUT_MODE_LAST && !wl_list_empty(&server->outputs)) {
|
wl_display_terminate(server->wl_display);
|
||||||
struct cg_output *prev = wl_container_of(server->outputs.next, prev, link);
|
}
|
||||||
output_enable(prev);
|
|
||||||
view_position_all(server);
|
static void
|
||||||
}
|
handle_output_damage_destroy(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct cg_output *output = wl_container_of(listener, output, damage_destroy);
|
||||||
|
output_destroy(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_output_destroy(struct wl_listener *listener, void *data)
|
handle_output_destroy(struct wl_listener *listener, void *data)
|
||||||
{
|
{
|
||||||
struct cg_output *output = wl_container_of(listener, output, destroy);
|
struct cg_output *output = wl_container_of(listener, output, destroy);
|
||||||
|
wlr_output_damage_destroy(output->damage);
|
||||||
output_destroy(output);
|
output_destroy(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -237,77 +341,85 @@ handle_new_output(struct wl_listener *listener, void *data)
|
||||||
struct cg_server *server = wl_container_of(listener, server, new_output);
|
struct cg_server *server = wl_container_of(listener, server, new_output);
|
||||||
struct wlr_output *wlr_output = data;
|
struct wlr_output *wlr_output = data;
|
||||||
|
|
||||||
if (!wlr_output_init_render(wlr_output, server->allocator, server->renderer)) {
|
/* On outputs that have modes, we need to set one before we
|
||||||
wlr_log(WLR_ERROR, "Failed to initialize output rendering");
|
* can use it. Each monitor supports only a specific set of
|
||||||
return;
|
* modes. We just pick the last, in the future we could pick
|
||||||
}
|
* the mode the display advertises as preferred. */
|
||||||
|
|
||||||
struct cg_output *output = calloc(1, sizeof(struct cg_output));
|
|
||||||
if (!output) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to allocate output");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
output->wlr_output = wlr_output;
|
|
||||||
wlr_output->data = output;
|
|
||||||
output->server = server;
|
|
||||||
|
|
||||||
wl_list_insert(&server->outputs, &output->link);
|
|
||||||
|
|
||||||
output->commit.notify = handle_output_commit;
|
|
||||||
wl_signal_add(&wlr_output->events.commit, &output->commit);
|
|
||||||
output->request_state.notify = handle_output_request_state;
|
|
||||||
wl_signal_add(&wlr_output->events.request_state, &output->request_state);
|
|
||||||
output->destroy.notify = handle_output_destroy;
|
|
||||||
wl_signal_add(&wlr_output->events.destroy, &output->destroy);
|
|
||||||
output->frame.notify = handle_output_frame;
|
|
||||||
wl_signal_add(&wlr_output->events.frame, &output->frame);
|
|
||||||
|
|
||||||
output->scene_output = wlr_scene_output_create(server->scene, wlr_output);
|
|
||||||
if (!output->scene_output) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to allocate scene output");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_output_state state = {0};
|
|
||||||
wlr_output_state_set_enabled(&state, true);
|
|
||||||
if (!wl_list_empty(&wlr_output->modes)) {
|
if (!wl_list_empty(&wlr_output->modes)) {
|
||||||
struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output);
|
struct wlr_output_mode *mode = wl_container_of(wlr_output->modes.prev, mode, link);
|
||||||
if (preferred_mode) {
|
wlr_output_set_mode(wlr_output, mode);
|
||||||
wlr_output_state_set_mode(&state, preferred_mode);
|
|
||||||
}
|
|
||||||
if (!wlr_output_test_state(wlr_output, &state)) {
|
|
||||||
struct wlr_output_mode *mode;
|
|
||||||
wl_list_for_each (mode, &wlr_output->modes, link) {
|
|
||||||
if (mode == preferred_mode) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_output_state_set_mode(&state, mode);
|
|
||||||
if (wlr_output_test_state(wlr_output, &state)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (server->output_mode == CAGE_MULTI_OUTPUT_MODE_LAST && wl_list_length(&server->outputs) > 1) {
|
server->output = calloc(1, sizeof(struct cg_output));
|
||||||
struct cg_output *next = wl_container_of(output->link.next, next, link);
|
server->output->wlr_output = wlr_output;
|
||||||
output_disable(next);
|
server->output->server = server;
|
||||||
}
|
server->output->damage = wlr_output_damage_create(wlr_output);
|
||||||
|
|
||||||
if (!wlr_xcursor_manager_load(server->seat->xcursor_manager, wlr_output->scale)) {
|
server->output->mode.notify = handle_output_mode;
|
||||||
wlr_log(WLR_ERROR, "Cannot load XCursor theme for output '%s' with scale %f", wlr_output->name,
|
wl_signal_add(&wlr_output->events.mode, &server->output->mode);
|
||||||
|
server->output->transform.notify = handle_output_transform;
|
||||||
|
wl_signal_add(&wlr_output->events.transform, &server->output->transform);
|
||||||
|
server->output->destroy.notify = handle_output_destroy;
|
||||||
|
wl_signal_add(&wlr_output->events.destroy, &server->output->destroy);
|
||||||
|
server->output->damage_frame.notify = handle_output_damage_frame;
|
||||||
|
wl_signal_add(&server->output->damage->events.frame, &server->output->damage_frame);
|
||||||
|
server->output->damage_destroy.notify = handle_output_damage_destroy;
|
||||||
|
wl_signal_add(&server->output->damage->events.destroy, &server->output->damage_destroy);
|
||||||
|
|
||||||
|
wlr_output_set_transform(wlr_output, server->output_transform);
|
||||||
|
|
||||||
|
wlr_output_layout_add_auto(server->output_layout, wlr_output);
|
||||||
|
|
||||||
|
/* Disconnect the signal now, because we only use one static output. */
|
||||||
|
wl_list_remove(&server->new_output.link);
|
||||||
|
|
||||||
|
if (wlr_xcursor_manager_load(server->seat->xcursor_manager, wlr_output->scale)) {
|
||||||
|
wlr_log(WLR_ERROR, "Cannot load XCursor theme for output '%s' with scale %f",
|
||||||
|
wlr_output->name,
|
||||||
wlr_output->scale);
|
wlr_output->scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
wlr_log(WLR_DEBUG, "Enabling new output %s", wlr_output->name);
|
/* Place the cursor in the center of the screen. */
|
||||||
if (wlr_output_commit_state(wlr_output, &state)) {
|
wlr_cursor_warp(server->seat->cursor, NULL, wlr_output->width / 2, wlr_output->height / 2);
|
||||||
output_layout_add_auto(output);
|
wlr_output_damage_add_whole(server->output->damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
view_position_all(output->server);
|
void
|
||||||
update_output_manager_config(output->server);
|
output_damage_view_surface(struct cg_output *cg_output, struct cg_view *view)
|
||||||
|
{
|
||||||
|
struct damage_data data = {
|
||||||
|
.output = cg_output,
|
||||||
|
.x = view->x,
|
||||||
|
.y = view->y,
|
||||||
|
.whole = false,
|
||||||
|
};
|
||||||
|
view_for_each_surface(view, damage_surface, &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
output_damage_view_whole(struct cg_output *cg_output, struct cg_view *view)
|
||||||
|
{
|
||||||
|
struct damage_data data = {
|
||||||
|
.output = cg_output,
|
||||||
|
.x = view->x,
|
||||||
|
.y = view->y,
|
||||||
|
.whole = true,
|
||||||
|
};
|
||||||
|
view_for_each_surface(view, damage_surface, &data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
output_damage_drag_icon(struct cg_output *cg_output, struct cg_drag_icon *drag_icon)
|
||||||
|
{
|
||||||
|
struct damage_data data = {
|
||||||
|
.output = cg_output,
|
||||||
|
.x = drag_icon->x,
|
||||||
|
.y = drag_icon->y,
|
||||||
|
.whole = true,
|
||||||
|
};
|
||||||
|
wlr_surface_for_each_surface(drag_icon->wlr_drag_icon->surface,
|
||||||
|
damage_surface,
|
||||||
|
&data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -315,11 +427,6 @@ output_set_window_title(struct cg_output *output, const char *title)
|
||||||
{
|
{
|
||||||
struct wlr_output *wlr_output = output->wlr_output;
|
struct wlr_output *wlr_output = output->wlr_output;
|
||||||
|
|
||||||
if (!wlr_output->enabled) {
|
|
||||||
wlr_log(WLR_DEBUG, "Not setting window title for disabled output %s", wlr_output->name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wlr_output_is_wl(wlr_output)) {
|
if (wlr_output_is_wl(wlr_output)) {
|
||||||
wlr_wl_output_set_title(wlr_output, title);
|
wlr_wl_output_set_title(wlr_output, title);
|
||||||
#if WLR_HAS_X11_BACKEND
|
#if WLR_HAS_X11_BACKEND
|
||||||
|
|
@ -328,95 +435,3 @@ output_set_window_title(struct cg_output *output, const char *title)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
output_config_apply(struct cg_server *server, struct wlr_output_configuration_v1 *config, bool test_only)
|
|
||||||
{
|
|
||||||
bool ok = false;
|
|
||||||
|
|
||||||
size_t states_len;
|
|
||||||
struct wlr_backend_output_state *states = wlr_output_configuration_v1_build_state(config, &states_len);
|
|
||||||
if (states == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_output_swapchain_manager swapchain_manager;
|
|
||||||
wlr_output_swapchain_manager_init(&swapchain_manager, server->backend);
|
|
||||||
|
|
||||||
ok = wlr_output_swapchain_manager_prepare(&swapchain_manager, states, states_len);
|
|
||||||
if (!ok || test_only) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < states_len; i++) {
|
|
||||||
struct wlr_backend_output_state *backend_state = &states[i];
|
|
||||||
struct cg_output *output = backend_state->output->data;
|
|
||||||
|
|
||||||
struct wlr_swapchain *swapchain =
|
|
||||||
wlr_output_swapchain_manager_get_swapchain(&swapchain_manager, backend_state->output);
|
|
||||||
struct wlr_scene_output_state_options options = {
|
|
||||||
.swapchain = swapchain,
|
|
||||||
};
|
|
||||||
struct wlr_output_state *state = &backend_state->base;
|
|
||||||
if (!wlr_scene_output_build_state(output->scene_output, state, &options)) {
|
|
||||||
ok = false;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ok = wlr_backend_commit(server->backend, states, states_len);
|
|
||||||
if (!ok) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_output_swapchain_manager_apply(&swapchain_manager);
|
|
||||||
|
|
||||||
struct wlr_output_configuration_head_v1 *head;
|
|
||||||
wl_list_for_each (head, &config->heads, link) {
|
|
||||||
struct cg_output *output = head->state.output->data;
|
|
||||||
|
|
||||||
if (head->state.enabled) {
|
|
||||||
output_layout_add(output, head->state.x, head->state.y);
|
|
||||||
} else {
|
|
||||||
output_layout_remove(output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
wlr_output_swapchain_manager_finish(&swapchain_manager);
|
|
||||||
for (size_t i = 0; i < states_len; i++) {
|
|
||||||
wlr_output_state_finish(&states[i].base);
|
|
||||||
}
|
|
||||||
free(states);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
handle_output_manager_apply(struct wl_listener *listener, void *data)
|
|
||||||
{
|
|
||||||
struct cg_server *server = wl_container_of(listener, server, output_manager_apply);
|
|
||||||
struct wlr_output_configuration_v1 *config = data;
|
|
||||||
|
|
||||||
if (output_config_apply(server, config, false)) {
|
|
||||||
wlr_output_configuration_v1_send_succeeded(config);
|
|
||||||
} else {
|
|
||||||
wlr_output_configuration_v1_send_failed(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_output_configuration_v1_destroy(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
handle_output_manager_test(struct wl_listener *listener, void *data)
|
|
||||||
{
|
|
||||||
struct cg_server *server = wl_container_of(listener, server, output_manager_test);
|
|
||||||
struct wlr_output_configuration_v1 *config = data;
|
|
||||||
|
|
||||||
if (output_config_apply(server, config, true)) {
|
|
||||||
wlr_output_configuration_v1_send_succeeded(config);
|
|
||||||
} else {
|
|
||||||
wlr_output_configuration_v1_send_failed(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_output_configuration_v1_destroy(config);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
21
output.h
21
output.h
|
|
@ -1,29 +1,30 @@
|
||||||
#ifndef CG_OUTPUT_H
|
#ifndef CG_OUTPUT_H
|
||||||
#define CG_OUTPUT_H
|
#define CG_OUTPUT_H
|
||||||
|
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server.h>
|
||||||
#include <wlr/types/wlr_output.h>
|
#include <wlr/types/wlr_output.h>
|
||||||
|
#include <wlr/types/wlr_output_damage.h>
|
||||||
|
|
||||||
|
#include "seat.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "view.h"
|
#include "view.h"
|
||||||
|
|
||||||
struct cg_output {
|
struct cg_output {
|
||||||
struct cg_server *server;
|
struct cg_server *server;
|
||||||
struct wlr_output *wlr_output;
|
struct wlr_output *wlr_output;
|
||||||
struct wlr_scene_output *scene_output;
|
struct wlr_output_damage *damage;
|
||||||
|
|
||||||
struct wl_listener commit;
|
struct wl_listener mode;
|
||||||
struct wl_listener request_state;
|
struct wl_listener transform;
|
||||||
struct wl_listener destroy;
|
struct wl_listener destroy;
|
||||||
struct wl_listener frame;
|
struct wl_listener damage_frame;
|
||||||
|
struct wl_listener damage_destroy;
|
||||||
struct wl_list link; // cg_server::outputs
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void handle_output_manager_apply(struct wl_listener *listener, void *data);
|
|
||||||
void handle_output_manager_test(struct wl_listener *listener, void *data);
|
|
||||||
void handle_output_layout_change(struct wl_listener *listener, void *data);
|
|
||||||
void handle_new_output(struct wl_listener *listener, void *data);
|
void handle_new_output(struct wl_listener *listener, void *data);
|
||||||
|
void output_damage_view_surface(struct cg_output *output, struct cg_view *view);
|
||||||
|
void output_damage_view_whole(struct cg_output *cg_output, struct cg_view *view);
|
||||||
|
void output_damage_drag_icon(struct cg_output *output, struct cg_drag_icon *icon);
|
||||||
void output_set_window_title(struct cg_output *output, const char *title);
|
void output_set_window_title(struct cg_output *output, const char *title);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
34
seat.h
34
seat.h
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef CG_SEAT_H
|
#ifndef CG_SEAT_H
|
||||||
#define CG_SEAT_H
|
#define CG_SEAT_H
|
||||||
|
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server.h>
|
||||||
#include <wlr/types/wlr_cursor.h>
|
#include <wlr/types/wlr_cursor.h>
|
||||||
#include <wlr/types/wlr_data_device.h>
|
#include <wlr/types/wlr_data_device.h>
|
||||||
#include <wlr/types/wlr_input_device.h>
|
#include <wlr/types/wlr_input_device.h>
|
||||||
|
|
@ -20,26 +20,24 @@ struct cg_seat {
|
||||||
struct wl_listener destroy;
|
struct wl_listener destroy;
|
||||||
|
|
||||||
struct wl_list keyboards;
|
struct wl_list keyboards;
|
||||||
struct wl_list keyboard_groups;
|
|
||||||
struct wl_list pointers;
|
struct wl_list pointers;
|
||||||
struct wl_list touch;
|
struct wl_list touch;
|
||||||
struct wl_listener new_input;
|
struct wl_listener new_input;
|
||||||
|
|
||||||
struct wlr_cursor *cursor;
|
struct wlr_cursor *cursor;
|
||||||
struct wlr_xcursor_manager *xcursor_manager;
|
struct wlr_xcursor_manager *xcursor_manager;
|
||||||
struct wl_listener cursor_motion_relative;
|
struct wl_listener cursor_motion;
|
||||||
struct wl_listener cursor_motion_absolute;
|
struct wl_listener cursor_motion_absolute;
|
||||||
struct wl_listener cursor_button;
|
struct wl_listener cursor_button;
|
||||||
struct wl_listener cursor_axis;
|
struct wl_listener cursor_axis;
|
||||||
struct wl_listener cursor_frame;
|
struct wl_listener cursor_frame;
|
||||||
|
|
||||||
int32_t touch_id;
|
int32_t touch_id;
|
||||||
double touch_lx;
|
double touch_x;
|
||||||
double touch_ly;
|
double touch_y;
|
||||||
struct wl_listener touch_down;
|
struct wl_listener touch_down;
|
||||||
struct wl_listener touch_up;
|
struct wl_listener touch_up;
|
||||||
struct wl_listener touch_motion;
|
struct wl_listener touch_motion;
|
||||||
struct wl_listener touch_frame;
|
|
||||||
|
|
||||||
struct wl_list drag_icons;
|
struct wl_list drag_icons;
|
||||||
struct wl_listener request_start_drag;
|
struct wl_listener request_start_drag;
|
||||||
|
|
@ -50,19 +48,20 @@ struct cg_seat {
|
||||||
struct wl_listener request_set_primary_selection;
|
struct wl_listener request_set_primary_selection;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cg_keyboard_group {
|
struct cg_keyboard {
|
||||||
struct wlr_keyboard_group *wlr_group;
|
struct wl_list link; // seat::keyboards
|
||||||
struct cg_seat *seat;
|
struct cg_seat *seat;
|
||||||
struct wl_listener key;
|
struct wlr_input_device *device;
|
||||||
|
|
||||||
struct wl_listener modifiers;
|
struct wl_listener modifiers;
|
||||||
struct wl_list link; // cg_seat::keyboard_groups
|
struct wl_listener key;
|
||||||
bool is_virtual;
|
struct wl_listener destroy;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cg_pointer {
|
struct cg_pointer {
|
||||||
struct wl_list link; // seat::pointers
|
struct wl_list link; // seat::pointers
|
||||||
struct cg_seat *seat;
|
struct cg_seat *seat;
|
||||||
struct wlr_pointer *pointer;
|
struct wlr_input_device *device;
|
||||||
|
|
||||||
struct wl_listener destroy;
|
struct wl_listener destroy;
|
||||||
};
|
};
|
||||||
|
|
@ -70,7 +69,7 @@ struct cg_pointer {
|
||||||
struct cg_touch {
|
struct cg_touch {
|
||||||
struct wl_list link; // seat::touch
|
struct wl_list link; // seat::touch
|
||||||
struct cg_seat *seat;
|
struct cg_seat *seat;
|
||||||
struct wlr_touch *touch;
|
struct wlr_input_device *device;
|
||||||
|
|
||||||
struct wl_listener destroy;
|
struct wl_listener destroy;
|
||||||
};
|
};
|
||||||
|
|
@ -79,18 +78,15 @@ struct cg_drag_icon {
|
||||||
struct wl_list link; // seat::drag_icons
|
struct wl_list link; // seat::drag_icons
|
||||||
struct cg_seat *seat;
|
struct cg_seat *seat;
|
||||||
struct wlr_drag_icon *wlr_drag_icon;
|
struct wlr_drag_icon *wlr_drag_icon;
|
||||||
struct wlr_scene_tree *scene_tree;
|
double x;
|
||||||
|
double y;
|
||||||
/* The drag icon has a position in layout coordinates. */
|
|
||||||
double lx, ly;
|
|
||||||
|
|
||||||
struct wl_listener destroy;
|
struct wl_listener destroy;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cg_seat *seat_create(struct cg_server *server, struct wlr_backend *backend);
|
struct cg_seat *seat_create(struct cg_server *server);
|
||||||
void seat_destroy(struct cg_seat *seat);
|
void seat_destroy(struct cg_seat *seat);
|
||||||
struct cg_view *seat_get_focus(struct cg_seat *seat);
|
struct cg_view *seat_get_focus(struct cg_seat *seat);
|
||||||
void seat_set_focus(struct cg_seat *seat, struct cg_view *view);
|
void seat_set_focus(struct cg_seat *seat, struct cg_view *view);
|
||||||
void seat_center_cursor(struct cg_seat *seat);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
53
server.h
53
server.h
|
|
@ -3,71 +3,46 @@
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server.h>
|
||||||
|
#include <wlr/backend.h>
|
||||||
|
#include <wlr/types/wlr_idle.h>
|
||||||
#include <wlr/types/wlr_idle_inhibit_v1.h>
|
#include <wlr/types/wlr_idle_inhibit_v1.h>
|
||||||
#include <wlr/types/wlr_idle_notify_v1.h>
|
|
||||||
#include <wlr/types/wlr_output_layout.h>
|
#include <wlr/types/wlr_output_layout.h>
|
||||||
#include <wlr/types/wlr_relative_pointer_v1.h>
|
|
||||||
#include <wlr/types/wlr_xdg_decoration_v1.h>
|
#include <wlr/types/wlr_xdg_decoration_v1.h>
|
||||||
#include <wlr/util/log.h>
|
|
||||||
|
|
||||||
#if CAGE_HAS_XWAYLAND
|
#if CAGE_HAS_XWAYLAND
|
||||||
#include <wlr/xwayland.h>
|
#include <wlr/xwayland.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum cg_multi_output_mode {
|
#include "output.h"
|
||||||
CAGE_MULTI_OUTPUT_MODE_EXTEND,
|
#include "seat.h"
|
||||||
CAGE_MULTI_OUTPUT_MODE_LAST,
|
#include "view.h"
|
||||||
};
|
|
||||||
|
|
||||||
struct cg_server {
|
struct cg_server {
|
||||||
struct wl_display *wl_display;
|
struct wl_display *wl_display;
|
||||||
struct wl_list views;
|
|
||||||
struct wlr_backend *backend;
|
struct wlr_backend *backend;
|
||||||
struct wlr_renderer *renderer;
|
struct wl_list views;
|
||||||
struct wlr_allocator *allocator;
|
|
||||||
struct wlr_session *session;
|
|
||||||
struct wl_listener display_destroy;
|
|
||||||
|
|
||||||
struct cg_seat *seat;
|
struct cg_seat *seat;
|
||||||
struct wlr_idle_notifier_v1 *idle;
|
struct wlr_idle *idle;
|
||||||
struct wlr_idle_inhibit_manager_v1 *idle_inhibit_v1;
|
struct wlr_idle_inhibit_manager_v1 *idle_inhibit_v1;
|
||||||
struct wl_listener new_idle_inhibitor_v1;
|
struct wl_listener new_idle_inhibitor_v1;
|
||||||
struct wl_list inhibitors;
|
struct wl_list inhibitors;
|
||||||
|
|
||||||
enum cg_multi_output_mode output_mode;
|
|
||||||
struct wlr_output_layout *output_layout;
|
struct wlr_output_layout *output_layout;
|
||||||
struct wlr_scene_output_layout *scene_output_layout;
|
struct cg_output *output;
|
||||||
|
|
||||||
struct wlr_scene *scene;
|
|
||||||
/* Includes disabled outputs; depending on the output_mode
|
|
||||||
* some outputs may be disabled. */
|
|
||||||
struct wl_list outputs; // cg_output::link
|
|
||||||
struct wl_listener new_output;
|
struct wl_listener new_output;
|
||||||
struct wl_listener output_layout_change;
|
|
||||||
|
|
||||||
struct wl_listener xdg_toplevel_decoration;
|
struct wl_listener xdg_toplevel_decoration;
|
||||||
struct wl_listener new_xdg_toplevel;
|
struct wl_listener new_xdg_shell_surface;
|
||||||
struct wl_listener new_xdg_popup;
|
|
||||||
|
|
||||||
struct wl_listener new_virtual_keyboard;
|
|
||||||
struct wl_listener new_virtual_pointer;
|
|
||||||
#if CAGE_HAS_XWAYLAND
|
#if CAGE_HAS_XWAYLAND
|
||||||
struct wl_listener new_xwayland_surface;
|
struct wl_listener new_xwayland_surface;
|
||||||
#endif
|
#endif
|
||||||
struct wlr_output_manager_v1 *output_manager_v1;
|
|
||||||
struct wl_listener output_manager_apply;
|
|
||||||
struct wl_listener output_manager_test;
|
|
||||||
|
|
||||||
struct wlr_relative_pointer_manager_v1 *relative_pointer_manager;
|
|
||||||
|
|
||||||
bool xdg_decoration;
|
bool xdg_decoration;
|
||||||
bool allow_vt_switch;
|
enum wl_output_transform output_transform;
|
||||||
bool return_app_code;
|
#ifdef DEBUG
|
||||||
bool terminated;
|
bool debug_damage_tracking;
|
||||||
enum wlr_log_importance log_level;
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
void server_terminate(struct cg_server *server);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
222
view.c
222
view.c
|
|
@ -1,20 +1,20 @@
|
||||||
/*
|
/*
|
||||||
* Cage: A Wayland kiosk.
|
* Cage: A Wayland kiosk.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2018-2021 Jente Hidskes
|
* Copyright (C) 2018-2019 Jente Hidskes
|
||||||
*
|
*
|
||||||
* See the LICENSE file accompanying this file.
|
* See the LICENSE file accompanying this file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define _POSIX_C_SOURCE 200809L
|
#define _POSIX_C_SOURCE 200809L
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server.h>
|
||||||
|
#include <wlr/types/wlr_box.h>
|
||||||
#include <wlr/types/wlr_output.h>
|
#include <wlr/types/wlr_output.h>
|
||||||
#include <wlr/types/wlr_scene.h>
|
#include <wlr/types/wlr_surface.h>
|
||||||
|
|
||||||
#include "output.h"
|
#include "output.h"
|
||||||
#include "seat.h"
|
#include "seat.h"
|
||||||
|
|
@ -24,6 +24,96 @@
|
||||||
#include "xwayland.h"
|
#include "xwayland.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void
|
||||||
|
view_child_handle_commit(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct cg_view_child *child = wl_container_of(listener, child, commit);
|
||||||
|
view_damage_surface(child->view);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void subsurface_create(struct cg_view *view, struct wlr_subsurface *wlr_subsurface);
|
||||||
|
|
||||||
|
static void
|
||||||
|
view_child_handle_new_subsurface(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct cg_view_child *child = wl_container_of(listener, child, new_subsurface);
|
||||||
|
struct wlr_subsurface *wlr_subsurface = data;
|
||||||
|
subsurface_create(child->view, wlr_subsurface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
view_child_finish(struct cg_view_child *child)
|
||||||
|
{
|
||||||
|
if (!child) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
view_damage_whole(child->view);
|
||||||
|
|
||||||
|
wl_list_remove(&child->link);
|
||||||
|
wl_list_remove(&child->commit.link);
|
||||||
|
wl_list_remove(&child->new_subsurface.link);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
view_child_init(struct cg_view_child *child, struct cg_view *view, struct wlr_surface *wlr_surface)
|
||||||
|
{
|
||||||
|
child->view = view;
|
||||||
|
child->wlr_surface = wlr_surface;
|
||||||
|
|
||||||
|
child->commit.notify = view_child_handle_commit;
|
||||||
|
wl_signal_add(&wlr_surface->events.commit, &child->commit);
|
||||||
|
child->new_subsurface.notify = view_child_handle_new_subsurface;
|
||||||
|
wl_signal_add(&wlr_surface->events.new_subsurface, &child->new_subsurface);
|
||||||
|
|
||||||
|
wl_list_insert(&view->children, &child->link);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
subsurface_destroy(struct cg_view_child *child)
|
||||||
|
{
|
||||||
|
if (!child) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct cg_subsurface *subsurface = (struct cg_subsurface *) child;
|
||||||
|
wl_list_remove(&subsurface->destroy.link);
|
||||||
|
view_child_finish(&subsurface->view_child);
|
||||||
|
free(subsurface);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
subsurface_handle_destroy(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct cg_subsurface *subsurface = wl_container_of(listener, subsurface, destroy);
|
||||||
|
struct cg_view_child *view_child = (struct cg_view_child *) subsurface;
|
||||||
|
subsurface_destroy(view_child);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
subsurface_create(struct cg_view *view, struct wlr_subsurface *wlr_subsurface)
|
||||||
|
{
|
||||||
|
struct cg_subsurface *subsurface = calloc(1, sizeof(struct cg_subsurface));
|
||||||
|
if (!subsurface) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
view_child_init(&subsurface->view_child, view, wlr_subsurface->surface);
|
||||||
|
subsurface->view_child.destroy = subsurface_destroy;
|
||||||
|
subsurface->wlr_subsurface = wlr_subsurface;
|
||||||
|
|
||||||
|
subsurface->destroy.notify = subsurface_handle_destroy;
|
||||||
|
wl_signal_add(&wlr_subsurface->events.destroy, &subsurface->destroy);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_new_subsurface(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct cg_view *view = wl_container_of(listener, view, new_subsurface);
|
||||||
|
struct wlr_subsurface *wlr_subsurface = data;
|
||||||
|
subsurface_create(view, wlr_subsurface);
|
||||||
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
view_get_title(struct cg_view *view)
|
view_get_title(struct cg_view *view)
|
||||||
{
|
{
|
||||||
|
|
@ -41,73 +131,67 @@ view_is_primary(struct cg_view *view)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
view_is_transient_for(struct cg_view *child, struct cg_view *parent)
|
view_is_transient_for(struct cg_view *child, struct cg_view *parent) {
|
||||||
{
|
|
||||||
return child->impl->is_transient_for(child, parent);
|
return child->impl->is_transient_for(child, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
view_damage_surface(struct cg_view *view)
|
||||||
|
{
|
||||||
|
output_damage_view_surface(view->server->output, view);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
view_damage_whole(struct cg_view *view)
|
||||||
|
{
|
||||||
|
output_damage_view_whole(view->server->output, view);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
view_activate(struct cg_view *view, bool activate)
|
view_activate(struct cg_view *view, bool activate)
|
||||||
{
|
{
|
||||||
view->impl->activate(view, activate);
|
view->impl->activate(view, activate);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
view_extends_output_layout(struct cg_view *view, struct wlr_box *layout_box)
|
view_maximize(struct cg_view *view)
|
||||||
{
|
{
|
||||||
int width, height;
|
struct cg_output *output = view->server->output;
|
||||||
view->impl->get_geometry(view, &width, &height);
|
int output_width, output_height;
|
||||||
|
|
||||||
return (layout_box->height < height || layout_box->width < width);
|
wlr_output_transformed_resolution(output->wlr_output, &output_width, &output_height);
|
||||||
|
view->impl->maximize(view, output_width, output_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
view_maximize(struct cg_view *view, struct wlr_box *layout_box)
|
view_center(struct cg_view *view)
|
||||||
{
|
{
|
||||||
view->lx = layout_box->x;
|
struct wlr_output *output = view->server->output->wlr_output;
|
||||||
view->ly = layout_box->y;
|
|
||||||
|
|
||||||
if (view->scene_tree) {
|
int output_width, output_height;
|
||||||
wlr_scene_node_set_position(&view->scene_tree->node, view->lx, view->ly);
|
wlr_output_transformed_resolution(output, &output_width, &output_height);
|
||||||
}
|
|
||||||
|
|
||||||
view->impl->maximize(view, layout_box->width, layout_box->height);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
view_center(struct cg_view *view, struct wlr_box *layout_box)
|
|
||||||
{
|
|
||||||
int width, height;
|
int width, height;
|
||||||
view->impl->get_geometry(view, &width, &height);
|
view->impl->get_geometry(view, &width, &height);
|
||||||
|
|
||||||
view->lx = (layout_box->width - width) / 2;
|
view->x = (output_width - width) / 2;
|
||||||
view->ly = (layout_box->height - height) / 2;
|
view->y = (output_height - height) / 2;
|
||||||
|
|
||||||
if (view->scene_tree) {
|
|
||||||
wlr_scene_node_set_position(&view->scene_tree->node, view->lx, view->ly);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
view_position(struct cg_view *view)
|
view_position(struct cg_view *view)
|
||||||
{
|
{
|
||||||
struct wlr_box layout_box;
|
if (view_is_primary(view)) {
|
||||||
wlr_output_layout_get_box(view->server->output_layout, NULL, &layout_box);
|
view_maximize(view);
|
||||||
|
|
||||||
if (view_is_primary(view) || view_extends_output_layout(view, &layout_box)) {
|
|
||||||
view_maximize(view, &layout_box);
|
|
||||||
} else {
|
} else {
|
||||||
view_center(view, &layout_box);
|
view_center(view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
view_position_all(struct cg_server *server)
|
view_for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data)
|
||||||
{
|
{
|
||||||
struct cg_view *view;
|
view->impl->for_each_surface(view, iterator, data);
|
||||||
wl_list_for_each (view, &server->views, link) {
|
|
||||||
view_position(view);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -115,24 +199,28 @@ view_unmap(struct cg_view *view)
|
||||||
{
|
{
|
||||||
wl_list_remove(&view->link);
|
wl_list_remove(&view->link);
|
||||||
|
|
||||||
wlr_scene_node_destroy(&view->scene_tree->node);
|
wl_list_remove(&view->new_subsurface.link);
|
||||||
|
|
||||||
|
struct cg_view_child *child, *tmp;
|
||||||
|
wl_list_for_each_safe(child, tmp, &view->children, link) {
|
||||||
|
child->destroy(child);
|
||||||
|
}
|
||||||
|
|
||||||
view->wlr_surface->data = NULL;
|
|
||||||
view->wlr_surface = NULL;
|
view->wlr_surface = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
view_map(struct cg_view *view, struct wlr_surface *surface)
|
view_map(struct cg_view *view, struct wlr_surface *surface)
|
||||||
{
|
{
|
||||||
view->scene_tree = wlr_scene_subsurface_tree_create(&view->server->scene->tree, surface);
|
|
||||||
if (!view->scene_tree) {
|
|
||||||
wl_resource_post_no_memory(surface->resource);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
view->scene_tree->node.data = view;
|
|
||||||
|
|
||||||
view->wlr_surface = surface;
|
view->wlr_surface = surface;
|
||||||
surface->data = view;
|
|
||||||
|
struct wlr_subsurface *subsurface;
|
||||||
|
wl_list_for_each(subsurface, &view->wlr_surface->subsurfaces, parent_link) {
|
||||||
|
subsurface_create(view, subsurface);
|
||||||
|
}
|
||||||
|
|
||||||
|
view->new_subsurface.notify = handle_new_subsurface;
|
||||||
|
wl_signal_add(&view->wlr_surface->events.new_subsurface, &view->new_subsurface);
|
||||||
|
|
||||||
#if CAGE_HAS_XWAYLAND
|
#if CAGE_HAS_XWAYLAND
|
||||||
/* We shouldn't position override-redirect windows. They set
|
/* We shouldn't position override-redirect windows. They set
|
||||||
|
|
@ -151,6 +239,14 @@ void
|
||||||
view_destroy(struct cg_view *view)
|
view_destroy(struct cg_view *view)
|
||||||
{
|
{
|
||||||
struct cg_server *server = view->server;
|
struct cg_server *server = view->server;
|
||||||
|
bool ever_been_mapped = true;
|
||||||
|
|
||||||
|
#if CAGE_HAS_XWAYLAND
|
||||||
|
if (view->type == CAGE_XWAYLAND_VIEW) {
|
||||||
|
struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
|
||||||
|
ever_been_mapped = xwayland_view->ever_been_mapped;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (view->wlr_surface != NULL) {
|
if (view->wlr_surface != NULL) {
|
||||||
view_unmap(view);
|
view_unmap(view);
|
||||||
|
|
@ -163,20 +259,38 @@ view_destroy(struct cg_view *view)
|
||||||
if (!empty) {
|
if (!empty) {
|
||||||
struct cg_view *prev = wl_container_of(server->views.next, prev, link);
|
struct cg_view *prev = wl_container_of(server->views.next, prev, link);
|
||||||
seat_set_focus(server->seat, prev);
|
seat_set_focus(server->seat, prev);
|
||||||
|
} else if (ever_been_mapped) {
|
||||||
|
/* The list is empty and the last view has been
|
||||||
|
mapped, so we can safely exit. */
|
||||||
|
wl_display_terminate(server->wl_display);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
view_init(struct cg_view *view, struct cg_server *server, enum cg_view_type type, const struct cg_view_impl *impl)
|
view_init(struct cg_view *view, struct cg_server *server, enum cg_view_type type,
|
||||||
|
const struct cg_view_impl *impl)
|
||||||
{
|
{
|
||||||
view->server = server;
|
view->server = server;
|
||||||
view->type = type;
|
view->type = type;
|
||||||
view->impl = impl;
|
view->impl = impl;
|
||||||
|
|
||||||
|
wl_list_init(&view->children);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct cg_view *
|
struct cg_view *
|
||||||
view_from_wlr_surface(struct wlr_surface *surface)
|
view_from_wlr_surface(struct cg_server *server, struct wlr_surface *surface)
|
||||||
{
|
{
|
||||||
assert(surface);
|
struct cg_view *view;
|
||||||
return surface->data;
|
wl_list_for_each(view, &server->views, link) {
|
||||||
|
if (view->wlr_surface == surface) {
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct wlr_surface *
|
||||||
|
view_wlr_surface_at(struct cg_view *view, double sx, double sy, double *sub_x, double *sub_y)
|
||||||
|
{
|
||||||
|
return view->impl->wlr_surface_at(view, sx, sy, sub_x, sub_y);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
50
view.h
50
view.h
|
|
@ -4,10 +4,10 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server.h>
|
||||||
#include <wlr/types/wlr_compositor.h>
|
#include <wlr/types/wlr_box.h>
|
||||||
|
#include <wlr/types/wlr_surface.h>
|
||||||
#include <wlr/types/wlr_xdg_shell.h>
|
#include <wlr/types/wlr_xdg_shell.h>
|
||||||
#include <wlr/util/box.h>
|
|
||||||
#if CAGE_HAS_XWAYLAND
|
#if CAGE_HAS_XWAYLAND
|
||||||
#include <wlr/xwayland.h>
|
#include <wlr/xwayland.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -24,14 +24,14 @@ enum cg_view_type {
|
||||||
struct cg_view {
|
struct cg_view {
|
||||||
struct cg_server *server;
|
struct cg_server *server;
|
||||||
struct wl_list link; // server::views
|
struct wl_list link; // server::views
|
||||||
|
struct wl_list children; // cg_view_child::link
|
||||||
struct wlr_surface *wlr_surface;
|
struct wlr_surface *wlr_surface;
|
||||||
struct wlr_scene_tree *scene_tree;
|
int x, y;
|
||||||
|
|
||||||
/* The view has a position in layout coordinates. */
|
|
||||||
int lx, ly;
|
|
||||||
|
|
||||||
enum cg_view_type type;
|
enum cg_view_type type;
|
||||||
const struct cg_view_impl *impl;
|
const struct cg_view_impl *impl;
|
||||||
|
|
||||||
|
struct wl_listener new_subsurface;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cg_view_impl {
|
struct cg_view_impl {
|
||||||
|
|
@ -42,19 +42,49 @@ struct cg_view_impl {
|
||||||
void (*activate)(struct cg_view *view, bool activate);
|
void (*activate)(struct cg_view *view, bool activate);
|
||||||
void (*maximize)(struct cg_view *view, int output_width, int output_height);
|
void (*maximize)(struct cg_view *view, int output_width, int output_height);
|
||||||
void (*destroy)(struct cg_view *view);
|
void (*destroy)(struct cg_view *view);
|
||||||
|
void (*for_each_surface)(struct cg_view *view, wlr_surface_iterator_func_t iterator,
|
||||||
|
void *data);
|
||||||
|
struct wlr_surface *(*wlr_surface_at)(struct cg_view *view, double sx, double sy,
|
||||||
|
double *sub_x, double *sub_y);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cg_view_child {
|
||||||
|
struct cg_view *view;
|
||||||
|
struct wlr_surface *wlr_surface;
|
||||||
|
struct wl_list link;
|
||||||
|
|
||||||
|
struct wl_listener commit;
|
||||||
|
struct wl_listener new_subsurface;
|
||||||
|
|
||||||
|
void (*destroy)(struct cg_view_child *child);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cg_subsurface {
|
||||||
|
struct cg_view_child view_child;
|
||||||
|
struct wlr_subsurface *wlr_subsurface;
|
||||||
|
|
||||||
|
struct wl_listener destroy;
|
||||||
};
|
};
|
||||||
|
|
||||||
char *view_get_title(struct cg_view *view);
|
char *view_get_title(struct cg_view *view);
|
||||||
bool view_is_primary(struct cg_view *view);
|
bool view_is_primary(struct cg_view *view);
|
||||||
bool view_is_transient_for(struct cg_view *child, struct cg_view *parent);
|
bool view_is_transient_for(struct cg_view *child, struct cg_view *parent);
|
||||||
|
void view_damage_surface(struct cg_view *view);
|
||||||
|
void view_damage_whole(struct cg_view *view);
|
||||||
void view_activate(struct cg_view *view, bool activate);
|
void view_activate(struct cg_view *view, bool activate);
|
||||||
void view_position(struct cg_view *view);
|
void view_position(struct cg_view *view);
|
||||||
void view_position_all(struct cg_server *server);
|
void view_for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data);
|
||||||
void view_unmap(struct cg_view *view);
|
void view_unmap(struct cg_view *view);
|
||||||
void view_map(struct cg_view *view, struct wlr_surface *surface);
|
void view_map(struct cg_view *view, struct wlr_surface *surface);
|
||||||
void view_destroy(struct cg_view *view);
|
void view_destroy(struct cg_view *view);
|
||||||
void view_init(struct cg_view *view, struct cg_server *server, enum cg_view_type type, const struct cg_view_impl *impl);
|
void view_init(struct cg_view *view, struct cg_server *server, enum cg_view_type type,
|
||||||
|
const struct cg_view_impl *impl);
|
||||||
|
|
||||||
struct cg_view *view_from_wlr_surface(struct wlr_surface *surface);
|
struct cg_view *view_from_wlr_surface(struct cg_server *server, struct wlr_surface *surface);
|
||||||
|
struct wlr_surface *view_wlr_surface_at(struct cg_view *view, double sx, double sy,
|
||||||
|
double *sub_x, double *sub_y);
|
||||||
|
|
||||||
|
void view_child_finish(struct cg_view_child *child);
|
||||||
|
void view_child_init(struct cg_view_child *child, struct cg_view *view, struct wlr_surface *wlr_surface);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
403
xdg_shell.c
403
xdg_shell.c
|
|
@ -6,11 +6,10 @@
|
||||||
* See the LICENSE file accompanying this file.
|
* See the LICENSE file accompanying this file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server.h>
|
||||||
#include <wlr/types/wlr_scene.h>
|
#include <wlr/types/wlr_box.h>
|
||||||
#include <wlr/types/wlr_xdg_shell.h>
|
#include <wlr/types/wlr_xdg_shell.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/util/log.h>
|
||||||
|
|
||||||
|
|
@ -19,9 +18,21 @@
|
||||||
#include "xdg_shell.h"
|
#include "xdg_shell.h"
|
||||||
|
|
||||||
static void
|
static void
|
||||||
xdg_decoration_set_mode(struct cg_xdg_decoration *xdg_decoration)
|
xdg_decoration_handle_destroy(struct wl_listener *listener, void *data)
|
||||||
{
|
{
|
||||||
|
struct cg_xdg_decoration *xdg_decoration = wl_container_of(listener, xdg_decoration, destroy);
|
||||||
|
|
||||||
|
wl_list_remove(&xdg_decoration->destroy.link);
|
||||||
|
wl_list_remove(&xdg_decoration->request_mode.link);
|
||||||
|
free(xdg_decoration);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xdg_decoration_handle_request_mode(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct cg_xdg_decoration *xdg_decoration = wl_container_of(listener, xdg_decoration, request_mode);
|
||||||
enum wlr_xdg_toplevel_decoration_v1_mode mode;
|
enum wlr_xdg_toplevel_decoration_v1_mode mode;
|
||||||
|
|
||||||
if (xdg_decoration->server->xdg_decoration) {
|
if (xdg_decoration->server->xdg_decoration) {
|
||||||
mode = WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
|
mode = WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -31,84 +42,101 @@ xdg_decoration_set_mode(struct cg_xdg_decoration *xdg_decoration)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
xdg_decoration_handle_destroy(struct wl_listener *listener, void *data)
|
xdg_popup_destroy(struct cg_view_child *child)
|
||||||
{
|
{
|
||||||
struct cg_xdg_decoration *xdg_decoration = wl_container_of(listener, xdg_decoration, destroy);
|
if (!child) {
|
||||||
|
|
||||||
wl_list_remove(&xdg_decoration->destroy.link);
|
|
||||||
wl_list_remove(&xdg_decoration->commit.link);
|
|
||||||
wl_list_remove(&xdg_decoration->request_mode.link);
|
|
||||||
free(xdg_decoration);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
xdg_decoration_handle_commit(struct wl_listener *listener, void *data)
|
|
||||||
{
|
|
||||||
struct cg_xdg_decoration *xdg_decoration = wl_container_of(listener, xdg_decoration, commit);
|
|
||||||
|
|
||||||
if (xdg_decoration->wlr_decoration->toplevel->base->initial_commit) {
|
|
||||||
xdg_decoration_set_mode(xdg_decoration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
xdg_decoration_handle_request_mode(struct wl_listener *listener, void *data)
|
|
||||||
{
|
|
||||||
struct cg_xdg_decoration *xdg_decoration = wl_container_of(listener, xdg_decoration, request_mode);
|
|
||||||
|
|
||||||
if (xdg_decoration->wlr_decoration->toplevel->base->initialized) {
|
|
||||||
xdg_decoration_set_mode(xdg_decoration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct cg_view *
|
|
||||||
popup_get_view(struct wlr_xdg_popup *popup)
|
|
||||||
{
|
|
||||||
while (true) {
|
|
||||||
if (popup->parent == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_try_from_wlr_surface(popup->parent);
|
|
||||||
if (xdg_surface == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
switch (xdg_surface->role) {
|
|
||||||
case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
|
|
||||||
return xdg_surface->data;
|
|
||||||
case WLR_XDG_SURFACE_ROLE_POPUP:
|
|
||||||
popup = xdg_surface->popup;
|
|
||||||
break;
|
|
||||||
case WLR_XDG_SURFACE_ROLE_NONE:
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
popup_unconstrain(struct wlr_xdg_popup *popup)
|
|
||||||
{
|
|
||||||
struct cg_view *view = popup_get_view(popup);
|
|
||||||
if (view == NULL) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct cg_server *server = view->server;
|
struct cg_xdg_popup *popup = (struct cg_xdg_popup *) child;
|
||||||
struct wlr_box *popup_box = &popup->current.geometry;
|
wl_list_remove(&popup->destroy.link);
|
||||||
|
wl_list_remove(&popup->map.link);
|
||||||
|
wl_list_remove(&popup->unmap.link);
|
||||||
|
wl_list_remove(&popup->new_popup.link);
|
||||||
|
view_child_finish(&popup->view_child);
|
||||||
|
free(popup);
|
||||||
|
}
|
||||||
|
|
||||||
struct wlr_output_layout *output_layout = server->output_layout;
|
static void
|
||||||
struct wlr_output *wlr_output =
|
handle_xdg_popup_map(struct wl_listener *listener, void *data)
|
||||||
wlr_output_layout_output_at(output_layout, view->lx + popup_box->x, view->ly + popup_box->y);
|
{
|
||||||
struct wlr_box output_box;
|
struct cg_xdg_popup *popup = wl_container_of(listener, popup, map);
|
||||||
wlr_output_layout_get_box(output_layout, wlr_output, &output_box);
|
view_damage_whole(popup->view_child.view);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_xdg_popup_unmap(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct cg_xdg_popup *popup = wl_container_of(listener, popup, unmap);
|
||||||
|
view_damage_whole(popup->view_child.view);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_xdg_popup_destroy(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct cg_xdg_popup *popup = wl_container_of(listener, popup, destroy);
|
||||||
|
struct cg_view_child *view_child = (struct cg_view_child *) popup;
|
||||||
|
xdg_popup_destroy(view_child);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xdg_popup_create(struct cg_view *view, struct wlr_xdg_popup *wlr_popup);
|
||||||
|
|
||||||
|
static void
|
||||||
|
popup_handle_new_xdg_popup(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct cg_xdg_popup *popup = wl_container_of(listener, popup, new_popup);
|
||||||
|
struct wlr_xdg_popup *wlr_popup = data;
|
||||||
|
xdg_popup_create(popup->view_child.view, wlr_popup);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
popup_unconstrain(struct cg_xdg_popup *popup)
|
||||||
|
{
|
||||||
|
struct cg_view *view = popup->view_child.view;
|
||||||
|
struct wlr_output *output = view->server->output->wlr_output;
|
||||||
|
|
||||||
|
int width, height;
|
||||||
|
wlr_output_effective_resolution(output, &width, &height);
|
||||||
|
|
||||||
struct wlr_box output_toplevel_box = {
|
struct wlr_box output_toplevel_box = {
|
||||||
.x = output_box.x - view->lx,
|
.x = output->lx - view->x,
|
||||||
.y = output_box.y - view->ly,
|
.y = output->ly - view->y,
|
||||||
.width = output_box.width,
|
.width = width,
|
||||||
.height = output_box.height,
|
.height = height
|
||||||
};
|
};
|
||||||
|
|
||||||
wlr_xdg_popup_unconstrain_from_box(popup, &output_toplevel_box);
|
wlr_xdg_popup_unconstrain_from_box(popup->wlr_popup, &output_toplevel_box);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xdg_popup_create(struct cg_view *view, struct wlr_xdg_popup *wlr_popup)
|
||||||
|
{
|
||||||
|
struct cg_xdg_popup *popup = calloc(1, sizeof(struct cg_xdg_popup));
|
||||||
|
if (!popup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
popup->wlr_popup = wlr_popup;
|
||||||
|
view_child_init(&popup->view_child, view, wlr_popup->base->surface);
|
||||||
|
popup->view_child.destroy = xdg_popup_destroy;
|
||||||
|
popup->destroy.notify = handle_xdg_popup_destroy;
|
||||||
|
wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
|
||||||
|
popup->map.notify = handle_xdg_popup_map;
|
||||||
|
wl_signal_add(&wlr_popup->base->events.map, &popup->map);
|
||||||
|
popup->unmap.notify = handle_xdg_popup_unmap;
|
||||||
|
wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap);
|
||||||
|
popup->new_popup.notify = popup_handle_new_xdg_popup;
|
||||||
|
wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
|
||||||
|
|
||||||
|
popup_unconstrain(popup);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_new_xdg_popup(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, new_popup);
|
||||||
|
struct wlr_xdg_popup *wlr_popup = data;
|
||||||
|
xdg_popup_create(&xdg_shell_view->view, wlr_popup);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct cg_xdg_shell_view *
|
static struct cg_xdg_shell_view *
|
||||||
|
|
@ -121,26 +149,27 @@ static char *
|
||||||
get_title(struct cg_view *view)
|
get_title(struct cg_view *view)
|
||||||
{
|
{
|
||||||
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
||||||
return xdg_shell_view->xdg_toplevel->title;
|
return xdg_shell_view->xdg_surface->toplevel->title;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
get_geometry(struct cg_view *view, int *width_out, int *height_out)
|
get_geometry(struct cg_view *view, int *width_out, int *height_out)
|
||||||
{
|
{
|
||||||
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
||||||
struct wlr_xdg_surface *xdg_surface = xdg_shell_view->xdg_toplevel->base;
|
struct wlr_box geom;
|
||||||
|
|
||||||
*width_out = xdg_surface->geometry.width;
|
wlr_xdg_surface_get_geometry(xdg_shell_view->xdg_surface, &geom);
|
||||||
*height_out = xdg_surface->geometry.height;
|
*width_out = geom.width;
|
||||||
|
*height_out = geom.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
is_primary(struct cg_view *view)
|
is_primary(struct cg_view *view)
|
||||||
{
|
{
|
||||||
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
||||||
struct wlr_xdg_toplevel *parent = xdg_shell_view->xdg_toplevel->parent;
|
struct wlr_xdg_surface *parent = xdg_shell_view->xdg_surface->toplevel->parent;
|
||||||
|
/* FIXME: role is 0? */
|
||||||
return parent == NULL;
|
return parent == NULL; /*&& role == WLR_XDG_SURFACE_ROLE_TOPLEVEL */
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -150,13 +179,14 @@ is_transient_for(struct cg_view *child, struct cg_view *parent)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
struct cg_xdg_shell_view *_child = xdg_shell_view_from_view(child);
|
struct cg_xdg_shell_view *_child = xdg_shell_view_from_view(child);
|
||||||
struct wlr_xdg_toplevel *xdg_toplevel = _child->xdg_toplevel;
|
struct wlr_xdg_surface *xdg_surface = _child->xdg_surface;
|
||||||
struct cg_xdg_shell_view *_parent = xdg_shell_view_from_view(parent);
|
struct cg_xdg_shell_view *_parent = xdg_shell_view_from_view(parent);
|
||||||
while (xdg_toplevel) {
|
struct wlr_xdg_surface *parent_xdg_surface = _parent->xdg_surface;
|
||||||
if (xdg_toplevel->parent == _parent->xdg_toplevel) {
|
while (xdg_surface) {
|
||||||
|
if (xdg_surface->toplevel->parent == parent_xdg_surface) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
xdg_toplevel = xdg_toplevel->parent;
|
xdg_surface = xdg_surface->toplevel->parent;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -165,15 +195,15 @@ static void
|
||||||
activate(struct cg_view *view, bool activate)
|
activate(struct cg_view *view, bool activate)
|
||||||
{
|
{
|
||||||
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
||||||
wlr_xdg_toplevel_set_activated(xdg_shell_view->xdg_toplevel, activate);
|
wlr_xdg_toplevel_set_activated(xdg_shell_view->xdg_surface, activate);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
maximize(struct cg_view *view, int output_width, int output_height)
|
maximize(struct cg_view *view, int output_width, int output_height)
|
||||||
{
|
{
|
||||||
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
||||||
wlr_xdg_toplevel_set_size(xdg_shell_view->xdg_toplevel, output_width, output_height);
|
wlr_xdg_toplevel_set_size(xdg_shell_view->xdg_surface, output_width, output_height);
|
||||||
wlr_xdg_toplevel_set_maximized(xdg_shell_view->xdg_toplevel, true);
|
wlr_xdg_toplevel_set_maximized(xdg_shell_view->xdg_surface, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -184,68 +214,74 @@ destroy(struct cg_view *view)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_xdg_toplevel_request_fullscreen(struct wl_listener *listener, void *data)
|
for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data)
|
||||||
{
|
{
|
||||||
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, request_fullscreen);
|
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
||||||
|
wlr_xdg_surface_for_each_surface(xdg_shell_view->xdg_surface, iterator, data);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
static struct wlr_surface *
|
||||||
* Certain clients do not like figuring out their own window geometry if they
|
wlr_surface_at(struct cg_view *view, double sx, double sy, double *sub_x, double *sub_y)
|
||||||
* display in fullscreen mode, so we set it here.
|
{
|
||||||
*/
|
struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
|
||||||
struct wlr_box layout_box;
|
return wlr_xdg_surface_surface_at(xdg_shell_view->xdg_surface, sx, sy, sub_x, sub_y);
|
||||||
wlr_output_layout_get_box(xdg_shell_view->view.server->output_layout, NULL, &layout_box);
|
|
||||||
wlr_xdg_toplevel_set_size(xdg_shell_view->xdg_toplevel, layout_box.width, layout_box.height);
|
|
||||||
|
|
||||||
wlr_xdg_toplevel_set_fullscreen(xdg_shell_view->xdg_toplevel,
|
|
||||||
xdg_shell_view->xdg_toplevel->requested.fullscreen);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_xdg_toplevel_unmap(struct wl_listener *listener, void *data)
|
handle_xdg_shell_surface_request_fullscreen(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, request_fullscreen);
|
||||||
|
struct wlr_xdg_toplevel_set_fullscreen_event *event = data;
|
||||||
|
wlr_xdg_toplevel_set_fullscreen(xdg_shell_view->xdg_surface, event->fullscreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_xdg_shell_surface_commit(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, commit);
|
||||||
|
struct cg_view *view = &xdg_shell_view->view;
|
||||||
|
view_damage_surface(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_xdg_shell_surface_unmap(struct wl_listener *listener, void *data)
|
||||||
{
|
{
|
||||||
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, unmap);
|
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, unmap);
|
||||||
struct cg_view *view = &xdg_shell_view->view;
|
struct cg_view *view = &xdg_shell_view->view;
|
||||||
|
|
||||||
|
view_damage_whole(view);
|
||||||
|
|
||||||
|
wl_list_remove(&xdg_shell_view->commit.link);
|
||||||
|
|
||||||
view_unmap(view);
|
view_unmap(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_xdg_toplevel_map(struct wl_listener *listener, void *data)
|
handle_xdg_shell_surface_map(struct wl_listener *listener, void *data)
|
||||||
{
|
{
|
||||||
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, map);
|
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, map);
|
||||||
struct cg_view *view = &xdg_shell_view->view;
|
struct cg_view *view = &xdg_shell_view->view;
|
||||||
|
|
||||||
view_map(view, xdg_shell_view->xdg_toplevel->base->surface);
|
xdg_shell_view->commit.notify = handle_xdg_shell_surface_commit;
|
||||||
|
wl_signal_add(&xdg_shell_view->xdg_surface->surface->events.commit, &xdg_shell_view->commit);
|
||||||
|
|
||||||
|
view_map(view, xdg_shell_view->xdg_surface->surface);
|
||||||
|
|
||||||
|
view_damage_whole(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_xdg_toplevel_commit(struct wl_listener *listener, void *data)
|
handle_xdg_shell_surface_destroy(struct wl_listener *listener, void *data)
|
||||||
{
|
|
||||||
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, commit);
|
|
||||||
|
|
||||||
if (!xdg_shell_view->xdg_toplevel->base->initial_commit) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_xdg_toplevel_set_wm_capabilities(xdg_shell_view->xdg_toplevel, XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
|
|
||||||
|
|
||||||
/* When an xdg_surface performs an initial commit, the compositor must
|
|
||||||
* reply with a configure so the client can map the surface. */
|
|
||||||
view_position(&xdg_shell_view->view);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
handle_xdg_toplevel_destroy(struct wl_listener *listener, void *data)
|
|
||||||
{
|
{
|
||||||
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, destroy);
|
struct cg_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, destroy);
|
||||||
struct cg_view *view = &xdg_shell_view->view;
|
struct cg_view *view = &xdg_shell_view->view;
|
||||||
|
|
||||||
wl_list_remove(&xdg_shell_view->commit.link);
|
|
||||||
wl_list_remove(&xdg_shell_view->map.link);
|
wl_list_remove(&xdg_shell_view->map.link);
|
||||||
wl_list_remove(&xdg_shell_view->unmap.link);
|
wl_list_remove(&xdg_shell_view->unmap.link);
|
||||||
wl_list_remove(&xdg_shell_view->destroy.link);
|
wl_list_remove(&xdg_shell_view->destroy.link);
|
||||||
wl_list_remove(&xdg_shell_view->request_fullscreen.link);
|
wl_list_remove(&xdg_shell_view->request_fullscreen.link);
|
||||||
xdg_shell_view->xdg_toplevel = NULL;
|
wl_list_remove(&xdg_shell_view->new_popup.link);
|
||||||
|
xdg_shell_view->xdg_surface = NULL;
|
||||||
|
|
||||||
view_destroy(view);
|
view_destroy(view);
|
||||||
}
|
}
|
||||||
|
|
@ -258,13 +294,19 @@ static const struct cg_view_impl xdg_shell_view_impl = {
|
||||||
.activate = activate,
|
.activate = activate,
|
||||||
.maximize = maximize,
|
.maximize = maximize,
|
||||||
.destroy = destroy,
|
.destroy = destroy,
|
||||||
|
.for_each_surface = for_each_surface,
|
||||||
|
.wlr_surface_at = wlr_surface_at,
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
handle_new_xdg_toplevel(struct wl_listener *listener, void *data)
|
handle_xdg_shell_surface_new(struct wl_listener *listener, void *data)
|
||||||
{
|
{
|
||||||
struct cg_server *server = wl_container_of(listener, server, new_xdg_toplevel);
|
struct cg_server *server = wl_container_of(listener, server, new_xdg_shell_surface);
|
||||||
struct wlr_xdg_toplevel *toplevel = data;
|
struct wlr_xdg_surface *xdg_surface = data;
|
||||||
|
|
||||||
|
if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
struct cg_xdg_shell_view *xdg_shell_view = calloc(1, sizeof(struct cg_xdg_shell_view));
|
struct cg_xdg_shell_view *xdg_shell_view = calloc(1, sizeof(struct cg_xdg_shell_view));
|
||||||
if (!xdg_shell_view) {
|
if (!xdg_shell_view) {
|
||||||
|
|
@ -273,105 +315,18 @@ handle_new_xdg_toplevel(struct wl_listener *listener, void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
view_init(&xdg_shell_view->view, server, CAGE_XDG_SHELL_VIEW, &xdg_shell_view_impl);
|
view_init(&xdg_shell_view->view, server, CAGE_XDG_SHELL_VIEW, &xdg_shell_view_impl);
|
||||||
xdg_shell_view->xdg_toplevel = toplevel;
|
xdg_shell_view->xdg_surface = xdg_surface;
|
||||||
|
|
||||||
xdg_shell_view->commit.notify = handle_xdg_toplevel_commit;
|
xdg_shell_view->map.notify = handle_xdg_shell_surface_map;
|
||||||
wl_signal_add(&toplevel->base->surface->events.commit, &xdg_shell_view->commit);
|
wl_signal_add(&xdg_surface->events.map, &xdg_shell_view->map);
|
||||||
xdg_shell_view->map.notify = handle_xdg_toplevel_map;
|
xdg_shell_view->unmap.notify = handle_xdg_shell_surface_unmap;
|
||||||
wl_signal_add(&toplevel->base->surface->events.map, &xdg_shell_view->map);
|
wl_signal_add(&xdg_surface->events.unmap, &xdg_shell_view->unmap);
|
||||||
xdg_shell_view->unmap.notify = handle_xdg_toplevel_unmap;
|
xdg_shell_view->destroy.notify = handle_xdg_shell_surface_destroy;
|
||||||
wl_signal_add(&toplevel->base->surface->events.unmap, &xdg_shell_view->unmap);
|
wl_signal_add(&xdg_surface->events.destroy, &xdg_shell_view->destroy);
|
||||||
xdg_shell_view->destroy.notify = handle_xdg_toplevel_destroy;
|
xdg_shell_view->request_fullscreen.notify = handle_xdg_shell_surface_request_fullscreen;
|
||||||
wl_signal_add(&toplevel->events.destroy, &xdg_shell_view->destroy);
|
wl_signal_add(&xdg_surface->toplevel->events.request_fullscreen, &xdg_shell_view->request_fullscreen);
|
||||||
xdg_shell_view->request_fullscreen.notify = handle_xdg_toplevel_request_fullscreen;
|
xdg_shell_view->new_popup.notify = handle_new_xdg_popup;
|
||||||
wl_signal_add(&toplevel->events.request_fullscreen, &xdg_shell_view->request_fullscreen);
|
wl_signal_add(&xdg_surface->events.new_popup, &xdg_shell_view->new_popup);
|
||||||
|
|
||||||
toplevel->base->data = xdg_shell_view;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
popup_handle_destroy(struct wl_listener *listener, void *data)
|
|
||||||
{
|
|
||||||
struct cg_xdg_popup *popup = wl_container_of(listener, popup, destroy);
|
|
||||||
wl_list_remove(&popup->destroy.link);
|
|
||||||
wl_list_remove(&popup->commit.link);
|
|
||||||
wl_list_remove(&popup->reposition.link);
|
|
||||||
free(popup);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
popup_handle_commit(struct wl_listener *listener, void *data)
|
|
||||||
{
|
|
||||||
struct cg_xdg_popup *popup = wl_container_of(listener, popup, commit);
|
|
||||||
|
|
||||||
if (popup->xdg_popup->base->initial_commit) {
|
|
||||||
popup_unconstrain(popup->xdg_popup);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
popup_handle_reposition(struct wl_listener *listener, void *data)
|
|
||||||
{
|
|
||||||
struct cg_xdg_popup *popup = wl_container_of(listener, popup, reposition);
|
|
||||||
|
|
||||||
popup_unconstrain(popup->xdg_popup);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
handle_new_xdg_popup(struct wl_listener *listener, void *data)
|
|
||||||
{
|
|
||||||
struct cg_server *server = wl_container_of(listener, server, new_xdg_popup);
|
|
||||||
struct wlr_xdg_popup *wlr_popup = data;
|
|
||||||
|
|
||||||
struct cg_view *view = popup_get_view(wlr_popup);
|
|
||||||
if (view == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct wlr_scene_tree *parent_scene_tree = NULL;
|
|
||||||
struct wlr_xdg_surface *parent = wlr_xdg_surface_try_from_wlr_surface(wlr_popup->parent);
|
|
||||||
if (parent == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (parent->role) {
|
|
||||||
case WLR_XDG_SURFACE_ROLE_TOPLEVEL:;
|
|
||||||
parent_scene_tree = view->scene_tree;
|
|
||||||
break;
|
|
||||||
case WLR_XDG_SURFACE_ROLE_POPUP:
|
|
||||||
parent_scene_tree = parent->data;
|
|
||||||
break;
|
|
||||||
case WLR_XDG_SURFACE_ROLE_NONE:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (parent_scene_tree == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct cg_xdg_popup *popup = calloc(1, sizeof(*popup));
|
|
||||||
if (popup == NULL) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to allocate popup");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
popup->xdg_popup = wlr_popup;
|
|
||||||
|
|
||||||
popup->destroy.notify = popup_handle_destroy;
|
|
||||||
wl_signal_add(&wlr_popup->events.destroy, &popup->destroy);
|
|
||||||
|
|
||||||
popup->commit.notify = popup_handle_commit;
|
|
||||||
wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
|
|
||||||
|
|
||||||
popup->reposition.notify = popup_handle_reposition;
|
|
||||||
wl_signal_add(&wlr_popup->events.reposition, &popup->reposition);
|
|
||||||
|
|
||||||
struct wlr_scene_tree *popup_scene_tree = wlr_scene_xdg_surface_create(parent_scene_tree, wlr_popup->base);
|
|
||||||
if (popup_scene_tree == NULL) {
|
|
||||||
wlr_log(WLR_ERROR, "Failed to allocate scene-graph node for XDG popup");
|
|
||||||
free(popup);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wlr_popup->base->data = popup_scene_tree;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -390,8 +345,8 @@ handle_xdg_toplevel_decoration(struct wl_listener *listener, void *data)
|
||||||
|
|
||||||
xdg_decoration->destroy.notify = xdg_decoration_handle_destroy;
|
xdg_decoration->destroy.notify = xdg_decoration_handle_destroy;
|
||||||
wl_signal_add(&wlr_decoration->events.destroy, &xdg_decoration->destroy);
|
wl_signal_add(&wlr_decoration->events.destroy, &xdg_decoration->destroy);
|
||||||
xdg_decoration->commit.notify = xdg_decoration_handle_commit;
|
|
||||||
wl_signal_add(&wlr_decoration->toplevel->base->surface->events.commit, &xdg_decoration->commit);
|
|
||||||
xdg_decoration->request_mode.notify = xdg_decoration_handle_request_mode;
|
xdg_decoration->request_mode.notify = xdg_decoration_handle_request_mode;
|
||||||
wl_signal_add(&wlr_decoration->events.request_mode, &xdg_decoration->request_mode);
|
wl_signal_add(&wlr_decoration->events.request_mode, &xdg_decoration->request_mode);
|
||||||
|
|
||||||
|
xdg_decoration_handle_request_mode(&xdg_decoration->request_mode, wlr_decoration);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
29
xdg_shell.h
29
xdg_shell.h
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef CG_XDG_SHELL_H
|
#ifndef CG_XDG_SHELL_H
|
||||||
#define CG_XDG_SHELL_H
|
#define CG_XDG_SHELL_H
|
||||||
|
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server.h>
|
||||||
#include <wlr/types/wlr_xdg_decoration_v1.h>
|
#include <wlr/types/wlr_xdg_decoration_v1.h>
|
||||||
#include <wlr/types/wlr_xdg_shell.h>
|
#include <wlr/types/wlr_xdg_shell.h>
|
||||||
|
|
||||||
|
|
@ -9,33 +9,34 @@
|
||||||
|
|
||||||
struct cg_xdg_shell_view {
|
struct cg_xdg_shell_view {
|
||||||
struct cg_view view;
|
struct cg_view view;
|
||||||
struct wlr_xdg_toplevel *xdg_toplevel;
|
struct wlr_xdg_surface *xdg_surface;
|
||||||
|
|
||||||
struct wl_listener destroy;
|
struct wl_listener destroy;
|
||||||
struct wl_listener commit;
|
|
||||||
struct wl_listener unmap;
|
struct wl_listener unmap;
|
||||||
struct wl_listener map;
|
struct wl_listener map;
|
||||||
|
struct wl_listener commit;
|
||||||
struct wl_listener request_fullscreen;
|
struct wl_listener request_fullscreen;
|
||||||
|
struct wl_listener new_popup;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cg_xdg_popup {
|
||||||
|
struct cg_view_child view_child;
|
||||||
|
struct wlr_xdg_popup *wlr_popup;
|
||||||
|
|
||||||
|
struct wl_listener destroy;
|
||||||
|
struct wl_listener map;
|
||||||
|
struct wl_listener unmap;
|
||||||
|
struct wl_listener new_popup;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cg_xdg_decoration {
|
struct cg_xdg_decoration {
|
||||||
struct wlr_xdg_toplevel_decoration_v1 *wlr_decoration;
|
struct wlr_xdg_toplevel_decoration_v1 *wlr_decoration;
|
||||||
struct cg_server *server;
|
struct cg_server *server;
|
||||||
struct wl_listener destroy;
|
struct wl_listener destroy;
|
||||||
struct wl_listener commit;
|
|
||||||
struct wl_listener request_mode;
|
struct wl_listener request_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cg_xdg_popup {
|
void handle_xdg_shell_surface_new(struct wl_listener *listener, void *data);
|
||||||
struct wlr_xdg_popup *xdg_popup;
|
|
||||||
|
|
||||||
struct wl_listener destroy;
|
|
||||||
struct wl_listener commit;
|
|
||||||
struct wl_listener reposition;
|
|
||||||
};
|
|
||||||
|
|
||||||
void handle_new_xdg_toplevel(struct wl_listener *listener, void *data);
|
|
||||||
void handle_new_xdg_popup(struct wl_listener *listener, void *data);
|
|
||||||
|
|
||||||
void handle_xdg_toplevel_decoration(struct wl_listener *listener, void *data);
|
void handle_xdg_toplevel_decoration(struct wl_listener *listener, void *data);
|
||||||
|
|
||||||
|
|
|
||||||
89
xwayland.c
89
xwayland.c
|
|
@ -1,16 +1,17 @@
|
||||||
/*
|
/*
|
||||||
* Cage: A Wayland kiosk.
|
* Cage: A Wayland kiosk.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2018-2020 Jente Hidskes
|
* Copyright (C) 2018-2019 Jente Hidskes
|
||||||
*
|
*
|
||||||
* See the LICENSE file accompanying this file.
|
* See the LICENSE file accompanying this file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server.h>
|
||||||
#include <wlr/util/log.h>
|
#include <wlr/types/wlr_box.h>
|
||||||
#include <wlr/xwayland.h>
|
#include <wlr/xwayland.h>
|
||||||
|
#include <wlr/util/log.h>
|
||||||
|
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "view.h"
|
#include "view.h"
|
||||||
|
|
@ -41,15 +42,8 @@ static void
|
||||||
get_geometry(struct cg_view *view, int *width_out, int *height_out)
|
get_geometry(struct cg_view *view, int *width_out, int *height_out)
|
||||||
{
|
{
|
||||||
struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
|
struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
|
||||||
struct wlr_xwayland_surface *xsurface = xwayland_view->xwayland_surface;
|
*width_out = xwayland_view->xwayland_surface->surface->current.width;
|
||||||
if (xsurface->surface == NULL) {
|
*height_out = xwayland_view->xwayland_surface->surface->current.height;
|
||||||
*width_out = 0;
|
|
||||||
*height_out = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
*width_out = xsurface->surface->current.width;
|
|
||||||
*height_out = xsurface->surface->current.height;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -90,9 +84,8 @@ static void
|
||||||
maximize(struct cg_view *view, int output_width, int output_height)
|
maximize(struct cg_view *view, int output_width, int output_height)
|
||||||
{
|
{
|
||||||
struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
|
struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
|
||||||
wlr_xwayland_surface_configure(xwayland_view->xwayland_surface, view->lx, view->ly, output_width,
|
wlr_xwayland_surface_configure(xwayland_view->xwayland_surface, 0, 0, output_width, output_height);
|
||||||
output_height);
|
wlr_xwayland_surface_set_maximized(xwayland_view->xwayland_surface, true);
|
||||||
wlr_xwayland_surface_set_maximized(xwayland_view->xwayland_surface, true, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -102,6 +95,18 @@ destroy(struct cg_view *view)
|
||||||
free(xwayland_view);
|
free(xwayland_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
for_each_surface(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data)
|
||||||
|
{
|
||||||
|
wlr_surface_for_each_surface(view->wlr_surface, iterator, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct wlr_surface *
|
||||||
|
wlr_surface_at(struct cg_view *view, double sx, double sy, double *sub_x, double *sub_y)
|
||||||
|
{
|
||||||
|
return wlr_surface_surface_at(view->wlr_surface, sx, sy, sub_x, sub_y);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_xwayland_surface_request_fullscreen(struct wl_listener *listener, void *data)
|
handle_xwayland_surface_request_fullscreen(struct wl_listener *listener, void *data)
|
||||||
{
|
{
|
||||||
|
|
@ -110,12 +115,24 @@ handle_xwayland_surface_request_fullscreen(struct wl_listener *listener, void *d
|
||||||
wlr_xwayland_surface_set_fullscreen(xwayland_view->xwayland_surface, xwayland_surface->fullscreen);
|
wlr_xwayland_surface_set_fullscreen(xwayland_view->xwayland_surface, xwayland_surface->fullscreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_xwayland_surface_commit(struct wl_listener *listener, void *data)
|
||||||
|
{
|
||||||
|
struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, commit);
|
||||||
|
struct cg_view *view = &xwayland_view->view;
|
||||||
|
view_damage_surface(view);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_xwayland_surface_unmap(struct wl_listener *listener, void *data)
|
handle_xwayland_surface_unmap(struct wl_listener *listener, void *data)
|
||||||
{
|
{
|
||||||
struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, unmap);
|
struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, unmap);
|
||||||
struct cg_view *view = &xwayland_view->view;
|
struct cg_view *view = &xwayland_view->view;
|
||||||
|
|
||||||
|
view_damage_whole(view);
|
||||||
|
|
||||||
|
wl_list_remove(&xwayland_view->commit.link);
|
||||||
|
|
||||||
view_unmap(view);
|
view_unmap(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,11 +143,17 @@ handle_xwayland_surface_map(struct wl_listener *listener, void *data)
|
||||||
struct cg_view *view = &xwayland_view->view;
|
struct cg_view *view = &xwayland_view->view;
|
||||||
|
|
||||||
if (!xwayland_view_should_manage(view)) {
|
if (!xwayland_view_should_manage(view)) {
|
||||||
view->lx = xwayland_view->xwayland_surface->x;
|
view->x = xwayland_view->xwayland_surface->x;
|
||||||
view->ly = xwayland_view->xwayland_surface->y;
|
view->y = xwayland_view->xwayland_surface->y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xwayland_view->commit.notify = handle_xwayland_surface_commit;
|
||||||
|
wl_signal_add(&xwayland_view->xwayland_surface->surface->events.commit, &xwayland_view->commit);
|
||||||
|
|
||||||
|
xwayland_view->ever_been_mapped = true;
|
||||||
view_map(view, xwayland_view->xwayland_surface->surface);
|
view_map(view, xwayland_view->xwayland_surface->surface);
|
||||||
|
|
||||||
|
view_damage_whole(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -139,6 +162,8 @@ handle_xwayland_surface_destroy(struct wl_listener *listener, void *data)
|
||||||
struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, destroy);
|
struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, destroy);
|
||||||
struct cg_view *view = &xwayland_view->view;
|
struct cg_view *view = &xwayland_view->view;
|
||||||
|
|
||||||
|
wl_list_remove(&xwayland_view->map.link);
|
||||||
|
wl_list_remove(&xwayland_view->unmap.link);
|
||||||
wl_list_remove(&xwayland_view->destroy.link);
|
wl_list_remove(&xwayland_view->destroy.link);
|
||||||
wl_list_remove(&xwayland_view->request_fullscreen.link);
|
wl_list_remove(&xwayland_view->request_fullscreen.link);
|
||||||
xwayland_view->xwayland_surface = NULL;
|
xwayland_view->xwayland_surface = NULL;
|
||||||
|
|
@ -154,28 +179,10 @@ static const struct cg_view_impl xwayland_view_impl = {
|
||||||
.activate = activate,
|
.activate = activate,
|
||||||
.maximize = maximize,
|
.maximize = maximize,
|
||||||
.destroy = destroy,
|
.destroy = destroy,
|
||||||
|
.for_each_surface = for_each_surface,
|
||||||
|
.wlr_surface_at = wlr_surface_at,
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
|
||||||
handle_xwayland_associate(struct wl_listener *listener, void *data)
|
|
||||||
{
|
|
||||||
struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, associate);
|
|
||||||
struct wlr_xwayland_surface *xsurface = xwayland_view->xwayland_surface;
|
|
||||||
|
|
||||||
xwayland_view->map.notify = handle_xwayland_surface_map;
|
|
||||||
wl_signal_add(&xsurface->surface->events.map, &xwayland_view->map);
|
|
||||||
xwayland_view->unmap.notify = handle_xwayland_surface_unmap;
|
|
||||||
wl_signal_add(&xsurface->surface->events.unmap, &xwayland_view->unmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
handle_xwayland_dissociate(struct wl_listener *listener, void *data)
|
|
||||||
{
|
|
||||||
struct cg_xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, dissociate);
|
|
||||||
wl_list_remove(&xwayland_view->map.link);
|
|
||||||
wl_list_remove(&xwayland_view->unmap.link);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
handle_xwayland_surface_new(struct wl_listener *listener, void *data)
|
handle_xwayland_surface_new(struct wl_listener *listener, void *data)
|
||||||
{
|
{
|
||||||
|
|
@ -191,10 +198,10 @@ handle_xwayland_surface_new(struct wl_listener *listener, void *data)
|
||||||
view_init(&xwayland_view->view, server, CAGE_XWAYLAND_VIEW, &xwayland_view_impl);
|
view_init(&xwayland_view->view, server, CAGE_XWAYLAND_VIEW, &xwayland_view_impl);
|
||||||
xwayland_view->xwayland_surface = xwayland_surface;
|
xwayland_view->xwayland_surface = xwayland_surface;
|
||||||
|
|
||||||
xwayland_view->associate.notify = handle_xwayland_associate;
|
xwayland_view->map.notify = handle_xwayland_surface_map;
|
||||||
wl_signal_add(&xwayland_surface->events.associate, &xwayland_view->associate);
|
wl_signal_add(&xwayland_surface->events.map, &xwayland_view->map);
|
||||||
xwayland_view->dissociate.notify = handle_xwayland_dissociate;
|
xwayland_view->unmap.notify = handle_xwayland_surface_unmap;
|
||||||
wl_signal_add(&xwayland_surface->events.dissociate, &xwayland_view->dissociate);
|
wl_signal_add(&xwayland_surface->events.unmap, &xwayland_view->unmap);
|
||||||
xwayland_view->destroy.notify = handle_xwayland_surface_destroy;
|
xwayland_view->destroy.notify = handle_xwayland_surface_destroy;
|
||||||
wl_signal_add(&xwayland_surface->events.destroy, &xwayland_view->destroy);
|
wl_signal_add(&xwayland_surface->events.destroy, &xwayland_view->destroy);
|
||||||
xwayland_view->request_fullscreen.notify = handle_xwayland_surface_request_fullscreen;
|
xwayland_view->request_fullscreen.notify = handle_xwayland_surface_request_fullscreen;
|
||||||
|
|
|
||||||
20
xwayland.h
20
xwayland.h
|
|
@ -1,7 +1,7 @@
|
||||||
#ifndef CG_XWAYLAND_H
|
#ifndef CG_XWAYLAND_H
|
||||||
#define CG_XWAYLAND_H
|
#define CG_XWAYLAND_H
|
||||||
|
|
||||||
#include <wayland-server-core.h>
|
#include <wayland-server.h>
|
||||||
#include <wlr/xwayland.h>
|
#include <wlr/xwayland.h>
|
||||||
|
|
||||||
#include "view.h"
|
#include "view.h"
|
||||||
|
|
@ -9,11 +9,25 @@
|
||||||
struct cg_xwayland_view {
|
struct cg_xwayland_view {
|
||||||
struct cg_view view;
|
struct cg_view view;
|
||||||
struct wlr_xwayland_surface *xwayland_surface;
|
struct wlr_xwayland_surface *xwayland_surface;
|
||||||
|
|
||||||
|
/* Some applications that aren't yet Wayland-native or
|
||||||
|
otherwise "special" (e.g. Firefox Nightly and Google
|
||||||
|
Chrome/Chromium) spawn an XWayland surface upon startup
|
||||||
|
that is almost immediately closed again. This makes Cage
|
||||||
|
think there are no views left, which results in it
|
||||||
|
exiting. However, after this initial (unmapped) surface,
|
||||||
|
the "real" application surface is opened. This leads to
|
||||||
|
these applications' startup sequences being interrupted by
|
||||||
|
Cage exiting. Hence, to work around this issue, Cage checks
|
||||||
|
whether an XWayland surface has ever been mapped and exits
|
||||||
|
only if 1) the XWayland surface has ever been mapped and 2)
|
||||||
|
this was the last surface Cage manages. */
|
||||||
|
bool ever_been_mapped;
|
||||||
|
|
||||||
struct wl_listener destroy;
|
struct wl_listener destroy;
|
||||||
struct wl_listener associate;
|
|
||||||
struct wl_listener dissociate;
|
|
||||||
struct wl_listener unmap;
|
struct wl_listener unmap;
|
||||||
struct wl_listener map;
|
struct wl_listener map;
|
||||||
|
struct wl_listener commit;
|
||||||
struct wl_listener request_fullscreen;
|
struct wl_listener request_fullscreen;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue