pax_global_header 0000666 0000000 0000000 00000000064 14641446446 0014527 g ustar 00root root 0000000 0000000 52 comment=bebd8742fcf68bdd8804f50aa33aedad2169c207
wlmaker-0.3/ 0000775 0000000 0000000 00000000000 14641446446 0013033 5 ustar 00root root 0000000 0000000 wlmaker-0.3/.github/ 0000775 0000000 0000000 00000000000 14641446446 0014373 5 ustar 00root root 0000000 0000000 wlmaker-0.3/.github/workflows/ 0000775 0000000 0000000 00000000000 14641446446 0016430 5 ustar 00root root 0000000 0000000 wlmaker-0.3/.github/workflows/build-for-linux.yml 0000664 0000000 0000000 00000005541 14641446446 0022200 0 ustar 00root root 0000000 0000000 name: Build for Linux
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
INSTALL_PATH: "${HOME}/wlmaker"
INSTALL_LIBRARY_PATH: "${HOME}/wlmaker/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)"
INSTALL_PKGCONFIG_PATH: "${HOME}/wlmaker/lib/$(dpkg-architecture -qDEB_HOST_MULTIARCH)/pkgconfig/:${HOME}/wlmaker/share/pkgconfig/"
jobs:
build_matrix:
strategy:
matrix:
compiler: [ "gcc", "clang" ]
runs-on: ubuntu-latest
container:
image: debian:bookworm
steps:
- name: Install package dependencies.
run: |
apt-get update
apt-get install -y \
bison \
clang \
cmake \
doxygen \
flex \
foot \
git \
glslang-dev \
glslang-tools \
graphviz \
libcairo2-dev \
libgbm-dev \
libinput-dev \
libncurses-dev \
libseat-dev \
libudev-dev \
libvulkan-dev \
libwayland-dev \
libxcb-composite0-dev \
libxcb-dri3-dev \
libxcb-ewmh-dev \
libxcb-icccm4-dev \
libxcb-present-dev \
libxcb-render-util0-dev \
libxcb-res0-dev \
libxcb-xinput-dev \
libxkbcommon-dev \
libxml2-dev \
meson \
plantuml \
seatd \
wayland-protocols \
xmlto \
xsltproc \
xwayland
- name: Checkout code including submodule dependencies.
uses: actions/checkout@v3
with:
submodules: recursive
- name: Configure and build submodule dependencies.
run: |
export CC="${{ matrix.compiler }}"
export PKG_CONFIG_PATH="${{ env.INSTALL_PKGCONFIG_PATH }}"
export LD_LIBRARY_PATH="${{ env.INSTALL_LIBRARY_PATH }}"
export PATH="${PATH}:${{ env.INSTALL_PATH }}/bin"
cmake -DCMAKE_INSTALL_PREFIX:PATH=${{ env.INSTALL_PATH }} dependencies/ -B dependencies/build/
cmake --build dependencies/build
- name: Configure wlmaker through CMake.
run: |
export CC="${{ matrix.compiler }}"
export PKG_CONFIG_PATH="${{ env.INSTALL_PKGCONFIG_PATH }}"
export LD_LIBRARY_PATH="${{ env.INSTALL_LIBRARY_PATH }}"
export PATH="${PATH}:${{ env.INSTALL_PATH }}/bin"
cmake -DCMAKE_INSTALL_PREFIX:PATH=${{ env.INSTALL_PATH }} -B build/
- name: Build wlmaker.
run: |
export CC="${{ matrix.compiler }}"
export LD_LIBRARY_PATH="${{ env.INSTALL_LIBRARY_PATH }}"
export PATH="${PATH}:${{ env.INSTALL_PATH }}/bin"
cmake --build build/
- name: Build documentation.
run: cmake --build build/ --target doc
- name: Run all tests
run: |
export LD_LIBRARY_PATH="${{ env.INSTALL_LIBRARY_PATH }}"
ctest --test-dir build/ --build-run-dir build/ -V
wlmaker-0.3/.gitignore 0000664 0000000 0000000 00000000174 14641446446 0015025 0 ustar 00root root 0000000 0000000 **/*~
**/CMakeFiles/*
**/CMakeCache.txt
**/build/*
**/build-clang/*
TAGS
cscope.files
cscope.in.out
cscope.out
cscope.po.out wlmaker-0.3/.gitmodules 0000664 0000000 0000000 00000003100 14641446446 0015202 0 ustar 00root root 0000000 0000000 [submodule "submodules/libbase"]
path = submodules/libbase
url = ../libbase
branch = main
update = rebase
[submodule "dependencies/drm"]
path = dependencies/drm
url = https://gitlab.freedesktop.org/mesa/drm.git
branch = main
update = rebase
[submodule "dependencies/pixman"]
path = dependencies/pixman
url = https://gitlab.freedesktop.org/pixman/pixman.git
branch = master
update = rebase
[submodule "dependencies/wayland"]
path = dependencies/wayland
url = https://gitlab.freedesktop.org/wayland/wayland.git
branch = main
update = rebase
[submodule "dependencies/wayland-protocols"]
path = dependencies/wayland-protocols
url = https://gitlab.freedesktop.org/wayland/wayland-protocols.git
branch = main
update = rebase
[submodule "dependencies/hwdata"]
path = dependencies/hwdata
url = https://github.com/vcrhonek/hwdata.git
branch = master
ignore = dirty
update = rebase
[submodule "dependencies/libdisplay-info"]
path = dependencies/libdisplay-info
url = https://gitlab.freedesktop.org/emersion/libdisplay-info.git
branch = main
update = rebase
[submodule "dependencies/wlroots"]
path = dependencies/wlroots
url = https://gitlab.freedesktop.org/wlroots/wlroots.git
branch = master
update = rebase
[submodule "examples/gtk-layer-shell"]
path = examples/gtk-layer-shell
url = https://github.com/wmww/gtk-layer-shell.git
wlmaker-0.3/CMakeLists.txt 0000664 0000000 0000000 00000006476 14641446446 0015610 0 ustar 00root root 0000000 0000000 # Copyright 2023 Google LLC
#
# 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
#
# https://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.
#
# Default arguments:
# cmake -DCMAKE_INSTALL_PREFIX:PATH=${HOME}/.local -Dconfig_DOXYGEN_CRITICAL=ON -B build
# CC=clang cmake -DCMAKE_INSTALL_PREFIX:PATH=${HOME}/.local -Dconfig_DOXYGEN_CRITICAL=ON -B build-clang
CMAKE_MINIMUM_REQUIRED(VERSION 3.13)
PROJECT(wlmaker VERSION 0.1
DESCRIPTION "Wayland Maker - A Wayland compositor inspired by Window Maker"
LANGUAGES C)
# Requires out-of-source builds.
FILE(TO_CMAKE_PATH "${PROJECT_BINARY_DIR}/CMakeLists.txt" LOCATION_PATH)
IF(EXISTS "${LOCATION_PATH}")
MESSAGE(
FATAL_ERROR
"You cannot build into a source directory (containing a CMakeLists.txt file).\n"
"Please make a build subdirectory, for example:\n"
"cmake -B build\n"
"(cd build && make)")
ENDIF()
# Defaults to /usr/local/lib for installation.
INCLUDE(GNUInstallDirs)
INCLUDE(CTest)
FIND_PACKAGE(PkgConfig REQUIRED)
# Further dependency versions, as submodules:
# * drm at libdrm-2.4.117
# * hwdata at v0.371
# * libdisplay-info at 0.2.0
# * pixman at pixman-0.43.0
PKG_CHECK_MODULES(CAIRO REQUIRED IMPORTED_TARGET cairo>=1.16.0)
PKG_CHECK_MODULES(
WAYLAND REQUIRED IMPORTED_TARGET
wayland-client>=1.22.91
wayland-protocols>=1.32
wayland-server>=1.22.91)
PKG_GET_VARIABLE(WAYLAND_PROTOCOL_DIR wayland-protocols pkgdatadir)
PKG_CHECK_MODULES(WLROOTS REQUIRED IMPORTED_TARGET wlroots>=0.17.4)
PKG_CHECK_MODULES(XCB REQUIRED IMPORTED_TARGET xcb>=1.15)
PKG_CHECK_MODULES(XKBCOMMON REQUIRED IMPORTED_TARGET xkbcommon>=1.5.0)
# XWayland considered optional.
PKG_CHECK_MODULES(XWAYLAND xwayland>=22.1.9)
# Configuration. Remove CMakeCache.txt to rerun...
OPTION(config_DEBUG "Include debugging information" ON)
OPTION(config_OPTIM "Optimizations" OFF)
OPTION(config_DOXYGEN_CRITICAL "Whether to fail on doxygen warnings" OFF)
# Toplevel compile options, for GCC and clang.
IF(CMAKE_C_COMPILER_ID MATCHES "Clang|GNU")
ADD_COMPILE_OPTIONS(-Wall -Wextra -Werror)
IF(config_DEBUG)
ADD_COMPILE_OPTIONS(-ggdb -DDEBUG)
ENDIF(config_DEBUG)
IF(config_OPTIM)
ADD_COMPILE_OPTIONS(-O2)
ELSE (config_OPTIM)
ADD_COMPILE_OPTIONS(-O0)
ENDIF(config_OPTIM)
# CMake provides absolute paths to GCC, hence the __FILE__ macro includes the
# full path. This option resets it to a path relative to project source.
ADD_COMPILE_OPTIONS(-fmacro-prefix-map=${PROJECT_SOURCE_DIR}=.)
ENDIF(CMAKE_C_COMPILER_ID MATCHES "Clang|GNU")
SET(CMAKE_C_STANDARD 11)
LIST(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
ADD_SUBDIRECTORY(apps)
ADD_SUBDIRECTORY(doc)
ADD_SUBDIRECTORY(icons)
ADD_SUBDIRECTORY(protocols)
ADD_SUBDIRECTORY(third_party/protocols)
ADD_SUBDIRECTORY(share)
ADD_SUBDIRECTORY(src)
ADD_SUBDIRECTORY(src/conf)
ADD_SUBDIRECTORY(src/toolkit)
# Adds submodules last, to permit checking on already-existing targets.
ADD_SUBDIRECTORY(submodules/libbase)
wlmaker-0.3/CODE_OF_CONDUCT.md 0000664 0000000 0000000 00000010623 14641446446 0015634 0 ustar 00root root 0000000 0000000 # Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of
experience, education, socio-economic status, nationality, personal appearance,
race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, or to ban temporarily or permanently any
contributor for other behaviors that they deem inappropriate, threatening,
offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
This Code of Conduct also applies outside the project spaces when the Project
Steward has a reasonable belief that an individual's behavior may have a
negative impact on the project or its community.
## Conflict Resolution
We do not believe that all conflict is bad; healthy debate and disagreement
often yield positive results. However, it is never okay to be disrespectful or
to engage in behavior that violates the project’s code of conduct.
If you see someone violating the code of conduct, you are encouraged to address
the behavior directly with those involved. Many issues can be resolved quickly
and easily, and this gives people more control over the outcome of their
dispute. If you are unable to resolve the matter for any reason, or if the
behavior is threatening or harassing, report it. We are dedicated to providing
an environment where participants feel welcome and safe.
Reports should be directed to *[PROJECT STEWARD NAME(s) AND EMAIL(s)]*, the
Project Steward(s) for *[PROJECT NAME]*. It is the Project Steward’s duty to
receive and address reported violations of the code of conduct. They will then
work with a committee consisting of representatives from the Open Source
Programs Office and the Google Open Source Strategy team. If for any reason you
are uncomfortable reaching out to the Project Steward, please email
opensource@google.com.
We will investigate every complaint, but you may not receive a direct response.
We will use our discretion in determining when and how to follow up on reported
incidents, which may range from not taking action to permanent expulsion from
the project and project-sponsored spaces. We will notify the accused of the
report and provide them an opportunity to discuss it before any action is taken.
The identity of the reporter will be omitted from the details of the report
supplied to the accused. In potentially harmful situations, such as ongoing
harassment or threats to anyone's safety, we may take action without notice.
## Attribution
This Code of Conduct is adapted from the Contributor Covenant, version 1.4,
available at
https://www.contributor-covenant.org/version/1/4/code-of-conduct/
wlmaker-0.3/CONTRIBUTING.md 0000664 0000000 0000000 00000002054 14641446446 0015265 0 ustar 00root root 0000000 0000000 # How to Contribute
We would love to accept your patches and contributions to this project.
## Before you begin
### Sign our Contributor License Agreement
Contributions to this project must be accompanied by a
[Contributor License Agreement](https://cla.developers.google.com/about) (CLA).
You (or your employer) retain the copyright to your contribution; this simply
gives us permission to use and redistribute your contributions as part of the
project.
If you or your current employer have already signed the Google CLA (even if it
was for a different project), you probably don't need to do it again.
Visit to see your current agreements or to
sign a new one.
### Review our Community Guidelines
This project follows [Google's Open Source Community
Guidelines](https://opensource.google/conduct/).
## Contribution process
### Code Reviews
All submissions, including submissions by project members, require review. We
use [GitHub pull requests](https://docs.github.com/articles/about-pull-requests)
for this purpose.
wlmaker-0.3/LICENSE 0000664 0000000 0000000 00000026135 14641446446 0014047 0 ustar 00root root 0000000 0000000
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. wlmaker-0.3/README.md 0000664 0000000 0000000 00000004537 14641446446 0014323 0 ustar 00root root 0000000 0000000 # wlmaker - Wayland Maker
A [Wayland](https://wayland.freedesktop.org/) compositor inspired by
[Window Maker](https://www.windowmaker.org/).
Key features:
* Compositor for windows in stacking mode.
* Supports multiple workspaces.
* Appearance inspired by Window Maker, following the look and feel of NeXTSTEP.
* Easy to use, lightweight, low gimmicks and fast.
* Dock and clip, to be extended for dockable apps.
### Current status
Wayland Maker is in early development stage. Highlights for current version (0.3):
* *new:* Screen saver support, through `ext_session_lock_v1` and `idle_inhibit_unstable_v1` protocols.
* *new:* Configurable through plist text files: [base configuration](etc/wlmaker.plist), [style](/etc/style-default.plist) and [docks & workspaces](etc/wlmaker-state.plist).
* *new:* wlr layer shell support (`wlr_layer_shell_unstable_v1`), fully implemented & tested.
* Appearance matches Window Maker: Decorations, dock, clip.
* Support for Wayland XDG shell (mostly complete. Bug reports welcome).
* Initial support for X11 applications (positioning and specific modes are missing).
Use `--start_xwayland` argument to enable XWayland, it's off by default.
* A prototype DockApp (`apps/wlmclock`).
For further details, see the [roadmap](doc/ROADMAP.md).
Protocol support:
* `xdg-decoration-unstable-v1`: Implemented & tested.
* `ext_session_lock_v1`: Implemented & tested.
* `wlr_layer_shell_unstable_v1`: Implemented & tested.
* `xdg_shell`: Largely implemented & tested.
* `idle_inhibit_unstable_v1`: Implemented, untested.
### Build & use it!
* From source: Please follow the [detailled build instructions](doc/BUILD.md)
for a step-by-step guide.
* Once compiled, see the [these instructions](doc/RUN.md) on how to run
Wayland Maker in a window or standalone, and to configure it for your needs.
> [!NOTE]
> `wlmaker` is still in early development, so it's not recommended to use it as
> your primary compositor.
## Contributing
Contributions and help are highly welcome! See
[`CONTRIBUTING.md`](CONTRIBUTING.md) for details, and
[code of conduct](CODE_OF_CONDUCT.md) for more.
## License
Apache 2.0; see [`LICENSE`](LICENSE) for details.
## Disclaimer
This project is not an official Google project. It is not supported by
Google and Google specifically disclaims all warranties as to its quality,
merchantability, or fitness for a particular purpose.
wlmaker-0.3/apps/ 0000775 0000000 0000000 00000000000 14641446446 0013776 5 ustar 00root root 0000000 0000000 wlmaker-0.3/apps/CMakeLists.txt 0000664 0000000 0000000 00000001556 14641446446 0016545 0 ustar 00root root 0000000 0000000 # Copyright 2023 Google LLC
#
# 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
#
# https://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.
CMAKE_MINIMUM_REQUIRED(VERSION 3.13)
ADD_SUBDIRECTORY(libwlclient)
ADD_SUBDIRECTORY(primitives)
ADD_EXECUTABLE(wlmclock wlmclock.c)
TARGET_INCLUDE_DIRECTORIES(wlmclock PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
TARGET_LINK_LIBRARIES(wlmclock libwlclient primitives m)
INSTALL(TARGETS wlmclock DESTINATION bin)
wlmaker-0.3/apps/libwlclient/ 0000775 0000000 0000000 00000000000 14641446446 0016306 5 ustar 00root root 0000000 0000000 wlmaker-0.3/apps/libwlclient/CMakeLists.txt 0000664 0000000 0000000 00000002725 14641446446 0021054 0 ustar 00root root 0000000 0000000 # Copyright 2023 Google LLC
#
# 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
#
# https://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.
CMAKE_MINIMUM_REQUIRED(VERSION 3.13)
INCLUDE(WaylandProtocol)
ADD_LIBRARY(libwlclient STATIC)
SET(SOURCES libwlclient.h client.c buffer.h buffer.c icon.h icon.c)
WaylandProtocol_ADD(
SOURCES
BASE_NAME xdg-shell
PROTOCOL_FILE "${WAYLAND_PROTOCOL_DIR}/stable/xdg-shell/xdg-shell.xml"
SIDE client)
WaylandProtocol_ADD(
SOURCES
BASE_NAME wlmaker-icon-unstable-v1
PROTOCOL_FILE "${PROJECT_SOURCE_DIR}/protocols/wlmaker-icon-unstable-v1.xml"
SIDE client)
TARGET_SOURCES(libwlclient PRIVATE ${SOURCES})
TARGET_INCLUDE_DIRECTORIES(
libwlclient PRIVATE
${WAYLAND_INCLUDE_DIRS}
${CMAKE_CURRENT_BINARY_DIR})
TARGET_LINK_LIBRARIES(
libwlclient
base
PkgConfig::WAYLAND)
INCLUDE(CheckSymbolExists)
CHECK_SYMBOL_EXISTS(signalfd "sys/signalfd.h" HAVE_SIGNALFD)
IF(NOT HAVE_SIGNALFD)
PKG_CHECK_MODULES(EPOLL REQUIRED IMPORTED_TARGET epoll-shim)
TARGET_LINK_LIBRARIES(libwlclient PkgConfig::EPOLL)
ENDIF()
wlmaker-0.3/apps/libwlclient/buffer.c 0000664 0000000 0000000 00000024343 14641446446 0017731 0 ustar 00root root 0000000 0000000 /* ========================================================================= */
/**
* @file buffer.c
*
* @copyright
* Copyright 2023 Google LLC
*
* 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
*
* https://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.
*/
#include "buffer.h"
#include
#include
#include
#include
#include
#include
#include
/* == Declarations ========================================================= */
/** Actual buffer. TODO(kaeser@gubbe.ch): Clean this up. */
typedef struct {
/** Points to the data area, ie. the pixels. */
uint32_t *data_ptr;
/** Corresponding wayland buffer. */
struct wl_buffer *wl_buffer_ptr;
/** Corresponding (unmanaged) `bs_gfxbuf_t`. */
bs_gfxbuf_t *bs_gfxbuf_ptr;
/** Indicates that the buffer is committed, and not ready to draw into. */
bool committed;
/** Back-link to the client buffer state. */
wlclient_buffer_t *client_buffer_ptr;
} buffer_t;
/** All elements contributing to a wl_buffer. */
struct _wlclient_buffer_t {
/** Mapped data. */
void *data_ptr;
/** Shared memory pool. */
struct wl_shm_pool *wl_shm_pool_ptr;
/** Width of the buffer, in pixels. */
unsigned width;
/** Height of the buffer, in pixels. */
unsigned height;
/** Actual buffer. */
buffer_t *buffer_ptr;
/** Callback to indicate the buffer is ready to draw into. */
wlclient_buffer_ready_callback_t ready_callback;
/** Argument to said callback. */
void *ready_callback_ud_ptr;
};
static void handle_wl_buffer_release(
void *data_ptr,
struct wl_buffer *wl_buffer_ptr);
static int shm_creat(const char *app_id_ptr, size_t size);
static buffer_t *create_buffer(
struct wl_shm_pool *wl_shm_pool_ptr,
void *data_base_ptr,
size_t ofs,
unsigned width,
unsigned height,
unsigned bytes_per_line);
static void buffer_destroy(buffer_t *buffer_ptr);
/* == Data ================================================================= */
/** How many attempts to try shm_open before giving up. */
static const uint32_t SHM_OPEN_RETRIES = 256;
/** Listener implementation for the `wl_buffer`. */
static const struct wl_buffer_listener wl_buffer_listener = {
.release = handle_wl_buffer_release,
};
/* == Exported methods ===================================================== */
/* ------------------------------------------------------------------------- */
wlclient_buffer_t *wlclient_buffer_create(
const wlclient_t *wlclient_ptr,
unsigned width,
unsigned height,
wlclient_buffer_ready_callback_t ready_callback,
void *ready_callback_ud_ptr)
{
wlclient_buffer_t *client_buffer_ptr = logged_calloc(
1, sizeof(wlclient_buffer_t));
if (NULL == client_buffer_ptr) return NULL;
client_buffer_ptr->ready_callback = ready_callback;
client_buffer_ptr->ready_callback_ud_ptr = ready_callback_ud_ptr;
client_buffer_ptr->width = width;
client_buffer_ptr->height = height;
size_t shm_pool_size = width * height * sizeof(uint32_t);
int fd = shm_creat(
wlclient_attributes(wlclient_ptr)->app_id_ptr, shm_pool_size);
if (0 >= fd) {
wlclient_buffer_destroy(client_buffer_ptr);
return NULL;
}
client_buffer_ptr->data_ptr = mmap(
NULL, shm_pool_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (MAP_FAILED == client_buffer_ptr->data_ptr) {
bs_log(BS_ERROR | BS_ERRNO, "Failed mmap(NULL, %zu, "
"PROT_READ|PROT_WRITE, MAP_SHARED, %d, 0)",
shm_pool_size, fd);
close(fd);
wlclient_buffer_destroy(client_buffer_ptr);
return NULL;
}
struct wl_shm_pool *wl_shm_pool_ptr = wl_shm_create_pool(
wlclient_attributes(wlclient_ptr)->wl_shm_ptr, fd, shm_pool_size);
close(fd);
if (NULL == wl_shm_pool_ptr) {
bs_log(BS_ERROR, "Failed wl_shm_create_pool(%p, %d, %zu)",
wlclient_attributes(wlclient_ptr)->wl_shm_ptr,
fd, shm_pool_size);
wlclient_buffer_destroy(client_buffer_ptr);
return NULL;
}
client_buffer_ptr->buffer_ptr = create_buffer(
wl_shm_pool_ptr,
client_buffer_ptr->data_ptr,
0,
width,
height,
width * sizeof(uint32_t));
if (NULL == client_buffer_ptr->buffer_ptr) {
wlclient_buffer_destroy(client_buffer_ptr);
return NULL;
}
client_buffer_ptr->buffer_ptr->client_buffer_ptr = client_buffer_ptr;
wl_shm_pool_destroy(wl_shm_pool_ptr);
if (NULL != client_buffer_ptr->ready_callback) {
client_buffer_ptr->ready_callback(
client_buffer_ptr->ready_callback_ud_ptr);
}
return client_buffer_ptr;
}
/* ------------------------------------------------------------------------- */
void wlclient_buffer_destroy(wlclient_buffer_t *client_buffer_ptr)
{
if (NULL != client_buffer_ptr->buffer_ptr) {
buffer_destroy(client_buffer_ptr->buffer_ptr);
client_buffer_ptr->buffer_ptr = NULL;
}
bs_log(BS_WARNING, "Destroyed %p", client_buffer_ptr);
free(client_buffer_ptr);
}
/* ------------------------------------------------------------------------- */
bs_gfxbuf_t *bs_gfxbuf_from_wlclient_buffer(
wlclient_buffer_t *client_buffer_ptr)
{
return client_buffer_ptr->buffer_ptr->bs_gfxbuf_ptr;
}
/* ------------------------------------------------------------------------- */
void wlclient_buffer_attach_to_surface_and_commit(
wlclient_buffer_t *client_buffer_ptr,
struct wl_surface *wl_surface_ptr)
{
BS_ASSERT(!client_buffer_ptr->buffer_ptr->committed);
wl_surface_attach(
wl_surface_ptr,
client_buffer_ptr->buffer_ptr->wl_buffer_ptr,
0, 0);
client_buffer_ptr->buffer_ptr->committed = true;
wl_surface_commit(wl_surface_ptr);
}
/* == Local (static) methods =============================================== */
/* ------------------------------------------------------------------------- */
/**
* Handles the `release` notification of the wl_buffer interface.
*
* @param data_ptr
* @param wl_buffer_ptr
*/
static void handle_wl_buffer_release(
void *data_ptr,
__UNUSED__ struct wl_buffer *wl_buffer_ptr)
{
buffer_t *buffer_ptr = data_ptr;
buffer_ptr->committed = false;
// Signal a potential user that this buffer is ready to draw into.
if (NULL != buffer_ptr->client_buffer_ptr->ready_callback) {
buffer_ptr->client_buffer_ptr->ready_callback(
buffer_ptr->client_buffer_ptr->ready_callback_ud_ptr);
}
}
/* ------------------------------------------------------------------------- */
/**
* Creates a POSIX shared memory object and allocates `size` bytes to it.
*
* @param app_id_ptr
* @param size
*
* @return The file descriptor (a non-negative integer) on success, or -1 on
* failure. The file descriptor must be closed with close(2).
*/
int shm_creat(const char *app_id_ptr, size_t size)
{
char shm_name[NAME_MAX];
int fd = -1;
shm_name[0] = '\0';
for (uint32_t sequence = 0; sequence < SHM_OPEN_RETRIES; ++sequence) {
snprintf(shm_name, NAME_MAX, "/%s_%"PRIdMAX"_shm_%"PRIx64"_%"PRIu32,
app_id_ptr ? app_id_ptr : "wlclient",
(intmax_t)getpid(), bs_usec(), sequence);
fd = shm_open(shm_name, O_RDWR|O_CREAT|O_EXCL, 0600);
if (0 > fd && errno == EEXIST) continue;
if (0 < fd) break;
bs_log(BS_WARNING | BS_ERRNO,
"Failed shm_open(%s, O_RDWR|O_CREAT|O_EXCL, 0600)",
shm_name);
return -1;
}
if (0 != shm_unlink(shm_name)) {
bs_log(BS_ERROR | BS_ERRNO, "Failed shm_unlink(%s)", shm_name);
close(fd);
return -1;
}
while (0 != ftruncate(fd, size)) {
if (EINTR == errno) continue; // try again...
bs_log(BS_ERROR | BS_ERRNO, "Failed ftruncate(%d, %zu)", fd, size);
close(fd);
return -1;
}
return fd;
}
/* ------------------------------------------------------------------------- */
/** Creates the actual buffer. */
buffer_t *create_buffer(struct wl_shm_pool *wl_shm_pool_ptr,
void *data_base_ptr,
size_t ofs,
unsigned width,
unsigned height,
unsigned bytes_per_line)
{
buffer_t *buffer_ptr = logged_calloc(1, sizeof(buffer_t));
if (NULL == buffer_ptr) return buffer_ptr;
buffer_ptr->data_ptr = (uint32_t*)((uint8_t*)data_base_ptr + ofs);
buffer_ptr->wl_buffer_ptr = wl_shm_pool_create_buffer(
wl_shm_pool_ptr, ofs, width, height, bytes_per_line,
WL_SHM_FORMAT_ARGB8888);
if (NULL == buffer_ptr->wl_buffer_ptr) {
buffer_destroy(buffer_ptr);
return NULL;
}
buffer_ptr->bs_gfxbuf_ptr = bs_gfxbuf_create_unmanaged(
width, height, bytes_per_line / sizeof(uint32_t), buffer_ptr->data_ptr);
if (NULL == buffer_ptr->bs_gfxbuf_ptr) {
buffer_destroy(buffer_ptr);
return NULL;
}
wl_buffer_add_listener(
buffer_ptr->wl_buffer_ptr,
&wl_buffer_listener,
buffer_ptr);
return buffer_ptr;
}
/* ------------------------------------------------------------------------- */
/** Destroys the actual buffer. */
void buffer_destroy(buffer_t *buffer_ptr)
{
if (NULL != buffer_ptr->bs_gfxbuf_ptr) {
bs_gfxbuf_destroy(buffer_ptr->bs_gfxbuf_ptr);
buffer_ptr->bs_gfxbuf_ptr = NULL;
}
if (NULL != buffer_ptr->wl_buffer_ptr) {
wl_buffer_destroy(buffer_ptr->wl_buffer_ptr);
buffer_ptr->wl_buffer_ptr = NULL;
}
free(buffer_ptr);
}
/* == End of buffer.c ====================================================== */
wlmaker-0.3/apps/libwlclient/buffer.h 0000664 0000000 0000000 00000005052 14641446446 0017732 0 ustar 00root root 0000000 0000000 /* ========================================================================= */
/**
* @file buffer.h
*
* @copyright
* Copyright 2023 Google LLC
*
* 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
*
* https://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.
*/
#ifndef __WLCLIENT_BUFFER_H__
#define __WLCLIENT_BUFFER_H__
#include "libwlclient.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
/** Forward declaration of the buffer state. */
typedef struct _wlclient_buffer_t wlclient_buffer_t;
/** Forward declaration of a wayland surface. */
struct wl_surface;
/** Callback to report that a buffer is ready to draw into. */
typedef void (*wlclient_buffer_ready_callback_t)(void *ud_ptr);
/**
* Creates a wlclient wayland buffer with the given dimensions.
*
* @param wlclient_ptr
* @param width
* @param height
* @param ready_callback
* @param ready_callback_ud_ptr
*
* @return A pointer to the created client buffer, or NULL on error. The
* buffer must be destroyed by calling @ref wlclient_buffer_destroy.
*/
wlclient_buffer_t *wlclient_buffer_create(
const wlclient_t *wlclient_ptr,
unsigned width,
unsigned height,
wlclient_buffer_ready_callback_t ready_callback,
void *ready_callback_ud_ptr);
/**
* Destroys the wlclient wayland buffer.
*
* @param buffer_ptr
*/
void wlclient_buffer_destroy(
wlclient_buffer_t *buffer_ptr);
/**
* Attaches the buffer to the surface and commits it.
*
* @param buffer_ptr
* @param wl_surface_ptr
*/
void wlclient_buffer_attach_to_surface_and_commit(
wlclient_buffer_t *buffer_ptr,
struct wl_surface *wl_surface_ptr);
/**
* Returns the`bs_gfxbuf_t` corresponding to the client buffer.
*
* @param buffer_ptr
*
* @return Pointer to the `bs_gfxbuf_t`. The `bs_gfxbuf_t` remains valid
* throughout the lifetime of buffer_ptr, and does not need to be
* released by the caller.
*/
bs_gfxbuf_t *bs_gfxbuf_from_wlclient_buffer(
wlclient_buffer_t *buffer_ptr);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif /* __WLCLIENT_BUFFER_H__ */
/* == End of buffer.h ====================================================== */
wlmaker-0.3/apps/libwlclient/client.c 0000664 0000000 0000000 00000042146 14641446446 0017737 0 ustar 00root root 0000000 0000000 /* ========================================================================= */
/**
* @file client.c
*
* @copyright
* Copyright 2023 Google LLC
*
* 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
*
* https://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.
*/
#include "libwlclient.h"
#include
#include
#include
#include
#include
#include
#include "wlmaker-icon-unstable-v1-client-protocol.h"
#include "xdg-shell-client-protocol.h"
/* == Declarations ========================================================= */
/** State of the wayland client. */
struct _wlclient_t {
/** Shareable attributes. */
wlclient_attributes_t attributes;
/** Registry singleton for the above connection. */
struct wl_registry *wl_registry_ptr;
/** List of registered timers. TODO(kaeser@gubbe.ch): Replace with HEAP. */
bs_dllist_t timers;
/** File descriptor to monitor SIGINT. */
int signal_fd;
/** Whether to keep the client running. */
volatile bool keep_running;
};
/** State of a registered timer. */
typedef struct {
/** Node within the list of timers, see `wlclient_t.timers`. */
bs_dllist_node_t dlnode;
/** Target time, in usec since epoch. */
uint64_t target_usec;
/** Callback once the timer is triggered. */
wlclient_callback_t callback;
/** Argument to the callback. */
void *callback_ud_ptr;
} wlclient_timer_t;
/** Descriptor for a wayland object to bind to. */
typedef struct {
/** The interface definition. */
const struct wl_interface *wl_interface_ptr;
/** Version desired to bind to. */
uint32_t desired_version;
/** Offset of the bound interface, relative to `wlclient_t`. */
size_t bound_ptr_offset;
} object_t;
static void wl_to_bs_log(
const char *fmt,
va_list args);
static void handle_global_announce(
void *data_ptr,
struct wl_registry *wl_registry_ptr,
uint32_t name,
const char *interface_ptr,
uint32_t version);
static void handle_global_remove(
void *data_ptr,
struct wl_registry *registry,
uint32_t name);
static wlclient_timer_t *wlc_timer_create(
wlclient_t *client_ptr,
uint64_t target_usec,
wlclient_callback_t callback,
void *callback_ud_ptr);
static void wlc_timer_destroy(
wlclient_timer_t *timer_ptr);
/* == Data ================================================================= */
/** Listener for the registry, taking note of registry updates. */
static const struct wl_registry_listener registry_listener = {
.global = handle_global_announce,
.global_remove = handle_global_remove,
};
/** List of wayland objects we want to bind to. */
static const object_t objects[] = {
{ &wl_compositor_interface, 4,
offsetof(wlclient_attributes_t, wl_compositor_ptr) },
{ &wl_shm_interface, 1,
offsetof(wlclient_attributes_t, wl_shm_ptr) },
{ &xdg_wm_base_interface, 1,
offsetof(wlclient_attributes_t, xdg_wm_base_ptr) },
{ &zwlmaker_icon_manager_v1_interface, 1,
offsetof(wlclient_attributes_t, icon_manager_ptr) },
{ NULL, 0, 0 } // sentinel.
};
/* == Exported methods ===================================================== */
/* ------------------------------------------------------------------------- */
wlclient_t *wlclient_create(const char *app_id_ptr)
{
wlclient_t *wlclient_ptr = logged_calloc(1, sizeof(wlclient_t));
if (NULL == wlclient_ptr) return NULL;
wl_log_set_handler_client(wl_to_bs_log);
if (NULL != app_id_ptr) {
wlclient_ptr->attributes.app_id_ptr = logged_strdup(app_id_ptr);
if (NULL == wlclient_ptr->attributes.app_id_ptr) {
wlclient_destroy(wlclient_ptr);
return NULL;
}
}
sigset_t signal_set;
if (sigemptyset(&signal_set)) {
bs_log(BS_ERROR | BS_ERRNO, "Failed sigemptyset(%p)", &signal_set);
wlclient_destroy(wlclient_ptr);
return NULL;
}
if (sigaddset(&signal_set, SIGINT)) {
bs_log(BS_ERROR | BS_ERRNO, "Failed sigemptyset(%p, %d)",
&signal_set, SIGINT);
wlclient_destroy(wlclient_ptr);
return NULL;
}
if (sigprocmask(SIG_BLOCK, &signal_set, NULL) == -1) {
bs_log(BS_ERROR | BS_ERRNO, "Failed sigprocmask(SIG_BLOCK, %p, NULL)",
&signal_set);
wlclient_destroy(wlclient_ptr);
return NULL;
}
wlclient_ptr->signal_fd = signalfd(-1, &signal_set, SFD_NONBLOCK);
if (0 >= wlclient_ptr->signal_fd) {
bs_log(BS_ERROR | BS_ERRNO, "Failed signalfd(-1, %p, SFD_NONBLOCK)",
&signal_set);
wlclient_destroy(wlclient_ptr);
return NULL;
}
wlclient_ptr->attributes.wl_display_ptr = wl_display_connect(NULL);
if (NULL == wlclient_ptr->attributes.wl_display_ptr) {
bs_log(BS_ERROR, "Failed wl_display_connect(NULL).");
wlclient_destroy(wlclient_ptr);
return NULL;
}
wlclient_ptr->wl_registry_ptr = wl_display_get_registry(
wlclient_ptr->attributes.wl_display_ptr);
if (NULL == wlclient_ptr->wl_registry_ptr) {
bs_log(BS_ERROR, "Failed wl_display_get_registry(%p).",
wlclient_ptr->wl_registry_ptr);
wlclient_destroy(wlclient_ptr);
return NULL;
}
if (0 != wl_registry_add_listener(
wlclient_ptr->wl_registry_ptr,
®istry_listener,
&wlclient_ptr->attributes)) {
bs_log(BS_ERROR, "Failed wl_registry_add_listener(%p, %p, %p).",
wlclient_ptr->wl_registry_ptr,
®istry_listener,
&wlclient_ptr->attributes);
wlclient_destroy(wlclient_ptr);
return NULL;
}
wl_display_roundtrip(wlclient_ptr->attributes.wl_display_ptr);
if (NULL == wlclient_ptr->attributes.wl_compositor_ptr) {
bs_log(BS_ERROR, "'wl_compositor' interface not found on Wayland.");
wlclient_destroy(wlclient_ptr);
return NULL;
}
if (NULL == wlclient_ptr->attributes.wl_shm_ptr) {
bs_log(BS_ERROR, "'wl_shm' interface not found on Wayland.");
wlclient_destroy(wlclient_ptr);
return NULL;
}
if (NULL == wlclient_ptr->attributes.xdg_wm_base_ptr) {
bs_log(BS_ERROR, "'xdg_wm_base' interface not found on Wayland.");
wlclient_destroy(wlclient_ptr);
return NULL;
}
return wlclient_ptr;
}
/* ------------------------------------------------------------------------- */
void wlclient_destroy(wlclient_t *wlclient_ptr)
{
bs_dllist_node_t *dlnode_ptr;
while (NULL != (dlnode_ptr = bs_dllist_pop_front(&wlclient_ptr->timers))) {
wlc_timer_destroy((wlclient_timer_t*)dlnode_ptr);
}
if (NULL != wlclient_ptr->wl_registry_ptr) {
wl_registry_destroy(wlclient_ptr->wl_registry_ptr);
wlclient_ptr->wl_registry_ptr = NULL;
}
if (NULL != wlclient_ptr->attributes.wl_display_ptr) {
wl_display_disconnect(wlclient_ptr->attributes.wl_display_ptr);
wlclient_ptr->attributes.wl_display_ptr = NULL;
}
if (0 < wlclient_ptr->signal_fd) {
close(wlclient_ptr->signal_fd);
wlclient_ptr->signal_fd = 0;
}
if (NULL != wlclient_ptr->attributes.app_id_ptr) {
// Cheated when saying it's const...
free((char*)wlclient_ptr->attributes.app_id_ptr);
wlclient_ptr->attributes.app_id_ptr = NULL;
}
free(wlclient_ptr);
}
/* ------------------------------------------------------------------------- */
const wlclient_attributes_t *wlclient_attributes(
const wlclient_t *wlclient_ptr)
{
return &wlclient_ptr->attributes;
}
/* ------------------------------------------------------------------------- */
// TODO(kaeser@gubbe.ch): Clean up.
void wlclient_run(wlclient_t *wlclient_ptr)
{
wlclient_ptr->keep_running = true;
do {
while (0 != wl_display_prepare_read(wlclient_ptr->attributes.wl_display_ptr)) {
if (0 > wl_display_dispatch_pending(wlclient_ptr->attributes.wl_display_ptr)) {
bs_log(BS_ERROR | BS_ERRNO,
"Failed wl_display_dispatch_pending(%p)",
wlclient_ptr->attributes.wl_display_ptr);
break; // Error (?)
}
}
if (0 > wl_display_flush(wlclient_ptr->attributes.wl_display_ptr)) {
if (EAGAIN != errno) {
bs_log(BS_ERROR | BS_ERRNO,
"Failed wl_display_flush(%p)", wlclient_ptr->attributes.wl_display_ptr);
wl_display_cancel_read(wlclient_ptr->attributes.wl_display_ptr);
break; // Error!
}
}
struct pollfd pollfds[2];
pollfds[0].fd = wl_display_get_fd(wlclient_ptr->attributes.wl_display_ptr);
pollfds[0].events = POLLIN;
pollfds[0].revents = 0;
pollfds[1].fd = wlclient_ptr->signal_fd;
pollfds[1].events = POLLIN;
pollfds[1].revents = 0;
int rv = poll(&pollfds[0], 2, 100);
if (0 > rv && EINTR != errno) {
bs_log(BS_ERROR | BS_ERRNO, "Failed poll(%p, 1, 100)", &pollfds);
wl_display_cancel_read(wlclient_ptr->attributes.wl_display_ptr);
break; // Error!
}
if (pollfds[0].revents & POLLIN) {
if (0 > wl_display_read_events(wlclient_ptr->attributes.wl_display_ptr)) {
bs_log(BS_ERROR | BS_ERRNO, "Failed wl_display_read_events(%p)",
wlclient_ptr->attributes.wl_display_ptr);
break; // Error!
}
} else {
wl_display_cancel_read(wlclient_ptr->attributes.wl_display_ptr);
}
if (pollfds[1].revents & POLLIN) {
struct signalfd_siginfo siginfo;
ssize_t rd = read(wlclient_ptr->signal_fd, &siginfo, sizeof(siginfo));
if (0 > rd) {
bs_log(BS_ERROR, "Failed read(%d, %p, %zu)",
wlclient_ptr->signal_fd, &siginfo, sizeof(siginfo));
break;
} else if ((size_t)rd != sizeof(siginfo)) {
bs_log(BS_ERROR, "Bytes read from signal_fd %zu != %zd",
sizeof(siginfo), rd);
break;
}
bs_log(BS_ERROR, "Signal caught: %d", siginfo.ssi_signo);
wlclient_ptr->keep_running = false;
}
if (0 > wl_display_dispatch_pending(wlclient_ptr->attributes.wl_display_ptr)) {
bs_log(BS_ERROR | BS_ERRNO,
"Failed wl_display_dispatch_queue_pending(%p)",
wlclient_ptr->attributes.wl_display_ptr);
int err = wl_display_get_error(wlclient_ptr->attributes.wl_display_ptr);
if (0 != err) {
bs_log(BS_ERROR, "Display error %d", err);
}
uint32_t id;
const struct wl_interface *wl_interface_ptr;
uint32_t perr = wl_display_get_protocol_error(
wlclient_ptr->attributes.wl_display_ptr, &wl_interface_ptr, &id);
if (0 != perr) {
bs_log(BS_ERROR,
"Protocol error %"PRIu32", interface %s id %"PRIu32,
perr, wl_interface_ptr->name, id);
}
break; // Error!
}
// Flush the timer queue.
uint64_t current_usec = bs_usec();
bs_dllist_node_t *dlnode_ptr;
while (NULL != (dlnode_ptr = wlclient_ptr->timers.head_ptr) &&
((wlclient_timer_t*)dlnode_ptr)->target_usec <= current_usec) {
bs_dllist_pop_front(&wlclient_ptr->timers);
wlclient_timer_t *timer_ptr = (wlclient_timer_t*)dlnode_ptr;
timer_ptr->callback(wlclient_ptr, timer_ptr->callback_ud_ptr);
wlc_timer_destroy(timer_ptr);
}
} while (wlclient_ptr->keep_running);
}
/* ------------------------------------------------------------------------- */
bool wlclient_register_timer(
wlclient_t *wlclient_ptr,
uint64_t target_usec,
wlclient_callback_t callback,
void *callback_ud_ptr)
{
wlclient_timer_t *timer_ptr = wlc_timer_create(
wlclient_ptr, target_usec, callback, callback_ud_ptr);
return (timer_ptr != NULL);
}
/* == Local (static) methods =============================================== */
/* ------------------------------------------------------------------------- */
/**
* Redirects a wayland log call into s_log.
*
* @param fmt_ptr
* @param args
*/
void wl_to_bs_log(
const char *fmt_ptr,
va_list args)
{
bs_log_vwrite(BS_ERROR, __FILE__, __LINE__, fmt_ptr, args);
}
/* ------------------------------------------------------------------------- */
/**
* Handles the announcement of a global object.
*
* Called by `struct wl_registry_listener` `global` callback, invoked to notify
* clients of global objects.
*
* @param data_ptr Points to a @ref wlclient_t.
* @param wl_registry_ptr The `struct wl_registry` this is invoked for.
* @param name Numeric name of the global object.
* @param interface_name_ptr Name of the interface implemented by the object.
* @param version Interface version.
*/
void handle_global_announce(
void *data_ptr,
struct wl_registry *wl_registry_ptr,
uint32_t name,
const char *interface_name_ptr,
uint32_t version)
{
for (const object_t *object_ptr = &objects[0];
NULL != object_ptr->wl_interface_ptr;
++object_ptr) {
// Proceed, if the interface name doesn't match.
if (0 != strcmp(interface_name_ptr,
object_ptr->wl_interface_ptr->name)) {
continue;
}
void *bound_ptr = wl_registry_bind(
wl_registry_ptr, name,
object_ptr->wl_interface_ptr,
object_ptr->desired_version);
if (NULL == bound_ptr) {
bs_log(BS_ERROR,
"Failed wl_registry_bind(%p, %"PRIu32", %p, %"PRIu32") "
"for interface %s, version %"PRIu32".",
wl_registry_ptr, name,
object_ptr->wl_interface_ptr,
object_ptr->desired_version,
interface_name_ptr, version);
continue;
}
((void**)((uint8_t*)data_ptr + object_ptr->bound_ptr_offset))[0] =
bound_ptr;
bs_log(BS_DEBUG, "Bound interface %s to %p",
interface_name_ptr, bound_ptr);
}
}
/* ------------------------------------------------------------------------- */
/**
* Handles the removal of a wayland global object.
*
* Called by `struct wl_registry_listener` `global_remove`, invoked to notify
* clients of removed global objects.
*
* @param data_ptr Points to a @ref wlclient_t.
* @param wl_registry_ptr The `struct wl_registry` this is invoked for.
* @param name Numeric name of the global object.
*/
void handle_global_remove(
void *data_ptr,
struct wl_registry *wl_registry_ptr,
uint32_t name)
{
// TODO(kaeser@gubbe.ch): Add implementation.
bs_log(BS_INFO, "handle_global_remove(%p, %p, %"PRIu32").",
data_ptr, wl_registry_ptr, name);
}
/* ------------------------------------------------------------------------- */
/**
* Creates a timer and registers it with the client.
*
* @param client_ptr
* @param target_usec
* @param callback
* @param callback_ud_ptr
*
* @return A pointer to the created timer, or NULL on error. The pointer must
* be destroyed by @ref wlc_timer_destroy.
*/
wlclient_timer_t *wlc_timer_create(
wlclient_t *client_ptr,
uint64_t target_usec,
wlclient_callback_t callback,
void *callback_ud_ptr)
{
wlclient_timer_t *timer_ptr = logged_calloc(1, sizeof(wlclient_timer_t));
if (NULL == timer_ptr) return NULL;
timer_ptr->target_usec = target_usec;
timer_ptr->callback = callback;
timer_ptr->callback_ud_ptr = callback_ud_ptr;
// TODO(kaeser@gubbe.ch): This should be a HEAP.
bs_dllist_node_t *dlnode_ptr = client_ptr->timers.head_ptr;
for (; dlnode_ptr != NULL; dlnode_ptr = dlnode_ptr->next_ptr) {
wlclient_timer_t *ref_timer_ptr = (wlclient_timer_t *)dlnode_ptr;
if (timer_ptr->target_usec > ref_timer_ptr->target_usec) continue;
bs_dllist_insert_node_before(
&client_ptr->timers, dlnode_ptr, &timer_ptr->dlnode);
}
if (NULL == dlnode_ptr) {
bs_dllist_push_back(&client_ptr->timers, &timer_ptr->dlnode);
}
return timer_ptr;
}
/* ------------------------------------------------------------------------- */
/**
* Destroys the timer. Note: The timer will NOT be unregistered first.
*
* @param timer_ptr
*/
void wlc_timer_destroy(wlclient_timer_t *timer_ptr)
{
free(timer_ptr);
}
/* == End of client.c ====================================================== */
wlmaker-0.3/apps/libwlclient/icon.c 0000664 0000000 0000000 00000021712 14641446446 0017405 0 ustar 00root root 0000000 0000000 /* ========================================================================= */
/**
* @file icon.c
*
* @copyright
* Copyright 2023 Google LLC
*
* 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
*
* https://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.
*/
#include "icon.h"
#include "buffer.h"
#include "wlmaker-icon-unstable-v1-client-protocol.h"
#include
/* == Declarations ========================================================= */
/** State of the icon. */
typedef struct _wlclient_icon_t {
/** Back-link to the client. */
wlclient_t *wlclient_ptr;
/** Surface. */
struct wl_surface *wl_surface_ptr;
/** The icon interface. */
struct zwlmaker_toplevel_icon_v1 *toplevel_icon_ptr;
/** Width of the icon, once suggested by the server. */
unsigned width;
/** Height of the icon, once suggested by the server. */
unsigned height;
/** Callback for when the icon's buffer is ready to be drawn into. */
wlclient_icon_gfxbuf_callback_t buffer_ready_callback;
/** Argument to that callback. */
void *buffer_ready_callback_ud_ptr;
/** The buffer backing the icon. */
wlclient_buffer_t *buffer_ptr;
/** Outstanding frames to display. Considered ready to draw when zero. */
int pending_frames;
/** Whether the buffer was reported as ready. */
bool buffer_ready;
/** Whether there is currently a callback in progress. */
bool callback_in_progress;
} wlclient_icon_t;
static void handle_toplevel_icon_configure(
void *data_ptr,
struct zwlmaker_toplevel_icon_v1 *zwlmaker_toplevel_icon_v1_ptr,
int32_t width,
int32_t height,
uint32_t serial);
static void handle_frame_done(
void *data_ptr,
struct wl_callback *callback,
uint32_t time);
static void handle_buffer_ready(void *data_ptr);
static void state(wlclient_icon_t *icon_ptr);
/* == Data ================================================================= */
/** Listener implementation for toplevel icon. */
static const struct zwlmaker_toplevel_icon_v1_listener toplevel_icon_listener={
.configure = handle_toplevel_icon_configure,
};
/** Listener implementation for the frame. */
static const struct wl_callback_listener frame_listener = {
.done = handle_frame_done
};
/* == Exported methods ===================================================== */
/* ------------------------------------------------------------------------- */
wlclient_icon_t *wlclient_icon_create(wlclient_t *wlclient_ptr)
{
if (!wlclient_icon_supported(wlclient_ptr)) {
bs_log(BS_ERROR, "Icon manager is not supported.");
return NULL;
}
wlclient_icon_t *icon_ptr = logged_calloc(1, sizeof(wlclient_icon_t));
if (NULL == icon_ptr) return NULL;
icon_ptr->wlclient_ptr = wlclient_ptr;
icon_ptr->wl_surface_ptr = wl_compositor_create_surface(
wlclient_attributes(wlclient_ptr)->wl_compositor_ptr);
if (NULL == icon_ptr->wl_surface_ptr) {
bs_log(BS_ERROR, "Failed wl_compositor_create_surface(%p).",
wlclient_attributes(wlclient_ptr)->wl_compositor_ptr);
wlclient_icon_destroy(icon_ptr);
return NULL;
}
icon_ptr->toplevel_icon_ptr = zwlmaker_icon_manager_v1_get_toplevel_icon(
wlclient_attributes(wlclient_ptr)->icon_manager_ptr,
NULL,
icon_ptr->wl_surface_ptr);
if (NULL == icon_ptr->toplevel_icon_ptr) {
bs_log(BS_ERROR, "Failed zwlmaker_icon_manager_v1_get_toplevel_icon"
"(%p, NULL, %p).",
wlclient_attributes(wlclient_ptr)->icon_manager_ptr,
icon_ptr->wl_surface_ptr);
wlclient_icon_destroy(icon_ptr);
return NULL;
}
zwlmaker_toplevel_icon_v1_add_listener(
icon_ptr->toplevel_icon_ptr,
&toplevel_icon_listener,
icon_ptr);
wl_surface_commit(icon_ptr->wl_surface_ptr);
return icon_ptr;
}
/* ------------------------------------------------------------------------- */
void wlclient_icon_destroy(wlclient_icon_t *icon_ptr)
{
if (NULL != icon_ptr->toplevel_icon_ptr) {
// TODO(kaeser@gubbe.ch): Destroy the icon!
icon_ptr->toplevel_icon_ptr = NULL;
}
if (NULL != icon_ptr->wl_surface_ptr) {
wl_surface_destroy(icon_ptr->wl_surface_ptr);
icon_ptr->wl_surface_ptr = NULL;
}
free(icon_ptr);
}
/* ------------------------------------------------------------------------- */
bool wlclient_icon_supported(
wlclient_t *wlclient_ptr)
{
return (NULL != wlclient_attributes(wlclient_ptr)->icon_manager_ptr);
}
/* ------------------------------------------------------------------------ */
void wlclient_icon_callback_when_ready(
wlclient_icon_t *icon_ptr,
wlclient_icon_gfxbuf_callback_t callback,
void *ud_ptr)
{
icon_ptr->buffer_ready_callback = callback;
icon_ptr->buffer_ready_callback_ud_ptr = ud_ptr;
state(icon_ptr);
}
/* == Local (static) methods =============================================== */
/* ------------------------------------------------------------------------- */
/**
* Handles the 'configure' event: Creates appropriately sized buffer.
*
* @param data_ptr
* @param zwlmaker_toplevel_icon_v1_ptr
* @param width
* @param height
* @param serial
*/
void handle_toplevel_icon_configure(
void *data_ptr,
struct zwlmaker_toplevel_icon_v1 *zwlmaker_toplevel_icon_v1_ptr,
int32_t width,
int32_t height,
uint32_t serial)
{
wlclient_icon_t *icon_ptr = data_ptr;
icon_ptr->width = width;
icon_ptr->height = height;
bs_log(BS_DEBUG, "Configured icon to %"PRId32" x %"PRId32, width, height);
zwlmaker_toplevel_icon_v1_ack_configure(
zwlmaker_toplevel_icon_v1_ptr, serial);
wlclient_t *wlclient_ptr = icon_ptr->wlclient_ptr;
icon_ptr->buffer_ptr = wlclient_buffer_create(
wlclient_ptr, icon_ptr->width, icon_ptr->height,
handle_buffer_ready, icon_ptr);
if (NULL == icon_ptr->buffer_ptr) {
bs_log(BS_FATAL, "Failed wlclient_buffer_create(%p, %u, %u)",
wlclient_ptr, icon_ptr->width, icon_ptr->height);
// TODO(kaeser@gubbe.ch): Error handling.
return;
}
state(icon_ptr);
}
/* ------------------------------------------------------------------------- */
/**
* Updates the information that there is a buffer ready to be drawn into.
*
* @param data_ptr
*/
void handle_buffer_ready(void *data_ptr)
{
wlclient_icon_t *icon_ptr = data_ptr;
icon_ptr->buffer_ready = true;
state(icon_ptr);
}
/* ------------------------------------------------------------------------- */
/**
* Registers the frame got displayed, potentially triggers the callback.
*
* @param data_ptr
* @param callback
* @param time
*/
void handle_frame_done(
void *data_ptr,
struct wl_callback *callback,
__UNUSED__ uint32_t time)
{
wl_callback_destroy(callback);
wlclient_icon_t *icon_ptr = data_ptr;
icon_ptr->pending_frames--;
state(icon_ptr);
}
/* ------------------------------------------------------------------------- */
/**
* Runs the ready callback, if due.
*
* @param icon_ptr
*/
void state(wlclient_icon_t *icon_ptr)
{
// Not fully initialized, skip this attempt.
if (NULL == icon_ptr->buffer_ptr) return;
// ... or, no callback...
if (NULL == icon_ptr->buffer_ready_callback) return;
// ... or, actually not ready.
if (0 < icon_ptr->pending_frames || !icon_ptr->buffer_ready) return;
// ... or, a callback is currently in progress.
if (icon_ptr->callback_in_progress) return;
wlclient_icon_gfxbuf_callback_t callback = icon_ptr->buffer_ready_callback;
void *ud_ptr = icon_ptr->buffer_ready_callback_ud_ptr;
icon_ptr->buffer_ready_callback = NULL;
icon_ptr->buffer_ready_callback_ud_ptr = NULL;
icon_ptr->callback_in_progress = true;
bool rv = callback(
icon_ptr, bs_gfxbuf_from_wlclient_buffer(icon_ptr->buffer_ptr), ud_ptr);
icon_ptr->callback_in_progress = false;
if (!rv) return;
struct wl_callback *wl_callback = wl_surface_frame(
icon_ptr->wl_surface_ptr);
wl_callback_add_listener(wl_callback, &frame_listener, icon_ptr);
wl_surface_damage_buffer(
icon_ptr->wl_surface_ptr,
0, 0, INT32_MAX, INT32_MAX);
icon_ptr->pending_frames++;
icon_ptr->buffer_ready = false;
wlclient_buffer_attach_to_surface_and_commit(
icon_ptr->buffer_ptr,
icon_ptr->wl_surface_ptr);
}
/* == End of icon.c ======================================================== */
wlmaker-0.3/apps/libwlclient/icon.h 0000664 0000000 0000000 00000005323 14641446446 0017412 0 ustar 00root root 0000000 0000000 /* ========================================================================= */
/**
* @file icon.h
*
* @copyright
* Copyright 2023 Google LLC
*
* 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
*
* https://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.
*/
#ifndef __LIBWLCLIENT_ICON_H__
#define __LIBWLCLIENT_ICON_H__
#include "libwlclient.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
/** Forward declaration of an icon's state. */
typedef struct _wlclient_icon_t wlclient_icon_t;
/**
* Type of the callback for @ref wlclient_icon_callback_when_ready.
*
* @param icon_ptr
* @param gfxbuf_ptr
* @param ud_ptr
*/
typedef bool (*wlclient_icon_gfxbuf_callback_t)(
wlclient_icon_t *icon_ptr,
bs_gfxbuf_t *gfxbuf_ptr,
void *ud_ptr);
/**
* Creates an icon.
*
* @param wlclient_ptr
*
* @return An icon state or NULL on error. The state must be free'd by calling
* @ref wlclient_icon_destroy.
*/
wlclient_icon_t *wlclient_icon_create(
wlclient_t *wlclient_ptr);
/**
* Destroys the icon.
*
* @param icon_ptr
*/
void wlclient_icon_destroy(
wlclient_icon_t *icon_ptr);
/**
* Returns whether the icon protocol is supported on the client.
*
* @param wlclient_ptr
*/
bool wlclient_icon_supported(wlclient_t *wlclient_ptr);
/**
* Sets a callback to invoke when the background buffer is ready for drawing.
*
* If the background buffer is already ready, the callback will get executed
* right away. Otherwise, the callback will be registered for the icon, and
* executed as the background buffer becomes available.
*
* The callback will be invoked once only. If repeated calls are desired,
* the callee should call @ref wlclient_icon_callback_when_ready again from
* within the `callback` method.
*
* Only one callback may be active at any time. Any further invocation will
* replace the already-registered callback. To unregister a callback, call
* the function with callback == NULL.
*
* @param icon_ptr
* @param callback
* @param ud_ptr
*/
void wlclient_icon_callback_when_ready(
wlclient_icon_t *icon_ptr,
wlclient_icon_gfxbuf_callback_t callback,
void *ud_ptr);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif /* __LIBWLCLIENT_ICON_H__ */
/* == End of icon.h ======================================================== */
wlmaker-0.3/apps/libwlclient/libwlclient.h 0000664 0000000 0000000 00000006420 14641446446 0020771 0 ustar 00root root 0000000 0000000 /* ========================================================================= */
/**
* @file libwlclient.h
*
* @copyright
* Copyright 2023 Google LLC
*
* 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
*
* https://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.
*/
#ifndef __LIBWLCLIENT_H__
#define __LIBWLCLIENT_H__
#include
#include
#include
/** Forward declaration: Wayland client handle. */
typedef struct _wlclient_t wlclient_t;
#include "icon.h"
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
/**
* A client's callback, as used in @ref wlclient_register_timer.
*
* @param wlclient_ptr
* @param ud_ptr
*/
typedef void (*wlclient_callback_t)(
wlclient_t *wlclient_ptr,
void *ud_ptr);
/** Accessor to 'public' client attributes. */
typedef struct {
/** Wayland display connection. */
struct wl_display *wl_display_ptr;
/** The bound compositor interface. */
struct wl_compositor *wl_compositor_ptr;
/** The bound SHM interface. */
struct wl_shm *wl_shm_ptr;
/** The bound XDG wm_base interface. */
struct xdg_wm_base *xdg_wm_base_ptr;
/** The bound Toplevel Icon Manager. Will be NULL if not supported. */
struct zwlmaker_icon_manager_v1 *icon_manager_ptr;
/** Application ID, as a string. Or NULL, if not set. */
const char *app_id_ptr;
} wlclient_attributes_t;
/**
* Creates a wayland client for simple buffer interactions.
*
* @param app_id_ptr Application ID or NULL if not set.
*
* @return The client state, or NULL on error. The state needs to be free'd
* via @ref wlclient_destroy.
*/
wlclient_t *wlclient_create(const char *app_id_ptr);
/**
* Destroys the wayland client, as created by @ref wlclient_create.
*
* @param wlclient_ptr
*/
void wlclient_destroy(wlclient_t *wlclient_ptr);
/**
* Gets the client attributes.
*
* @param wlclient_ptr
*
* @return A pointer to the attributes.
*/
const wlclient_attributes_t *wlclient_attributes(
const wlclient_t *wlclient_ptr);
/**
* Runs the client's mainloop.
*
* @param wlclient_ptr
*/
void wlclient_run(wlclient_t *wlclient_ptr);
/**
* Registers a timer with the client.
*
* Once the system clock reaches (or has passed) `target_usec`, `callback` will
* be called with the provided arguments. This is a one-time registration. For
* repeated calls, clients need to re-register.
*
* @param wlclient_ptr
* @param target_usec
* @param callback
* @param callback_ud_ptr
*
* @return true on success.
*/
bool wlclient_register_timer(
wlclient_t *wlclient_ptr,
uint64_t target_usec,
wlclient_callback_t callback,
void *callback_ud_ptr);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif /* __LIBWLCLIENT_H__ */
/* == End of libwlclient.h ================================================= */
wlmaker-0.3/apps/primitives/ 0000775 0000000 0000000 00000000000 14641446446 0016171 5 ustar 00root root 0000000 0000000 wlmaker-0.3/apps/primitives/CMakeLists.txt 0000664 0000000 0000000 00000002403 14641446446 0020730 0 ustar 00root root 0000000 0000000 # Copyright 2023 Google LLC
#
# 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
#
# https://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.
CMAKE_MINIMUM_REQUIRED(VERSION 3.13)
ADD_LIBRARY(primitives STATIC)
TARGET_SOURCES(
primitives PRIVATE
primitives.h primitives.c
segment_display.h segment_display.c)
TARGET_INCLUDE_DIRECTORIES(
primitives PRIVATE)
TARGET_LINK_LIBRARIES(
primitives
base)
ADD_EXECUTABLE(segment_display_test
segment_display_test.c
segment_display.c
segment_display.h)
TARGET_COMPILE_DEFINITIONS(
segment_display_test PRIVATE
TEST_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}")
TARGET_INCLUDE_DIRECTORIES(
segment_display_test PRIVATE
${CAIRO_INCLUDE_DIRS})
TARGET_LINK_LIBRARIES(
segment_display_test PRIVATE
base
PkgConfig::CAIRO)
ADD_TEST(
NAME segment_display_test
COMMAND segment_display_test)
wlmaker-0.3/apps/primitives/primitives.c 0000664 0000000 0000000 00000005155 14641446446 0020536 0 ustar 00root root 0000000 0000000 /* ========================================================================= */
/**
* @file primitives.c
*
* @copyright
* Copyright 2023 Google LLC
*
* 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
*
* https://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.
*/
#include "primitives.h"
/* == Exported methods ===================================================== */
/* ------------------------------------------------------------------------- */
void wlm_primitives_set_bezel_color(
cairo_t *cairo_ptr,
bool illuminated)
{
if (illuminated) {
cairo_set_source_rgba(cairo_ptr, 1.0, 1.0, 1.0, 0.6);
} else {
cairo_set_source_rgba(cairo_ptr, 0.0, 0.0, 0.0, 0.4);
}
}
/* ------------------------------------------------------------------------- */
void wlm_primitives_draw_bezel_at(
cairo_t *cairo_ptr,
int x,
int y,
unsigned width,
unsigned height,
double bezel_width,
bool raised)
{
cairo_save(cairo_ptr);
cairo_set_line_width(cairo_ptr, 0);
// Northwestern corner is illuminted when raised.
wlm_primitives_set_bezel_color(cairo_ptr, raised);
cairo_move_to(cairo_ptr, x, y);
cairo_line_to(cairo_ptr, x + width, y + 0);
cairo_line_to(cairo_ptr, x + width - bezel_width, y + bezel_width);
cairo_line_to(cairo_ptr, x + bezel_width, y + bezel_width);
cairo_line_to(cairo_ptr, x + bezel_width, y + height - bezel_width);
cairo_line_to(cairo_ptr, x + 0, y + height);
cairo_line_to(cairo_ptr, x + 0, y + 0);
cairo_fill(cairo_ptr);
// Southeastern corner is illuminated when sunken.
wlm_primitives_set_bezel_color(cairo_ptr, !raised);
cairo_move_to(cairo_ptr, x + width, y + height);
cairo_line_to(cairo_ptr, x + 0, y + height);
cairo_line_to(cairo_ptr, x + bezel_width, y + height - bezel_width);
cairo_line_to(cairo_ptr, x + width - bezel_width, y + height - bezel_width);
cairo_line_to(cairo_ptr, x + width - bezel_width, y + bezel_width);
cairo_line_to(cairo_ptr, x + width, y + 0);
cairo_line_to(cairo_ptr, x + width, y + height);
cairo_fill(cairo_ptr);
cairo_restore(cairo_ptr);
}
/* == End of primitives.c ================================================== */
wlmaker-0.3/apps/primitives/primitives.h 0000664 0000000 0000000 00000004077 14641446446 0020545 0 ustar 00root root 0000000 0000000 /* ========================================================================= */
/**
* @file primitives.h
*
* @copyright
* Copyright 2023 Google LLC
*
* 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
*
* https://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.
*/
#ifndef __WLM_PRIMITIVES_H__
#define __WLM_PRIMITIVES_H__
#include
#include
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
/**
* Sets the bezel color.
*
* Note: Window Maker draws the bezel by adding 80 (0x50) to each R, G, B of
* the underlying title for the illuminated side; respectively by subtracting
* 40 (0x28) on the non-illuminated side.
* We are using cairo's overlaying with the respective "alpha" values below,
* which leads to different results.
*
* @param cairo_ptr
* @param illuminated
*/
void wlm_primitives_set_bezel_color(
cairo_t *cairo_ptr,
bool illuminated);
/**
* Draws a bezel into the cairo, at specified position and width/height.
*
* TODO(kaeser@gubbe.ch): Share this code with the server.
*
* @param cairo_ptr A cairo, backed by an image surface.
* @param x
* @param y
* @param width
* @param height
* @param bezel_width
* @param raised Whether the bezel is to highlight a raised (true)
* or pressed (false) state.
*/
void wlm_primitives_draw_bezel_at(
cairo_t *cairo_ptr,
int x,
int y,
unsigned width,
unsigned height,
double bezel_width,
bool raised);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif /* __WLM_PRIMITIVES_H__ */
/* == End of primitives.h ================================================== */
wlmaker-0.3/apps/primitives/segment_display.c 0000664 0000000 0000000 00000024511 14641446446 0021527 0 ustar 00root root 0000000 0000000 /* ========================================================================= */
/**
* @file segment_display.c
*
* @copyright
* Copyright 2023 Google LLC
*
* 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
*
* https://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.
*/
#include "segment_display.h"
#include
#include
#include
/* == Declarations ========================================================= */
static void draw_segment(
cairo_t *cairo_ptr,
const bs_vector_2f_t origin,
const bs_vector_2f_t longitudinal,
const bs_vector_2f_t lateral,
const wlm_cairo_7segment_param_t *param_ptr,
const double length);
/**
* Encoding bits to indicate coloring of segments for each digit.
*
* The sequence follows https://en.wikipedia.org/wiki/Seven-segment_display,
* as follows:
```
<- 0 ->
^ ^
5 1
v v
<- 6 ->
^ ^
4 2
v v
<- 3 ->
```
*/
static const uint8_t seven_segment_encoding[10] = {
// 6543210 <-- segment.
0b00111111, // 0
0b00000110, // 1
0b01011011, // 2
0b01001111, // 3
0b01100110, // 4
0b01101101, // 5
0b01111101, // 6
0b00000111, // 7
0b01111111, // 8
0b01101111 // 9
};
const wlm_cairo_7segment_param_t wlm_cairo_7segment_param_6x8 = {
.offset = 0.6,
.width = 1.0,
.hlength = 4.0,
.vlength = 3.0
};
const wlm_cairo_7segment_param_t wlm_cairo_7segment_param_7x10 = {
.offset = 0.6,
.width = 1.0,
.hlength = 5.0,
.vlength = 4.0
};
const wlm_cairo_7segment_param_t wlm_cairo_7segment_param_8x12 = {
.offset = 0.8,
.width = 1.0,
.hlength = 6,
.vlength = 5
};
const wlm_cairo_7segment_param_t wlm_cairo_7segment_param_16x24 = {
.offset = 1.2,
.width = 2.0,
.hlength = 12.0,
.vlength = 10.0
};
/* == Exported methods ===================================================== */
/* ------------------------------------------------------------------------- */
void wlm_cairo_7segment_display_digit(
cairo_t *cairo_ptr,
const wlm_cairo_7segment_param_t *pptr,
uint32_t x,
uint32_t y,
uint32_t color_on,
uint32_t color_off,
uint8_t digit)
{
BS_ASSERT(digit < UINT8_C(10));
uint8_t segments = seven_segment_encoding[digit];
// Vectors spanning up a segment.
bs_vector_2f_t longitudinal = { .x = 1.0, .y = 0.0 };
bs_vector_2f_t lateral = { .x = 0.0, .y = 1.0 };
bs_vector_2f_t origin = BS_VECTOR_2F(
x + pptr->width / 2.0, y - 2 * pptr->vlength - pptr->width / 2.0);
bs_vector_2f_t pos;
cairo_save(cairo_ptr);
// Segment 0.
cairo_set_source_argb8888(
cairo_ptr, segments & (0x01 << 0) ? color_on : color_off);
pos = origin;
draw_segment(cairo_ptr, pos, longitudinal, lateral, pptr, pptr->hlength);
// Segment 1.
cairo_set_source_argb8888(
cairo_ptr, segments & (0x01 << 1) ? color_on : color_off);
pos = bs_vec_add_2f(origin, BS_VECTOR_2F(pptr->hlength, 0));
draw_segment(cairo_ptr, pos, lateral, longitudinal, pptr, pptr->vlength);
// Segment 2.
cairo_set_source_argb8888(
cairo_ptr, segments & (0x01 << 2) ? color_on : color_off);
pos = bs_vec_add_2f(origin, BS_VECTOR_2F(pptr->hlength, pptr->vlength));
draw_segment(cairo_ptr, pos, lateral, longitudinal, pptr, pptr->vlength);
// Segment 3.
cairo_set_source_argb8888(
cairo_ptr, segments & (0x01 << 3) ? color_on : color_off);
pos = bs_vec_add_2f(origin, BS_VECTOR_2F(0, 2 * pptr->vlength));
draw_segment(cairo_ptr, pos, longitudinal, lateral, pptr, pptr->hlength);
// Segment 4.
cairo_set_source_argb8888(
cairo_ptr, segments & (0x01 << 4) ? color_on : color_off);
pos = bs_vec_add_2f(origin, BS_VECTOR_2F(0, pptr->vlength));
draw_segment(cairo_ptr, pos, lateral, longitudinal, pptr, pptr->vlength);
// Segment 5.
cairo_set_source_argb8888(
cairo_ptr, segments & (0x01 << 5) ? color_on : color_off);
pos = origin;
draw_segment(cairo_ptr, pos, lateral, longitudinal, pptr, pptr->vlength);
// Segment 6.
cairo_set_source_argb8888(
cairo_ptr, segments & (0x01 << 6) ? color_on : color_off);
pos = bs_vec_add_2f(origin, BS_VECTOR_2F(0, pptr->vlength));
draw_segment(cairo_ptr, pos, longitudinal, lateral, pptr, pptr->hlength);
cairo_restore(cairo_ptr);
}
/* == Local (static) methods =============================================== */
/* ------------------------------------------------------------------------- */
/**
* Draws a segment, from `origin` along `longitudinal`/`lateral` direction.
*
* A segment spans from the point `origin` along the `longitudinal` vector,
* and expands by (width/2) along `lateral` direction. It will use the current
* cairo source color to fill the segment.
*
```
+---------------------+
/ \
+--+ + + +--+ ^
\ / | width/2
+---------------------+ v
<----> offset <----> offset
<-> width/2 <-> width/2
<-------------------------------> length
```
*
* @param cairo_ptr
* @param origin
* @param longitudinal
* @param lateral
* @param param_ptr
* @param length
*/
void draw_segment(
cairo_t *cairo_ptr,
const bs_vector_2f_t origin,
const bs_vector_2f_t longitudinal,
const bs_vector_2f_t lateral,
const wlm_cairo_7segment_param_t *param_ptr,
const double length)
{
bs_vector_2f_t rel;
rel = bs_vec_mul_2f(param_ptr->offset - param_ptr->width/2, longitudinal);
cairo_move_to(cairo_ptr, origin.x + rel.x, origin.y + rel.y);
rel = bs_vec_add_2f(
bs_vec_mul_2f(param_ptr->offset, longitudinal),
bs_vec_mul_2f(param_ptr->width / 2, lateral));
cairo_line_to(cairo_ptr, origin.x + rel.x, origin.y + rel.y);
rel = bs_vec_add_2f(
bs_vec_mul_2f(length - param_ptr->offset, longitudinal),
bs_vec_mul_2f(param_ptr->width / 2, lateral));
cairo_line_to(cairo_ptr, origin.x + rel.x, origin.y + rel.y);
rel = bs_vec_mul_2f(length - param_ptr->offset + param_ptr->width/2,
longitudinal);
cairo_line_to(cairo_ptr, origin.x + rel.x, origin.y + rel.y);
rel = bs_vec_add_2f(
bs_vec_mul_2f(length - param_ptr->offset, longitudinal),
bs_vec_mul_2f(-param_ptr->width / 2, lateral));
cairo_line_to(cairo_ptr, origin.x + rel.x, origin.y + rel.y);
rel = bs_vec_add_2f(
bs_vec_mul_2f(param_ptr->offset, longitudinal),
bs_vec_mul_2f(-param_ptr->width / 2, lateral));
cairo_line_to(cairo_ptr, origin.x + rel.x, origin.y + rel.y);
rel = bs_vec_mul_2f(param_ptr->offset - param_ptr->width/2, longitudinal);
cairo_line_to(cairo_ptr, origin.x + rel.x, origin.y + rel.y);
cairo_fill(cairo_ptr);
cairo_stroke(cairo_ptr);
}
/* == Unit tests =========================================================== */
static void test_6x8(bs_test_t *test_ptr);
static void test_7x10(bs_test_t *test_ptr);
static void test_16x24(bs_test_t *test_ptr);
const bs_test_case_t wlm_cairo_segment_display_test_cases[] = {
{ 1, "6x8", test_6x8 },
{ 1, "7x10", test_7x10 },
{ 1, "16x24", test_16x24 },
{ 0, NULL, NULL } // sentinel.
};
/* ------------------------------------------------------------------------- */
/** Test for the 6x8-sized digits. */
void test_6x8(bs_test_t *test_ptr)
{
bs_gfxbuf_t *gfxbuf_ptr = bs_gfxbuf_create(60, 8);
if (NULL == gfxbuf_ptr) {
BS_TEST_FAIL(test_ptr, "Failed bs_gfxbuf_create(60, 8)");
return;
}
cairo_t *cairo_ptr = cairo_create_from_bs_gfxbuf(gfxbuf_ptr);
if (NULL == cairo_ptr) {
BS_TEST_FAIL(test_ptr, "Failed cairo_create_from_bs_gfxbuf.");
bs_gfxbuf_destroy(gfxbuf_ptr);
return;
}
for (int i = 0; i < 10; i++) {
wlm_cairo_7segment_display_digit(
cairo_ptr, &wlm_cairo_7segment_param_6x8, i * 6, 8,
0xffc0c0ff, 0xff202040, i);
}
cairo_destroy(cairo_ptr);
BS_TEST_VERIFY_GFXBUF_EQUALS_PNG(
test_ptr, gfxbuf_ptr, "segment_display_6x8.png");
bs_gfxbuf_destroy(gfxbuf_ptr);
}
/* ------------------------------------------------------------------------- */
/** Test for the 7x10-sized digits. */
void test_7x10(bs_test_t *test_ptr)
{
bs_gfxbuf_t *gfxbuf_ptr = bs_gfxbuf_create(70, 10);
if (NULL == gfxbuf_ptr) {
BS_TEST_FAIL(test_ptr, "Failed bs_gfxbuf_create(70, 10)");
return;
}
cairo_t *cairo_ptr = cairo_create_from_bs_gfxbuf(gfxbuf_ptr);
if (NULL == cairo_ptr) {
BS_TEST_FAIL(test_ptr, "Failed cairo_create_from_bs_gfxbuf.");
bs_gfxbuf_destroy(gfxbuf_ptr);
return;
}
for (int i = 0; i < 10; i++) {
wlm_cairo_7segment_display_digit(
cairo_ptr, &wlm_cairo_7segment_param_7x10, i * 7, 10,
0xffc0c0ff, 0xff202040, i);
}
cairo_destroy(cairo_ptr);
BS_TEST_VERIFY_GFXBUF_EQUALS_PNG(
test_ptr, gfxbuf_ptr, "segment_display_7x10.png");
bs_gfxbuf_destroy(gfxbuf_ptr);
}
/* ------------------------------------------------------------------------- */
/** Test for the 16x24-sized digits. */
void test_16x24(bs_test_t *test_ptr)
{
bs_gfxbuf_t *gfxbuf_ptr = bs_gfxbuf_create(160, 24);
if (NULL == gfxbuf_ptr) {
BS_TEST_FAIL(test_ptr, "Failed bs_gfxbuf_create(160, 24)");
return;
}
cairo_t *cairo_ptr = cairo_create_from_bs_gfxbuf(gfxbuf_ptr);
if (NULL == cairo_ptr) {
BS_TEST_FAIL(test_ptr, "Failed cairo_create_from_bs_gfxbuf.");
bs_gfxbuf_destroy(gfxbuf_ptr);
return;
}
for (int i = 0; i < 10; i++) {
wlm_cairo_7segment_display_digit(
cairo_ptr, &wlm_cairo_7segment_param_16x24, i * 16, 24,
0xffc0c0ff, 0xff202040, i);
}
cairo_destroy(cairo_ptr);
BS_TEST_VERIFY_GFXBUF_EQUALS_PNG(
test_ptr, gfxbuf_ptr, "segment_display_16x24.png");
bs_gfxbuf_destroy(gfxbuf_ptr);
}
/* == End of segment_display.c ============================================= */
wlmaker-0.3/apps/primitives/segment_display.h 0000664 0000000 0000000 00000005606 14641446446 0021540 0 ustar 00root root 0000000 0000000 /* ========================================================================= */
/**
* @file segment_display.h
*
* @copyright
* Copyright 2023 Google LLC
*
* 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
*
* https://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.
*/
#ifndef __WLM_SEGMENT_DISPLAY_H__
#define __WLM_SEGMENT_DISPLAY_H__
#include
#include
#include
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
/** Parameters to describe segment properties. */
typedef struct {
/** Offset distance, from origin to start of segment. */
double offset;
/** Full width of the segment, along the lateral direction. */
double width;
/** Length of a horizontal segment, along longitudinal direction. */
double hlength;
/** Length of a vertical segment, along longitudinal direction. */
double vlength;
} wlm_cairo_7segment_param_t;
/** Parameters for a 6x8-pixel-sized 7-segment digit display. */
extern const wlm_cairo_7segment_param_t wlm_cairo_7segment_param_6x8;
/** Parameters for a 7x10-pixel-sized 7-segment digit display. */
extern const wlm_cairo_7segment_param_t wlm_cairo_7segment_param_7x10;
/** Parameters for a 8x12-pixel-sized 7-segment digit display. */
extern const wlm_cairo_7segment_param_t wlm_cairo_7segment_param_8x12;
/** Parameters for a 16x24-pixel-sized 7-segment digit display. */
extern const wlm_cairo_7segment_param_t wlm_cairo_7segment_param_16x24;
/**
* Draws a digit using 7-segment visualization.
*
* @param cairo_ptr The `cairo_t` target to draw the digits to.
* @param param_ptr Visualization parameters for the segments.
* @param x X coordinate of lower left corner.
* @param y Y coordinate of lower left corner.
* @param color_on An ARGB32 value for an illuminated segment.
* @param color_off An ARGB32 value for a non-illuminated segment.
* @param digit Digit to draw. Must be 0 <= digit < 10.
*/
void wlm_cairo_7segment_display_digit(
cairo_t *cairo_ptr,
const wlm_cairo_7segment_param_t *param_ptr,
uint32_t x,
uint32_t y,
uint32_t color_on,
uint32_t color_off,
uint8_t digit);
/** Unit test cases. */
extern const bs_test_case_t wlm_cairo_segment_display_test_cases[];
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif /* __WLM_SEGMENT_DISPLAY_H__ */
/* == End of segment_display.h ============================================= */
wlmaker-0.3/apps/primitives/segment_display_16x24.png 0000664 0000000 0000000 00000001174 14641446446 0022735 0 ustar 00root root 0000000 0000000 PNG
IHDR }V5 bKGD 1IDATh홱j@+4ج\%1IW s66k@7w9?ؓF|p+a?hbAXW f l,Yό8` k gt7ͿK Kd x fq8~3Q~?89<ظY̊oc&ɅRh<