2022-09-02 18:06:00 +02:00
|
|
|
#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<CLayerSurface>(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);
|
|
|
|
|
2022-09-02 18:34:04 +02:00
|
|
|
int XCURSOR_SIZE = 24;
|
|
|
|
if (getenv("XCURSOR_SIZE")) {
|
|
|
|
XCURSOR_SIZE = std::stoi(getenv("XCURSOR_SIZE"));
|
|
|
|
}
|
|
|
|
|
|
|
|
ls->pCursorImg = wl_cursor_theme_get_cursor(wl_cursor_theme_load(getenv("XCURSOR_THEME"), XCURSOR_SIZE * ls->m_pMonitor->scale, m_pWLSHM), "left_ptr")->images[0];
|
2022-09-02 18:06:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2022-09-02 19:39:47 +02:00
|
|
|
|
|
|
|
PBUFFER->surface = cairo_image_surface_create_for_data((unsigned char*)PBUFFER->data, CAIRO_FORMAT_ARGB32, pSurface->m_pMonitor->size.x * pSurface->m_pMonitor->scale, pSurface->m_pMonitor->size.y * pSurface->m_pMonitor->scale, PBUFFER->pixelSize.x * 4);
|
2022-09-02 18:06:00 +02:00
|
|
|
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);
|
|
|
|
|
2022-09-02 19:39:47 +02:00
|
|
|
const auto SCALEBUFS = Vector2D { pSurface->screenBuffer.pixelSize.x / PBUFFER->pixelSize.x, pSurface->screenBuffer.pixelSize.y / PBUFFER->pixelSize.y };
|
|
|
|
const auto SCALECURSOR = Vector2D{
|
|
|
|
g_pHyprpicker->m_pLastSurface->screenBuffer.pixelSize.x / (g_pHyprpicker->m_pLastSurface->buffers[0].pixelSize.x / g_pHyprpicker->m_pLastSurface->m_pMonitor->scale),
|
|
|
|
g_pHyprpicker->m_pLastSurface->screenBuffer.pixelSize.y / (g_pHyprpicker->m_pLastSurface->buffers[0].pixelSize.y / g_pHyprpicker->m_pLastSurface->m_pMonitor->scale)};
|
2022-09-02 20:25:46 +02:00
|
|
|
const auto CLICKPOS = Vector2D{g_pHyprpicker->m_vLastCoords.floor().x * SCALECURSOR.x + 5, g_pHyprpicker->m_vLastCoords.floor().y * SCALECURSOR.y + 5};
|
2022-09-02 19:39:47 +02:00
|
|
|
|
|
|
|
const auto PATTERNPRE = cairo_pattern_create_for_surface(pSurface->screenBuffer.surface);
|
|
|
|
cairo_pattern_set_filter(PATTERNPRE, CAIRO_FILTER_BILINEAR);
|
|
|
|
cairo_matrix_t matrixPre;
|
|
|
|
cairo_matrix_init_identity(&matrixPre);
|
|
|
|
cairo_matrix_scale(&matrixPre, SCALEBUFS.x, SCALEBUFS.y);
|
|
|
|
cairo_pattern_set_matrix(PATTERNPRE, &matrixPre);
|
|
|
|
cairo_set_source(PCAIRO, PATTERNPRE);
|
|
|
|
cairo_paint(PCAIRO);
|
|
|
|
|
|
|
|
cairo_surface_flush(PBUFFER->surface);
|
|
|
|
|
|
|
|
cairo_pattern_destroy(PATTERNPRE);
|
2022-09-02 18:06:00 +02:00
|
|
|
|
|
|
|
// we draw the preview like this
|
|
|
|
//
|
|
|
|
// 200px ZOOM: 10x
|
|
|
|
// | --------- |
|
|
|
|
// | |
|
|
|
|
// | x | 200px
|
|
|
|
// | |
|
|
|
|
// | --------- |
|
|
|
|
//
|
|
|
|
|
|
|
|
cairo_restore(PCAIRO);
|
|
|
|
cairo_save(PCAIRO);
|
|
|
|
|
2022-09-02 19:39:47 +02:00
|
|
|
const auto PIXCOLOR = getColorFromPixel(pSurface, CLICKPOS);
|
|
|
|
cairo_set_source_rgba(PCAIRO, PIXCOLOR.r / 255.f, PIXCOLOR.g / 255.f, PIXCOLOR.b / 255.f, PIXCOLOR.a / 255.f);
|
2022-09-02 18:06:00 +02:00
|
|
|
|
|
|
|
cairo_scale(PCAIRO, 1, 1);
|
|
|
|
|
2022-09-02 19:39:47 +02:00
|
|
|
cairo_arc(PCAIRO, m_vLastCoords.x * pSurface->m_pMonitor->scale, m_vLastCoords.y * pSurface->m_pMonitor->scale, 105 / SCALEBUFS.x, 0, 2 * M_PI);
|
2022-09-02 18:06:00 +02:00
|
|
|
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);
|
2022-09-02 19:39:47 +02:00
|
|
|
cairo_matrix_translate(&matrix, CLICKPOS.x, CLICKPOS.y);
|
2022-09-02 18:06:00 +02:00
|
|
|
cairo_matrix_scale(&matrix, 0.1f, 0.1f);
|
2022-09-02 20:25:46 +02:00
|
|
|
cairo_matrix_translate(&matrix, - CLICKPOS.x, - CLICKPOS.y);
|
2022-09-02 18:06:00 +02:00
|
|
|
cairo_pattern_set_matrix(PATTERN, &matrix);
|
|
|
|
cairo_set_source(PCAIRO, PATTERN);
|
2022-09-02 19:39:47 +02:00
|
|
|
cairo_arc(PCAIRO, m_vLastCoords.x * pSurface->m_pMonitor->scale, m_vLastCoords.y * pSurface->m_pMonitor->scale, 100 / SCALEBUFS.x, 0, 2 * M_PI);
|
2022-09-02 18:06:00 +02:00
|
|
|
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);
|
2022-09-02 19:39:47 +02:00
|
|
|
wl_surface_set_buffer_scale(pSurface->pSurface, pSurface->m_pMonitor->scale);
|
2022-09-02 18:06:00 +02:00
|
|
|
wl_surface_damage_buffer(pSurface->pSurface, 0, 0, INT32_MAX, INT32_MAX);
|
|
|
|
wl_surface_commit(pSurface->pSurface);
|
|
|
|
|
|
|
|
pSurface->dirty = false;
|
2022-09-02 19:39:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CColor CHyprpicker::getColorFromPixel(CLayerSurface* pLS, Vector2D pix) {
|
|
|
|
struct pixel {
|
|
|
|
unsigned char blue;
|
|
|
|
unsigned char green;
|
|
|
|
unsigned char red;
|
|
|
|
unsigned char alpha;
|
|
|
|
}* px = (struct pixel*)(pLS->screenBuffer.data + (int)pix.y * (int)pLS->screenBuffer.pixelSize.x * 4 + (int)pix.x * 4);
|
|
|
|
|
|
|
|
return CColor{(uint8_t)px->red, (uint8_t)px->green, (uint8_t)px->blue, (uint8_t)px->alpha};
|
2022-09-02 18:06:00 +02:00
|
|
|
}
|