File magpie-0.0.0+209.obscpio of Package magpie-1
07070100000000000081A400000000000000000000000167624870000005E7000000000000000000000000000000000000001F00000000magpie-0.0.0+209/.clang-format---
Language: Cpp
AccessModifierOffset: -2
AlignAfterOpenBracket: DontAlign
AlignConsecutiveAssignments:
Enabled: false
AlignOperands: false
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortLoopsOnASingleLine: true
AlwaysBreakBeforeMultilineStrings: true
BinPackArguments: true
BinPackParameters: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
ColumnLimit: 128
CommentPragmas: '\*\<'
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
IndentCaseLabels: true
IndentWidth: 4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 3
PenaltyBreakBeforeFirstCallParameter: 400
PenaltyBreakComment: 0
PenaltyBreakString: 500
PenaltyExcessCharacter: 10000
PenaltyReturnTypeOnItsOwnLine: 600
PointerAlignment: Left
SortIncludes: Never
SpaceAfterCStyleCast: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: c++20
TabWidth: 4
UseTab: Always
...
07070100000001000081A400000000000000000000000167624870000000BD000000000000000000000000000000000000001F00000000magpie-0.0.0+209/.editorconfig# http://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.{c,cpp,h,hpp}]
indent_style = tab
tab_width = 4
07070100000002000041ED0000000000000000000000026762487000000000000000000000000000000000000000000000001900000000magpie-0.0.0+209/.github07070100000003000081A40000000000000000000000016762487000000023000000000000000000000000000000000000002500000000magpie-0.0.0+209/.github/FUNDING.ymlopen_collective: buddies-of-budgie
07070100000004000041ED0000000000000000000000026762487000000000000000000000000000000000000000000000002300000000magpie-0.0.0+209/.github/workflows07070100000005000081A40000000000000000000000016762487000000561000000000000000000000000000000000000002D00000000magpie-0.0.0+209/.github/workflows/build.ymlname: Build
on: [push, pull_request]
jobs:
alpine:
runs-on: ubuntu-latest
container: "alpine:edge"
steps:
- name: Install prerequisites
run: |
apk add build-base git meson hwdata-dev libdisplay-info-dev libinput-dev libliftoff-dev libseat-dev libxcb-dev libxkbcommon-dev pixman-dev wayland-dev wayland-protocols xcb-util-renderutil-dev xcb-util-wm-dev xwayland-dev
- uses: actions/checkout@v4
- name: Build magpie
run: |
meson setup build
meson compile -C build
fedora:
runs-on: ubuntu-latest
container: "registry.fedoraproject.org/fedora:rawhide"
steps:
- name: Install prerequisites
run: |
dnf --assumeyes install gcc-c++ meson git-core pixman-devel \
'pkgconfig(hwdata)' \
'pkgconfig(libdisplay-info)' \
'pkgconfig(libdrm)' \
'pkgconfig(libliftoff)' \
'pkgconfig(libseat)' \
'pkgconfig(wayland-protocols)' \
'pkgconfig(wayland-scanner)' \
'pkgconfig(wayland-server)' \
'pkgconfig(wlroots)' \
'pkgconfig(xcb)' \
'pkgconfig(xcb-ewmh)' \
'pkgconfig(xcb-renderutil)' \
'pkgconfig(xkbcommon)' \
'pkgconfig(xwayland)'
- uses: actions/checkout@v4
- name: Build magpie
run: |
meson setup build
meson compile -C build
07070100000006000081A400000000000000000000000167624870000001D3000000000000000000000000000000000000002C00000000magpie-0.0.0+209/.github/workflows/test.ymlname: test
on: [push, pull_request]
jobs:
test:
name: flawfinder
runs-on: ubuntu-latest
steps:
- name: Flawfinder Scan
uses: david-a-wheeler/flawfinder@2.0.19
with:
arguments: 'src'
output: 'flawfinder_results.txt'
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: 'flawfinder_results.txt'
path: 'flawfinder_results.txt'
retention-days: 7
07070100000007000081A4000000000000000000000001676248700000004D000000000000000000000000000000000000001C00000000magpie-0.0.0+209/.gitignoresubprojects/*/
/*build*/
*.tar.*
*.out
*.vgcore
justfile
/.*/
!/.github/
07070100000008000081A40000000000000000000000016762487000000072000000000000000000000000000000000000001C00000000magpie-0.0.0+209/CODEOWNERS# Global rule, if anything matches after this then that rule takes precedent
* @serebit @BuddiesOfBudgie/best-buds07070100000009000081A40000000000000000000000016762487000002C5E000000000000000000000000000000000000001D00000000magpie-0.0.0+209/LICENSE.txt
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
0707010000000A000081A40000000000000000000000016762487000000334000000000000000000000000000000000000001B00000000magpie-0.0.0+209/README.md# Magpie
[](https://matrix.to/#/#buddies-of-budgie:matrix.org)
[](https://buddiesofbudgie.org)
[](https://opencollective.com/buddies-of-budgie)
Magpie v1 is a wlroots-based Wayland compositor designed for the Budgie Desktop.
## License
Magpie v1 is available under the `Apache-2.0` license. Magpie v0, a fork of GNOME's Mutter window manager, was licensed under the terms of the `GPL-2.0-or-later` license.
## Authors
Copyright © Budgie Desktop Developers
See our [contributors graph](https://github.com/BuddiesOfBudgie/magpie/graphs/contributors)!
0707010000000B000041ED0000000000000000000000026762487000000000000000000000000000000000000000000000001600000000magpie-0.0.0+209/data0707010000000C000081A40000000000000000000000016762487000000072000000000000000000000000000000000000002200000000magpie-0.0.0+209/data/meson.buildinstall_data(
'org.buddiesofbudgie.MagpieWm.desktop',
install_dir: join_paths(datadir, 'applications'),
)
0707010000000D000081A40000000000000000000000016762487000000117000000000000000000000000000000000000003B00000000magpie-0.0.0+209/data/org.buddiesofbudgie.MagpieWm.desktop[Desktop Entry]
Type=Application
Name=Magpie Window Manager
Exec=magpie-wm
NoDisplay=true
X-GNOME-WMSettingsModule=metacity
X-GNOME-WMName=Magpie
X-GnomeWMSettingsLibrary=metacity
X-GNOME-Autostart-Phase=WindowManager
X-GNOME-Provides=windowmanager
X-GNOME-Autostart-Notify=true
0707010000000E000081A40000000000000000000000016762487000000DF4000000000000000000000000000000000000001D00000000magpie-0.0.0+209/meson.buildproject(
'magpie',
['c', 'cpp'],
version: '1.0',
license: 'Apache-2.0',
default_options: [
'cpp_std=c++20',
'warning_level=3'
],
meson_version: '>= 0.62.0',
)
prefix = get_option('prefix')
datadir = join_paths(prefix, get_option('datadir'))
dep_m = meson.get_compiler('cpp').find_library('m', required: false)
dep_argparse = dependency('argparse', version: '>= 3.0', fallback: ['argparse'])
dep_wayland_protocols = dependency('wayland-protocols', version: '>= 1.31')
dep_wayland_scanner = dependency('wayland-scanner')
dep_wayland_server = dependency('wayland-server')
dep_wlroots = dependency('wlroots-0.18', version: ['>= 0.18.0', '< 0.19.0'], fallback: 'wlroots', default_options: ['examples=false'])
dep_xcb = dependency('xcb')
dep_xkbcommon = dependency('xkbcommon')
foreach feature : ['drm_backend', 'libinput_backend', 'session', 'xwayland']
var_feature = 'have_' + feature
if dep_wlroots.get_variable(pkgconfig: var_feature, internal: var_feature, default_value: 'false') != 'true'
error('Wlroots was not built with ' + feature + ' support. Check for missing dependencies.')
endif
endforeach
wl = import('unstable-wayland')
budgie_keyboard_shortcuts_protocol = wl.scan_xml(join_paths('protocols', 'budgie-keyboard-shortcuts.xml'), server: true)
wayland_protocol_dir = dep_wayland_protocols.get_variable('pkgdatadir')
wayland_scanner = find_program(
dep_wayland_scanner.get_variable('wayland_scanner'),
native: true,
)
content_type_protocol = custom_target(
'content_type_v1_protocol_h',
input: join_paths(wayland_protocol_dir, 'staging', 'content-type', 'content-type-v1.xml'),
output: 'content-type-v1-protocol.h',
command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'],
build_by_default: false,
)
cursor_shape_protocol = custom_target(
'cursor_shape_v1_protocol_h',
input: join_paths(wayland_protocol_dir, 'staging', 'cursor-shape', 'cursor-shape-v1.xml'),
output: 'cursor-shape-v1-protocol.h',
command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'],
build_by_default: false,
)
xdg_shell_protocol = custom_target(
'xdg_shell_server_h',
input: join_paths(wayland_protocol_dir, 'stable', 'xdg-shell', 'xdg-shell.xml'),
output: 'xdg-shell-protocol.h',
command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'],
build_by_default: false,
)
wlr_pointer_constraints_protocol = custom_target(
'wlr_pointer_constraints_unstable_v1_protocol_h',
input: join_paths(wayland_protocol_dir, 'unstable', 'pointer-constraints', 'pointer-constraints-unstable-v1.xml'),
output: 'pointer-constraints-unstable-v1-protocol.h',
command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'],
build_by_default: false,
)
wlr_output_power_management_protocol = custom_target(
'wlr_output_power_management_unstable_v1_protocol_h',
input: join_paths(meson.project_source_root(), 'protocols', 'wlr-output-power-management-unstable-v1.xml'),
output: 'wlr-output-power-management-unstable-v1-protocol.h',
command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'],
build_by_default: false,
)
wlr_layer_shell_protocol = custom_target(
'wlr_layer_shell_unstable_v1_protocol_h',
input: join_paths(meson.project_source_root(), 'protocols', 'wlr-layer-shell-unstable-v1.xml'),
output: 'wlr-layer-shell-unstable-v1-protocol.h',
command: [wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@'],
build_by_default: false,
)
subdir('src')
subdir('data')
0707010000000F000041ED0000000000000000000000026762487000000000000000000000000000000000000000000000001B00000000magpie-0.0.0+209/protocols07070100000010000081A400000000000000000000000167624870000009AE000000000000000000000000000000000000003900000000magpie-0.0.0+209/protocols/budgie-keyboard-shortcuts.xml<?xml version="1.0" encoding="UTF-8"?>
<protocol name="budgie_keyboard_shortcuts">
<copyright>
Copyright Budgie Desktop Developers
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
</copyright>
<description summary="Budgie-specific protocol">
This is a private Budgie Desktop protocol. Regular Wayland clients must not use it.
This protocol is currently unversioned and may change without notice.
</description>
<interface name="budgie_keyboard_shortcuts_manager" version="1">
<request name="destroy" type="destructor"></request>
<request name="subscribe">
<description summary="Subscribe to keyboard shortcut events"></description>
<arg name="id" type="new_id" interface="budgie_keyboard_shortcuts_subscriber" />
</request>
</interface>
<interface name="budgie_keyboard_shortcuts_subscriber" version="1">
<request name="destroy" type="destructor"></request>
<request name="register_shortcut">
<description summary="Register a shortcut"></description>
<arg name="modifiers" type="uint"/>
<arg name="keycode" type="uint"/>
</request>
<request name="unregister_shortcut">
<description summary="Unregister a shortcut"></description>
<arg name="modifiers" type="uint"/>
<arg name="keycode" type="uint"/>
</request>
<event name="shortcut_press">
<description summary="Shortcut press event">
Notification that a registered keyboard shortcut has been pressed.
The keycode is provided in xkb_v1 format. The modifier field is a bitmask of possible modifiers.
</description>
<arg name="time_msec" type="uint"/>
<arg name="modifiers" type="uint"/>
<arg name="keycode" type="uint"/>
</event>
</interface>
</protocol>
07070100000011000081A4000000000000000000000001676248700000481E000000000000000000000000000000000000003B00000000magpie-0.0.0+209/protocols/wlr-layer-shell-unstable-v1.xml<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_layer_shell_unstable_v1">
<copyright>
Copyright © 2017 Drew DeVault
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
</copyright>
<interface name="zwlr_layer_shell_v1" version="4">
<description summary="create surfaces that are layers of the desktop">
Clients can use this interface to assign the surface_layer role to
wl_surfaces. Such surfaces are assigned to a "layer" of the output and
rendered with a defined z-depth respective to each other. They may also be
anchored to the edges and corners of a screen and specify input handling
semantics. This interface should be suitable for the implementation of
many desktop shell components, and a broad number of other applications
that interact with the desktop.
</description>
<request name="get_layer_surface">
<description summary="create a layer_surface from a surface">
Create a layer surface for an existing surface. This assigns the role of
layer_surface, or raises a protocol error if another role is already
assigned.
Creating a layer surface from a wl_surface which has a buffer attached
or committed is a client error, and any attempts by a client to attach
or manipulate a buffer prior to the first layer_surface.configure call
must also be treated as errors.
After creating a layer_surface object and setting it up, the client
must perform an initial commit without any buffer attached.
The compositor will reply with a layer_surface.configure event.
The client must acknowledge it and is then allowed to attach a buffer
to map the surface.
You may pass NULL for output to allow the compositor to decide which
output to use. Generally this will be the one that the user most
recently interacted with.
Clients can specify a namespace that defines the purpose of the layer
surface.
</description>
<arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
<arg name="surface" type="object" interface="wl_surface"/>
<arg name="output" type="object" interface="wl_output" allow-null="true"/>
<arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
<arg name="namespace" type="string" summary="namespace for the layer surface"/>
</request>
<enum name="error">
<entry name="role" value="0" summary="wl_surface has another role"/>
<entry name="invalid_layer" value="1" summary="layer value is invalid"/>
<entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
</enum>
<enum name="layer">
<description summary="available layers for surfaces">
These values indicate which layers a surface can be rendered in. They
are ordered by z depth, bottom-most first. Traditional shell surfaces
will typically be rendered between the bottom and top layers.
Fullscreen shell surfaces are typically rendered at the top layer.
Multiple surfaces can share a single layer, and ordering within a
single layer is undefined.
</description>
<entry name="background" value="0"/>
<entry name="bottom" value="1"/>
<entry name="top" value="2"/>
<entry name="overlay" value="3"/>
</enum>
<!-- Version 3 additions -->
<request name="destroy" type="destructor" since="3">
<description summary="destroy the layer_shell object">
This request indicates that the client will not use the layer_shell
object any more. Objects that have been created through this instance
are not affected.
</description>
</request>
</interface>
<interface name="zwlr_layer_surface_v1" version="4">
<description summary="layer metadata interface">
An interface that may be implemented by a wl_surface, for surfaces that
are designed to be rendered as a layer of a stacked desktop-like
environment.
Layer surface state (layer, size, anchor, exclusive zone,
margin, interactivity) is double-buffered, and will be applied at the
time wl_surface.commit of the corresponding wl_surface is called.
Attaching a null buffer to a layer surface unmaps it.
Unmapping a layer_surface means that the surface cannot be shown by the
compositor until it is explicitly mapped again. The layer_surface
returns to the state it had right after layer_shell.get_layer_surface.
The client can re-map the surface by performing a commit without any
buffer attached, waiting for a configure event and handling it as usual.
</description>
<request name="set_size">
<description summary="sets the size of the surface">
Sets the size of the surface in surface-local coordinates. The
compositor will display the surface centered with respect to its
anchors.
If you pass 0 for either value, the compositor will assign it and
inform you of the assignment in the configure event. You must set your
anchor to opposite edges in the dimensions you omit; not doing so is a
protocol error. Both values are 0 by default.
Size is double-buffered, see wl_surface.commit.
</description>
<arg name="width" type="uint"/>
<arg name="height" type="uint"/>
</request>
<request name="set_anchor">
<description summary="configures the anchor point of the surface">
Requests that the compositor anchor the surface to the specified edges
and corners. If two orthogonal edges are specified (e.g. 'top' and
'left'), then the anchor point will be the intersection of the edges
(e.g. the top left corner of the output); otherwise the anchor point
will be centered on that edge, or in the center if none is specified.
Anchor is double-buffered, see wl_surface.commit.
</description>
<arg name="anchor" type="uint" enum="anchor"/>
</request>
<request name="set_exclusive_zone">
<description summary="configures the exclusive geometry of this surface">
Requests that the compositor avoids occluding an area with other
surfaces. The compositor's use of this information is
implementation-dependent - do not assume that this region will not
actually be occluded.
A positive value is only meaningful if the surface is anchored to one
edge or an edge and both perpendicular edges. If the surface is not
anchored, anchored to only two perpendicular edges (a corner), anchored
to only two parallel edges or anchored to all edges, a positive value
will be treated the same as zero.
A positive zone is the distance from the edge in surface-local
coordinates to consider exclusive.
Surfaces that do not wish to have an exclusive zone may instead specify
how they should interact with surfaces that do. If set to zero, the
surface indicates that it would like to be moved to avoid occluding
surfaces with a positive exclusive zone. If set to -1, the surface
indicates that it would not like to be moved to accommodate for other
surfaces, and the compositor should extend it all the way to the edges
it is anchored to.
For example, a panel might set its exclusive zone to 10, so that
maximized shell surfaces are not shown on top of it. A notification
might set its exclusive zone to 0, so that it is moved to avoid
occluding the panel, but shell surfaces are shown underneath it. A
wallpaper or lock screen might set their exclusive zone to -1, so that
they stretch below or over the panel.
The default value is 0.
Exclusive zone is double-buffered, see wl_surface.commit.
</description>
<arg name="zone" type="int"/>
</request>
<request name="set_margin">
<description summary="sets a margin from the anchor point">
Requests that the surface be placed some distance away from the anchor
point on the output, in surface-local coordinates. Setting this value
for edges you are not anchored to has no effect.
The exclusive zone includes the margin.
Margin is double-buffered, see wl_surface.commit.
</description>
<arg name="top" type="int"/>
<arg name="right" type="int"/>
<arg name="bottom" type="int"/>
<arg name="left" type="int"/>
</request>
<enum name="keyboard_interactivity">
<description summary="types of keyboard interaction possible for a layer shell surface">
Types of keyboard interaction possible for layer shell surfaces. The
rationale for this is twofold: (1) some applications are not interested
in keyboard events and not allowing them to be focused can improve the
desktop experience; (2) some applications will want to take exclusive
keyboard focus.
</description>
<entry name="none" value="0">
<description summary="no keyboard focus is possible">
This value indicates that this surface is not interested in keyboard
events and the compositor should never assign it the keyboard focus.
This is the default value, set for newly created layer shell surfaces.
This is useful for e.g. desktop widgets that display information or
only have interaction with non-keyboard input devices.
</description>
</entry>
<entry name="exclusive" value="1">
<description summary="request exclusive keyboard focus">
Request exclusive keyboard focus if this surface is above the shell surface layer.
For the top and overlay layers, the seat will always give
exclusive keyboard focus to the top-most layer which has keyboard
interactivity set to exclusive. If this layer contains multiple
surfaces with keyboard interactivity set to exclusive, the compositor
determines the one receiving keyboard events in an implementation-
defined manner. In this case, no guarantee is made when this surface
will receive keyboard focus (if ever).
For the bottom and background layers, the compositor is allowed to use
normal focus semantics.
This setting is mainly intended for applications that need to ensure
they receive all keyboard events, such as a lock screen or a password
prompt.
</description>
</entry>
<entry name="on_demand" value="2" since="4">
<description summary="request regular keyboard focus semantics">
This requests the compositor to allow this surface to be focused and
unfocused by the user in an implementation-defined manner. The user
should be able to unfocus this surface even regardless of the layer
it is on.
Typically, the compositor will want to use its normal mechanism to
manage keyboard focus between layer shell surfaces with this setting
and regular toplevels on the desktop layer (e.g. click to focus).
Nevertheless, it is possible for a compositor to require a special
interaction to focus or unfocus layer shell surfaces (e.g. requiring
a click even if focus follows the mouse normally, or providing a
keybinding to switch focus between layers).
This setting is mainly intended for desktop shell components (e.g.
panels) that allow keyboard interaction. Using this option can allow
implementing a desktop shell that can be fully usable without the
mouse.
</description>
</entry>
</enum>
<request name="set_keyboard_interactivity">
<description summary="requests keyboard events">
Set how keyboard events are delivered to this surface. By default,
layer shell surfaces do not receive keyboard events; this request can
be used to change this.
This setting is inherited by child surfaces set by the get_popup
request.
Layer surfaces receive pointer, touch, and tablet events normally. If
you do not want to receive them, set the input region on your surface
to an empty region.
Keyboard interactivity is double-buffered, see wl_surface.commit.
</description>
<arg name="keyboard_interactivity" type="uint" enum="keyboard_interactivity"/>
</request>
<request name="get_popup">
<description summary="assign this layer_surface as an xdg_popup parent">
This assigns an xdg_popup's parent to this layer_surface. This popup
should have been created via xdg_surface::get_popup with the parent set
to NULL, and this request must be invoked before committing the popup's
initial state.
See the documentation of xdg_popup for more details about what an
xdg_popup is and how it is used.
</description>
<arg name="popup" type="object" interface="xdg_popup"/>
</request>
<request name="ack_configure">
<description summary="ack a configure event">
When a configure event is received, if a client commits the
surface in response to the configure event, then the client
must make an ack_configure request sometime before the commit
request, passing along the serial of the configure event.
If the client receives multiple configure events before it
can respond to one, it only has to ack the last configure event.
A client is not required to commit immediately after sending
an ack_configure request - it may even ack_configure several times
before its next surface commit.
A client may send multiple ack_configure requests before committing, but
only the last request sent before a commit indicates which configure
event the client really is responding to.
</description>
<arg name="serial" type="uint" summary="the serial from the configure event"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the layer_surface">
This request destroys the layer surface.
</description>
</request>
<event name="configure">
<description summary="suggest a surface change">
The configure event asks the client to resize its surface.
Clients should arrange their surface for the new states, and then send
an ack_configure request with the serial sent in this configure event at
some point before committing the new surface.
The client is free to dismiss all but the last configure event it
received.
The width and height arguments specify the size of the window in
surface-local coordinates.
The size is a hint, in the sense that the client is free to ignore it if
it doesn't resize, pick a smaller size (to satisfy aspect ratio or
resize in steps of NxM pixels). If the client picks a smaller size and
is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
surface will be centered on this axis.
If the width or height arguments are zero, it means the client should
decide its own window dimension.
</description>
<arg name="serial" type="uint"/>
<arg name="width" type="uint"/>
<arg name="height" type="uint"/>
</event>
<event name="closed">
<description summary="surface should be closed">
The closed event is sent by the compositor when the surface will no
longer be shown. The output may have been destroyed or the user may
have asked for it to be removed. Further changes to the surface will be
ignored. The client should destroy the resource after receiving this
event, and create a new surface if they so choose.
</description>
</event>
<enum name="error">
<entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
<entry name="invalid_size" value="1" summary="size is invalid"/>
<entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
<entry name="invalid_keyboard_interactivity" value="3" summary="keyboard interactivity is invalid"/>
</enum>
<enum name="anchor" bitfield="true">
<entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
<entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
<entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
<entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
</enum>
<!-- Version 2 additions -->
<request name="set_layer" since="2">
<description summary="change the layer of the surface">
Change the layer that the surface is rendered on.
Layer is double-buffered, see wl_surface.commit.
</description>
<arg name="layer" type="uint" enum="zwlr_layer_shell_v1.layer" summary="layer to move this surface to"/>
</request>
</interface>
</protocol>
07070100000012000081A400000000000000000000000167624870000015DD000000000000000000000000000000000000004700000000magpie-0.0.0+209/protocols/wlr-output-power-management-unstable-v1.xml<?xml version="1.0" encoding="UTF-8"?>
<protocol name="wlr_output_power_management_unstable_v1">
<copyright>
Copyright © 2019 Purism SPC
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
</copyright>
<description summary="Control power management modes of outputs">
This protocol allows clients to control power management modes
of outputs that are currently part of the compositor space. The
intent is to allow special clients like desktop shells to power
down outputs when the system is idle.
To modify outputs not currently part of the compositor space see
wlr-output-management.
Warning! The protocol described in this file is experimental and
backward incompatible changes may be made. Backward compatible changes
may be added together with the corresponding interface version bump.
Backward incompatible changes are done by bumping the version number in
the protocol and interface names and resetting the interface version.
Once the protocol is to be declared stable, the 'z' prefix and the
version number in the protocol and interface names are removed and the
interface version number is reset.
</description>
<interface name="zwlr_output_power_manager_v1" version="1">
<description summary="manager to create per-output power management">
This interface is a manager that allows creating per-output power
management mode controls.
</description>
<request name="get_output_power">
<description summary="get a power management for an output">
Create an output power management mode control that can be used to
adjust the power management mode for a given output.
</description>
<arg name="id" type="new_id" interface="zwlr_output_power_v1"/>
<arg name="output" type="object" interface="wl_output"/>
</request>
<request name="destroy" type="destructor">
<description summary="destroy the manager">
All objects created by the manager will still remain valid, until their
appropriate destroy request has been called.
</description>
</request>
</interface>
<interface name="zwlr_output_power_v1" version="1">
<description summary="adjust power management mode for an output">
This object offers requests to set the power management mode of
an output.
</description>
<enum name="mode">
<entry name="off" value="0"
summary="Output is turned off."/>
<entry name="on" value="1"
summary="Output is turned on, no power saving"/>
</enum>
<enum name="error">
<entry name="invalid_mode" value="1" summary="nonexistent power save mode"/>
</enum>
<request name="set_mode">
<description summary="Set an outputs power save mode">
Set an output's power save mode to the given mode. The mode change
is effective immediately. If the output does not support the given
mode a failed event is sent.
</description>
<arg name="mode" type="uint" enum="mode" summary="the power save mode to set"/>
</request>
<event name="mode">
<description summary="Report a power management mode change">
Report the power management mode change of an output.
The mode event is sent after an output changed its power
management mode. The reason can be a client using set_mode or the
compositor deciding to change an output's mode.
This event is also sent immediately when the object is created
so the client is informed about the current power management mode.
</description>
<arg name="mode" type="uint" enum="mode"
summary="the output's new power management mode"/>
</event>
<event name="failed">
<description summary="object no longer valid">
This event indicates that the output power management mode control
is no longer valid. This can happen for a number of reasons,
including:
- The output doesn't support power management
- Another client already has exclusive power management mode control
for this output
- The output disappeared
Upon receiving this event, the client should destroy this object.
</description>
</event>
<request name="destroy" type="destructor">
<description summary="destroy this power management">
Destroys the output power management mode control object.
</description>
</request>
</interface>
</protocol>
07070100000013000041ED0000000000000000000000026762487000000000000000000000000000000000000000000000001500000000magpie-0.0.0+209/src07070100000014000081A4000000000000000000000001676248700000171B000000000000000000000000000000000000002A00000000magpie-0.0.0+209/src/foreign_toplevel.cpp#include "foreign_toplevel.hpp"
#include "output.hpp"
#include "server.hpp"
#include "surface/view.hpp"
#include "wlr-wrap-start.hpp"
#include <wlr/util/log.h>
#include "wlr-wrap-end.hpp"
static void foreign_toplevel_handle_request_maximize_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_foreign_toplevel_handle_v1.events.request_maximize(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_foreign_toplevel_handle_v1.events.request_maximize");
return;
}
const ForeignToplevelHandle& handle = magpie_container_of(listener, handle, request_maximize);
const auto& event = *static_cast<wlr_foreign_toplevel_handle_v1_maximized_event*>(data);
const auto placement = event.maximized ? VIEW_PLACEMENT_MAXIMIZED : VIEW_PLACEMENT_STACKING;
handle.view.set_placement(placement);
}
static void foreign_toplevel_handle_request_fullscreen_notify(wl_listener* listener, void* data) {
wlr_log(
WLR_DEBUG, "wlr_foreign_toplevel_handle_v1.events.request_fullscreen(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_foreign_toplevel_handle_v1.events.request_fullscreen");
return;
}
const ForeignToplevelHandle& handle = magpie_container_of(listener, handle, request_fullscreen);
const auto& event = *static_cast<wlr_foreign_toplevel_handle_v1_maximized_event*>(data);
const auto placement = event.maximized ? VIEW_PLACEMENT_FULLSCREEN : VIEW_PLACEMENT_STACKING;
handle.view.set_placement(placement);
}
static void foreign_toplevel_handle_request_minimize_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_foreign_toplevel_handle_v1.events.request_minimize(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_foreign_toplevel_handle_v1.events.request_minimize");
return;
}
const ForeignToplevelHandle& handle = magpie_container_of(listener, handle, request_minimize);
const auto& event = *static_cast<wlr_foreign_toplevel_handle_v1_minimized_event*>(data);
handle.view.set_minimized(event.minimized);
}
static void foreign_toplevel_handle_request_activate_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_foreign_toplevel_handle_v1.events.request_activate(listener=%p, data=%p)", (void*) listener, data);
const ForeignToplevelHandle& handle = magpie_container_of(listener, handle, request_activate);
handle.view.set_minimized(false);
handle.view.get_server().focus_view(std::dynamic_pointer_cast<View>(handle.view.shared_from_this()));
}
static void foreign_toplevel_handle_request_close_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_foreign_toplevel_handle_v1.events.request_close(listener=%p, data=%p)", (void*) listener, data);
const ForeignToplevelHandle& handle = magpie_container_of(listener, handle, request_close);
handle.view.close();
}
ForeignToplevelHandle::ForeignToplevelHandle(View& view) noexcept
: listeners(*this), view(view), wlr(*wlr_foreign_toplevel_handle_v1_create(view.get_server().foreign_toplevel_manager)) {
wlr.data = this;
listeners.request_maximize.notify = foreign_toplevel_handle_request_maximize_notify;
wl_signal_add(&wlr.events.request_maximize, &listeners.request_maximize);
listeners.request_minimize.notify = foreign_toplevel_handle_request_minimize_notify;
wl_signal_add(&wlr.events.request_minimize, &listeners.request_minimize);
listeners.request_activate.notify = foreign_toplevel_handle_request_activate_notify;
wl_signal_add(&wlr.events.request_activate, &listeners.request_activate);
listeners.request_fullscreen.notify = foreign_toplevel_handle_request_fullscreen_notify;
wl_signal_add(&wlr.events.request_fullscreen, &listeners.request_fullscreen);
listeners.request_close.notify = foreign_toplevel_handle_request_close_notify;
wl_signal_add(&wlr.events.request_close, &listeners.request_close);
}
ForeignToplevelHandle::~ForeignToplevelHandle() noexcept {
wlr_foreign_toplevel_handle_v1_destroy(&wlr);
wl_list_remove(&listeners.request_maximize.link);
wl_list_remove(&listeners.request_minimize.link);
wl_list_remove(&listeners.request_activate.link);
wl_list_remove(&listeners.request_fullscreen.link);
wl_list_remove(&listeners.request_close.link);
}
void ForeignToplevelHandle::set_title(const char* title) const {
if (title != nullptr) {
wlr_foreign_toplevel_handle_v1_set_title(&wlr, title);
}
}
void ForeignToplevelHandle::set_app_id(const char* app_id) const {
if (app_id != nullptr) {
wlr_foreign_toplevel_handle_v1_set_app_id(&wlr, app_id);
}
}
void ForeignToplevelHandle::set_parent(const std::optional<ForeignToplevelHandle>& parent) const {
wlr_foreign_toplevel_handle_v1_set_parent(&wlr, parent.has_value() ? &parent.value().wlr : nullptr);
}
void ForeignToplevelHandle::set_placement(const ViewPlacement placement) const {
set_maximized(placement == VIEW_PLACEMENT_MAXIMIZED);
set_fullscreen(placement == VIEW_PLACEMENT_FULLSCREEN);
}
void ForeignToplevelHandle::set_maximized(const bool maximized) const {
wlr_foreign_toplevel_handle_v1_set_maximized(&wlr, maximized);
}
void ForeignToplevelHandle::set_minimized(const bool minimized) const {
wlr_foreign_toplevel_handle_v1_set_minimized(&wlr, minimized);
}
void ForeignToplevelHandle::set_activated(const bool activated) const {
wlr_foreign_toplevel_handle_v1_set_activated(&wlr, activated);
}
void ForeignToplevelHandle::set_fullscreen(const bool fullscreen) const {
wlr_foreign_toplevel_handle_v1_set_fullscreen(&wlr, fullscreen);
}
void ForeignToplevelHandle::output_enter(const Output& output) const {
wlr_foreign_toplevel_handle_v1_output_enter(&wlr, &output.wlr);
}
void ForeignToplevelHandle::output_leave(const Output& output) const {
wlr_foreign_toplevel_handle_v1_output_leave(&wlr, &output.wlr);
}
07070100000015000081A40000000000000000000000016762487000000584000000000000000000000000000000000000002A00000000magpie-0.0.0+209/src/foreign_toplevel.hpp#ifndef FOREIGN_TOPLEVEL_HPP
#define FOREIGN_TOPLEVEL_HPP
#include "types.hpp"
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include "wlr-wrap-start.hpp"
#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
#include "wlr-wrap-end.hpp"
class ForeignToplevelHandle final : public std::enable_shared_from_this<ForeignToplevelHandle> {
public:
struct Listeners {
std::reference_wrapper<ForeignToplevelHandle> parent;
wl_listener request_maximize = {};
wl_listener request_minimize = {};
wl_listener request_activate = {};
wl_listener request_fullscreen = {};
wl_listener request_close = {};
explicit Listeners(ForeignToplevelHandle& parent) noexcept : parent(parent) {}
};
private:
Listeners listeners;
public:
View& view;
wlr_foreign_toplevel_handle_v1& wlr;
explicit ForeignToplevelHandle(View& view) noexcept;
~ForeignToplevelHandle() noexcept;
void set_title(const char* title) const;
void set_app_id(const char* app_id) const;
void set_parent(const std::optional<ForeignToplevelHandle>& parent) const;
void set_placement(ViewPlacement placement) const;
void set_maximized(bool maximized) const;
void set_fullscreen(bool fullscreen) const;
void set_minimized(bool minimized) const;
void set_activated(bool activated) const;
void output_enter(const Output& output) const;
void output_leave(const Output& output) const;
};
#endif
07070100000016000041ED0000000000000000000000026762487000000000000000000000000000000000000000000000001B00000000magpie-0.0.0+209/src/input07070100000017000081A4000000000000000000000001676248700000052D000000000000000000000000000000000000002A00000000magpie-0.0.0+209/src/input/constraint.cpp#include "constraint.hpp"
#include "seat.hpp"
#include "types.hpp"
#include <wayland-util.h>
#include "wlr-wrap-start.hpp"
#include <wlr/types/wlr_compositor.h>
#include <wlr/util/log.h>
#include "wlr-wrap-end.hpp"
static void constraint_destroy_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_pointer_constraint_v1.events.destroy(listener=%p, data=%p)", (void*) listener, data);
PointerConstraint& constraint = magpie_container_of(listener, constraint, destroy);
auto& current_constraint = constraint.seat.current_constraint;
if (current_constraint != nullptr && ¤t_constraint->wlr == &constraint.wlr) {
constraint.seat.cursor.warp_to_constraint(*current_constraint);
constraint.deactivate();
current_constraint.reset();
}
}
PointerConstraint::PointerConstraint(Seat& seat, wlr_pointer_constraint_v1& wlr) noexcept
: listeners(*this), seat(seat), wlr(wlr) {
listeners.destroy.notify = constraint_destroy_notify;
wl_signal_add(&wlr.events.destroy, &listeners.destroy);
}
PointerConstraint::~PointerConstraint() noexcept {
wl_list_remove(&listeners.destroy.link);
}
void PointerConstraint::activate() const {
wlr_pointer_constraint_v1_send_activated(&wlr);
}
void PointerConstraint::deactivate() const {
wlr_pointer_constraint_v1_send_deactivated(&wlr);
}
07070100000018000081A40000000000000000000000016762487000000325000000000000000000000000000000000000002A00000000magpie-0.0.0+209/src/input/constraint.hpp#ifndef MAGPIE_CONSTRAINT_HPP
#define MAGPIE_CONSTRAINT_HPP
#include "types.hpp"
#include <functional>
#include <memory>
#include <wayland-server-core.h>
#include "wlr-wrap-start.hpp"
#include <wlr/types/wlr_pointer_constraints_v1.h>
#include "wlr-wrap-end.hpp"
class PointerConstraint final : public std::enable_shared_from_this<PointerConstraint> {
public:
struct Listeners {
std::reference_wrapper<PointerConstraint> parent;
wl_listener destroy = {};
explicit Listeners(PointerConstraint& parent) noexcept : parent(parent) {}
};
private:
Listeners listeners;
public:
Seat& seat;
wlr_pointer_constraint_v1& wlr;
PointerConstraint(Seat& seat, wlr_pointer_constraint_v1& wlr) noexcept;
~PointerConstraint() noexcept;
void activate() const;
void deactivate() const;
};
#endif
07070100000019000081A40000000000000000000000016762487000004BF4000000000000000000000000000000000000002600000000magpie-0.0.0+209/src/input/cursor.cpp#include "cursor.hpp"
#include "input/constraint.hpp"
#include "output.hpp"
#include "seat.hpp"
#include "server.hpp"
#include "surface/surface.hpp"
#include "surface/view.hpp"
#include <algorithm>
#include <cstring>
#include <iostream>
#include "wlr-wrap-start.hpp"
#include <wlr/types/wlr_cursor_shape_v1.h>
#include <wlr/types/wlr_idle_notify_v1.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/util/edges.h>
#include <wlr/util/log.h>
#include "wlr-wrap-end.hpp"
void Cursor::process_resize(const uint32_t time) const {
(void) time;
/*
* Resizing the grabbed view can be a little bit complicated, because we
* could be resizing from any corner or edge. This not only resizes the view
* on one or two axes, but can also move the view if you resize from the top
* or left edges (or top-left corner).
*
* Note that I took some shortcuts here. In a more fleshed-out compositor,
* you'd wait for the client to prepare a buffer at the new size, then
* commit any movement that was prepared.
*/
const std::shared_ptr<View> view = seat.server.grabbed_view.lock();
if (view == nullptr) {
wlr_log(WLR_ERROR, "Attempted to process_resize without a grabbed view");
return;
}
const wlr_box min_size = view->get_min_size();
const wlr_box max_size = view->get_max_size();
const double border_x = wlr.x - seat.server.grab_x;
const double border_y = wlr.y - seat.server.grab_y;
int32_t new_left = seat.server.grab_geobox.x;
int32_t new_right = seat.server.grab_geobox.x + seat.server.grab_geobox.width;
int32_t new_top = seat.server.grab_geobox.y;
int32_t new_bottom = seat.server.grab_geobox.y + seat.server.grab_geobox.height;
if ((seat.server.resize_edges & WLR_EDGE_TOP) != 0) {
new_top = static_cast<int32_t>(std::round(border_y));
if (new_top >= new_bottom) {
new_top = new_bottom - 1;
}
} else if ((seat.server.resize_edges & WLR_EDGE_BOTTOM) != 0) {
new_bottom = static_cast<int32_t>(std::round(border_y));
if (new_bottom <= new_top) {
new_bottom = new_top + 1;
}
}
if ((seat.server.resize_edges & WLR_EDGE_LEFT) != 0) {
new_left = static_cast<int32_t>(std::round(border_x));
if (new_left >= new_right) {
new_left = new_right - 1;
}
} else if ((seat.server.resize_edges & WLR_EDGE_RIGHT) != 0) {
new_right = static_cast<int32_t>(std::round(border_x));
if (new_right <= new_left) {
new_right = new_left + 1;
}
}
const wlr_box geo_box = view->get_geometry();
const int32_t new_width = std::clamp(new_right - new_left, min_size.width, max_size.width);
const int32_t new_height = std::clamp(new_bottom - new_top, min_size.height, max_size.height);
const int32_t new_x = new_width == view->current.width ? view->current.x : new_left - geo_box.x;
const int32_t new_y = new_height == view->current.height ? view->current.y : new_top - geo_box.y;
view->set_geometry(new_x, new_y, new_width, new_height);
view->update_outputs();
}
void Cursor::process_move(const uint32_t time) {
(void) time;
set_image("fleur");
/* Move the grabbed view to the new position. */
std::shared_ptr<View> view = seat.server.grabbed_view.lock();
if (view == nullptr) {
wlr_log(WLR_ERROR, "Attempted to process_move without a grabbed view");
return;
}
const auto new_x = static_cast<int32_t>(std::round(wlr.x - seat.server.grab_x));
const auto new_y = static_cast<int32_t>(std::round(std::fmax(wlr.y - seat.server.grab_y, 0)));
view->set_position(new_x, new_y);
view->update_outputs();
}
/* This event is forwarded by the cursor when a pointer emits an axis event,
* for example when you move the scroll wheel. */
static void cursor_axis_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_cursor.events.axis(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_cursor.events.axis");
return;
}
Cursor& cursor = magpie_container_of(listener, cursor, axis);
const auto* event = static_cast<wlr_pointer_axis_event*>(data);
/* Notify the client with pointer focus of the axis event. */
wlr_seat_pointer_notify_axis(cursor.seat.wlr, event->time_msec, event->orientation, event->delta, event->delta_discrete,
event->source, event->relative_direction);
}
/* This event is forwarded by the cursor when a pointer emits an frame
* event. Frame events are sent after regular pointer events to group
* multiple events together. For instance, two axis events may happen at the
* same time, in which case a frame event won't be sent in between. */
static void cursor_frame_notify(wl_listener* listener, [[maybe_unused]] void* data) {
Cursor& cursor = magpie_container_of(listener, cursor, frame);
/* Notify the client with pointer focus of the frame event. */
wlr_seat_pointer_notify_frame(cursor.seat.wlr);
}
/* This event is forwarded by the cursor when a pointer emits an _absolute_
* motion event, from 0..1 on each axis. This happens, for example, when
* wlroots is running under a Wayland window rather than KMS+DRM, and you
* move the mouse over the window. You could enter the window from any edge,
* so we have to warp the mouse there. There is also some hardware which
* emits these events. */
static void cursor_motion_absolute_notify(wl_listener* listener, void* data) {
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_cursor.events.motion_absolute");
return;
}
Cursor& cursor = magpie_container_of(listener, cursor, motion_absolute);
const auto* event = static_cast<wlr_pointer_motion_absolute_event*>(data);
double lx;
double ly;
wlr_cursor_absolute_to_layout_coords(&cursor.wlr, &event->pointer->base, event->x, event->y, &lx, &ly);
double dx = lx - cursor.wlr.x;
double dy = ly - cursor.wlr.y;
wlr_relative_pointer_manager_v1_send_relative_motion(
cursor.relative_pointer_mgr, cursor.seat.wlr, static_cast<uint64_t>(event->time_msec) * 1000, dx, dy, dx, dy);
if (cursor.seat.is_pointer_locked(event->pointer)) {
return;
}
cursor.seat.apply_constraint(event->pointer, &dx, &dy);
wlr_cursor_move(&cursor.wlr, &event->pointer->base, dx, dy);
cursor.process_motion(event->time_msec);
}
/* This event is forwarded by the cursor when a pointer emits a button event. */
static void cursor_button_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_cursor.events.button(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_cursor.events.button");
return;
}
Cursor& cursor = magpie_container_of(listener, cursor, button);
const auto* event = static_cast<wlr_pointer_button_event*>(data);
Server& server = cursor.seat.server;
/* Notify the client with pointer focus that a button press has occurred */
wlr_seat_pointer_notify_button(server.seat->wlr, event->time_msec, event->button, event->state);
double sx;
double sy;
wlr_surface* surface = nullptr;
const auto magpie_surface = server.surface_at(cursor.wlr.x, cursor.wlr.y, &surface, &sx, &sy).lock();
if (event->state == WL_POINTER_BUTTON_STATE_RELEASED) {
/* If you released any buttons, we exit interactive move/resize mode. */
if (cursor.mode != MAGPIE_CURSOR_PASSTHROUGH) {
cursor.reset_mode();
}
} else if (magpie_surface != nullptr && magpie_surface->is_view()) {
/* Focus that client if the button was _pressed_ */
server.focus_view(std::dynamic_pointer_cast<View>(magpie_surface));
} else {
server.focus_view(nullptr);
}
}
/* This event is forwarded by the cursor when a pointer emits a _relative_
* pointer motion event (i.e. a delta) */
static void cursor_motion_notify(wl_listener* listener, void* data) {
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_cursor.events.motion");
return;
}
Cursor& cursor = magpie_container_of(listener, cursor, motion);
const auto* event = static_cast<wlr_pointer_motion_event*>(data);
wlr_relative_pointer_manager_v1_send_relative_motion(cursor.relative_pointer_mgr, cursor.seat.wlr,
static_cast<uint64_t>(event->time_msec) * 1000, event->delta_x, event->delta_y, event->unaccel_dx, event->unaccel_dy);
if (cursor.seat.is_pointer_locked(event->pointer)) {
return;
}
double dx = event->delta_x;
double dy = event->delta_y;
cursor.seat.apply_constraint(event->pointer, &dx, &dy);
wlr_cursor_move(&cursor.wlr, &event->pointer->base, dx, dy);
cursor.process_motion(event->time_msec);
}
static void gesture_pinch_begin_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_cursor.events.gesture_pinch_begin(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_cursor.events.gesture_pinch_begin");
return;
}
Cursor& cursor = magpie_container_of(listener, cursor, gesture_pinch_begin);
const auto* event = static_cast<wlr_pointer_pinch_begin_event*>(data);
wlr_pointer_gestures_v1_send_pinch_begin(cursor.pointer_gestures, cursor.seat.wlr, event->time_msec, event->fingers);
}
static void gesture_pinch_update_notify(wl_listener* listener, void* data) {
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_cursor.events.gesture_pinch_update");
return;
}
Cursor& cursor = magpie_container_of(listener, cursor, gesture_pinch_update);
const auto* event = static_cast<wlr_pointer_pinch_update_event*>(data);
wlr_pointer_gestures_v1_send_pinch_update(
cursor.pointer_gestures, cursor.seat.wlr, event->time_msec, event->dx, event->dy, event->scale, event->rotation);
}
static void gesture_pinch_end_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_cursor.events.gesture_pinch_end(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_cursor.events.gesture_pinch_end");
return;
}
Cursor& cursor = magpie_container_of(listener, cursor, gesture_pinch_end);
const auto* event = static_cast<wlr_pointer_pinch_end_event*>(data);
wlr_pointer_gestures_v1_send_pinch_end(cursor.pointer_gestures, cursor.seat.wlr, event->time_msec, event->cancelled);
}
static void gesture_swipe_begin_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_cursor.events.gesture_swipe_begin(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_cursor.events.gesture_swipe_begin");
return;
}
Cursor& cursor = magpie_container_of(listener, cursor, gesture_swipe_begin);
const auto* event = static_cast<wlr_pointer_swipe_begin_event*>(data);
wlr_pointer_gestures_v1_send_swipe_begin(cursor.pointer_gestures, cursor.seat.wlr, event->time_msec, event->fingers);
}
static void gesture_swipe_update_notify(wl_listener* listener, void* data) {
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_cursor.events.gesture_swipe_update");
return;
}
Cursor& cursor = magpie_container_of(listener, cursor, gesture_swipe_update);
const auto* event = static_cast<wlr_pointer_swipe_update_event*>(data);
wlr_pointer_gestures_v1_send_swipe_update(cursor.pointer_gestures, cursor.seat.wlr, event->time_msec, event->dx, event->dy);
}
static void gesture_swipe_end_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_cursor.events.gesture_swipe_end(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_cursor.events.gesture_swipe_end");
return;
}
Cursor& cursor = magpie_container_of(listener, cursor, gesture_swipe_end);
const auto* event = static_cast<wlr_pointer_swipe_end_event*>(data);
wlr_pointer_gestures_v1_send_swipe_end(cursor.pointer_gestures, cursor.seat.wlr, event->time_msec, event->cancelled);
}
static void gesture_hold_begin_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_cursor.events.gesture_hold_begin(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_cursor.events.gesture_hold_begin");
return;
}
Cursor& cursor = magpie_container_of(listener, cursor, gesture_hold_begin);
const auto* event = static_cast<wlr_pointer_hold_begin_event*>(data);
wlr_pointer_gestures_v1_send_hold_begin(cursor.pointer_gestures, cursor.seat.wlr, event->time_msec, event->fingers);
}
static void gesture_hold_end_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_cursor.events.gesture_hold_end(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_cursor.events.gesture_hold_end");
return;
}
Cursor& cursor = magpie_container_of(listener, cursor, gesture_hold_end);
const auto* event = static_cast<wlr_pointer_hold_end_event*>(data);
wlr_pointer_gestures_v1_send_hold_end(cursor.pointer_gestures, cursor.seat.wlr, event->time_msec, event->cancelled);
}
static void request_set_shape_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_cursor.events.set_shape(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_cursor.events.set_shape");
return;
}
Cursor& cursor = magpie_container_of(listener, cursor, request_set_shape);
const auto* event = static_cast<wlr_cursor_shape_manager_v1_request_set_shape_event*>(data);
cursor.set_image(wlr_cursor_shape_v1_name(event->shape));
}
Cursor::Cursor(Seat& seat) noexcept : listeners(*this), seat(seat), wlr(*wlr_cursor_create()) {
/*
* Creates a cursor, which is a wlroots utility for tracking the cursor
* image shown on screen.
*/
wlr_cursor_attach_output_layout(&wlr, seat.server.output_layout);
/* Creates an xcursor manager, another wlroots utility which loads up
* Xcursor themes to source cursor images from and makes sure that cursor
* images are available at all scale factors on the screen (necessary for
* HiDPI support). We add a cursor theme at scale factor 1 to begin with. */
cursor_mgr = wlr_xcursor_manager_create(nullptr, 24);
wlr_xcursor_manager_load(cursor_mgr, 1);
relative_pointer_mgr = wlr_relative_pointer_manager_v1_create(seat.server.display);
pointer_gestures = wlr_pointer_gestures_v1_create(seat.server.display);
shape_mgr = wlr_cursor_shape_manager_v1_create(seat.server.display, 1);
/*
* wlr_cursor *only* displays an image on screen. It does not move around
* when the pointer moves. However, we can attach input devices to it, and
* it will generate aggregate events for all of them. In these events, we
* can choose how we want to process them, forwarding them to clients and
* moving the cursor around. More detail on this process is described in my
* input handling blog post:
*
* https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html
*
* And more comments are sprinkled throughout the notify functions above.
*/
mode = MAGPIE_CURSOR_PASSTHROUGH;
listeners.motion.notify = cursor_motion_notify;
wl_signal_add(&wlr.events.motion, &listeners.motion);
listeners.motion_absolute.notify = cursor_motion_absolute_notify;
wl_signal_add(&wlr.events.motion_absolute, &listeners.motion_absolute);
listeners.button.notify = cursor_button_notify;
wl_signal_add(&wlr.events.button, &listeners.button);
listeners.axis.notify = cursor_axis_notify;
wl_signal_add(&wlr.events.axis, &listeners.axis);
listeners.frame.notify = cursor_frame_notify;
wl_signal_add(&wlr.events.frame, &listeners.frame);
listeners.gesture_pinch_begin.notify = gesture_pinch_begin_notify;
wl_signal_add(&wlr.events.pinch_begin, &listeners.gesture_pinch_begin);
listeners.gesture_pinch_update.notify = gesture_pinch_update_notify;
wl_signal_add(&wlr.events.pinch_update, &listeners.gesture_pinch_update);
listeners.gesture_pinch_end.notify = gesture_pinch_end_notify;
wl_signal_add(&wlr.events.pinch_end, &listeners.gesture_pinch_end);
listeners.gesture_swipe_begin.notify = gesture_swipe_begin_notify;
wl_signal_add(&wlr.events.swipe_begin, &listeners.gesture_swipe_begin);
listeners.gesture_swipe_update.notify = gesture_swipe_update_notify;
wl_signal_add(&wlr.events.swipe_update, &listeners.gesture_swipe_update);
listeners.gesture_swipe_end.notify = gesture_swipe_end_notify;
wl_signal_add(&wlr.events.swipe_end, &listeners.gesture_swipe_end);
listeners.gesture_hold_begin.notify = gesture_hold_begin_notify;
wl_signal_add(&wlr.events.hold_begin, &listeners.gesture_swipe_update);
listeners.gesture_hold_end.notify = gesture_hold_end_notify;
wl_signal_add(&wlr.events.hold_end, &listeners.gesture_swipe_end);
listeners.request_set_shape.notify = request_set_shape_notify;
wl_signal_add(&shape_mgr->events.request_set_shape, &listeners.request_set_shape);
}
void Cursor::attach_input_device(wlr_input_device* device) const {
wlr_cursor_attach_input_device(&wlr, device);
}
void Cursor::process_motion(const uint32_t time) {
wlr_idle_notifier_v1_notify_activity(seat.server.idle_notifier, seat.wlr);
/* If the mode is non-passthrough, delegate to those functions. */
if (mode == MAGPIE_CURSOR_MOVE) {
process_move(time);
return;
}
if (mode == MAGPIE_CURSOR_RESIZE) {
process_resize(time);
return;
}
/* Otherwise, find the view under the pointer and send the event along. */
double sx;
double sy;
wlr_surface* surface = nullptr;
auto magpie_surface = seat.server.surface_at(wlr.x, wlr.y, &surface, &sx, &sy).lock();
if (magpie_surface == nullptr) {
/* If there's no view under the cursor, set the cursor image to a
* default. This is what makes the cursor image appear when you move it
* around the screen, not over any views. */
set_image("left_ptr");
}
if (surface != nullptr) {
/*
* Send pointer enter and motion events.
*
* The enter event gives the surface "pointer focus", which is distinct
* from keyboard focus. You get pointer focus by moving the pointer over
* a window.
*
* Note that wlroots will avoid sending duplicate enter/motion events if
* the surface has already had pointer focus or if the client is already
* aware of the coordinates passed.
*/
current_image = "";
wlr_seat_pointer_notify_enter(seat.wlr, surface, sx, sy);
wlr_seat_pointer_notify_motion(seat.wlr, time, sx, sy);
} else {
/* Clear pointer focus so future button events and such are not sent to
* the last client to have the cursor over it. */
wlr_seat_pointer_clear_focus(seat.wlr);
}
}
void Cursor::reset_mode() {
if (mode != MAGPIE_CURSOR_PASSTHROUGH) {
set_image("left_ptr");
}
mode = MAGPIE_CURSOR_PASSTHROUGH;
seat.server.grabbed_view.reset();
}
void Cursor::warp_to_constraint(const PointerConstraint& constraint) const {
const auto focused_view = seat.server.focused_view.lock();
if (focused_view == nullptr) {
// only warp to constraints tied to views...
return;
}
if (focused_view->get_wlr_surface() != constraint.wlr.surface) {
return;
}
if ((constraint.wlr.current.committed & WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) != 0) {
const double x = constraint.wlr.current.cursor_hint.x;
const double y = constraint.wlr.current.cursor_hint.y;
wlr_cursor_warp(&wlr, nullptr, focused_view->current.x + x, focused_view->current.y + y);
wlr_seat_pointer_warp(seat.wlr, x, y);
}
}
void Cursor::set_image(const std::string& name) {
if (current_image != name) {
current_image = name;
reload_image();
}
}
void Cursor::reload_image() const {
wlr_cursor_set_xcursor(&wlr, cursor_mgr, current_image.c_str());
}
0707010000001A000081A40000000000000000000000016762487000000735000000000000000000000000000000000000002600000000magpie-0.0.0+209/src/input/cursor.hpp#ifndef MAGPIE_CURSOR_HPP
#define MAGPIE_CURSOR_HPP
#include "types.hpp"
#include <functional>
#include <memory>
#include <string>
#include "wlr-wrap-start.hpp"
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_cursor_shape_v1.h>
#include <wlr/types/wlr_pointer_gestures_v1.h>
#include <wlr/types/wlr_relative_pointer_v1.h>
#include "wlr-wrap-end.hpp"
enum CursorMode { MAGPIE_CURSOR_PASSTHROUGH, MAGPIE_CURSOR_MOVE, MAGPIE_CURSOR_RESIZE };
class Cursor final : public std::enable_shared_from_this<Cursor> {
public:
struct Listeners {
std::reference_wrapper<Cursor> parent;
wl_listener motion = {};
wl_listener motion_absolute = {};
wl_listener button = {};
wl_listener axis = {};
wl_listener frame = {};
wl_listener gesture_pinch_begin = {};
wl_listener gesture_pinch_update = {};
wl_listener gesture_pinch_end = {};
wl_listener gesture_swipe_begin = {};
wl_listener gesture_swipe_update = {};
wl_listener gesture_swipe_end = {};
wl_listener gesture_hold_begin = {};
wl_listener gesture_hold_end = {};
wl_listener request_set_shape = {};
explicit Listeners(Cursor& parent) noexcept : parent(parent) {}
};
private:
Listeners listeners;
void process_move(uint32_t time);
void process_resize(uint32_t time) const;
public:
const Seat& seat;
wlr_cursor& wlr;
CursorMode mode;
wlr_xcursor_manager* cursor_mgr;
wlr_cursor_shape_manager_v1* shape_mgr;
wlr_relative_pointer_manager_v1* relative_pointer_mgr;
wlr_pointer_gestures_v1* pointer_gestures;
std::string current_image;
explicit Cursor(Seat& seat) noexcept;
void attach_input_device(wlr_input_device* device) const;
void process_motion(uint32_t time);
void reset_mode();
void warp_to_constraint(const PointerConstraint& constraint) const;
void set_image(const std::string& name);
void reload_image() const;
};
#endif
0707010000001B000081A4000000000000000000000001676248700000150D000000000000000000000000000000000000002800000000magpie-0.0.0+209/src/input/keyboard.cpp#include "keyboard.hpp"
#include "seat.hpp"
#include "server.hpp"
#include "surface/view.hpp"
#include <algorithm>
#include <xkbcommon/xkbcommon.h>
#include "wlr-wrap-start.hpp"
#include <wlr/backend/multi.h>
#include <wlr/backend/session.h>
#include <wlr/types/wlr_idle_notify_v1.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/util/log.h>
#include "wlr-wrap-end.hpp"
/* This event is raised by the keyboard base wlr_input_device to signal
* the destruction of the wlr_keyboard. It will no longer receive events
* and should be destroyed. */
static void keyboard_handle_destroy(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_keyboard.events.destroy(listener=%p, data=%p)", (void*) listener, data);
Keyboard& keyboard = magpie_container_of(listener, keyboard, destroy);
auto& keyboards = keyboard.seat.keyboards;
(void) std::ranges::remove(keyboards, keyboard.shared_from_this());
}
static bool handle_compositor_keybinding(const Keyboard& keyboard, const uint32_t modifiers, const xkb_keysym_t sym) {
const Server& server = keyboard.seat.server;
if (modifiers == WLR_MODIFIER_ALT) {
switch (sym) {
case XKB_KEY_Escape: {
wl_display_terminate(server.display);
return true;
}
default: {
break;
}
}
} else if (sym >= XKB_KEY_XF86Switch_VT_1 && sym <= XKB_KEY_XF86Switch_VT_12) {
if (wlr_backend_is_multi(keyboard.seat.server.backend)) {
const unsigned vt = sym - XKB_KEY_XF86Switch_VT_1 + 1;
wlr_session_change_vt(keyboard.seat.server.session, vt);
}
return true;
}
return false;
}
/* This event is raised when a key is pressed or released. */
static void keyboard_handle_key(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_keyboard.events.key(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_keyboard.events.key");
return;
}
const Keyboard& keyboard = magpie_container_of(listener, keyboard, key);
const auto* event = static_cast<wlr_keyboard_key_event*>(data);
wlr_seat* seat = keyboard.seat.wlr;
wlr_idle_notifier_v1_notify_activity(keyboard.seat.server.idle_notifier, seat);
/* Translate libinput keycode -> xkbcommon */
const uint32_t keycode = event->keycode + 8;
/* Get a list of keysyms based on the keymap for this keyboard */
const xkb_keysym_t* syms;
const int32_t nsyms = xkb_state_key_get_syms(keyboard.wlr.xkb_state, keycode, &syms);
bool handled = false;
const uint32_t modifiers = wlr_keyboard_get_modifiers(&keyboard.wlr);
if (event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
if ((modifiers & WLR_MODIFIER_ALT) != 0) {
/* If alt is held down and this button was _pressed_, we attempt to
* process it as a compositor keybinding. */
for (int32_t i = 0; i < nsyms; i++) {
handled = handle_compositor_keybinding(keyboard, modifiers, syms[i]);
}
}
if (!handled && modifiers != 0) {
for (const auto& subscriber : keyboard.seat.keyboard_shortcuts_subscribers) {
for (const auto& item : subscriber->shortcuts) {
if (item->modifiers == modifiers && item->keycode == keycode) {
handled = true;
budgie_keyboard_shortcuts_manager_send_shortcut_press(
&subscriber->wlr, event->time_msec, modifiers, keycode);
}
}
}
}
}
if (!handled) {
/* Otherwise, we pass it along to the client. */
wlr_seat_set_keyboard(seat, &keyboard.wlr);
wlr_seat_keyboard_notify_key(seat, event->time_msec, event->keycode, event->state);
}
}
/* This event is raised when a modifier key, such as shift or alt, is
* pressed. We simply communicate this to the client. */
static void keyboard_handle_modifiers(wl_listener* listener, [[maybe_unused]] void* data) {
Keyboard& keyboard = magpie_container_of(listener, keyboard, modifiers);
/*
* A seat can only have one keyboard, but this is a limitation of the
* Wayland protocol - not wlroots. We assign all connected keyboards to the
* same seat. You can swap out the underlying wlr_keyboard like this and
* wlr_seat handles this transparently.
*/
wlr_seat_set_keyboard(keyboard.seat.wlr, &keyboard.wlr);
/* Send modifiers to the client. */
wlr_seat_keyboard_notify_modifiers(keyboard.seat.wlr, &keyboard.wlr.modifiers);
}
Keyboard::Keyboard(Seat& seat, wlr_keyboard& keyboard) noexcept : listeners(*this), seat(seat), wlr(keyboard) {
/* We need to prepare an XKB keymap and assign it to the keyboard. This
* assumes the defaults (e.g. layout = "us"). */
xkb_context* context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
xkb_keymap* keymap = xkb_keymap_new_from_names(context, nullptr, XKB_KEYMAP_COMPILE_NO_FLAGS);
wlr_keyboard_set_keymap(&keyboard, keymap);
xkb_keymap_unref(keymap);
xkb_context_unref(context);
wlr_keyboard_set_repeat_info(&keyboard, 25, 600);
/* Here we set up listeners for keyboard events. */
listeners.modifiers.notify = keyboard_handle_modifiers;
wl_signal_add(&keyboard.events.modifiers, &listeners.modifiers);
listeners.key.notify = keyboard_handle_key;
wl_signal_add(&keyboard.events.key, &listeners.key);
listeners.destroy.notify = keyboard_handle_destroy;
wl_signal_add(&keyboard.base.events.destroy, &listeners.destroy);
wlr_seat_set_keyboard(seat.wlr, &keyboard);
}
Keyboard::~Keyboard() noexcept {
wl_list_remove(&listeners.modifiers.link);
wl_list_remove(&listeners.key.link);
wl_list_remove(&listeners.destroy.link);
}
0707010000001C000081A400000000000000000000000167624870000002AA000000000000000000000000000000000000002800000000magpie-0.0.0+209/src/input/keyboard.hpp#ifndef MAGPIE_KEYBOARD_HPP
#define MAGPIE_KEYBOARD_HPP
#include "types.hpp"
#include <functional>
#include <memory>
#include "wlr-wrap-start.hpp"
#include <wlr/types/wlr_keyboard.h>
#include "wlr-wrap-end.hpp"
class Keyboard final : public std::enable_shared_from_this<Keyboard> {
public:
struct Listeners {
std::reference_wrapper<Keyboard> parent;
wl_listener modifiers = {};
wl_listener key = {};
wl_listener destroy = {};
explicit Listeners(Keyboard& parent) noexcept : parent(parent) {}
};
private:
Listeners listeners;
public:
Seat& seat;
wlr_keyboard& wlr;
Keyboard(Seat& seat, wlr_keyboard& keyboard) noexcept;
~Keyboard() noexcept;
};
#endif
0707010000001D000081A40000000000000000000000016762487000000A30000000000000000000000000000000000000003D00000000magpie-0.0.0+209/src/input/keyboard_shortcuts_subscriber.cpp#include "keyboard_shortcuts_subscriber.hpp"
#include "seat.hpp"
#include <algorithm>
#include "wlr-wrap-start.hpp"
#include <wlr/util/log.h>
#include "wlr-wrap-end.hpp"
void subscriber_destroy_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "budgie_keyboard_shortcuts_subscriber.events.destroy(listener=%p, data=%p)", (void*) listener, data);
KeyboardShortcutsSubscriber& subscriber = magpie_container_of(listener, subscriber, destroy);
auto& subscribers = subscriber.seat.keyboard_shortcuts_subscribers;
(void) std::ranges::remove(subscribers, subscriber.shared_from_this());
}
void subscriber_register_shortcut_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "budgie_keyboard_shortcuts_subscriber.events.register_shortcut(listener=%p, data=%p)", (void*) listener,
data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to budgie_keyboard_shortcuts_subscriber.events.register_shortcut");
return;
}
KeyboardShortcutsSubscriber& subscriber = magpie_container_of(listener, subscriber, register_shortcut);
const auto* shortcut = static_cast<budgie_keyboard_shortcuts_shortcut*>(data);
subscriber.shortcuts.insert(shortcut);
}
void subscriber_unregister_shortcut_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "budgie_keyboard_shortcuts_subscriber.events.unregister_shortcut(listener=%p, data=%p)",
(void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to budgie_keyboard_shortcuts_subscriber.events.unregister_shortcut");
return;
}
KeyboardShortcutsSubscriber& subscriber = magpie_container_of(listener, subscriber, register_shortcut);
const auto* shortcut = static_cast<budgie_keyboard_shortcuts_shortcut*>(data);
subscriber.shortcuts.erase(shortcut);
}
KeyboardShortcutsSubscriber::KeyboardShortcutsSubscriber(Seat& seat, budgie_keyboard_shortcuts_subscriber& subscriber) noexcept
: listeners(*this), seat(seat), wlr(subscriber) {
listeners.destroy.notify = subscriber_destroy_notify;
wl_signal_add(&wlr.events.destroy, &listeners.destroy);
listeners.register_shortcut.notify = subscriber_register_shortcut_notify;
wl_signal_add(&wlr.events.register_shortcut, &listeners.register_shortcut);
listeners.unregister_shortcut.notify = subscriber_unregister_shortcut_notify;
wl_signal_add(&wlr.events.unregister_shortcut, &listeners.unregister_shortcut);
}
KeyboardShortcutsSubscriber::~KeyboardShortcutsSubscriber() noexcept {
wl_list_remove(&listeners.register_shortcut.link);
wl_list_remove(&listeners.unregister_shortcut.link);
wl_list_remove(&listeners.destroy.link);
}
0707010000001E000081A4000000000000000000000001676248700000039E000000000000000000000000000000000000003D00000000magpie-0.0.0+209/src/input/keyboard_shortcuts_subscriber.hpp#ifndef MAGPIE_KEYBOARD_SHORTCUTS_SUBSCRIBER_HPP
#define MAGPIE_KEYBOARD_SHORTCUTS_SUBSCRIBER_HPP
#include "types.hpp"
#include "protocols/budgie_keyboard_shortcuts.hpp"
#include <memory>
#include <set>
class KeyboardShortcutsSubscriber final : public std::enable_shared_from_this<KeyboardShortcutsSubscriber> {
public:
struct Listeners {
std::reference_wrapper<KeyboardShortcutsSubscriber> parent;
wl_listener register_shortcut = {};
wl_listener unregister_shortcut = {};
wl_listener destroy = {};
explicit Listeners(KeyboardShortcutsSubscriber& parent) noexcept : parent(parent) {}
};
private:
Listeners listeners;
public:
Seat& seat;
budgie_keyboard_shortcuts_subscriber& wlr;
std::set<const budgie_keyboard_shortcuts_shortcut*> shortcuts;
KeyboardShortcutsSubscriber(Seat& seat, budgie_keyboard_shortcuts_subscriber& subscriber) noexcept;
~KeyboardShortcutsSubscriber() noexcept;
};
#endif
0707010000001F000081A400000000000000000000000167624870000022B0000000000000000000000000000000000000002400000000magpie-0.0.0+209/src/input/seat.cpp#include "seat.hpp"
#include "constraint.hpp"
#include "keyboard.hpp"
#include "server.hpp"
#include "surface/view.hpp"
#include "types.hpp"
#include <algorithm>
#include <wayland-util.h>
#include "wlr-wrap-start.hpp"
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/util/log.h>
#include <wlr/util/region.h>
#include "wlr-wrap-end.hpp"
static void new_input_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_seat.events.new_input(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_seat.events.new_input");
return;
}
Seat& seat = magpie_container_of(listener, seat, new_input);
auto* device = static_cast<wlr_input_device*>(data);
seat.new_input_device(device);
}
static void new_virtual_pointer_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_seat.events.new_virtual_pointer(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_seat.events.new_virtual_pointer");
return;
}
Seat& seat = magpie_container_of(listener, seat, new_virtual_pointer);
const auto* event = static_cast<wlr_virtual_pointer_v1_new_pointer_event*>(data);
seat.new_input_device(&event->new_pointer->pointer.base);
}
static void new_virtual_keyboard_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_seat.events.new_virtual_keyboard(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_seat.events.new_virtual_keyboard");
return;
}
Seat& seat = magpie_container_of(listener, seat, new_virtual_keyboard);
auto* keyboard = static_cast<wlr_virtual_keyboard_v1*>(data);
seat.new_input_device(&keyboard->keyboard.base);
}
static void new_pointer_constraint_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_seat.events.new_pointer_constraint(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_seat.events.new_pointer_constraint");
return;
}
Seat& seat = magpie_container_of(listener, seat, new_pointer_constraint);
auto* wlr_constraint = static_cast<wlr_pointer_constraint_v1*>(data);
const auto* focused_surface = seat.wlr->keyboard_state.focused_surface;
if (focused_surface == wlr_constraint->surface) {
// only allow creating constraints for the focused view
seat.set_constraint(wlr_constraint);
}
}
static void request_cursor_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_seat.events.request_cursor(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_seat.events.request_cursor");
return;
}
const Seat& seat = magpie_container_of(listener, seat, request_cursor);
const auto* event = static_cast<wlr_seat_pointer_request_set_cursor_event*>(data);
const wlr_seat_client* focused_client = seat.wlr->pointer_state.focused_client;
if (focused_client == event->seat_client) {
/* Once we've vetted the client, we can tell the cursor to use the
* provided surface as the cursor image. It will set the hardware cursor
* on the output that it's currently on and continue to do so as the
* cursor moves between outputs. */
wlr_cursor_set_surface(&seat.cursor.wlr, event->surface, event->hotspot_x, event->hotspot_y);
}
}
/* This event is raised by the seat when a client wants to set the selection,
* usually when the user copies something. wlroots allows compositors to
* ignore such requests if they so choose, but in magpie we always honor
*/
static void request_set_selection_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_seat.events.set_selection(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_seat.events.request_set_selection");
return;
}
const Seat& seat = magpie_container_of(listener, seat, request_set_selection);
const auto* event = static_cast<wlr_seat_request_set_selection_event*>(data);
wlr_seat_set_selection(seat.wlr, event->source, event->serial);
}
static void keyboard_shortcuts_subscribe_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "budgie_keyboard_shortcuts_manager.events.subscribe(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to budgie_keyboard_shortcuts_manager.events.subscribe");
return;
}
Seat& seat = magpie_container_of(listener, seat, keyboard_shortcuts_subscribe);
auto* subscriber = static_cast<budgie_keyboard_shortcuts_subscriber*>(data);
seat.keyboard_shortcuts_subscribers.push_back(std::make_shared<KeyboardShortcutsSubscriber>(seat, *subscriber));
}
/*
* Configures a seat, which is a single "seat" at which a user sits and
* operates the computer. This conceptually includes up to one keyboard,
* pointer, touch, and drawing tablet device. We also rig up a listener to
* let us know when new input devices are available on the backend.
*/
Seat::Seat(Server& server) noexcept : listeners(*this), server(server), cursor(*this) {
wlr = wlr_seat_create(server.display, "seat0");
listeners.new_input.notify = new_input_notify;
wl_signal_add(&server.backend->events.new_input, &listeners.new_input);
listeners.request_cursor.notify = request_cursor_notify;
wl_signal_add(&wlr->events.request_set_cursor, &listeners.request_cursor);
listeners.request_set_selection.notify = request_set_selection_notify;
wl_signal_add(&wlr->events.request_set_selection, &listeners.request_set_selection);
virtual_pointer_mgr = wlr_virtual_pointer_manager_v1_create(server.display);
listeners.new_virtual_pointer.notify = new_virtual_pointer_notify;
wl_signal_add(&virtual_pointer_mgr->events.new_virtual_pointer, &listeners.new_virtual_pointer);
virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(server.display);
listeners.new_virtual_keyboard.notify = new_virtual_keyboard_notify;
wl_signal_add(&virtual_keyboard_mgr->events.new_virtual_keyboard, &listeners.new_virtual_keyboard);
pointer_constraints = wlr_pointer_constraints_v1_create(server.display);
listeners.new_pointer_constraint.notify = new_pointer_constraint_notify;
wl_signal_add(&pointer_constraints->events.new_constraint, &listeners.new_pointer_constraint);
keyboard_shortcuts_manager = budgie_keyboard_shortcuts_manager_create(server.display, 1);
listeners.keyboard_shortcuts_subscribe.notify = keyboard_shortcuts_subscribe_notify;
wl_signal_add(&keyboard_shortcuts_manager->events.subscribe, &listeners.keyboard_shortcuts_subscribe);
}
void Seat::new_input_device(wlr_input_device* device) {
switch (device->type) {
case WLR_INPUT_DEVICE_KEYBOARD:
keyboards.push_back(std::make_shared<Keyboard>(*this, *wlr_keyboard_from_input_device(device)));
break;
case WLR_INPUT_DEVICE_POINTER:
case WLR_INPUT_DEVICE_TABLET:
case WLR_INPUT_DEVICE_TABLET_PAD:
case WLR_INPUT_DEVICE_TOUCH:
cursor.attach_input_device(device);
break;
default:
break;
}
uint32_t caps = WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_TOUCH;
if (!keyboards.empty()) {
caps |= WL_SEAT_CAPABILITY_KEYBOARD;
}
wlr_seat_set_capabilities(wlr, caps);
}
void Seat::set_constraint(wlr_pointer_constraint_v1* wlr_constraint) {
if (current_constraint != nullptr) {
if (¤t_constraint->wlr == wlr_constraint) {
// we already have this constraint marked as the current constraint
return;
}
cursor.warp_to_constraint(*current_constraint);
current_constraint.reset();
}
if (wlr_constraint != nullptr) {
current_constraint = std::make_shared<PointerConstraint>(*this, *wlr_constraint);
current_constraint->activate();
}
}
void Seat::apply_constraint(const wlr_pointer* pointer, double* dx, double* dy) const {
if (current_constraint == nullptr || pointer->base.type != WLR_INPUT_DEVICE_POINTER) {
return;
}
const auto focused_view = server.focused_view.lock();
if (focused_view == nullptr) {
wlr_log(WLR_DEBUG, "Attempted to apply a pointer constraint without a focused view");
return;
}
double x = cursor.wlr.x;
double y = cursor.wlr.y;
x -= focused_view->current.x;
y -= focused_view->current.y;
double confined_x = 0;
double confined_y = 0;
if (!wlr_region_confine(¤t_constraint->wlr.region, x, y, x + *dx, y + *dy, &confined_x, &confined_y)) {
wlr_log(WLR_ERROR, "Couldn't confine\n");
return;
}
*dx = confined_x - x;
*dy = confined_y - y;
}
bool Seat::is_pointer_locked(const wlr_pointer* pointer) const {
return current_constraint != nullptr && pointer->base.type == WLR_INPUT_DEVICE_POINTER &&
current_constraint->wlr.type == WLR_POINTER_CONSTRAINT_V1_LOCKED;
}
07070100000020000081A400000000000000000000000167624870000006B4000000000000000000000000000000000000002400000000magpie-0.0.0+209/src/input/seat.hpp#ifndef MAGPIE_SEAT_HPP
#define MAGPIE_SEAT_HPP
#include "cursor.hpp"
#include "keyboard_shortcuts_subscriber.hpp"
#include <memory>
#include <optional>
#include <vector>
#include "wlr-wrap-start.hpp"
#include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/types/wlr_virtual_keyboard_v1.h>
#include <wlr/types/wlr_virtual_pointer_v1.h>
#include "wlr-wrap-end.hpp"
class Seat final : public std::enable_shared_from_this<Seat> {
public:
struct Listeners {
std::reference_wrapper<Seat> parent;
wl_listener new_input = {};
wl_listener new_virtual_pointer = {};
wl_listener new_virtual_keyboard = {};
wl_listener new_pointer_constraint = {};
wl_listener request_cursor = {};
wl_listener request_set_selection = {};
wl_listener keyboard_shortcuts_subscribe = {};
explicit Listeners(Seat& parent) noexcept : parent(parent) {}
};
private:
Listeners listeners;
public:
Server& server;
wlr_seat* wlr;
Cursor cursor;
std::vector<std::shared_ptr<Keyboard>> keyboards;
wlr_virtual_pointer_manager_v1* virtual_pointer_mgr;
wlr_virtual_keyboard_manager_v1* virtual_keyboard_mgr;
wlr_pointer_constraints_v1* pointer_constraints;
std::shared_ptr<PointerConstraint> current_constraint;
budgie_keyboard_shortcuts_manager* keyboard_shortcuts_manager;
std::vector<std::shared_ptr<KeyboardShortcutsSubscriber>> keyboard_shortcuts_subscribers;
explicit Seat(Server& server) noexcept;
void new_input_device(wlr_input_device* device);
void set_constraint(wlr_pointer_constraint_v1* wlr_constraint);
void apply_constraint(const wlr_pointer* pointer, double* dx, double* dy) const;
bool is_pointer_locked(const wlr_pointer* pointer) const;
};
#endif
07070100000021000081A40000000000000000000000016762487000000CDF000000000000000000000000000000000000001E00000000magpie-0.0.0+209/src/main.cpp#include "server.hpp"
#include <argparse/argparse.hpp>
#include <cstdio>
#include <future>
#include <optional>
#include <string>
#include <thread>
#include <unistd.h>
#include <utility>
#include "wlr-wrap-start.hpp"
#include <wlr/backend.h>
#include <wlr/util/log.h>
#include "wlr-wrap-end.hpp"
int32_t run_compositor(const std::vector<std::string>& startup_cmds, std::promise<const char*> socket_promise = {}) {
const auto server = Server();
/* Add a Unix socket to the Wayland display. */
const char* socket = wl_display_add_socket_auto(server.display);
if (socket == nullptr) {
std::printf("Unix socket for display failed to initialize\n");
return 1;
}
socket_promise.set_value(socket);
/* Start the backend. This will enumerate outputs and inputs, become the DRM master, etc */
if (!wlr_backend_start(server.backend)) {
wlr_backend_destroy(server.backend);
wl_display_destroy(server.display);
return 1;
}
setenv("WAYLAND_DISPLAY", socket, 1);
wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", socket);
for (const auto& cmd : std::as_const(startup_cmds)) {
if (fork() == 0) {
execl("/bin/sh", "/bin/sh", "-c", cmd.c_str(), nullptr);
}
}
wl_display_run(server.display);
wl_display_destroy_clients(server.display);
wl_display_destroy(server.display);
return 0;
}
int32_t main(const int32_t argc, char** argv) {
auto argparser = argparse::ArgumentParser(argv[0], PROJECT_VERSION);
auto& logging_group = argparser.add_mutually_exclusive_group();
logging_group.add_argument("-d", "--debug").help("enable full logging").nargs(0);
logging_group.add_argument("-q", "--quiet").help("suppress informational messages").nargs(0);
auto& subprocess_group = argparser.add_mutually_exclusive_group();
subprocess_group.add_argument("-k", "--kiosk")
.help("specify a single executable whose lifecycle will be adopted, such as a login manager")
.nargs(1);
subprocess_group.add_argument("-s", "--subprocess")
.help("specify one or more executables which will be started as detached subprocesses")
.nargs(argparse::nargs_pattern::at_least_one);
try {
argparser.parse_args(argc, argv);
} catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << argparser;
return 1;
}
std::optional<std::string> kiosk_cmd;
std::vector<std::string> startup_cmds;
try {
kiosk_cmd = argparser.present("--kiosk");
startup_cmds = argparser.get<std::vector<std::string>>("--subprocess");
} catch (const std::exception& err) {
std::cerr << err.what() << std::endl;
std::cerr << argparser;
return 1;
}
if (argparser.is_used("--debug")) {
wlr_log_init(WLR_DEBUG, nullptr);
} else if (argparser.is_used("--quiet")) {
wlr_log_init(WLR_ERROR, nullptr);
} else {
wlr_log_init(WLR_INFO, nullptr);
}
if (kiosk_cmd.has_value()) {
wlr_log(WLR_INFO, "Running in kiosk mode with command '%s'.", kiosk_cmd->c_str());
std::promise<const char*> socket_promise;
std::future<const char*> socket_future = socket_promise.get_future();
auto display_thread = std::thread(run_compositor, startup_cmds, std::move(socket_promise));
display_thread.detach();
setenv("WAYLAND_DISPLAY", socket_future.get(), 1);
return system(kiosk_cmd.value().c_str());
}
return run_compositor(startup_cmds);
}
07070100000022000081A40000000000000000000000016762487000000507000000000000000000000000000000000000002100000000magpie-0.0.0+209/src/meson.buildmagpie_sources = [
'main.cpp',
'foreign_toplevel.cpp',
'output.cpp',
'server.cpp',
'xwayland.cpp',
'input/constraint.cpp',
'input/cursor.cpp',
'input/keyboard.cpp',
'input/keyboard_shortcuts_subscriber.cpp',
'input/seat.cpp',
'protocols/budgie_keyboard_shortcuts.cpp',
'surface/layer.cpp',
'surface/popup.cpp',
'surface/subsurface.cpp',
'surface/view.cpp',
'surface/xdg_view.cpp',
'surface/xwayland_view.cpp',
budgie_keyboard_shortcuts_protocol,
content_type_protocol,
cursor_shape_protocol,
xdg_shell_protocol,
wlr_layer_shell_protocol,
wlr_output_power_management_protocol,
wlr_pointer_constraints_protocol,
]
exe = executable(
'magpie-wm',
sources: magpie_sources,
dependencies: [dep_argparse, dep_m, dep_wayland_server, dep_wlroots, dep_xcb, dep_xkbcommon],
cpp_args: '-DPROJECT_VERSION="' + meson.project_version() + '"',
install: true
)
glxgears = find_program('glxgears', required: false)
if glxgears.found()
test('glxgears', exe, args: ['--debug', '--kiosk', 'sh -c "timeout 5 glxgears"'])
endif
vkgears = find_program('vkgears', required: false)
if vkgears.found()
test('vkgears', exe, args: ['--debug', '--kiosk', 'sh -c "timeout 5 vkgears"'])
endif
07070100000023000081A40000000000000000000000016762487000000E2E000000000000000000000000000000000000002000000000magpie-0.0.0+209/src/output.cpp#include "output.hpp"
#include "server.hpp"
#include "surface/layer.hpp"
#include "types.hpp"
#include <utility>
#include <wlr-wrap-start.hpp>
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/util/log.h>
#include <wlr-wrap-end.hpp>
static void output_request_state_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_output.events.request_state(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_output.events.request_state");
return;
}
Output& output = magpie_container_of(listener, output, request_state);
const auto* event = static_cast<wlr_output_event_request_state*>(data);
wlr_output_commit_state(&output.wlr, event->state);
output.update_layout();
}
/* This function is called every time an output is ready to display a frame,
* generally at the output's refresh rate (e.g. 60Hz). */
static void output_frame_notify(wl_listener* listener, [[maybe_unused]] void* data) {
Output& output = magpie_container_of(listener, output, frame);
wlr_scene_output* scene_output = wlr_scene_get_scene_output(output.server.scene, &output.wlr);
if (scene_output == nullptr || output.is_leased || !output.wlr.enabled) {
return;
}
/* Render the scene if needed and commit the output */
wlr_scene_output_commit(scene_output, nullptr);
timespec now = {};
timespec_get(&now, TIME_UTC);
wlr_scene_output_send_frame_done(scene_output, &now);
}
static void output_destroy_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_output.events.destroy(listener=%p, data=%p)", (void*) listener, data);
Output& output = magpie_container_of(listener, output, destroy);
for (const auto& layer : output.layers) {
wlr_layer_surface_v1_destroy(&layer->wlr);
}
output.server.outputs.erase(output.shared_from_this());
}
Output::Output(Server& server, wlr_output& wlr) noexcept : listeners(*this), server(server), wlr(wlr) {
wlr.data = this;
wlr_output_init_render(&wlr, server.allocator, server.renderer);
wlr_output_state state = {};
wlr_output_state_init(&state);
wlr_output_state_set_enabled(&state, true);
wlr_output_mode* mode = wlr_output_preferred_mode(&wlr);
if (mode != nullptr) {
wlr_output_state_set_mode(&state, mode);
}
wlr_output_commit_state(&wlr, &state);
wlr_output_state_finish(&state);
listeners.request_state.notify = output_request_state_notify;
wl_signal_add(&wlr.events.request_state, &listeners.request_state);
listeners.frame.notify = output_frame_notify;
wl_signal_add(&wlr.events.frame, &listeners.frame);
listeners.destroy.notify = output_destroy_notify;
wl_signal_add(&wlr.events.destroy, &listeners.destroy);
wlr_output_layout_output* layout_output = wlr_output_layout_add_auto(server.output_layout, &wlr);
wlr_scene_output* scene_output = wlr_scene_output_create(server.scene, &wlr);
wlr_scene_output_layout_add_output(server.scene_layout, layout_output, scene_output);
}
Output::~Output() noexcept {
wl_list_remove(&listeners.request_state.link);
wl_list_remove(&listeners.frame.link);
wl_list_remove(&listeners.destroy.link);
}
void Output::update_layout() {
const wlr_scene_output* scene_output = wlr_scene_get_scene_output(server.scene, &wlr);
if (scene_output == nullptr) {
return;
}
full_area.x = scene_output->x;
full_area.y = scene_output->y;
wlr_output_effective_resolution(&wlr, &full_area.width, &full_area.height);
usable_area = full_area;
for (const auto& layer : layers) {
wlr_scene_layer_surface_v1_configure(layer->scene_surface, &full_area, &usable_area);
}
}
07070100000024000081A40000000000000000000000016762487000000376000000000000000000000000000000000000002000000000magpie-0.0.0+209/src/output.hpp#ifndef MAGPIE_OUTPUT_HPP
#define MAGPIE_OUTPUT_HPP
#include "types.hpp"
#include <functional>
#include <memory>
#include <set>
#include "wlr-wrap-start.hpp"
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/box.h>
#include "wlr-wrap-end.hpp"
class Output final : public std::enable_shared_from_this<Output> {
public:
struct Listeners {
std::reference_wrapper<Output> parent;
wl_listener request_state = {};
wl_listener frame = {};
wl_listener destroy = {};
explicit Listeners(Output& parent) noexcept : parent(parent) {}
};
private:
Listeners listeners;
public:
Server& server;
wlr_output& wlr;
wlr_box full_area = {};
wlr_box usable_area = {};
std::set<std::shared_ptr<Layer>> layers;
bool is_leased = false;
Output(Server& server, wlr_output& wlr) noexcept;
~Output() noexcept;
void update_layout();
};
#endif
07070100000025000041ED0000000000000000000000026762487000000000000000000000000000000000000000000000001F00000000magpie-0.0.0+209/src/protocols07070100000026000081A400000000000000000000000167624870000015EE000000000000000000000000000000000000003D00000000magpie-0.0.0+209/src/protocols/budgie_keyboard_shortcuts.cpp#include "budgie_keyboard_shortcuts.hpp"
#include <budgie-keyboard-shortcuts-server-protocol.h>
static void budgie_keyboard_shortcuts_subscriber_destroy([[maybe_unused]] wl_client* client, wl_resource* resource) {
auto* subscriber = static_cast<budgie_keyboard_shortcuts_subscriber*>(wl_resource_get_user_data(resource));
wl_signal_emit_mutable(&subscriber->events.destroy, subscriber);
wl_resource_set_user_data(resource, nullptr);
wl_list_remove(&subscriber->link);
wl_resource_destroy(resource);
}
static void budgie_keyboard_shortcuts_subscriber_register_shortcut(
[[maybe_unused]] wl_client* client, wl_resource* resource, uint32_t modifiers, uint32_t keycode) {
auto* subscriber = static_cast<budgie_keyboard_shortcuts_subscriber*>(wl_resource_get_user_data(resource));
budgie_keyboard_shortcuts_shortcut* it;
wl_list_for_each(it, &subscriber->registered_shortcuts, link) {
if (it->modifiers == modifiers && it->keycode == keycode) {
// already registered, can skip
return;
}
}
auto* shortcut = static_cast<budgie_keyboard_shortcuts_shortcut*>(calloc(1, sizeof(budgie_keyboard_shortcuts_shortcut)));
if (shortcut == nullptr) {
wl_client_post_no_memory(client);
return;
}
shortcut->modifiers = modifiers;
shortcut->keycode = keycode;
wl_list_insert(&subscriber->registered_shortcuts, &shortcut->link);
wl_signal_emit_mutable(&subscriber->events.register_shortcut, shortcut);
}
static void budgie_keyboard_shortcuts_subscriber_unregister_shortcut(
[[maybe_unused]] wl_client* client, wl_resource* resource, uint32_t modifiers, uint32_t keycode) {
auto* subscriber = static_cast<budgie_keyboard_shortcuts_subscriber*>(wl_resource_get_user_data(resource));
budgie_keyboard_shortcuts_shortcut* target = nullptr;
budgie_keyboard_shortcuts_shortcut* it;
wl_list_for_each(it, &subscriber->registered_shortcuts, link) {
if (it->modifiers == modifiers && it->keycode == keycode) {
target = it;
break;
}
}
if (target == nullptr) {
// the passed shortcut wasn't registered
return;
}
wl_list_remove(&target->link);
wl_signal_emit_mutable(&subscriber->events.unregister_shortcut, target);
free(target);
}
static constexpr struct budgie_keyboard_shortcuts_subscriber_interface budgie_keyboard_shortcuts_subscriber_impl = {
.destroy = budgie_keyboard_shortcuts_subscriber_destroy,
.register_shortcut = budgie_keyboard_shortcuts_subscriber_register_shortcut,
.unregister_shortcut = budgie_keyboard_shortcuts_subscriber_unregister_shortcut,
};
static void budgie_keyboard_shortcuts_subscriber_destroy(wl_resource* resource) {
budgie_keyboard_shortcuts_subscriber_destroy(nullptr, resource);
}
void budgie_keyboard_shortcuts_manager_send_shortcut_press(
budgie_keyboard_shortcuts_subscriber* subscriber, uint32_t time_msec, uint32_t modifiers, uint32_t keycode) {
budgie_keyboard_shortcuts_subscriber_send_shortcut_press(subscriber->resource, time_msec, modifiers, keycode);
}
static void budgie_keyboard_shortcuts_manager_destroy([[maybe_unused]] wl_client* client, wl_resource* resource) {
auto* manager = static_cast<budgie_keyboard_shortcuts_manager*>(wl_resource_get_user_data(resource));
wl_signal_emit_mutable(&manager->events.destroy, manager);
wl_resource_set_user_data(resource, nullptr);
wl_resource_destroy(resource);
}
static void budgie_keyboard_shortcuts_manager_subscribe(wl_client* client, wl_resource* resource, uint32_t id) {
auto* manager = static_cast<budgie_keyboard_shortcuts_manager*>(wl_resource_get_user_data(resource));
auto* subscriber =
static_cast<budgie_keyboard_shortcuts_subscriber*>(calloc(1, sizeof(budgie_keyboard_shortcuts_subscriber)));
if (subscriber == nullptr) {
wl_client_post_no_memory(client);
return;
}
int32_t version = wl_resource_get_version(resource);
auto* subscriber_resource = wl_resource_create(client, &budgie_keyboard_shortcuts_subscriber_interface, version, id);
if (subscriber_resource == nullptr) {
free(subscriber);
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(subscriber_resource, &budgie_keyboard_shortcuts_subscriber_impl, subscriber,
budgie_keyboard_shortcuts_subscriber_destroy);
wl_list_insert(&manager->subscribers, &subscriber->link);
wl_signal_emit_mutable(&manager->events.subscribe, subscriber);
}
static constexpr struct budgie_keyboard_shortcuts_manager_interface budgie_keyboard_shortcuts_manager_impl = {
.destroy = budgie_keyboard_shortcuts_manager_destroy,
.subscribe = budgie_keyboard_shortcuts_manager_subscribe,
};
static void budgie_keyboard_shortcuts_manager_bind(wl_client* client, void* data, uint32_t version, uint32_t id) {
auto* manager = static_cast<budgie_keyboard_shortcuts_manager*>(data);
wl_resource* resource = wl_resource_create(client, &budgie_keyboard_shortcuts_manager_interface, (int32_t) version, id);
if (resource == nullptr) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_implementation(resource, &budgie_keyboard_shortcuts_manager_impl, manager, nullptr);
}
budgie_keyboard_shortcuts_manager* budgie_keyboard_shortcuts_manager_create(wl_display* display, uint32_t version) {
auto* ret = static_cast<budgie_keyboard_shortcuts_manager*>(calloc(1, sizeof(budgie_keyboard_shortcuts_manager)));
ret->global = wl_global_create(display, &budgie_keyboard_shortcuts_manager_interface, (int32_t) version, nullptr,
budgie_keyboard_shortcuts_manager_bind);
if (ret->global == nullptr) {
free(ret);
return nullptr;
}
wl_list_init(&ret->subscribers);
wl_signal_init(&ret->events.destroy);
wl_signal_init(&ret->events.subscribe);
return ret;
}
07070100000027000081A40000000000000000000000016762487000000508000000000000000000000000000000000000003D00000000magpie-0.0.0+209/src/protocols/budgie_keyboard_shortcuts.hpp#ifndef MAGPIE_PROTOCOLS_BUDGIE_KEYBOARD_SHORTCUTS_HPP
#define MAGPIE_PROTOCOLS_BUDGIE_KEYBOARD_SHORTCUTS_HPP
#include <wayland-server-core.h>
struct budgie_keyboard_shortcuts_manager {
wl_global* global;
wl_list subscribers; // budgie_keyboard_shortcuts_subscriber.link
struct {
wl_signal destroy; // data: (budgie_keyboard_shortcuts_manager*)
wl_signal subscribe; // data: (budgie_keyboard_shortcuts_subscriber*)
} events;
void* data;
};
budgie_keyboard_shortcuts_manager* budgie_keyboard_shortcuts_manager_create(wl_display* display, uint32_t version);
struct budgie_keyboard_shortcuts_shortcut {
wl_list link;
uint32_t modifiers;
uint32_t keycode;
};
struct budgie_keyboard_shortcuts_subscriber {
wl_resource* resource;
wl_list link;
wl_list registered_shortcuts; // budgie_keyboard_shortcuts_shortcut.link
struct {
wl_signal destroy; // data: (budgie_keyboard_shortcuts_subscriber*)
wl_signal register_shortcut; // data: (budgie_keyboard_shortcuts_shortcut*)
wl_signal unregister_shortcut; // data: (budgie_keyboard_shortcuts_shortcut*)
} events;
void* data;
};
void budgie_keyboard_shortcuts_manager_send_shortcut_press(
budgie_keyboard_shortcuts_subscriber* subscriber, uint32_t time_msec, uint32_t modifiers, uint32_t keycode);
#endif
07070100000028000081A40000000000000000000000016762487000005E9F000000000000000000000000000000000000002000000000magpie-0.0.0+209/src/server.cpp#include "server.hpp"
#include "input/seat.hpp"
#include "output.hpp"
#include "surface/layer.hpp"
#include "surface/popup.hpp"
#include "surface/surface.hpp"
#include "surface/view.hpp"
#include "types.hpp"
#include "xwayland.hpp"
#include <utility>
#include "wlr-wrap-start.hpp"
#include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_data_control_v1.h>
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_fractional_scale_v1.h>
#include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_presentation_time.h>
#include <wlr/types/wlr_primary_selection_v1.h>
#include <wlr/types/wlr_screencopy_v1.h>
#include <wlr/types/wlr_security_context_v1.h>
#include <wlr/types/wlr_single_pixel_buffer_v1.h>
#include <wlr/types/wlr_viewporter.h>
#include <wlr/types/wlr_xcursor_manager.h>
#include <wlr/types/wlr_xdg_foreign_registry.h>
#include <wlr/types/wlr_xdg_foreign_v1.h>
#include <wlr/types/wlr_xdg_foreign_v2.h>
#include <wlr/types/wlr_xdg_output_v1.h>
#include <wlr/util/box.h>
#include <wlr/util/log.h>
#include <wlr/xwayland/shell.h>
#include "wlr-wrap-end.hpp"
static wlr_layer_surface_v1* find_subsurface_parent_layer(const wlr_subsurface* subsurface) {
wlr_surface* parent = subsurface->parent;
wlr_subsurface* parent_as_subsurface = wlr_subsurface_try_from_wlr_surface(parent);
wlr_layer_surface_v1* parent_as_layer_surface = wlr_layer_surface_v1_try_from_wlr_surface(parent);
// traverse up the tree to find the root parent surface
while (parent_as_subsurface != nullptr) {
parent = parent_as_subsurface->parent;
parent_as_subsurface = wlr_subsurface_try_from_wlr_surface(parent);
parent_as_layer_surface = wlr_layer_surface_v1_try_from_wlr_surface(parent);
}
return parent_as_layer_surface;
}
void Server::focus_view(std::shared_ptr<View>&& view) {
auto layer = this->focused_layer.lock();
if (layer != nullptr) {
if (layer->wlr.current.keyboard_interactive != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) {
// if we have a focused layer and it's not exclusive, give focus to the view instead
focused_layer.reset();
} else {
// we shouldn't apply constraints or keyboard focus, we're focused on an exclusive layer
return;
}
}
std::shared_ptr<View> prev_view = focused_view.lock();
if (view == prev_view) {
// Don't re-focus an already focused view, or clear focus if we already don't have it.
return;
}
if (prev_view != nullptr) {
focused_view.reset();
prev_view->set_activated(false);
}
// if we're just clearing focus, we're done!
if (view == nullptr) {
return;
}
// Move the view to the front
views.remove(view);
views.insert(views.begin(), view);
focused_view = view;
view->set_activated(true);
}
void Server::try_focus_next_exclusive_layer() {
std::shared_ptr<Layer> topmost_exclusive_layer = nullptr;
// find the topmost layer in exclusive focus mode. if there are multiple within a single scene layer, the spec defines that
// focus order within that scene layer is implementation defined, so it doesn't matter which is chosen
for (const auto& output : outputs) {
for (const auto& layer : output->layers) {
if (layer->wlr.current.keyboard_interactive == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE &&
(topmost_exclusive_layer == nullptr || layer->scene_layer > topmost_exclusive_layer->scene_layer)) {
topmost_exclusive_layer = layer;
}
}
}
focus_layer(topmost_exclusive_layer);
}
void Server::focus_layer(const std::shared_ptr<Layer>& layer) {
if (layer == nullptr) {
focused_layer.reset();
if (focused_view.lock() != nullptr) {
focused_view.lock()->set_activated(true);
}
return;
}
// if there's already an exclusive focused shell layer with an equal or higher scene layer, just return
auto focused_layer_locked = focused_layer.lock();
if (focused_layer_locked != nullptr &&
focused_layer_locked->wlr.current.keyboard_interactive == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE &&
focused_layer_locked->scene_layer >= layer->scene_layer) {
return;
}
// same if this layer can't gain focus
if (layer->wlr.current.keyboard_interactive == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE) {
return;
}
focused_layer = layer;
const auto* keyboard = wlr_seat_get_keyboard(seat->wlr);
if (keyboard != nullptr) {
wlr_seat_keyboard_notify_enter(
seat->wlr, layer->get_wlr_surface(), keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers);
}
}
std::weak_ptr<Surface> Server::surface_at(const double lx, const double ly, wlr_surface** wlr, double* sx, double* sy) const {
/* This returns the topmost node in the scene at the given layout coords.
* we only care about surface nodes as we are specifically looking for a
* surface in the surface tree of a magpie_view. */
wlr_scene_node* node = wlr_scene_node_at(&scene->tree.node, lx, ly, sx, sy);
if (node == nullptr || node->type != WLR_SCENE_NODE_BUFFER) {
return {};
}
wlr_scene_buffer* scene_buffer = wlr_scene_buffer_from_node(node);
const wlr_scene_surface* scene_surface = wlr_scene_surface_try_from_buffer(scene_buffer);
if (scene_surface == nullptr) {
return {};
}
*wlr = scene_surface->surface;
/* Find the node corresponding to the magpie_view at the root of this
* surface tree, it is the only one for which we set the data field. */
const wlr_scene_tree* tree = node->parent;
while (tree != nullptr && tree->node.data == nullptr) {
tree = tree->node.parent;
}
if (tree != nullptr) {
return static_cast<Surface*>(tree->node.data)->weak_from_this();
}
return {};
}
/* This event is raised by the backend when a new output (aka a display or
* monitor) becomes available. */
static void new_output_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_backend.events.new_output(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_backend.events.new_output");
return;
}
Server& server = magpie_container_of(listener, server, backend_new_output);
auto* new_output = static_cast<wlr_output*>(data);
if (server.drm_manager != nullptr) {
wlr_drm_lease_v1_manager_offer_output(server.drm_manager, new_output);
}
/* Configures the output created by the backend to use our allocator
* and our renderer. Must be done once, before commiting the output */
wlr_output_init_render(new_output, server.allocator, server.renderer);
/* Some backends don't have modes. DRM+KMS does, and we need to set a mode
* before we can use the output. The mode is a tuple of (width, height,
* refresh rate), and each monitor supports only a specific set of modes. We
* just pick the monitor's preferred mode, a more sophisticated compositor
* would let the user configure it. */
if (wl_list_empty(&new_output->modes) == 0) {
wlr_output_mode* mode = wlr_output_preferred_mode(new_output);
wlr_output_state state = {};
wlr_output_state_init(&state);
wlr_output_state_set_mode(&state, mode);
wlr_output_state_set_enabled(&state, true);
if (!wlr_output_commit_state(new_output, &state)) {
wlr_log(WLR_ERROR, "Failed to commit mode to new output %s", new_output->name);
wlr_output_state_finish(&state);
return;
}
wlr_output_state_finish(&state);
}
/* Allocates and configures our state for this output */
auto output = std::make_shared<Output>(server, *new_output);
server.outputs.emplace(output);
/* Adds this to the output layout. The add_auto function arranges outputs
* from left-to-right in the order they appear. A more sophisticated
* compositor would let the user configure the arrangement of outputs in the
* layout.
*
* The output layout utility automatically adds a wl_output global to the
* display, which Wayland clients can see to find out information about the
* output (such as DPI, scale factor, manufacturer, etc).
*/
wlr_output_layout_add_auto(server.output_layout, new_output);
output->update_layout();
}
static void output_power_manager_set_mode_notify([[maybe_unused]] wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_output_power_manager.events.set_mode(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_output_power_manager.events.set_mode");
return;
}
const auto& event = *static_cast<wlr_output_power_v1_set_mode_event*>(data);
wlr_output_state state = {};
wlr_output_state_init(&state);
wlr_output_state_set_enabled(&state, event.mode == ZWLR_OUTPUT_POWER_V1_MODE_ON);
if (!wlr_output_commit_state(event.output, &state)) {
wlr_log(WLR_ERROR, "Failed to set enabled state %d for output %s", state.enabled, event.output->name);
}
wlr_output_state_finish(&state);
}
static void new_xdg_toplevel_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_xdg_shell.events.new_toplevel(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_xdg_shell.events.new_toplevel");
return;
}
Server& server = magpie_container_of(listener, server, xdg_shell_new_xdg_toplevel);
auto& xdg_toplevel = *static_cast<wlr_xdg_toplevel*>(data);
server.views.emplace_back(std::make_shared<XdgView>(server, xdg_toplevel));
}
static void new_layer_surface_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_layer_shell_v1.events.new_surface(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_layer_shell_v1.events.new_surface");
return;
}
Server& server = magpie_container_of(listener, server, layer_shell_new_layer_surface);
auto& layer_surface = *static_cast<wlr_layer_surface_v1*>(data);
/* Allocate a View for this surface */
Output* output;
if (layer_surface.output == nullptr) {
output = static_cast<Output*>(wlr_output_layout_get_center_output(server.output_layout)->data);
layer_surface.output = &output->wlr;
} else {
output = static_cast<Output*>(layer_surface.output->data);
}
output->layers.emplace(std::make_shared<Layer>(*output, layer_surface));
}
static void request_activation_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_xdg_activation_v1.events.request_activation(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_xdg_activation_v1.events.request_activation");
return;
}
Server& server = magpie_container_of(listener, server, activation_request_activation);
const auto* event = static_cast<wlr_xdg_activation_v1_request_activate_event*>(data);
const auto* xdg_surface = wlr_xdg_surface_try_from_wlr_surface(event->surface);
if (xdg_surface != nullptr) {
auto* view = dynamic_cast<View*>(static_cast<Surface*>(xdg_surface->surface->data));
if (view != nullptr && xdg_surface->surface->mapped) {
server.focus_view(std::dynamic_pointer_cast<View>(view->shared_from_this()));
}
return;
}
const auto* layer_surface = wlr_layer_surface_v1_try_from_wlr_surface(event->surface);
if (layer_surface != nullptr) {
auto* layer = dynamic_cast<Layer*>(static_cast<Surface*>(layer_surface->surface->data));
if (layer != nullptr && layer_surface->surface->mapped) {
server.focus_layer(std::dynamic_pointer_cast<Layer>(layer->shared_from_this()));
}
return;
}
const auto* subsurface = wlr_subsurface_try_from_wlr_surface(event->surface);
if (subsurface != nullptr) {
wlr_layer_surface_v1* parent_as_layer_surface = find_subsurface_parent_layer(subsurface);
if (parent_as_layer_surface != nullptr) {
auto* layer = dynamic_cast<Layer*>(static_cast<Surface*>(layer_surface->surface->data));
if (layer != nullptr && parent_as_layer_surface->surface->mapped) {
server.focus_layer(std::dynamic_pointer_cast<Layer>(layer->shared_from_this()));
}
}
}
}
static void drm_lease_request_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_drm_lease_manager_v1.events.drm_lease_request(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_drm_lease_manager_v1.events.drm_lease_request");
return;
}
Server& server = magpie_container_of(listener, server, drm_lease_request);
auto* request = static_cast<wlr_drm_lease_request_v1*>(data);
const wlr_drm_lease_v1* lease = wlr_drm_lease_request_v1_grant(request);
if (lease == nullptr) {
wlr_drm_lease_request_v1_reject(request);
return;
}
for (size_t i = 0; i < request->n_connectors; i++) {
auto* output = static_cast<Output*>(request->connectors[i]->output->data);
if (output == nullptr) {
continue;
}
wlr_output_state state = {};
wlr_output_state_init(&state);
wlr_output_state_set_enabled(&state, false);
wlr_output_commit_state(&output->wlr, &state);
wlr_output_state_finish(&state);
wlr_output_layout_remove(server.output_layout, &output->wlr);
output->is_leased = true;
}
}
void output_layout_change_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_output_manager.events.change(listener=%p, data=%p)", (void*) listener, data);
Server& server = magpie_container_of(listener, server, output_layout_change);
if (server.num_pending_output_layout_changes > 0) {
return;
}
wlr_output_configuration_v1* config = wlr_output_configuration_v1_create();
for (const auto& output : server.outputs) {
wlr_output_configuration_head_v1* head = wlr_output_configuration_head_v1_create(config, &output->wlr);
wlr_box box = {};
wlr_output_layout_get_box(server.output_layout, &output->wlr, &box);
if (!wlr_box_empty(&box)) {
head->state.x = box.x;
head->state.y = box.y;
}
}
wlr_output_manager_v1_set_configuration(server.output_manager, config);
}
void output_manager_apply_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_output_manager_v1.events.apply(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_output_manager_v1.events.apply");
return;
}
Server& server = magpie_container_of(listener, server, output_manager_apply);
auto& config = *static_cast<wlr_output_configuration_v1*>(data);
server.num_pending_output_layout_changes++;
wlr_output_configuration_head_v1* head;
wl_list_for_each(head, &config.heads, link) {
Output& output = *static_cast<Output*>(head->state.output->data);
const bool enabled = head->state.enabled && !output.is_leased;
const bool adding = enabled && !output.wlr.enabled;
const bool removing = !enabled && output.wlr.enabled;
wlr_output_state state = {};
wlr_output_state_init(&state);
wlr_output_state_set_enabled(&state, enabled);
if (enabled) {
if (head->state.mode != nullptr) {
wlr_output_state_set_mode(&state, head->state.mode);
} else {
const int32_t width = head->state.custom_mode.width;
const int32_t height = head->state.custom_mode.height;
const int32_t refresh = head->state.custom_mode.refresh;
wlr_output_state_set_custom_mode(&state, width, height, refresh);
}
wlr_output_state_set_scale(&state, head->state.scale);
wlr_output_state_set_transform(&state, head->state.transform);
}
if (!wlr_output_commit_state(&output.wlr, &state)) {
wlr_log(WLR_ERROR, "Output config commit failed");
continue;
}
wlr_output_state_finish(&state);
if (adding) {
wlr_output_layout_add_auto(server.output_layout, &output.wlr);
}
if (enabled) {
wlr_box box = {};
wlr_output_layout_get_box(server.output_layout, &output.wlr, &box);
if (box.x != head->state.x || box.y != head->state.y) {
/* This overrides the automatic layout */
wlr_output_layout_add(server.output_layout, &output.wlr, head->state.x, head->state.y);
}
}
if (removing) {
wlr_output_layout_remove(server.output_layout, &output.wlr);
}
}
wlr_output_configuration_v1_send_succeeded(&config);
wlr_output_configuration_v1_destroy(&config);
for (const auto& output : server.outputs) {
wlr_xcursor_manager_load(server.seat->cursor.cursor_mgr, output->wlr.scale);
}
server.seat->cursor.reload_image();
}
bool filter_globals(const struct wl_client* client, const struct wl_global* global, void* data) {
const auto& server = *static_cast<Server*>(data);
if (server.xwayland != nullptr) {
const auto* wlr_xwayland = server.xwayland->wlr;
if (global == wlr_xwayland->shell_v1->global) {
return wlr_xwayland->server != nullptr && client == wlr_xwayland->server->client;
}
}
const auto* security_context =
wlr_security_context_manager_v1_lookup_client(server.security_context_manager, (wl_client*) client);
if (server.is_restricted(global)) {
return security_context == nullptr;
}
return true;
}
void early_exit(wl_display* display, const std::string& err) {
wlr_log(WLR_ERROR, "%s", err.c_str());
wl_display_destroy_clients(display);
wl_display_destroy(display);
std::exit(2);
}
Server::Server() : listeners(*this) {
/* The Wayland display is managed by libwayland. It handles accepting
* clients from the Unix socket, manging Wayland globals, and so on. */
display = wl_display_create();
if (display == nullptr) {
wlr_log(WLR_ERROR, "Failed to create a wl_display");
std::exit(2);
}
/* The backend is a wlroots feature which abstracts the underlying input and
* output hardware. The autocreate option will choose the most suitable
* backend based on the current environment, such as opening an X11 window
* if an X11 server is running. */
session = nullptr;
backend = wlr_backend_autocreate(wl_display_get_event_loop(display), &session);
if (backend == nullptr) {
early_exit(display, "Failed to create a wlr_backend for the Wayland display");
}
/* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The user
* can also specify a renderer using the WLR_RENDERER env var.
* The renderer is responsible for defining the various pixel formats it
* supports for shared memory, this configures that for clients. */
renderer = wlr_renderer_autocreate(backend);
if (renderer == nullptr) {
early_exit(display, "Failed to create a wlr_renderer for the Wayland display");
}
wlr_renderer_init_wl_display(renderer, display);
/* Autocreates an allocator for us.
* The allocator is the bridge between the renderer and the backend. It
* handles the buffer creation, allowing wlroots to render onto the
* screen */
allocator = wlr_allocator_autocreate(backend, renderer);
if (allocator == nullptr) {
early_exit(display, "Failed to create a wlr_allocator for the Wayland display");
}
/* This creates some hands-off wlroots interfaces. The compositor is
* necessary for clients to allocate surfaces, the subcompositor allows to
* assign the role of subsurfaces to surfaces and the data device manager
* handles the clipboard. Each of these wlroots interfaces has room for you
* to dig your fingers in and play with their behavior if you want. Note that
* the clients cannot set the selection directly without compositor approval,
* see the handling of the request_set_selection event below.*/
compositor = wlr_compositor_create(display, 6, renderer);
wlr_subcompositor_create(display);
wlr_data_device_manager_create(display);
security_context_manager = wlr_security_context_manager_v1_create(display);
wl_display_set_global_filter(display, filter_globals, this);
// https://wayfire.org/2020/08/04/Wayfire-0-5.html
wlr_primary_selection_v1_device_manager_create(display);
/* Creates an output layout, which a wlroots utility for working with an
* arrangement of screens in a physical layout. */
output_layout = wlr_output_layout_create(display);
listeners.output_layout_change.notify = output_layout_change_notify;
wl_signal_add(&output_layout->events.change, &listeners.output_layout_change);
wlr_xdg_output_manager_v1_create(display, output_layout);
output_manager = wlr_output_manager_v1_create(display);
listeners.output_manager_apply.notify = output_manager_apply_notify;
wl_signal_add(&output_manager->events.apply, &listeners.output_manager_apply);
output_power_manager = wlr_output_power_manager_v1_create(display);
listeners.output_power_manager_set_mode.notify = output_power_manager_set_mode_notify;
wl_signal_add(&output_power_manager->events.set_mode, &listeners.output_power_manager_set_mode);
seat = std::make_shared<Seat>(*this);
/* Configure a listener to be notified when new outputs are available on the
* backend. */
listeners.backend_new_output.notify = new_output_notify;
wl_signal_add(&backend->events.new_output, &listeners.backend_new_output);
/* Create a scene graph. This is a wlroots abstraction that handles all
* rendering and damage tracking. All the compositor author needs to do
* is add things that should be rendered to the scene graph at the proper
* positions and then call wlr_scene_output_commit() to render a frame if
* necessary.
*/
scene = wlr_scene_create();
if (scene == nullptr) {
early_exit(display, "Failed to create a wlr_scene for the Wayland display");
}
for (int32_t idx = 0; idx <= MAGPIE_SCENE_LAYER_LOCK; idx++) {
scene_layers[idx] = wlr_scene_tree_create(&scene->tree);
wlr_scene_node_raise_to_top(&scene_layers[idx]->node);
}
scene_layout = wlr_scene_attach_output_layout(scene, output_layout);
auto* presentation = wlr_presentation_create(display, backend);
if (presentation == nullptr) {
early_exit(display, "Failed to create a wlr_presentation for the Wayland display");
}
xdg_shell = wlr_xdg_shell_create(display, 5);
listeners.xdg_shell_new_xdg_toplevel.notify = new_xdg_toplevel_notify;
wl_signal_add(&xdg_shell->events.new_toplevel, &listeners.xdg_shell_new_xdg_toplevel);
layer_shell = wlr_layer_shell_v1_create(display, 4);
listeners.layer_shell_new_layer_surface.notify = new_layer_surface_notify;
wl_signal_add(&layer_shell->events.new_surface, &listeners.layer_shell_new_layer_surface);
xdg_activation = wlr_xdg_activation_v1_create(display);
listeners.activation_request_activation.notify = request_activation_notify;
wl_signal_add(&xdg_activation->events.request_activate, &listeners.activation_request_activation);
data_control_manager = wlr_data_control_manager_v1_create(display);
foreign_toplevel_manager = wlr_foreign_toplevel_manager_v1_create(display);
wlr_fractional_scale_manager_v1_create(display, 1);
xwayland = std::make_shared<XWayland>(*this);
wlr_viewporter_create(display);
wlr_single_pixel_buffer_manager_v1_create(display);
screencopy_manager = wlr_screencopy_manager_v1_create(display);
export_dmabuf_manager = wlr_export_dmabuf_manager_v1_create(display);
gamma_control_manager = wlr_gamma_control_manager_v1_create(display);
wlr_xdg_foreign_registry* foreign_registry = wlr_xdg_foreign_registry_create(display);
wlr_xdg_foreign_v1_create(display, foreign_registry);
wlr_xdg_foreign_v2_create(display, foreign_registry);
idle_notifier = wlr_idle_notifier_v1_create(display);
idle_inhibit_manager = wlr_idle_inhibit_v1_create(display);
drm_manager = wlr_drm_lease_v1_manager_create(display, backend);
if (drm_manager != nullptr) {
listeners.drm_lease_request.notify = drm_lease_request_notify;
wl_signal_add(&drm_manager->events.request, &listeners.drm_lease_request);
}
content_type_manager = wlr_content_type_manager_v1_create(display, 1);
}
bool Server::is_restricted(const wl_global* global) const {
if (drm_manager != nullptr) {
wlr_drm_lease_device_v1* drm_lease_dev;
wl_list_for_each(drm_lease_dev, &drm_manager->devices, link) {
if (global == drm_lease_dev->global) {
return true;
}
}
}
// clang-format off
return
global == data_control_manager->global ||
global == foreign_toplevel_manager->global ||
global == export_dmabuf_manager->global ||
global == gamma_control_manager->global ||
global == layer_shell->global ||
global == output_manager->global ||
global == output_power_manager->global ||
global == seat->keyboard_shortcuts_manager->global ||
global == seat->virtual_keyboard_mgr->global ||
global == seat->virtual_pointer_mgr->global ||
global == screencopy_manager->global ||
global == security_context_manager->global;
// clang-format on
}
07070100000029000081A40000000000000000000000016762487000000DF6000000000000000000000000000000000000002000000000magpie-0.0.0+209/src/server.hpp#ifndef MAGPIE_SERVER_HPP
#define MAGPIE_SERVER_HPP
#include "types.hpp"
#include <functional>
#include <list>
#include <memory>
#include <set>
#include "wlr-wrap-start.hpp"
#include <wlr/backend/session.h>
#include <wlr/render/allocator.h>
#include <wlr/types/wlr_content_type_v1.h>
#include <wlr/types/wlr_data_control_v1.h>
#include <wlr/types/wlr_drm_lease_v1.h>
#include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
#include <wlr/types/wlr_idle_inhibit_v1.h>
#include <wlr/types/wlr_idle_notify_v1.h>
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_output_management_v1.h>
#include <wlr/types/wlr_output_power_management_v1.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_screencopy_v1.h>
#include <wlr/types/wlr_security_context_v1.h>
#include <wlr/types/wlr_xdg_activation_v1.h>
#include <wlr/types/wlr_xdg_shell.h>
#include "wlr-wrap-end.hpp"
using magpie_scene_layer_t = enum magpie_scene_layer {
MAGPIE_SCENE_LAYER_BACKGROUND = 0,
MAGPIE_SCENE_LAYER_BOTTOM,
MAGPIE_SCENE_LAYER_NORMAL,
MAGPIE_SCENE_LAYER_TOP,
MAGPIE_SCENE_LAYER_OVERLAY,
MAGPIE_SCENE_LAYER_LOCK
};
class Server final : public std::enable_shared_from_this<Server> {
public:
struct Listeners {
std::reference_wrapper<Server> parent;
wl_listener xdg_shell_new_xdg_toplevel = {};
wl_listener layer_shell_new_layer_surface = {};
wl_listener activation_request_activation = {};
wl_listener backend_new_output = {};
wl_listener drm_lease_request = {};
wl_listener output_layout_change = {};
wl_listener output_manager_apply = {};
wl_listener output_power_manager_set_mode = {};
explicit Listeners(Server& parent) noexcept : parent(parent) {}
};
private:
Listeners listeners;
public:
wl_display* display;
wlr_session* session;
wlr_backend* backend;
wlr_renderer* renderer;
wlr_allocator* allocator;
wlr_compositor* compositor;
std::shared_ptr<XWayland> xwayland;
wlr_scene* scene;
wlr_scene_output_layout* scene_layout;
std::array<wlr_scene_tree*, MAGPIE_SCENE_LAYER_LOCK + 1> scene_layers = {};
wlr_xdg_shell* xdg_shell;
wlr_xdg_activation_v1* xdg_activation;
wlr_foreign_toplevel_manager_v1* foreign_toplevel_manager;
wlr_layer_shell_v1* layer_shell;
std::weak_ptr<Layer> focused_layer;
std::shared_ptr<Seat> seat;
std::list<std::shared_ptr<View>> views;
std::weak_ptr<View> focused_view;
std::weak_ptr<View> grabbed_view;
double grab_x = 0.0, grab_y = 0.0;
wlr_box grab_geobox = {};
uint32_t resize_edges = 0;
wlr_output_manager_v1* output_manager;
wlr_output_power_manager_v1* output_power_manager;
wlr_output_layout* output_layout;
std::set<std::shared_ptr<Output>> outputs;
uint8_t num_pending_output_layout_changes = 0;
wlr_idle_notifier_v1* idle_notifier;
wlr_idle_inhibit_manager_v1* idle_inhibit_manager;
wlr_drm_lease_v1_manager* drm_manager;
wlr_content_type_manager_v1* content_type_manager;
wlr_data_control_manager_v1* data_control_manager;
wlr_security_context_manager_v1* security_context_manager;
wlr_export_dmabuf_manager_v1* export_dmabuf_manager;
wlr_gamma_control_manager_v1* gamma_control_manager;
wlr_screencopy_manager_v1* screencopy_manager;
Server();
std::weak_ptr<Surface> surface_at(double lx, double ly, wlr_surface** wlr, double* sx, double* sy) const;
void focus_view(std::shared_ptr<View>&& view);
void focus_layer(const std::shared_ptr<Layer>& layer);
void try_focus_next_exclusive_layer();
bool is_restricted(const wl_global* global) const;
};
#endif
0707010000002A000041ED0000000000000000000000026762487000000000000000000000000000000000000000000000001D00000000magpie-0.0.0+209/src/surface0707010000002B000081A400000000000000000000000167624870000018BA000000000000000000000000000000000000002700000000magpie-0.0.0+209/src/surface/layer.cpp#include "layer.hpp"
#include "output.hpp"
#include "popup.hpp"
#include "server.hpp"
#include "surface.hpp"
#include "subsurface.hpp"
#include "types.hpp"
#include "wlr-wrap-start.hpp"
#include <wlr/types/wlr_fractional_scale_v1.h>
#include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/types/wlr_subcompositor.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/log.h>
#include "wlr-wrap-end.hpp"
static magpie_scene_layer_t magpie_layer_from_wlr_layer(const zwlr_layer_shell_v1_layer layer) {
switch (layer) {
case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND:
return MAGPIE_SCENE_LAYER_BACKGROUND;
case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM:
return MAGPIE_SCENE_LAYER_BOTTOM;
case ZWLR_LAYER_SHELL_V1_LAYER_TOP:
return MAGPIE_SCENE_LAYER_TOP;
default:
return MAGPIE_SCENE_LAYER_OVERLAY;
}
}
/* Called when the surface is mapped, or ready to display on-screen. */
static void wlr_layer_surface_v1_map_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_layer_surface_v1.events.map(listener=%p, data=%p)", (void*) listener, data);
Layer& layer = magpie_container_of(listener, layer, map);
wlr_scene_node_set_enabled(layer.scene_node, true);
wlr_surface_send_enter(layer.get_wlr_surface(), &layer.output.wlr);
}
/* Called when the surface is unmapped, and should no longer be shown. */
static void wlr_layer_surface_v1_unmap_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_layer_surface_v1.events.unmap(listener=%p, data=%p)", (void*) listener, data);
const Layer& layer = magpie_container_of(listener, layer, unmap);
wlr_scene_node_set_enabled(layer.scene_node, false);
}
/* Called when the surface is destroyed and should never be shown again. */
static void wlr_layer_surface_v1_destroy_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_layer_surface_v1.events.destroy(listener=%p, data=%p)", (void*) listener, data);
Layer& layer = magpie_container_of(listener, layer, destroy);
layer.output.layers.erase(std::dynamic_pointer_cast<Layer>(layer.shared_from_this()));
}
static void wlr_layer_surface_v1_commit_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_layer_surface_v1.events.commit(listener=%p, data=%p)", (void*) listener, data);
Layer& layer = magpie_container_of(listener, layer, commit);
Server& server = layer.output.server;
const wlr_layer_surface_v1& surface = layer.wlr;
const uint32_t committed = surface.current.committed;
if ((committed & WLR_LAYER_SURFACE_V1_STATE_LAYER) != 0) {
layer.scene_layer = magpie_layer_from_wlr_layer(surface.current.layer);
wlr_scene_node_reparent(layer.scene_node, server.scene_layers[layer.scene_layer]);
}
if ((committed & WLR_LAYER_SURFACE_V1_STATE_KEYBOARD_INTERACTIVITY) != 0) {
switch (layer.wlr.current.keyboard_interactive) {
case ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_NONE:
server.try_focus_next_exclusive_layer();
break;
case ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_ON_DEMAND:
case ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE:
server.focus_layer(std::dynamic_pointer_cast<Layer>(layer.shared_from_this()));
break;
}
}
if (committed != 0) {
layer.output.update_layout();
}
}
static void wlr_layer_surface_v1_new_popup_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_layer_surface_v1.events.new_popup(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_layer_surface_v1.events.new_popup");
return;
}
const Layer& layer = magpie_container_of(listener, layer, new_popup);
auto* surface = static_cast<Surface*>(layer.wlr.surface->data);
surface->popups.emplace(std::make_shared<Popup>(*surface, *static_cast<wlr_xdg_popup*>(data)));
}
static void wlr_layer_surface_v1_new_subsurface_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_layer_surface_v1.events.new_subsurface(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_layer_surface_v1.events.new_subsurface");
return;
}
Layer& layer = magpie_container_of(listener, layer, new_subsurface);
auto& subsurface = *static_cast<wlr_subsurface*>(data);
layer.subsurfaces.emplace(std::make_shared<Subsurface>(layer, subsurface));
}
Layer::Layer(Output& output, wlr_layer_surface_v1& surface) noexcept
: listeners(*this), server(output.server), output(output), wlr(surface) {
const magpie_scene_layer_t chosen_layer = magpie_layer_from_wlr_layer(surface.current.layer);
scene_surface = wlr_scene_layer_surface_v1_create(output.server.scene_layers[chosen_layer], &surface);
scene_node = &scene_surface->tree->node;
scene_node->data = this;
surface.surface->data = this;
wlr_fractional_scale_v1_notify_scale(surface.surface, surface.output->scale);
wlr_surface_set_preferred_buffer_scale(surface.surface, std::ceil(surface.output->scale));
listeners.map.notify = wlr_layer_surface_v1_map_notify;
wl_signal_add(&surface.surface->events.map, &listeners.map);
listeners.unmap.notify = wlr_layer_surface_v1_unmap_notify;
wl_signal_add(&surface.surface->events.unmap, &listeners.unmap);
listeners.destroy.notify = wlr_layer_surface_v1_destroy_notify;
wl_signal_add(&surface.events.destroy, &listeners.destroy);
listeners.commit.notify = wlr_layer_surface_v1_commit_notify;
wl_signal_add(&surface.surface->events.commit, &listeners.commit);
listeners.new_popup.notify = wlr_layer_surface_v1_new_popup_notify;
wl_signal_add(&surface.events.new_popup, &listeners.new_popup);
listeners.new_subsurface.notify = wlr_layer_surface_v1_new_subsurface_notify;
wl_signal_add(&surface.surface->events.new_subsurface, &listeners.new_subsurface);
}
Layer::~Layer() noexcept {
wl_list_remove(&listeners.map.link);
wl_list_remove(&listeners.unmap.link);
wl_list_remove(&listeners.destroy.link);
wl_list_remove(&listeners.commit.link);
wl_list_remove(&listeners.new_popup.link);
wl_list_remove(&listeners.new_subsurface.link);
}
wlr_surface* Layer::get_wlr_surface() const {
return wlr.surface;
}
Server& Layer::get_server() const {
return server;
}
bool Layer::is_view() const {
return false;
}
0707010000002C000081A4000000000000000000000001676248700000044D000000000000000000000000000000000000002700000000magpie-0.0.0+209/src/surface/layer.hpp#ifndef MAGPIE_LAYER_HPP
#define MAGPIE_LAYER_HPP
#include "server.hpp"
#include "surface.hpp"
#include "types.hpp"
#include <functional>
#include <memory>
#include <set>
#include "wlr-wrap-start.hpp"
#include <wlr/types/wlr_subcompositor.h>
#include "wlr-wrap-end.hpp"
class Layer final : public Surface {
public:
struct Listeners {
std::reference_wrapper<Layer> parent;
wl_listener map = {};
wl_listener unmap = {};
wl_listener destroy = {};
wl_listener commit = {};
wl_listener new_popup = {};
wl_listener new_subsurface = {};
explicit Listeners(Layer& parent) noexcept : parent(parent) {}
};
private:
Listeners listeners;
public:
Server& server;
Output& output;
wlr_layer_surface_v1& wlr;
wlr_scene_layer_surface_v1* scene_surface;
magpie_scene_layer_t scene_layer = MAGPIE_SCENE_LAYER_NORMAL;
Layer(Output& output, wlr_layer_surface_v1& surface) noexcept;
~Layer() noexcept override;
[[nodiscard]] wlr_surface* get_wlr_surface() const override;
[[nodiscard]] Server& get_server() const override;
[[nodiscard]] bool is_view() const override;
};
#endif
0707010000002D000081A40000000000000000000000016762487000000E06000000000000000000000000000000000000002700000000magpie-0.0.0+209/src/surface/popup.cpp#include "popup.hpp"
#include "output.hpp"
#include "server.hpp"
#include "subsurface.hpp"
#include "surface.hpp"
#include "types.hpp"
#include <utility>
#include "wlr-wrap-start.hpp"
#include <wlr/util/log.h>
#include "wlr-wrap-end.hpp"
static void popup_map_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xdg_popup.events.map(listener=%p, data=%p)", (void*) listener, data);
Popup& popup = magpie_container_of(listener, popup, map);
wlr_box current = {};
wlr_xdg_surface_get_geometry(popup.wlr->base, ¤t);
for (const auto& output : std::as_const(popup.server.outputs)) {
wlr_box output_area = output->full_area;
wlr_box intersect = {};
wlr_box_intersection(&intersect, ¤t, &output_area);
if (!wlr_box_empty(¤t)) {
wlr_surface_send_enter(popup.wlr->base->surface, &output->wlr);
}
}
wlr_scene_node_raise_to_top(popup.scene_node);
}
static void popup_destroy_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xdg_popup.events.destroy(listener=%p, data=%p)", (void*) listener, data);
Popup& popup = magpie_container_of(listener, popup, destroy);
popup.wlr = nullptr;
popup.parent.popups.erase(std::dynamic_pointer_cast<Popup>(popup.shared_from_this()));
}
static void popup_new_popup_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_xdg_popup.events.new_popup(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_xdg_popup.events.new_popup");
return;
}
Popup& popup = magpie_container_of(listener, popup, new_popup);
popup.popups.emplace(std::make_shared<Popup>(popup, *static_cast<wlr_xdg_popup*>(data)));
}
static void popup_new_subsurface_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_xdg_popup.events.new_subsurface(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_xdg_popup.events.new_subsurface");
return;
}
Popup& popup = magpie_container_of(listener, popup, new_subsurface);
popup.subsurfaces.emplace(std::make_shared<Subsurface>(popup, *static_cast<wlr_subsurface*>(data)));
}
Popup::Popup(Surface& parent, wlr_xdg_popup& wlr) noexcept
: listeners(*this), server(parent.get_server()), parent(parent), wlr(&wlr) {
auto* scene_tree = wlr_scene_xdg_surface_create(wlr_scene_tree_from_node(parent.scene_node), wlr.base);
scene_node = &scene_tree->node;
scene_node->data = this;
wlr.base->surface->data = this;
// just in case the popup hasn't been configured already (2024-07-13)
wlr_xdg_surface_schedule_configure(wlr.base);
listeners.map.notify = popup_map_notify;
wl_signal_add(&wlr.base->surface->events.map, &listeners.map);
listeners.destroy.notify = popup_destroy_notify;
wl_signal_add(&wlr.base->events.destroy, &listeners.destroy);
listeners.new_popup.notify = popup_new_popup_notify;
wl_signal_add(&wlr.base->events.new_popup, &listeners.new_popup);
listeners.new_subsurface.notify = popup_new_subsurface_notify;
wl_signal_add(&wlr.base->surface->events.new_subsurface, &listeners.new_subsurface);
}
Popup::~Popup() noexcept {
wl_list_remove(&listeners.map.link);
wl_list_remove(&listeners.destroy.link);
wl_list_remove(&listeners.new_popup.link);
wl_list_remove(&listeners.new_subsurface.link);
if (wlr != nullptr) {
wlr_xdg_popup_destroy(wlr);
}
}
wlr_surface* Popup::get_wlr_surface() const {
return wlr->base->surface;
}
Server& Popup::get_server() const {
return server;
}
bool Popup::is_view() const {
return false;
}
0707010000002E000081A40000000000000000000000016762487000000373000000000000000000000000000000000000002700000000magpie-0.0.0+209/src/surface/popup.hpp#ifndef MAGPIE_POPUP_HPP
#define MAGPIE_POPUP_HPP
#include "surface.hpp"
#include "types.hpp"
#include <functional>
#include <memory>
#include "wlr-wrap-start.hpp"
#include <wlr/types/wlr_xdg_shell.h>
#include "wlr-wrap-end.hpp"
class Popup final : public Surface {
public:
struct Listeners {
std::reference_wrapper<Popup> parent;
wl_listener map = {};
wl_listener destroy = {};
wl_listener new_popup = {};
wl_listener new_subsurface = {};
explicit Listeners(Popup& parent) noexcept : parent(parent) {}
};
private:
Listeners listeners;
public:
Server& server;
Surface& parent;
wlr_xdg_popup* wlr;
Popup(Surface& parent, wlr_xdg_popup& wlr) noexcept;
~Popup() noexcept override;
[[nodiscard]] wlr_surface* get_wlr_surface() const override;
[[nodiscard]] Server& get_server() const override;
[[nodiscard]] bool is_view() const override;
};
#endif
0707010000002F000081A40000000000000000000000016762487000000483000000000000000000000000000000000000002C00000000magpie-0.0.0+209/src/surface/subsurface.cpp#include "subsurface.hpp"
#include "types.hpp"
#include <utility>
#include "wlr-wrap-start.hpp"
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_subcompositor.h>
#include <wlr/util/log.h>
#include "wlr-wrap-end.hpp"
static void subsurface_destroy_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_subsurface.events.destroy(listener=%p, data=%p)", (void*) listener, data);
Subsurface& subsurface = magpie_container_of(listener, subsurface, destroy);
subsurface.parent.subsurfaces.erase(std::dynamic_pointer_cast<Subsurface>(subsurface.shared_from_this()));
}
Subsurface::Subsurface(Surface& parent, wlr_subsurface& subsurface) noexcept
: listeners(*this), parent(parent), wlr(subsurface) {
listeners.destroy.notify = subsurface_destroy_notify;
wl_signal_add(&subsurface.events.destroy, &listeners.destroy);
}
Subsurface::~Subsurface() noexcept {
wl_list_remove(&listeners.destroy.link);
}
wlr_surface* Subsurface::get_wlr_surface() const {
return wlr.surface;
}
Server& Subsurface::get_server() const {
return parent.get_server();
}
bool Subsurface::is_view() const {
return false;
}
07070100000030000081A4000000000000000000000001676248700000035E000000000000000000000000000000000000002C00000000magpie-0.0.0+209/src/surface/subsurface.hpp#ifndef MAGPIE_SUBSURFACE_HPP
#define MAGPIE_SUBSURFACE_HPP
#include "server.hpp"
#include "surface.hpp"
#include "types.hpp"
#include <functional>
#include <memory>
#include <set>
#include "wlr-wrap-start.hpp"
#include <wlr/types/wlr_subcompositor.h>
#include "wlr-wrap-end.hpp"
class Subsurface final : public Surface {
public:
struct Listeners {
std::reference_wrapper<Subsurface> parent;
wl_listener destroy = {};
explicit Listeners(Subsurface& parent) noexcept : parent(parent) {}
};
private:
Listeners listeners;
public:
Surface& parent;
wlr_subsurface& wlr;
Subsurface(Surface& parent, wlr_subsurface& subsurface) noexcept;
~Subsurface() noexcept override;
[[nodiscard]] wlr_surface* get_wlr_surface() const override;
[[nodiscard]] Server& get_server() const override;
[[nodiscard]] bool is_view() const override;
};
#endif
07070100000031000081A40000000000000000000000016762487000000276000000000000000000000000000000000000002900000000magpie-0.0.0+209/src/surface/surface.hpp#ifndef MAGPIE_SURFACE_HPP
#define MAGPIE_SURFACE_HPP
#include "types.hpp"
#include <memory>
#include <set>
#include "wlr-wrap-start.hpp"
#include <wlr/types/wlr_scene.h>
#include "wlr-wrap-end.hpp"
struct Surface : public virtual std::enable_shared_from_this<Surface> {
wlr_scene_node* scene_node = nullptr;
std::set<std::shared_ptr<Popup>> popups;
std::set<std::shared_ptr<Subsurface>> subsurfaces;
virtual ~Surface() noexcept = default;
[[nodiscard]] virtual Server& get_server() const = 0;
[[nodiscard]] virtual wlr_surface* get_wlr_surface() const = 0;
[[nodiscard]] virtual bool is_view() const = 0;
};
#endif
07070100000032000081A40000000000000000000000016762487000002BAB000000000000000000000000000000000000002600000000magpie-0.0.0+209/src/surface/view.cpp#include "view.hpp"
#include <algorithm>
#include <utility>
#include "foreign_toplevel.hpp"
#include "input/seat.hpp"
#include "output.hpp"
#include "server.hpp"
#include "types.hpp"
#include "wlr-wrap-start.hpp"
#include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_fractional_scale_v1.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_scene.h>
#include <wlr/util/edges.h>
#include "wlr-wrap-end.hpp"
std::optional<std::reference_wrapper<Output>> View::find_output_for_maximize() const {
const Server& server = get_server();
if (server.outputs.empty()) {
return {};
}
const Cursor& cursor = server.seat->cursor;
std::shared_ptr<Output> best_output = nullptr;
int64_t best_area = 0;
for (const auto& output : server.outputs) {
if (!wlr_output_layout_intersects(server.output_layout, &output->wlr, &previous)) {
continue;
}
wlr_box output_box = {};
wlr_output_layout_get_box(server.output_layout, &output->wlr, &output_box);
wlr_box intersection = {};
wlr_box_intersection(&intersection, &previous, &output_box);
auto intersection_width = static_cast<int64_t>(intersection.width);
auto intersection_height = static_cast<int64_t>(intersection.height);
const auto intersection_area = intersection_width * intersection_height;
if (intersection_area > best_area) {
best_area = intersection_area;
best_output = output;
}
}
// if it's outside all outputs, just use the pointer position
if (best_output == nullptr) {
for (const auto& output : server.outputs) {
const auto cx = static_cast<int32_t>(std::round(cursor.wlr.x));
const auto cy = static_cast<int32_t>(std::round(cursor.wlr.y));
if (wlr_output_layout_contains_point(server.output_layout, &output->wlr, cx, cy)) {
best_output = output;
break;
}
}
}
// still nothing? use the first output in the list
if (best_output == nullptr) {
best_output = static_cast<Output*>(wlr_output_layout_get_center_output(server.output_layout)->data)->shared_from_this();
}
if (best_output == nullptr) {
return {};
}
return std::ref(*best_output);
}
int32_t View::find_min_y() const {
const Server& server = get_server();
if (server.outputs.empty()) {
return 0;
}
wlr_box current_copy = {.x = current.x, .y = 0, .width = current.width, .height = current.height + current.y};
current_copy.height += current_copy.y;
current_copy.y = 0;
int32_t min_y = INT32_MAX;
for (const auto& output : server.outputs) {
wlr_box output_box = {};
wlr_output_layout_get_box(server.output_layout, &output->wlr, &output_box);
wlr_box intersection = {};
wlr_box_intersection(&intersection, &previous, &output_box);
if (!wlr_box_empty(&intersection)) {
min_y = std::min(min_y, output_box.y);
}
}
return min_y == INT32_MAX ? 0 : min_y;
}
void View::begin_interactive(const CursorMode mode, const uint32_t edges) {
Server& server = get_server();
Cursor& cursor = server.seat->cursor;
wlr_surface* focused_surface = server.seat->wlr->pointer_state.focused_surface;
if (focused_surface == nullptr || get_wlr_surface() != wlr_surface_get_root_surface(focused_surface)) {
/* Deny move/resize requests from unfocused clients. */
return;
}
server.grabbed_view = std::dynamic_pointer_cast<View>(shared_from_this());
cursor.mode = mode;
if (mode == MAGPIE_CURSOR_MOVE) {
server.grab_x = cursor.wlr.x - current.x;
server.grab_y = cursor.wlr.y - current.y;
} else {
const wlr_box geo_box = get_geometry();
const double border_x = current.x + geo_box.x + (((edges & WLR_EDGE_RIGHT) != 0) ? geo_box.width : 0);
const double border_y = current.y + geo_box.y + (((edges & WLR_EDGE_BOTTOM) != 0) ? geo_box.height : 0);
server.grab_x = cursor.wlr.x - border_x;
server.grab_y = cursor.wlr.y - border_y;
server.grab_geobox = geo_box;
server.grab_geobox.x += current.x;
server.grab_geobox.y += current.y;
server.resize_edges = edges;
}
}
void View::set_geometry(const int32_t x, const int32_t y, const int32_t width, const int32_t height) {
const wlr_box min_size = get_min_size();
const wlr_box max_size = get_max_size();
const int32_t bounded_width = std::clamp(width, min_size.width, max_size.width);
const int32_t bounded_height = std::clamp(height, min_size.height, max_size.height);
if (curr_placement == VIEW_PLACEMENT_STACKING) {
previous = current;
}
current = {.x = x, .y = y, .width = bounded_width, .height = bounded_height};
current.y = std::max(y, find_min_y());
if (scene_node != nullptr) {
wlr_scene_node_set_position(scene_node, current.x, current.y);
}
impl_set_geometry(current.x, current.y, current.width, current.height);
}
void View::set_position(const int32_t x, const int32_t y) {
if (curr_placement == VIEW_PLACEMENT_STACKING) {
previous.x = current.x;
previous.y = current.y;
}
current.x = x;
current.y = y;
current.y = std::max(y, find_min_y());
if (scene_node != nullptr) {
wlr_scene_node_set_position(scene_node, current.x, current.y);
}
impl_set_position(current.x, current.y);
}
void View::set_size(const int32_t width, const int32_t height) {
const wlr_box min_size = get_min_size();
const wlr_box max_size = get_max_size();
const int32_t bounded_width = std::clamp(width, min_size.width, max_size.width);
const int32_t bounded_height = std::clamp(height, min_size.height, max_size.height);
if (curr_placement == VIEW_PLACEMENT_STACKING) {
previous.width = current.width;
previous.height = current.height;
}
current.width = bounded_width;
current.height = bounded_height;
impl_set_size(current.width, current.height);
}
void View::update_outputs(const bool ignore_previous) const {
wlr_box largest_intersection = {};
auto target_buffer_scale = 1.0;
for (const auto& output : std::as_const(get_server().outputs)) {
wlr_box output_area = output->full_area;
wlr_box prev_intersect = {};
wlr_box_intersection(&prev_intersect, &previous, &output_area);
wlr_box curr_intersect = {};
wlr_box_intersection(&curr_intersect, ¤t, &output_area);
if (curr_intersect.width * curr_intersect.height > largest_intersection.width * largest_intersection.height) {
largest_intersection = curr_intersect;
target_buffer_scale = output->wlr.scale;
}
if (ignore_previous) {
if (!wlr_box_empty(&curr_intersect)) {
wlr_surface_send_enter(get_wlr_surface(), &output->wlr);
if (toplevel_handle.has_value()) {
toplevel_handle->output_enter(*output);
}
}
} else if (wlr_box_empty(&prev_intersect) && !wlr_box_empty(&curr_intersect)) {
wlr_surface_send_enter(get_wlr_surface(), &output->wlr);
if (toplevel_handle.has_value()) {
toplevel_handle->output_enter(*output);
}
} else if (!wlr_box_empty(&prev_intersect) && wlr_box_empty(&curr_intersect)) {
wlr_surface_send_leave(get_wlr_surface(), &output->wlr);
if (toplevel_handle.has_value()) {
toplevel_handle->output_leave(*output);
}
}
}
wlr_fractional_scale_v1_notify_scale(get_wlr_surface(), target_buffer_scale);
wlr_surface_set_preferred_buffer_scale(get_wlr_surface(), std::ceil(target_buffer_scale));
}
void View::set_activated(const bool activated) {
impl_set_activated(activated);
if (toplevel_handle.has_value()) {
toplevel_handle->set_activated(activated);
}
const auto seat = get_server().seat;
if (activated) {
wlr_scene_node_raise_to_top(scene_node);
/*
* Tell the seat to have the keyboard enter this surface. wlroots will keep
* track of this and automatically send key events to the appropriate
* clients without additional work on your part.
*/
const auto* keyboard = wlr_seat_get_keyboard(seat->wlr);
if (keyboard != nullptr) {
wlr_seat_keyboard_notify_enter(
seat->wlr, get_wlr_surface(), keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers);
}
wlr_pointer_constraint_v1* constraint =
wlr_pointer_constraints_v1_constraint_for_surface(seat->pointer_constraints, get_wlr_surface(), seat->wlr);
seat->set_constraint(constraint);
} else {
wlr_seat_keyboard_notify_clear_focus(seat->wlr);
seat->set_constraint(nullptr);
}
}
void View::set_placement(const ViewPlacement new_placement, const bool force) {
const Server& server = get_server();
if (!force) {
if (curr_placement == new_placement) {
return;
}
wlr_surface* focused_surface = server.seat->wlr->pointer_state.focused_surface;
if (focused_surface == nullptr || get_wlr_surface() != wlr_surface_get_root_surface(focused_surface)) {
/* Deny placement requests from unfocused clients. */
return;
}
}
bool res = true;
switch (new_placement) {
case VIEW_PLACEMENT_STACKING:
stack();
break;
case VIEW_PLACEMENT_MAXIMIZED:
res = maximize();
break;
case VIEW_PLACEMENT_FULLSCREEN:
res = fullscreen();
break;
}
if (res) {
prev_placement = curr_placement;
curr_placement = new_placement;
if (toplevel_handle.has_value()) {
toplevel_handle->set_placement(new_placement);
}
}
}
void View::stack() {
impl_set_maximized(false);
impl_set_fullscreen(false);
set_geometry(previous.x, previous.y, previous.width, previous.height);
update_outputs();
}
bool View::maximize() {
const auto best_output = find_output_for_maximize();
if (!best_output.has_value()) {
return false;
}
const wlr_box output_box = best_output->get().usable_area;
const wlr_box min_size = get_min_size();
if (output_box.width < min_size.width || output_box.height < min_size.height) {
return false;
}
const wlr_box max_size = get_max_size();
if (output_box.width > max_size.width || output_box.height > max_size.height) {
return false;
}
impl_set_fullscreen(false);
impl_set_maximized(true);
set_geometry(output_box.x, output_box.y, output_box.width, output_box.height);
update_outputs();
return true;
}
bool View::fullscreen() {
const auto best_output = find_output_for_maximize();
if (!best_output.has_value()) {
return false;
}
const wlr_box output_box = best_output->get().full_area;
const wlr_box min_size = get_min_size();
if (output_box.width < min_size.width || output_box.height < min_size.height) {
return false;
}
const wlr_box max_size = get_max_size();
if (output_box.width > max_size.width || output_box.height > max_size.height) {
return false;
}
impl_set_fullscreen(true);
set_geometry(output_box.x, output_box.y, output_box.width, output_box.height);
update_outputs();
return true;
}
void View::set_minimized(const bool minimized) {
if (minimized == is_minimized) {
return;
}
if (toplevel_handle.has_value()) {
toplevel_handle->set_minimized(minimized);
}
impl_set_minimized(minimized);
this->is_minimized = minimized;
if (minimized) {
wlr_scene_node_set_enabled(scene_node, false);
set_activated(false);
} else {
wlr_scene_node_set_enabled(scene_node, true);
}
}
void View::toggle_maximize() {
if (curr_placement != VIEW_PLACEMENT_FULLSCREEN) {
set_placement(curr_placement != VIEW_PLACEMENT_MAXIMIZED ? VIEW_PLACEMENT_MAXIMIZED : VIEW_PLACEMENT_STACKING);
}
}
void View::toggle_fullscreen() {
if (curr_placement == VIEW_PLACEMENT_FULLSCREEN) {
set_placement(prev_placement);
} else {
set_placement(VIEW_PLACEMENT_FULLSCREEN);
}
}
07070100000033000081A40000000000000000000000016762487000001491000000000000000000000000000000000000002600000000magpie-0.0.0+209/src/surface/view.hpp#ifndef MAGPIE_VIEW_HPP
#define MAGPIE_VIEW_HPP
#include "foreign_toplevel.hpp"
#include "input/cursor.hpp"
#include "surface.hpp"
#include "types.hpp"
#include <optional>
#include "wlr-wrap-start.hpp"
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/util/box.h>
#include <wlr/xwayland.h>
#include "wlr-wrap-end.hpp"
struct View : public Surface {
ViewPlacement prev_placement = VIEW_PLACEMENT_STACKING;
ViewPlacement curr_placement = VIEW_PLACEMENT_STACKING;
bool is_minimized = false;
wlr_box current = {};
wlr_box previous = {};
std::optional<ForeignToplevelHandle> toplevel_handle;
~View() noexcept override = default;
[[nodiscard]] virtual wlr_box get_geometry() const = 0;
[[nodiscard]] virtual wlr_box get_min_size() const = 0;
[[nodiscard]] virtual wlr_box get_max_size() const = 0;
virtual void map() = 0;
virtual void unmap() = 0;
virtual void close() = 0;
[[nodiscard]] bool is_view() const override {
return true;
}
void begin_interactive(CursorMode mode, uint32_t edges);
void set_position(int32_t x, int32_t y);
void set_size(int32_t width, int32_t height);
void set_geometry(int32_t x, int32_t y, int32_t width, int32_t height);
void update_outputs(bool ignore_previous = false) const;
void set_activated(bool activated);
void set_placement(ViewPlacement new_placement, bool force = false);
void set_minimized(bool minimized);
void toggle_maximize();
void toggle_fullscreen();
private:
[[nodiscard]] std::optional<std::reference_wrapper<Output>> find_output_for_maximize() const;
[[nodiscard]] int32_t find_min_y() const;
void stack();
bool maximize();
bool fullscreen();
protected:
virtual void impl_set_position(int32_t x, int32_t y) = 0;
virtual void impl_set_size(int32_t width, int32_t height) = 0;
virtual void impl_set_geometry(int x, int y, int width, int height) = 0;
virtual void impl_set_activated(bool activated) = 0;
virtual void impl_set_fullscreen(bool fullscreen) = 0;
virtual void impl_set_maximized(bool maximized) = 0;
virtual void impl_set_minimized(bool minimized) = 0;
};
class XdgView final : public View {
public:
struct Listeners {
std::reference_wrapper<XdgView> parent;
wl_listener map = {};
wl_listener unmap = {};
wl_listener destroy = {};
wl_listener commit = {};
wl_listener request_move = {};
wl_listener request_resize = {};
wl_listener request_maximize = {};
wl_listener request_minimize = {};
wl_listener request_fullscreen = {};
wl_listener set_title = {};
wl_listener set_app_id = {};
wl_listener set_parent = {};
wl_listener new_popup = {};
wl_listener new_subsurface = {};
explicit Listeners(XdgView& parent) noexcept : parent(parent) {}
};
private:
Listeners listeners;
bool pending_map = true;
public:
Server& server;
wlr_xdg_toplevel& wlr;
XdgView(Server& server, wlr_xdg_toplevel& xdg_toplevel) noexcept;
~XdgView() noexcept override;
[[nodiscard]] wlr_surface* get_wlr_surface() const override;
[[nodiscard]] Server& get_server() const override;
[[nodiscard]] wlr_box get_geometry() const override;
[[nodiscard]] wlr_box get_min_size() const override;
[[nodiscard]] wlr_box get_max_size() const override;
void map() override;
void unmap() override;
void close() override;
protected:
void impl_set_position(int32_t x, int32_t y) override;
void impl_set_size(int32_t width, int32_t height) override;
void impl_set_geometry(int x, int y, int width, int height) override;
void impl_set_activated(bool activated) override;
void impl_set_fullscreen(bool fullscreen) override;
void impl_set_maximized(bool maximized) override;
void impl_set_minimized(bool minimized) override;
};
class XWaylandView final : public View {
public:
struct Listeners {
std::reference_wrapper<XWaylandView> parent;
wl_listener map = {};
wl_listener unmap = {};
wl_listener associate = {};
wl_listener dissociate = {};
wl_listener destroy = {};
wl_listener commit = {};
wl_listener request_configure = {};
wl_listener request_move = {};
wl_listener request_resize = {};
wl_listener request_maximize = {};
wl_listener request_fullscreen = {};
wl_listener set_geometry = {};
wl_listener set_title = {};
wl_listener set_class = {};
wl_listener set_parent = {};
explicit Listeners(XWaylandView& parent) noexcept : parent(parent) {}
} listeners;
Server& server;
wlr_xwayland_surface& wlr;
XWaylandView(Server& server, wlr_xwayland_surface& surface) noexcept;
~XWaylandView() noexcept override;
[[nodiscard]] wlr_surface* get_wlr_surface() const override;
[[nodiscard]] Server& get_server() const override;
[[nodiscard]] wlr_box get_geometry() const override;
[[nodiscard]] wlr_box get_min_size() const override;
[[nodiscard]] wlr_box get_max_size() const override;
void map() override;
void unmap() override;
void close() override;
protected:
void impl_set_position(int32_t x, int32_t y) override;
void impl_set_size(int32_t width, int32_t height) override;
void impl_set_geometry(int32_t x, int32_t y, int width, int height) override;
void impl_set_activated(bool activated) override;
void impl_set_fullscreen(bool fullscreen) override;
void impl_set_maximized(bool maximized) override;
void impl_set_minimized(bool minimized) override;
};
#endif
07070100000034000081A4000000000000000000000001676248700000340A000000000000000000000000000000000000002A00000000magpie-0.0.0+209/src/surface/xdg_view.cpp#include "types.hpp"
#include "view.hpp"
#include "output.hpp"
#include "popup.hpp"
#include "server.hpp"
#include "subsurface.hpp"
#include "surface.hpp"
#include "input/seat.hpp"
#include "wlr-wrap-start.hpp"
#include <wlr/types/wlr_cursor.h>
#include <wlr/util/log.h>
#include "wlr-wrap-end.hpp"
/* Called when the surface is mapped, or ready to display on-screen. */
static void xdg_toplevel_map_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xdg_toplevel.events.map(listener=%p, data=%p)", (void*) listener, data);
XdgView& view = magpie_container_of(listener, view, map);
view.map();
}
/* Called when the surface is unmapped, and should no longer be shown. */
static void xdg_toplevel_unmap_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xdg_toplevel.events.unmap(listener=%p, data=%p)", (void*) listener, data);
XdgView& view = magpie_container_of(listener, view, unmap);
view.unmap();
}
static void xdg_toplevel_commit_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xdg_toplevel.events.commit(listener=%p, data=%p)", (void*) listener, data);
XdgView& view = magpie_container_of(listener, view, commit);
if (view.wlr.base->initial_commit) {
wlr_xdg_toplevel_set_wm_capabilities(&view.wlr,
WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE | WLR_XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE |
WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
}
auto geometry = view.get_geometry();
if (view.current.width != geometry.width || view.current.height != geometry.height) {
view.set_size(geometry.width, geometry.height);
}
}
/* Called when the surface is destroyed and should never be shown again. */
static void xdg_toplevel_destroy_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xdg_toplevel.events.destroy(listener=%p, data=%p)", (void*) listener, data);
XdgView& view = magpie_container_of(listener, view, destroy);
view.server.views.remove(std::dynamic_pointer_cast<View>(view.shared_from_this()));
}
/* This event is raised when a client would like to begin an interactive
* move, typically because the user clicked on their client-side
* decorations. Note that a more sophisticated compositor should check the
* provided serial against a list of button press serials sent to this
* client, to prevent the client from requesting this whenever they want. */
static void xdg_toplevel_request_move_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xdg_toplevel.events.request_move(listener=%p, data=%p)", (void*) listener, data);
XdgView& view = magpie_container_of(listener, view, request_move);
if (view.wlr.base->initialized) {
view.set_placement(VIEW_PLACEMENT_STACKING);
view.begin_interactive(MAGPIE_CURSOR_MOVE, 0);
}
}
/* This event is raised when a client would like to begin an interactive
* resize, typically because the user clicked on their client-side
* decorations. Note that a more sophisticated compositor should check the
* provided serial against a list of button press serials sent to this
* client, to prevent the client from requesting this whenever they want. */
static void xdg_toplevel_request_resize_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_xdg_toplevel.events.request_resize(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_xdg_toplevel.events.request_resize");
return;
}
XdgView& view = magpie_container_of(listener, view, request_resize);
const auto* event = static_cast<wlr_xdg_toplevel_resize_event*>(data);
if (view.wlr.base->initialized) {
view.set_placement(VIEW_PLACEMENT_STACKING);
view.begin_interactive(MAGPIE_CURSOR_RESIZE, event->edges);
}
}
/* This event is raised when a client would like to maximize itself,
* typically because the user clicked on the maximize button on
* client-side decorations. */
static void xdg_toplevel_request_maximize_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xdg_toplevel.events.request_maximize(listener=%p, data=%p)", (void*) listener, data);
XdgView& view = magpie_container_of(listener, view, request_maximize);
if (view.wlr.base->initialized) {
view.toggle_maximize();
wlr_xdg_surface_schedule_configure(view.wlr.base);
}
}
static void xdg_toplevel_request_fullscreen_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xdg_toplevel.events.request_fullscreen(listener=%p, data=%p)", (void*) listener, data);
XdgView& view = magpie_container_of(listener, view, request_fullscreen);
if (view.wlr.base->initialized) {
view.toggle_fullscreen();
wlr_xdg_surface_schedule_configure(view.wlr.base);
}
}
static void xdg_toplevel_request_minimize_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xdg_toplevel.events.request_minimize(listener=%p, data=%p)", (void*) listener, data);
XdgView& view = magpie_container_of(listener, view, request_minimize);
if (view.wlr.base->initialized) {
view.set_minimized(!view.is_minimized);
wlr_xdg_surface_schedule_configure(view.wlr.base);
}
}
static void xdg_toplevel_set_title_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xdg_toplevel.events.set_title(listener=%p, data=%p)", (void*) listener, data);
XdgView& view = magpie_container_of(listener, view, set_title);
view.toplevel_handle->set_title(view.wlr.title);
}
static void xdg_toplevel_set_app_id_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xdg_toplevel.events.set_app_id(listener=%p, data=%p)", (void*) listener, data);
XdgView& view = magpie_container_of(listener, view, set_app_id);
view.toplevel_handle->set_app_id(view.wlr.app_id);
}
static void xdg_toplevel_set_parent_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xdg_toplevel.events.set_parent(listener=%p, data=%p)", (void*) listener, data);
XdgView& view = magpie_container_of(listener, view, set_parent);
if (view.wlr.parent != nullptr) {
const auto* m_view = dynamic_cast<View*>(static_cast<Surface*>(view.wlr.parent->base->data));
if (m_view != nullptr) {
view.toplevel_handle->set_parent(m_view->toplevel_handle);
return;
}
}
view.toplevel_handle->set_parent({});
}
static void xdg_surface_new_popup_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_xdg_toplevel.events.new_popup(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_xdg_surface.events.new_popup");
return;
}
XdgView& view = magpie_container_of(listener, view, new_popup);
view.popups.emplace(std::make_shared<Popup>(view, *static_cast<wlr_xdg_popup*>(data)));
}
static void xdg_surface_new_subsurface_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_xdg_toplevel.events.new_subsurface(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_xdg_surface.events.new_subsurface");
return;
}
XdgView& view = magpie_container_of(listener, view, new_subsurface);
view.subsurfaces.emplace(std::make_shared<Subsurface>(view, *static_cast<wlr_subsurface*>(data)));
}
XdgView::XdgView(Server& server, wlr_xdg_toplevel& xdg_toplevel) noexcept
: listeners(*this), server(server), wlr(xdg_toplevel) {
auto* scene_tree = wlr_scene_xdg_surface_create(&server.scene->tree, xdg_toplevel.base);
scene_node = &scene_tree->node;
scene_node->data = this;
wlr.base->surface->data = this;
toplevel_handle.emplace(*this);
toplevel_handle->set_title(xdg_toplevel.title);
toplevel_handle->set_app_id(xdg_toplevel.app_id);
if (xdg_toplevel.parent != nullptr) {
const auto* m_view = dynamic_cast<View*>(static_cast<Surface*>(xdg_toplevel.parent->base->data));
if (m_view != nullptr) {
toplevel_handle->set_parent(m_view->toplevel_handle);
}
}
listeners.map.notify = xdg_toplevel_map_notify;
wl_signal_add(&wlr.base->surface->events.map, &listeners.map);
listeners.unmap.notify = xdg_toplevel_unmap_notify;
wl_signal_add(&wlr.base->surface->events.unmap, &listeners.unmap);
listeners.commit.notify = xdg_toplevel_commit_notify;
wl_signal_add(&wlr.base->surface->events.commit, &listeners.commit);
listeners.destroy.notify = xdg_toplevel_destroy_notify;
wl_signal_add(&wlr.base->events.destroy, &listeners.destroy);
listeners.request_move.notify = xdg_toplevel_request_move_notify;
wl_signal_add(&wlr.events.request_move, &listeners.request_move);
listeners.request_resize.notify = xdg_toplevel_request_resize_notify;
wl_signal_add(&wlr.events.request_resize, &listeners.request_resize);
listeners.request_maximize.notify = xdg_toplevel_request_maximize_notify;
wl_signal_add(&wlr.events.request_maximize, &listeners.request_maximize);
listeners.request_minimize.notify = xdg_toplevel_request_minimize_notify;
wl_signal_add(&wlr.events.request_minimize, &listeners.request_minimize);
listeners.request_fullscreen.notify = xdg_toplevel_request_fullscreen_notify;
wl_signal_add(&wlr.events.request_fullscreen, &listeners.request_fullscreen);
listeners.set_title.notify = xdg_toplevel_set_title_notify;
wl_signal_add(&wlr.events.set_title, &listeners.set_title);
listeners.set_app_id.notify = xdg_toplevel_set_app_id_notify;
wl_signal_add(&wlr.events.set_app_id, &listeners.set_app_id);
listeners.set_parent.notify = xdg_toplevel_set_parent_notify;
wl_signal_add(&wlr.events.set_parent, &listeners.set_parent);
listeners.new_popup.notify = xdg_surface_new_popup_notify;
wl_signal_add(&wlr.base->events.new_popup, &listeners.new_popup);
listeners.new_subsurface.notify = xdg_surface_new_subsurface_notify;
wl_signal_add(&wlr.base->surface->events.new_subsurface, &listeners.new_subsurface);
}
XdgView::~XdgView() noexcept {
wl_list_remove(&listeners.map.link);
wl_list_remove(&listeners.unmap.link);
wl_list_remove(&listeners.commit.link);
wl_list_remove(&listeners.destroy.link);
wl_list_remove(&listeners.request_move.link);
wl_list_remove(&listeners.request_resize.link);
wl_list_remove(&listeners.request_maximize.link);
wl_list_remove(&listeners.request_minimize.link);
wl_list_remove(&listeners.set_title.link);
wl_list_remove(&listeners.set_app_id.link);
wl_list_remove(&listeners.set_parent.link);
wl_list_remove(&listeners.new_popup.link);
wl_list_remove(&listeners.new_subsurface.link);
}
wlr_surface* XdgView::get_wlr_surface() const {
return wlr.base->surface;
}
Server& XdgView::get_server() const {
return server;
}
wlr_box XdgView::get_geometry() const {
wlr_box box = {};
wlr_xdg_surface_get_geometry(wlr.base, &box);
return box;
}
wlr_box XdgView::get_min_size() const {
return {.x = 0, .y = 0, .width = wlr.current.min_width, .height = wlr.current.min_height};
}
wlr_box XdgView::get_max_size() const {
const int32_t max_width = wlr.current.max_width > 0 ? wlr.current.max_width : INT32_MAX;
const int32_t max_height = wlr.current.max_height > 0 ? wlr.current.max_height : INT32_MAX;
return {.x = 0, .y = 0, .width = max_width, .height = max_height};
}
void XdgView::map() {
if (pending_map) {
wlr_xdg_surface_get_geometry(wlr.base, &previous);
wlr_xdg_surface_get_geometry(wlr.base, ¤t);
if (!server.outputs.empty()) {
const auto* const output = static_cast<Output*>(wlr_output_layout_get_center_output(server.output_layout)->data);
const auto usable_area = output->usable_area;
const auto center_x = usable_area.x + (usable_area.width / 2);
const auto center_y = usable_area.y + (usable_area.height / 2);
set_position(center_x - (current.width / 2), center_y - (current.height / 2));
}
pending_map = false;
}
wlr_scene_node_set_enabled(scene_node, true);
if (wlr.current.fullscreen) {
set_placement(VIEW_PLACEMENT_FULLSCREEN);
} else if (wlr.current.maximized) {
set_placement(VIEW_PLACEMENT_MAXIMIZED);
}
update_outputs(true);
server.focus_view(std::dynamic_pointer_cast<View>(shared_from_this()));
}
void XdgView::unmap() {
wlr_scene_node_set_enabled(scene_node, false);
/* Reset the cursor mode if the grabbed view was unmapped. */
if (this == server.grabbed_view.lock().get()) {
server.seat->cursor.reset_mode();
}
if (this == server.focused_view.lock().get()) {
server.focused_view.reset();
}
}
void XdgView::close() {
wlr_xdg_toplevel_send_close(&wlr);
}
void XdgView::impl_set_position(const int32_t x, const int32_t y) {
(void) x;
(void) y;
}
void XdgView::impl_set_size(const int32_t width, const int32_t height) {
wlr_xdg_toplevel_set_size(&wlr, width, height);
}
void XdgView::impl_set_geometry(const int32_t x, const int32_t y, const int32_t width, const int32_t height) {
(void) x;
(void) y;
wlr_xdg_toplevel_set_size(&wlr, width, height);
}
void XdgView::impl_set_activated(const bool activated) {
wlr_xdg_toplevel_set_activated(&wlr, activated);
}
void XdgView::impl_set_fullscreen(const bool fullscreen) {
wlr_xdg_toplevel_set_fullscreen(&wlr, fullscreen);
}
void XdgView::impl_set_maximized(const bool maximized) {
wlr_xdg_toplevel_set_maximized(&wlr, maximized);
}
void XdgView::impl_set_minimized(const bool minimized) {
(void) minimized;
}
07070100000035000081A400000000000000000000000167624870000036CE000000000000000000000000000000000000002F00000000magpie-0.0.0+209/src/surface/xwayland_view.cpp#include "view.hpp"
#include "input/seat.hpp"
#include "output.hpp"
#include "server.hpp"
#include "surface.hpp"
#include "types.hpp"
#include "xwayland.hpp"
#include <algorithm>
#include <cstdlib>
#include <wayland-server-core.h>
#include "wlr-wrap-start.hpp"
#include <wlr/util/log.h>
#include "wlr-wrap-end.hpp"
/* Called when the surface is mapped, or ready to display on-screen. */
static void xwayland_surface_map_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xwayland_surface.events.map(listener=%p, data=%p)", (void*) listener, data);
XWaylandView& view = magpie_container_of(listener, view, map);
view.map();
}
/* Called when the surface is unmapped, and should no longer be shown. */
static void xwayland_surface_unmap_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xwayland_surface.events.unmap(listener=%p, data=%p)", (void*) listener, data);
XWaylandView& view = magpie_container_of(listener, view, unmap);
view.unmap();
}
static void xwayland_surface_associate_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xwayland_surface.events.associate(listener=%p, data=%p)", (void*) listener, data);
XWaylandView& view = magpie_container_of(listener, view, associate);
view.listeners.map.notify = xwayland_surface_map_notify;
wl_signal_add(&view.wlr.surface->events.map, &view.listeners.map);
view.listeners.unmap.notify = xwayland_surface_unmap_notify;
wl_signal_add(&view.wlr.surface->events.unmap, &view.listeners.unmap);
}
static void xwayland_surface_dissociate_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xwayland_surface.events.dissociate(listener=%p, data=%p)", (void*) listener, data);
XWaylandView& view = magpie_container_of(listener, view, dissociate);
wl_list_remove(&view.listeners.map.link);
wl_list_remove(&view.listeners.unmap.link);
}
/* Called when the surface is destroyed and should never be shown again. */
static void xwayland_surface_destroy_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xwayland_surface.events.destroy(listener=%p, data=%p)", (void*) listener, data);
XWaylandView& view = magpie_container_of(listener, view, destroy);
auto view_ptr = std::dynamic_pointer_cast<View>(view.shared_from_this());
view.server.xwayland->unmapped_views.remove(view_ptr);
view.server.views.remove(view_ptr);
}
static void xwayland_surface_request_configure_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_xwayland_surface.events.request_configure(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_xwayland_surface.events.request_configure");
return;
}
XWaylandView& view = magpie_container_of(listener, view, request_configure);
const auto& event = *static_cast<wlr_xwayland_surface_configure_event*>(data);
view.set_geometry(event.x, event.y, event.width, event.height);
}
static void xwayland_surface_set_geometry_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xwayland_surface.events.set_geometry(listener=%p, data=%p)", (void*) listener, data);
XWaylandView& view = magpie_container_of(listener, view, set_geometry);
auto grabbed_view = view.server.grabbed_view.lock();
if (grabbed_view == nullptr || grabbed_view->get_wlr_surface() != view.get_wlr_surface()) {
const wlr_xwayland_surface& surface = view.wlr;
if (view.curr_placement == VIEW_PLACEMENT_STACKING) {
view.previous = view.current;
}
view.current = {.x = surface.x, .y = surface.y, .width = surface.width, .height = surface.height};
}
}
/* This event is raised when a client would like to begin an interactive
* move, typically because the user clicked on their client-side
* decorations. Note that a more sophisticated compositor should check the
* provided serial against a list of button press serials sent to this
* client, to prevent the client from requesting this whenever they want. */
static void xwayland_surface_request_move_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xwayland_surface.events.request_move(listener=%p, data=%p)", (void*) listener, data);
XWaylandView& view = magpie_container_of(listener, view, request_move);
view.set_placement(VIEW_PLACEMENT_STACKING);
view.begin_interactive(MAGPIE_CURSOR_MOVE, 0);
}
/* This event is raised when a client would like to begin an interactive
* resize, typically because the user clicked on their client-side
* decorations. Note that a more sophisticated compositor should check the
* provided serial against a list of button press serials sent to this
* client, to prevent the client from requesting this whenever they want. */
static void xwayland_surface_request_resize_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_xwayland_surface.events.request_resize(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_xwayland_surface.events.request_resize");
return;
}
XWaylandView& view = magpie_container_of(listener, view, request_resize);
const auto* event = static_cast<wlr_xwayland_resize_event*>(data);
view.set_placement(VIEW_PLACEMENT_STACKING);
view.begin_interactive(MAGPIE_CURSOR_RESIZE, event->edges);
}
static void xwayland_surface_request_maximize_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xwayland_surface.events.request_maximize(listener=%p, data=%p)", (void*) listener, data);
XWaylandView& view = magpie_container_of(listener, view, request_maximize);
view.toggle_maximize();
}
static void xwayland_surface_request_fullscreen_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xwayland_surface.events.request_fullscreen(listener=%p, data=%p)", (void*) listener, data);
XWaylandView& view = magpie_container_of(listener, view, request_fullscreen);
view.toggle_fullscreen();
}
static void xwayland_surface_set_title_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xwayland_surface.events.set_title(listener=%p, data=%p)", (void*) listener, data);
XWaylandView& view = magpie_container_of(listener, view, set_title);
if (view.toplevel_handle.has_value()) {
view.toplevel_handle->set_title(view.wlr.title);
}
}
static void xwayland_surface_set_class_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xwayland_surface.events.set_class(listener=%p, data=%p)", (void*) listener, data);
XWaylandView& view = magpie_container_of(listener, view, set_class);
if (view.toplevel_handle.has_value()) {
view.toplevel_handle->set_app_id(view.wlr._class);
}
}
static void xwayland_surface_set_parent_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xwayland_surface.events.set_parent(listener=%p, data=%p)", (void*) listener, data);
XWaylandView& view = magpie_container_of(listener, view, set_parent);
if (view.wlr.parent != nullptr) {
auto* m_view = dynamic_cast<View*>(static_cast<Surface*>(view.wlr.parent->data));
if (m_view != nullptr && view.scene_node != nullptr) {
wlr_scene_node_reparent(view.scene_node, m_view->scene_node->parent);
if (view.toplevel_handle.has_value() && m_view->toplevel_handle.has_value()) {
view.toplevel_handle->set_parent(m_view->toplevel_handle.value());
}
return;
}
}
if (view.toplevel_handle.has_value()) {
view.toplevel_handle->set_parent({});
}
}
XWaylandView::XWaylandView(Server& server, wlr_xwayland_surface& surface) noexcept
: listeners(*this), server(server), wlr(surface) {
listeners.associate.notify = xwayland_surface_associate_notify;
wl_signal_add(&surface.events.associate, &listeners.associate);
listeners.dissociate.notify = xwayland_surface_dissociate_notify;
wl_signal_add(&surface.events.dissociate, &listeners.dissociate);
listeners.destroy.notify = xwayland_surface_destroy_notify;
wl_signal_add(&surface.events.destroy, &listeners.destroy);
listeners.request_configure.notify = xwayland_surface_request_configure_notify;
wl_signal_add(&surface.events.request_configure, &listeners.request_configure);
listeners.request_move.notify = xwayland_surface_request_move_notify;
wl_signal_add(&surface.events.request_move, &listeners.request_move);
listeners.request_resize.notify = xwayland_surface_request_resize_notify;
wl_signal_add(&surface.events.request_resize, &listeners.request_resize);
listeners.request_maximize.notify = xwayland_surface_request_maximize_notify;
wl_signal_add(&surface.events.request_maximize, &listeners.request_maximize);
listeners.request_fullscreen.notify = xwayland_surface_request_fullscreen_notify;
wl_signal_add(&surface.events.request_fullscreen, &listeners.request_fullscreen);
listeners.set_geometry.notify = xwayland_surface_set_geometry_notify;
wl_signal_add(&surface.events.set_geometry, &listeners.set_geometry);
listeners.set_title.notify = xwayland_surface_set_title_notify;
wl_signal_add(&surface.events.set_title, &listeners.set_title);
listeners.set_class.notify = xwayland_surface_set_class_notify;
wl_signal_add(&surface.events.set_class, &listeners.set_class);
listeners.set_parent.notify = xwayland_surface_set_parent_notify;
wl_signal_add(&surface.events.set_parent, &listeners.set_parent);
}
XWaylandView::~XWaylandView() noexcept {
wl_list_remove(&listeners.associate.link);
wl_list_remove(&listeners.destroy.link);
wl_list_remove(&listeners.request_configure.link);
wl_list_remove(&listeners.request_move.link);
wl_list_remove(&listeners.request_resize.link);
wl_list_remove(&listeners.set_geometry.link);
wl_list_remove(&listeners.set_title.link);
wl_list_remove(&listeners.set_class.link);
wl_list_remove(&listeners.set_parent.link);
}
wlr_surface* XWaylandView::get_wlr_surface() const {
return wlr.surface;
}
Server& XWaylandView::get_server() const {
return server;
}
wlr_box XWaylandView::get_geometry() const {
return {.x = wlr.x, .y = wlr.y, .width = wlr.width, .height = wlr.height};
}
wlr_box XWaylandView::get_min_size() const {
wlr_box min = {.x = 0, .y = 0, .width = 0, .height = 0};
if (wlr.size_hints != nullptr) {
const auto& hints = *wlr.size_hints;
min.width = std::max(hints.min_width, hints.base_width);
min.height = std::max(hints.min_height, hints.base_height);
}
return min;
}
wlr_box XWaylandView::get_max_size() const {
wlr_box max = {.x = 0, .y = 0, .width = UINT16_MAX, .height = UINT16_MAX};
if (wlr.size_hints != nullptr) {
const auto& hints = *wlr.size_hints;
max.width = hints.max_width > 0 ? hints.max_width : UINT16_MAX;
max.height = hints.max_height > 0 ? hints.max_height : UINT16_MAX;
}
return max;
}
void XWaylandView::map() {
wlr.data = this;
wlr.surface->data = this;
toplevel_handle.emplace(*this);
toplevel_handle->set_title(wlr.title);
toplevel_handle->set_app_id(wlr._class);
wlr_scene_tree* scene_tree = wlr_scene_subsurface_tree_create(&server.scene->tree, wlr.surface);
scene_node = &scene_tree->node;
scene_node->data = this;
if (wlr.parent != nullptr) {
const auto* m_view = dynamic_cast<View*>(static_cast<Surface*>(wlr.parent->data));
if (m_view != nullptr) {
wlr_scene_node_reparent(scene_node, m_view->scene_node->parent);
toplevel_handle->set_parent(m_view->toplevel_handle);
}
}
wlr_scene_node_set_enabled(scene_node, true);
wlr_scene_node_set_position(scene_node, current.x, current.y);
if (wlr.fullscreen) {
set_placement(VIEW_PLACEMENT_FULLSCREEN);
} else if (wlr.maximized_horz && wlr.maximized_vert) {
set_placement(VIEW_PLACEMENT_MAXIMIZED);
}
auto view_ptr = std::dynamic_pointer_cast<View>(shared_from_this());
server.xwayland->unmapped_views.remove(view_ptr);
server.views.emplace_back(view_ptr);
update_outputs(true);
server.focus_view(std::dynamic_pointer_cast<View>(shared_from_this()));
}
void XWaylandView::unmap() {
wlr_scene_node_set_enabled(scene_node, false);
wlr_scene_node_destroy(scene_node);
scene_node = nullptr;
Cursor& cursor = server.seat->cursor;
/* Reset the cursor mode if the grabbed view was unmapped. */
if (this == server.grabbed_view.lock().get()) {
cursor.reset_mode();
}
if (this == server.focused_view.lock().get()) {
server.focused_view.reset();
}
if (server.seat->wlr->keyboard_state.focused_surface == wlr.surface) {
server.seat->wlr->keyboard_state.focused_surface = nullptr;
}
auto view_ptr = std::dynamic_pointer_cast<View>(shared_from_this());
server.views.remove(view_ptr);
server.xwayland->unmapped_views.emplace_back(view_ptr);
toplevel_handle.reset();
}
void XWaylandView::close() {
wlr_xwayland_surface_close(&wlr);
}
static int16_t trunc(const int32_t int32) {
return static_cast<int16_t>(std::clamp(int32, INT16_MIN, INT16_MAX));
}
void XWaylandView::impl_set_position(const int32_t x, const int32_t y) {
wlr_xwayland_surface_configure(&wlr, trunc(x), trunc(y), current.width, current.height);
}
void XWaylandView::impl_set_size(const int32_t width, const int32_t height) {
wlr_xwayland_surface_configure(&wlr, trunc(current.x), trunc(current.y), width, height);
}
void XWaylandView::impl_set_geometry(const int32_t x, const int32_t y, const int32_t width, const int32_t height) {
wlr_xwayland_surface_configure(&wlr, trunc(x), trunc(y), trunc(width), trunc(height));
}
void XWaylandView::impl_set_activated(const bool activated) {
wlr_xwayland_surface_activate(&wlr, activated);
if (activated) {
wlr_xwayland_surface_restack(&wlr, nullptr, XCB_STACK_MODE_ABOVE);
}
}
void XWaylandView::impl_set_fullscreen(const bool fullscreen) {
wlr_xwayland_surface_set_fullscreen(&wlr, fullscreen);
}
void XWaylandView::impl_set_maximized(const bool maximized) {
wlr_xwayland_surface_set_maximized(&wlr, maximized);
}
void XWaylandView::impl_set_minimized(const bool minimized) {
wlr_xwayland_surface_set_minimized(&wlr, minimized);
}
07070100000036000081A4000000000000000000000001676248700000039B000000000000000000000000000000000000001F00000000magpie-0.0.0+209/src/types.hpp#ifndef MAGPIE_TYPES_HPP
#define MAGPIE_TYPES_HPP
class Server;
class XWayland;
class Output;
class Seat;
class Keyboard;
class Cursor;
class PointerConstraint;
struct Surface;
struct View;
class XdgView;
class XWaylandView;
class Layer;
class Subsurface;
class Popup;
class ForeignToplevelHandle;
enum ViewPlacement {
VIEW_PLACEMENT_STACKING,
VIEW_PLACEMENT_MAXIMIZED,
VIEW_PLACEMENT_FULLSCREEN,
};
#define magpie_container_of(ptr, sample, member) \
(__extension__({ \
std::remove_reference<decltype(sample)>::type::Listeners* container = wl_container_of(ptr, container, member); \
container->parent; \
}))
#endif
07070100000037000081A400000000000000000000000167624870000000E9000000000000000000000000000000000000002600000000magpie-0.0.0+209/src/wlr-wrap-end.hpp#ifndef WLROOTS_INCLUDE_WRAP_STARTED
static_assert(0 == 1, "wlroots include wrap ended and not started");
#endif
#undef WLROOTS_INCLUDE_WRAP_STARTED
}
#undef class
#undef namespace
#undef static
#undef WLROOTS_INCLUDE_WRAP_STARTED
07070100000038000081A400000000000000000000000167624870000001EA000000000000000000000000000000000000002800000000magpie-0.0.0+209/src/wlr-wrap-start.hpp#ifdef WLROOTS_INCLUDE_WRAP_STARTED
static_assert(0 == 1, "wlroots include wrap started and not ended");
#endif
#define WLROOTS_INCLUDE_WRAP_STARTED
#include <pthread.h>
#include <cstdlib>
#include <wayland-server-core.h>
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wkeyword-macro"
#endif
#define class _class
#define namespace _namespace
#define static
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#define WLR_USE_UNSTABLE
extern "C" {
07070100000039000081A40000000000000000000000016762487000000ABF000000000000000000000000000000000000002200000000magpie-0.0.0+209/src/xwayland.cpp#include "xwayland.hpp"
#include "input/seat.hpp"
#include "server.hpp"
#include "types.hpp"
#include "surface/view.hpp"
#include "wlr-wrap-start.hpp"
#include <wlr/util/log.h>
#include "wlr-wrap-end.hpp"
static void ready_notify(wl_listener* listener, [[maybe_unused]] void* data) {
wlr_log(WLR_DEBUG, "wlr_xwayland.events.ready(listener=%p, data=%p)", (void*) listener, data);
const std::array<std::string, ATOM_LAST> atom_map = {
"_NET_WM_WINDOW_TYPE_NORMAL",
"_NET_WM_WINDOW_TYPE_DIALOG",
"_NET_WM_WINDOW_TYPE_UTILITY",
"_NET_WM_WINDOW_TYPE_TOOLBAR",
"_NET_WM_WINDOW_TYPE_SPLASH",
"_NET_WM_WINDOW_TYPE_MENU",
"_NET_WM_WINDOW_TYPE_DROPDOWN_MENU",
"_NET_WM_WINDOW_TYPE_POPUP_MENU",
"_NET_WM_WINDOW_TYPE_TOOLTIP",
"_NET_WM_WINDOW_TYPE_NOTIFICATION",
"_NET_WM_STATE_MODAL",
};
XWayland& xwayland = magpie_container_of(listener, xwayland, ready);
wlr_xwayland_set_seat(xwayland.wlr, xwayland.server.seat->wlr);
xcb_connection_t* xcb_conn = xcb_connect(nullptr, nullptr);
if (const int32_t err = xcb_connection_has_error(xcb_conn)) {
wlr_log(WLR_ERROR, "XCB connect failed: %d", err);
return;
}
std::array<xcb_intern_atom_cookie_t, ATOM_LAST> cookies = {};
for (size_t i = 0; i < ATOM_LAST; i++) {
cookies[i] = xcb_intern_atom(xcb_conn, 0, atom_map[i].length(), atom_map[i].c_str());
}
for (size_t i = 0; i < ATOM_LAST; i++) {
xcb_generic_error_t* error = nullptr;
xcb_intern_atom_reply_t* reply = xcb_intern_atom_reply(xcb_conn, cookies[i], &error);
if (reply != nullptr && error == nullptr) {
xwayland.atoms[i] = reply->atom;
}
free(reply);
if (error != nullptr) {
wlr_log(WLR_ERROR, "could not resolve atom %s: X11 error code %d", atom_map[i].c_str(), error->error_code);
free(error);
break;
}
}
xcb_disconnect(xcb_conn);
}
static void new_surface_notify(wl_listener* listener, void* data) {
wlr_log(WLR_DEBUG, "wlr_xwayland.events.new_surface(listener=%p, data=%p)", (void*) listener, data);
if (data == nullptr) {
wlr_log(WLR_ERROR, "No data passed to wlr_xwayland.events.new_surface");
return;
}
XWayland& xwayland = magpie_container_of(listener, xwayland, new_surface);
auto& xwayland_surface = *static_cast<wlr_xwayland_surface*>(data);
xwayland.unmapped_views.emplace_back(std::make_shared<XWaylandView>(xwayland.server, xwayland_surface));
}
XWayland::XWayland(Server& server) noexcept : listeners(*this), server(server) {
wlr = wlr_xwayland_create(server.display, server.compositor, true);
listeners.ready.notify = ready_notify;
wl_signal_add(&wlr->events.ready, &listeners.ready);
listeners.new_surface.notify = new_surface_notify;
wl_signal_add(&wlr->events.new_surface, &listeners.new_surface);
setenv("DISPLAY", wlr->display_name, 1);
}
0707010000003A000081A4000000000000000000000001676248700000045C000000000000000000000000000000000000002200000000magpie-0.0.0+209/src/xwayland.hpp#ifndef MAGPIE_XWAYLAND_HPP
#define MAGPIE_XWAYLAND_HPP
#include "types.hpp"
#include <functional>
#include <list>
#include <memory>
#include <xcb/xproto.h>
#include "wlr-wrap-start.hpp"
#include <wlr/xwayland/xwayland.h>
#include "wlr-wrap-end.hpp"
enum atom_name {
NET_WM_WINDOW_TYPE_NORMAL,
NET_WM_WINDOW_TYPE_DIALOG,
NET_WM_WINDOW_TYPE_UTILITY,
NET_WM_WINDOW_TYPE_TOOLBAR,
NET_WM_WINDOW_TYPE_SPLASH,
NET_WM_WINDOW_TYPE_MENU,
NET_WM_WINDOW_TYPE_DROPDOWN_MENU,
NET_WM_WINDOW_TYPE_POPUP_MENU,
NET_WM_WINDOW_TYPE_TOOLTIP,
NET_WM_WINDOW_TYPE_NOTIFICATION,
NET_WM_STATE_MODAL,
ATOM_LAST,
};
class XWayland final : public std::enable_shared_from_this<XWayland> {
public:
struct Listeners {
std::reference_wrapper<XWayland> parent;
wl_listener ready = {};
wl_listener new_surface = {};
explicit Listeners(XWayland& parent) noexcept : parent(parent) {}
};
private:
Listeners listeners;
public:
Server& server;
wlr_xwayland* wlr;
std::list<std::shared_ptr<View>> unmapped_views;
std::array<xcb_atom_t, ATOM_LAST> atoms = {};
explicit XWayland(Server& server) noexcept;
};
#endif
0707010000003B000041ED0000000000000000000000026762487000000000000000000000000000000000000000000000001D00000000magpie-0.0.0+209/subprojects0707010000003C000081A40000000000000000000000016762487000000251000000000000000000000000000000000000002B00000000magpie-0.0.0+209/subprojects/argparse.wrap[wrap-file]
directory = argparse-3.0
source_url = https://github.com/p-ranav/argparse/archive/refs/tags/v3.0.tar.gz
source_filename = argparse-3.0.tar.gz
source_hash = ba7b465759bb01069d57302855eaf4d1f7d677f21ad7b0b00b92939645c30f47
patch_filename = argparse_3.0-1_patch.zip
patch_url = https://wrapdb.mesonbuild.com/v2/argparse_3.0-1/get_patch
patch_hash = f83ed766f07c830d3922676c67959f2078a055c07bd360f19e0e114d375d1037
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/argparse_3.0-1/argparse-3.0.tar.gz
wrapdb_version = 3.0-1
[provide]
argparse = argparse_dep
0707010000003D000081A4000000000000000000000001676248700000008B000000000000000000000000000000000000002A00000000magpie-0.0.0+209/subprojects/wlroots.wrap[wrap-git]
url = https://gitlab.freedesktop.org/wlroots/wlroots.git
revision = 0.18.2
depth = 1
[provide]
dependency_names = wlroots-0.18
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!421 blocks