mirror of
https://github.com/hyprwm/hyprpaper.git
synced 2024-12-22 04:19:47 +01:00
initial code commit
This commit is contained in:
parent
496c7ba8ae
commit
05c4b91f95
20 changed files with 1221 additions and 0 deletions
76
CMakeLists.txt
Normal file
76
CMakeLists.txt
Normal file
|
@ -0,0 +1,76 @@
|
|||
cmake_minimum_required(VERSION 3.4)
|
||||
project(hyprpaper
|
||||
DESCRIPTION "A blazing fast wayland wallpaper utility"
|
||||
)
|
||||
|
||||
set(CMAKE_MESSAGE_LOG_LEVEL "STATUS")
|
||||
|
||||
message(STATUS "Configuring hyprpaper!")
|
||||
|
||||
# Get git info
|
||||
# hash and branch
|
||||
execute_process(
|
||||
COMMAND git rev-parse --abbrev-ref HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_BRANCH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
execute_process(
|
||||
COMMAND git rev-parse HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_COMMIT_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
execute_process(
|
||||
COMMAND bash -c "git show ${GIT_COMMIT_HASH} | head -n 5 | tail -n 1"
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_COMMIT_MESSAGE
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
execute_process(
|
||||
COMMAND bash -c "git diff-index --quiet HEAD -- || echo \"dirty\""
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_DIRTY
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
#
|
||||
#
|
||||
|
||||
include_directories(.)
|
||||
add_compile_options(-std=c++20 -DWLR_USE_UNSTABLE )
|
||||
add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value -Wno-missing-field-initializers -Wno-narrowing)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-server wayland-client wayland-cursor wayland-protocols cairo pango pangocairo wlroots libjpeg)
|
||||
|
||||
file(GLOB_RECURSE SRCFILES "src/*.cpp")
|
||||
|
||||
add_executable(hyprpaper ${SRCFILES})
|
||||
|
||||
target_compile_definitions(hyprpaper PRIVATE "-DGIT_COMMIT_HASH=\"${GIT_COMMIT_HASH}\"")
|
||||
target_compile_definitions(hyprpaper PRIVATE "-DGIT_BRANCH=\"${GIT_BRANCH}\"")
|
||||
target_compile_definitions(hyprpaper PRIVATE "-DGIT_COMMIT_MESSAGE=\"${GIT_COMMIT_MESSAGE}\"")
|
||||
target_compile_definitions(hyprpaper PRIVATE "-DGIT_DIRTY=\"${GIT_DIRTY}\"")
|
||||
|
||||
target_link_libraries(hyprpaper rt)
|
||||
|
||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
|
||||
include(CPack)
|
||||
|
||||
target_link_libraries(hyprpaper PkgConfig::deps)
|
||||
|
||||
target_link_libraries(hyprpaper
|
||||
OpenGL
|
||||
GLESv2
|
||||
pthread
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${CMAKE_SOURCE_DIR}/wlr-layer-shell-unstable-v1-protocol.o
|
||||
${CMAKE_SOURCE_DIR}/xdg-shell-protocol.o
|
||||
)
|
||||
|
||||
IF(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg -no-pie -fno-builtin")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pg -no-pie -fno-builtin")
|
||||
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -pg -no-pie -fno-builtin")
|
||||
ENDIF(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG)
|
45
Makefile
Normal file
45
Makefile
Normal file
|
@ -0,0 +1,45 @@
|
|||
PREFIX = /usr/local
|
||||
CFLAGS ?= -g -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-unused-function -Wno-unused-variable -Wno-unused-result -Wdeclaration-after-statement
|
||||
|
||||
CFLAGS += -I. -DWLR_USE_UNSTABLE -std=c99
|
||||
|
||||
WAYLAND_PROTOCOLS=$(shell pkg-config --variable=pkgdatadir wayland-protocols)
|
||||
WAYLAND_SCANNER=$(shell pkg-config --variable=wayland_scanner wayland-scanner)
|
||||
|
||||
PKGS = wlroots wayland-server
|
||||
CFLAGS += $(foreach p,$(PKGS),$(shell pkg-config --cflags $(p)))
|
||||
LDLIBS += $(foreach p,$(PKGS),$(shell pkg-config --libs $(p)))
|
||||
|
||||
wlr-layer-shell-unstable-v1-protocol.h:
|
||||
$(WAYLAND_SCANNER) client-header \
|
||||
protocols/wlr-layer-shell-unstable-v1.xml $@
|
||||
|
||||
wlr-layer-shell-unstable-v1-protocol.c:
|
||||
$(WAYLAND_SCANNER) private-code \
|
||||
protocols/wlr-layer-shell-unstable-v1.xml $@
|
||||
|
||||
wlr-layer-shell-unstable-v1-protocol.o: wlr-layer-shell-unstable-v1-protocol.h
|
||||
|
||||
xdg-shell-protocol.h:
|
||||
$(WAYLAND_SCANNER) client-header \
|
||||
$(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
|
||||
|
||||
xdg-shell-protocol.c:
|
||||
$(WAYLAND_SCANNER) private-code \
|
||||
$(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
|
||||
|
||||
xdg-shell-protocol.o: xdg-shell-protocol.h
|
||||
|
||||
protocols: wlr-layer-shell-unstable-v1-protocol.o xdg-shell-protocol.o
|
||||
|
||||
clear:
|
||||
rm -rf build
|
||||
rm -f *.o *-protocol.h *-protocol.c
|
||||
|
||||
release:
|
||||
mkdir -p build && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Release -H./ -B./build -G Ninja
|
||||
cmake --build ./build --config Release --target all -j 10
|
||||
|
||||
debug:
|
||||
mkdir -p build && cmake --no-warn-unused-cli -DCMAKE_BUILD_TYPE:STRING=Debug -H./ -B./build -G Ninja
|
||||
cmake --build ./build --config Debug --target all -j 10
|
285
protocols/wlr-layer-shell-unstable-v1.xml
Normal file
285
protocols/wlr-layer-shell-unstable-v1.xml
Normal file
|
@ -0,0 +1,285 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<protocol name="wlr_layer_shell_v1_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="1">
|
||||
<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.
|
||||
|
||||
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>
|
||||
</interface>
|
||||
|
||||
<interface name="zwlr_layer_surface_v1" version="1">
|
||||
<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 (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.
|
||||
</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 orthoginal 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 of the surface
|
||||
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 an
|
||||
edge, rather than a corner. The zone is the number of surface-local
|
||||
coordinates from the edge that are considered 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 excluzive 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>
|
||||
|
||||
<request name="set_keyboard_interactivity">
|
||||
<description summary="requests keyboard events">
|
||||
Set to 1 to request that the seat send keyboard events to this layer
|
||||
surface. For layers below the shell surface layer, the seat will use
|
||||
normal focus semantics. For layers above the shell surface layers, the
|
||||
seat will always give exclusive keyboard focus to the top-most layer
|
||||
which has keyboard interactivity set to true.
|
||||
|
||||
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.
|
||||
|
||||
Events is double-buffered, see wl_surface.commit.
|
||||
</description>
|
||||
<arg name="keyboard_interactivity" type="uint"/>
|
||||
</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"/>
|
||||
</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>
|
||||
</interface>
|
||||
</protocol>
|
233
src/Hyprpaper.cpp
Normal file
233
src/Hyprpaper.cpp
Normal file
|
@ -0,0 +1,233 @@
|
|||
#include "Hyprpaper.hpp"
|
||||
|
||||
CHyprpaper::CHyprpaper() { }
|
||||
|
||||
void CHyprpaper::init() {
|
||||
g_pConfigManager = std::make_unique<CConfigManager>();
|
||||
|
||||
m_sDisplay = (wl_display *)wl_display_connect(NULL);
|
||||
|
||||
if (!m_sDisplay) {
|
||||
Debug::log(CRIT, "No wayland compositor running!");
|
||||
exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
preloadAllWallpapersFromConfig();
|
||||
|
||||
// run
|
||||
wl_registry *registry = wl_display_get_registry(m_sDisplay);
|
||||
wl_registry_add_listener(registry, &Events::registryListener, nullptr);
|
||||
|
||||
while (wl_display_dispatch(m_sDisplay) != -1) {
|
||||
recheckAllMonitors();
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
}
|
||||
|
||||
void CHyprpaper::preloadAllWallpapersFromConfig() {
|
||||
for (auto& wp : g_pConfigManager->m_dRequestedPreloads) {
|
||||
m_mWallpaperTargets[wp] = CWallpaperTarget();
|
||||
m_mWallpaperTargets[wp].create(wp);
|
||||
}
|
||||
}
|
||||
|
||||
void CHyprpaper::recheckAllMonitors() {
|
||||
for (auto& m : m_vMonitors) {
|
||||
ensureMonitorHasActiveWallpaper(&m);
|
||||
|
||||
if (m.wantsACK) {
|
||||
m.wantsACK = false;
|
||||
zwlr_layer_surface_v1_ack_configure(m.pLayerSurface, m.configureSerial);
|
||||
}
|
||||
|
||||
if (m.wantsReload) {
|
||||
m.wantsReload = false;
|
||||
renderWallpaperForMonitor(&m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CHyprpaper::ensureMonitorHasActiveWallpaper(SMonitor* pMonitor) {
|
||||
if (!pMonitor->readyForLS)
|
||||
return;
|
||||
|
||||
auto it = m_mMonitorActiveWallpaperTargets.find(pMonitor);
|
||||
|
||||
if (it == m_mMonitorActiveWallpaperTargets.end()) {
|
||||
m_mMonitorActiveWallpaperTargets[pMonitor] = nullptr;
|
||||
it = m_mMonitorActiveWallpaperTargets.find(pMonitor);
|
||||
}
|
||||
|
||||
if (it->second)
|
||||
return; // has
|
||||
|
||||
// create it for thy
|
||||
createLSForMonitor(pMonitor);
|
||||
|
||||
// get the target
|
||||
for (auto&[mon, path1] : m_mMonitorActiveWallpapers) {
|
||||
if (mon == pMonitor->name) {
|
||||
for (auto&[path2, target] : m_mWallpaperTargets) {
|
||||
if (path1 == path2) {
|
||||
it->second = ⌖
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!it->second) {
|
||||
Debug::log(CRIT, "No target for monitor %s!!", pMonitor->name.c_str());
|
||||
exit(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CHyprpaper::createLSForMonitor(SMonitor* pMonitor) {
|
||||
pMonitor->pSurface = wl_compositor_create_surface(m_sCompositor);
|
||||
|
||||
if (!pMonitor->pSurface) {
|
||||
Debug::log(CRIT, "The compositor did not allow hyprpaper a surface!");
|
||||
exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto PINPUTREGION = wl_compositor_create_region(m_sCompositor);
|
||||
|
||||
if (!PINPUTREGION) {
|
||||
Debug::log(CRIT, "The compositor did not allow hyprpaper a region!");
|
||||
exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
wl_surface_set_input_region(pMonitor->pSurface, PINPUTREGION);
|
||||
|
||||
pMonitor->pLayerSurface = zwlr_layer_shell_v1_get_layer_surface(g_pHyprpaper->m_sLayerShell, pMonitor->pSurface, pMonitor->output, ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "hyprpaper");
|
||||
|
||||
if (!pMonitor->pLayerSurface) {
|
||||
Debug::log(CRIT, "The compositor did not allow hyprpaper a layersurface!");
|
||||
exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
zwlr_layer_surface_v1_set_size(pMonitor->pLayerSurface, 0, 0);
|
||||
zwlr_layer_surface_v1_set_anchor(pMonitor->pLayerSurface, ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
|
||||
zwlr_layer_surface_v1_set_exclusive_zone(pMonitor->pLayerSurface, -1);
|
||||
zwlr_layer_surface_v1_add_listener(pMonitor->pLayerSurface, &Events::layersurfaceListener, pMonitor);
|
||||
wl_surface_commit(pMonitor->pSurface);
|
||||
|
||||
wl_region_destroy(PINPUTREGION);
|
||||
}
|
||||
|
||||
bool CHyprpaper::setCloexec(const int& FD) {
|
||||
long flags = fcntl(FD, F_GETFD);
|
||||
if (flags == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fcntl(FD, F_SETFD, flags | FD_CLOEXEC) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CHyprpaper::createPoolFile(size_t size, std::string& name) {
|
||||
const auto XDGRUNTIMEDIR = getenv("XDG_RUNTIME_DIR");
|
||||
if (!XDGRUNTIMEDIR) {
|
||||
Debug::log(CRIT, "XDG_RUNTIME_DIR not set!");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
name = std::string(XDGRUNTIMEDIR) + "/.hyprpaper_XXXXXX";
|
||||
|
||||
const auto FD = mkstemp((char*)name.c_str());
|
||||
if (FD < 0) {
|
||||
Debug::log(CRIT, "createPoolFile: fd < 0");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!setCloexec(FD)) {
|
||||
close(FD);
|
||||
Debug::log(CRIT, "createPoolFile: !setCloexec");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (ftruncate(FD, size) < 0) {
|
||||
close(FD);
|
||||
Debug::log(CRIT, "createPoolFile: ftruncate < 0");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return FD;
|
||||
}
|
||||
|
||||
void CHyprpaper::createBuffer(SPoolBuffer* pBuffer, int32_t w, int32_t h, uint32_t format) {
|
||||
const uint STRIDE = w * 4;
|
||||
const size_t SIZE = STRIDE * h;
|
||||
|
||||
std::string name;
|
||||
const auto FD = createPoolFile(SIZE, name);
|
||||
|
||||
if (FD == -1) {
|
||||
Debug::log(CRIT, "Unable to create pool file!");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const auto DATA = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, FD, 0);
|
||||
const auto POOL = wl_shm_create_pool(g_pHyprpaper->m_sSHM, FD, SIZE);
|
||||
pBuffer->buffer = wl_shm_pool_create_buffer(POOL, 0, w, h, STRIDE, format);
|
||||
wl_shm_pool_destroy(POOL);
|
||||
|
||||
close(FD);
|
||||
|
||||
pBuffer->size = SIZE;
|
||||
pBuffer->data = DATA;
|
||||
pBuffer->surface = cairo_image_surface_create_for_data((unsigned char*)DATA, CAIRO_FORMAT_ARGB32, w, h, STRIDE);
|
||||
pBuffer->cairo = cairo_create(pBuffer->surface);
|
||||
}
|
||||
|
||||
void CHyprpaper::destroyBuffer(SPoolBuffer* pBuffer) {
|
||||
wl_buffer_destroy(pBuffer->buffer);
|
||||
cairo_destroy(pBuffer->cairo);
|
||||
cairo_surface_destroy(pBuffer->surface);
|
||||
munmap(pBuffer->data, pBuffer->size);
|
||||
}
|
||||
|
||||
void CHyprpaper::renderWallpaperForMonitor(SMonitor* pMonitor) {
|
||||
SPoolBuffer buffer;
|
||||
createBuffer(&buffer, pMonitor->size.x * pMonitor->scale, pMonitor->size.y * pMonitor->scale, WL_SHM_FORMAT_ARGB8888);
|
||||
|
||||
const auto PCAIRO = buffer.cairo;
|
||||
cairo_save(PCAIRO);
|
||||
cairo_set_operator(PCAIRO, CAIRO_OPERATOR_CLEAR);
|
||||
cairo_paint(PCAIRO);
|
||||
cairo_restore(PCAIRO);
|
||||
|
||||
// render
|
||||
// get wp
|
||||
const auto PWALLPAPERTARGET = m_mMonitorActiveWallpaperTargets[pMonitor];
|
||||
|
||||
if (!PWALLPAPERTARGET) {
|
||||
Debug::log(CRIT, "wallpaper target null in render??");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
Debug::log(LOG, "Scale: %i", pMonitor->scale);
|
||||
cairo_scale(PCAIRO, pMonitor->scale, pMonitor->scale);
|
||||
cairo_set_source_surface(PCAIRO, PWALLPAPERTARGET->m_pCairoSurface, 0, 0);
|
||||
|
||||
cairo_paint(PCAIRO);
|
||||
cairo_restore(PCAIRO);
|
||||
|
||||
wl_surface_set_buffer_scale(pMonitor->pSurface, pMonitor->scale);
|
||||
wl_surface_attach(pMonitor->pSurface, buffer.buffer, 0, 0);
|
||||
wl_surface_damage_buffer(pMonitor->pSurface, 0, 0, INT32_MAX, INT32_MAX);
|
||||
wl_surface_commit(pMonitor->pSurface);
|
||||
|
||||
// we will not reuse the buffer, so destroy it immediately
|
||||
destroyBuffer(&buffer);
|
||||
}
|
38
src/Hyprpaper.hpp
Normal file
38
src/Hyprpaper.hpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include "defines.hpp"
|
||||
#include "config/ConfigManager.hpp"
|
||||
#include "render/WallpaperTarget.hpp"
|
||||
#include "helpers/Monitor.hpp"
|
||||
#include "events/Events.hpp"
|
||||
#include "helpers/PoolBuffer.hpp"
|
||||
|
||||
class CHyprpaper {
|
||||
public:
|
||||
// important
|
||||
wl_display* m_sDisplay;
|
||||
wl_compositor* m_sCompositor;
|
||||
wl_shm* m_sSHM;
|
||||
zwlr_layer_shell_v1* m_sLayerShell;
|
||||
|
||||
// init the utility
|
||||
CHyprpaper();
|
||||
void init();
|
||||
|
||||
std::unordered_map<std::string, CWallpaperTarget> m_mWallpaperTargets;
|
||||
std::unordered_map<std::string, std::string> m_mMonitorActiveWallpapers;
|
||||
std::unordered_map<SMonitor*, CWallpaperTarget*> m_mMonitorActiveWallpaperTargets;
|
||||
std::vector<SMonitor> m_vMonitors;
|
||||
|
||||
void preloadAllWallpapersFromConfig();
|
||||
void recheckAllMonitors();
|
||||
void ensureMonitorHasActiveWallpaper(SMonitor*);
|
||||
void createLSForMonitor(SMonitor*);
|
||||
void renderWallpaperForMonitor(SMonitor*);
|
||||
void createBuffer(SPoolBuffer*, int32_t, int32_t, uint32_t);
|
||||
void destroyBuffer(SPoolBuffer*);
|
||||
int createPoolFile(size_t, std::string&);
|
||||
bool setCloexec(const int&);
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CHyprpaper> g_pHyprpaper;
|
141
src/config/ConfigManager.cpp
Normal file
141
src/config/ConfigManager.cpp
Normal file
|
@ -0,0 +1,141 @@
|
|||
#include "ConfigManager.hpp"
|
||||
#include "../Hyprpaper.hpp"
|
||||
|
||||
CConfigManager::CConfigManager() {
|
||||
// init the entire thing
|
||||
|
||||
const char* const ENVHOME = getenv("HOME");
|
||||
const std::string CONFIGPATH = ENVHOME + (std::string) "/.config/hypr/hyprpaper.conf";
|
||||
|
||||
std::ifstream ifs;
|
||||
ifs.open(CONFIGPATH);
|
||||
|
||||
if (!ifs.good()) {
|
||||
Debug::log(CRIT, "Hyprpaper was not provided a config!");
|
||||
exit(1);
|
||||
return; //jic
|
||||
}
|
||||
|
||||
std::string line = "";
|
||||
int linenum = 1;
|
||||
if (ifs.is_open()) {
|
||||
while (std::getline(ifs, line)) {
|
||||
// Read line by line.
|
||||
try {
|
||||
parseLine(line);
|
||||
} catch (...) {
|
||||
Debug::log(ERR, "Error reading line from config. Line:");
|
||||
Debug::log(NONE, "%s", line.c_str());
|
||||
|
||||
parseError += "Config error at line " + std::to_string(linenum) + ": Line parsing error.";
|
||||
}
|
||||
|
||||
if (parseError != "" && parseError.find("Config error at line") != 0) {
|
||||
parseError = "Config error at line " + std::to_string(linenum) + ": " + parseError;
|
||||
}
|
||||
|
||||
++linenum;
|
||||
}
|
||||
|
||||
ifs.close();
|
||||
}
|
||||
|
||||
if (parseError != "") {
|
||||
Debug::log(CRIT, "Exiting because of config parse errors!\n%s", parseError.c_str());
|
||||
exit(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::string CConfigManager::removeBeginEndSpacesTabs(std::string str) {
|
||||
while (str[0] == ' ' || str[0] == '\t') {
|
||||
str = str.substr(1);
|
||||
}
|
||||
|
||||
while (str.length() != 0 && (str[str.length() - 1] == ' ' || str[str.length() - 1] == '\t')) {
|
||||
str = str.substr(0, str.length() - 1);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
void CConfigManager::parseLine(std::string& line) {
|
||||
// first check if its not a comment
|
||||
const auto COMMENTSTART = line.find_first_of('#');
|
||||
if (COMMENTSTART == 0)
|
||||
return;
|
||||
|
||||
// now, cut the comment off
|
||||
if (COMMENTSTART != std::string::npos)
|
||||
line = line.substr(0, COMMENTSTART);
|
||||
|
||||
// remove shit at the beginning
|
||||
while (line[0] == ' ' || line[0] == '\t') {
|
||||
line = line.substr(1);
|
||||
}
|
||||
|
||||
// And parse
|
||||
// check if command
|
||||
const auto EQUALSPLACE = line.find_first_of('=');
|
||||
|
||||
if (EQUALSPLACE == std::string::npos)
|
||||
return;
|
||||
|
||||
const auto COMMAND = removeBeginEndSpacesTabs(line.substr(0, EQUALSPLACE));
|
||||
const auto VALUE = removeBeginEndSpacesTabs(line.substr(EQUALSPLACE + 1));
|
||||
//
|
||||
|
||||
parseKeyword(COMMAND, VALUE);
|
||||
}
|
||||
|
||||
void CConfigManager::parseKeyword(const std::string& COMMAND, const std::string& VALUE) {
|
||||
if (COMMAND == "wallpaper")
|
||||
handleWallpaper(COMMAND, VALUE);
|
||||
else if (COMMAND == "preload")
|
||||
handlePreload(COMMAND, VALUE);
|
||||
else
|
||||
parseError = "unknown keyword " + COMMAND;
|
||||
}
|
||||
|
||||
void CConfigManager::handleWallpaper(const std::string& COMMAND, const std::string& VALUE) {
|
||||
if (VALUE.find_first_of(',') == std::string::npos) {
|
||||
parseError = "wallpaper failed (syntax)";
|
||||
return;
|
||||
}
|
||||
|
||||
auto MONITOR = VALUE.substr(0, VALUE.find_first_of(','));
|
||||
auto WALLPAPER = VALUE.substr(VALUE.find_first_of(',') + 1);
|
||||
|
||||
if (WALLPAPER[0] == '~') {
|
||||
static const char* const ENVHOME = getenv("HOME");
|
||||
WALLPAPER = std::string(ENVHOME) + WALLPAPER.substr(1);
|
||||
}
|
||||
|
||||
if (!std::filesystem::exists(WALLPAPER)) {
|
||||
parseError = "wallpaper failed (no such file)";
|
||||
return;
|
||||
}
|
||||
|
||||
if (std::find(m_dRequestedPreloads.begin(), m_dRequestedPreloads.end(), WALLPAPER) == m_dRequestedPreloads.end()) {
|
||||
parseError = "wallpaper failed (not preloaded)";
|
||||
return;
|
||||
}
|
||||
|
||||
g_pHyprpaper->m_mMonitorActiveWallpapers[MONITOR] = WALLPAPER;
|
||||
}
|
||||
|
||||
void CConfigManager::handlePreload(const std::string& COMMAND, const std::string& VALUE) {
|
||||
auto WALLPAPER = VALUE;
|
||||
|
||||
if (WALLPAPER[0] == '~') {
|
||||
static const char* const ENVHOME = getenv("HOME");
|
||||
WALLPAPER = std::string(ENVHOME) + WALLPAPER.substr(1);
|
||||
}
|
||||
|
||||
if (!std::filesystem::exists(WALLPAPER)) {
|
||||
parseError = "preload failed (no such file)";
|
||||
return;
|
||||
}
|
||||
|
||||
m_dRequestedPreloads.emplace_back(WALLPAPER);
|
||||
}
|
22
src/config/ConfigManager.hpp
Normal file
22
src/config/ConfigManager.hpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
#include "../defines.hpp"
|
||||
|
||||
class CConfigManager {
|
||||
public:
|
||||
// gets all the data from the config
|
||||
CConfigManager();
|
||||
|
||||
std::deque<std::string> m_dRequestedPreloads;
|
||||
|
||||
private:
|
||||
std::string parseError = "";
|
||||
|
||||
void parseLine(std::string&);
|
||||
std::string removeBeginEndSpacesTabs(std::string in);
|
||||
void parseKeyword(const std::string&, const std::string&);
|
||||
|
||||
void handleWallpaper(const std::string&, const std::string&);
|
||||
void handlePreload(const std::string&, const std::string&);
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CConfigManager> g_pConfigManager;
|
60
src/debug/Log.cpp
Normal file
60
src/debug/Log.cpp
Normal file
|
@ -0,0 +1,60 @@
|
|||
#include "Log.hpp"
|
||||
#include "../includes.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
void Debug::log(LogLevel level, const char* fmt, ...) {
|
||||
|
||||
std::string levelstr = "";
|
||||
|
||||
switch (level) {
|
||||
case LOG:
|
||||
levelstr = "[LOG] ";
|
||||
break;
|
||||
case WARN:
|
||||
levelstr = "[WARN] ";
|
||||
break;
|
||||
case ERR:
|
||||
levelstr = "[ERR] ";
|
||||
break;
|
||||
case CRIT:
|
||||
levelstr = "[CRITICAL] ";
|
||||
break;
|
||||
case INFO:
|
||||
levelstr = "[INFO] ";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
char buf[LOGMESSAGESIZE] = "";
|
||||
char* outputStr;
|
||||
int logLen;
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
logLen = vsnprintf(buf, sizeof buf, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if ((long unsigned int)logLen < sizeof buf) {
|
||||
outputStr = strdup(buf);
|
||||
} else {
|
||||
outputStr = (char*)malloc(logLen + 1);
|
||||
|
||||
if (!outputStr) {
|
||||
printf("CRITICAL: Cannot alloc size %d for log! (Out of memory?)", logLen + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
va_start(args, fmt);
|
||||
vsnprintf(outputStr, logLen + 1U, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// hyprpaper only logs to stdout
|
||||
std::cout << levelstr << outputStr << "\n";
|
||||
|
||||
// free the log
|
||||
free(outputStr);
|
||||
}
|
17
src/debug/Log.hpp
Normal file
17
src/debug/Log.hpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
|
||||
#define LOGMESSAGESIZE 1024
|
||||
|
||||
enum LogLevel {
|
||||
NONE = -1,
|
||||
LOG = 0,
|
||||
WARN,
|
||||
ERR,
|
||||
CRIT,
|
||||
INFO
|
||||
};
|
||||
|
||||
namespace Debug {
|
||||
void log(LogLevel level, const char* fmt, ...);
|
||||
};
|
19
src/defines.hpp
Normal file
19
src/defines.hpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "includes.hpp"
|
||||
#include "debug/Log.hpp"
|
||||
#include "helpers/Vector2D.hpp"
|
||||
|
||||
// git stuff
|
||||
#ifndef GIT_COMMIT_HASH
|
||||
#define GIT_COMMIT_HASH "?"
|
||||
#endif
|
||||
#ifndef GIT_BRANCH
|
||||
#define GIT_BRANCH "?"
|
||||
#endif
|
||||
#ifndef GIT_COMMIT_MESSAGE
|
||||
#define GIT_COMMIT_MESSAGE "?"
|
||||
#endif
|
||||
#ifndef GIT_DIRTY
|
||||
#define GIT_DIRTY "?"
|
||||
#endif
|
65
src/events/Events.cpp
Normal file
65
src/events/Events.cpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
#include "Events.hpp"
|
||||
#include "../Hyprpaper.hpp"
|
||||
|
||||
void Events::geometry(void *data, wl_output *output, int32_t x, int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel, const char *make, const char *model, int32_t transform) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
void Events::mode(void *data, wl_output *output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
void Events::done(void *data, wl_output *wl_output) {
|
||||
const auto PMONITOR = (SMonitor*)data;
|
||||
|
||||
PMONITOR->readyForLS = true;
|
||||
|
||||
g_pHyprpaper->recheckAllMonitors();
|
||||
}
|
||||
|
||||
void Events::scale(void *data, wl_output *wl_output, int32_t scale) {
|
||||
const auto PMONITOR = (SMonitor*)data;
|
||||
|
||||
PMONITOR->scale = scale;
|
||||
}
|
||||
|
||||
void Events::name(void *data, wl_output *wl_output, const char *name) {
|
||||
const auto PMONITOR = (SMonitor*)data;
|
||||
|
||||
PMONITOR->name = std::string(name);
|
||||
}
|
||||
|
||||
void Events::description(void *data, wl_output *wl_output, const char *description) {
|
||||
// i do not care
|
||||
}
|
||||
|
||||
void Events::ls_configure(void *data, zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t width, uint32_t height) {
|
||||
const auto PMONITOR = (SMonitor *)data;
|
||||
|
||||
PMONITOR->size = Vector2D(width, height);
|
||||
PMONITOR->wantsReload = true;
|
||||
PMONITOR->configureSerial = serial;
|
||||
PMONITOR->wantsACK = true;
|
||||
|
||||
Debug::log(LOG, "configure for %s", PMONITOR->name.c_str());
|
||||
}
|
||||
|
||||
void Events::handleGlobal(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) {
|
||||
if (strcmp(interface, wl_compositor_interface.name) == 0) {
|
||||
g_pHyprpaper->m_sCompositor = (wl_compositor *)wl_registry_bind(registry, name, &wl_compositor_interface, 4);
|
||||
} else if (strcmp(interface, wl_shm_interface.name) == 0) {
|
||||
g_pHyprpaper->m_sSHM = (wl_shm *)wl_registry_bind(registry, name, &wl_shm_interface, 1);
|
||||
} else if (strcmp(interface, wl_output_interface.name) == 0) {
|
||||
const auto PMONITOR = &g_pHyprpaper->m_vMonitors.emplace_back();
|
||||
PMONITOR->wayland_name = name;
|
||||
PMONITOR->output = (wl_output *)wl_registry_bind(registry, name, &wl_output_interface, 4);
|
||||
wl_output_add_listener(PMONITOR->output, &Events::outputListener, PMONITOR);
|
||||
} else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
|
||||
g_pHyprpaper->m_sLayerShell = (zwlr_layer_shell_v1 *)wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void Events::handleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name) {
|
||||
// todo
|
||||
}
|
||||
|
29
src/events/Events.hpp
Normal file
29
src/events/Events.hpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include "../defines.hpp"
|
||||
|
||||
namespace Events {
|
||||
void geometry(void *data, wl_output *output, int32_t x, int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel, const char *make, const char *model, int32_t transform);
|
||||
|
||||
void mode(void *data, wl_output *output, uint32_t flags, int32_t width, int32_t height, int32_t refresh);
|
||||
|
||||
void done(void *data, wl_output *wl_output);
|
||||
|
||||
void scale(void *data, wl_output *wl_output, int32_t scale);
|
||||
|
||||
void name(void *data, wl_output *wl_output, const char *name);
|
||||
|
||||
void description(void *data, wl_output *wl_output, const char *description);
|
||||
|
||||
void ls_configure(void *data, zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t width, uint32_t height);
|
||||
|
||||
void handleGlobal(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version);
|
||||
|
||||
void handleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name);
|
||||
|
||||
inline const wl_output_listener outputListener = {.geometry = geometry, .mode = mode, .done = done, .scale = scale, .name = name, .description = description};
|
||||
|
||||
inline const zwlr_layer_surface_v1_listener layersurfaceListener = { .configure = ls_configure };
|
||||
|
||||
inline const struct wl_registry_listener registryListener = { .global = handleGlobal, .global_remove = handleGlobalRemove };
|
||||
};
|
20
src/helpers/Monitor.hpp
Normal file
20
src/helpers/Monitor.hpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "../defines.hpp"
|
||||
|
||||
struct SMonitor {
|
||||
std::string name = "";
|
||||
wl_output* output = nullptr;
|
||||
uint32_t wayland_name = 0;
|
||||
Vector2D size;
|
||||
int scale;
|
||||
|
||||
bool readyForLS = false;
|
||||
|
||||
zwlr_layer_surface_v1* pLayerSurface = nullptr;
|
||||
wl_surface* pSurface = nullptr;
|
||||
uint32_t configureSerial = 0;
|
||||
|
||||
bool wantsReload = false;
|
||||
bool wantsACK = false;
|
||||
};
|
11
src/helpers/PoolBuffer.hpp
Normal file
11
src/helpers/PoolBuffer.hpp
Normal file
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include "../defines.hpp"
|
||||
|
||||
struct SPoolBuffer {
|
||||
wl_buffer* buffer;
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cairo;
|
||||
void* data;
|
||||
size_t size;
|
||||
};
|
23
src/helpers/Vector2D.cpp
Normal file
23
src/helpers/Vector2D.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
#include "Vector2D.hpp"
|
||||
|
||||
Vector2D::Vector2D(double xx, double yy) {
|
||||
x = xx;
|
||||
y = yy;
|
||||
}
|
||||
|
||||
Vector2D::Vector2D() { x = 0; y = 0; }
|
||||
Vector2D::~Vector2D() {}
|
||||
|
||||
double Vector2D::normalize() {
|
||||
// get max abs
|
||||
const auto max = abs(x) > abs(y) ? abs(x) : abs(y);
|
||||
|
||||
x /= max;
|
||||
y /= max;
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
Vector2D Vector2D::floor() {
|
||||
return Vector2D((int)x, (int)y);
|
||||
}
|
39
src/helpers/Vector2D.hpp
Normal file
39
src/helpers/Vector2D.hpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include <math.h>
|
||||
|
||||
class Vector2D {
|
||||
public:
|
||||
Vector2D(double, double);
|
||||
Vector2D();
|
||||
~Vector2D();
|
||||
|
||||
double x = 0;
|
||||
double y = 0;
|
||||
|
||||
// returns the scale
|
||||
double normalize();
|
||||
|
||||
Vector2D operator+(const Vector2D a) const {
|
||||
return Vector2D(this->x + a.x, this->y + a.y);
|
||||
}
|
||||
Vector2D operator-(const Vector2D a) const {
|
||||
return Vector2D(this->x - a.x, this->y - a.y);
|
||||
}
|
||||
Vector2D operator*(const float a) const {
|
||||
return Vector2D(this->x * a, this->y * a);
|
||||
}
|
||||
Vector2D operator/(const float a) const {
|
||||
return Vector2D(this->x / a, this->y / a);
|
||||
}
|
||||
|
||||
bool operator==(const Vector2D& a) const {
|
||||
return a.x == x && a.y == y;
|
||||
}
|
||||
|
||||
bool operator!=(const Vector2D& a) const {
|
||||
return a.x != x || a.y != y;
|
||||
}
|
||||
|
||||
Vector2D floor();
|
||||
};
|
44
src/includes.hpp
Normal file
44
src/includes.hpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <cmath>
|
||||
#include <math.h>
|
||||
|
||||
#define class _class
|
||||
#define namespace _namespace
|
||||
#define static
|
||||
|
||||
extern "C" {
|
||||
#include "wlr-layer-shell-unstable-v1-protocol.h"
|
||||
#include "xdg-shell-protocol.h"
|
||||
#include <wayland-client.h>
|
||||
}
|
||||
|
||||
#undef class
|
||||
#undef namespace
|
||||
#undef static
|
||||
|
||||
#include <GLES3/gl32.h>
|
||||
#include <GLES3/gl3ext.h>
|
||||
#include <assert.h>
|
||||
#include <cairo.h>
|
||||
#include <cairo/cairo.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
13
src/main.cpp
Normal file
13
src/main.cpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#include <iostream>
|
||||
#include "defines.hpp"
|
||||
#include "Hyprpaper.hpp"
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
Debug::log(LOG, "Welcome to hyprpaper!\nbuilt from commit %s (%s)", GIT_COMMIT_HASH, GIT_COMMIT_MESSAGE);
|
||||
|
||||
// starts
|
||||
g_pHyprpaper = std::make_unique<CHyprpaper>();
|
||||
g_pHyprpaper->init();
|
||||
|
||||
return 0;
|
||||
}
|
25
src/render/WallpaperTarget.cpp
Normal file
25
src/render/WallpaperTarget.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include "WallpaperTarget.hpp"
|
||||
|
||||
void CWallpaperTarget::create(const std::string& path) {
|
||||
m_szPath = path;
|
||||
|
||||
cairo_surface_t* CAIROSURFACE = nullptr;
|
||||
if (path.find(".png") == path.length() - 4) {
|
||||
CAIROSURFACE = cairo_image_surface_create_from_png(path.c_str());
|
||||
} else if (path.find(".jpg") == path.length() - 4 || path.find(".jpeg") == path.length() - 5) {
|
||||
Debug::log(ERR, ".jpg images are not yet supported! :(");
|
||||
exit(1);
|
||||
return;
|
||||
} else {
|
||||
Debug::log(CRIT, "unrecognized image %s", path.c_str());
|
||||
exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cairo_surface_status(CAIROSURFACE) != CAIRO_STATUS_SUCCESS) {
|
||||
Debug::log(CRIT, "Failed to read image %s because of:\n%s", path.c_str(), cairo_status_to_string(cairo_surface_status(CAIROSURFACE)));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
m_pCairoSurface = CAIROSURFACE;
|
||||
}
|
16
src/render/WallpaperTarget.hpp
Normal file
16
src/render/WallpaperTarget.hpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "../defines.hpp"
|
||||
|
||||
class CWallpaperTarget {
|
||||
public:
|
||||
|
||||
void create(const std::string& path);
|
||||
void render();
|
||||
|
||||
std::string m_szPath;
|
||||
|
||||
Vector2D m_vSize;
|
||||
|
||||
cairo_surface_t* m_pCairoSurface;
|
||||
};
|
Loading…
Reference in a new issue