diff --git a/src/Hyprpaper.cpp b/src/Hyprpaper.cpp index abaa362..279e17c 100644 --- a/src/Hyprpaper.cpp +++ b/src/Hyprpaper.cpp @@ -1,4 +1,5 @@ #include "Hyprpaper.hpp" +#include "render/WallpaperTransform.hpp" #include #include #include @@ -520,6 +521,7 @@ SPoolBuffer* CHyprpaper::getPoolBuffer(SMonitor* pMonitor, CWallpaperTarget* pWa } void CHyprpaper::renderWallpaperForMonitor(SMonitor* pMonitor) { +#include "render/WallpaperTransform.hpp" static auto PRENDERSPLASH = Hyprlang::CSimpleConfigValue(g_pConfigManager->config.get(), "splash"); static auto PSPLASHOFFSET = Hyprlang::CSimpleConfigValue(g_pConfigManager->config.get(), "splash_offset"); @@ -564,30 +566,21 @@ void CHyprpaper::renderWallpaperForMonitor(SMonitor* pMonitor) { cairo_fill(PCAIRO); cairo_surface_flush(PBUFFER->surface); - // get scale - // we always do cover - double scale; - Vector2D origin; - - const bool LOWASPECTRATIO = pMonitor->size.x / pMonitor->size.y > PWALLPAPERTARGET->m_vSize.x / PWALLPAPERTARGET->m_vSize.y; - if ((CONTAIN && !LOWASPECTRATIO) || (!CONTAIN && LOWASPECTRATIO)) { - scale = DIMENSIONS.x / PWALLPAPERTARGET->m_vSize.x; - origin.y = -(PWALLPAPERTARGET->m_vSize.y * scale - DIMENSIONS.y) / 2.0 / scale; - } else { - scale = DIMENSIONS.y / PWALLPAPERTARGET->m_vSize.y; - origin.x = -(PWALLPAPERTARGET->m_vSize.x * scale - DIMENSIONS.x) / 2.0 / scale; - } - - Debug::log(LOG, "Image data for {}: {} at [{:.2f}, {:.2f}], scale: {:.2f} (original image size: [{}, {}])", pMonitor->name, PWALLPAPERTARGET->m_szPath, origin.x, origin.y, - scale, (int)PWALLPAPERTARGET->m_vSize.x, (int)PWALLPAPERTARGET->m_vSize.y); + // Apply wallpaper transform (handles scaling, rotation, and centering) + applyWallpaperTransform( + PCAIRO, + Vector2D(PWALLPAPERTARGET->m_vSize.x, PWALLPAPERTARGET->m_vSize.y), + DIMENSIONS, + pMonitor->wallpaperRotation + ); if (TILE) { cairo_pattern_t* pattern = cairo_pattern_create_for_surface(PWALLPAPERTARGET->m_pCairoSurface->cairo()); cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); cairo_set_source(PCAIRO, pattern); } else { - cairo_scale(PCAIRO, scale, scale); - cairo_set_source_surface(PCAIRO, PWALLPAPERTARGET->m_pCairoSurface->cairo(), origin.x, origin.y); + // No additional scaling/positioning - the transform handles it all + cairo_set_source_surface(PCAIRO, PWALLPAPERTARGET->m_pCairoSurface->cairo(), 0, 0); } cairo_paint(PCAIRO); @@ -601,6 +594,15 @@ void CHyprpaper::renderWallpaperForMonitor(SMonitor* pMonitor) { cairo_select_font_face(PCAIRO, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + // Calculate scale for splash text (same logic as transform function) + int rot = pMonitor->wallpaperRotation % 360; + bool swapWH = (rot == 90 || rot == 270); + double imgW = swapWH ? PWALLPAPERTARGET->m_vSize.y : PWALLPAPERTARGET->m_vSize.x; + double imgH = swapWH ? PWALLPAPERTARGET->m_vSize.x : PWALLPAPERTARGET->m_vSize.y; + double scaleX = DIMENSIONS.x / imgW; + double scaleY = DIMENSIONS.y / imgH; + double scale = std::max(scaleX, scaleY); + const auto FONTSIZE = (int)(DIMENSIONS.y / 76.0 / scale); cairo_set_font_size(PCAIRO, FONTSIZE); diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 8d21aa0..38e36f3 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -8,13 +8,29 @@ static Hyprlang::CParseResult handleWallpaper(const char* C, const char* V) { const std::string VALUE = V; Hyprlang::CParseResult result; - if (VALUE.find_first_of(',') == std::string::npos) { + + // Support syntax: monitor_name,wallpaper_path[,rotation] + size_t firstComma = VALUE.find_first_of(','); + if (firstComma == std::string::npos) { result.setError("wallpaper failed (syntax)"); return result; } - auto MONITOR = VALUE.substr(0, VALUE.find_first_of(',')); - auto WALLPAPER = g_pConfigManager->trimPath(VALUE.substr(VALUE.find_first_of(',') + 1)); + size_t secondComma = VALUE.find_first_of(',', firstComma + 1); + auto MONITOR = VALUE.substr(0, firstComma); + std::string WALLPAPER; + int rotation = 0; + if (secondComma == std::string::npos) { + WALLPAPER = g_pConfigManager->trimPath(VALUE.substr(firstComma + 1)); + } else { + WALLPAPER = g_pConfigManager->trimPath(VALUE.substr(firstComma + 1, secondComma - firstComma - 1)); + std::string rotationStr = VALUE.substr(secondComma + 1); + try { + rotation = std::stoi(rotationStr); + } catch (...) { + rotation = 0; + } + } bool contain = false; @@ -48,11 +64,20 @@ static Hyprlang::CParseResult handleWallpaper(const char* C, const char* V) { return result; } + g_pHyprpaper->clearWallpaperFromMonitor(MONITOR); g_pHyprpaper->m_mMonitorActiveWallpapers[MONITOR] = WALLPAPER; g_pHyprpaper->m_mMonitorWallpaperRenderData[MONITOR].contain = contain; g_pHyprpaper->m_mMonitorWallpaperRenderData[MONITOR].tile = tile; + // Set wallpaper rotation for the monitor + for (auto& m : g_pHyprpaper->m_vMonitors) { + if (m->name == MONITOR) { + m->wallpaperRotation = rotation; + break; + } + } + if (MONITOR.empty()) { for (auto& m : g_pHyprpaper->m_vMonitors) { if (!m->hasATarget || m->wildcard) { diff --git a/src/helpers/Monitor.hpp b/src/helpers/Monitor.hpp index c9b5e9d..bf74627 100644 --- a/src/helpers/Monitor.hpp +++ b/src/helpers/Monitor.hpp @@ -26,6 +26,9 @@ struct SMonitor { bool wantsACK = false; bool initialized = false; + // Wallpaper rotation in degrees (0, 90, 180, 270) + int wallpaperRotation = 0; + std::vector> layerSurfaces; CLayerSurface* pCurrentLayerSurface = nullptr; diff --git a/src/render/WallpaperTransform.hpp b/src/render/WallpaperTransform.hpp new file mode 100644 index 0000000..4d17704 --- /dev/null +++ b/src/render/WallpaperTransform.hpp @@ -0,0 +1,46 @@ +#pragma once +#include +#include + +// Applies robust rotation, scaling, and centering for wallpaper rendering +// Always fills the monitor (cover mode), centers image, and handles all rotations correctly +inline void applyWallpaperTransform(cairo_t* cr, const Vector2D& imgSize, const Vector2D& monSize, int rotation) { + double imgW = imgSize.x; + double imgH = imgSize.y; + double monW = monSize.x; + double monH = monSize.y; + + // Normalize rotation + int rot = rotation % 360; + if (rot < 0) rot += 360; + + // For 90/270 degrees, the image dimensions are effectively swapped + bool isRotated90or270 = (rot == 90 || rot == 270); + double effectiveImgW = isRotated90or270 ? imgH : imgW; + double effectiveImgH = isRotated90or270 ? imgW : imgH; + + // Calculate scale to cover the monitor (larger scale wins) + double scaleX = monW / effectiveImgW; + double scaleY = monH / effectiveImgH; + double scale = std::max(scaleX, scaleY); + + // Debug output + printf("DEBUG Transform: rotation=%d, imgSize=[%.1f,%.1f], monSize=[%.1f,%.1f]\n", + rot, imgW, imgH, monW, monH); + printf("DEBUG effective=[%.1f,%.1f], scales=[%.3f,%.3f], final_scale=%.3f\n", + effectiveImgW, effectiveImgH, scaleX, scaleY, scale); + printf("DEBUG transforms: translate=[%.1f,%.1f], rotate=%.1f°, scale=%.3f, img_center=[%.1f,%.1f]\n", + monW/2.0, monH/2.0, (double)rot, scale, imgW/2.0, imgH/2.0); + + // Move to center of monitor + cairo_translate(cr, monW / 2.0, monH / 2.0); + + // Rotate around center + cairo_rotate(cr, rot * M_PI / 180.0); + + // Scale the image + cairo_scale(cr, scale, scale); + + // Move to center of image (so image center aligns with rotation center) + cairo_translate(cr, -imgW / 2.0, -imgH / 2.0); +}