From 7d18b4816e85cf82dc9e38a026594528f795c894 Mon Sep 17 00:00:00 2001
From: vaxerski <43317083+vaxerski@users.noreply.github.com>
Date: Fri, 2 Sep 2022 18:06:00 +0200
Subject: [PATCH] initial commit
---
.gitignore | 23 ++
CMakeLists.txt | 78 ++++++
Makefile | 60 +++++
README.md | 13 +
protocols/wlr-layer-shell-unstable-v1.xml | 285 ++++++++++++++++++++
protocols/wlr-screencopy-unstable-v1.xml | 179 +++++++++++++
src/debug/Log.cpp | 60 +++++
src/debug/Log.hpp | 17 ++
src/defines.hpp | 20 ++
src/events/Events.cpp | 173 ++++++++++++
src/events/Events.hpp | 63 +++++
src/helpers/LayerSurface.cpp | 40 +++
src/helpers/LayerSurface.hpp | 36 +++
src/helpers/Monitor.hpp | 15 ++
src/helpers/PoolBuffer.hpp | 18 ++
src/helpers/Vector2D.cpp | 23 ++
src/helpers/Vector2D.hpp | 39 +++
src/hyprpicker.cpp | 308 ++++++++++++++++++++++
src/hyprpicker.hpp | 53 ++++
src/includes.hpp | 46 ++++
src/main.cpp | 10 +
21 files changed, 1559 insertions(+)
create mode 100644 .gitignore
create mode 100644 CMakeLists.txt
create mode 100644 Makefile
create mode 100644 README.md
create mode 100644 protocols/wlr-layer-shell-unstable-v1.xml
create mode 100644 protocols/wlr-screencopy-unstable-v1.xml
create mode 100644 src/debug/Log.cpp
create mode 100644 src/debug/Log.hpp
create mode 100644 src/defines.hpp
create mode 100644 src/events/Events.cpp
create mode 100644 src/events/Events.hpp
create mode 100644 src/helpers/LayerSurface.cpp
create mode 100644 src/helpers/LayerSurface.hpp
create mode 100644 src/helpers/Monitor.hpp
create mode 100644 src/helpers/PoolBuffer.hpp
create mode 100644 src/helpers/Vector2D.cpp
create mode 100644 src/helpers/Vector2D.hpp
create mode 100644 src/hyprpicker.cpp
create mode 100644 src/hyprpicker.hpp
create mode 100644 src/includes.hpp
create mode 100644 src/main.cpp
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..47c137e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,23 @@
+CMakeLists.txt.user
+CMakeCache.txt
+CMakeFiles
+CMakeScripts
+Testing
+cmake_install.cmake
+install_manifest.txt
+compile_commands.json
+CTestTestfile.cmake
+_deps
+
+build/
+result
+/.vscode/
+
+*.o
+*-protocol.c
+*-protocol.h
+.ccls-cache
+
+gmon.out
+*.out
+*.tar.gz
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..f93f2ce
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,78 @@
+cmake_minimum_required(VERSION 3.4)
+project(hyprpicker
+ DESCRIPTION "A blazing fast wayland wallpaper utility"
+)
+
+set(CMAKE_MESSAGE_LOG_LEVEL "STATUS")
+
+message(STATUS "Configuring hyprpicker!")
+
+# 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++23 -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-client wayland-protocols cairo pango pangocairo libjpeg)
+
+file(GLOB_RECURSE SRCFILES "src/*.cpp")
+
+add_executable(hyprpicker ${SRCFILES})
+
+target_compile_definitions(hyprpicker PRIVATE "-DGIT_COMMIT_HASH=\"${GIT_COMMIT_HASH}\"")
+target_compile_definitions(hyprpicker PRIVATE "-DGIT_BRANCH=\"${GIT_BRANCH}\"")
+target_compile_definitions(hyprpicker PRIVATE "-DGIT_COMMIT_MESSAGE=\"${GIT_COMMIT_MESSAGE}\"")
+target_compile_definitions(hyprpicker PRIVATE "-DGIT_DIRTY=\"${GIT_DIRTY}\"")
+
+target_link_libraries(hyprpicker rt)
+
+set(CPACK_PROJECT_NAME ${PROJECT_NAME})
+set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
+include(CPack)
+
+target_link_libraries(hyprpicker PkgConfig::deps)
+
+target_link_libraries(hyprpicker
+ OpenGL
+ GLESv2
+ pthread
+ ${CMAKE_THREAD_LIBS_INIT}
+ ${CMAKE_SOURCE_DIR}/wlr-layer-shell-unstable-v1-protocol.o
+ ${CMAKE_SOURCE_DIR}/wlr-screencopy-unstable-v1-protocol.o
+ ${CMAKE_SOURCE_DIR}/xdg-shell-protocol.o
+ wayland-cursor
+)
+
+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)
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..7ff7e70
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,60 @@
+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
+
+wlr-screencopy-unstable-v1-protocol.h:
+ $(WAYLAND_SCANNER) client-header \
+ protocols/wlr-screencopy-unstable-v1.xml $@
+
+wlr-screencopy-unstable-v1-protocol.c:
+ $(WAYLAND_SCANNER) private-code \
+ protocols/wlr-screencopy-unstable-v1.xml $@
+
+wlr-screencopy-unstable-v1-protocol.o: wlr-screencopy-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 wlr-screencopy-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
+
+all:
+ make clear
+ make protocols
+ make release
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..046ddd7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,13 @@
+# hyprpicker
+
+A wlroots-compatible Wayland color picker that does not suck.
+
+# Usage
+
+launch it. Click. That's it.
+
+# Building
+
+`make all`
+
+the output binary is in `./build/hyprpicker`
\ No newline at end of file
diff --git a/protocols/wlr-layer-shell-unstable-v1.xml b/protocols/wlr-layer-shell-unstable-v1.xml
new file mode 100644
index 0000000..f29eb87
--- /dev/null
+++ b/protocols/wlr-layer-shell-unstable-v1.xml
@@ -0,0 +1,285 @@
+
+
+
+ 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.
+
+
+
+
+ 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.
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+ This request destroys the layer surface.
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/protocols/wlr-screencopy-unstable-v1.xml b/protocols/wlr-screencopy-unstable-v1.xml
new file mode 100644
index 0000000..a7a2d17
--- /dev/null
+++ b/protocols/wlr-screencopy-unstable-v1.xml
@@ -0,0 +1,179 @@
+
+
+
+ Copyright © 2018 Simon Ser
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+
+
+ This protocol allows clients to ask the compositor to copy part of the
+ screen content to a client buffer.
+
+ Warning! The protocol described in this file is experimental and
+ backward incompatible changes may be made. Backward compatible changes
+ may be added together with the corresponding interface version bump.
+ Backward incompatible changes are done by bumping the version number in
+ the protocol and interface names and resetting the interface version.
+ Once the protocol is to be declared stable, the 'z' prefix and the
+ version number in the protocol and interface names are removed and the
+ interface version number is reset.
+
+
+
+
+ This object is a manager which offers requests to start capturing from a
+ source.
+
+
+
+
+ Capture the next frame of an entire output.
+
+
+
+
+
+
+
+
+ Capture the next frame of an output's region.
+
+ The region is given in output logical coordinates, see
+ xdg_output.logical_size. The region will be clipped to the output's
+ extents.
+
+
+
+
+
+
+
+
+
+
+
+
+ All objects created by the manager will still remain valid, until their
+ appropriate destroy request has been called.
+
+
+
+
+
+
+ This object represents a single frame.
+
+ When created, a "buffer" event will be sent. The client will then be able
+ to send a "copy" request. If the capture is successful, the compositor
+ will send a "flags" followed by a "ready" event.
+
+ If the capture failed, the "failed" event is sent. This can happen anytime
+ before the "ready" event.
+
+ Once either a "ready" or a "failed" event is received, the client should
+ destroy the frame.
+
+
+
+
+ Provides information about the frame's buffer. This event is sent once
+ as soon as the frame is created.
+
+ The client should then create a buffer with the provided attributes, and
+ send a "copy" request.
+
+
+
+
+
+
+
+
+
+ Copy the frame to the supplied buffer. The buffer must have a the
+ correct size, see zwlr_screencopy_frame_v1.buffer. The buffer needs to
+ have a supported format.
+
+ If the frame is successfully copied, a "flags" and a "ready" events are
+ sent. Otherwise, a "failed" event is sent.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Provides flags about the frame. This event is sent once before the
+ "ready" event.
+
+
+
+
+
+
+ Called as soon as the frame is copied, indicating it is available
+ for reading. This event includes the time at which presentation happened
+ at.
+
+ The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples,
+ each component being an unsigned 32-bit value. Whole seconds are in
+ tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo,
+ and the additional fractional part in tv_nsec as nanoseconds. Hence,
+ for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part
+ may have an arbitrary offset at start.
+
+ After receiving this event, the client should destroy the object.
+
+
+
+
+
+
+
+
+ This event indicates that the attempted frame copy has failed.
+
+ After receiving this event, the client should destroy the object.
+
+
+
+
+
+ Destroys the frame. This request can be sent at any time by the client.
+
+
+
+
diff --git a/src/debug/Log.cpp b/src/debug/Log.cpp
new file mode 100644
index 0000000..3f639e0
--- /dev/null
+++ b/src/debug/Log.cpp
@@ -0,0 +1,60 @@
+#include "Log.hpp"
+
+#include
+#include
+
+#include "../includes.hpp"
+
+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);
+}
diff --git a/src/debug/Log.hpp b/src/debug/Log.hpp
new file mode 100644
index 0000000..8948086
--- /dev/null
+++ b/src/debug/Log.hpp
@@ -0,0 +1,17 @@
+#pragma once
+#include
+
+#define LOGMESSAGESIZE 1024
+
+enum LogLevel {
+ NONE = -1,
+ LOG = 0,
+ WARN,
+ ERR,
+ CRIT,
+ INFO
+};
+
+namespace Debug {
+ void log(LogLevel level, const char* fmt, ...);
+};
\ No newline at end of file
diff --git a/src/defines.hpp b/src/defines.hpp
new file mode 100644
index 0000000..f8516fc
--- /dev/null
+++ b/src/defines.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "debug/Log.hpp"
+#include "helpers/Vector2D.hpp"
+#include "includes.hpp"
+#include "helpers/Monitor.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
\ No newline at end of file
diff --git a/src/events/Events.cpp b/src/events/Events.cpp
new file mode 100644
index 0000000..428d9e7
--- /dev/null
+++ b/src/events/Events.cpp
@@ -0,0 +1,173 @@
+#include "Events.hpp"
+#include "../hyprpicker.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->ready = true;
+}
+
+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 = 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 PLAYERSURFACE = (CLayerSurface *)data;
+
+ PLAYERSURFACE->m_pMonitor->size = Vector2D(width, height);
+ PLAYERSURFACE->ACKSerial = serial;
+ PLAYERSURFACE->wantsACK = true;
+ PLAYERSURFACE->working = true;
+
+ g_pHyprpicker->recheckACK();
+}
+
+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_pHyprpicker->m_pCompositor = (wl_compositor *)wl_registry_bind(registry, name, &wl_compositor_interface, 4);
+ } else if (strcmp(interface, wl_shm_interface.name) == 0) {
+ g_pHyprpicker->m_pWLSHM = (wl_shm *)wl_registry_bind(registry, name, &wl_shm_interface, 1);
+ } else if (strcmp(interface, wl_output_interface.name) == 0) {
+ g_pHyprpicker->m_mtTickMutex.lock();
+
+ const auto PMONITOR = g_pHyprpicker->m_vMonitors.emplace_back(std::make_unique()).get();
+ PMONITOR->wayland_name = name;
+ PMONITOR->name = "";
+ PMONITOR->output = (wl_output *)wl_registry_bind(registry, name, &wl_output_interface, 4);
+ wl_output_add_listener(PMONITOR->output, &Events::outputListener, PMONITOR);
+
+ g_pHyprpicker->m_mtTickMutex.unlock();
+ } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) {
+ g_pHyprpicker->m_pLayerShell = (zwlr_layer_shell_v1 *)wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1);
+ } else if (strcmp(interface, wl_seat_interface.name) == 0) {
+ g_pHyprpicker->createSeat((wl_seat*)wl_registry_bind(registry, name, &wl_seat_interface, 1));
+ } else if (strcmp(interface, zwlr_screencopy_manager_v1_interface.name) == 0) {
+ g_pHyprpicker->m_pSCMgr = (zwlr_screencopy_manager_v1*)wl_registry_bind(registry, name, &zwlr_screencopy_manager_v1_interface, 1);
+ }
+}
+
+void Events::handleGlobalRemove(void *data, struct wl_registry *registry, uint32_t name) {
+ // todo
+}
+
+void Events::handleCapabilities(void *data, wl_seat *wl_seat, uint32_t capabilities) {
+ if (capabilities & WL_SEAT_CAPABILITY_POINTER) {
+ wl_pointer_add_listener(wl_seat_get_pointer(wl_seat), &pointerListener, wl_seat);
+ } else {
+ Debug::log(CRIT, "Hyprpicker cannot work without a pointer!");
+ g_pHyprpicker->finish(1);
+ }
+}
+
+void Events::handlePointerEnter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) {
+ for (auto& ls : g_pHyprpicker->m_vLayerSurfaces) {
+ if (ls->pSurface == surface) {
+ g_pHyprpicker->m_pLastSurface = ls.get();
+
+ wl_surface_set_buffer_scale(ls->pCursorSurface, ls->m_pMonitor->scale);
+ wl_surface_attach(ls->pCursorSurface, wl_cursor_image_get_buffer(ls->pCursorImg), 0, 0);
+ wl_pointer_set_cursor(wl_pointer, serial, ls->pCursorSurface, ls->pCursorImg->hotspot_x / ls->m_pMonitor->scale, ls->pCursorImg->hotspot_y / ls->m_pMonitor->scale);
+ wl_surface_commit(ls->pCursorSurface);
+ }
+ }
+}
+
+void Events::handlePointerLeave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface) {
+ // ignored
+}
+
+void Events::handlePointerAxis(void *data, wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) {
+ // ignored
+}
+
+void Events::handlePointerMotion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) {
+
+ auto x = wl_fixed_to_double(surface_x);
+ auto y = wl_fixed_to_double(surface_y);
+
+ g_pHyprpicker->m_vLastCoords = {x, y};
+
+ g_pHyprpicker->markDirty();
+}
+
+void Events::handlePointerButton(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t button_state) {
+ // get the px and print it
+ const auto CLICKPOS = g_pHyprpicker->m_vLastCoords.floor();
+
+ const auto PLS = g_pHyprpicker->m_pLastSurface;
+
+ struct pixel {
+ unsigned char blue;
+ unsigned char green;
+ unsigned char red;
+ unsigned char alpha;
+ } *px = (struct pixel *)(PLS->screenBuffer.data + (int)CLICKPOS.y * (int)PLS->screenBuffer.pixelSize.x * 4 + (int)CLICKPOS.x * 4);
+
+ Debug::log(NONE, "Result RGBA: %i %i %i %i", px->red, px->green, px->blue, px->alpha);
+
+ g_pHyprpicker->finish();
+}
+
+void Events::handleFrameDone(void *data, struct wl_callback *callback, uint32_t time) {
+ CLayerSurface* pLS = (CLayerSurface*)data;
+
+ if (pLS->frame_callback)
+ wl_callback_destroy(pLS->frame_callback);
+
+ pLS->frame_callback = nullptr;
+
+ if (pLS->dirty || !pLS->rendered)
+ g_pHyprpicker->renderSurface(g_pHyprpicker->m_pLastSurface);
+}
+
+void Events::handleBufferRelease(void *data, struct wl_buffer *wl_buffer) {
+ auto buf = (SPoolBuffer*)data;
+ buf->busy = false;
+}
+
+void Events::handleSCBuffer(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
+ const auto PLS = (CLayerSurface*)data;
+
+ g_pHyprpicker->createBuffer(&PLS->screenBuffer, width, height, format);
+
+ zwlr_screencopy_frame_v1_copy(frame, PLS->screenBuffer.buffer);
+}
+
+void Events::handleSCFlags(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t flags) {
+ const auto PLS = (CLayerSurface *)data;
+
+ PLS->scflags = flags;
+
+ g_pHyprpicker->recheckACK();
+
+ g_pHyprpicker->renderSurface(PLS);
+}
+
+void Events::handleSCReady(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
+ // ignore
+}
+
+void Events::handleSCFailed(void *data, struct zwlr_screencopy_frame_v1 *frame) {
+ Debug::log(CRIT, "Failed to get a Screencopy!");
+ g_pHyprpicker->finish(1);
+}
\ No newline at end of file
diff --git a/src/events/Events.hpp b/src/events/Events.hpp
new file mode 100644
index 0000000..bbf6a19
--- /dev/null
+++ b/src/events/Events.hpp
@@ -0,0 +1,63 @@
+#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, wl_registry *registry, uint32_t name, const char *interface, uint32_t version);
+
+ void handleGlobalRemove(void *data, wl_registry *registry, uint32_t name);
+
+ void handleCapabilities(void *data, wl_seat *wl_seat, uint32_t capabilities);
+
+ void handlePointerMotion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y);
+
+ void handlePointerButton(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t button_state);
+
+ void handlePointerAxis(void *data, wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value);
+
+ void handlePointerEnter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y);
+
+ void handlePointerLeave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface);
+
+ void handleFrameDone(void *data, struct wl_callback *callback, uint32_t time);
+
+ void handleBufferRelease(void *data, struct wl_buffer *wl_buffer);
+
+ void handleSCBuffer(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t format, uint32_t width, uint32_t height, uint32_t stride);
+
+ void handleSCFlags(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t flags);
+
+ void handleSCReady(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec);
+
+ void handleSCFailed(void *data, struct zwlr_screencopy_frame_v1 *frame);
+
+ 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 wl_registry_listener registryListener = { .global = handleGlobal, .global_remove = handleGlobalRemove };
+
+ inline const wl_seat_listener seatListener = { .capabilities = handleCapabilities };
+
+ inline const wl_pointer_listener pointerListener = { .enter = handlePointerEnter, .leave = handlePointerLeave, .motion = handlePointerMotion, .button = handlePointerButton, .axis = handlePointerAxis };
+
+ inline const wl_callback_listener frameListener = { .done = handleFrameDone };
+
+ inline const wl_buffer_listener bufferListener = { .release = handleBufferRelease };
+
+ inline const zwlr_screencopy_frame_v1_listener screencopyListener = { .buffer = handleSCBuffer, .flags = handleSCFlags, .ready = handleSCReady, .failed = handleSCFailed };
+};
\ No newline at end of file
diff --git a/src/helpers/LayerSurface.cpp b/src/helpers/LayerSurface.cpp
new file mode 100644
index 0000000..dc2b4bc
--- /dev/null
+++ b/src/helpers/LayerSurface.cpp
@@ -0,0 +1,40 @@
+#include "LayerSurface.hpp"
+
+#include "../hyprpicker.hpp"
+#include "../events/Events.hpp"
+
+CLayerSurface::CLayerSurface(SMonitor* pMonitor) {
+ m_pMonitor = pMonitor;
+
+ pSurface = wl_compositor_create_surface(g_pHyprpicker->m_pCompositor);
+
+ if (!pSurface) {
+ Debug::log(CRIT, "The compositor did not allow hyprpicker a surface!");
+ g_pHyprpicker->finish(1);
+ return;
+ }
+
+ pLayerSurface = zwlr_layer_shell_v1_get_layer_surface(g_pHyprpicker->m_pLayerShell, pSurface, pMonitor->output, ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "hyprpicker");
+
+ if (!pLayerSurface) {
+ Debug::log(CRIT, "The compositor did not allow hyprpicker a layersurface!");
+ g_pHyprpicker->finish(1);
+ return;
+ }
+
+ zwlr_layer_surface_v1_set_size(pLayerSurface, 0, 0);
+ zwlr_layer_surface_v1_set_anchor(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(pLayerSurface, -1);
+ zwlr_layer_surface_v1_set_keyboard_interactivity(pLayerSurface, true);
+ zwlr_layer_surface_v1_add_listener(pLayerSurface, &Events::layersurfaceListener, this);
+ wl_surface_commit(pSurface);
+
+ wl_display_flush(g_pHyprpicker->m_pWLDisplay);
+}
+
+CLayerSurface::~CLayerSurface() {
+ wl_surface_destroy(pSurface);
+ zwlr_layer_surface_v1_destroy(pLayerSurface);
+
+ wl_display_flush(g_pHyprpicker->m_pWLDisplay);
+}
\ No newline at end of file
diff --git a/src/helpers/LayerSurface.hpp b/src/helpers/LayerSurface.hpp
new file mode 100644
index 0000000..6a86c87
--- /dev/null
+++ b/src/helpers/LayerSurface.hpp
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "../defines.hpp"
+#include "PoolBuffer.hpp"
+
+struct SMonitor;
+
+class CLayerSurface {
+public:
+ CLayerSurface(SMonitor*);
+ ~CLayerSurface();
+
+ SMonitor* m_pMonitor = nullptr;
+
+ zwlr_layer_surface_v1* pLayerSurface = nullptr;
+ wl_surface* pSurface = nullptr;
+ wl_surface* pCursorSurface = nullptr;
+
+ bool wantsACK = false;
+ uint32_t ACKSerial = 0;
+ bool working = false;
+
+ int lastBuffer = 0;
+ SPoolBuffer buffers[2];
+
+ SPoolBuffer screenBuffer;
+ uint32_t scflags = 0;
+
+ bool dirty = true;
+
+ bool rendered = false;
+
+ wl_callback* frame_callback = nullptr;
+
+ wl_cursor_image* pCursorImg = nullptr;
+};
\ No newline at end of file
diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp
new file mode 100644
index 0000000..48583f2
--- /dev/null
+++ b/src/helpers/Monitor.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "../defines.hpp"
+
+struct SMonitor {
+ std::string name = "";
+ wl_output* output = nullptr;
+ uint32_t wayland_name = 0;
+ Vector2D size;
+ int scale;
+
+ bool ready = false;
+
+ zwlr_screencopy_frame_v1* pSCFrame = nullptr;
+};
\ No newline at end of file
diff --git a/src/helpers/PoolBuffer.hpp b/src/helpers/PoolBuffer.hpp
new file mode 100644
index 0000000..bf2db3e
--- /dev/null
+++ b/src/helpers/PoolBuffer.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "../defines.hpp"
+
+struct SPoolBuffer {
+ wl_buffer* buffer = nullptr;
+ cairo_surface_t* surface = nullptr;
+ cairo_t* cairo = nullptr;
+ void* data = nullptr;
+ size_t size = 0;
+ Vector2D pixelSize;
+
+ uint32_t format;
+
+ std::string name;
+
+ bool busy = false;
+};
\ No newline at end of file
diff --git a/src/helpers/Vector2D.cpp b/src/helpers/Vector2D.cpp
new file mode 100644
index 0000000..acaace1
--- /dev/null
+++ b/src/helpers/Vector2D.cpp
@@ -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);
+}
\ No newline at end of file
diff --git a/src/helpers/Vector2D.hpp b/src/helpers/Vector2D.hpp
new file mode 100644
index 0000000..7288a08
--- /dev/null
+++ b/src/helpers/Vector2D.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include
+
+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();
+};
\ No newline at end of file
diff --git a/src/hyprpicker.cpp b/src/hyprpicker.cpp
new file mode 100644
index 0000000..6590a4c
--- /dev/null
+++ b/src/hyprpicker.cpp
@@ -0,0 +1,308 @@
+#include "hyprpicker.hpp"
+#include "events/Events.hpp"
+
+void CHyprpicker::init() {
+ m_pWLDisplay = wl_display_connect(nullptr);
+
+ if (!m_pWLDisplay) {
+ Debug::log(CRIT, "No wayland compositor running!");
+ exit(1);
+ return;
+ }
+
+ m_pWLRegistry = wl_display_get_registry(m_pWLDisplay);
+
+ wl_registry_add_listener(m_pWLRegistry, &Events::registryListener, nullptr);
+
+ wl_display_roundtrip(m_pWLDisplay);
+
+ for (auto& m : m_vMonitors) {
+ m_vLayerSurfaces.emplace_back(std::make_unique(m.get()));
+
+ m_pLastSurface = m_vLayerSurfaces.back().get();
+
+ m->pSCFrame = zwlr_screencopy_manager_v1_capture_output(m_pSCMgr, false, m->output);
+
+ zwlr_screencopy_frame_v1_add_listener(m->pSCFrame, &Events::screencopyListener, m_pLastSurface);
+
+ m_pLastSurface->pCursorSurface = wl_compositor_create_surface(m_pCompositor);
+ }
+
+ wl_display_roundtrip(m_pWLDisplay);
+
+ while (m_bRunning && wl_display_dispatch(m_pWLDisplay) != -1) {
+ //renderSurface(m_pLastSurface);
+ }
+}
+
+void CHyprpicker::finish(int code) {
+ for (auto& ls : m_vLayerSurfaces) {
+ destroyBuffer(&ls->buffers[0]);
+ destroyBuffer(&ls->buffers[1]);
+ destroyBuffer(&ls->screenBuffer);
+ }
+
+ exit(code);
+}
+
+void CHyprpicker::recheckACK() {
+ for (auto& ls : m_vLayerSurfaces) {
+ if (ls->wantsACK && ls->screenBuffer.buffer) {
+ ls->wantsACK = false;
+ zwlr_layer_surface_v1_ack_configure(ls->pLayerSurface, ls->ACKSerial);
+
+ if (!ls->buffers[0].buffer) {
+ createBuffer(&ls->buffers[0], ls->m_pMonitor->size.x * ls->m_pMonitor->scale, ls->m_pMonitor->size.y * ls->m_pMonitor->scale, WL_SHM_FORMAT_ARGB8888);
+ createBuffer(&ls->buffers[1], ls->m_pMonitor->size.x * ls->m_pMonitor->scale, ls->m_pMonitor->size.y * ls->m_pMonitor->scale, WL_SHM_FORMAT_ARGB8888);
+
+ ls->pCursorImg = wl_cursor_theme_get_cursor(wl_cursor_theme_load(getenv("XCURSOR_THEME"), std::stoi(getenv("XCURSOR_SIZE")) * ls->m_pMonitor->scale, m_pWLSHM), "left_ptr")->images[0];
+ }
+ }
+ }
+
+ markDirty();
+}
+
+void CHyprpicker::markDirty() {
+ for (auto& ls : m_vLayerSurfaces) {
+ if (ls->frame_callback)
+ continue;
+
+ ls->frame_callback = wl_surface_frame(ls->pSurface);
+ wl_callback_add_listener(ls->frame_callback, &Events::frameListener, ls.get());
+ wl_surface_commit(ls->pSurface);
+
+ ls->dirty = true;
+ }
+}
+
+SPoolBuffer* CHyprpicker::getBufferForLS(CLayerSurface* pLS) {
+ SPoolBuffer* returns = nullptr;
+
+ for (auto i = 0; i < 2; ++i) {
+ if (pLS->buffers[i].busy)
+ continue;
+
+ returns = &pLS->buffers[i];
+ }
+
+ if (!returns)
+ return nullptr;
+
+ returns->busy = true;
+
+ return returns;
+}
+
+bool CHyprpicker::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 CHyprpicker::createPoolFile(size_t size, std::string& name) {
+ const auto XDGRUNTIMEDIR = getenv("XDG_RUNTIME_DIR");
+ if (!XDGRUNTIMEDIR) {
+ Debug::log(CRIT, "XDG_RUNTIME_DIR not set!");
+ g_pHyprpicker->finish(1);
+ }
+
+ name = std::string(XDGRUNTIMEDIR) + "/.hyprpicker_XXXXXX";
+
+ const auto FD = mkstemp((char*)name.c_str());
+ if (FD < 0) {
+ Debug::log(CRIT, "createPoolFile: fd < 0");
+ g_pHyprpicker->finish(1);
+ }
+
+ if (!setCloexec(FD)) {
+ close(FD);
+ Debug::log(CRIT, "createPoolFile: !setCloexec");
+ g_pHyprpicker->finish(1);
+ }
+
+ if (ftruncate(FD, size) < 0) {
+ close(FD);
+ Debug::log(CRIT, "createPoolFile: ftruncate < 0");
+ g_pHyprpicker->finish(1);
+ }
+
+ return FD;
+}
+
+void CHyprpicker::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!");
+ g_pHyprpicker->finish(1);
+ }
+
+ const auto DATA = mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, FD, 0);
+ const auto POOL = wl_shm_create_pool(g_pHyprpicker->m_pWLSHM, FD, SIZE);
+ pBuffer->buffer = wl_shm_pool_create_buffer(POOL, 0, w, h, STRIDE, format);
+
+ wl_buffer_add_listener(pBuffer->buffer, &Events::bufferListener, pBuffer);
+
+ wl_shm_pool_destroy(POOL);
+
+ close(FD);
+
+ pBuffer->format = format;
+ pBuffer->size = SIZE;
+ pBuffer->data = DATA;
+ pBuffer->pixelSize = Vector2D(w, h);
+ pBuffer->name = name;
+}
+
+void CHyprpicker::destroyBuffer(SPoolBuffer* pBuffer) {
+ wl_buffer_destroy(pBuffer->buffer);
+ cairo_destroy(pBuffer->cairo);
+ cairo_surface_destroy(pBuffer->surface);
+ munmap(pBuffer->data, pBuffer->size);
+
+ pBuffer->buffer = nullptr;
+ pBuffer->cairo = nullptr;
+ pBuffer->surface = nullptr;
+
+ unlink(pBuffer->name.c_str());
+}
+
+void CHyprpicker::createSeat(wl_seat* pSeat) {
+ wl_seat_add_listener(pSeat, &Events::seatListener, pSeat);
+}
+
+void CHyprpicker::convertBuffer(SPoolBuffer* pBuffer) {
+ switch (pBuffer->format) {
+ case WL_SHM_FORMAT_ARGB8888:
+ case WL_SHM_FORMAT_XRGB8888:
+ break;
+ case WL_SHM_FORMAT_ABGR8888:
+ case WL_SHM_FORMAT_XBGR8888: {
+ uint8_t* data = (uint8_t*)pBuffer->data;
+
+ for (int y = 0; y < pBuffer->pixelSize.y; ++y) {
+ for (int x = 0; x < pBuffer->pixelSize.x; ++x) {
+ struct pixel {
+ // little-endian ARGB
+ unsigned char blue;
+ unsigned char green;
+ unsigned char red;
+ unsigned char alpha;
+ }* px = (struct pixel*)(data + y * (int)pBuffer->pixelSize.x * 4 + x * 4);
+
+ std::swap(px->red, px->blue);
+ }
+ }
+ }
+ break;
+ default: {
+ Debug::log(CRIT, "Unsupported format %i", pBuffer->format);
+ }
+ g_pHyprpicker->finish(1);
+ }
+}
+
+void CHyprpicker::renderSurface(CLayerSurface* pSurface) {
+ const auto PBUFFER = getBufferForLS(pSurface);
+
+ if (!PBUFFER || !pSurface->screenBuffer.buffer)
+ return;
+
+ if (!pSurface->screenBuffer.surface) {
+ convertBuffer(&pSurface->screenBuffer);
+ pSurface->screenBuffer.surface = cairo_image_surface_create_for_data((unsigned char*)pSurface->screenBuffer.data, CAIRO_FORMAT_ARGB32, pSurface->screenBuffer.pixelSize.x, pSurface->screenBuffer.pixelSize.y, pSurface->screenBuffer.pixelSize.x * 4);
+ }
+
+ PBUFFER->surface = cairo_image_surface_create_for_data((unsigned char*)PBUFFER->data, CAIRO_FORMAT_ARGB32, PBUFFER->pixelSize.x, PBUFFER->pixelSize.y, PBUFFER->pixelSize.x * 4);
+ PBUFFER->cairo = cairo_create(PBUFFER->surface);
+
+ const auto PCAIRO = PBUFFER->cairo;
+
+ cairo_save(PCAIRO);
+
+ cairo_set_source_rgba(PCAIRO, 0, 0, 0, 0);
+ cairo_rectangle(PCAIRO, 0, 0, pSurface->m_pMonitor->size.x * pSurface->m_pMonitor->scale, pSurface->m_pMonitor->size.y * pSurface->m_pMonitor->scale);
+ cairo_fill(PCAIRO);
+
+ cairo_set_source_surface(PCAIRO, pSurface->screenBuffer.surface, 0, 0);
+ cairo_rectangle(PCAIRO, 0, 0, pSurface->m_pMonitor->size.x * pSurface->m_pMonitor->scale, pSurface->m_pMonitor->size.y * pSurface->m_pMonitor->scale);
+ cairo_fill(PCAIRO);
+
+ // we draw the preview like this
+ //
+ // 200px ZOOM: 10x
+ // | --------- |
+ // | |
+ // | x | 200px
+ // | |
+ // | --------- |
+ //
+
+ cairo_restore(PCAIRO);
+ cairo_save(PCAIRO);
+
+ cairo_set_source_rgba(PCAIRO, 1.f, 0.4f, 0.4f, 0.8f);
+
+ cairo_scale(PCAIRO, 1, 1);
+
+ cairo_arc(PCAIRO, m_vLastCoords.x, m_vLastCoords.y, 101, 0, 2 * M_PI);
+ cairo_clip(PCAIRO);
+
+ cairo_fill(PCAIRO);
+ cairo_paint(PCAIRO);
+
+ cairo_surface_flush(PBUFFER->surface);
+
+ cairo_restore(PCAIRO);
+ cairo_save(PCAIRO);
+
+ const auto PATTERN = cairo_pattern_create_for_surface(pSurface->screenBuffer.surface);
+ cairo_pattern_set_filter(PATTERN, CAIRO_FILTER_NEAREST);
+ cairo_matrix_t matrix;
+ cairo_matrix_init_identity(&matrix);
+ cairo_matrix_translate(&matrix, (m_vLastCoords.x) / 1.112f, (m_vLastCoords.y) / 1.112f); // WHAT IS THIS SHIT???? WHY????
+ cairo_matrix_scale(&matrix, 0.1f, 0.1f);
+ cairo_pattern_set_matrix(PATTERN, &matrix);
+ cairo_set_source(PCAIRO, PATTERN);
+ cairo_arc(PCAIRO, m_vLastCoords.x, m_vLastCoords.y, 100, 0, 2 * M_PI);
+ cairo_clip(PCAIRO);
+ cairo_paint(PCAIRO);
+
+ cairo_surface_flush(PBUFFER->surface);
+
+ cairo_restore(PCAIRO);
+
+ cairo_pattern_destroy(PATTERN);
+
+ sendFrame(pSurface);
+ cairo_destroy(PCAIRO);
+ cairo_surface_destroy(PBUFFER->surface);
+
+ PBUFFER->cairo = nullptr;
+ PBUFFER->surface = nullptr;
+
+ pSurface->rendered = true;
+}
+
+void CHyprpicker::sendFrame(CLayerSurface* pSurface) {
+ pSurface->frame_callback = wl_surface_frame(pSurface->pSurface);
+ wl_callback_add_listener(pSurface->frame_callback, &Events::frameListener, pSurface);
+
+ wl_surface_attach(pSurface->pSurface, pSurface->lastBuffer == 0 ? pSurface->buffers[0].buffer : pSurface->buffers[1].buffer, 0, 0);
+ wl_surface_damage_buffer(pSurface->pSurface, 0, 0, INT32_MAX, INT32_MAX);
+ wl_surface_commit(pSurface->pSurface);
+
+ pSurface->dirty = false;
+}
\ No newline at end of file
diff --git a/src/hyprpicker.hpp b/src/hyprpicker.hpp
new file mode 100644
index 0000000..bdefac4
--- /dev/null
+++ b/src/hyprpicker.hpp
@@ -0,0 +1,53 @@
+#pragma once
+
+#include "defines.hpp"
+#include "helpers/LayerSurface.hpp"
+#include "helpers/PoolBuffer.hpp"
+
+class CHyprpicker {
+public:
+ void init();
+
+ std::mutex m_mtTickMutex;
+
+ wl_compositor* m_pCompositor;
+ wl_display* m_pWLDisplay;
+ wl_registry* m_pWLRegistry;
+ wl_shm* m_pWLSHM;
+ zwlr_layer_shell_v1* m_pLayerShell;
+ zwlr_screencopy_manager_v1* m_pSCMgr;
+
+ bool m_bRunning = true;
+
+ std::vector> m_vMonitors;
+ std::vector> m_vLayerSurfaces;
+
+ void createSeat(wl_seat*);
+
+ CLayerSurface* m_pLastSurface;
+
+ Vector2D m_vLastCoords;
+
+ void renderSurface(CLayerSurface*);
+
+ void createBuffer(SPoolBuffer*, int32_t, int32_t, uint32_t);
+ void destroyBuffer(SPoolBuffer*);
+ int createPoolFile(size_t, std::string&);
+ bool setCloexec(const int&);
+
+ void recheckACK();
+
+ void sendFrame(CLayerSurface*);
+
+ SPoolBuffer* getBufferForLS(CLayerSurface*);
+
+ void convertBuffer(SPoolBuffer*);
+
+ void markDirty();
+
+ void finish(int code = 0);
+private:
+
+};
+
+inline std::unique_ptr g_pHyprpicker;
\ No newline at end of file
diff --git a/src/includes.hpp b/src/includes.hpp
new file mode 100644
index 0000000..79f4d1d
--- /dev/null
+++ b/src/includes.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#define class _class
+#define namespace _namespace
+#define static
+
+extern "C" {
+#include "wlr-layer-shell-unstable-v1-protocol.h"
+#include "wlr-screencopy-unstable-v1-protocol.h"
+#include "xdg-shell-protocol.h"
+#include
+#include
+}
+
+#undef class
+#undef namespace
+#undef static
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..8a9b501
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,10 @@
+#include
+#include "hyprpicker.hpp"
+
+
+int main(int argc, char** argv, char** envp) {
+ g_pHyprpicker = std::make_unique();
+ g_pHyprpicker->init();
+
+ return 0;
+}
\ No newline at end of file