mirror of
				https://github.com/cage-kiosk/cage.git
				synced 2025-11-03 09:01:43 -05:00 
			
		
		
		
	Compare commits
	
		
			No commits in common. "master" and "v0.1.2.1" have entirely different histories.
		
	
	
		
	
		
					 31 changed files with 1520 additions and 1302 deletions
				
			
		
							
								
								
									
										34
									
								
								.builds/alpine.yml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								.builds/alpine.yml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,34 @@
 | 
				
			||||||
 | 
					image: alpine/edge
 | 
				
			||||||
 | 
					packages:
 | 
				
			||||||
 | 
					  - eudev-dev
 | 
				
			||||||
 | 
					  - mesa-dev
 | 
				
			||||||
 | 
					  - meson
 | 
				
			||||||
 | 
					  - libinput-dev
 | 
				
			||||||
 | 
					  - libxkbcommon-dev
 | 
				
			||||||
 | 
					  - pixman-dev
 | 
				
			||||||
 | 
					  - scdoc
 | 
				
			||||||
 | 
					  - 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
 | 
				
			||||||
 | 
					      git checkout 0.11.0
 | 
				
			||||||
 | 
					      meson --prefix=/usr build -Dexamples=false
 | 
				
			||||||
 | 
					      ninja -C build
 | 
				
			||||||
 | 
					      sudo ninja -C build install
 | 
				
			||||||
 | 
					  - build: |
 | 
				
			||||||
 | 
					      cd cage
 | 
				
			||||||
 | 
					      meson build --werror -Dxwayland=true
 | 
				
			||||||
 | 
					      ninja -C build
 | 
				
			||||||
 | 
					      rm -rf build
 | 
				
			||||||
 | 
					  - build-no-xwayland: |
 | 
				
			||||||
 | 
					      cd cage
 | 
				
			||||||
 | 
					      meson build --werror -Dxwayland=false
 | 
				
			||||||
 | 
					      ninja -C build
 | 
				
			||||||
 | 
					      rm -rf build
 | 
				
			||||||
							
								
								
									
										43
									
								
								.builds/archlinux.yml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								.builds/archlinux.yml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,43 @@
 | 
				
			||||||
 | 
					image: archlinux
 | 
				
			||||||
 | 
					packages:
 | 
				
			||||||
 | 
					  - clang
 | 
				
			||||||
 | 
					  - meson
 | 
				
			||||||
 | 
					  - libinput
 | 
				
			||||||
 | 
					  - libxkbcommon
 | 
				
			||||||
 | 
					  - scdoc
 | 
				
			||||||
 | 
					  - 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
 | 
				
			||||||
 | 
					      git checkout 0.11.0
 | 
				
			||||||
 | 
					      meson --prefix=/usr build -Dexamples=false
 | 
				
			||||||
 | 
					      ninja -C build
 | 
				
			||||||
 | 
					      sudo ninja -C build install
 | 
				
			||||||
 | 
					  - build: |
 | 
				
			||||||
 | 
					      cd cage
 | 
				
			||||||
 | 
					      meson build --werror -Dxwayland=true
 | 
				
			||||||
 | 
					      ninja -C build
 | 
				
			||||||
 | 
					      rm -rf build
 | 
				
			||||||
 | 
					  - build-no-xwayland: |
 | 
				
			||||||
 | 
					      cd cage
 | 
				
			||||||
 | 
					      meson build --werror -Dxwayland=false
 | 
				
			||||||
 | 
					      ninja -C build
 | 
				
			||||||
 | 
					      rm -rf build
 | 
				
			||||||
 | 
					  - scan-build: |
 | 
				
			||||||
 | 
					      cd cage
 | 
				
			||||||
 | 
					      CC=clang meson build --werror -Dxwayland=true
 | 
				
			||||||
 | 
					      CC=clang ninja -C build scan-build
 | 
				
			||||||
 | 
					      rm -rf build
 | 
				
			||||||
 | 
					  - clang-format: |
 | 
				
			||||||
 | 
					      cd cage
 | 
				
			||||||
 | 
					      meson build --werror -Dxwayland=true
 | 
				
			||||||
 | 
					      ninja -C build clang-format
 | 
				
			||||||
 | 
					      rm -rf build
 | 
				
			||||||
 | 
					      git diff --exit-code
 | 
				
			||||||
							
								
								
									
										35
									
								
								.builds/freebsd.yml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								.builds/freebsd.yml
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,35 @@
 | 
				
			||||||
 | 
					image: freebsd/latest
 | 
				
			||||||
 | 
					packages:
 | 
				
			||||||
 | 
					  - devel/evdev-proto
 | 
				
			||||||
 | 
					  - devel/meson
 | 
				
			||||||
 | 
					  - devel/libepoll-shim
 | 
				
			||||||
 | 
					  - devel/pkgconf
 | 
				
			||||||
 | 
					  - graphics/mesa-libs
 | 
				
			||||||
 | 
					  - graphics/wayland
 | 
				
			||||||
 | 
					  - graphics/wayland-protocols
 | 
				
			||||||
 | 
					  - textproc/scdoc
 | 
				
			||||||
 | 
					  - x11/libinput
 | 
				
			||||||
 | 
					  - x11/libxkbcommon
 | 
				
			||||||
 | 
					  - x11/pixman
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
					      git checkout 0.11.0
 | 
				
			||||||
 | 
					      meson --prefix=/usr/local build -Dexamples=false
 | 
				
			||||||
 | 
					      ninja -C build
 | 
				
			||||||
 | 
					      sudo ninja -C build install
 | 
				
			||||||
 | 
					  - build: |
 | 
				
			||||||
 | 
					      cd cage
 | 
				
			||||||
 | 
					      PKG_CONFIG_PATH=/usr/local/lib/pkgconfig meson build --werror -Dxwayland=true
 | 
				
			||||||
 | 
					      PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ninja -C build
 | 
				
			||||||
 | 
					      rm -rf build
 | 
				
			||||||
 | 
					  - build-no-xwayland: |
 | 
				
			||||||
 | 
					      cd cage
 | 
				
			||||||
 | 
					      PKG_CONFIG_PATH=/usr/local/lib/pkgconfig meson build --werror -Dxwayland=false
 | 
				
			||||||
 | 
					      PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ninja -C build
 | 
				
			||||||
 | 
					      rm -rf build
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
					 | 
				
			||||||
							
								
								
									
										39
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										39
									
								
								README.md
									
										
									
									
									
								
							| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
# 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">
 | 
					<img src="https://www.hjdskes.nl/img/projects/cage/cage.svg" alt="Cage's logo" width="150px" align="right">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,54 +8,57 @@ application.
 | 
				
			||||||
This README is only relevant for development resources and instructions. For a
 | 
					This README is only relevant for development resources and instructions. For a
 | 
				
			||||||
description of Cage and installation instructions for end-users, please see
 | 
					description of Cage and installation instructions for end-users, please see
 | 
				
			||||||
[its project page](https://www.hjdskes.nl/projects/cage) and [the
 | 
					[its project page](https://www.hjdskes.nl/projects/cage) and [the
 | 
				
			||||||
Wiki](https://github.com/cage-kiosk/cage/wiki/).
 | 
					Wiki](https://github.com/Hjdskes/cage/wiki/).
 | 
				
			||||||
See [the man page](./cage.1.scd) for a list of possible environment variables and run options.
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Release signatures
 | 
					## 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
 | 
					Releases are signed with
 | 
				
			||||||
[E88F5E48](https://keys.openpgp.org/search?q=34FF9526CFEF0E97A340E2E40FDE7BE0E88F5E48)
 | 
					[6EBC43B1](http://keys.gnupg.net/pks/lookup?op=vindex&fingerprint=on&search=0x37C445296EBC43B1)
 | 
				
			||||||
All releases are published on [GitHub](https://github.com/cage-kiosk/cage/releases).
 | 
					and published on [GitHub](https://github.com/Hjdskes/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. Optionally, install
 | 
				
			||||||
scdoc for manual pages. Cage is currently based on branch 0.18 of wlroots.
 | 
					scdoc for manual pages. Note that Cage is 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
 | 
					By default, this builds a debug build. To build a release build, use `meson
 | 
				
			||||||
setup build --buildtype=release`.
 | 
					build --buildtype=release`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Cage comes with compile-time support for XWayland. To enable this, make sure
 | 
					Cage comes with compile-time support for XWayland. To enable this,
 | 
				
			||||||
that your version of wlroots is compiled with this option. Note that you'll
 | 
					first make sure that your version of wlroots is compiled with this
 | 
				
			||||||
need to have the XWayland binary installed on your system for this to work.
 | 
					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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
You can run Cage by running `./build/cage APPLICATION`. If you run it from
 | 
					You can run Cage by running `./build/cage APPLICATION`. If you run it from
 | 
				
			||||||
within an existing X11 or Wayland session, it will open in a virtual output as
 | 
					within an existing X11 or Wayland session, it will open in a virtual output as
 | 
				
			||||||
a window in your existing session. If you run it at a TTY, it'll run with the
 | 
					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
 | 
					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
 | 
					<kbd>Alt</kbd>+<kbd>Esc</kbd> to quit. For more configuration options, see
 | 
				
			||||||
[Configuration](https://github.com/cage-kiosk/cage/wiki/Configuration).
 | 
					[Configuration](https://github.com/Hjdskes/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-2020 Jente Hidskes <dev@hjdskes.nl>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										11
									
								
								cage.1.scd
									
										
									
									
									
								
							
							
						
						
									
										11
									
								
								cage.1.scd
									
										
									
									
									
								
							| 
						 | 
					@ -6,7 +6,7 @@ cage - a Wayland kiosk compositor
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# SYNOPSIS
 | 
					# SYNOPSIS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*cage* [options...] [--] [_application_...]
 | 
					*cage* [-dhmrsv] [--] _application_ [application argument ...]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# DESCRIPTION
 | 
					# DESCRIPTION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,9 +19,6 @@ activities outside the scope of the running application are prevented.
 | 
				
			||||||
*-d*
 | 
					*-d*
 | 
				
			||||||
	Don't draw client side decorations when possible.
 | 
						Don't draw client side decorations when possible.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*-D*
 | 
					 | 
				
			||||||
	Enable debug logging.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
*-h*
 | 
					*-h*
 | 
				
			||||||
	Show the help message.
 | 
						Show the help message.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,6 +27,10 @@ activities outside the scope of the running application are prevented.
 | 
				
			||||||
	*last* Cage uses only the last connected monitor.
 | 
						*last* Cage uses only the last connected monitor.
 | 
				
			||||||
	*extend* Cage extends the display across all connected monitors.
 | 
						*extend* Cage extends the display across all connected monitors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*-r*
 | 
				
			||||||
 | 
						Rotate the output 90 degrees clockwise. This can be specified up to three
 | 
				
			||||||
 | 
						times, each resulting in an additional 90 degrees clockwise rotation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
*-s*
 | 
					*-s*
 | 
				
			||||||
	Allow VT switching
 | 
						Allow VT switching
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -64,7 +65,7 @@ _XKB_DEFAULT_VARIANT_, _XKB_DEFAULT_OPTIONS_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# BUGS
 | 
					# BUGS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Report bugs at https://github.com/cage-kiosk/cage
 | 
					Report bugs at https://github.com/Hjdskes/cage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# AUTHORS
 | 
					# AUTHORS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										347
									
								
								cage.c
									
										
									
									
									
								
							
							
						
						
									
										347
									
								
								cage.c
									
										
									
									
									
								
							| 
						 | 
					@ -19,27 +19,16 @@
 | 
				
			||||||
#include <unistd.h>
 | 
					#include <unistd.h>
 | 
				
			||||||
#include <wayland-server-core.h>
 | 
					#include <wayland-server-core.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_export_dmabuf_v1.h>
 | 
				
			||||||
#include <wlr/types/wlr_gamma_control_v1.h>
 | 
					#include <wlr/types/wlr_gamma_control_v1.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_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_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
 | 
				
			||||||
| 
						 | 
					@ -61,28 +50,10 @@
 | 
				
			||||||
#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
 | 
					static int
 | 
				
			||||||
sigchld_handler(int fd, uint32_t mask, void *data)
 | 
					sigchld_handler(int fd, uint32_t mask, void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct cg_server *server = data;
 | 
						struct wl_display *display = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Close Cage's read pipe. */
 | 
						/* Close Cage's read pipe. */
 | 
				
			||||||
	close(fd);
 | 
						close(fd);
 | 
				
			||||||
| 
						 | 
					@ -93,8 +64,7 @@ sigchld_handler(int fd, uint32_t mask, void *data)
 | 
				
			||||||
		wlr_log(WLR_DEBUG, "Connection closed by server");
 | 
							wlr_log(WLR_DEBUG, "Connection closed by server");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	server->return_app_code = true;
 | 
						wl_display_terminate(display);
 | 
				
			||||||
	server_terminate(server);
 | 
					 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -118,7 +88,7 @@ set_cloexec(int fd)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool
 | 
					static bool
 | 
				
			||||||
spawn_primary_client(struct cg_server *server, char *argv[], pid_t *pid_out, struct wl_event_source **sigchld_source)
 | 
					spawn_primary_client(struct wl_display *display, char *argv[], pid_t *pid_out, struct wl_event_source **sigchld_source)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int fd[2];
 | 
						int fd[2];
 | 
				
			||||||
	if (pipe(fd) != 0) {
 | 
						if (pipe(fd) != 0) {
 | 
				
			||||||
| 
						 | 
					@ -134,8 +104,6 @@ spawn_primary_client(struct cg_server *server, char *argv[], pid_t *pid_out, str
 | 
				
			||||||
		/* Close read, we only need write in the primary client process. */
 | 
							/* Close read, we only need write in the primary client process. */
 | 
				
			||||||
		close(fd[0]);
 | 
							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");
 | 
				
			||||||
| 
						 | 
					@ -152,15 +120,15 @@ spawn_primary_client(struct cg_server *server, char *argv[], pid_t *pid_out, str
 | 
				
			||||||
	/* Close write, we only need read in Cage. */
 | 
						/* Close write, we only need read in Cage. */
 | 
				
			||||||
	close(fd[1]);
 | 
						close(fd[1]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct wl_event_loop *event_loop = wl_display_get_event_loop(server->wl_display);
 | 
						struct wl_event_loop *event_loop = wl_display_get_event_loop(display);
 | 
				
			||||||
	uint32_t mask = WL_EVENT_HANGUP | WL_EVENT_ERROR;
 | 
						uint32_t mask = WL_EVENT_HANGUP | WL_EVENT_ERROR;
 | 
				
			||||||
	*sigchld_source = wl_event_loop_add_fd(event_loop, fd[0], mask, sigchld_handler, server);
 | 
						*sigchld_source = wl_event_loop_add_fd(event_loop, fd[0], mask, sigchld_handler, display);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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
 | 
					static void
 | 
				
			||||||
cleanup_primary_client(pid_t pid)
 | 
					cleanup_primary_client(pid_t pid)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int status;
 | 
						int status;
 | 
				
			||||||
| 
						 | 
					@ -169,25 +137,15 @@ cleanup_primary_client(pid_t pid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (WIFEXITED(status)) {
 | 
						if (WIFEXITED(status)) {
 | 
				
			||||||
		wlr_log(WLR_DEBUG, "Child exited normally with exit status %d", WEXITSTATUS(status));
 | 
							wlr_log(WLR_DEBUG, "Child exited normally with exit status %d", WEXITSTATUS(status));
 | 
				
			||||||
		return WEXITSTATUS(status);
 | 
					 | 
				
			||||||
	} else if (WIFSIGNALED(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));
 | 
							wlr_log(WLR_DEBUG, "Child was terminated by a signal (%d)", WTERMSIG(status));
 | 
				
			||||||
		return 128 + WTERMSIG(status);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	return 0;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool
 | 
					static bool
 | 
				
			||||||
drop_permissions(void)
 | 
					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()) {
 | 
						if (getuid() != geteuid() || getgid() != getegid()) {
 | 
				
			||||||
		wlr_log(WLR_INFO, "setuid/setgid bit detected, dropping permissions");
 | 
					 | 
				
			||||||
		// Set the gid and uid in the correct order.
 | 
							// Set the gid and uid in the correct order.
 | 
				
			||||||
		if (setgid(getgid()) != 0 || setuid(getuid()) != 0) {
 | 
							if (setgid(getgid()) != 0 || setuid(getuid()) != 0) {
 | 
				
			||||||
			wlr_log(WLR_ERROR, "Unable to drop root, refusing to start");
 | 
								wlr_log(WLR_ERROR, "Unable to drop root, refusing to start");
 | 
				
			||||||
| 
						 | 
					@ -207,13 +165,13 @@ drop_permissions(void)
 | 
				
			||||||
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 0;
 | 
				
			||||||
| 
						 | 
					@ -224,13 +182,16 @@ 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"
 | 
					#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 extend Extend the display across all connected outputs (default)\n"
 | 
				
			||||||
		" -m last Use only the last connected output\n"
 | 
							" -m last Use only the last connected output\n"
 | 
				
			||||||
 | 
							" -r\t Rotate the output 90 degrees clockwise, specify up to three times\n"
 | 
				
			||||||
		" -s\t Allow VT switching\n"
 | 
							" -s\t Allow VT switching\n"
 | 
				
			||||||
		" -v\t Show the version number and exit\n"
 | 
							" -v\t Show the version number and exit\n"
 | 
				
			||||||
		"\n"
 | 
							"\n"
 | 
				
			||||||
| 
						 | 
					@ -242,14 +203,20 @@ 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, "dDhm:rsv")) != -1) {
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
						while ((c = getopt(argc, argv, "dhm:rsv")) != -1) {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
		switch (c) {
 | 
							switch (c) {
 | 
				
			||||||
		case 'd':
 | 
							case 'd':
 | 
				
			||||||
			server->xdg_decoration = true;
 | 
								server->xdg_decoration = true;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					#ifdef DEBUG
 | 
				
			||||||
		case 'D':
 | 
							case 'D':
 | 
				
			||||||
			server->log_level = WLR_DEBUG;
 | 
								server->debug_damage_tracking = true;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
		case 'h':
 | 
							case 'h':
 | 
				
			||||||
			usage(stdout, argv[0]);
 | 
								usage(stdout, argv[0]);
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
| 
						 | 
					@ -260,6 +227,12 @@ parse_args(struct cg_server *server, int argc, char *argv[])
 | 
				
			||||||
				server->output_mode = CAGE_MULTI_OUTPUT_MODE_EXTEND;
 | 
									server->output_mode = CAGE_MULTI_OUTPUT_MODE_EXTEND;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
							case 'r':
 | 
				
			||||||
 | 
								server->output_transform++;
 | 
				
			||||||
 | 
								if (server->output_transform > WL_OUTPUT_TRANSFORM_270) {
 | 
				
			||||||
 | 
									server->output_transform = WL_OUTPUT_TRANSFORM_NORMAL;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
		case 's':
 | 
							case 's':
 | 
				
			||||||
			server->allow_vt_switch = true;
 | 
								server->allow_vt_switch = true;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
| 
						 | 
					@ -272,26 +245,48 @@ parse_args(struct cg_server *server, int argc, char *argv[])
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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_loop *event_loop = NULL;
 | 
				
			||||||
 | 
						struct wl_event_source *sigint_source = NULL;
 | 
				
			||||||
 | 
						struct wl_event_source *sigterm_source = NULL;
 | 
				
			||||||
	struct wl_event_source *sigchld_source = NULL;
 | 
						struct wl_event_source *sigchld_source = NULL;
 | 
				
			||||||
	pid_t pid = 0;
 | 
						struct wlr_renderer *renderer = NULL;
 | 
				
			||||||
	int ret = 0, app_ret = 0;
 | 
						struct wlr_compositor *compositor = NULL;
 | 
				
			||||||
 | 
						struct wlr_data_device_manager *data_device_manager = NULL;
 | 
				
			||||||
#ifdef DEBUG
 | 
						struct wlr_server_decoration_manager *server_decoration_manager = NULL;
 | 
				
			||||||
	server.log_level = WLR_DEBUG;
 | 
						struct wlr_xdg_decoration_manager_v1 *xdg_decoration_manager = NULL;
 | 
				
			||||||
 | 
						struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager = NULL;
 | 
				
			||||||
 | 
						struct wlr_screencopy_manager_v1 *screencopy_manager = NULL;
 | 
				
			||||||
 | 
						struct wlr_xdg_output_manager_v1 *output_manager = NULL;
 | 
				
			||||||
 | 
						struct wlr_gamma_control_manager_v1 *gamma_control_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
 | 
				
			||||||
 | 
						pid_t pid = 0;
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
						wlr_log_init(WLR_ERROR, NULL);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Wayland requires XDG_RUNTIME_DIR to be set. */
 | 
						/* Wayland requires XDG_RUNTIME_DIR to be set. */
 | 
				
			||||||
	if (!getenv("XDG_RUNTIME_DIR")) {
 | 
						if (!getenv("XDG_RUNTIME_DIR")) {
 | 
				
			||||||
| 
						 | 
					@ -305,14 +300,11 @@ 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;
 | 
				
			||||||
| 
						 | 
					@ -324,68 +316,33 @@ main(int argc, char *argv[])
 | 
				
			||||||
		goto end;
 | 
							goto end;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	server.renderer = wlr_renderer_autocreate(server.backend);
 | 
						renderer = wlr_backend_get_renderer(server.backend);
 | 
				
			||||||
	if (!server.renderer) {
 | 
						wlr_renderer_init_wl_display(renderer, server.wl_display);
 | 
				
			||||||
		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);
 | 
						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_manager = wlr_data_device_manager_create(server.wl_display);
 | 
				
			||||||
		wlr_log(WLR_ERROR, "Unable to create the wlroots subcompositor");
 | 
						if (!data_device_manager) {
 | 
				
			||||||
		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. */
 | 
				
			||||||
| 
						 | 
					@ -399,7 +356,7 @@ main(int argc, char *argv[])
 | 
				
			||||||
		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 +373,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,8 +391,7 @@ 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;
 | 
				
			||||||
| 
						 | 
					@ -448,117 +401,65 @@ main(int argc, char *argv[])
 | 
				
			||||||
		server_decoration_manager, server.xdg_decoration ? WLR_SERVER_DECORATION_MANAGER_MODE_SERVER
 | 
							server_decoration_manager, server.xdg_decoration ? WLR_SERVER_DECORATION_MANAGER_MODE_SERVER
 | 
				
			||||||
								 : WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT);
 | 
													 : WLR_SERVER_DECORATION_MANAGER_MODE_CLIENT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!wlr_viewporter_create(server.wl_display)) {
 | 
						export_dmabuf_manager = wlr_export_dmabuf_manager_v1_create(server.wl_display);
 | 
				
			||||||
		wlr_log(WLR_ERROR, "Unable to create the viewporter interface");
 | 
						if (!export_dmabuf_manager) {
 | 
				
			||||||
		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");
 | 
							wlr_log(WLR_ERROR, "Unable to create the export DMABUF manager");
 | 
				
			||||||
		ret = 1;
 | 
							ret = 1;
 | 
				
			||||||
		goto end;
 | 
							goto end;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!wlr_screencopy_manager_v1_create(server.wl_display)) {
 | 
						screencopy_manager = wlr_screencopy_manager_v1_create(server.wl_display);
 | 
				
			||||||
 | 
						if (!screencopy_manager) {
 | 
				
			||||||
		wlr_log(WLR_ERROR, "Unable to create the screencopy manager");
 | 
							wlr_log(WLR_ERROR, "Unable to create the screencopy manager");
 | 
				
			||||||
		ret = 1;
 | 
							ret = 1;
 | 
				
			||||||
		goto end;
 | 
							goto end;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!wlr_single_pixel_buffer_manager_v1_create(server.wl_display)) {
 | 
						output_manager = wlr_xdg_output_manager_v1_create(server.wl_display, server.output_layout);
 | 
				
			||||||
		wlr_log(WLR_ERROR, "Unable to create the single pixel buffer manager");
 | 
						if (!output_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");
 | 
							wlr_log(WLR_ERROR, "Unable to create the output manager");
 | 
				
			||||||
		ret = 1;
 | 
							ret = 1;
 | 
				
			||||||
		goto end;
 | 
							goto end;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	server.output_manager_v1 = wlr_output_manager_v1_create(server.wl_display);
 | 
						gamma_control_manager = wlr_gamma_control_manager_v1_create(server.wl_display);
 | 
				
			||||||
	if (!server.output_manager_v1) {
 | 
						if (!gamma_control_manager) {
 | 
				
			||||||
		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");
 | 
							wlr_log(WLR_ERROR, "Unable to create the gamma control manager");
 | 
				
			||||||
		ret = 1;
 | 
							ret = 1;
 | 
				
			||||||
		goto end;
 | 
							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 = wlr_xcursor_manager_get_xcursor(xcursor_manager, DEFAULT_XCURSOR, 1);
 | 
				
			||||||
			goto end;
 | 
						if (xcursor) {
 | 
				
			||||||
		}
 | 
							struct wlr_xcursor_image *image = xcursor->images[0];
 | 
				
			||||||
 | 
							wlr_xwayland_set_cursor(xwayland, image->buffer, image->width * 4, image->width, image->height,
 | 
				
			||||||
		if (setenv("DISPLAY", xwayland->display_name, true) < 0) {
 | 
										image->hotspot_x, image->hotspot_y);
 | 
				
			||||||
			wlr_log_errno(WLR_ERROR,
 | 
					 | 
				
			||||||
				      "Unable to set DISPLAY for XWayland. Clients may not be able to connect");
 | 
					 | 
				
			||||||
		} 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -582,44 +483,28 @@ main(int argc, char *argv[])
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#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)) {
 | 
						if (!spawn_primary_client(server.wl_display, argv + optind, &pid, &sigchld_source)) {
 | 
				
			||||||
		ret = 1;
 | 
							ret = 1;
 | 
				
			||||||
		goto end;
 | 
							goto end;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	seat_center_cursor(server.seat);
 | 
						/* Place the cursor in the center of the output layout. */
 | 
				
			||||||
 | 
						struct wlr_box *layout_box = wlr_output_layout_get_box(server.output_layout, NULL);
 | 
				
			||||||
 | 
						wlr_cursor_warp(server.seat->cursor, NULL, layout_box->width / 2, layout_box->height / 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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);
 | 
					 | 
				
			||||||
	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)
 | 
						cleanup_primary_client(pid);
 | 
				
			||||||
		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);
 | 
				
			||||||
| 
						 | 
					@ -630,8 +515,6 @@ end:
 | 
				
			||||||
	/* 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_output_layout_destroy(server.output_layout);
 | 
				
			||||||
	wlr_allocator_destroy(server.allocator);
 | 
					 | 
				
			||||||
	wlr_renderer_destroy(server.renderer);
 | 
					 | 
				
			||||||
	return ret;
 | 
						return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 -
 | 
					 | 
				
			||||||
| 
						 | 
					@ -8,8 +8,8 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <wayland-server-core.h>
 | 
					#include <wayland-server-core.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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										50
									
								
								meson.build
									
										
									
									
									
								
							
							
						
						
									
										50
									
								
								meson.build
									
										
									
									
									
								
							| 
						 | 
					@ -1,17 +1,16 @@
 | 
				
			||||||
project('cage', 'c',
 | 
					project('cage', 'c',
 | 
				
			||||||
  version: '0.2.1',
 | 
					  version: '0.1.2',
 | 
				
			||||||
  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',
 | 
					 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_project_arguments(
 | 
					add_project_arguments(
 | 
				
			||||||
  [
 | 
					  [
 | 
				
			||||||
    '-DWLR_USE_UNSTABLE',
 | 
					    '-DWLR_USE_UNSTABLE',
 | 
				
			||||||
 | 
					    '-Wall',
 | 
				
			||||||
    '-Wundef',
 | 
					    '-Wundef',
 | 
				
			||||||
    '-Wno-unused-parameter',
 | 
					    '-Wno-unused-parameter',
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
| 
						 | 
					@ -35,13 +34,14 @@ if is_freebsd
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
wlroots        = dependency('wlroots-0.19', fallback: ['wlroots', 'wlroots'])
 | 
					wlroots        = dependency('wlroots', version: '>= 0.11.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,13 +64,22 @@ 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'
 | 
				
			||||||
 | 
					  if not wlroots_has_xwayland
 | 
				
			||||||
 | 
					    error('Cannot build Cage with XWayland support: wlroots has been built without it')
 | 
				
			||||||
 | 
					  else
 | 
				
			||||||
 | 
					    have_xwayland = true
 | 
				
			||||||
 | 
					  endif
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					  have_xwayland = false
 | 
				
			||||||
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
version = '@0@'.format(meson.project_version())
 | 
					version = '@0@'.format(meson.project_version())
 | 
				
			||||||
git = find_program('git', native: true, required: false)
 | 
					git = find_program('git', native: true, required: false)
 | 
				
			||||||
if git.found()
 | 
					if git.found()
 | 
				
			||||||
  git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'], check: false)
 | 
					  git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'])
 | 
				
			||||||
  git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'], check: false)
 | 
					  git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'])
 | 
				
			||||||
  if git_commit.returncode() == 0 and git_branch.returncode() == 0
 | 
					  if git_commit.returncode() == 0 and git_branch.returncode() == 0
 | 
				
			||||||
    version = '@0@-@1@ (branch \'@2@\')'.format(
 | 
					    version = '@0@-@1@ (branch \'@2@\')'.format(
 | 
				
			||||||
      meson.project_version(),
 | 
					      meson.project_version(),
 | 
				
			||||||
| 
						 | 
					@ -86,7 +95,7 @@ conf_data.set_quoted('CAGE_VERSION', version)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages'))
 | 
					scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages'))
 | 
				
			||||||
if scdoc.found()
 | 
					if scdoc.found()
 | 
				
			||||||
  scdoc_prog = find_program(scdoc.get_variable('scdoc'), native: true)
 | 
					  scdoc_prog = find_program(scdoc.get_pkgconfig_variable('scdoc'), native: true)
 | 
				
			||||||
  sh = find_program('sh', native: true)
 | 
					  sh = find_program('sh', native: true)
 | 
				
			||||||
  mandir = get_option('mandir')
 | 
					  mandir = get_option('mandir')
 | 
				
			||||||
  man_files = [
 | 
					  man_files = [
 | 
				
			||||||
| 
						 | 
					@ -102,7 +111,7 @@ if scdoc.found()
 | 
				
			||||||
      input: filename,
 | 
					      input: filename,
 | 
				
			||||||
      output: output,
 | 
					      output: output,
 | 
				
			||||||
      command: [
 | 
					      command: [
 | 
				
			||||||
        sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.full_path(), output)
 | 
					        sh, '-c', '@0@ < @INPUT@ > @1@'.format(scdoc_prog.path(), output)
 | 
				
			||||||
      ],
 | 
					      ],
 | 
				
			||||||
      install: true,
 | 
					      install: true,
 | 
				
			||||||
      install_dir: '@0@/man@1@'.format(mandir, section)
 | 
					      install_dir: '@0@/man@1@'.format(mandir, section)
 | 
				
			||||||
| 
						 | 
					@ -114,19 +123,23 @@ cage_sources = [
 | 
				
			||||||
  'cage.c',
 | 
					  'cage.c',
 | 
				
			||||||
  'idle_inhibit_v1.c',
 | 
					  'idle_inhibit_v1.c',
 | 
				
			||||||
  'output.c',
 | 
					  'output.c',
 | 
				
			||||||
 | 
					  'render.c',
 | 
				
			||||||
  'seat.c',
 | 
					  'seat.c',
 | 
				
			||||||
 | 
					  'util.c',
 | 
				
			||||||
  'view.c',
 | 
					  'view.c',
 | 
				
			||||||
  'xdg_shell.c',
 | 
					  'xdg_shell.c',
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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',
 | 
				
			||||||
 | 
					  'render.h',
 | 
				
			||||||
  'seat.h',
 | 
					  'seat.h',
 | 
				
			||||||
  'server.h',
 | 
					  'server.h',
 | 
				
			||||||
 | 
					  'util.h',
 | 
				
			||||||
  'view.h',
 | 
					  'view.h',
 | 
				
			||||||
  'xdg_shell.h',
 | 
					  'xdg_shell.h',
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
| 
						 | 
					@ -144,16 +157,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(version),
 | 
				
			||||||
  '',
 | 
						'',
 | 
				
			||||||
  '    xwayland: @0@'.format(have_xwayland),
 | 
						'    xwayland: @0@'.format(have_xwayland),
 | 
				
			||||||
  ''
 | 
						''
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
message('\n'.join(summary))
 | 
					message('\n'.join(summary))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1 +1,2 @@
 | 
				
			||||||
option('man-pages', type: 'feature', value: 'auto', description: 'Generate and install man pages')
 | 
					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')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										605
									
								
								output.c
									
										
									
									
									
								
							
							
						
						
									
										605
									
								
								output.c
									
										
									
									
									
								
							| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Cage: A Wayland kiosk.
 | 
					 * Cage: A Wayland kiosk.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Copyright (C) 2018-2021 Jente Hidskes
 | 
					 * Copyright (C) 2018-2020 Jente Hidskes
 | 
				
			||||||
 * Copyright (C) 2019 The Sway authors
 | 
					 * Copyright (C) 2019 The Sway authors
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * See the LICENSE file accompanying this file.
 | 
					 * See the LICENSE file accompanying this file.
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "config.h"
 | 
					#include "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-core.h>
 | 
				
			||||||
| 
						 | 
					@ -21,79 +20,220 @@
 | 
				
			||||||
#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 "render.h"
 | 
				
			||||||
#include "seat.h"
 | 
					#include "seat.h"
 | 
				
			||||||
#include "server.h"
 | 
					#include "server.h"
 | 
				
			||||||
 | 
					#include "util.h"
 | 
				
			||||||
#include "view.h"
 | 
					#include "view.h"
 | 
				
			||||||
#if CAGE_HAS_XWAYLAND
 | 
					#if CAGE_HAS_XWAYLAND
 | 
				
			||||||
#include "xwayland.h"
 | 
					#include "xwayland.h"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define OUTPUT_CONFIG_UPDATED                                                                                          \
 | 
					static void output_for_each_surface(struct cg_output *output, cg_surface_iterator_func_t iterator, void *user_data);
 | 
				
			||||||
	(WLR_OUTPUT_STATE_ENABLED | WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_SCALE | WLR_OUTPUT_STATE_TRANSFORM |      \
 | 
					 | 
				
			||||||
	 WLR_OUTPUT_STATE_ADAPTIVE_SYNC_ENABLED)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					struct surface_iterator_data {
 | 
				
			||||||
update_output_manager_config(struct cg_server *server)
 | 
						cg_surface_iterator_func_t user_iterator;
 | 
				
			||||||
{
 | 
						void *user_data;
 | 
				
			||||||
	struct wlr_output_configuration_v1 *config = wlr_output_configuration_v1_create();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct cg_output *output;
 | 
						struct cg_output *output;
 | 
				
			||||||
	wl_list_for_each (output, &server->outputs, link) {
 | 
					 | 
				
			||||||
		struct wlr_output *wlr_output = output->wlr_output;
 | 
					 | 
				
			||||||
		struct wlr_output_configuration_head_v1 *config_head =
 | 
					 | 
				
			||||||
			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);
 | 
						/* Output-local coordinates. */
 | 
				
			||||||
		if (!wlr_box_empty(&output_box)) {
 | 
						double ox, oy;
 | 
				
			||||||
			config_head->state.x = output_box.x;
 | 
					};
 | 
				
			||||||
			config_head->state.y = output_box.y;
 | 
					
 | 
				
			||||||
 | 
					static bool
 | 
				
			||||||
 | 
					intersects_with_output(struct cg_output *output, struct wlr_output_layout *output_layout, struct wlr_box *surface_box)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Since the surface_box's x- and y-coordinates are already output local,
 | 
				
			||||||
 | 
						 * the x- and y-coordinates of this box need to be 0 for this function to
 | 
				
			||||||
 | 
						 * work correctly. */
 | 
				
			||||||
 | 
						struct wlr_box output_box = {0};
 | 
				
			||||||
 | 
						wlr_output_effective_resolution(output->wlr_output, &output_box.width, &output_box.height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct wlr_box intersection;
 | 
				
			||||||
 | 
						return wlr_box_intersection(&intersection, &output_box, surface_box);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					output_for_each_surface_iterator(struct wlr_surface *surface, int sx, int sy, void *user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct surface_iterator_data *data = user_data;
 | 
				
			||||||
 | 
						struct cg_output *output = data->output;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!wlr_surface_has_buffer(surface)) {
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct wlr_box surface_box = {
 | 
				
			||||||
 | 
							.x = data->ox + sx + surface->sx,
 | 
				
			||||||
 | 
							.y = data->oy + sy + surface->sy,
 | 
				
			||||||
 | 
							.width = surface->current.width,
 | 
				
			||||||
 | 
							.height = surface->current.height,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!intersects_with_output(output, output->server->output_layout, &surface_box)) {
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data->user_iterator(data->output, surface, &surface_box, data->user_data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					output_surface_for_each_surface(struct cg_output *output, struct wlr_surface *surface, double ox, double oy,
 | 
				
			||||||
 | 
									cg_surface_iterator_func_t iterator, void *user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct surface_iterator_data data = {
 | 
				
			||||||
 | 
							.user_iterator = iterator,
 | 
				
			||||||
 | 
							.user_data = user_data,
 | 
				
			||||||
 | 
							.output = output,
 | 
				
			||||||
 | 
							.ox = ox,
 | 
				
			||||||
 | 
							.oy = oy,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wlr_surface_for_each_surface(surface, output_for_each_surface_iterator, &data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					output_view_for_each_surface(struct cg_output *output, struct cg_view *view, cg_surface_iterator_func_t iterator,
 | 
				
			||||||
 | 
								     void *user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct surface_iterator_data data = {
 | 
				
			||||||
 | 
							.user_iterator = iterator,
 | 
				
			||||||
 | 
							.user_data = user_data,
 | 
				
			||||||
 | 
							.output = output,
 | 
				
			||||||
 | 
							.ox = view->lx,
 | 
				
			||||||
 | 
							.oy = view->ly,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wlr_output_layout_output_coords(output->server->output_layout, output->wlr_output, &data.ox, &data.oy);
 | 
				
			||||||
 | 
						view_for_each_surface(view, output_for_each_surface_iterator, &data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					output_view_for_each_popup(struct cg_output *output, struct cg_view *view, cg_surface_iterator_func_t iterator,
 | 
				
			||||||
 | 
								   void *user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct surface_iterator_data data = {
 | 
				
			||||||
 | 
							.user_iterator = iterator,
 | 
				
			||||||
 | 
							.user_data = user_data,
 | 
				
			||||||
 | 
							.output = output,
 | 
				
			||||||
 | 
							.ox = view->lx,
 | 
				
			||||||
 | 
							.oy = view->ly,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wlr_output_layout_output_coords(output->server->output_layout, output->wlr_output, &data.ox, &data.oy);
 | 
				
			||||||
 | 
						view_for_each_popup(view, output_for_each_surface_iterator, &data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					output_drag_icons_for_each_surface(struct cg_output *output, struct wl_list *drag_icons,
 | 
				
			||||||
 | 
									   cg_surface_iterator_func_t iterator, void *user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct cg_drag_icon *drag_icon;
 | 
				
			||||||
 | 
						wl_list_for_each (drag_icon, drag_icons, link) {
 | 
				
			||||||
 | 
							if (drag_icon->wlr_drag_icon->mapped) {
 | 
				
			||||||
 | 
								double ox = drag_icon->lx;
 | 
				
			||||||
 | 
								double oy = drag_icon->ly;
 | 
				
			||||||
 | 
								wlr_output_layout_output_coords(output->server->output_layout, output->wlr_output, &ox, &oy);
 | 
				
			||||||
 | 
								output_surface_for_each_surface(output, drag_icon->wlr_drag_icon->surface, ox, oy, iterator,
 | 
				
			||||||
 | 
												user_data);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					output_for_each_surface(struct cg_output *output, cg_surface_iterator_func_t iterator, void *user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct cg_view *view;
 | 
				
			||||||
 | 
						wl_list_for_each_reverse (view, &output->server->views, link) {
 | 
				
			||||||
 | 
							output_view_for_each_surface(output, view, iterator, user_data);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						output_drag_icons_for_each_surface(output, &output->server->seat->drag_icons, iterator, user_data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct send_frame_done_data {
 | 
				
			||||||
 | 
						struct timespec when;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					send_frame_done_iterator(struct cg_output *output, struct wlr_surface *surface, struct wlr_box *box, void *user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct send_frame_done_data *data = user_data;
 | 
				
			||||||
 | 
						wlr_surface_send_frame_done(surface, &data->when);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					send_frame_done(struct cg_output *output, struct send_frame_done_data *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						output_for_each_surface(output, send_frame_done_iterator, data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					count_surface_iterator(struct cg_output *output, struct wlr_surface *surface, struct wlr_box *_box, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						size_t *n = data;
 | 
				
			||||||
 | 
						n++;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool
 | 
				
			||||||
 | 
					scan_out_primary_view(struct cg_output *output)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct cg_server *server = output->server;
 | 
				
			||||||
 | 
						struct wlr_output *wlr_output = output->wlr_output;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct cg_drag_icon *drag_icon;
 | 
				
			||||||
 | 
						wl_list_for_each (drag_icon, &server->seat->drag_icons, link) {
 | 
				
			||||||
 | 
							if (drag_icon->wlr_drag_icon->mapped) {
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_output_manager_v1_set_configuration(server->output_manager_v1, config);
 | 
						struct cg_view *view = seat_get_focus(server->seat);
 | 
				
			||||||
}
 | 
						if (!view || !view->wlr_surface) {
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
static inline void
 | 
					 | 
				
			||||||
output_layout_add_auto(struct cg_output *output)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	assert(output->scene_output != NULL);
 | 
					 | 
				
			||||||
	struct wlr_output_layout_output *layout_output =
 | 
					 | 
				
			||||||
		wlr_output_layout_add_auto(output->server->output_layout, output->wlr_output);
 | 
					 | 
				
			||||||
	wlr_scene_output_layout_add_output(output->server->scene_output_layout, layout_output, output->scene_output);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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
 | 
						size_t n_surfaces = 0;
 | 
				
			||||||
output_layout_remove(struct cg_output *output)
 | 
						output_view_for_each_surface(output, view, count_surface_iterator, &n_surfaces);
 | 
				
			||||||
{
 | 
						if (n_surfaces > 1) {
 | 
				
			||||||
	wlr_output_layout_remove(output->server->output_layout, output->wlr_output);
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if CAGE_HAS_XWAYLAND
 | 
				
			||||||
 | 
						if (view->type == CAGE_XWAYLAND_VIEW) {
 | 
				
			||||||
 | 
							struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
 | 
				
			||||||
 | 
							if (!wl_list_empty(&xwayland_view->xwayland_surface->children)) {
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct wlr_surface *surface = view->wlr_surface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!surface->buffer) {
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((float) surface->current.scale != wlr_output->scale ||
 | 
				
			||||||
 | 
						    surface->current.transform != wlr_output->transform) {
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wlr_output_attach_buffer(wlr_output, &surface->buffer->base);
 | 
				
			||||||
 | 
						return wlr_output_commit(wlr_output);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
| 
						 | 
					@ -106,128 +246,190 @@ output_enable(struct cg_output *output)
 | 
				
			||||||
	 * duplicate the enabled property in cg_output. */
 | 
						 * duplicate the enabled property in cg_output. */
 | 
				
			||||||
	wlr_log(WLR_DEBUG, "Enabling output %s", wlr_output->name);
 | 
						wlr_log(WLR_DEBUG, "Enabling output %s", wlr_output->name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct wlr_output_state state = {0};
 | 
						wlr_output_layout_add_auto(output->server->output_layout, wlr_output);
 | 
				
			||||||
	wlr_output_state_set_enabled(&state, true);
 | 
						wlr_output_enable(wlr_output, true);
 | 
				
			||||||
 | 
						wlr_output_commit(wlr_output);
 | 
				
			||||||
	if (wlr_output_commit_state(wlr_output, &state)) {
 | 
					 | 
				
			||||||
		output_layout_add_auto(output);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	update_output_manager_config(output->server);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
output_disable(struct cg_output *output)
 | 
					output_disable(struct cg_output *output)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct wlr_output *wlr_output = output->wlr_output;
 | 
						struct wlr_output *wlr_output = output->wlr_output;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!wlr_output->enabled) {
 | 
						if (!wlr_output->enabled) {
 | 
				
			||||||
		wlr_log(WLR_DEBUG, "Not disabling already disabled output %s", wlr_output->name);
 | 
							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);
 | 
						wlr_log(WLR_DEBUG, "Disabling output %s", wlr_output->name);
 | 
				
			||||||
	struct wlr_output_state state = {0};
 | 
						wlr_output_enable(wlr_output, false);
 | 
				
			||||||
	wlr_output_state_set_enabled(&state, false);
 | 
						wlr_output_layout_remove(output->server->output_layout, wlr_output);
 | 
				
			||||||
	wlr_output_commit_state(wlr_output, &state);
 | 
						wlr_output_commit(wlr_output);
 | 
				
			||||||
	output_layout_remove(output);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
handle_output_frame(struct wl_listener *listener, void *data)
 | 
					damage_surface_iterator(struct cg_output *output, struct wlr_surface *surface, struct wlr_box *box, void *user_data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct cg_output *output = wl_container_of(listener, output, frame);
 | 
						struct wlr_output *wlr_output = output->wlr_output;
 | 
				
			||||||
 | 
						bool whole = *(bool *) user_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!output->wlr_output->enabled || !output->scene_output) {
 | 
						scale_box(box, output->wlr_output->scale);
 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_scene_output_commit(output->scene_output, NULL);
 | 
						if (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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct timespec now = {0};
 | 
							wlr_region_scale(&damage, &damage, wlr_output->scale);
 | 
				
			||||||
	clock_gettime(CLOCK_MONOTONIC, &now);
 | 
							if (ceil(wlr_output->scale) > surface->current.scale) {
 | 
				
			||||||
	wlr_scene_output_send_frame_done(output->scene_output, &now);
 | 
								/* When scaling up a surface it'll become
 | 
				
			||||||
}
 | 
								   blurry, so we need to expand the damage
 | 
				
			||||||
 | 
								   region. */
 | 
				
			||||||
static void
 | 
								wlr_region_expand(&damage, &damage, ceil(wlr_output->scale) - surface->current.scale);
 | 
				
			||||||
handle_output_commit(struct wl_listener *listener, void *data)
 | 
							}
 | 
				
			||||||
{
 | 
							pixman_region32_translate(&damage, box->x, box->y);
 | 
				
			||||||
	struct cg_output *output = wl_container_of(listener, output, commit);
 | 
							wlr_output_damage_add(output->damage, &damage);
 | 
				
			||||||
	struct wlr_output_event_commit *event = data;
 | 
							pixman_region32_fini(&damage);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* 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
 | 
					 | 
				
			||||||
handle_output_request_state(struct wl_listener *listener, void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	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
 | 
					void
 | 
				
			||||||
handle_output_layout_change(struct wl_listener *listener, void *data)
 | 
					output_damage_surface(struct cg_output *output, struct wlr_surface *surface, double lx, double ly, bool whole)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct cg_server *server = wl_container_of(listener, server, output_layout_change);
 | 
						if (!output->wlr_output->enabled) {
 | 
				
			||||||
 | 
							wlr_log(WLR_DEBUG, "Not adding damage for disabled output %s", output->wlr_output->name);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	view_position_all(server);
 | 
						double ox = lx, oy = ly;
 | 
				
			||||||
	update_output_manager_config(server);
 | 
						wlr_output_layout_output_coords(output->server->output_layout, output->wlr_output, &ox, &oy);
 | 
				
			||||||
 | 
						output_surface_for_each_surface(output, surface, ox, oy, damage_surface_iterator, &whole);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool
 | 
					static void
 | 
				
			||||||
is_nested_output(struct cg_output *output)
 | 
					handle_output_damage_frame(struct wl_listener *listener, void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (wlr_output_is_wl(output->wlr_output)) {
 | 
						struct cg_output *output = wl_container_of(listener, output, damage_frame);
 | 
				
			||||||
		return true;
 | 
						struct send_frame_done_data frame_data = {0};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!output->wlr_output->enabled) {
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
#if WLR_HAS_X11_BACKEND
 | 
					
 | 
				
			||||||
	if (wlr_output_is_x11(output->wlr_output)) {
 | 
						/* Check if we can scan-out the primary view. */
 | 
				
			||||||
		return true;
 | 
						static bool last_scanned_out = false;
 | 
				
			||||||
 | 
						bool scanned_out = scan_out_primary_view(output);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (scanned_out && !last_scanned_out) {
 | 
				
			||||||
 | 
							wlr_log(WLR_DEBUG, "Scanning out primary view");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (last_scanned_out && !scanned_out) {
 | 
				
			||||||
 | 
							wlr_log(WLR_DEBUG, "Stopping primary view scan out");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						last_scanned_out = scanned_out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (scanned_out) {
 | 
				
			||||||
 | 
							goto frame_done;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool needs_frame;
 | 
				
			||||||
 | 
						pixman_region32_t damage;
 | 
				
			||||||
 | 
						pixman_region32_init(&damage);
 | 
				
			||||||
 | 
						if (!wlr_output_damage_attach_render(output->damage, &needs_frame, &damage)) {
 | 
				
			||||||
 | 
							wlr_log(WLR_ERROR, "Cannot make damage output current");
 | 
				
			||||||
 | 
							goto damage_finish;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!needs_frame) {
 | 
				
			||||||
 | 
							wlr_output_rollback(output->wlr_output);
 | 
				
			||||||
 | 
							goto damage_finish;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						output_render(output, &damage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					damage_finish:
 | 
				
			||||||
 | 
						pixman_region32_fini(&damage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					frame_done:
 | 
				
			||||||
 | 
						clock_gettime(CLOCK_MONOTONIC, &frame_data.when);
 | 
				
			||||||
 | 
						send_frame_done(output, &frame_data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					handle_output_transform(struct wl_listener *listener, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct cg_output *output = wl_container_of(listener, output, transform);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!output->wlr_output->enabled) {
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!output->wlr_output->enabled) {
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct cg_view *view;
 | 
				
			||||||
 | 
						wl_list_for_each (view, &output->server->views, link) {
 | 
				
			||||||
 | 
							view_position(view);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
	return false;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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->damage_destroy.link);
 | 
				
			||||||
	wl_list_remove(&output->link);
 | 
						wl_list_remove(&output->link);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	output_layout_remove(output);
 | 
						wlr_output_layout_remove(server->output_layout, output->wlr_output);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	free(output);
 | 
						free(output);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (wl_list_empty(&server->outputs) && was_nested_output) {
 | 
						if (wl_list_empty(&server->outputs)) {
 | 
				
			||||||
		server_terminate(server);
 | 
							wl_display_terminate(server->wl_display);
 | 
				
			||||||
	} else if (server->output_mode == CAGE_MULTI_OUTPUT_MODE_LAST && !wl_list_empty(&server->outputs)) {
 | 
						} else if (server->output_mode == CAGE_MULTI_OUTPUT_MODE_LAST) {
 | 
				
			||||||
		struct cg_output *prev = wl_container_of(server->outputs.next, prev, link);
 | 
							struct cg_output *prev = wl_container_of(server->outputs.next, prev, link);
 | 
				
			||||||
		output_enable(prev);
 | 
							if (prev) {
 | 
				
			||||||
		view_position_all(server);
 | 
								output_enable(prev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								struct cg_view *view;
 | 
				
			||||||
 | 
								wl_list_for_each (view, &server->views, link) {
 | 
				
			||||||
 | 
									view_position(view);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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,11 +439,6 @@ 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)) {
 | 
					 | 
				
			||||||
		wlr_log(WLR_ERROR, "Failed to initialize output rendering");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct cg_output *output = calloc(1, sizeof(struct cg_output));
 | 
						struct cg_output *output = calloc(1, sizeof(struct cg_output));
 | 
				
			||||||
	if (!output) {
 | 
						if (!output) {
 | 
				
			||||||
		wlr_log(WLR_ERROR, "Failed to allocate output");
 | 
							wlr_log(WLR_ERROR, "Failed to allocate output");
 | 
				
			||||||
| 
						 | 
					@ -249,51 +446,32 @@ handle_new_output(struct wl_listener *listener, void *data)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	output->wlr_output = wlr_output;
 | 
						output->wlr_output = wlr_output;
 | 
				
			||||||
	wlr_output->data = output;
 | 
					 | 
				
			||||||
	output->server = server;
 | 
						output->server = server;
 | 
				
			||||||
 | 
						output->damage = wlr_output_damage_create(wlr_output);
 | 
				
			||||||
	wl_list_insert(&server->outputs, &output->link);
 | 
						wl_list_insert(&server->outputs, &output->link);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	output->commit.notify = handle_output_commit;
 | 
						output->mode.notify = handle_output_mode;
 | 
				
			||||||
	wl_signal_add(&wlr_output->events.commit, &output->commit);
 | 
						wl_signal_add(&wlr_output->events.mode, &output->mode);
 | 
				
			||||||
	output->request_state.notify = handle_output_request_state;
 | 
						output->transform.notify = handle_output_transform;
 | 
				
			||||||
	wl_signal_add(&wlr_output->events.request_state, &output->request_state);
 | 
						wl_signal_add(&wlr_output->events.transform, &output->transform);
 | 
				
			||||||
	output->destroy.notify = handle_output_destroy;
 | 
						output->destroy.notify = handle_output_destroy;
 | 
				
			||||||
	wl_signal_add(&wlr_output->events.destroy, &output->destroy);
 | 
						wl_signal_add(&wlr_output->events.destroy, &output->destroy);
 | 
				
			||||||
	output->frame.notify = handle_output_frame;
 | 
						output->damage_frame.notify = handle_output_damage_frame;
 | 
				
			||||||
	wl_signal_add(&wlr_output->events.frame, &output->frame);
 | 
						wl_signal_add(&output->damage->events.frame, &output->damage_frame);
 | 
				
			||||||
 | 
						output->damage_destroy.notify = handle_output_damage_destroy;
 | 
				
			||||||
 | 
						wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	output->scene_output = wlr_scene_output_create(server->scene, wlr_output);
 | 
						struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output);
 | 
				
			||||||
	if (!output->scene_output) {
 | 
						if (preferred_mode) {
 | 
				
			||||||
		wlr_log(WLR_ERROR, "Failed to allocate scene output");
 | 
							wlr_output_set_mode(wlr_output, preferred_mode);
 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						wlr_output_set_transform(wlr_output, output->server->output_transform);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct wlr_output_state state = {0};
 | 
						if (server->output_mode == CAGE_MULTI_OUTPUT_MODE_LAST) {
 | 
				
			||||||
	wlr_output_state_set_enabled(&state, true);
 | 
					 | 
				
			||||||
	if (!wl_list_empty(&wlr_output->modes)) {
 | 
					 | 
				
			||||||
		struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output);
 | 
					 | 
				
			||||||
		if (preferred_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) {
 | 
					 | 
				
			||||||
		struct cg_output *next = wl_container_of(output->link.next, next, link);
 | 
							struct cg_output *next = wl_container_of(output->link.next, next, link);
 | 
				
			||||||
		output_disable(next);
 | 
							if (next) {
 | 
				
			||||||
 | 
								output_disable(next);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!wlr_xcursor_manager_load(server->seat->xcursor_manager, wlr_output->scale)) {
 | 
						if (!wlr_xcursor_manager_load(server->seat->xcursor_manager, wlr_output->scale)) {
 | 
				
			||||||
| 
						 | 
					@ -301,13 +479,12 @@ handle_new_output(struct wl_listener *listener, void *data)
 | 
				
			||||||
			wlr_output->scale);
 | 
								wlr_output->scale);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_log(WLR_DEBUG, "Enabling new output %s", wlr_output->name);
 | 
						output_enable(output);
 | 
				
			||||||
	if (wlr_output_commit_state(wlr_output, &state)) {
 | 
					 | 
				
			||||||
		output_layout_add_auto(output);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	view_position_all(output->server);
 | 
						struct cg_view *view;
 | 
				
			||||||
	update_output_manager_config(output->server);
 | 
						wl_list_for_each (view, &output->server->views, link) {
 | 
				
			||||||
 | 
							view_position(view);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
| 
						 | 
					@ -328,95 +505,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);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										23
									
								
								output.h
									
										
									
									
									
								
							
							
						
						
									
										23
									
								
								output.h
									
										
									
									
									
								
							| 
						 | 
					@ -3,6 +3,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <wayland-server-core.h>
 | 
					#include <wayland-server-core.h>
 | 
				
			||||||
#include <wlr/types/wlr_output.h>
 | 
					#include <wlr/types/wlr_output.h>
 | 
				
			||||||
 | 
					#include <wlr/types/wlr_output_damage.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "server.h"
 | 
					#include "server.h"
 | 
				
			||||||
#include "view.h"
 | 
					#include "view.h"
 | 
				
			||||||
| 
						 | 
					@ -10,20 +11,28 @@
 | 
				
			||||||
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
 | 
						struct wl_list link; // cg_server::outputs
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void handle_output_manager_apply(struct wl_listener *listener, void *data);
 | 
					typedef void (*cg_surface_iterator_func_t)(struct cg_output *output, struct wlr_surface *surface, struct wlr_box *box,
 | 
				
			||||||
void handle_output_manager_test(struct wl_listener *listener, void *data);
 | 
										   void *user_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_surface_for_each_surface(struct cg_output *output, struct wlr_surface *surface, double ox, double oy,
 | 
				
			||||||
 | 
									     cg_surface_iterator_func_t iterator, void *user_data);
 | 
				
			||||||
 | 
					void output_view_for_each_popup(struct cg_output *output, struct cg_view *view, cg_surface_iterator_func_t iterator,
 | 
				
			||||||
 | 
									void *user_data);
 | 
				
			||||||
 | 
					void output_drag_icons_for_each_surface(struct cg_output *output, struct wl_list *drag_icons,
 | 
				
			||||||
 | 
										cg_surface_iterator_func_t iterator, void *user_data);
 | 
				
			||||||
 | 
					void output_damage_surface(struct cg_output *output, struct wlr_surface *surface, double lx, double ly, bool whole);
 | 
				
			||||||
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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										215
									
								
								render.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								render.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,215 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Cage: A Wayland kiosk.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2018-2020 Jente Hidskes
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 The Sway authors
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * See the LICENSE file accompanying this file.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <wayland-server-core.h>
 | 
				
			||||||
 | 
					#include <wlr/backend.h>
 | 
				
			||||||
 | 
					#include <wlr/render/wlr_renderer.h>
 | 
				
			||||||
 | 
					#include <wlr/types/wlr_box.h>
 | 
				
			||||||
 | 
					#include <wlr/types/wlr_matrix.h>
 | 
				
			||||||
 | 
					#include <wlr/types/wlr_output.h>
 | 
				
			||||||
 | 
					#include <wlr/types/wlr_output_layout.h>
 | 
				
			||||||
 | 
					#include <wlr/types/wlr_surface.h>
 | 
				
			||||||
 | 
					#include <wlr/util/log.h>
 | 
				
			||||||
 | 
					#include <wlr/util/region.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "output.h"
 | 
				
			||||||
 | 
					#include "seat.h"
 | 
				
			||||||
 | 
					#include "server.h"
 | 
				
			||||||
 | 
					#include "util.h"
 | 
				
			||||||
 | 
					#include "view.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					scissor_output(struct wlr_output *output, pixman_box32_t *rect)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct render_data {
 | 
				
			||||||
 | 
						pixman_region32_t *damage;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					render_texture(struct wlr_output *wlr_output, pixman_region32_t *output_damage, struct wlr_texture *texture,
 | 
				
			||||||
 | 
						       const struct wlr_box *box, const float matrix[static 9])
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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, output_damage);
 | 
				
			||||||
 | 
						if (!pixman_region32_not_empty(&damage)) {
 | 
				
			||||||
 | 
							goto damage_finish;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int nrects;
 | 
				
			||||||
 | 
						pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
 | 
				
			||||||
 | 
						for (int i = 0; i < nrects; i++) {
 | 
				
			||||||
 | 
							scissor_output(wlr_output, &rects[i]);
 | 
				
			||||||
 | 
							wlr_render_texture_with_matrix(renderer, texture, matrix, 1.0f);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					damage_finish:
 | 
				
			||||||
 | 
						pixman_region32_fini(&damage);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					render_surface_iterator(struct cg_output *output, struct wlr_surface *surface, struct wlr_box *box, void *user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct render_data *data = user_data;
 | 
				
			||||||
 | 
						struct wlr_output *wlr_output = output->wlr_output;
 | 
				
			||||||
 | 
						pixman_region32_t *output_damage = data->damage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct wlr_texture *texture = wlr_surface_get_texture(surface);
 | 
				
			||||||
 | 
						if (!texture) {
 | 
				
			||||||
 | 
							wlr_log(WLR_DEBUG, "Cannot obtain surface texture");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						scale_box(box, wlr_output->scale);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float matrix[9];
 | 
				
			||||||
 | 
						enum wl_output_transform transform = wlr_output_transform_invert(surface->current.transform);
 | 
				
			||||||
 | 
						wlr_matrix_project_box(matrix, box, transform, 0.0f, wlr_output->transform_matrix);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						render_texture(wlr_output, output_damage, texture, box, matrix);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					render_drag_icons(struct cg_output *output, pixman_region32_t *damage, struct wl_list *drag_icons)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct render_data data = {
 | 
				
			||||||
 | 
							.damage = damage,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						output_drag_icons_for_each_surface(output, drag_icons, render_surface_iterator, &data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Render all toplevels without descending into popups.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					render_view_toplevels(struct cg_view *view, struct cg_output *output, pixman_region32_t *damage)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct render_data data = {
 | 
				
			||||||
 | 
							.damage = damage,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						double ox = view->lx;
 | 
				
			||||||
 | 
						double oy = view->ly;
 | 
				
			||||||
 | 
						wlr_output_layout_output_coords(output->server->output_layout, output->wlr_output, &ox, &oy);
 | 
				
			||||||
 | 
						output_surface_for_each_surface(output, view->wlr_surface, ox, oy, render_surface_iterator, &data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					render_popup_iterator(struct cg_output *output, struct wlr_surface *surface, struct wlr_box *box, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Render this popup's surface. */
 | 
				
			||||||
 | 
						render_surface_iterator(output, surface, box, data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Render this popup's child toplevels. */
 | 
				
			||||||
 | 
						output_surface_for_each_surface(output, surface, box->x, box->y, render_surface_iterator, data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					render_view_popups(struct cg_view *view, struct cg_output *output, pixman_region32_t *damage)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct render_data data = {
 | 
				
			||||||
 | 
							.damage = damage,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						output_view_for_each_popup(output, view, render_popup_iterator, &data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					output_render(struct cg_output *output, pixman_region32_t *damage)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct cg_server *server = output->server;
 | 
				
			||||||
 | 
						struct wlr_output *wlr_output = output->wlr_output;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct wlr_renderer *renderer = wlr_backend_get_renderer(wlr_output->backend);
 | 
				
			||||||
 | 
						if (!renderer) {
 | 
				
			||||||
 | 
							wlr_log(WLR_DEBUG, "Expected the output backend to have a renderer");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!pixman_region32_not_empty(damage)) {
 | 
				
			||||||
 | 
							wlr_log(WLR_DEBUG, "Output isn't damaged but needs a buffer swap");
 | 
				
			||||||
 | 
							goto renderer_end;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef DEBUG
 | 
				
			||||||
 | 
						if (server->debug_damage_tracking) {
 | 
				
			||||||
 | 
							wlr_renderer_clear(renderer, (float[]){1.0f, 0.0f, 0.0f, 1.0f});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
 | 
				
			||||||
 | 
						int nrects;
 | 
				
			||||||
 | 
						pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
 | 
				
			||||||
 | 
						for (int i = 0; i < nrects; i++) {
 | 
				
			||||||
 | 
							scissor_output(wlr_output, &rects[i]);
 | 
				
			||||||
 | 
							wlr_renderer_clear(renderer, color);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO: render only top view, possibly use focused view for this, see #35.
 | 
				
			||||||
 | 
						struct cg_view *view;
 | 
				
			||||||
 | 
						wl_list_for_each_reverse (view, &server->views, link) {
 | 
				
			||||||
 | 
							render_view_toplevels(view, output, damage);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct cg_view *focused_view = seat_get_focus(server->seat);
 | 
				
			||||||
 | 
						if (focused_view) {
 | 
				
			||||||
 | 
							render_view_popups(focused_view, output, damage);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						render_drag_icons(output, damage, &server->seat->drag_icons);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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(wlr_output, damage);
 | 
				
			||||||
 | 
						wlr_renderer_scissor(renderer, NULL);
 | 
				
			||||||
 | 
						wlr_renderer_end(renderer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int output_width, output_height;
 | 
				
			||||||
 | 
						wlr_output_transformed_resolution(wlr_output, &output_width, &output_height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pixman_region32_t frame_damage;
 | 
				
			||||||
 | 
						pixman_region32_init(&frame_damage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						enum wl_output_transform transform = wlr_output_transform_invert(wlr_output->transform);
 | 
				
			||||||
 | 
						wlr_region_transform(&frame_damage, &output->damage->current, transform, output_width, output_height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef DEBUG
 | 
				
			||||||
 | 
						if (server->debug_damage_tracking) {
 | 
				
			||||||
 | 
							pixman_region32_union_rect(&frame_damage, &frame_damage, 0, 0, output_width, output_height);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						wlr_output_set_damage(wlr_output, &frame_damage);
 | 
				
			||||||
 | 
						pixman_region32_fini(&frame_damage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!wlr_output_commit(wlr_output)) {
 | 
				
			||||||
 | 
							wlr_log(WLR_ERROR, "Could not commit output");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										8
									
								
								render.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								render.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,8 @@
 | 
				
			||||||
 | 
					#ifndef CG_RENDER_H
 | 
				
			||||||
 | 
					#define CG_RENDER_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "output.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void output_render(struct cg_output *output, pixman_region32_t *damage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										393
									
								
								seat.c
									
										
									
									
									
								
							
							
						
						
									
										393
									
								
								seat.c
									
										
									
									
									
								
							| 
						 | 
					@ -6,29 +6,20 @@
 | 
				
			||||||
 * See the LICENSE file accompanying this file.
 | 
					 * See the LICENSE file accompanying this file.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define _POSIX_C_SOURCE 200809L
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "config.h"
 | 
					#include "config.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <assert.h>
 | 
					 | 
				
			||||||
#include <linux/input-event-codes.h>
 | 
					#include <linux/input-event-codes.h>
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <string.h>
 | 
					 | 
				
			||||||
#include <wayland-server-core.h>
 | 
					#include <wayland-server-core.h>
 | 
				
			||||||
#include <wlr/backend.h>
 | 
					#include <wlr/backend.h>
 | 
				
			||||||
#include <wlr/backend/multi.h>
 | 
					#include <wlr/backend/multi.h>
 | 
				
			||||||
#include <wlr/backend/session.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_idle_notify_v1.h>
 | 
					#include <wlr/types/wlr_idle.h>
 | 
				
			||||||
#include <wlr/types/wlr_keyboard_group.h>
 | 
					#include <wlr/types/wlr_keyboard_group.h>
 | 
				
			||||||
#include <wlr/types/wlr_primary_selection.h>
 | 
					#include <wlr/types/wlr_primary_selection.h>
 | 
				
			||||||
#include <wlr/types/wlr_relative_pointer_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_scene.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_seat.h>
 | 
					#include <wlr/types/wlr_seat.h>
 | 
				
			||||||
#include <wlr/types/wlr_touch.h>
 | 
					#include <wlr/types/wlr_surface.h>
 | 
				
			||||||
#include <wlr/types/wlr_virtual_keyboard_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_virtual_pointer_v1.h>
 | 
					 | 
				
			||||||
#include <wlr/types/wlr_xcursor_manager.h>
 | 
					#include <wlr/types/wlr_xcursor_manager.h>
 | 
				
			||||||
#include <wlr/util/log.h>
 | 
					#include <wlr/util/log.h>
 | 
				
			||||||
#if CAGE_HAS_XWAYLAND
 | 
					#if CAGE_HAS_XWAYLAND
 | 
				
			||||||
| 
						 | 
					@ -49,42 +40,42 @@ static void drag_icon_update_position(struct cg_drag_icon *drag_icon);
 | 
				
			||||||
 * menus or tooltips. This function tests if any of those are underneath the
 | 
					 * menus or tooltips. This function tests if any of those are underneath the
 | 
				
			||||||
 * coordinates lx and ly (in output Layout Coordinates). If so, it sets the
 | 
					 * coordinates lx and ly (in output Layout Coordinates). If so, it sets the
 | 
				
			||||||
 * surface pointer to that wlr_surface and the sx and sy coordinates to the
 | 
					 * surface pointer to that wlr_surface and the sx and sy coordinates to the
 | 
				
			||||||
 * coordinates relative to that surface's top-left corner.
 | 
					 * coordinates relative to that surface's top-left corner. */
 | 
				
			||||||
 *
 | 
					static bool
 | 
				
			||||||
 * This function iterates over all of our surfaces and attempts to find one
 | 
					view_at(struct cg_view *view, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy)
 | 
				
			||||||
 * under the cursor. If desktop_view_at returns a view, there is also a
 | 
					{
 | 
				
			||||||
 * surface. There cannot be a surface without a view, either. It's both or
 | 
						double view_sx = lx - view->lx;
 | 
				
			||||||
 * nothing.
 | 
						double view_sy = ly - view->ly;
 | 
				
			||||||
 */
 | 
					
 | 
				
			||||||
 | 
						double _sx, _sy;
 | 
				
			||||||
 | 
						struct wlr_surface *_surface = view_wlr_surface_at(view, view_sx, view_sy, &_sx, &_sy);
 | 
				
			||||||
 | 
						if (_surface != NULL) {
 | 
				
			||||||
 | 
							*sx = _sx;
 | 
				
			||||||
 | 
							*sy = _sy;
 | 
				
			||||||
 | 
							*surface = _surface;
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* This iterates over all of our surfaces and attempts to find one
 | 
				
			||||||
 | 
					 * under the cursor. This relies on server->views being ordered from
 | 
				
			||||||
 | 
					 * top-to-bottom. If desktop_view_at returns a view, there is also a
 | 
				
			||||||
 | 
					 * surface. There cannot be a surface without a view, either. It's
 | 
				
			||||||
 | 
					 * both or nothing. */
 | 
				
			||||||
static struct cg_view *
 | 
					static struct cg_view *
 | 
				
			||||||
desktop_view_at(struct cg_server *server, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy)
 | 
					desktop_view_at(struct cg_server *server, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct wlr_scene_node *node = wlr_scene_node_at(&server->scene->tree.node, lx, ly, sx, sy);
 | 
						struct cg_view *view;
 | 
				
			||||||
	if (node == NULL || node->type != WLR_SCENE_NODE_BUFFER) {
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct wlr_scene_buffer *scene_buffer = wlr_scene_buffer_from_node(node);
 | 
						wl_list_for_each (view, &server->views, link) {
 | 
				
			||||||
	struct wlr_scene_surface *scene_surface = wlr_scene_surface_try_from_buffer(scene_buffer);
 | 
							if (view_at(view, lx, ly, surface, sx, sy)) {
 | 
				
			||||||
	if (!scene_surface) {
 | 
								return view;
 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	*surface = scene_surface->surface;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Walk up the tree until we find a node with a data pointer. When done,
 | 
					 | 
				
			||||||
	 * we've found the node representing the view. */
 | 
					 | 
				
			||||||
	while (!node->data) {
 | 
					 | 
				
			||||||
		if (!node->parent) {
 | 
					 | 
				
			||||||
			node = NULL;
 | 
					 | 
				
			||||||
			break;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					 | 
				
			||||||
		node = &node->parent->node;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert(node != NULL);
 | 
						return NULL;
 | 
				
			||||||
	return node->data;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
| 
						 | 
					@ -128,23 +119,23 @@ update_capabilities(struct cg_seat *seat)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Hide cursor if the seat doesn't have pointer capability. */
 | 
						/* Hide cursor if the seat doesn't have pointer capability. */
 | 
				
			||||||
	if ((caps & WL_SEAT_CAPABILITY_POINTER) == 0) {
 | 
						if ((caps & WL_SEAT_CAPABILITY_POINTER) == 0) {
 | 
				
			||||||
		wlr_cursor_unset_image(seat->cursor);
 | 
							wlr_cursor_set_image(seat->cursor, NULL, 0, 0, 0, 0, 0, 0);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		wlr_cursor_set_xcursor(seat->cursor, seat->xcursor_manager, DEFAULT_XCURSOR);
 | 
							wlr_xcursor_manager_set_cursor_image(seat->xcursor_manager, DEFAULT_XCURSOR, seat->cursor);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
map_input_device_to_output(struct cg_seat *seat, struct wlr_input_device *device, const char *output_name)
 | 
					map_input_device_to_output(struct cg_seat *seat, struct wlr_input_device *device)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (!output_name) {
 | 
						if (!device->output_name) {
 | 
				
			||||||
		wlr_log(WLR_INFO, "Input device %s cannot be mapped to an output device\n", device->name);
 | 
							wlr_log(WLR_INFO, "Input device %s cannot be mapped to an output device\n", device->name);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct cg_output *output;
 | 
						struct cg_output *output;
 | 
				
			||||||
	wl_list_for_each (output, &seat->server->outputs, link) {
 | 
						wl_list_for_each (output, &seat->server->outputs, link) {
 | 
				
			||||||
		if (strcmp(output_name, output->wlr_output->name) == 0) {
 | 
							if (strcmp(device->output_name, output->wlr_output->name) == 0) {
 | 
				
			||||||
			wlr_log(WLR_INFO, "Mapping input device %s to output device %s\n", device->name,
 | 
								wlr_log(WLR_INFO, "Mapping input device %s to output device %s\n", device->name,
 | 
				
			||||||
				output->wlr_output->name);
 | 
									output->wlr_output->name);
 | 
				
			||||||
			wlr_cursor_map_input_to_output(seat->cursor, device, output->wlr_output);
 | 
								wlr_cursor_map_input_to_output(seat->cursor, device, output->wlr_output);
 | 
				
			||||||
| 
						 | 
					@ -162,7 +153,7 @@ handle_touch_destroy(struct wl_listener *listener, void *data)
 | 
				
			||||||
	struct cg_seat *seat = touch->seat;
 | 
						struct cg_seat *seat = touch->seat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wl_list_remove(&touch->link);
 | 
						wl_list_remove(&touch->link);
 | 
				
			||||||
	wlr_cursor_detach_input_device(seat->cursor, &touch->touch->base);
 | 
						wlr_cursor_detach_input_device(seat->cursor, touch->device);
 | 
				
			||||||
	wl_list_remove(&touch->destroy.link);
 | 
						wl_list_remove(&touch->destroy.link);
 | 
				
			||||||
	free(touch);
 | 
						free(touch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -170,7 +161,7 @@ handle_touch_destroy(struct wl_listener *listener, void *data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
handle_new_touch(struct cg_seat *seat, struct wlr_touch *wlr_touch)
 | 
					handle_new_touch(struct cg_seat *seat, struct wlr_input_device *device)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct cg_touch *touch = calloc(1, sizeof(struct cg_touch));
 | 
						struct cg_touch *touch = calloc(1, sizeof(struct cg_touch));
 | 
				
			||||||
	if (!touch) {
 | 
						if (!touch) {
 | 
				
			||||||
| 
						 | 
					@ -179,14 +170,14 @@ handle_new_touch(struct cg_seat *seat, struct wlr_touch *wlr_touch)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	touch->seat = seat;
 | 
						touch->seat = seat;
 | 
				
			||||||
	touch->touch = wlr_touch;
 | 
						touch->device = device;
 | 
				
			||||||
	wlr_cursor_attach_input_device(seat->cursor, &wlr_touch->base);
 | 
						wlr_cursor_attach_input_device(seat->cursor, device);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wl_list_insert(&seat->touch, &touch->link);
 | 
						wl_list_insert(&seat->touch, &touch->link);
 | 
				
			||||||
	touch->destroy.notify = handle_touch_destroy;
 | 
						touch->destroy.notify = handle_touch_destroy;
 | 
				
			||||||
	wl_signal_add(&wlr_touch->base.events.destroy, &touch->destroy);
 | 
						wl_signal_add(&touch->device->events.destroy, &touch->destroy);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	map_input_device_to_output(seat, &wlr_touch->base, wlr_touch->output_name);
 | 
						map_input_device_to_output(seat, device);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
| 
						 | 
					@ -196,7 +187,7 @@ handle_pointer_destroy(struct wl_listener *listener, void *data)
 | 
				
			||||||
	struct cg_seat *seat = pointer->seat;
 | 
						struct cg_seat *seat = pointer->seat;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wl_list_remove(&pointer->link);
 | 
						wl_list_remove(&pointer->link);
 | 
				
			||||||
	wlr_cursor_detach_input_device(seat->cursor, &pointer->pointer->base);
 | 
						wlr_cursor_detach_input_device(seat->cursor, pointer->device);
 | 
				
			||||||
	wl_list_remove(&pointer->destroy.link);
 | 
						wl_list_remove(&pointer->destroy.link);
 | 
				
			||||||
	free(pointer);
 | 
						free(pointer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -204,7 +195,7 @@ handle_pointer_destroy(struct wl_listener *listener, void *data)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
handle_new_pointer(struct cg_seat *seat, struct wlr_pointer *wlr_pointer)
 | 
					handle_new_pointer(struct cg_seat *seat, struct wlr_input_device *device)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct cg_pointer *pointer = calloc(1, sizeof(struct cg_pointer));
 | 
						struct cg_pointer *pointer = calloc(1, sizeof(struct cg_pointer));
 | 
				
			||||||
	if (!pointer) {
 | 
						if (!pointer) {
 | 
				
			||||||
| 
						 | 
					@ -213,44 +204,23 @@ handle_new_pointer(struct cg_seat *seat, struct wlr_pointer *wlr_pointer)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pointer->seat = seat;
 | 
						pointer->seat = seat;
 | 
				
			||||||
	pointer->pointer = wlr_pointer;
 | 
						pointer->device = device;
 | 
				
			||||||
	wlr_cursor_attach_input_device(seat->cursor, &wlr_pointer->base);
 | 
						wlr_cursor_attach_input_device(seat->cursor, device);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wl_list_insert(&seat->pointers, &pointer->link);
 | 
						wl_list_insert(&seat->pointers, &pointer->link);
 | 
				
			||||||
	pointer->destroy.notify = handle_pointer_destroy;
 | 
						pointer->destroy.notify = handle_pointer_destroy;
 | 
				
			||||||
	wl_signal_add(&wlr_pointer->base.events.destroy, &pointer->destroy);
 | 
						wl_signal_add(&device->events.destroy, &pointer->destroy);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	map_input_device_to_output(seat, &wlr_pointer->base, wlr_pointer->output_name);
 | 
						map_input_device_to_output(seat, device);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
handle_virtual_pointer(struct wl_listener *listener, void *data)
 | 
					handle_modifier_event(struct wlr_input_device *device, struct cg_seat *seat)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct cg_server *server = wl_container_of(listener, server, new_virtual_pointer);
 | 
						wlr_seat_set_keyboard(seat->seat, device);
 | 
				
			||||||
	struct cg_seat *seat = server->seat;
 | 
						wlr_seat_keyboard_notify_modifiers(seat->seat, &device->keyboard->modifiers);
 | 
				
			||||||
	struct wlr_virtual_pointer_v1_new_pointer_event *event = data;
 | 
					 | 
				
			||||||
	struct wlr_virtual_pointer_v1 *pointer = event->new_pointer;
 | 
					 | 
				
			||||||
	struct wlr_pointer *wlr_pointer = &pointer->pointer;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* We'll want to map the device back to an output later, this is a bit
 | 
						wlr_idle_notify_activity(seat->server->idle, seat->seat);
 | 
				
			||||||
	 * sub-optimal (we could just keep the suggested_output), but just copy
 | 
					 | 
				
			||||||
	 * its name so we do like other devices
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	if (event->suggested_output != NULL) {
 | 
					 | 
				
			||||||
		wlr_pointer->output_name = strdup(event->suggested_output->name);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	/* TODO: event->suggested_seat should be checked if we handle multiple seats */
 | 
					 | 
				
			||||||
	handle_new_pointer(seat, wlr_pointer);
 | 
					 | 
				
			||||||
	update_capabilities(seat);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
handle_modifier_event(struct wlr_keyboard *keyboard, struct cg_seat *seat)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	wlr_seat_set_keyboard(seat->seat, keyboard);
 | 
					 | 
				
			||||||
	wlr_seat_keyboard_notify_modifiers(seat->seat, &keyboard->modifiers);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool
 | 
					static bool
 | 
				
			||||||
| 
						 | 
					@ -258,38 +228,40 @@ handle_keybinding(struct cg_server *server, xkb_keysym_t sym)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
#ifdef DEBUG
 | 
					#ifdef DEBUG
 | 
				
			||||||
	if (sym == XKB_KEY_Escape) {
 | 
						if (sym == XKB_KEY_Escape) {
 | 
				
			||||||
		server_terminate(server);
 | 
							wl_display_terminate(server->wl_display);
 | 
				
			||||||
		return true;
 | 
						} else
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	if (server->allow_vt_switch && sym >= XKB_KEY_XF86Switch_VT_1 && sym <= XKB_KEY_XF86Switch_VT_12) {
 | 
						if (server->allow_vt_switch && sym >= XKB_KEY_XF86Switch_VT_1
 | 
				
			||||||
 | 
								&& sym <= XKB_KEY_XF86Switch_VT_12) {
 | 
				
			||||||
		if (wlr_backend_is_multi(server->backend)) {
 | 
							if (wlr_backend_is_multi(server->backend)) {
 | 
				
			||||||
			if (server->session) {
 | 
								struct wlr_session *session =
 | 
				
			||||||
 | 
									wlr_backend_get_session(server->backend);
 | 
				
			||||||
 | 
								if (session) {
 | 
				
			||||||
				unsigned vt = sym - XKB_KEY_XF86Switch_VT_1 + 1;
 | 
									unsigned vt = sym - XKB_KEY_XF86Switch_VT_1 + 1;
 | 
				
			||||||
				wlr_session_change_vt(server->session, vt);
 | 
									wlr_session_change_vt(session, vt);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	wlr_idle_notifier_v1_notify_activity(server->idle, server->seat->seat);
 | 
						wlr_idle_notify_activity(server->idle, server->seat->seat);
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
handle_key_event(struct wlr_keyboard *keyboard, struct cg_seat *seat, void *data)
 | 
					handle_key_event(struct wlr_input_device *device, struct cg_seat *seat, void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct wlr_keyboard_key_event *event = data;
 | 
						struct wlr_event_keyboard_key *event = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Translate from libinput keycode to an xkbcommon keycode. */
 | 
						/* Translate from libinput keycode to an xkbcommon keycode. */
 | 
				
			||||||
	xkb_keycode_t keycode = event->keycode + 8;
 | 
						xkb_keycode_t keycode = event->keycode + 8;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	const xkb_keysym_t *syms;
 | 
						const xkb_keysym_t *syms;
 | 
				
			||||||
	int nsyms = xkb_state_key_get_syms(keyboard->xkb_state, keycode, &syms);
 | 
						int nsyms = xkb_state_key_get_syms(device->keyboard->xkb_state, keycode, &syms);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool handled = false;
 | 
						bool handled = false;
 | 
				
			||||||
	uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard);
 | 
						uint32_t modifiers = wlr_keyboard_get_modifiers(device->keyboard);
 | 
				
			||||||
	if ((modifiers & WLR_MODIFIER_ALT) && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
 | 
						if ((modifiers & WLR_MODIFIER_ALT) && event->state == WLR_KEY_PRESSED) {
 | 
				
			||||||
		/* If Alt is held down and this button was pressed, we
 | 
							/* If Alt is held down and this button was pressed, we
 | 
				
			||||||
		 * attempt to process it as a compositor
 | 
							 * attempt to process it as a compositor
 | 
				
			||||||
		 * keybinding. */
 | 
							 * keybinding. */
 | 
				
			||||||
| 
						 | 
					@ -300,43 +272,38 @@ handle_key_event(struct wlr_keyboard *keyboard, struct cg_seat *seat, void *data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!handled) {
 | 
						if (!handled) {
 | 
				
			||||||
		/* Otherwise, we pass it along to the client. */
 | 
							/* Otherwise, we pass it along to the client. */
 | 
				
			||||||
		wlr_seat_set_keyboard(seat->seat, keyboard);
 | 
							wlr_seat_set_keyboard(seat->seat, device);
 | 
				
			||||||
		wlr_seat_keyboard_notify_key(seat->seat, event->time_msec, event->keycode, event->state);
 | 
							wlr_seat_keyboard_notify_key(seat->seat, event->time_msec, event->keycode, event->state);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
 | 
						wlr_idle_notify_activity(seat->server->idle, seat->seat);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
handle_keyboard_group_key(struct wl_listener *listener, void *data)
 | 
					handle_keyboard_group_key(struct wl_listener *listener, void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct cg_keyboard_group *cg_group = wl_container_of(listener, cg_group, key);
 | 
						struct cg_keyboard_group *cg_group = wl_container_of(listener, cg_group, key);
 | 
				
			||||||
	handle_key_event(&cg_group->wlr_group->keyboard, cg_group->seat, data);
 | 
						handle_key_event(cg_group->wlr_group->input_device, cg_group->seat, data);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
handle_keyboard_group_modifiers(struct wl_listener *listener, void *data)
 | 
					handle_keyboard_group_modifiers(struct wl_listener *listener, void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct cg_keyboard_group *group = wl_container_of(listener, group, modifiers);
 | 
						struct cg_keyboard_group *group = wl_container_of(listener, group, modifiers);
 | 
				
			||||||
	handle_modifier_event(&group->wlr_group->keyboard, group->seat);
 | 
						handle_modifier_event(group->wlr_group->input_device, group->seat);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
cg_keyboard_group_add(struct wlr_keyboard *keyboard, struct cg_seat *seat, bool virtual)
 | 
					cg_keyboard_group_add(struct wlr_input_device *device, struct cg_seat *seat)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/* We apparently should not group virtual keyboards,
 | 
						struct wlr_keyboard *wlr_keyboard = device->keyboard;
 | 
				
			||||||
	 * so create a new group with it
 | 
					
 | 
				
			||||||
	 */
 | 
						struct cg_keyboard_group *group;
 | 
				
			||||||
	if (!virtual) {
 | 
						wl_list_for_each (group, &seat->keyboard_groups, link) {
 | 
				
			||||||
		struct cg_keyboard_group *group;
 | 
							struct wlr_keyboard_group *wlr_group = group->wlr_group;
 | 
				
			||||||
		wl_list_for_each (group, &seat->keyboard_groups, link) {
 | 
							if (wlr_keyboard_group_add_keyboard(wlr_group, wlr_keyboard)) {
 | 
				
			||||||
			if (group->is_virtual)
 | 
								wlr_log(WLR_DEBUG, "Added new keyboard to existing group");
 | 
				
			||||||
				continue;
 | 
								return;
 | 
				
			||||||
			struct wlr_keyboard_group *wlr_group = group->wlr_group;
 | 
					 | 
				
			||||||
			if (wlr_keyboard_group_add_keyboard(wlr_group, keyboard)) {
 | 
					 | 
				
			||||||
				wlr_log(WLR_DEBUG, "Added new keyboard to existing group");
 | 
					 | 
				
			||||||
				return;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -348,7 +315,6 @@ cg_keyboard_group_add(struct wlr_keyboard *keyboard, struct cg_seat *seat, bool
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	cg_group->seat = seat;
 | 
						cg_group->seat = seat;
 | 
				
			||||||
	cg_group->is_virtual = virtual;
 | 
					 | 
				
			||||||
	cg_group->wlr_group = wlr_keyboard_group_create();
 | 
						cg_group->wlr_group = wlr_keyboard_group_create();
 | 
				
			||||||
	if (cg_group->wlr_group == NULL) {
 | 
						if (cg_group->wlr_group == NULL) {
 | 
				
			||||||
		wlr_log(WLR_ERROR, "Failed to create wlr keyboard group.");
 | 
							wlr_log(WLR_ERROR, "Failed to create wlr keyboard group.");
 | 
				
			||||||
| 
						 | 
					@ -356,14 +322,14 @@ cg_keyboard_group_add(struct wlr_keyboard *keyboard, struct cg_seat *seat, bool
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cg_group->wlr_group->data = cg_group;
 | 
						cg_group->wlr_group->data = cg_group;
 | 
				
			||||||
	wlr_keyboard_set_keymap(&cg_group->wlr_group->keyboard, keyboard->keymap);
 | 
						wlr_keyboard_set_keymap(&cg_group->wlr_group->keyboard, device->keyboard->keymap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_keyboard_set_repeat_info(&cg_group->wlr_group->keyboard, keyboard->repeat_info.rate,
 | 
						wlr_keyboard_set_repeat_info(&cg_group->wlr_group->keyboard, wlr_keyboard->repeat_info.rate,
 | 
				
			||||||
				     keyboard->repeat_info.delay);
 | 
									     wlr_keyboard->repeat_info.delay);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_log(WLR_DEBUG, "Created keyboard group");
 | 
						wlr_log(WLR_DEBUG, "Created keyboard group");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_keyboard_group_add_keyboard(cg_group->wlr_group, keyboard);
 | 
						wlr_keyboard_group_add_keyboard(cg_group->wlr_group, wlr_keyboard);
 | 
				
			||||||
	wl_list_insert(&seat->keyboard_groups, &cg_group->link);
 | 
						wl_list_insert(&seat->keyboard_groups, &cg_group->link);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wl_signal_add(&cg_group->wlr_group->keyboard.events.key, &cg_group->key);
 | 
						wl_signal_add(&cg_group->wlr_group->keyboard.events.key, &cg_group->key);
 | 
				
			||||||
| 
						 | 
					@ -381,55 +347,36 @@ cleanup:
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
keyboard_group_destroy(struct cg_keyboard_group *keyboard_group)
 | 
					handle_new_keyboard(struct cg_seat *seat, struct wlr_input_device *device)
 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	wl_list_remove(&keyboard_group->key.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&keyboard_group->modifiers.link);
 | 
					 | 
				
			||||||
	wlr_keyboard_group_destroy(keyboard_group->wlr_group);
 | 
					 | 
				
			||||||
	wl_list_remove(&keyboard_group->link);
 | 
					 | 
				
			||||||
	free(keyboard_group);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
handle_new_keyboard(struct cg_seat *seat, struct wlr_keyboard *keyboard, bool virtual)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
 | 
						struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
 | 
				
			||||||
	if (!context) {
 | 
						if (!context) {
 | 
				
			||||||
		wlr_log(WLR_ERROR, "Unable to create XKB context");
 | 
							wlr_log(WLR_ERROR, "Unable to create XBK context");
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct xkb_keymap *keymap = xkb_keymap_new_from_names(context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS);
 | 
						struct xkb_rule_names rules = {0};
 | 
				
			||||||
 | 
						rules.rules = getenv("XKB_DEFAULT_RULES");
 | 
				
			||||||
 | 
						rules.model = getenv("XKB_DEFAULT_MODEL");
 | 
				
			||||||
 | 
						rules.layout = getenv("XKB_DEFAULT_LAYOUT");
 | 
				
			||||||
 | 
						rules.variant = getenv("XKB_DEFAULT_VARIANT");
 | 
				
			||||||
 | 
						rules.options = getenv("XKB_DEFAULT_OPTIONS");
 | 
				
			||||||
 | 
						struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
 | 
				
			||||||
	if (!keymap) {
 | 
						if (!keymap) {
 | 
				
			||||||
		wlr_log(WLR_ERROR, "Unable to configure keyboard: keymap does not exist");
 | 
							wlr_log(WLR_ERROR, "Unable to configure keyboard: keymap does not exist");
 | 
				
			||||||
		xkb_context_unref(context);
 | 
							xkb_context_unref(context);
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_keyboard_set_keymap(keyboard, keymap);
 | 
						wlr_keyboard_set_keymap(device->keyboard, keymap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	xkb_keymap_unref(keymap);
 | 
						xkb_keymap_unref(keymap);
 | 
				
			||||||
	xkb_context_unref(context);
 | 
						xkb_context_unref(context);
 | 
				
			||||||
	wlr_keyboard_set_repeat_info(keyboard, 25, 600);
 | 
						wlr_keyboard_set_repeat_info(device->keyboard, 25, 600);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cg_keyboard_group_add(keyboard, seat, virtual);
 | 
						cg_keyboard_group_add(device, seat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_seat_set_keyboard(seat->seat, keyboard);
 | 
						wlr_seat_set_keyboard(seat->seat, device);
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
handle_virtual_keyboard(struct wl_listener *listener, void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct cg_server *server = wl_container_of(listener, server, new_virtual_keyboard);
 | 
					 | 
				
			||||||
	struct cg_seat *seat = server->seat;
 | 
					 | 
				
			||||||
	struct wlr_virtual_keyboard_v1 *keyboard = data;
 | 
					 | 
				
			||||||
	struct wlr_keyboard *wlr_keyboard = &keyboard->keyboard;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* TODO: If multiple seats are supported, check keyboard->seat
 | 
					 | 
				
			||||||
	 * to select the appropriate one */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	handle_new_keyboard(seat, wlr_keyboard, true);
 | 
					 | 
				
			||||||
	update_capabilities(seat);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
| 
						 | 
					@ -440,18 +387,18 @@ handle_new_input(struct wl_listener *listener, void *data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (device->type) {
 | 
						switch (device->type) {
 | 
				
			||||||
	case WLR_INPUT_DEVICE_KEYBOARD:
 | 
						case WLR_INPUT_DEVICE_KEYBOARD:
 | 
				
			||||||
		handle_new_keyboard(seat, wlr_keyboard_from_input_device(device), false);
 | 
							handle_new_keyboard(seat, device);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case WLR_INPUT_DEVICE_POINTER:
 | 
						case WLR_INPUT_DEVICE_POINTER:
 | 
				
			||||||
		handle_new_pointer(seat, wlr_pointer_from_input_device(device));
 | 
							handle_new_pointer(seat, device);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case WLR_INPUT_DEVICE_TOUCH:
 | 
						case WLR_INPUT_DEVICE_TOUCH:
 | 
				
			||||||
		handle_new_touch(seat, wlr_touch_from_input_device(device));
 | 
							handle_new_touch(seat, device);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case WLR_INPUT_DEVICE_SWITCH:
 | 
						case WLR_INPUT_DEVICE_SWITCH:
 | 
				
			||||||
		wlr_log(WLR_DEBUG, "Switch input is not implemented");
 | 
							wlr_log(WLR_DEBUG, "Switch input is not implemented");
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	case WLR_INPUT_DEVICE_TABLET:
 | 
						case WLR_INPUT_DEVICE_TABLET_TOOL:
 | 
				
			||||||
	case WLR_INPUT_DEVICE_TABLET_PAD:
 | 
						case WLR_INPUT_DEVICE_TABLET_PAD:
 | 
				
			||||||
		wlr_log(WLR_DEBUG, "Tablet input is not implemented");
 | 
							wlr_log(WLR_DEBUG, "Tablet input is not implemented");
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
| 
						 | 
					@ -501,10 +448,10 @@ static void
 | 
				
			||||||
handle_touch_down(struct wl_listener *listener, void *data)
 | 
					handle_touch_down(struct wl_listener *listener, void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct cg_seat *seat = wl_container_of(listener, seat, touch_down);
 | 
						struct cg_seat *seat = wl_container_of(listener, seat, touch_down);
 | 
				
			||||||
	struct wlr_touch_down_event *event = data;
 | 
						struct wlr_event_touch_down *event = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	double lx, ly;
 | 
						double lx, ly;
 | 
				
			||||||
	wlr_cursor_absolute_to_layout_coords(seat->cursor, &event->touch->base, event->x, event->y, &lx, &ly);
 | 
						wlr_cursor_absolute_to_layout_coords(seat->cursor, event->device, event->x, event->y, &lx, &ly);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	double sx, sy;
 | 
						double sx, sy;
 | 
				
			||||||
	struct wlr_surface *surface;
 | 
						struct wlr_surface *surface;
 | 
				
			||||||
| 
						 | 
					@ -519,43 +466,43 @@ handle_touch_down(struct wl_listener *listener, void *data)
 | 
				
			||||||
		seat->touch_id = event->touch_id;
 | 
							seat->touch_id = event->touch_id;
 | 
				
			||||||
		seat->touch_lx = lx;
 | 
							seat->touch_lx = lx;
 | 
				
			||||||
		seat->touch_ly = ly;
 | 
							seat->touch_ly = ly;
 | 
				
			||||||
		press_cursor_button(seat, &event->touch->base, event->time_msec, BTN_LEFT, WLR_BUTTON_PRESSED, lx, ly);
 | 
							press_cursor_button(seat, event->device, event->time_msec, BTN_LEFT, WLR_BUTTON_PRESSED, lx, ly);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
 | 
						wlr_idle_notify_activity(seat->server->idle, seat->seat);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
handle_touch_up(struct wl_listener *listener, void *data)
 | 
					handle_touch_up(struct wl_listener *listener, void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct cg_seat *seat = wl_container_of(listener, seat, touch_up);
 | 
						struct cg_seat *seat = wl_container_of(listener, seat, touch_up);
 | 
				
			||||||
	struct wlr_touch_up_event *event = data;
 | 
						struct wlr_event_touch_up *event = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!wlr_seat_touch_get_point(seat->seat, event->touch_id)) {
 | 
						if (!wlr_seat_touch_get_point(seat->seat, event->touch_id)) {
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (wlr_seat_touch_num_points(seat->seat) == 1) {
 | 
						if (wlr_seat_touch_num_points(seat->seat) == 1) {
 | 
				
			||||||
		press_cursor_button(seat, &event->touch->base, event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED,
 | 
							press_cursor_button(seat, event->device, event->time_msec, BTN_LEFT, WLR_BUTTON_RELEASED,
 | 
				
			||||||
				    seat->touch_lx, seat->touch_ly);
 | 
									    seat->touch_lx, seat->touch_ly);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_seat_touch_notify_up(seat->seat, event->time_msec, event->touch_id);
 | 
						wlr_seat_touch_notify_up(seat->seat, event->time_msec, event->touch_id);
 | 
				
			||||||
	wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
 | 
						wlr_idle_notify_activity(seat->server->idle, seat->seat);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
handle_touch_motion(struct wl_listener *listener, void *data)
 | 
					handle_touch_motion(struct wl_listener *listener, void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct cg_seat *seat = wl_container_of(listener, seat, touch_motion);
 | 
						struct cg_seat *seat = wl_container_of(listener, seat, touch_motion);
 | 
				
			||||||
	struct wlr_touch_motion_event *event = data;
 | 
						struct wlr_event_touch_motion *event = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!wlr_seat_touch_get_point(seat->seat, event->touch_id)) {
 | 
						if (!wlr_seat_touch_get_point(seat->seat, event->touch_id)) {
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	double lx, ly;
 | 
						double lx, ly;
 | 
				
			||||||
	wlr_cursor_absolute_to_layout_coords(seat->cursor, &event->touch->base, event->x, event->y, &lx, &ly);
 | 
						wlr_cursor_absolute_to_layout_coords(seat->cursor, event->device, event->x, event->y, &lx, &ly);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	double sx, sy;
 | 
						double sx, sy;
 | 
				
			||||||
	struct wlr_surface *surface;
 | 
						struct wlr_surface *surface;
 | 
				
			||||||
| 
						 | 
					@ -573,16 +520,7 @@ handle_touch_motion(struct wl_listener *listener, void *data)
 | 
				
			||||||
		seat->touch_ly = ly;
 | 
							seat->touch_ly = ly;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
 | 
						wlr_idle_notify_activity(seat->server->idle, seat->seat);
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
handle_touch_frame(struct wl_listener *listener, void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	struct cg_seat *seat = wl_container_of(listener, seat, touch_frame);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_seat_touch_notify_frame(seat->seat);
 | 
					 | 
				
			||||||
	wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
| 
						 | 
					@ -591,52 +529,50 @@ handle_cursor_frame(struct wl_listener *listener, void *data)
 | 
				
			||||||
	struct cg_seat *seat = wl_container_of(listener, seat, cursor_frame);
 | 
						struct cg_seat *seat = wl_container_of(listener, seat, cursor_frame);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_seat_pointer_notify_frame(seat->seat);
 | 
						wlr_seat_pointer_notify_frame(seat->seat);
 | 
				
			||||||
	wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
 | 
						wlr_idle_notify_activity(seat->server->idle, seat->seat);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
handle_cursor_axis(struct wl_listener *listener, void *data)
 | 
					handle_cursor_axis(struct wl_listener *listener, void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct cg_seat *seat = wl_container_of(listener, seat, cursor_axis);
 | 
						struct cg_seat *seat = wl_container_of(listener, seat, cursor_axis);
 | 
				
			||||||
	struct wlr_pointer_axis_event *event = data;
 | 
						struct wlr_event_pointer_axis *event = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_seat_pointer_notify_axis(seat->seat, event->time_msec, event->orientation, event->delta,
 | 
						wlr_seat_pointer_notify_axis(seat->seat, event->time_msec, event->orientation, event->delta,
 | 
				
			||||||
				     event->delta_discrete, event->source, event->relative_direction);
 | 
									     event->delta_discrete, event->source);
 | 
				
			||||||
	wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
 | 
						wlr_idle_notify_activity(seat->server->idle, seat->seat);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
handle_cursor_button(struct wl_listener *listener, void *data)
 | 
					handle_cursor_button(struct wl_listener *listener, void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct cg_seat *seat = wl_container_of(listener, seat, cursor_button);
 | 
						struct cg_seat *seat = wl_container_of(listener, seat, cursor_button);
 | 
				
			||||||
	struct wlr_pointer_button_event *event = data;
 | 
						struct wlr_event_pointer_button *event = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_seat_pointer_notify_button(seat->seat, event->time_msec, event->button, event->state);
 | 
						wlr_seat_pointer_notify_button(seat->seat, event->time_msec, event->button, event->state);
 | 
				
			||||||
	press_cursor_button(seat, &event->pointer->base, event->time_msec, event->button, event->state, seat->cursor->x,
 | 
						press_cursor_button(seat, event->device, event->time_msec, event->button, event->state, seat->cursor->x,
 | 
				
			||||||
			    seat->cursor->y);
 | 
								    seat->cursor->y);
 | 
				
			||||||
	wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
 | 
						wlr_idle_notify_activity(seat->server->idle, seat->seat);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
process_cursor_motion(struct cg_seat *seat, uint32_t time_msec, double dx, double dy, double dx_unaccel,
 | 
					process_cursor_motion(struct cg_seat *seat, uint32_t time)
 | 
				
			||||||
		      double dy_unaccel)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	double sx, sy;
 | 
						double sx, sy;
 | 
				
			||||||
	struct wlr_seat *wlr_seat = seat->seat;
 | 
						struct wlr_seat *wlr_seat = seat->seat;
 | 
				
			||||||
	struct wlr_surface *surface = NULL;
 | 
						struct wlr_surface *surface = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct cg_view *view = desktop_view_at(seat->server, seat->cursor->x, seat->cursor->y, &surface, &sx, &sy);
 | 
						struct cg_view *view = desktop_view_at(seat->server, seat->cursor->x, seat->cursor->y, &surface, &sx, &sy);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (!view) {
 | 
						if (!view) {
 | 
				
			||||||
		wlr_seat_pointer_clear_focus(wlr_seat);
 | 
							wlr_seat_pointer_clear_focus(wlr_seat);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy);
 | 
							wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy);
 | 
				
			||||||
		wlr_seat_pointer_notify_motion(wlr_seat, time_msec, sx, sy);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (dx != 0 || dy != 0) {
 | 
							bool focus_changed = wlr_seat->pointer_state.focused_surface != surface;
 | 
				
			||||||
		wlr_relative_pointer_manager_v1_send_relative_motion(seat->server->relative_pointer_manager, wlr_seat,
 | 
							if (!focus_changed && time > 0) {
 | 
				
			||||||
								     (uint64_t) time_msec * 1000, dx, dy, dx_unaccel,
 | 
								wlr_seat_pointer_notify_motion(wlr_seat, time, sx, sy);
 | 
				
			||||||
								     dy_unaccel);
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct cg_drag_icon *drag_icon;
 | 
						struct cg_drag_icon *drag_icon;
 | 
				
			||||||
| 
						 | 
					@ -644,36 +580,38 @@ process_cursor_motion(struct cg_seat *seat, uint32_t time_msec, double dx, doubl
 | 
				
			||||||
		drag_icon_update_position(drag_icon);
 | 
							drag_icon_update_position(drag_icon);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
 | 
						wlr_idle_notify_activity(seat->server->idle, seat->seat);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
handle_cursor_motion_absolute(struct wl_listener *listener, void *data)
 | 
					handle_cursor_motion_absolute(struct wl_listener *listener, void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct cg_seat *seat = wl_container_of(listener, seat, cursor_motion_absolute);
 | 
						struct cg_seat *seat = wl_container_of(listener, seat, cursor_motion_absolute);
 | 
				
			||||||
	struct wlr_pointer_motion_absolute_event *event = data;
 | 
						struct wlr_event_pointer_motion_absolute *event = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	double lx, ly;
 | 
						wlr_cursor_warp_absolute(seat->cursor, event->device, event->x, event->y);
 | 
				
			||||||
	wlr_cursor_absolute_to_layout_coords(seat->cursor, &event->pointer->base, event->x, event->y, &lx, &ly);
 | 
						process_cursor_motion(seat, event->time_msec);
 | 
				
			||||||
 | 
						wlr_idle_notify_activity(seat->server->idle, seat->seat);
 | 
				
			||||||
	double dx = lx - seat->cursor->x;
 | 
					 | 
				
			||||||
	double dy = ly - seat->cursor->y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wlr_cursor_warp_absolute(seat->cursor, &event->pointer->base, event->x, event->y);
 | 
					 | 
				
			||||||
	process_cursor_motion(seat, event->time_msec, dx, dy, dx, dy);
 | 
					 | 
				
			||||||
	wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
handle_cursor_motion_relative(struct wl_listener *listener, void *data)
 | 
					handle_cursor_motion(struct wl_listener *listener, void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct cg_seat *seat = wl_container_of(listener, seat, cursor_motion_relative);
 | 
						struct cg_seat *seat = wl_container_of(listener, seat, cursor_motion);
 | 
				
			||||||
	struct wlr_pointer_motion_event *event = data;
 | 
						struct wlr_event_pointer_motion *event = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_cursor_move(seat->cursor, &event->pointer->base, event->delta_x, event->delta_y);
 | 
						wlr_cursor_move(seat->cursor, event->device, event->delta_x, event->delta_y);
 | 
				
			||||||
	process_cursor_motion(seat, event->time_msec, event->delta_x, event->delta_y, event->unaccel_dx,
 | 
						process_cursor_motion(seat, event->time_msec);
 | 
				
			||||||
			      event->unaccel_dy);
 | 
						wlr_idle_notify_activity(seat->server->idle, seat->seat);
 | 
				
			||||||
	wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					drag_icon_damage(struct cg_drag_icon *drag_icon)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct cg_output *output;
 | 
				
			||||||
 | 
						wl_list_for_each (output, &drag_icon->seat->server->outputs, link) {
 | 
				
			||||||
 | 
							output_damage_surface(output, drag_icon->wlr_drag_icon->surface, drag_icon->lx, drag_icon->ly, true);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
| 
						 | 
					@ -683,6 +621,8 @@ drag_icon_update_position(struct cg_drag_icon *drag_icon)
 | 
				
			||||||
	struct cg_seat *seat = drag_icon->seat;
 | 
						struct cg_seat *seat = drag_icon->seat;
 | 
				
			||||||
	struct wlr_touch_point *point;
 | 
						struct wlr_touch_point *point;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						drag_icon_damage(drag_icon);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (wlr_icon->drag->grab_type) {
 | 
						switch (wlr_icon->drag->grab_type) {
 | 
				
			||||||
	case WLR_DRAG_GRAB_KEYBOARD:
 | 
						case WLR_DRAG_GRAB_KEYBOARD:
 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
| 
						 | 
					@ -700,7 +640,7 @@ drag_icon_update_position(struct cg_drag_icon *drag_icon)
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wlr_scene_node_set_position(&drag_icon->scene_tree->node, drag_icon->lx, drag_icon->ly);
 | 
						drag_icon_damage(drag_icon);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
| 
						 | 
					@ -710,7 +650,6 @@ handle_drag_icon_destroy(struct wl_listener *listener, void *data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wl_list_remove(&drag_icon->link);
 | 
						wl_list_remove(&drag_icon->link);
 | 
				
			||||||
	wl_list_remove(&drag_icon->destroy.link);
 | 
						wl_list_remove(&drag_icon->destroy.link);
 | 
				
			||||||
	wlr_scene_node_destroy(&drag_icon->scene_tree->node);
 | 
					 | 
				
			||||||
	free(drag_icon);
 | 
						free(drag_icon);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -753,11 +692,6 @@ handle_start_drag(struct wl_listener *listener, void *data)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	drag_icon->seat = seat;
 | 
						drag_icon->seat = seat;
 | 
				
			||||||
	drag_icon->wlr_drag_icon = wlr_drag_icon;
 | 
						drag_icon->wlr_drag_icon = wlr_drag_icon;
 | 
				
			||||||
	drag_icon->scene_tree = wlr_scene_subsurface_tree_create(&seat->server->scene->tree, wlr_drag_icon->surface);
 | 
					 | 
				
			||||||
	if (!drag_icon->scene_tree) {
 | 
					 | 
				
			||||||
		free(drag_icon);
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	drag_icon->destroy.notify = handle_drag_icon_destroy;
 | 
						drag_icon->destroy.notify = handle_drag_icon_destroy;
 | 
				
			||||||
	wl_signal_add(&wlr_drag_icon->events.destroy, &drag_icon->destroy);
 | 
						wl_signal_add(&wlr_drag_icon->events.destroy, &drag_icon->destroy);
 | 
				
			||||||
| 
						 | 
					@ -772,7 +706,7 @@ handle_destroy(struct wl_listener *listener, void *data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct cg_seat *seat = wl_container_of(listener, seat, destroy);
 | 
						struct cg_seat *seat = wl_container_of(listener, seat, destroy);
 | 
				
			||||||
	wl_list_remove(&seat->destroy.link);
 | 
						wl_list_remove(&seat->destroy.link);
 | 
				
			||||||
	wl_list_remove(&seat->cursor_motion_relative.link);
 | 
						wl_list_remove(&seat->cursor_motion.link);
 | 
				
			||||||
	wl_list_remove(&seat->cursor_motion_absolute.link);
 | 
						wl_list_remove(&seat->cursor_motion_absolute.link);
 | 
				
			||||||
	wl_list_remove(&seat->cursor_button.link);
 | 
						wl_list_remove(&seat->cursor_button.link);
 | 
				
			||||||
	wl_list_remove(&seat->cursor_axis.link);
 | 
						wl_list_remove(&seat->cursor_axis.link);
 | 
				
			||||||
| 
						 | 
					@ -780,7 +714,6 @@ handle_destroy(struct wl_listener *listener, void *data)
 | 
				
			||||||
	wl_list_remove(&seat->touch_down.link);
 | 
						wl_list_remove(&seat->touch_down.link);
 | 
				
			||||||
	wl_list_remove(&seat->touch_up.link);
 | 
						wl_list_remove(&seat->touch_up.link);
 | 
				
			||||||
	wl_list_remove(&seat->touch_motion.link);
 | 
						wl_list_remove(&seat->touch_motion.link);
 | 
				
			||||||
	wl_list_remove(&seat->touch_frame.link);
 | 
					 | 
				
			||||||
	wl_list_remove(&seat->request_set_cursor.link);
 | 
						wl_list_remove(&seat->request_set_cursor.link);
 | 
				
			||||||
	wl_list_remove(&seat->request_set_selection.link);
 | 
						wl_list_remove(&seat->request_set_selection.link);
 | 
				
			||||||
	wl_list_remove(&seat->request_set_primary_selection.link);
 | 
						wl_list_remove(&seat->request_set_primary_selection.link);
 | 
				
			||||||
| 
						 | 
					@ -846,8 +779,8 @@ seat_create(struct cg_server *server, struct wlr_backend *backend)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	seat->cursor_motion_relative.notify = handle_cursor_motion_relative;
 | 
						seat->cursor_motion.notify = handle_cursor_motion;
 | 
				
			||||||
	wl_signal_add(&seat->cursor->events.motion, &seat->cursor_motion_relative);
 | 
						wl_signal_add(&seat->cursor->events.motion, &seat->cursor_motion);
 | 
				
			||||||
	seat->cursor_motion_absolute.notify = handle_cursor_motion_absolute;
 | 
						seat->cursor_motion_absolute.notify = handle_cursor_motion_absolute;
 | 
				
			||||||
	wl_signal_add(&seat->cursor->events.motion_absolute, &seat->cursor_motion_absolute);
 | 
						wl_signal_add(&seat->cursor->events.motion_absolute, &seat->cursor_motion_absolute);
 | 
				
			||||||
	seat->cursor_button.notify = handle_cursor_button;
 | 
						seat->cursor_button.notify = handle_cursor_button;
 | 
				
			||||||
| 
						 | 
					@ -863,8 +796,6 @@ seat_create(struct cg_server *server, struct wlr_backend *backend)
 | 
				
			||||||
	wl_signal_add(&seat->cursor->events.touch_up, &seat->touch_up);
 | 
						wl_signal_add(&seat->cursor->events.touch_up, &seat->touch_up);
 | 
				
			||||||
	seat->touch_motion.notify = handle_touch_motion;
 | 
						seat->touch_motion.notify = handle_touch_motion;
 | 
				
			||||||
	wl_signal_add(&seat->cursor->events.touch_motion, &seat->touch_motion);
 | 
						wl_signal_add(&seat->cursor->events.touch_motion, &seat->touch_motion);
 | 
				
			||||||
	seat->touch_frame.notify = handle_touch_frame;
 | 
					 | 
				
			||||||
	wl_signal_add(&seat->cursor->events.touch_frame, &seat->touch_frame);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	seat->request_set_cursor.notify = handle_request_set_cursor;
 | 
						seat->request_set_cursor.notify = handle_request_set_cursor;
 | 
				
			||||||
	wl_signal_add(&seat->seat->events.request_set_cursor, &seat->request_set_cursor);
 | 
						wl_signal_add(&seat->seat->events.request_set_cursor, &seat->request_set_cursor);
 | 
				
			||||||
| 
						 | 
					@ -881,9 +812,6 @@ seat_create(struct cg_server *server, struct wlr_backend *backend)
 | 
				
			||||||
	seat->new_input.notify = handle_new_input;
 | 
						seat->new_input.notify = handle_new_input;
 | 
				
			||||||
	wl_signal_add(&backend->events.new_input, &seat->new_input);
 | 
						wl_signal_add(&backend->events.new_input, &seat->new_input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	server->new_virtual_keyboard.notify = handle_virtual_keyboard;
 | 
					 | 
				
			||||||
	server->new_virtual_pointer.notify = handle_virtual_pointer;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	wl_list_init(&seat->drag_icons);
 | 
						wl_list_init(&seat->drag_icons);
 | 
				
			||||||
	seat->request_start_drag.notify = handle_request_start_drag;
 | 
						seat->request_start_drag.notify = handle_request_start_drag;
 | 
				
			||||||
	wl_signal_add(&seat->seat->events.request_start_drag, &seat->request_start_drag);
 | 
						wl_signal_add(&seat->seat->events.request_start_drag, &seat->request_start_drag);
 | 
				
			||||||
| 
						 | 
					@ -903,11 +831,6 @@ seat_destroy(struct cg_seat *seat)
 | 
				
			||||||
	wl_list_remove(&seat->request_start_drag.link);
 | 
						wl_list_remove(&seat->request_start_drag.link);
 | 
				
			||||||
	wl_list_remove(&seat->start_drag.link);
 | 
						wl_list_remove(&seat->start_drag.link);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct cg_keyboard_group *keyboard_group, *keyboard_group_tmp;
 | 
					 | 
				
			||||||
	wl_list_for_each_safe (keyboard_group, keyboard_group_tmp, &seat->keyboard_groups, link) {
 | 
					 | 
				
			||||||
		keyboard_group_destroy(keyboard_group);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Destroying the wlr seat will trigger the destroy handler on our seat,
 | 
						// Destroying the wlr seat will trigger the destroy handler on our seat,
 | 
				
			||||||
	// which will in turn free it.
 | 
						// which will in turn free it.
 | 
				
			||||||
	wlr_seat_destroy(seat->seat);
 | 
						wlr_seat_destroy(seat->seat);
 | 
				
			||||||
| 
						 | 
					@ -917,10 +840,7 @@ struct cg_view *
 | 
				
			||||||
seat_get_focus(struct cg_seat *seat)
 | 
					seat_get_focus(struct cg_seat *seat)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct wlr_surface *prev_surface = seat->seat->keyboard_state.focused_surface;
 | 
						struct wlr_surface *prev_surface = seat->seat->keyboard_state.focused_surface;
 | 
				
			||||||
	if (!prev_surface) {
 | 
						return view_from_wlr_surface(seat->server, prev_surface);
 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return view_from_wlr_surface(prev_surface);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
| 
						 | 
					@ -937,7 +857,7 @@ seat_set_focus(struct cg_seat *seat, struct cg_view *view)
 | 
				
			||||||
#if CAGE_HAS_XWAYLAND
 | 
					#if CAGE_HAS_XWAYLAND
 | 
				
			||||||
	if (view->type == CAGE_XWAYLAND_VIEW) {
 | 
						if (view->type == CAGE_XWAYLAND_VIEW) {
 | 
				
			||||||
		struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
 | 
							struct cg_xwayland_view *xwayland_view = xwayland_view_from_view(view);
 | 
				
			||||||
		if (!wlr_xwayland_surface_override_redirect_wants_focus(xwayland_view->xwayland_surface)) {
 | 
							if (!wlr_xwayland_or_surface_wants_focus(xwayland_view->xwayland_surface)) {
 | 
				
			||||||
			return;
 | 
								return;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -970,14 +890,5 @@ seat_set_focus(struct cg_seat *seat, struct cg_view *view)
 | 
				
			||||||
		wlr_seat_keyboard_notify_enter(wlr_seat, view->wlr_surface, NULL, 0, NULL);
 | 
							wlr_seat_keyboard_notify_enter(wlr_seat, view->wlr_surface, NULL, 0, NULL);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	process_cursor_motion(seat, -1, 0, 0, 0, 0);
 | 
						process_cursor_motion(seat, -1);
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
seat_center_cursor(struct cg_seat *seat)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	/* Place the cursor in the center of the output layout. */
 | 
					 | 
				
			||||||
	struct wlr_box layout_box;
 | 
					 | 
				
			||||||
	wlr_output_layout_get_box(seat->server->output_layout, NULL, &layout_box);
 | 
					 | 
				
			||||||
	wlr_cursor_warp(seat->cursor, NULL, layout_box.width / 2, layout_box.height / 2);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										10
									
								
								seat.h
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								seat.h
									
										
									
									
									
								
							| 
						 | 
					@ -27,7 +27,7 @@ struct cg_seat {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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;
 | 
				
			||||||
| 
						 | 
					@ -39,7 +39,6 @@ struct cg_seat {
 | 
				
			||||||
	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;
 | 
				
			||||||
| 
						 | 
					@ -56,13 +55,12 @@ struct cg_keyboard_group {
 | 
				
			||||||
	struct wl_listener key;
 | 
						struct wl_listener key;
 | 
				
			||||||
	struct wl_listener modifiers;
 | 
						struct wl_listener modifiers;
 | 
				
			||||||
	struct wl_list link; // cg_seat::keyboard_groups
 | 
						struct wl_list link; // cg_seat::keyboard_groups
 | 
				
			||||||
	bool is_virtual;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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 +68,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,7 +77,6 @@ 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;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* The drag icon has a position in layout coordinates. */
 | 
						/* The drag icon has a position in layout coordinates. */
 | 
				
			||||||
	double lx, ly;
 | 
						double lx, ly;
 | 
				
			||||||
| 
						 | 
					@ -91,6 +88,5 @@ struct cg_seat *seat_create(struct cg_server *server, struct wlr_backend *backen
 | 
				
			||||||
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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										39
									
								
								server.h
									
										
									
									
									
								
							
							
						
						
									
										39
									
								
								server.h
									
										
									
									
									
								
							| 
						 | 
					@ -4,17 +4,18 @@
 | 
				
			||||||
#include "config.h"
 | 
					#include "config.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <wayland-server-core.h>
 | 
					#include <wayland-server-core.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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "output.h"
 | 
				
			||||||
 | 
					#include "seat.h"
 | 
				
			||||||
 | 
					#include "view.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum cg_multi_output_mode {
 | 
					enum cg_multi_output_mode {
 | 
				
			||||||
	CAGE_MULTI_OUTPUT_MODE_EXTEND,
 | 
						CAGE_MULTI_OUTPUT_MODE_EXTEND,
 | 
				
			||||||
	CAGE_MULTI_OUTPUT_MODE_LAST,
 | 
						CAGE_MULTI_OUTPUT_MODE_LAST,
 | 
				
			||||||
| 
						 | 
					@ -24,50 +25,32 @@ struct cg_server {
 | 
				
			||||||
	struct wl_display *wl_display;
 | 
						struct wl_display *wl_display;
 | 
				
			||||||
	struct wl_list views;
 | 
						struct wl_list views;
 | 
				
			||||||
	struct wlr_backend *backend;
 | 
						struct wlr_backend *backend;
 | 
				
			||||||
	struct wlr_renderer *renderer;
 | 
					 | 
				
			||||||
	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;
 | 
						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 wlr_scene *scene;
 | 
					 | 
				
			||||||
	/* Includes disabled outputs; depending on the output_mode
 | 
						/* Includes disabled outputs; depending on the output_mode
 | 
				
			||||||
	 * some outputs may be disabled. */
 | 
						 * some outputs may be disabled. */
 | 
				
			||||||
	struct wl_list outputs; // cg_output::link
 | 
						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;
 | 
						bool allow_vt_switch;
 | 
				
			||||||
	bool return_app_code;
 | 
						enum wl_output_transform output_transform;
 | 
				
			||||||
	bool terminated;
 | 
					#ifdef DEBUG
 | 
				
			||||||
	enum wlr_log_importance log_level;
 | 
						bool debug_damage_tracking;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void server_terminate(struct cg_server *server);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										36
									
								
								util.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								util.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,36 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Cage: A Wayland kiosk.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2019 The Sway authors
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * See the LICENSE file accompanying this file.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <wlr/types/wlr_box.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "util.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					scale_length(int length, int offset, float scale)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * One does not simply multiply the width by the scale. We allow fractional
 | 
				
			||||||
 | 
						 * scaling, which means the resulting scaled width might be a decimal.
 | 
				
			||||||
 | 
						 * So we round it.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * But even this can produce undesirable results depending on the X or Y
 | 
				
			||||||
 | 
						 * offset of the box. For example, with a scale of 1.5, a box with
 | 
				
			||||||
 | 
						 * width=1 should not scale to 2px if its X coordinate is 1, because the
 | 
				
			||||||
 | 
						 * X coordinate would have scaled to 2px.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						return round((offset + length) * scale) - round(offset * scale);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					scale_box(struct wlr_box *box, float scale)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						box->width = scale_length(box->width, box->x, scale);
 | 
				
			||||||
 | 
						box->height = scale_length(box->height, box->y, scale);
 | 
				
			||||||
 | 
						box->x = round(box->x * scale);
 | 
				
			||||||
 | 
						box->y = round(box->y * scale);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										11
									
								
								util.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								util.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					#ifndef CG_UTIL_H
 | 
				
			||||||
 | 
					#define CG_UTIL_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <wlr/types/wlr_box.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Apply scale to a width or height. */
 | 
				
			||||||
 | 
					int scale_length(int length, int offset, float scale);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void scale_box(struct wlr_box *box, float scale);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										189
									
								
								view.c
									
										
									
									
									
								
							
							
						
						
									
										189
									
								
								view.c
									
										
									
									
									
								
							| 
						 | 
					@ -1,20 +1,20 @@
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Cage: A Wayland kiosk.
 | 
					 * Cage: A Wayland kiosk.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Copyright (C) 2018-2021 Jente Hidskes
 | 
					 * Copyright (C) 2018-2020 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-core.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_part(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)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -46,6 +136,24 @@ 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_part(struct cg_view *view)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct cg_output *output;
 | 
				
			||||||
 | 
						wl_list_for_each (output, &view->server->outputs, link) {
 | 
				
			||||||
 | 
							output_damage_surface(output, view->wlr_surface, view->lx, view->ly, false);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					view_damage_whole(struct cg_view *view)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct cg_output *output;
 | 
				
			||||||
 | 
						wl_list_for_each (output, &view->server->outputs, link) {
 | 
				
			||||||
 | 
							output_damage_surface(output, view->wlr_surface, view->lx, view->ly, true);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
view_activate(struct cg_view *view, bool activate)
 | 
					view_activate(struct cg_view *view, bool activate)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -66,11 +174,6 @@ view_maximize(struct cg_view *view, struct wlr_box *layout_box)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	view->lx = layout_box->x;
 | 
						view->lx = layout_box->x;
 | 
				
			||||||
	view->ly = layout_box->y;
 | 
						view->ly = layout_box->y;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (view->scene_tree) {
 | 
					 | 
				
			||||||
		wlr_scene_node_set_position(&view->scene_tree->node, view->lx, view->ly);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	view->impl->maximize(view, layout_box->width, layout_box->height);
 | 
						view->impl->maximize(view, layout_box->width, layout_box->height);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -82,32 +185,33 @@ view_center(struct cg_view *view, struct wlr_box *layout_box)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	view->lx = (layout_box->width - width) / 2;
 | 
						view->lx = (layout_box->width - width) / 2;
 | 
				
			||||||
	view->ly = (layout_box->height - height) / 2;
 | 
						view->ly = (layout_box->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;
 | 
						struct wlr_box *layout_box = wlr_output_layout_get_box(view->server->output_layout, NULL);
 | 
				
			||||||
	wlr_output_layout_get_box(view->server->output_layout, NULL, &layout_box);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (view_is_primary(view) || view_extends_output_layout(view, &layout_box)) {
 | 
						if (view_is_primary(view) || view_extends_output_layout(view, layout_box)) {
 | 
				
			||||||
		view_maximize(view, &layout_box);
 | 
							view_maximize(view, layout_box);
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		view_center(view, &layout_box);
 | 
							view_center(view, layout_box);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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
 | 
				
			||||||
 | 
					view_for_each_popup(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!view->impl->for_each_popup) {
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						view->impl->for_each_popup(view, iterator, data);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
| 
						 | 
					@ -115,24 +219,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
 | 
				
			||||||
| 
						 | 
					@ -172,11 +280,24 @@ view_init(struct cg_view *view, struct cg_server *server, enum cg_view_type type
 | 
				
			||||||
	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);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										40
									
								
								view.h
									
										
									
									
									
								
							
							
						
						
									
										40
									
								
								view.h
									
										
									
									
									
								
							| 
						 | 
					@ -5,9 +5,9 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <stdbool.h>
 | 
					#include <stdbool.h>
 | 
				
			||||||
#include <wayland-server-core.h>
 | 
					#include <wayland-server-core.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,16 @@ 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;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* The view has a position in layout coordinates. */
 | 
						/* The view has a position in layout coordinates. */
 | 
				
			||||||
	int lx, ly;
 | 
						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 +44,47 @@ 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);
 | 
				
			||||||
 | 
						void (*for_each_popup)(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_part(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_for_each_popup(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
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										406
									
								
								xdg_shell.c
									
										
									
									
									
								
							
							
						
						
									
										406
									
								
								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-core.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,104 @@ 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_xdg_popup *popup = (struct cg_xdg_popup *) child;
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					handle_xdg_popup_map(struct wl_listener *listener, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct cg_xdg_popup *popup = wl_container_of(listener, popup, map);
 | 
				
			||||||
 | 
						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 cg_server *server = view->server;
 | 
						struct cg_server *server = view->server;
 | 
				
			||||||
	struct wlr_box *popup_box = &popup->current.geometry;
 | 
						struct wlr_box *popup_box = &popup->wlr_popup->geometry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct wlr_output_layout *output_layout = server->output_layout;
 | 
						struct wlr_output_layout *output_layout = server->output_layout;
 | 
				
			||||||
	struct wlr_output *wlr_output =
 | 
						struct wlr_output *wlr_output =
 | 
				
			||||||
		wlr_output_layout_output_at(output_layout, view->lx + popup_box->x, view->ly + popup_box->y);
 | 
							wlr_output_layout_output_at(output_layout, view->lx + popup_box->x, view->ly + popup_box->y);
 | 
				
			||||||
	struct wlr_box output_box;
 | 
						struct wlr_box *output_box = wlr_output_layout_get_box(output_layout, wlr_output);
 | 
				
			||||||
	wlr_output_layout_get_box(output_layout, wlr_output, &output_box);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct wlr_box output_toplevel_box = {
 | 
						struct wlr_box output_toplevel_box = {
 | 
				
			||||||
		.x = output_box.x - view->lx,
 | 
							.x = output_box->x - view->lx,
 | 
				
			||||||
		.y = output_box.y - view->ly,
 | 
							.y = output_box->y - view->ly,
 | 
				
			||||||
		.width = output_box.width,
 | 
							.width = output_box->width,
 | 
				
			||||||
		.height = output_box.height,
 | 
							.height = output_box->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 +152,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 +182,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 +198,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 +217,81 @@ 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);
 | 
				
			||||||
	/**
 | 
					 | 
				
			||||||
	 * Certain clients do not like figuring out their own window geometry if they
 | 
					 | 
				
			||||||
	 * display in fullscreen mode, so we set it here.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	struct wlr_box layout_box;
 | 
					 | 
				
			||||||
	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)
 | 
					for_each_popup(struct cg_view *view, wlr_surface_iterator_func_t iterator, void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
 | 
				
			||||||
 | 
						wlr_xdg_surface_for_each_popup(xdg_shell_view->xdg_surface, iterator, data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct wlr_surface *
 | 
				
			||||||
 | 
					wlr_surface_at(struct cg_view *view, double sx, double sy, double *sub_x, double *sub_y)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct cg_xdg_shell_view *xdg_shell_view = xdg_shell_view_from_view(view);
 | 
				
			||||||
 | 
						return wlr_xdg_surface_surface_at(xdg_shell_view->xdg_surface, sx, sy, sub_x, sub_y);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					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_part(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 +304,20 @@ 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,
 | 
				
			||||||
 | 
						.for_each_popup = for_each_popup,
 | 
				
			||||||
 | 
						.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 +326,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 +356,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);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										27
									
								
								xdg_shell.h
									
										
									
									
									
								
							
							
						
						
									
										27
									
								
								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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										77
									
								
								xwayland.c
									
										
									
									
									
								
							
							
						
						
									
										77
									
								
								xwayland.c
									
										
									
									
									
								
							| 
						 | 
					@ -9,6 +9,7 @@
 | 
				
			||||||
#include <stdbool.h>
 | 
					#include <stdbool.h>
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include <wayland-server-core.h>
 | 
					#include <wayland-server-core.h>
 | 
				
			||||||
 | 
					#include <wlr/types/wlr_box.h>
 | 
				
			||||||
#include <wlr/util/log.h>
 | 
					#include <wlr/util/log.h>
 | 
				
			||||||
#include <wlr/xwayland.h>
 | 
					#include <wlr/xwayland.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
 | 
				
			||||||
| 
						 | 
					@ -92,7 +86,7 @@ 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, view->lx, view->ly, output_width,
 | 
				
			||||||
				       output_height);
 | 
									       output_height);
 | 
				
			||||||
	wlr_xwayland_surface_set_maximized(xwayland_view->xwayland_surface, true, true);
 | 
						wlr_xwayland_surface_set_maximized(xwayland_view->xwayland_surface, true);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
| 
						 | 
					@ -102,6 +96,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 +116,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_part(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);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -130,7 +148,12 @@ handle_xwayland_surface_map(struct wl_listener *listener, void *data)
 | 
				
			||||||
		view->ly = xwayland_view->xwayland_surface->y;
 | 
							view->ly = 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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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,12 @@ 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,
 | 
				
			||||||
 | 
						/* XWayland doesn't have a separate popup iterator. */
 | 
				
			||||||
 | 
						.for_each_popup = NULL,
 | 
				
			||||||
 | 
						.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 +200,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;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,10 +10,9 @@ struct cg_xwayland_view {
 | 
				
			||||||
	struct cg_view view;
 | 
						struct cg_view view;
 | 
				
			||||||
	struct wlr_xwayland_surface *xwayland_surface;
 | 
						struct wlr_xwayland_surface *xwayland_surface;
 | 
				
			||||||
	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