From a859f2606728006afde5c52396ecf5f804d1683a Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Sat, 30 Dec 2023 21:44:46 +0000 Subject: [PATCH 01/33] new project structure --- .gitignore | 5 + CMakeLists.txt | 88 ++----- main.c | 240 ------------------ src/core/CMakeLists.txt | 51 ++++ src/core/main.c | 31 +++ src/window_templates/main.glade | 175 +++++++++++++ src/wlp/CMakeLists.txt | 77 ++++++ debug.c => src/wlp/debug.c | 0 debug.h => src/wlp/debug.h | 0 src/wlp/main.c | 95 +++++++ main.h => src/wlp/main.h | 11 +- parser.c => src/wlp/parser.c | 4 +- parser.h => src/wlp/parser.h | 2 +- platform_guard.h => src/wlp/platform_guard.h | 0 .../wlp/resource.template.rc | 4 +- src/wlp/wallpaper.c | 211 +++++++++++++++ src/wlp/wallpaper.h | 10 + window.c => src/wlp/window.c | 37 +-- window.h => src/wlp/window.h | 2 +- wallpaper.c | 73 ------ wallpaper.h | 9 - 21 files changed, 700 insertions(+), 425 deletions(-) delete mode 100644 main.c create mode 100644 src/core/CMakeLists.txt create mode 100644 src/core/main.c create mode 100644 src/window_templates/main.glade create mode 100644 src/wlp/CMakeLists.txt rename debug.c => src/wlp/debug.c (100%) rename debug.h => src/wlp/debug.h (100%) create mode 100644 src/wlp/main.c rename main.h => src/wlp/main.h (95%) rename parser.c => src/wlp/parser.c (98%) rename parser.h => src/wlp/parser.h (79%) rename platform_guard.h => src/wlp/platform_guard.h (100%) rename resource.template.rc => src/wlp/resource.template.rc (91%) create mode 100644 src/wlp/wallpaper.c create mode 100644 src/wlp/wallpaper.h rename window.c => src/wlp/window.c (86%) rename window.h => src/wlp/window.h (81%) delete mode 100644 wallpaper.c delete mode 100644 wallpaper.h diff --git a/.gitignore b/.gitignore index 8ec5a32..90b3ebb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,15 @@ resource.rc .DS_Store +compile_commands.json +.cache + data/ build/ *.res +.vscode + # Prerequisites *.d diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b839e4..2d73e6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,19 +1,17 @@ cmake_minimum_required(VERSION 3.13) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + project(lwp LANGUAGES C) -if(SDL2_RUNTIME_DIR) - string(REPLACE "\\" "/" SDL2_RUNTIME_DIR ${SDL2_RUNTIME_DIR}) -endif() - # Detect the platform if (APPLE) - set(_UNAME "DARWIN") + set(_UNAME "DARWIN") elseif (WIN32) - set(_UNAME "WIN32") + set(_UNAME "WIN32") else() - set(_UNAME "LINUX") + set(_UNAME "LINUX") endif() # Detect version @@ -35,47 +33,6 @@ if(NOT DEFINED PROGRAM_VERSION) message(WARNING "Can't determine Layered WallPaper version") endif() -set(_SOURCE_FILES - main.c - debug.c - parser.c - wallpaper.c - window.c - ) - -# Windows resource file -if(_UNAME STREQUAL "WIN32") - if(MINGW) - set(CMAKE_RC_COMPILER_INIT windres) - ENABLE_LANGUAGE(RC) - SET(CMAKE_RC_COMPILE_OBJECT - " -O coff -i -o ") - endif(MINGW) - - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/resource.template.rc ${CMAKE_CURRENT_SOURCE_DIR}/resource.rc - @ONLY) - list(APPEND _SOURCE_FILES "resource.rc") -endif() - -# SDL2 dependency -find_package(SDL2 REQUIRED CONFIG) -set(_INCLUDE_DIRS ${SDL2_INCLUDE_DIRS}) -set(_LIBS ${SDL2_LIBRARIES}) - -if (_UNAME STREQUAL "DARWIN") - # MacOSX framework dependencies - list(APPEND _LIBS "-framework CoreGraphics" "-framework Foundation") -endif() - -if (_UNAME STREQUAL "LINUX") - # X11 dependency - find_package(X11 REQUIRED) - list(APPEND _INCLUDE_DIRS ${X11_INCLUDE_DIR}) - list(APPEND _LIBS ${X11_LIBRARIES}) -endif() - -option(LWP_INSTALL_LAUNCHD "Launch lwp on login (MacOSX only)" OFF) - # Config file set(_DEFAULT_CONFIG_FILE default.cfg) if(_UNAME STREQUAL "WIN32") @@ -85,32 +42,18 @@ if (_UNAME STREQUAL "DARWIN") set(_DEFAULT_CONFIG_FILE defaultMac.cfg) endif() -# Main executable -if (_UNAME STREQUAL "DARWIN") - add_executable(lwp MACOSX_BUNDLE ${_SOURCE_FILES}) -else() - add_executable(lwp ${_SOURCE_FILES}) -endif() +add_subdirectory(src/wlp) +link_directories(src/wlp) -# Windows specific properties for executable -if(_UNAME STREQUAL "WIN32") - set_property(TARGET lwp PROPERTY VS_DPI_AWARE "PerMonitor") -endif() - -if(MSVC) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS") -endif() - -target_compile_definitions(lwp PUBLIC __${_UNAME}) -target_include_directories(lwp PUBLIC ${_INCLUDE_DIRS}) -target_link_libraries(lwp PRIVATE ${_LIBS}) +add_subdirectory(src/core) +link_directories(src/core) # Installation rules if(_UNAME STREQUAL "WIN32") - install(TARGETS lwp - DESTINATION .) install(DIRECTORY wallpapers DESTINATION .) + install(DIRECTORY src/window_templates + DESTINATION .) install(FILES LICENSE.txt DESTINATION .) install(FILES ${_DEFAULT_CONFIG_FILE} @@ -120,20 +63,20 @@ if(_UNAME STREQUAL "WIN32") install(FILES ${SDL2_RUNTIME_DIR}/README-SDL.txt DESTINATION .) elseif(_UNAME STREQUAL "LINUX") - install(TARGETS lwp - DESTINATION usr/local/bin) install(DIRECTORY wallpapers DESTINATION usr/local/share/lwp) + install(DIRECTORY src/window_templates + DESTINATION usr/local/share/lwp) install(FILES LICENSE.txt DESTINATION usr/local/share/lwp) install(FILES ${_DEFAULT_CONFIG_FILE} TYPE SYSCONF RENAME lwp.cfg) else() - install(TARGETS lwp - DESTINATION Layered_WallPaper) install(DIRECTORY wallpapers DESTINATION Layered_WallPaper) + install(DIRECTORY src/window_templates + DESTINATION Layered_WallPaper) install(FILES LICENSE.txt DESTINATION Layered_WallPaper) install(FILES ${_DEFAULT_CONFIG_FILE} @@ -152,6 +95,7 @@ else() endif() + # Installer if (_UNAME STREQUAL "WIN32") set(CPACK_GENERATOR NSIS) diff --git a/main.c b/main.c deleted file mode 100644 index def3652..0000000 --- a/main.c +++ /dev/null @@ -1,240 +0,0 @@ -#include "main.h" - -#include "debug.h" -#include "parser.h" -#include "platform_guard.h" -#include "wallpaper.h" -#include "window.h" - -static int lerp(int a, int b, float t) -{ - if (t > 1) t = 1; - return (int)((float)a + (float)t * ((float)b - (float)a)); -} - -static int init(App *app, Config *cfg) -{ - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) - { - lwpLog(LOG_ERROR, "%s", SDL_GetError()); - return 0; - } - - initWindow(app, cfg); - - app->renderer = - SDL_CreateRenderer(app->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); - if (app->renderer == NULL) - { - lwpLog(LOG_ERROR, "%s", SDL_GetError()); - return 0; - } - - return 1; -} - -#ifdef __WIN32 -static void initCmd() -{ - // Create console - AllocConsole(); - AttachConsole(ATTACH_PARENT_PROCESS); - freopen("CONOUT$", "w", stdout); - HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD dwMode = 0; - GetConsoleMode(hOut, &dwMode); - SetConsoleMode(hOut, dwMode | 0x0004); - - // Remove closing button (because closing it closes the entire app) - HWND hwnd = GetConsoleWindow(); - HMENU hMenu = GetSystemMenu(hwnd, FALSE); - DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND); - - // Set console title - SetConsoleTitle("Layered WallPaper"); -} -BOOL monitorenumproc( - HMONITOR monitor, - HDC hdc, - LPRECT rect, - LPARAM param -) -{ - - int px = GetSystemMetrics(SM_XVIRTUALSCREEN); - int py = GetSystemMetrics(SM_YVIRTUALSCREEN); - - MONITORINFO monitorInfo; - monitorInfo.cbSize = sizeof(MONITORINFO); - - GetMonitorInfo(monitor, &monitorInfo); - lwpLog(LOG_INFO, " Monitor: position %ldx%ld, size %ldx%ld %s", - monitorInfo.rcMonitor.left - px, - monitorInfo.rcMonitor.top - py, - monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left, - monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top, - (monitorInfo.dwFlags & MONITORINFOF_PRIMARY) == MONITORINFOF_PRIMARY ? "primary" : "" - ); - - return TRUE; -} -static void scanMonitors() -{ - lwpLog(LOG_INFO, "Scanning monitors..."); - EnumDisplayMonitors(NULL, NULL, &monitorenumproc, NULL); -} -#endif - -int main(int argc, char *argv[]) -{ - lwpLog(LOG_INFO, "Starting Layered WallPaper"); - -#ifdef __WIN32 - if (argc == 2 && strcmp(argv[1], "/console") == 0) initCmd(); - initTrayIcon(); - - scanMonitors(); -#endif - - App app; - Config cfg; - - int canRender = 1; - - if (!parseConfig(&app, &cfg) || !init(&app, &cfg) || !loadMonitors(&app, &cfg)) canRender = 0; - - SDL_Event event; - int quit = 0; - - int mx = 0; - int my = 0; - - while (!quit) - { - if(canRender) - { - static int currentX = 0; - static int currentY = 0; - - static int lastTicks = 0; - - int ticks = SDL_GetTicks(); - float dT = (ticks - lastTicks) / 1000.0f; - lastTicks = ticks; - - #ifdef __WIN32 - POINT mPos; - GetCursorPos(&mPos); - mx = mPos.x - GetSystemMetrics(SM_XVIRTUALSCREEN); - my = mPos.y - GetSystemMetrics(SM_YVIRTUALSCREEN); - #else - SDL_GetGlobalMouseState(&mx, &my); - #endif - - while (SDL_PollEvent(&event)) - if (event.type == SDL_QUIT) quit = 1; - - currentX = lerp(currentX, mx, dT * cfg.smooth); - currentY = lerp(currentY, my, dT * cfg.smooth); - - for (int m = 0; m < cfg.monitorsCount; m++) - { - int relativeCurrentX = currentX - cfg.monitors[m].x; - int relativeCurrentY = currentY - cfg.monitors[m].y; - - if (relativeCurrentX < 0) relativeCurrentX = 0; - if (relativeCurrentY < 0) relativeCurrentY = 0; - if (relativeCurrentX > cfg.monitors[m].w) relativeCurrentX = cfg.monitors[m].w; - if (relativeCurrentY > cfg.monitors[m].h) relativeCurrentY = cfg.monitors[m].h; - - SDL_SetRenderTarget(app.renderer, cfg.monitors[m].wallpaper.tex); - SDL_RenderClear(app.renderer); - - for (int i = 0; i < cfg.monitors[m].wallpaper.layersCount; i++) - { - SDL_Rect src = { - .x = 0, - .y = 0, - .w = cfg.monitors[m].wallpaper.originalW, - .h = cfg.monitors[m].wallpaper.originalH, - }; - - int x = -((relativeCurrentX - cfg.monitors[m].w / 2) * - cfg.monitors[m].wallpaper.layers[i].sensitivityX); - int y = -((relativeCurrentY - cfg.monitors[m].h / 2) * - cfg.monitors[m].wallpaper.layers[i].sensitivityY); - - for (int k = -cfg.monitors[m].wallpaper.repeatY; k <= cfg.monitors[m].wallpaper.repeatY; - k++) - { - for (int j = -cfg.monitors[m].wallpaper.repeatX; j <= cfg.monitors[m].wallpaper.repeatX; - j++) - { - SDL_Rect dest = { - .x = x + j * cfg.monitors[m].wallpaperW, - .y = y + k * cfg.monitors[m].wallpaperH, - .w = cfg.monitors[m].wallpaperW, - .h = cfg.monitors[m].wallpaperH, - }; - - SDL_RenderCopy(app.renderer, cfg.monitors[m].wallpaper.layers[i].tex, &src, &dest); - } - } - } - - SDL_SetRenderTarget(app.renderer, cfg.monitors[m].tex); - - SDL_Rect src = { - .x = 0, - .y = 0, - .w = cfg.monitors[m].wallpaperW, - .h = cfg.monitors[m].wallpaperH, - }; - - SDL_Rect dest = { - .x = cfg.monitors[m].wallpaperX, - .y = cfg.monitors[m].wallpaperY, - .w = cfg.monitors[m].wallpaperW, - .h = cfg.monitors[m].wallpaperH, - }; - - SDL_RenderCopy(app.renderer, cfg.monitors[m].wallpaper.tex, &src, &dest); - - SDL_SetRenderTarget(app.renderer, NULL); - - SDL_Rect finalSrc = { - .x = 0, - .y = 0, - .w = cfg.monitors[m].w, - .h = cfg.monitors[m].h, - }; - - SDL_Rect finalDest = { - .x = cfg.monitors[m].x, - .y = cfg.monitors[m].y, - .w = cfg.monitors[m].w, - .h = cfg.monitors[m].h, - }; - - SDL_RenderCopy(app.renderer, cfg.monitors[m].tex, &finalSrc, &finalDest); - } - SDL_RenderPresent(app.renderer); - } - SDL_Delay(1000 / cfg.targetFPS); -#ifdef __WIN32 - if(!updateTrayIcon()) quit = 1; -#endif - } - -#ifdef __WIN32 - removeTrayIcon(); -#endif - - freeConfig(&cfg); - - SDL_DestroyRenderer(app.renderer); - SDL_DestroyWindow(app.window); - SDL_Quit(); - - return 0; -} diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt new file mode 100644 index 0000000..f59c767 --- /dev/null +++ b/src/core/CMakeLists.txt @@ -0,0 +1,51 @@ +set(_SOURCE_FILES + main.c + ) + +# Windows resource file +if(_UNAME STREQUAL "WIN32") + if(MINGW) + set(CMAKE_RC_COMPILER_INIT windres) + ENABLE_LANGUAGE(RC) + SET(CMAKE_RC_COMPILE_OBJECT + " -O coff -i -o ") + endif(MINGW) + + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/resource.template.rc ${CMAKE_CURRENT_SOURCE_DIR}/resource.rc + @ONLY) + list(APPEND _SOURCE_FILES "resource.rc") +endif() + +# GTK dependency +find_package (PkgConfig REQUIRED) +pkg_check_modules (GTK3 REQUIRED gtk+-3.0) +list(APPEND _INCLUDE_DIRS ${GTK3_INCLUDE_DIRS}) +list(APPEND _LIBS ${GTK3_LINK_LIBRARIES}) + +# Main executable +if (_UNAME STREQUAL "DARWIN") + add_executable(lwp MACOSX_BUNDLE ${_SOURCE_FILES}) +else() + add_executable(lwp ${_SOURCE_FILES}) +endif() + +if(MSVC) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS") +endif() + +target_compile_definitions(lwp PUBLIC __${_UNAME}) +target_include_directories(lwp PUBLIC ${_INCLUDE_DIRS}) +target_link_libraries(lwp PRIVATE ${_LIBS}) + +# Installation rules +if(_UNAME STREQUAL "WIN32") + install(TARGETS lwp + DESTINATION .) +elseif(_UNAME STREQUAL "LINUX") + install(TARGETS lwp + DESTINATION usr/local/bin) +else() + install(TARGETS lwp + DESTINATION Layered_WallPaper) +endif() + diff --git a/src/core/main.c b/src/core/main.c new file mode 100644 index 0000000..d1bce05 --- /dev/null +++ b/src/core/main.c @@ -0,0 +1,31 @@ +#include + +#ifdef __WIN32 + #define MAIN_WINDOW_TEMPLATE_PATH "" +#elif __DARWIN + #define MAIN_WINDOW_TEMPLATE_PATH "" +#else + #define MAIN_WINDOW_TEMPLATE_PATH "/usr/local/share/lwp/window_templates/main.glade" +#endif + +static void activate(GtkApplication *app, gpointer userdata) +{ + GtkBuilder *builder = gtk_builder_new_from_file(MAIN_WINDOW_TEMPLATE_PATH); + GtkWidget *window = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow"); + if (window == NULL) printf("wnd not found\n"); + gtk_window_set_application(GTK_WINDOW(window), GTK_APPLICATION(app)); + gtk_widget_set_visible(window, 1); +} + +int main(int argc, char *argv[]) +{ + GtkApplication *app; + int status; + + app = gtk_application_new ("com.github.jszczerbinsky.lwp", G_APPLICATION_DEFAULT_FLAGS); + g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); + status = g_application_run (G_APPLICATION (app), argc, argv); + g_object_unref (app); + + return status; +} \ No newline at end of file diff --git a/src/window_templates/main.glade b/src/window_templates/main.glade new file mode 100644 index 0000000..bd863eb --- /dev/null +++ b/src/window_templates/main.glade @@ -0,0 +1,175 @@ + + + + + + False + False + 800 + 600 + + + True + False + vertical + + + True + False + + + Github page + True + True + True + none + https://github.com/jszczerbinsky/lwp + + + False + True + end + 0 + + + + + True + False + 11 + v 1.0.0 + + + False + True + 1 + + + + + False + True + end + 0 + + + + + True + False + vertical + + + True + False + 10 + 10 + Layered WallPaper + + + + + + False + True + 0 + + + + + True + False + Enhance your desktop experiance + + + False + True + 1 + + + + + False + True + 1 + + + + + True + False + 120 + 120 + 50 + vertical + + + True + False + 5 + 5 + Setup your screens + + + + + + False + True + 0 + + + + + True + False + + + True + True + + + True + False + start + 10 + 10 + Monitor 2 + + + + + + + True + True + + + True + False + start + 10 + 10 + Monitor 1 + + + + + + + False + True + 1 + + + + + True + True + 2 + + + + + + diff --git a/src/wlp/CMakeLists.txt b/src/wlp/CMakeLists.txt new file mode 100644 index 0000000..64e71d8 --- /dev/null +++ b/src/wlp/CMakeLists.txt @@ -0,0 +1,77 @@ +if(SDL2_RUNTIME_DIR) + string(REPLACE "\\" "/" SDL2_RUNTIME_DIR ${SDL2_RUNTIME_DIR}) +endif() + +set(_SOURCE_FILES + main.c + debug.c + parser.c + wallpaper.c + window.c + ) + +# Windows resource file +if(_UNAME STREQUAL "WIN32") + if(MINGW) + set(CMAKE_RC_COMPILER_INIT windres) + ENABLE_LANGUAGE(RC) + SET(CMAKE_RC_COMPILE_OBJECT + " -O coff -i -o ") + endif(MINGW) + + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/resource.template.rc ${CMAKE_CURRENT_SOURCE_DIR}/resource.rc + @ONLY) + list(APPEND _SOURCE_FILES "resource.rc") +endif() + +# SDL2 dependency +find_package(SDL2 REQUIRED CONFIG) +set(_INCLUDE_DIRS ${SDL2_INCLUDE_DIRS}) +set(_LIBS ${SDL2_LIBRARIES}) + +if (_UNAME STREQUAL "DARWIN") + # MacOSX framework dependencies + list(APPEND _LIBS "-framework CoreGraphics" "-framework Foundation") +endif() + +if (_UNAME STREQUAL "LINUX") + # X11 dependency + find_package(X11 REQUIRED) + list(APPEND _INCLUDE_DIRS ${X11_INCLUDE_DIR}) + list(APPEND _LIBS ${X11_LIBRARIES}) +endif() + +option(LWP_INSTALL_LAUNCHD "Launch lwp on login (MacOSX only)" OFF) + +# Main executable +if (_UNAME STREQUAL "DARWIN") + add_executable(lwpwlp MACOSX_BUNDLE ${_SOURCE_FILES}) +else() + add_executable(lwpwlp ${_SOURCE_FILES}) +endif() + +# Windows specific properties for executable +if(_UNAME STREQUAL "WIN32") + set_property(TARGET lwpwlp PROPERTY VS_DPI_AWARE "PerMonitor") +endif() + +if(MSVC) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS") +endif() + +target_compile_definitions(lwpwlp PUBLIC __${_UNAME}) +target_include_directories(lwpwlp PUBLIC ${_INCLUDE_DIRS}) +target_link_libraries(lwpwlp PRIVATE ${_LIBS}) + +# Installation rules +if(_UNAME STREQUAL "WIN32") + install(TARGETS lwpwlp + DESTINATION .) +elseif(_UNAME STREQUAL "LINUX") + install(TARGETS lwpwlp + DESTINATION usr/local/bin) +else() + install(TARGETS lwpwlp + DESTINATION Layered_WallPaper) +endif() + diff --git a/debug.c b/src/wlp/debug.c similarity index 100% rename from debug.c rename to src/wlp/debug.c diff --git a/debug.h b/src/wlp/debug.h similarity index 100% rename from debug.h rename to src/wlp/debug.h diff --git a/src/wlp/main.c b/src/wlp/main.c new file mode 100644 index 0000000..5c66e9a --- /dev/null +++ b/src/wlp/main.c @@ -0,0 +1,95 @@ +#include "main.h" + +#include "debug.h" +#include "parser.h" +#include "platform_guard.h" +#include "wallpaper.h" +#include "window.h" + +#ifdef __WIN32 +static void initCmd() +{ + // Create console + AllocConsole(); + AttachConsole(ATTACH_PARENT_PROCESS); + freopen("CONOUT$", "w", stdout); + HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD dwMode = 0; + GetConsoleMode(hOut, &dwMode); + SetConsoleMode(hOut, dwMode | 0x0004); + + // Remove closing button (because closing it closes the entire app) + HWND hwnd = GetConsoleWindow(); + HMENU hMenu = GetSystemMenu(hwnd, FALSE); + DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND); + + // Set console title + SetConsoleTitle("Layered WallPaper"); +} +BOOL monitorenumproc(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM param) +{ + int px = GetSystemMetrics(SM_XVIRTUALSCREEN); + int py = GetSystemMetrics(SM_YVIRTUALSCREEN); + + MONITORINFO monitorInfo; + monitorInfo.cbSize = sizeof(MONITORINFO); + + GetMonitorInfo(monitor, &monitorInfo); + lwpLog( + LOG_INFO, + " Monitor: position %ldx%ld, size %ldx%ld %s", + monitorInfo.rcMonitor.left - px, + monitorInfo.rcMonitor.top - py, + monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left, + monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top, + (monitorInfo.dwFlags & MONITORINFOF_PRIMARY) == MONITORINFOF_PRIMARY ? "primary" : "" + ); + + return TRUE; +} +static void scanMonitors() +{ + lwpLog(LOG_INFO, "Scanning monitors..."); + EnumDisplayMonitors(NULL, NULL, &monitorenumproc, NULL); +} +#endif + +int main(int argc, char *argv[]) +{ + lwpLog(LOG_INFO, "Starting Layered WallPaper"); + +#ifdef __WIN32 + if (argc == 2 && strcmp(argv[1], "/console") == 0) initCmd(); + initTrayIcon(); + + scanMonitors(); +#endif + + Config cfg; + parseConfig(&cfg); + + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); + initWindow(&cfg); + + cfg.renderer = + SDL_CreateRenderer(cfg.window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); + if (cfg.renderer == NULL) + { + lwpLog(LOG_ERROR, "%s", SDL_GetError()); + } + + runWallpaperLoop(&cfg); + + +#ifdef __WIN32 + removeTrayIcon(); +#endif + + freeConfig(&cfg); + + SDL_DestroyRenderer(cfg.renderer); + SDL_DestroyWindow(cfg.window); + SDL_Quit(); + + return 0; +} diff --git a/main.h b/src/wlp/main.h similarity index 95% rename from main.h rename to src/wlp/main.h index 55d0e08..f30a5b3 100644 --- a/main.h +++ b/src/wlp/main.h @@ -49,20 +49,17 @@ typedef struct int wallpaperX, wallpaperY, wallpaperW, wallpaperH; SDL_Texture *tex; Wallpaper wallpaper; -} Monitor; +} WallpaperDest; + typedef struct { int reloadRootWnd; int monitorsCount; float smooth; int targetFPS; - Monitor *monitors; -} Config; - -typedef struct -{ + WallpaperDest *monitors; SDL_Window *window; SDL_Renderer *renderer; -} App; +} Config; #endif // MAIN_H diff --git a/parser.c b/src/wlp/parser.c similarity index 98% rename from parser.c rename to src/wlp/parser.c index 85b0c76..26a407c 100644 --- a/parser.c +++ b/src/wlp/parser.c @@ -146,7 +146,7 @@ static int findLine(FILE *f, const char *name, int type, void *output) return found; } -int parseConfig(App *app, Config *cfg) +int parseConfig(Config *cfg) { lwpLog(LOG_INFO, "Loading config file"); @@ -182,7 +182,7 @@ int parseConfig(App *app, Config *cfg) lwpLog(LOG_INFO, " reload_rootwindow: %d", cfg->reloadRootWnd); #endif - cfg->monitors = malloc(cfg->monitorsCount * sizeof(Monitor)); + cfg->monitors = malloc(cfg->monitorsCount * sizeof(WallpaperDest)); for (int m = 0; m < cfg->monitorsCount; m++) { diff --git a/parser.h b/src/wlp/parser.h similarity index 79% rename from parser.h rename to src/wlp/parser.h index 55fa65a..d20603c 100644 --- a/parser.h +++ b/src/wlp/parser.h @@ -3,7 +3,7 @@ #include "main.h" -int parseConfig(App *app, Config *cfg); +int parseConfig(Config *cfg); int parseWallpaperConfig(Wallpaper *wallpaper, const char *path); void freeConfig(Config *cfg); diff --git a/platform_guard.h b/src/wlp/platform_guard.h similarity index 100% rename from platform_guard.h rename to src/wlp/platform_guard.h diff --git a/resource.template.rc b/src/wlp/resource.template.rc similarity index 91% rename from resource.template.rc rename to src/wlp/resource.template.rc index 892e717..16de8c9 100644 --- a/resource.template.rc +++ b/src/wlp/resource.template.rc @@ -3,7 +3,7 @@ #define PROGRAM_VER "@PROGRAM_VERSION@" -id ICON "icon.ico" +id ICON "..\..\icon.ico" VS_VERSION_INFO VERSIONINFO FILEOS VOS_NT @@ -19,7 +19,7 @@ BEGIN VALUE "FileVersion", PROGRAM_VER VALUE "InternalName", "Layered WallPaper" VALUE "LegalCopyright", "(c) 2022 Jakub SzczerbiƄski" - VALUE "OriginalFilename", "lwp.exe" + VALUE "OriginalFilename", "lwpwlp.exe" VALUE "ProductName", "Layered WallPaper" VALUE "ProductVersion", PROGRAM_VER END diff --git a/src/wlp/wallpaper.c b/src/wlp/wallpaper.c new file mode 100644 index 0000000..edec74a --- /dev/null +++ b/src/wlp/wallpaper.c @@ -0,0 +1,211 @@ +#ifdef _MSC_VER +#include +#else +#include +#endif + +#include "debug.h" +#include "parser.h" + +static int appplyWallpaper(Config *cfg, WallpaperDest *monitor, Wallpaper *wallpaper, const char *dirPath) +{ + char path[PATH_MAX]; + + snprintf(path, PATH_MAX, "%s/wallpaper.cfg", dirPath); + + if (!parseWallpaperConfig(wallpaper, path)) + { + return 0; + } + + for (int i = 0; i < wallpaper->layersCount; i++) + { + snprintf(path, PATH_MAX, "%s/%d.bmp", dirPath, i + 1); + + SDL_Surface *surf = SDL_LoadBMP(path); + if (!surf) + { + lwpLog(LOG_ERROR, "File %s not found", path); + return 0; + } + + if (i == 0) + { + wallpaper->originalW = surf->w; + wallpaper->originalH = surf->h; + } + + wallpaper->layers[i].tex = SDL_CreateTextureFromSurface(cfg->renderer, surf); + + SDL_FreeSurface(surf); + } + + wallpaper->tex = + SDL_CreateTexture(cfg->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, + monitor->wallpaperW, monitor->wallpaperH); + + monitor->tex = SDL_CreateTexture(cfg->renderer, SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_TARGET, monitor->w, monitor->h); + + return 1; +} + +int applyWallpapers(Config *cfg) +{ + for (int m = 0; m < cfg->monitorsCount; m++) + if (!appplyWallpaper(cfg, &cfg->monitors[m], &cfg->monitors[m].wallpaper, + cfg->monitors[m].wallpaper.dirPath)) + return 0; + + return 1; +} + +void freeMonitor(WallpaperDest *monitor) +{ + SDL_DestroyTexture(monitor->tex); + SDL_DestroyTexture(monitor->wallpaper.tex); + + for (int i = 0; i < monitor->wallpaper.layersCount; i++) + { + SDL_DestroyTexture(monitor->wallpaper.layers[i].tex); + } + free(monitor->wallpaper.layers); +} + +static int lerp(int a, int b, float t) +{ + if (t > 1) t = 1; + return (int)((float)a + (float)t * ((float)b - (float)a)); +} + +void runWallpaperLoop(Config *cfg) +{ + int quit = 0; + + if(!applyWallpapers(cfg)) + { + lwpLog(LOG_ERROR, "Failed applying wallpapers"); + quit = 1; + } + + SDL_Event event; + + int mx = 0; + int my = 0; + + while (!quit) + { + static int currentX = 0; + static int currentY = 0; + + static int lastTicks = 0; + + int ticks = SDL_GetTicks(); + float dT = (ticks - lastTicks) / 1000.0f; + lastTicks = ticks; + +#ifdef __WIN32 + POINT mPos; + GetCursorPos(&mPos); + mx = mPos.x - GetSystemMetrics(SM_XVIRTUALSCREEN); + my = mPos.y - GetSystemMetrics(SM_YVIRTUALSCREEN); +#else + SDL_GetGlobalMouseState(&mx, &my); +#endif + + while (SDL_PollEvent(&event)) + if (event.type == SDL_QUIT) quit = 1; + + currentX = lerp(currentX, mx, dT * cfg->smooth); + currentY = lerp(currentY, my, dT * cfg->smooth); + + for (int m = 0; m < cfg->monitorsCount; m++) + { + int relativeCurrentX = currentX - cfg->monitors[m].x; + int relativeCurrentY = currentY - cfg->monitors[m].y; + + if (relativeCurrentX < 0) relativeCurrentX = 0; + if (relativeCurrentY < 0) relativeCurrentY = 0; + if (relativeCurrentX > cfg->monitors[m].w) relativeCurrentX = cfg->monitors[m].w; + if (relativeCurrentY > cfg->monitors[m].h) relativeCurrentY = cfg->monitors[m].h; + + SDL_SetRenderTarget(cfg->renderer, cfg->monitors[m].wallpaper.tex); + SDL_RenderClear(cfg->renderer); + + for (int i = 0; i < cfg->monitors[m].wallpaper.layersCount; i++) + { + SDL_Rect src = { + .x = 0, + .y = 0, + .w = cfg->monitors[m].wallpaper.originalW, + .h = cfg->monitors[m].wallpaper.originalH, + }; + + int x = + -((relativeCurrentX - cfg->monitors[m].w / 2) * + cfg->monitors[m].wallpaper.layers[i].sensitivityX); + int y = + -((relativeCurrentY - cfg->monitors[m].h / 2) * + cfg->monitors[m].wallpaper.layers[i].sensitivityY); + + for (int k = -cfg->monitors[m].wallpaper.repeatY; k <= cfg->monitors[m].wallpaper.repeatY; + k++) + { + for (int j = -cfg->monitors[m].wallpaper.repeatX; j <= cfg->monitors[m].wallpaper.repeatX; + j++) + { + SDL_Rect dest = { + .x = x + j * cfg->monitors[m].wallpaperW, + .y = y + k * cfg->monitors[m].wallpaperH, + .w = cfg->monitors[m].wallpaperW, + .h = cfg->monitors[m].wallpaperH, + }; + + SDL_RenderCopy(cfg->renderer, cfg->monitors[m].wallpaper.layers[i].tex, &src, &dest); + } + } + } + + SDL_SetRenderTarget(cfg->renderer, cfg->monitors[m].tex); + + SDL_Rect src = { + .x = 0, + .y = 0, + .w = cfg->monitors[m].wallpaperW, + .h = cfg->monitors[m].wallpaperH, + }; + + SDL_Rect dest = { + .x = cfg->monitors[m].wallpaperX, + .y = cfg->monitors[m].wallpaperY, + .w = cfg->monitors[m].wallpaperW, + .h = cfg->monitors[m].wallpaperH, + }; + + SDL_RenderCopy(cfg->renderer, cfg->monitors[m].wallpaper.tex, &src, &dest); + + SDL_SetRenderTarget(cfg->renderer, NULL); + + SDL_Rect finalSrc = { + .x = 0, + .y = 0, + .w = cfg->monitors[m].w, + .h = cfg->monitors[m].h, + }; + + SDL_Rect finalDest = { + .x = cfg->monitors[m].x, + .y = cfg->monitors[m].y, + .w = cfg->monitors[m].w, + .h = cfg->monitors[m].h, + }; + + SDL_RenderCopy(cfg->renderer, cfg->monitors[m].tex, &finalSrc, &finalDest); + } + SDL_RenderPresent(cfg->renderer); + SDL_Delay(1000 / cfg->targetFPS); +#ifdef __WIN32 + if (!updateTrayIcon()) quit = 1; +#endif + } +} \ No newline at end of file diff --git a/src/wlp/wallpaper.h b/src/wlp/wallpaper.h new file mode 100644 index 0000000..92b1631 --- /dev/null +++ b/src/wlp/wallpaper.h @@ -0,0 +1,10 @@ +#ifndef WALLPAPER_H +#define WALLPAPER_H + +#include "main.h" + +int applyWallpapers(Config *cfg); +void freeMonitor(WallpaperDest *monitor); +void runWallpaperLoop(Config *cfg); + +#endif diff --git a/window.c b/src/wlp/window.c similarity index 86% rename from window.c rename to src/wlp/window.c index 90aa3dd..68b6228 100644 --- a/window.c +++ b/src/wlp/window.c @@ -104,7 +104,7 @@ void initTrayIcon() wc.hInstance = hInstance; wc.lpszClassName = CLASS_NAME; RegisterClass(&wc); - HWND hWnd = CreateWindowEx(0, CLASS_NAME, L"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, + HWND hWnd = CreateWindowEx(0, CLASS_NAME, L"", WS_OVERLcfgEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); // Create tray icon @@ -118,15 +118,15 @@ void initTrayIcon() Shell_NotifyIcon(NIM_ADD, &nid); } -void initWindow(App *app, Config *cfg) +void initWindow(Config *cfg) { - app->window = + cfg->window = SDL_CreateWindow("Parallax wallpaper", 0, 0, 0, 0, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); - if (app->window == NULL) lwpLog(LOG_ERROR, "%s", SDL_GetError()); + if (cfg->window == NULL) lwpLog(LOG_ERROR, "%s", SDL_GetError()); SDL_SysWMinfo sysWmInfo; SDL_VERSION(&sysWmInfo.version) - SDL_GetWindowWMInfo(app->window, &sysWmInfo); + SDL_GetWindowWMInfo(cfg->window, &sysWmInfo); HWND hWindow = sysWmInfo.info.win.window; HWND progman = FindWindow("Progman", NULL); @@ -155,7 +155,7 @@ void initWindow(App *app, Config *cfg) #define OBJC_MSG_CLS ((id(*)(Class, SEL))objc_msgSend) #define OBJC_MSG_CLS_CHR ((id(*)(Class, SEL, char *))objc_msgSend) -void initWindow(App *app, Config *cfg) +void initWindow(Config *cfg) { // Get main display size const CGDirectDisplayID displayID = CGMainDisplayID(); @@ -163,10 +163,10 @@ void initWindow(App *app, Config *cfg) const size_t h = CGDisplayPixelsHigh(displayID); const struct CGRect frameRect = {0, 0, w, h}; - // Get shared NSApplication instance - const id ns_app = OBJC_MSG_CLS(objc_getClass("NSApplication"), sel_getUid("sharedApplication")); - OBJC_MSG_INT(ns_app, sel_getUid("setActivationPolicy:"), - 0); // NSApplicationActivationPolicyRegular + // Get shared NScfglication instance + const id ns_cfg = OBJC_MSG_CLS(objc_getClass("NScfglication"), sel_getUid("sharedcfglication")); + OBJC_MSG_INT(ns_cfg, sel_getUid("setActivationPolicy:"), + 0); // NScfglicationActivationPolicyRegular // Create NSWindow const id window = ((id(*)(id, SEL, struct CGRect, int, int, int))objc_msgSend)( @@ -182,28 +182,28 @@ void initWindow(App *app, Config *cfg) "Parallax wallpaper")); OBJC_MSG_PTR(window, sel_getUid("makeKeyAndOrderFront:"), nil); OBJC_MSG_INT(window, sel_getUid("setLevel:"), kCGDesktopWindowLevel - 1); - OBJC_MSG_INT(ns_app, sel_getUid("activateIgnoringOtherApps:"), true); + OBJC_MSG_INT(ns_cfg, sel_getUid("activateIgnoringOthercfgs:"), true); // Create SDL window from NSWindow - app->window = SDL_CreateWindowFrom((void *)window); - if (app->window == NULL) lwpLog(LOG_ERROR, "%s", SDL_GetError()); + cfg->window = SDL_CreateWindowFrom((void *)window); + if (cfg->window == NULL) lwpLog(LOG_ERROR, "%s", SDL_GetError()); } #elif __LINUX -void initWindow(App *app, Config *cfg) +void initWindow(Config *cfg) { if (cfg->reloadRootWnd) { Display *display = XOpenDisplay(NULL); XCloseDisplay(display); - app->window = SDL_CreateWindow("Parallax wallpaper", 0, 0, DisplayWidth(display, 0), + cfg->window = SDL_CreateWindow("Parallax wallpaper", 0, 0, DisplayWidth(display, 0), DisplayHeight(display, 0), SDL_WINDOW_OPENGL); SDL_SysWMinfo wmInfo; SDL_GetVersion(&wmInfo.version); - SDL_GetWindowWMInfo(app->window, &wmInfo); + SDL_GetWindowWMInfo(cfg->window, &wmInfo); Window xWnd = wmInfo.info.x11.window; display = wmInfo.info.x11.display; @@ -223,10 +223,11 @@ void initWindow(App *app, Config *cfg) { Display *display = XOpenDisplay(NULL); Window rootWindow = RootWindow(display, DefaultScreen(display)); - app->window = SDL_CreateWindowFrom((void *)rootWindow); + cfg->window = SDL_CreateWindowFrom((void *)rootWindow); XCloseDisplay(display); } - if (app->window == NULL) lwpLog(LOG_ERROR, "%s", SDL_GetError()); + if (cfg->window == NULL) lwpLog(LOG_ERROR, "%s", SDL_GetError()); + } #endif diff --git a/window.h b/src/wlp/window.h similarity index 81% rename from window.h rename to src/wlp/window.h index b69063f..fc4a498 100644 --- a/window.h +++ b/src/wlp/window.h @@ -4,7 +4,7 @@ #include "main.h" #include "parser.h" -void initWindow(App *app, Config *cfg); +void initWindow(Config *cfg); #ifdef __WIN32 void initTrayIcon(); diff --git a/wallpaper.c b/wallpaper.c deleted file mode 100644 index a57d906..0000000 --- a/wallpaper.c +++ /dev/null @@ -1,73 +0,0 @@ -#ifdef _MSC_VER -#include -#else -#include -#endif - -#include "debug.h" -#include "parser.h" - -static int loadMonitor(App *app, Monitor *monitor, Wallpaper *wallpaper, const char *dirPath) -{ - char path[PATH_MAX]; - - snprintf(path, PATH_MAX, "%s/wallpaper.cfg", dirPath); - - if (!parseWallpaperConfig(wallpaper, path)) - { - return 0; - } - - for (int i = 0; i < wallpaper->layersCount; i++) - { - snprintf(path, PATH_MAX, "%s/%d.bmp", dirPath, i + 1); - - SDL_Surface *surf = SDL_LoadBMP(path); - if (!surf) - { - lwpLog(LOG_ERROR, "File %s not found", path); - return 0; - } - - if (i == 0) - { - wallpaper->originalW = surf->w; - wallpaper->originalH = surf->h; - } - - wallpaper->layers[i].tex = SDL_CreateTextureFromSurface(app->renderer, surf); - - SDL_FreeSurface(surf); - } - - wallpaper->tex = - SDL_CreateTexture(app->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, - monitor->wallpaperW, monitor->wallpaperH); - - monitor->tex = SDL_CreateTexture(app->renderer, SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_TARGET, monitor->w, monitor->h); - - return 1; -} - -int loadMonitors(App *app, Config *cfg) -{ - for (int m = 0; m < cfg->monitorsCount; m++) - if (!loadMonitor(app, &cfg->monitors[m], &cfg->monitors[m].wallpaper, - cfg->monitors[m].wallpaper.dirPath)) - return 0; - - return 1; -} - -void freeMonitor(Monitor *monitor) -{ - SDL_DestroyTexture(monitor->tex); - SDL_DestroyTexture(monitor->wallpaper.tex); - - for (int i = 0; i < monitor->wallpaper.layersCount; i++) - { - SDL_DestroyTexture(monitor->wallpaper.layers[i].tex); - } - free(monitor->wallpaper.layers); -} diff --git a/wallpaper.h b/wallpaper.h deleted file mode 100644 index 11da0eb..0000000 --- a/wallpaper.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef WALLPAPER_H -#define WALLPAPER_H - -#include "main.h" - -int loadMonitors(App *app, Config *cfg); -void freeMonitor(Monitor *monitor); - -#endif From 814ad6cc8df65faf9d72f5d34ba20cdb0e1473d7 Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Sun, 31 Dec 2023 17:23:46 +0000 Subject: [PATCH 02/33] Wallpaper subprocess --- src/core/main.c | 53 ++++++++++-- src/window_templates/main.glade | 137 ++++++++++++++++++++++++++------ src/wlp/main.c | 39 ++++++--- src/wlp/main.h | 2 + 4 files changed, 189 insertions(+), 42 deletions(-) diff --git a/src/core/main.c b/src/core/main.c index d1bce05..bd6cf22 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -8,13 +8,56 @@ #define MAIN_WINDOW_TEMPLATE_PATH "/usr/local/share/lwp/window_templates/main.glade" #endif +GtkBuilder *builder = NULL; +static FILE *wlpProcess = NULL; +int wlpPid = 0; + +static void killWlp() +{ + if(wlpPid != 0) + kill(wlpPid, SIGINT); + if(wlpProcess != NULL) + pclose(wlpProcess); + + wlpPid = 0; + wlpProcess = NULL; +} + +static void runWlp() +{ + wlpProcess = popen("/home/cziken/Projects/lwp/build/src/wlp/lwpwlp", "r"); + char buff[10]; + fgets(buff, sizeof(buff) - 1, wlpProcess); + wlpPid = atoi(buff); +} + +static gboolean exitClicked(GtkWidget *btn, GdkEvent *event, gpointer userdata) +{ + killWlp(); + g_application_quit(G_APPLICATION(userdata)); +} + +static gboolean deleted(GtkWidget *window, GdkEvent *event, gpointer userdata) +{ + gtk_widget_hide(window); +} + static void activate(GtkApplication *app, gpointer userdata) { - GtkBuilder *builder = gtk_builder_new_from_file(MAIN_WINDOW_TEMPLATE_PATH); - GtkWidget *window = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow"); - if (window == NULL) printf("wnd not found\n"); - gtk_window_set_application(GTK_WINDOW(window), GTK_APPLICATION(app)); - gtk_widget_set_visible(window, 1); + builder = gtk_builder_new_from_file(MAIN_WINDOW_TEMPLATE_PATH); + GtkWidget *window = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow"); + if (window == NULL) printf("wnd not found\n"); + + g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(deleted), NULL); + + GtkWidget *exitBtn = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow_ExitBtn"); + + g_signal_connect(G_OBJECT(exitBtn), "clicked", G_CALLBACK(exitClicked), app); + + gtk_window_set_application(GTK_WINDOW(window), GTK_APPLICATION(app)); + gtk_widget_set_visible(window, 1); + + runWlp(); } int main(int argc, char *argv[]) diff --git a/src/window_templates/main.glade b/src/window_templates/main.glade index bd863eb..173c979 100644 --- a/src/window_templates/main.glade +++ b/src/window_templates/main.glade @@ -2,6 +2,66 @@ + + False + dialog + + + False + vertical + 2 + + + False + end + + + Yes + True + True + True + + + True + True + 0 + + + + + No + True + True + True + + + True + True + 1 + + + + + False + False + 0 + + + + + True + False + Are you sure, you want to exit Layered WallPaper? + + + False + True + 1 + + + + + False False @@ -17,32 +77,32 @@ True False - - Github page + True - True - True - none - https://github.com/jszczerbinsky/lwp + False + 11 + v 1.0.0 False True - end - 0 + 1 - + + Github page True - False - 11 - v 1.0.0 + True + True + none + https://github.com/jszczerbinsky/lwp False True - 1 + end + 2 @@ -57,33 +117,60 @@ True False - vertical - + True False - 10 - 10 - Layered WallPaper - - - + vertical + + + True + False + 10 + 10 + Layered WallPaper + center + + + + + + False + True + 0 + + + + + True + False + Enhance your desktop experiance + center + + + False + True + 1 + + - False + True True 0 - + + Exit True - False - Enhance your desktop experiance + True + True False True + end 1 diff --git a/src/wlp/main.c b/src/wlp/main.c index 5c66e9a..0f68eb6 100644 --- a/src/wlp/main.c +++ b/src/wlp/main.c @@ -54,8 +54,30 @@ static void scanMonitors() } #endif +static Config cfg; + +static void atExit() +{ + freeConfig(&cfg); + + SDL_DestroyRenderer(cfg.renderer); + SDL_DestroyWindow(cfg.window); + SDL_Quit(); +} + +void exitSignalHandler(int s) +{ + exit(0); +} + int main(int argc, char *argv[]) { + char pidStr[10]; + sprintf(pidStr, "%d\0", getpid()); + fputs(pidStr, stdout); + + signal(SIGINT, exitSignalHandler); + lwpLog(LOG_INFO, "Starting Layered WallPaper"); #ifdef __WIN32 @@ -65,31 +87,24 @@ int main(int argc, char *argv[]) scanMonitors(); #endif - Config cfg; parseConfig(&cfg); SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); initWindow(&cfg); + atexit(atExit); + cfg.renderer = SDL_CreateRenderer(cfg.window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); if (cfg.renderer == NULL) { lwpLog(LOG_ERROR, "%s", SDL_GetError()); } - runWallpaperLoop(&cfg); - -#ifdef __WIN32 - removeTrayIcon(); -#endif - - freeConfig(&cfg); - - SDL_DestroyRenderer(cfg.renderer); - SDL_DestroyWindow(cfg.window); - SDL_Quit(); + #ifdef __WIN32 + removeTrayIcon(); + #endif return 0; } diff --git a/src/wlp/main.h b/src/wlp/main.h index f30a5b3..09de760 100644 --- a/src/wlp/main.h +++ b/src/wlp/main.h @@ -18,12 +18,14 @@ #include #include #include +#include #elif __LINUX #include #include #include #include #include +#include #endif typedef struct From cc55b03045333775f873c91626a7df4fbbda76d6 Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Sun, 31 Dec 2023 17:46:24 +0000 Subject: [PATCH 03/33] Exit dialog --- src/core/main.c | 31 +++++++++++++++++++++++++------ src/window_templates/main.glade | 4 ++-- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/core/main.c b/src/core/main.c index bd6cf22..d62e5af 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -9,6 +9,7 @@ #endif GtkBuilder *builder = NULL; +GtkWidget *exitDialog = NULL; static FILE *wlpProcess = NULL; int wlpPid = 0; @@ -31,28 +32,46 @@ static void runWlp() wlpPid = atoi(buff); } +static gboolean exitDialogResult(GtkWidget *btn, GdkEvent *event, gpointer userdata) +{ + if(userdata != NULL) + { + killWlp(); + g_application_quit(G_APPLICATION(userdata)); + } + else + { + gtk_widget_set_visible(exitDialog, 0); + } +} + static gboolean exitClicked(GtkWidget *btn, GdkEvent *event, gpointer userdata) { - killWlp(); - g_application_quit(G_APPLICATION(userdata)); + gtk_widget_set_visible(exitDialog, 1); } static gboolean deleted(GtkWidget *window, GdkEvent *event, gpointer userdata) { - gtk_widget_hide(window); + gtk_widget_set_visible(window,0); } static void activate(GtkApplication *app, gpointer userdata) { builder = gtk_builder_new_from_file(MAIN_WINDOW_TEMPLATE_PATH); + GtkWidget *window = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow"); - if (window == NULL) printf("wnd not found\n"); - g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(deleted), NULL); - GtkWidget *exitBtn = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow_ExitBtn"); + exitDialog = (GtkWidget *)gtk_builder_get_object(builder, "ExitDialog"); + + GtkWidget *exitDialogYesBtn =(GtkWidget *)gtk_builder_get_object(builder, "ExitDialog_Yes"); + g_signal_connect(G_OBJECT(exitDialogYesBtn), "clicked", G_CALLBACK(exitDialogResult), app); + GtkWidget *exitDialogNoBtn =(GtkWidget *)gtk_builder_get_object(builder, "ExitDialog_No"); + g_signal_connect(G_OBJECT(exitDialogNoBtn), "clicked", G_CALLBACK(exitDialogResult), NULL); + GtkWidget *exitBtn = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow_ExitBtn"); g_signal_connect(G_OBJECT(exitBtn), "clicked", G_CALLBACK(exitClicked), app); + gtk_window_set_application(GTK_WINDOW(window), GTK_APPLICATION(app)); gtk_widget_set_visible(window, 1); diff --git a/src/window_templates/main.glade b/src/window_templates/main.glade index 173c979..7556ee1 100644 --- a/src/window_templates/main.glade +++ b/src/window_templates/main.glade @@ -15,7 +15,7 @@ False end - + Yes True True @@ -28,7 +28,7 @@ - + No True True From 99837c73e420a8799fd1aa189f8897045c71ddc1 Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Sun, 31 Dec 2023 23:10:56 +0000 Subject: [PATCH 04/33] Monitor scanner --- src/core/CMakeLists.txt | 15 ++ src/core/main.c | 77 ++++----- src/core/main.h | 41 +++++ src/core/monitorScanner.c | 31 ++++ src/core/windowHandlers.c | 48 ++++++ src/window_templates/main.glade | 285 ++++++++++++++++++++++---------- 6 files changed, 369 insertions(+), 128 deletions(-) create mode 100644 src/core/main.h create mode 100644 src/core/monitorScanner.c create mode 100644 src/core/windowHandlers.c diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index f59c767..3921755 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,5 +1,7 @@ set(_SOURCE_FILES main.c + windowHandlers.c + monitorScanner.c ) # Windows resource file @@ -16,6 +18,17 @@ if(_UNAME STREQUAL "WIN32") list(APPEND _SOURCE_FILES "resource.rc") endif() +if (_UNAME STREQUAL "LINUX") + # X11 and Xrandr dependency + include(FindX11) + + list(APPEND _INCLUDE_DIRS ${X11_INCLUDE_DIR}) + list(APPEND _LIBS ${X11_LIBRARIES}) + + list(APPEND _INCLUDE_DIRS ${X11_Xrandr_INCLUDE_PATH}) + list(APPEND _LIBS ${X11_Xrandr_LIB}) +endif() + # GTK dependency find_package (PkgConfig REQUIRED) pkg_check_modules (GTK3 REQUIRED gtk+-3.0) @@ -33,6 +46,8 @@ if(MSVC) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS") endif() +set_target_properties(lwp PROPERTIES ENABLE_EXPORTS on) + target_compile_definitions(lwp PUBLIC __${_UNAME}) target_include_directories(lwp PUBLIC ${_INCLUDE_DIRS}) target_link_libraries(lwp PRIVATE ${_LIBS}) diff --git a/src/core/main.c b/src/core/main.c index d62e5af..69761a8 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1,4 +1,4 @@ -#include +#include "main.h" #ifdef __WIN32 #define MAIN_WINDOW_TEMPLATE_PATH "" @@ -8,12 +8,18 @@ #define MAIN_WINDOW_TEMPLATE_PATH "/usr/local/share/lwp/window_templates/main.glade" #endif +GtkApplication *app = NULL; GtkBuilder *builder = NULL; + +GtkWidget *mainWnd = NULL; GtkWidget *exitDialog = NULL; +GtkWidget *wallpaperMgrWnd = NULL; +GtkWidget *screenWnd = NULL; + static FILE *wlpProcess = NULL; -int wlpPid = 0; +static int wlpPid = 0; -static void killWlp() +void killWlp() { if(wlpPid != 0) kill(wlpPid, SIGINT); @@ -32,58 +38,39 @@ static void runWlp() wlpPid = atoi(buff); } -static gboolean exitDialogResult(GtkWidget *btn, GdkEvent *event, gpointer userdata) -{ - if(userdata != NULL) - { - killWlp(); - g_application_quit(G_APPLICATION(userdata)); - } - else - { - gtk_widget_set_visible(exitDialog, 0); - } -} - -static gboolean exitClicked(GtkWidget *btn, GdkEvent *event, gpointer userdata) -{ - gtk_widget_set_visible(exitDialog, 1); -} - -static gboolean deleted(GtkWidget *window, GdkEvent *event, gpointer userdata) -{ - gtk_widget_set_visible(window,0); -} - static void activate(GtkApplication *app, gpointer userdata) { - builder = gtk_builder_new_from_file(MAIN_WINDOW_TEMPLATE_PATH); - - GtkWidget *window = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow"); - g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(deleted), NULL); - - exitDialog = (GtkWidget *)gtk_builder_get_object(builder, "ExitDialog"); + static int alreadyRunning = 0; - GtkWidget *exitDialogYesBtn =(GtkWidget *)gtk_builder_get_object(builder, "ExitDialog_Yes"); - g_signal_connect(G_OBJECT(exitDialogYesBtn), "clicked", G_CALLBACK(exitDialogResult), app); - GtkWidget *exitDialogNoBtn =(GtkWidget *)gtk_builder_get_object(builder, "ExitDialog_No"); - g_signal_connect(G_OBJECT(exitDialogNoBtn), "clicked", G_CALLBACK(exitDialogResult), NULL); - - GtkWidget *exitBtn = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow_ExitBtn"); - g_signal_connect(G_OBJECT(exitBtn), "clicked", G_CALLBACK(exitClicked), app); - - - gtk_window_set_application(GTK_WINDOW(window), GTK_APPLICATION(app)); - gtk_widget_set_visible(window, 1); + if(!alreadyRunning) + { + alreadyRunning = 1; + + builder = gtk_builder_new_from_file(MAIN_WINDOW_TEMPLATE_PATH); + gtk_builder_connect_signals(builder, NULL); + + mainWnd = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow"); + exitDialog = (GtkWidget *)gtk_builder_get_object(builder, "ExitDialog"); + wallpaperMgrWnd = (GtkWidget *)gtk_builder_get_object(builder, "WallpaperManagerWindow"); + screenWnd = (GtkWidget *)gtk_builder_get_object(builder, "ScreenWindow"); + + gtk_window_set_application(GTK_WINDOW(mainWnd), GTK_APPLICATION(app)); + gtk_window_set_application(GTK_WINDOW(exitDialog), GTK_APPLICATION(app)); + gtk_window_set_application(GTK_WINDOW(wallpaperMgrWnd), GTK_APPLICATION(app)); + gtk_window_set_application(GTK_WINDOW(screenWnd), GTK_APPLICATION(app)); + runWlp(); + } - runWlp(); + gtk_widget_set_visible(mainWnd, 1); } int main(int argc, char *argv[]) { - GtkApplication *app; int status; + Monitor *monitors = scanMonitors(); + free(monitors); + app = gtk_application_new ("com.github.jszczerbinsky.lwp", G_APPLICATION_DEFAULT_FLAGS); g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); status = g_application_run (G_APPLICATION (app), argc, argv); diff --git a/src/core/main.h b/src/core/main.h new file mode 100644 index 0000000..c978773 --- /dev/null +++ b/src/core/main.h @@ -0,0 +1,41 @@ +#ifndef MAIN_H +#define MAIN_H + +#include + +#define MONITOR_NAME_MAX 100 + +typedef struct +{ + int x; + int y; + int w; + int h; +} Bounds; + +typedef struct +{ + int set; + char wlpPath[PATH_MAX]; + Bounds wlpBounds; +} MonitorConfig; + +typedef struct +{ + char name[MONITOR_NAME_MAX]; + Bounds bounds; + MonitorConfig config; +} Monitor; + +extern GtkApplication *app; + +extern GtkWidget *mainWnd; +extern GtkWidget *exitDialog; +extern GtkWidget *wallpaperMgrWnd; +extern GtkWidget *screenWnd; + +void killWlp(); + +Monitor* scanMonitors(); + +#endif \ No newline at end of file diff --git a/src/core/monitorScanner.c b/src/core/monitorScanner.c new file mode 100644 index 0000000..7e160ba --- /dev/null +++ b/src/core/monitorScanner.c @@ -0,0 +1,31 @@ +#include "main.h" + +#include +#include +#include +#include + +Monitor* scanMonitors() +{ + int monitorCount; + Display *display = XOpenDisplay((getenv("DISPLAY"))); + Window wnd = DefaultRootWindow(display); + XRRMonitorInfo *info = XRRGetMonitors(display, wnd, 0, &monitorCount); + + Monitor *m = malloc(sizeof(Monitor) * monitorCount); + + int i = 0; + while (i < monitorCount) + { + snprintf(m[i].name, MONITOR_NAME_MAX, "%s", XGetAtomName(display, info->name)); + m[i].bounds.x = info->x; + m[i].bounds.y = info->y; + m[i].bounds.w = info->width; + m[i].bounds.h = info->height; + m[i].config.set = 0; + + info++; + i++; + } + return m; +} \ No newline at end of file diff --git a/src/core/windowHandlers.c b/src/core/windowHandlers.c new file mode 100644 index 0000000..91449c7 --- /dev/null +++ b/src/core/windowHandlers.c @@ -0,0 +1,48 @@ +#include "main.h" + +// Exit Dialog handlers + +G_MODULE_EXPORT void ExitDialogClose() +{ + gtk_widget_set_visible(exitDialog,0); +} + +G_MODULE_EXPORT void ExitDialog_No() +{ + gtk_widget_set_visible(exitDialog, 0); +} + +G_MODULE_EXPORT void ExitDialog_Yes() +{ + killWlp(); + g_application_quit(G_APPLICATION(app)); +} + +// Main Window handlers + +G_MODULE_EXPORT void MainWindow_ManageWallpapersBtnClick() +{ + gtk_widget_set_visible(wallpaperMgrWnd, 1); +} + +G_MODULE_EXPORT void MainWindow_ExitBtnClick() +{ + gtk_widget_set_visible(exitDialog, 1); +} + +G_MODULE_EXPORT void MainWindowClose() +{ + gtk_widget_set_visible(mainWnd,0); +} + +// Wallpapaer Manager Window handlers + +G_MODULE_EXPORT void WallpaperManagerWindowClose() +{ + gtk_widget_set_visible(wallpaperMgrWnd,0); +} + +G_MODULE_EXPORT void ScreenWindowClose() +{ + gtk_widget_set_visible(screenWnd,0); +} \ No newline at end of file diff --git a/src/window_templates/main.glade b/src/window_templates/main.glade index 7556ee1..ad77c14 100644 --- a/src/window_templates/main.glade +++ b/src/window_templates/main.glade @@ -4,7 +4,10 @@ False + False dialog + False + False @@ -15,11 +18,12 @@ False end - + Yes True True True + True @@ -28,11 +32,12 @@ - + No True True True + True @@ -67,6 +72,7 @@ False 800 600 + True @@ -76,101 +82,35 @@ True False + vertical True False - 11 - v 1.0.0 - - - False - True - 1 - - - - - Github page - True - True - True - none - https://github.com/jszczerbinsky/lwp + 10 + 10 + Layered WallPaper + center + + + False True - end - 2 - - - - - False - True - end - 0 - - - - - True - False - - - True - False - vertical - - - True - False - 10 - 10 - Layered WallPaper - center - - - - - - False - True - 0 - - - - - True - False - Enhance your desktop experiance - center - - - False - True - 1 - - - - - True - True 0 - - Exit + True - True - True + False + Enhance your desktop experiance + center False True - end 1 @@ -178,7 +118,7 @@ False True - 1 + 0 @@ -187,7 +127,7 @@ False 120 120 - 50 + 8 vertical @@ -207,9 +147,11 @@ - + True False + 40 + 40 True @@ -253,6 +195,183 @@ True True + 1 + + + + + True + False + + + True + False + 11 + v 1.0.0 + + + False + True + 1 + + + + + Github page + True + True + True + none + https://github.com/jszczerbinsky/lwp + + + False + True + end + 2 + + + + + False + True + end + 2 + + + + + True + False + center + + + Manage wallpapers + True + True + True + + + + False + True + 0 + + + + + Exit + True + True + True + + + + False + True + end + 1 + + + + + False + True + 3 + + + + + + + False + + + + True + False + vertical + + + + + + + + + + + False + + + + True + False + vertical + + + True + False + 10 + 10 + Manage wallpapers + + + + + + False + True + 0 + + + + + True + False + + + False + True + 1 + + + + + True + False + center + + + Add + True + True + True + + + False + True + 0 + + + + + Remove + True + True + True + + + False + True + 1 + + + + + False + True + end 2 From ff6042627ba2bf990fdd1e3f55e7766d4d48497d Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Tue, 2 Jan 2024 10:59:45 +0000 Subject: [PATCH 05/33] Monitor list box --- src/core/main.c | 131 +++++++++++++++++++------------- src/core/main.h | 27 +++---- src/core/monitorScanner.c | 5 +- src/core/windowHandlers.c | 36 +++------ src/window_templates/main.glade | 32 -------- 5 files changed, 107 insertions(+), 124 deletions(-) diff --git a/src/core/main.c b/src/core/main.c index 69761a8..d951a83 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1,80 +1,109 @@ #include "main.h" #ifdef __WIN32 - #define MAIN_WINDOW_TEMPLATE_PATH "" +#define MAIN_WINDOW_TEMPLATE_PATH "" #elif __DARWIN - #define MAIN_WINDOW_TEMPLATE_PATH "" +#define MAIN_WINDOW_TEMPLATE_PATH "" #else - #define MAIN_WINDOW_TEMPLATE_PATH "/usr/local/share/lwp/window_templates/main.glade" +#define MAIN_WINDOW_TEMPLATE_PATH "/usr/local/share/lwp/window_templates/main.glade" #endif -GtkApplication *app = NULL; -GtkBuilder *builder = NULL; +GtkApplication *app = NULL; +GtkBuilder *builder = NULL; -GtkWidget *mainWnd = NULL; -GtkWidget *exitDialog = NULL; +GtkWidget *mainWnd = NULL; +GtkWidget *exitDialog = NULL; GtkWidget *wallpaperMgrWnd = NULL; -GtkWidget *screenWnd = NULL; +GtkWidget *screenWnd = NULL; +GtkWidget *screenListBox = NULL; static FILE *wlpProcess = NULL; -static int wlpPid = 0; +static int wlpPid = 0; void killWlp() { - if(wlpPid != 0) - kill(wlpPid, SIGINT); - if(wlpProcess != NULL) - pclose(wlpProcess); + if (wlpPid != 0) kill(wlpPid, SIGINT); + if (wlpProcess != NULL) pclose(wlpProcess); - wlpPid = 0; - wlpProcess = NULL; + wlpPid = 0; + wlpProcess = NULL; } static void runWlp() { - wlpProcess = popen("/home/cziken/Projects/lwp/build/src/wlp/lwpwlp", "r"); - char buff[10]; - fgets(buff, sizeof(buff) - 1, wlpProcess); - wlpPid = atoi(buff); + wlpProcess = popen("/home/cziken/Projects/lwp/build/src/wlp/lwpwlp", "r"); + char buff[10]; + fgets(buff, sizeof(buff) - 1, wlpProcess); + wlpPid = atoi(buff); +} + +static void reloadScreenListBox() +{ + GList *rows = gtk_container_get_children(GTK_CONTAINER(screenListBox)); + + GList *ptr = rows; + while (ptr) + { + gtk_container_remove(GTK_CONTAINER(screenListBox), ptr->data); + ptr = ptr->next; + } + + g_list_free(rows); + + int monitorsCount; + Monitor *monitors; + + monitors = scanMonitors(&monitorsCount); + + for (int i = 0; i < 1; i++) + { + GtkWidget *label = gtk_label_new(monitors[i].name); + GtkWidget *row = gtk_list_box_row_new(); + gtk_container_add(GTK_CONTAINER(row), label); + + gtk_list_box_insert(GTK_LIST_BOX(screenListBox), row, 0); + gtk_widget_show_all(row); + } } static void activate(GtkApplication *app, gpointer userdata) { - static int alreadyRunning = 0; - - if(!alreadyRunning) - { - alreadyRunning = 1; - - builder = gtk_builder_new_from_file(MAIN_WINDOW_TEMPLATE_PATH); - gtk_builder_connect_signals(builder, NULL); - - mainWnd = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow"); - exitDialog = (GtkWidget *)gtk_builder_get_object(builder, "ExitDialog"); - wallpaperMgrWnd = (GtkWidget *)gtk_builder_get_object(builder, "WallpaperManagerWindow"); - screenWnd = (GtkWidget *)gtk_builder_get_object(builder, "ScreenWindow"); - - gtk_window_set_application(GTK_WINDOW(mainWnd), GTK_APPLICATION(app)); - gtk_window_set_application(GTK_WINDOW(exitDialog), GTK_APPLICATION(app)); - gtk_window_set_application(GTK_WINDOW(wallpaperMgrWnd), GTK_APPLICATION(app)); - gtk_window_set_application(GTK_WINDOW(screenWnd), GTK_APPLICATION(app)); - runWlp(); - } - - gtk_widget_set_visible(mainWnd, 1); + static int alreadyRunning = 0; + + if (!alreadyRunning) + { + alreadyRunning = 1; + + builder = gtk_builder_new_from_file(MAIN_WINDOW_TEMPLATE_PATH); + gtk_builder_connect_signals(builder, NULL); + + mainWnd = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow"); + exitDialog = (GtkWidget *)gtk_builder_get_object(builder, "ExitDialog"); + wallpaperMgrWnd = (GtkWidget *)gtk_builder_get_object(builder, "WallpaperManagerWindow"); + screenWnd = (GtkWidget *)gtk_builder_get_object(builder, "ScreenWindow"); + screenListBox = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow_ScreenListBox"); + + gtk_window_set_application(GTK_WINDOW(mainWnd), GTK_APPLICATION(app)); + gtk_window_set_application(GTK_WINDOW(exitDialog), GTK_APPLICATION(app)); + gtk_window_set_application(GTK_WINDOW(wallpaperMgrWnd), GTK_APPLICATION(app)); + gtk_window_set_application(GTK_WINDOW(screenWnd), GTK_APPLICATION(app)); + + runWlp(); + } + + reloadScreenListBox(); + + gtk_widget_set_visible(mainWnd, 1); } int main(int argc, char *argv[]) { - int status; + int status; - Monitor *monitors = scanMonitors(); - free(monitors); + app = gtk_application_new("com.github.jszczerbinsky.lwp", G_APPLICATION_DEFAULT_FLAGS); + g_signal_connect(app, "activate", G_CALLBACK(activate), NULL); + status = g_application_run(G_APPLICATION(app), argc, argv); + g_object_unref(app); - app = gtk_application_new ("com.github.jszczerbinsky.lwp", G_APPLICATION_DEFAULT_FLAGS); - g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); - status = g_application_run (G_APPLICATION (app), argc, argv); - g_object_unref (app); - - return status; -} \ No newline at end of file + return status; +} diff --git a/src/core/main.h b/src/core/main.h index c978773..2c2e46a 100644 --- a/src/core/main.h +++ b/src/core/main.h @@ -7,35 +7,36 @@ typedef struct { - int x; - int y; - int w; - int h; + int x; + int y; + int w; + int h; } Bounds; typedef struct { - int set; - char wlpPath[PATH_MAX]; - Bounds wlpBounds; + int set; + char wlpPath[PATH_MAX]; + Bounds wlpBounds; } MonitorConfig; typedef struct { - char name[MONITOR_NAME_MAX]; - Bounds bounds; - MonitorConfig config; + char name[MONITOR_NAME_MAX]; + Bounds bounds; + MonitorConfig config; } Monitor; extern GtkApplication *app; -extern GtkWidget *mainWnd; +extern GtkWidget *mainWnd; extern GtkWidget *exitDialog; extern GtkWidget *wallpaperMgrWnd; extern GtkWidget *screenWnd; +extern GtkWidget *screenListBox; void killWlp(); -Monitor* scanMonitors(); +Monitor *scanMonitors(int *count); -#endif \ No newline at end of file +#endif diff --git a/src/core/monitorScanner.c b/src/core/monitorScanner.c index 7e160ba..28f5ebc 100644 --- a/src/core/monitorScanner.c +++ b/src/core/monitorScanner.c @@ -5,7 +5,7 @@ #include #include -Monitor* scanMonitors() +Monitor* scanMonitors(int *count) { int monitorCount; Display *display = XOpenDisplay((getenv("DISPLAY"))); @@ -27,5 +27,8 @@ Monitor* scanMonitors() info++; i++; } + + *count = monitorCount; + return m; } \ No newline at end of file diff --git a/src/core/windowHandlers.c b/src/core/windowHandlers.c index 91449c7..462fef9 100644 --- a/src/core/windowHandlers.c +++ b/src/core/windowHandlers.c @@ -2,47 +2,29 @@ // Exit Dialog handlers -G_MODULE_EXPORT void ExitDialogClose() -{ - gtk_widget_set_visible(exitDialog,0); -} +G_MODULE_EXPORT void ExitDialogClose() { gtk_widget_set_visible(exitDialog, 0); } -G_MODULE_EXPORT void ExitDialog_No() -{ - gtk_widget_set_visible(exitDialog, 0); -} +G_MODULE_EXPORT void ExitDialog_No() { gtk_widget_set_visible(exitDialog, 0); } G_MODULE_EXPORT void ExitDialog_Yes() { - killWlp(); - g_application_quit(G_APPLICATION(app)); + killWlp(); + g_application_quit(G_APPLICATION(app)); } // Main Window handlers G_MODULE_EXPORT void MainWindow_ManageWallpapersBtnClick() { - gtk_widget_set_visible(wallpaperMgrWnd, 1); + gtk_widget_set_visible(wallpaperMgrWnd, 1); } -G_MODULE_EXPORT void MainWindow_ExitBtnClick() -{ - gtk_widget_set_visible(exitDialog, 1); -} +G_MODULE_EXPORT void MainWindow_ExitBtnClick() { gtk_widget_set_visible(exitDialog, 1); } -G_MODULE_EXPORT void MainWindowClose() -{ - gtk_widget_set_visible(mainWnd,0); -} +G_MODULE_EXPORT void MainWindowClose() { gtk_widget_set_visible(mainWnd, 0); } // Wallpapaer Manager Window handlers -G_MODULE_EXPORT void WallpaperManagerWindowClose() -{ - gtk_widget_set_visible(wallpaperMgrWnd,0); -} +G_MODULE_EXPORT void WallpaperManagerWindowClose() { gtk_widget_set_visible(wallpaperMgrWnd, 0); } -G_MODULE_EXPORT void ScreenWindowClose() -{ - gtk_widget_set_visible(screenWnd,0); -} \ No newline at end of file +G_MODULE_EXPORT void ScreenWindowClose() { gtk_widget_set_visible(screenWnd, 0); } diff --git a/src/window_templates/main.glade b/src/window_templates/main.glade index ad77c14..831f258 100644 --- a/src/window_templates/main.glade +++ b/src/window_templates/main.glade @@ -152,38 +152,6 @@ False 40 40 - - - True - True - - - True - False - start - 10 - 10 - Monitor 2 - - - - - - - True - True - - - True - False - start - 10 - 10 - Monitor 1 - - - - False From e3475a52ae6ba192c144837441e4b3a4b6a961af Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Tue, 2 Jan 2024 12:00:28 +0000 Subject: [PATCH 06/33] Changed screenWindow to monitorWindow --- src/core/main.c | 20 ++++++++++---------- src/core/main.h | 3 +-- src/core/windowHandlers.c | 4 +++- src/window_templates/main.glade | 22 ++++++++++++++++++---- 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/core/main.c b/src/core/main.c index d951a83..6f0ce76 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -14,8 +14,8 @@ GtkBuilder *builder = NULL; GtkWidget *mainWnd = NULL; GtkWidget *exitDialog = NULL; GtkWidget *wallpaperMgrWnd = NULL; -GtkWidget *screenWnd = NULL; -GtkWidget *screenListBox = NULL; +GtkWidget *monitorWnd = NULL; +GtkWidget *monitorListBox = NULL; static FILE *wlpProcess = NULL; static int wlpPid = 0; @@ -37,14 +37,14 @@ static void runWlp() wlpPid = atoi(buff); } -static void reloadScreenListBox() +static void reloadMonitorListBox() { - GList *rows = gtk_container_get_children(GTK_CONTAINER(screenListBox)); + GList *rows = gtk_container_get_children(GTK_CONTAINER(monitorListBox)); GList *ptr = rows; while (ptr) { - gtk_container_remove(GTK_CONTAINER(screenListBox), ptr->data); + gtk_container_remove(GTK_CONTAINER(monitorListBox), ptr->data); ptr = ptr->next; } @@ -61,7 +61,7 @@ static void reloadScreenListBox() GtkWidget *row = gtk_list_box_row_new(); gtk_container_add(GTK_CONTAINER(row), label); - gtk_list_box_insert(GTK_LIST_BOX(screenListBox), row, 0); + gtk_list_box_insert(GTK_LIST_BOX(monitorListBox), row, 0); gtk_widget_show_all(row); } } @@ -80,18 +80,18 @@ static void activate(GtkApplication *app, gpointer userdata) mainWnd = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow"); exitDialog = (GtkWidget *)gtk_builder_get_object(builder, "ExitDialog"); wallpaperMgrWnd = (GtkWidget *)gtk_builder_get_object(builder, "WallpaperManagerWindow"); - screenWnd = (GtkWidget *)gtk_builder_get_object(builder, "ScreenWindow"); - screenListBox = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow_ScreenListBox"); + monitorWnd = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow"); + monitorListBox = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow_MonitorListBox"); gtk_window_set_application(GTK_WINDOW(mainWnd), GTK_APPLICATION(app)); gtk_window_set_application(GTK_WINDOW(exitDialog), GTK_APPLICATION(app)); gtk_window_set_application(GTK_WINDOW(wallpaperMgrWnd), GTK_APPLICATION(app)); - gtk_window_set_application(GTK_WINDOW(screenWnd), GTK_APPLICATION(app)); + gtk_window_set_application(GTK_WINDOW(monitorWnd), GTK_APPLICATION(app)); runWlp(); } - reloadScreenListBox(); + reloadMonitorListBox(); gtk_widget_set_visible(mainWnd, 1); } diff --git a/src/core/main.h b/src/core/main.h index 2c2e46a..fa2a9de 100644 --- a/src/core/main.h +++ b/src/core/main.h @@ -32,8 +32,7 @@ extern GtkApplication *app; extern GtkWidget *mainWnd; extern GtkWidget *exitDialog; extern GtkWidget *wallpaperMgrWnd; -extern GtkWidget *screenWnd; -extern GtkWidget *screenListBox; +extern GtkWidget *monitorWnd; void killWlp(); diff --git a/src/core/windowHandlers.c b/src/core/windowHandlers.c index 462fef9..9a3233f 100644 --- a/src/core/windowHandlers.c +++ b/src/core/windowHandlers.c @@ -19,6 +19,8 @@ G_MODULE_EXPORT void MainWindow_ManageWallpapersBtnClick() gtk_widget_set_visible(wallpaperMgrWnd, 1); } +G_MODULE_EXPORT void MainWindow_MonitorEditBtnClick() { gtk_widget_set_visible(monitorWnd, 1); } + G_MODULE_EXPORT void MainWindow_ExitBtnClick() { gtk_widget_set_visible(exitDialog, 1); } G_MODULE_EXPORT void MainWindowClose() { gtk_widget_set_visible(mainWnd, 0); } @@ -27,4 +29,4 @@ G_MODULE_EXPORT void MainWindowClose() { gtk_widget_set_visible(mainWnd, 0); } G_MODULE_EXPORT void WallpaperManagerWindowClose() { gtk_widget_set_visible(wallpaperMgrWnd, 0); } -G_MODULE_EXPORT void ScreenWindowClose() { gtk_widget_set_visible(screenWnd, 0); } +G_MODULE_EXPORT void MonitorWindowClose() { gtk_widget_set_visible(monitorWnd, 0); } diff --git a/src/window_templates/main.glade b/src/window_templates/main.glade index 831f258..5c7c2f3 100644 --- a/src/window_templates/main.glade +++ b/src/window_templates/main.glade @@ -135,7 +135,7 @@ False 5 5 - Setup your screens + Setup your monitors @@ -147,7 +147,7 @@ - + True False 40 @@ -159,6 +159,20 @@ 1 + + + Edit settings + True + True + True + + + + False + True + 2 + + True @@ -251,9 +265,9 @@ - + False - + True From 9f8d8c86f999f391a922cf8a111f27e364149d6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Szczerbi=C5=84ski?= Date: Tue, 2 Jan 2024 18:29:34 +0100 Subject: [PATCH 07/33] Building on Windows --- CMakeLists.txt | 26 +++++++++++++++----------- distributeDLLs.sh | 18 ++++++++++++++++++ src/core/CMakeLists.txt | 1 + src/core/main.c | 4 +++- src/core/monitorScanner.c | 14 +++++++++++++- src/{wlp => core}/resource.template.rc | 0 src/wlp/CMakeLists.txt | 14 -------------- src/wlp/main.c | 4 ++++ src/wlp/window.c | 2 +- 9 files changed, 55 insertions(+), 28 deletions(-) create mode 100644 distributeDLLs.sh rename src/{wlp => core}/resource.template.rc (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d73e6f..9069ed9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,10 +8,12 @@ project(lwp # Detect the platform if (APPLE) set(_UNAME "DARWIN") -elseif (WIN32) +elseif (MSYS OR MINGW) set(_UNAME "WIN32") -else() +elseif(UNIX) set(_UNAME "LINUX") +else() + message(FATAL_ERROR "Unsupported platform") endif() # Detect version @@ -46,12 +48,14 @@ add_subdirectory(src/wlp) link_directories(src/wlp) add_subdirectory(src/core) -link_directories(src/core) +link_directories(src/core) # Installation rules if(_UNAME STREQUAL "WIN32") install(DIRECTORY wallpapers DESTINATION .) + install(DIRECTORY build/dlls/ + DESTINATION .) install(DIRECTORY src/window_templates DESTINATION .) install(FILES LICENSE.txt @@ -73,15 +77,15 @@ elseif(_UNAME STREQUAL "LINUX") TYPE SYSCONF RENAME lwp.cfg) else() - install(DIRECTORY wallpapers - DESTINATION Layered_WallPaper) - install(DIRECTORY src/window_templates - DESTINATION Layered_WallPaper) - install(FILES LICENSE.txt - DESTINATION Layered_WallPaper) - install(FILES ${_DEFAULT_CONFIG_FILE} + install(DIRECTORY wallpapers + DESTINATION Layered_WallPaper) + install(DIRECTORY src/window_templates + DESTINATION Layered_WallPaper) + install(FILES LICENSE.txt + DESTINATION Layered_WallPaper) + install(FILES ${_DEFAULT_CONFIG_FILE} DESTINATION Layered_WallPaper - RENAME lwp.cfg) + RENAME lwp.cfg) install(FILES lwp.template.plist DESTINATION Layered_WallPaper) install (FILES setupPlist.command diff --git a/distributeDLLs.sh b/distributeDLLs.sh new file mode 100644 index 0000000..e18b501 --- /dev/null +++ b/distributeDLLs.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +echo +PS3='Choose the correct version of gdbus: ' +options=("/mingw64/bin/gdbus.exe" "/mingw32/bin/gdbus.exe") +select opt in "${options[@]}" +do + echo "Preparing gdbus.exe..." + cp $opt ./dlls/ + break; +done + +echo "Preparing DLLs..." + +mkdir -p dlls +ldd ./src/core/lwp.exe | grep '\/mingw.*\.dll' -o | xargs -I{} cp "{}" ./dlls/ + +echo "Done" \ No newline at end of file diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3921755..542ddb2 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -35,6 +35,7 @@ pkg_check_modules (GTK3 REQUIRED gtk+-3.0) list(APPEND _INCLUDE_DIRS ${GTK3_INCLUDE_DIRS}) list(APPEND _LIBS ${GTK3_LINK_LIBRARIES}) + # Main executable if (_UNAME STREQUAL "DARWIN") add_executable(lwp MACOSX_BUNDLE ${_SOURCE_FILES}) diff --git a/src/core/main.c b/src/core/main.c index 6f0ce76..8267ca1 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1,7 +1,7 @@ #include "main.h" #ifdef __WIN32 -#define MAIN_WINDOW_TEMPLATE_PATH "" +#define MAIN_WINDOW_TEMPLATE_PATH "./window_templates/main.glade" #elif __DARWIN #define MAIN_WINDOW_TEMPLATE_PATH "" #else @@ -22,11 +22,13 @@ static int wlpPid = 0; void killWlp() { + #ifndef __WIN32 if (wlpPid != 0) kill(wlpPid, SIGINT); if (wlpProcess != NULL) pclose(wlpProcess); wlpPid = 0; wlpProcess = NULL; + #endif } static void runWlp() diff --git a/src/core/monitorScanner.c b/src/core/monitorScanner.c index 28f5ebc..ba5c0dd 100644 --- a/src/core/monitorScanner.c +++ b/src/core/monitorScanner.c @@ -2,9 +2,20 @@ #include #include + +#ifndef __WIN32 #include #include +#endif + +#ifdef __WIN32 +Monitor* scanMonitors(int *count) +{ + return NULL; +} +#endif +#ifdef LINUX Monitor* scanMonitors(int *count) { int monitorCount; @@ -31,4 +42,5 @@ Monitor* scanMonitors(int *count) *count = monitorCount; return m; -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/wlp/resource.template.rc b/src/core/resource.template.rc similarity index 100% rename from src/wlp/resource.template.rc rename to src/core/resource.template.rc diff --git a/src/wlp/CMakeLists.txt b/src/wlp/CMakeLists.txt index 64e71d8..7ba99e0 100644 --- a/src/wlp/CMakeLists.txt +++ b/src/wlp/CMakeLists.txt @@ -9,20 +9,6 @@ set(_SOURCE_FILES wallpaper.c window.c ) - -# Windows resource file -if(_UNAME STREQUAL "WIN32") - if(MINGW) - set(CMAKE_RC_COMPILER_INIT windres) - ENABLE_LANGUAGE(RC) - SET(CMAKE_RC_COMPILE_OBJECT - " -O coff -i -o ") - endif(MINGW) - - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/resource.template.rc ${CMAKE_CURRENT_SOURCE_DIR}/resource.rc - @ONLY) - list(APPEND _SOURCE_FILES "resource.rc") -endif() # SDL2 dependency find_package(SDL2 REQUIRED CONFIG) diff --git a/src/wlp/main.c b/src/wlp/main.c index 0f68eb6..3fd68e5 100644 --- a/src/wlp/main.c +++ b/src/wlp/main.c @@ -65,10 +65,12 @@ static void atExit() SDL_Quit(); } +#ifndef __WIN32 void exitSignalHandler(int s) { exit(0); } +#endif int main(int argc, char *argv[]) { @@ -76,7 +78,9 @@ int main(int argc, char *argv[]) sprintf(pidStr, "%d\0", getpid()); fputs(pidStr, stdout); +#ifndef __WIN32 signal(SIGINT, exitSignalHandler); +#endif lwpLog(LOG_INFO, "Starting Layered WallPaper"); diff --git a/src/wlp/window.c b/src/wlp/window.c index 68b6228..2a4e61f 100644 --- a/src/wlp/window.c +++ b/src/wlp/window.c @@ -104,7 +104,7 @@ void initTrayIcon() wc.hInstance = hInstance; wc.lpszClassName = CLASS_NAME; RegisterClass(&wc); - HWND hWnd = CreateWindowEx(0, CLASS_NAME, L"", WS_OVERLcfgEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, + HWND hWnd = CreateWindowEx(0, CLASS_NAME, L"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); // Create tray icon From 27f41e81ae25bb5a0abb26b0b837518275b5947e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Szczerbi=C5=84ski?= Date: Thu, 4 Jan 2024 11:15:51 +0100 Subject: [PATCH 08/33] Windows wlp subprocess and monitor scanner --- distributeDLLs.sh | 3 +- src/common.h | 4 +++ src/core/CMakeLists.txt | 1 + src/core/main.c | 22 ------------- src/core/main.h | 1 + src/core/monitorScanner.c | 42 ++++++++++++++++++++---- src/core/wlp.c | 67 +++++++++++++++++++++++++++++++++++++++ src/wlp/main.c | 6 ++-- src/wlp/main.h | 4 +-- 9 files changed, 115 insertions(+), 35 deletions(-) create mode 100644 src/common.h create mode 100644 src/core/wlp.c diff --git a/distributeDLLs.sh b/distributeDLLs.sh index e18b501..ccfcab3 100644 --- a/distributeDLLs.sh +++ b/distributeDLLs.sh @@ -1,5 +1,7 @@ #!/bin/bash +mkdir -p dlls + echo PS3='Choose the correct version of gdbus: ' options=("/mingw64/bin/gdbus.exe" "/mingw32/bin/gdbus.exe") @@ -12,7 +14,6 @@ done echo "Preparing DLLs..." -mkdir -p dlls ldd ./src/core/lwp.exe | grep '\/mingw.*\.dll' -o | xargs -I{} cp "{}" ./dlls/ echo "Done" \ No newline at end of file diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..05ce8e1 --- /dev/null +++ b/src/common.h @@ -0,0 +1,4 @@ +#ifndef COMMON_H +#define COMMON_H + +#endif \ No newline at end of file diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 542ddb2..8722b68 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -2,6 +2,7 @@ set(_SOURCE_FILES main.c windowHandlers.c monitorScanner.c + wlp.c ) # Windows resource file diff --git a/src/core/main.c b/src/core/main.c index 8267ca1..39e3ef8 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -17,28 +17,6 @@ GtkWidget *wallpaperMgrWnd = NULL; GtkWidget *monitorWnd = NULL; GtkWidget *monitorListBox = NULL; -static FILE *wlpProcess = NULL; -static int wlpPid = 0; - -void killWlp() -{ - #ifndef __WIN32 - if (wlpPid != 0) kill(wlpPid, SIGINT); - if (wlpProcess != NULL) pclose(wlpProcess); - - wlpPid = 0; - wlpProcess = NULL; - #endif -} - -static void runWlp() -{ - wlpProcess = popen("/home/cziken/Projects/lwp/build/src/wlp/lwpwlp", "r"); - char buff[10]; - fgets(buff, sizeof(buff) - 1, wlpProcess); - wlpPid = atoi(buff); -} - static void reloadMonitorListBox() { GList *rows = gtk_container_get_children(GTK_CONTAINER(monitorListBox)); diff --git a/src/core/main.h b/src/core/main.h index fa2a9de..d4b6317 100644 --- a/src/core/main.h +++ b/src/core/main.h @@ -34,6 +34,7 @@ extern GtkWidget *exitDialog; extern GtkWidget *wallpaperMgrWnd; extern GtkWidget *monitorWnd; +void runWlp(); void killWlp(); Monitor *scanMonitors(int *count); diff --git a/src/core/monitorScanner.c b/src/core/monitorScanner.c index ba5c0dd..0745bf7 100644 --- a/src/core/monitorScanner.c +++ b/src/core/monitorScanner.c @@ -3,27 +3,55 @@ #include #include -#ifndef __WIN32 +#ifdef __WIN32 +#include +#else #include #include #endif #ifdef __WIN32 -Monitor* scanMonitors(int *count) +int monitorEnumIndex = 0; + +BOOL monitorenumproc(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM param) { - return NULL; + MONITORINFO info; + info.cbSize = sizeof(MONITORINFO); + + GetMonitorInfo(monitor, &info); + + Monitor *m = (Monitor*)param; + + snprintf(m[monitorEnumIndex].name, MONITOR_NAME_MAX, "Monitor %d%s", monitorEnumIndex + 1, info.dwFlags == MONITORINFOF_PRIMARY ? " (main)" : ""); + m[monitorEnumIndex].bounds.x = info.rcWork.left; + m[monitorEnumIndex].bounds.y = info.rcWork.top; + m[monitorEnumIndex].bounds.w = info.rcWork.right - info.rcWork.left; + m[monitorEnumIndex].bounds.h = info.rcWork.bottom - info.rcWork.top; + m[monitorEnumIndex].config.set = 0; + + monitorEnumIndex++; + return TRUE; } #endif -#ifdef LINUX Monitor* scanMonitors(int *count) { int monitorCount; + Monitor *m = NULL; + +#ifdef __WIN32 + monitorCount = GetSystemMetrics(SM_CMONITORS); + m = malloc(sizeof(Monitor) * monitorCount); + + monitorEnumIndex = 0; + EnumDisplayMonitors(NULL, NULL, &monitorenumproc, (LPARAM)m); + +#else Display *display = XOpenDisplay((getenv("DISPLAY"))); Window wnd = DefaultRootWindow(display); XRRMonitorInfo *info = XRRGetMonitors(display, wnd, 0, &monitorCount); - Monitor *m = malloc(sizeof(Monitor) * monitorCount); + m = malloc(sizeof(Monitor) * monitorCount); int i = 0; while (i < monitorCount) @@ -41,6 +69,6 @@ Monitor* scanMonitors(int *count) *count = monitorCount; +#endif return m; -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/src/core/wlp.c b/src/core/wlp.c new file mode 100644 index 0000000..0e6b032 --- /dev/null +++ b/src/core/wlp.c @@ -0,0 +1,67 @@ +#include "main.h" + +#ifdef __WIN32 +#include +#include +#endif + +#ifdef __WIN32 +static HANDLE hWlpProcess = NULL; +#else +static FILE *wlpProcess = NULL; +static int wlpPid = 0; +#endif + +void killWlp() +{ +#ifdef __WIN32 + TerminateProcess(hWlpProcess, 0); +#else + if (wlpPid != 0) kill(wlpPid, SIGINT); + if (wlpProcess != NULL) pclose(wlpProcess); + + wlpPid = 0; + wlpProcess = NULL; +#endif +} + +void runWlp() +{ +#ifdef __WIN32 + STARTUPINFO si; + PROCESS_INFORMATION pi; + + ZeroMemory(&si, sizeof(si)); + ZeroMemory(&pi, sizeof(pi)); + si.cb = sizeof(si); + + TCHAR path[MAX_PATH]; + GetModuleFileName(NULL, path, MAX_PATH); + path[_tcslen(path)-4] = '\0'; + _tcscat(path, TEXT("wlp.exe")); + printf("%s\n", path); + + if(CreateProcess( + NULL, + path, + NULL, + NULL, + FALSE, + 0, + NULL, + NULL, + &si, + &pi + )) + hWlpProcess = pi.hProcess; + else + printf("Failed to start a wallpaper subprocess"); + + +#else + wlpProcess = popen("/home/cziken/Projects/lwp/build/src/wlp/lwpwlp", "r"); + char buff[10]; + fgets(buff, sizeof(buff) - 1, wlpProcess); + wlpPid = atoi(buff); +#endif +} \ No newline at end of file diff --git a/src/wlp/main.c b/src/wlp/main.c index 3fd68e5..d7b5842 100644 --- a/src/wlp/main.c +++ b/src/wlp/main.c @@ -65,12 +65,10 @@ static void atExit() SDL_Quit(); } -#ifndef __WIN32 void exitSignalHandler(int s) { exit(0); } -#endif int main(int argc, char *argv[]) { @@ -78,7 +76,9 @@ int main(int argc, char *argv[]) sprintf(pidStr, "%d\0", getpid()); fputs(pidStr, stdout); -#ifndef __WIN32 +#ifdef __WIN32 + signal(SIGTERM, exitSignalHandler); +#else signal(SIGINT, exitSignalHandler); #endif diff --git a/src/wlp/main.h b/src/wlp/main.h index 09de760..71cb43b 100644 --- a/src/wlp/main.h +++ b/src/wlp/main.h @@ -8,6 +8,8 @@ #include #endif +#include + #ifdef __WIN32 #include #include @@ -18,14 +20,12 @@ #include #include #include -#include #elif __LINUX #include #include #include #include #include -#include #endif typedef struct From 079f0d530774dcdeb8192c5ae8eff40ad30c8f8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Szczerbi=C5=84ski?= Date: Thu, 4 Jan 2024 11:51:26 +0100 Subject: [PATCH 09/33] Update compilation steps for Windows in README --- README.md | 70 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 364df25..0869246 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +
# Layered WallPaper @@ -106,33 +107,50 @@ https://user-images.githubusercontent.com/38699473/220888934-09788a6b-873c-469b- - Layered WallPaper should run immediately after the installation #### Build from source instead - - Layered WallPaper is built using [cmake](https://cmake.org/), so You must install it. - - This project supports `MinGW` and `MSVC` compilers. Using different one could lead to unpredicted behavior. If You want to use `MSVC`, it should be installed with Visual Studio. - - Download `SDL2` and `SDL2-devel` package for Your compiler from [SDL2 releases](https://github.com/libsdl-org/SDL/releases/latest) and extract them somewhere. - - You also must install [NSIS](https://nsis.sourceforge.io/Download). It's required to build the installer, which is needed to correctly set the registry keys, that will make Layered WallPaper run on OS startup etc. - - Clone the repository and create `build` directory - ```shell - git clone https://github.com/jszczerbinsky/lwp - cd lwp - mkdir -p build - cd build - ``` - - Type the following commands, replace square brackets elements with paths to extracted `SDL2` packages, that You've downloaded: + To compile Layered WallPaper on Windows you need to install [MSYS2](https://www.msys2.org/). After the installation follow the guide for setting up [GTK development enviroment](https://www.gtk.org/docs/installations/windows#using-gtk-from-msys2-packages). From now on continue using MSYS2 MinGW terminal (make sure you're using `MSYS2 MINGW64`/`MSYS2 MINGW32` instead of `MSYS2`). + +##### Install the remaining dependencies +There is a problem with the newest version of SDL2 on MSYS2 repository (see [issue](https://github.com/haskell-game/sdl2/issues/277)), that leads to a compilation error, thus You should install an older version of SDL2 instead. +```shell +# For 64bit: +curl -O https://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-SDL2-2.0.14-2-any.pkg.tar.zst +pacman -U mingw-w64-x86_64-SDL2-2.0.14-2-any.pkg.tar.zst + +# For 32bit: +curl -O https://repo.msys2.org/mingw/i686/mingw-w64-i686-SDL2-2.0.14-2-any.pkg.tar.zst +pacman -U mingw-w64-i686-SDL2-2.0.14-2-any.pkg.tar.zst +``` +You also have to install cmake +```shell +# For 64bit: +pacman -S mingw-w64-x86_64-cmake - For `MSVC`: - ```shell - cmake -G "Visual Studio 17" -DSDL2_DIR=[PATH TO SDL2-MSVC-DEVEL DIRECTORY]\cmake -DSDL2_RUNTIME_DIR=[PATH TO SDL2 RUNTIME DIRECTORY] ../ - cmake --build . --config Release - cpack - ``` - For `MinGW`: - ```shell - cmake -G "MinGW Makefiles" -DSDL2_DIR=[PATH TO SDL2-MINGW-DEVEL DIRECTORY]\cmake -DSDL2_RUNTIME_DIR=[PATH TO SDL2 RUNTIME DIRECTORY] -DCMAKE_BUILD_TYPE=Release ../ - cmake --build . - cpack - ``` - - The installer should appear in `build` directory, that You've created earlier. After completing the installation Layered WallPaper should run immediately. +# For 32bit: +pacman -S mingw-w64-i686-cmake +``` + +##### Clone the repository +```shell +git clone https://github.com/jszczerbinsky/lwp +cd lwp +mkdir build +cd build +``` + +##### Compile and install +- Download [SDL2](https://github.com/libsdl-org/SDL/releases/latest) runtime package and unpack it. +- Compile the program +```shell +# Remember to use unix path format (instead of C:/path/to/dir use /c/path/to/dir) +cmake -DSDL2_RUNTIME_DIR=/path/to/dir ../ +cmake --build . +# Prepare the DLLs, that will be shipped with the program. +# The script is going to ask you which version of gdbus.exe should it use (32bit or 64bit) +../distributeDLLs.sh +cpack +``` +After this the installer should appear in the current directory. @@ -251,4 +269,4 @@ 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. +SOFTWARE. \ No newline at end of file From a8f65193348551d6b0b4129956eb4f7ecd3fe8d8 Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Mon, 15 Jan 2024 19:34:17 +0000 Subject: [PATCH 10/33] Wallpaper scanner --- src/common.h | 46 ++++++++++++++++++- src/common/monitorScanner.c | 80 +++++++++++++++++++++++++++++++++ src/common/wallpaperScanner.c | 47 +++++++++++++++++++ src/core/CMakeLists.txt | 9 ++-- src/core/main.c | 15 ++++--- src/core/main.h | 27 +---------- src/core/monitorScanner.c | 74 ------------------------------ src/core/windowHandlers.c | 20 +++++++++ src/window_templates/main.glade | 3 +- 9 files changed, 210 insertions(+), 111 deletions(-) create mode 100644 src/common/monitorScanner.c create mode 100644 src/common/wallpaperScanner.c delete mode 100644 src/core/monitorScanner.c diff --git a/src/common.h b/src/common.h index 05ce8e1..dbce32b 100644 --- a/src/common.h +++ b/src/common.h @@ -1,4 +1,48 @@ #ifndef COMMON_H #define COMMON_H -#endif \ No newline at end of file +#ifdef __LINUX +#include +#elif __DARWIN +#include +#else +#include +#endif + +#define MONITOR_NAME_MAX 100 +#define WALLPAPER_NAME_MAX 100 + +typedef struct +{ + int x; + int y; + int w; + int h; +} Bounds; + +typedef struct +{ + int set; + char wlpPath[PATH_MAX]; + Bounds wlpBounds; +} MonitorConfig; + +typedef struct +{ + char name[MONITOR_NAME_MAX]; + Bounds bounds; + MonitorConfig config; +} Monitor; + +Monitor *scanMonitors(int *count); + +typedef struct +{ + char name[WALLPAPER_NAME_MAX]; + char dirPath[PATH_MAX]; + int isDefault; +} WallpaperInfo; + +WallpaperInfo *scanWallpapers(int *count); + +#endif diff --git a/src/common/monitorScanner.c b/src/common/monitorScanner.c new file mode 100644 index 0000000..dd39d73 --- /dev/null +++ b/src/common/monitorScanner.c @@ -0,0 +1,80 @@ +#include +#include + +#include "../common.h" + +#ifdef __WIN32 +#include +#else +#include +#include +#endif + +#ifdef __WIN32 +int monitorEnumIndex = 0; + +BOOL monitorenumproc(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM param) +{ + MONITORINFO info; + info.cbSize = sizeof(MONITORINFO); + + GetMonitorInfo(monitor, &info); + + Monitor *m = (Monitor *)param; + + snprintf( + m[monitorEnumIndex].name, + MONITOR_NAME_MAX, + "Monitor %d%s", + monitorEnumIndex + 1, + info.dwFlags == MONITORINFOF_PRIMARY ? " (main)" : "" + ); + m[monitorEnumIndex].bounds.x = info.rcWork.left; + m[monitorEnumIndex].bounds.y = info.rcWork.top; + m[monitorEnumIndex].bounds.w = info.rcWork.right - info.rcWork.left; + m[monitorEnumIndex].bounds.h = info.rcWork.bottom - info.rcWork.top; + m[monitorEnumIndex].config.set = 0; + + monitorEnumIndex++; + return TRUE; +} +#endif + +Monitor *scanMonitors(int *count) +{ + int monitorCount; + Monitor *m = NULL; + +#ifdef __WIN32 + monitorCount = GetSystemMetrics(SM_CMONITORS); + m = malloc(sizeof(Monitor) * monitorCount); + + monitorEnumIndex = 0; + EnumDisplayMonitors(NULL, NULL, &monitorenumproc, (LPARAM)m); + +#else + Display *display = XOpenDisplay((getenv("DISPLAY"))); + Window wnd = DefaultRootWindow(display); + XRRMonitorInfo *info = XRRGetMonitors(display, wnd, 0, &monitorCount); + + m = malloc(sizeof(Monitor) * monitorCount); + + int i = 0; + while (i < monitorCount) + { + snprintf(m[i].name, MONITOR_NAME_MAX, "%s", XGetAtomName(display, info->name)); + m[i].bounds.x = info->x; + m[i].bounds.y = info->y; + m[i].bounds.w = info->width; + m[i].bounds.h = info->height; + m[i].config.set = 0; + + info++; + i++; + } + + *count = monitorCount; + +#endif + return m; +} diff --git a/src/common/wallpaperScanner.c b/src/common/wallpaperScanner.c new file mode 100644 index 0000000..2b835a2 --- /dev/null +++ b/src/common/wallpaperScanner.c @@ -0,0 +1,47 @@ +#include +#include + +#include "../common.h" + +#define SYSTEM_WALLPAPERS_PATH "/usr/local/share/lwp/wallpapers" + +WallpaperInfo *scanWallpapers(int *count) +{ + *count = 0; + + char userWlpPath[PATH_MAX]; + + sprintf(userWlpPath, "%s/.config/lwp/wallpapers", g_get_home_dir()); + + GDir *dir; + GError *error; + const gchar *filename; + + dir = g_dir_open(SYSTEM_WALLPAPERS_PATH, 0, &error); + while ((g_dir_read_name(dir))) (*count)++; + dir = g_dir_open(userWlpPath, 0, &error); + while ((g_dir_read_name(dir))) (*count)++; + + WallpaperInfo *list = malloc(sizeof(WallpaperInfo) * (*count)); + WallpaperInfo *ptr = list; + + dir = g_dir_open(SYSTEM_WALLPAPERS_PATH, 0, &error); + while ((filename = g_dir_read_name(dir))) + { + sprintf(ptr->name, "%s", filename); + sprintf(ptr->dirPath, "%s/%s", SYSTEM_WALLPAPERS_PATH, filename); + ptr->isDefault = 1; + ptr++; + } + + dir = g_dir_open(userWlpPath, 0, &error); + while ((filename = g_dir_read_name(dir))) + { + sprintf(ptr->name, "%s", filename); + sprintf(ptr->dirPath, "%s/%s", SYSTEM_WALLPAPERS_PATH, filename); + ptr->isDefault = 0; + ptr++; + } + + return list; +} diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8722b68..002f0c5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,9 +1,10 @@ set(_SOURCE_FILES main.c - windowHandlers.c - monitorScanner.c - wlp.c - ) + windowHandlers.c + wlp.c + ../common/wallpaperScanner.c + ../common/monitorScanner.c + ) # Windows resource file if(_UNAME STREQUAL "WIN32") diff --git a/src/core/main.c b/src/core/main.c index 39e3ef8..843af0a 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -11,11 +11,12 @@ GtkApplication *app = NULL; GtkBuilder *builder = NULL; -GtkWidget *mainWnd = NULL; -GtkWidget *exitDialog = NULL; -GtkWidget *wallpaperMgrWnd = NULL; -GtkWidget *monitorWnd = NULL; -GtkWidget *monitorListBox = NULL; +GtkWidget *mainWnd = NULL; +GtkWidget *exitDialog = NULL; +GtkWidget *wallpaperMgrWnd = NULL; +GtkWidget *monitorWnd = NULL; +GtkWidget *monitorListBox = NULL; +GtkWidget *wallpaperListBox = NULL; static void reloadMonitorListBox() { @@ -35,7 +36,7 @@ static void reloadMonitorListBox() monitors = scanMonitors(&monitorsCount); - for (int i = 0; i < 1; i++) + for (int i = 0; i < monitorsCount; i++) { GtkWidget *label = gtk_label_new(monitors[i].name); GtkWidget *row = gtk_list_box_row_new(); @@ -62,6 +63,8 @@ static void activate(GtkApplication *app, gpointer userdata) wallpaperMgrWnd = (GtkWidget *)gtk_builder_get_object(builder, "WallpaperManagerWindow"); monitorWnd = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow"); monitorListBox = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow_MonitorListBox"); + wallpaperListBox = + (GtkWidget *)gtk_builder_get_object(builder, "WallpaperManagerWindow_WallpaperListBox"); gtk_window_set_application(GTK_WINDOW(mainWnd), GTK_APPLICATION(app)); gtk_window_set_application(GTK_WINDOW(exitDialog), GTK_APPLICATION(app)); diff --git a/src/core/main.h b/src/core/main.h index d4b6317..1216a32 100644 --- a/src/core/main.h +++ b/src/core/main.h @@ -3,29 +3,7 @@ #include -#define MONITOR_NAME_MAX 100 - -typedef struct -{ - int x; - int y; - int w; - int h; -} Bounds; - -typedef struct -{ - int set; - char wlpPath[PATH_MAX]; - Bounds wlpBounds; -} MonitorConfig; - -typedef struct -{ - char name[MONITOR_NAME_MAX]; - Bounds bounds; - MonitorConfig config; -} Monitor; +#include "../common.h" extern GtkApplication *app; @@ -33,10 +11,9 @@ extern GtkWidget *mainWnd; extern GtkWidget *exitDialog; extern GtkWidget *wallpaperMgrWnd; extern GtkWidget *monitorWnd; +extern GtkWidget *wallpaperListBox; void runWlp(); void killWlp(); -Monitor *scanMonitors(int *count); - #endif diff --git a/src/core/monitorScanner.c b/src/core/monitorScanner.c deleted file mode 100644 index 0745bf7..0000000 --- a/src/core/monitorScanner.c +++ /dev/null @@ -1,74 +0,0 @@ -#include "main.h" - -#include -#include - -#ifdef __WIN32 -#include -#else -#include -#include -#endif - -#ifdef __WIN32 -int monitorEnumIndex = 0; - -BOOL monitorenumproc(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM param) -{ - MONITORINFO info; - info.cbSize = sizeof(MONITORINFO); - - GetMonitorInfo(monitor, &info); - - Monitor *m = (Monitor*)param; - - snprintf(m[monitorEnumIndex].name, MONITOR_NAME_MAX, "Monitor %d%s", monitorEnumIndex + 1, info.dwFlags == MONITORINFOF_PRIMARY ? " (main)" : ""); - m[monitorEnumIndex].bounds.x = info.rcWork.left; - m[monitorEnumIndex].bounds.y = info.rcWork.top; - m[monitorEnumIndex].bounds.w = info.rcWork.right - info.rcWork.left; - m[monitorEnumIndex].bounds.h = info.rcWork.bottom - info.rcWork.top; - m[monitorEnumIndex].config.set = 0; - - monitorEnumIndex++; - return TRUE; -} -#endif - -Monitor* scanMonitors(int *count) -{ - int monitorCount; - Monitor *m = NULL; - -#ifdef __WIN32 - monitorCount = GetSystemMetrics(SM_CMONITORS); - m = malloc(sizeof(Monitor) * monitorCount); - - monitorEnumIndex = 0; - EnumDisplayMonitors(NULL, NULL, &monitorenumproc, (LPARAM)m); - -#else - Display *display = XOpenDisplay((getenv("DISPLAY"))); - Window wnd = DefaultRootWindow(display); - XRRMonitorInfo *info = XRRGetMonitors(display, wnd, 0, &monitorCount); - - m = malloc(sizeof(Monitor) * monitorCount); - - int i = 0; - while (i < monitorCount) - { - snprintf(m[i].name, MONITOR_NAME_MAX, "%s", XGetAtomName(display, info->name)); - m[i].bounds.x = info->x; - m[i].bounds.y = info->y; - m[i].bounds.w = info->width; - m[i].bounds.h = info->height; - m[i].config.set = 0; - - info++; - i++; - } - - *count = monitorCount; - -#endif - return m; -} \ No newline at end of file diff --git a/src/core/windowHandlers.c b/src/core/windowHandlers.c index 9a3233f..5fba3d7 100644 --- a/src/core/windowHandlers.c +++ b/src/core/windowHandlers.c @@ -29,4 +29,24 @@ G_MODULE_EXPORT void MainWindowClose() { gtk_widget_set_visible(mainWnd, 0); } G_MODULE_EXPORT void WallpaperManagerWindowClose() { gtk_widget_set_visible(wallpaperMgrWnd, 0); } +G_MODULE_EXPORT void WallpaperManagerWindowShow() +{ + int wlpCount; + WallpaperInfo *wlpList = scanWallpapers(&wlpCount); + + for (int i = 0; i < wlpCount; i++) + { + GtkWidget *label = gtk_label_new(wlpList[i].name); + GtkWidget *row = gtk_list_box_row_new(); + gtk_container_add(GTK_CONTAINER(row), label); + + gtk_list_box_insert(GTK_LIST_BOX(wallpaperListBox), row, 0); + gtk_widget_show_all(row); + } + + free(wlpList); +} + +// Monitor Window handlers + G_MODULE_EXPORT void MonitorWindowClose() { gtk_widget_set_visible(monitorWnd, 0); } diff --git a/src/window_templates/main.glade b/src/window_templates/main.glade index 5c7c2f3..4bff1bc 100644 --- a/src/window_templates/main.glade +++ b/src/window_templates/main.glade @@ -285,6 +285,7 @@ False + True @@ -308,7 +309,7 @@ - + True False From 9a917a7d393f13ba745b7a8f2ae69d7c0875ace8 Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Mon, 15 Jan 2024 20:47:20 +0000 Subject: [PATCH 11/33] Monitor config window --- src/core/main.c | 23 ++- src/core/main.h | 5 + src/core/windowHandlers.c | 27 ++++ src/window_templates/main.glade | 238 ++++++++++++++++++++++++++++++-- 4 files changed, 275 insertions(+), 18 deletions(-) diff --git a/src/core/main.c b/src/core/main.c index 843af0a..233e179 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -11,12 +11,17 @@ GtkApplication *app = NULL; GtkBuilder *builder = NULL; -GtkWidget *mainWnd = NULL; -GtkWidget *exitDialog = NULL; -GtkWidget *wallpaperMgrWnd = NULL; -GtkWidget *monitorWnd = NULL; -GtkWidget *monitorListBox = NULL; -GtkWidget *wallpaperListBox = NULL; +GtkWidget *mainWnd = NULL; +GtkWidget *exitDialog = NULL; +GtkWidget *wallpaperMgrWnd = NULL; +GtkWidget *monitorWnd = NULL; +GtkWidget *monitorListBox = NULL; +GtkWidget *wallpaperListBox = NULL; +GtkWidget *wallpaperComboBox = NULL; +GtkWidget *xPosSpinBtn = NULL; +GtkWidget *yPosSpinBtn = NULL; +GtkWidget *widthSpinBtn = NULL; +GtkWidget *heightSpinBtn = NULL; static void reloadMonitorListBox() { @@ -65,6 +70,12 @@ static void activate(GtkApplication *app, gpointer userdata) monitorListBox = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow_MonitorListBox"); wallpaperListBox = (GtkWidget *)gtk_builder_get_object(builder, "WallpaperManagerWindow_WallpaperListBox"); + wallpaperComboBox = + (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow_WallpaperComboBox"); + xPosSpinBtn = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow_XPosSpinBtn"); + yPosSpinBtn = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow_YPosSpinBtn"); + widthSpinBtn = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow_WidthSpinBtn"); + widthSpinBtn = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow_HeightSpinBtn"); gtk_window_set_application(GTK_WINDOW(mainWnd), GTK_APPLICATION(app)); gtk_window_set_application(GTK_WINDOW(exitDialog), GTK_APPLICATION(app)); diff --git a/src/core/main.h b/src/core/main.h index 1216a32..ccf6181 100644 --- a/src/core/main.h +++ b/src/core/main.h @@ -12,6 +12,11 @@ extern GtkWidget *exitDialog; extern GtkWidget *wallpaperMgrWnd; extern GtkWidget *monitorWnd; extern GtkWidget *wallpaperListBox; +extern GtkWidget *wallpaperComboBox; +extern GtkWidget *xPosSpinBtn; +extern GtkWidget *yPosSpinBtn; +extern GtkWidget *widthSpinBtn; +extern GtkWidget *heightSpinBtn; void runWlp(); void killWlp(); diff --git a/src/core/windowHandlers.c b/src/core/windowHandlers.c index 5fba3d7..eeaf615 100644 --- a/src/core/windowHandlers.c +++ b/src/core/windowHandlers.c @@ -50,3 +50,30 @@ G_MODULE_EXPORT void WallpaperManagerWindowShow() // Monitor Window handlers G_MODULE_EXPORT void MonitorWindowClose() { gtk_widget_set_visible(monitorWnd, 0); } +G_MODULE_EXPORT void MonitorWindowShow() +{ + int wlpCount; + WallpaperInfo *wlpList = scanWallpapers(&wlpCount); + + for (int i = 0; i < wlpCount; i++) + { + gtk_combo_box_text_insert( + GTK_COMBO_BOX_TEXT(wallpaperComboBox), 0, wlpList[i].dirPath, wlpList[i].name + ); + } + + free(wlpList); +} + +G_MODULE_EXPORT void MonitorWindow_ApplyBtnClick() +{ + MonitorConfig mc; + strcpy(mc.wlpPath, gtk_combo_box_get_active_id(GTK_COMBO_BOX(wallpaperComboBox))); + mc.wlpBounds.x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(xPosSpinBtn)); + mc.wlpBounds.y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(yPosSpinBtn)); + mc.wlpBounds.w = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widthSpinBtn)); + mc.wlpBounds.h = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(heightSpinBtn)); + + killWlp(); + runWlp(); +} diff --git a/src/window_templates/main.glade b/src/window_templates/main.glade index 4bff1bc..af1d59e 100644 --- a/src/window_templates/main.glade +++ b/src/window_templates/main.glade @@ -67,6 +67,11 @@ + + 10000 + 1 + 10 + False False @@ -265,27 +270,106 @@ - + False - + + True False vertical - + + True + False + 10 + 10 + Manage wallpapers + + + + + + False + True + 0 + - + + True + False + + + False + True + 1 + + + + + True + False + center + + + Add + True + True + True + + + False + True + 0 + + + + + Remove + True + True + True + + + False + True + 1 + + + + + False + True + end + 2 + - + + 10000 + 1 + 10 + + + -10000 + 10000 + 1 + 10 + + + -10000 + 10000 + 1 + 10 + + False - - + + True @@ -297,7 +381,8 @@ False 10 10 - Manage wallpapers + Monitor name + center @@ -309,9 +394,136 @@ - + + True False + 20 + 20 + 15 + True + + + True + False + + + 1 + 0 + 3 + + + + + True + False + Wallpaper + + + 0 + 0 + + + + + True + False + X position + + + 0 + 1 + + + + + True + False + Y position + + + 0 + 2 + + + + + True + False + Height + + + 2 + 2 + + + + + True + False + Width + + + 2 + 1 + + + + + True + True + 6 + 1 + number + XPosAdjustment + + + 1 + 1 + + + + + True + True + 6 + 1 + number + WidthAdjustment + + + 3 + 1 + + + + + True + True + 6 + 1 + number + HeightAdjustment + + + 3 + 2 + + + + + True + True + 6 + 1 + number + YPosAdjustment + + + 1 + 2 + + False @@ -324,12 +536,14 @@ True False center + 20 - - Add + + Apply True True True + False @@ -339,7 +553,7 @@ - Remove + Exit True True True From d6a287311814b385a5b7514bc86144543954ba0f Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Mon, 15 Jan 2024 21:17:12 +0000 Subject: [PATCH 12/33] Saving basic monitor config --- src/common.h | 2 ++ src/common/config.c | 32 ++++++++++++++++++++++++++++++++ src/core/CMakeLists.txt | 8 +++++++- src/core/main.c | 8 ++++---- src/core/windowHandlers.c | 2 ++ 5 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 src/common/config.c diff --git a/src/common.h b/src/common.h index dbce32b..47c9550 100644 --- a/src/common.h +++ b/src/common.h @@ -45,4 +45,6 @@ typedef struct WallpaperInfo *scanWallpapers(int *count); +void saveMonitorConfig(MonitorConfig *mc); + #endif diff --git a/src/common/config.c b/src/common/config.c new file mode 100644 index 0000000..18c8764 --- /dev/null +++ b/src/common/config.c @@ -0,0 +1,32 @@ +#include + +#include "../common.h" + +void saveMonitorConfig(MonitorConfig *mc) +{ + config_t cfg; + config_setting_t *root, *setting; + + config_init(&cfg); + root = config_root_setting(&cfg); + + setting = config_setting_add(root, "wlpPath", CONFIG_TYPE_STRING); + config_setting_set_string(setting, mc->wlpPath); + + setting = config_setting_add(root, "x", CONFIG_TYPE_INT); + config_setting_set_int(setting, mc->wlpBounds.x); + setting = config_setting_add(root, "y", CONFIG_TYPE_INT); + config_setting_set_int(setting, mc->wlpBounds.y); + setting = config_setting_add(root, "w", CONFIG_TYPE_INT); + config_setting_set_int(setting, mc->wlpBounds.w); + setting = config_setting_add(root, "h", CONFIG_TYPE_INT); + config_setting_set_int(setting, mc->wlpBounds.h); + + if (!config_write_file(&cfg, "test.cfg")) + { + fprintf(stderr, "Error while writing file.\n"); + config_destroy(&cfg); + } + + config_destroy(&cfg); +} diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 002f0c5..dd4556e 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -4,6 +4,7 @@ set(_SOURCE_FILES wlp.c ../common/wallpaperScanner.c ../common/monitorScanner.c + ../common/config.c ) # Windows resource file @@ -20,6 +21,8 @@ if(_UNAME STREQUAL "WIN32") list(APPEND _SOURCE_FILES "resource.rc") endif() +find_package (PkgConfig REQUIRED) + if (_UNAME STREQUAL "LINUX") # X11 and Xrandr dependency include(FindX11) @@ -32,11 +35,14 @@ if (_UNAME STREQUAL "LINUX") endif() # GTK dependency -find_package (PkgConfig REQUIRED) pkg_check_modules (GTK3 REQUIRED gtk+-3.0) list(APPEND _INCLUDE_DIRS ${GTK3_INCLUDE_DIRS}) list(APPEND _LIBS ${GTK3_LINK_LIBRARIES}) +# libconfig dependency +pkg_check_modules (libconfig REQUIRED libconfig) +list(APPEND _INCLUDE_DIRS ${libconfig_INCLUDE_DIRS}) +list(APPEND _LIBS ${libconfig_LINK_LIBRARIES}) # Main executable if (_UNAME STREQUAL "DARWIN") diff --git a/src/core/main.c b/src/core/main.c index 233e179..b6bf496 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -72,10 +72,10 @@ static void activate(GtkApplication *app, gpointer userdata) (GtkWidget *)gtk_builder_get_object(builder, "WallpaperManagerWindow_WallpaperListBox"); wallpaperComboBox = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow_WallpaperComboBox"); - xPosSpinBtn = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow_XPosSpinBtn"); - yPosSpinBtn = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow_YPosSpinBtn"); - widthSpinBtn = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow_WidthSpinBtn"); - widthSpinBtn = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow_HeightSpinBtn"); + xPosSpinBtn = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow_XPosSpinBtn"); + yPosSpinBtn = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow_YPosSpinBtn"); + widthSpinBtn = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow_WidthSpinBtn"); + heightSpinBtn = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow_HeightSpinBtn"); gtk_window_set_application(GTK_WINDOW(mainWnd), GTK_APPLICATION(app)); gtk_window_set_application(GTK_WINDOW(exitDialog), GTK_APPLICATION(app)); diff --git a/src/core/windowHandlers.c b/src/core/windowHandlers.c index eeaf615..23be66d 100644 --- a/src/core/windowHandlers.c +++ b/src/core/windowHandlers.c @@ -74,6 +74,8 @@ G_MODULE_EXPORT void MonitorWindow_ApplyBtnClick() mc.wlpBounds.w = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widthSpinBtn)); mc.wlpBounds.h = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(heightSpinBtn)); + saveMonitorConfig(&mc); + killWlp(); runWlp(); } From 281805d6980a3d469786b71c0192337d037f4a4d Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Tue, 16 Jan 2024 00:21:55 +0000 Subject: [PATCH 13/33] Saving and loading monitor configuration --- src/common.h | 9 ++++--- src/common/config.c | 46 ++++++++++++++++++++++++++++++--- src/common/monitorScanner.c | 8 +++--- src/core/main.c | 7 +++-- src/core/main.h | 2 ++ src/core/windowHandlers.c | 27 ++++++++++++++++--- src/window_templates/main.glade | 3 ++- 7 files changed, 84 insertions(+), 18 deletions(-) diff --git a/src/common.h b/src/common.h index 47c9550..2d127fa 100644 --- a/src/common.h +++ b/src/common.h @@ -23,7 +23,7 @@ typedef struct typedef struct { int set; - char wlpPath[PATH_MAX]; + char wlpName[WALLPAPER_NAME_MAX]; Bounds wlpBounds; } MonitorConfig; @@ -32,9 +32,9 @@ typedef struct char name[MONITOR_NAME_MAX]; Bounds bounds; MonitorConfig config; -} Monitor; +} MonitorInfo; -Monitor *scanMonitors(int *count); +MonitorInfo *scanMonitors(int *count); typedef struct { @@ -45,6 +45,7 @@ typedef struct WallpaperInfo *scanWallpapers(int *count); -void saveMonitorConfig(MonitorConfig *mc); +void saveMonitorConfig(const char *name, MonitorConfig *mc); +int loadMonitorConfig(const char *name, MonitorConfig *mc); #endif diff --git a/src/common/config.c b/src/common/config.c index 18c8764..6cac664 100644 --- a/src/common/config.c +++ b/src/common/config.c @@ -1,8 +1,14 @@ +#include #include #include "../common.h" -void saveMonitorConfig(MonitorConfig *mc) +void getMonitorConfigPath(const char *name, char *path) +{ + sprintf(path, "%s/.config/lwp/monitors/%s.cfg", g_get_home_dir(), name); +} + +void saveMonitorConfig(const char *name, MonitorConfig *mc) { config_t cfg; config_setting_t *root, *setting; @@ -10,8 +16,8 @@ void saveMonitorConfig(MonitorConfig *mc) config_init(&cfg); root = config_root_setting(&cfg); - setting = config_setting_add(root, "wlpPath", CONFIG_TYPE_STRING); - config_setting_set_string(setting, mc->wlpPath); + setting = config_setting_add(root, "wlpName", CONFIG_TYPE_STRING); + config_setting_set_string(setting, mc->wlpName); setting = config_setting_add(root, "x", CONFIG_TYPE_INT); config_setting_set_int(setting, mc->wlpBounds.x); @@ -22,7 +28,10 @@ void saveMonitorConfig(MonitorConfig *mc) setting = config_setting_add(root, "h", CONFIG_TYPE_INT); config_setting_set_int(setting, mc->wlpBounds.h); - if (!config_write_file(&cfg, "test.cfg")) + char path[PATH_MAX]; + getMonitorConfigPath(name, path); + + if (!config_write_file(&cfg, path)) { fprintf(stderr, "Error while writing file.\n"); config_destroy(&cfg); @@ -30,3 +39,32 @@ void saveMonitorConfig(MonitorConfig *mc) config_destroy(&cfg); } + +int loadMonitorConfig(const char *name, MonitorConfig *mc) +{ + config_t cfg; + config_setting_t *root, *setting; + + char path[PATH_MAX]; + getMonitorConfigPath(name, path); + + config_init(&cfg); + if (config_read_file(&cfg, path) == CONFIG_FALSE) return 0; + root = config_root_setting(&cfg); + + setting = config_setting_get_member(root, "wlpName"); + strcpy(mc->wlpName, config_setting_get_string(setting)); + + setting = config_setting_get_member(root, "x"); + mc->wlpBounds.x = config_setting_get_int(setting); + setting = config_setting_get_member(root, "y"); + mc->wlpBounds.y = config_setting_get_int(setting); + setting = config_setting_get_member(root, "w"); + mc->wlpBounds.w = config_setting_get_int(setting); + setting = config_setting_get_member(root, "h"); + mc->wlpBounds.h = config_setting_get_int(setting); + + config_destroy(&cfg); + + return 1; +} diff --git a/src/common/monitorScanner.c b/src/common/monitorScanner.c index dd39d73..0e562f3 100644 --- a/src/common/monitorScanner.c +++ b/src/common/monitorScanner.c @@ -40,10 +40,10 @@ BOOL monitorenumproc(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM param) } #endif -Monitor *scanMonitors(int *count) +MonitorInfo *scanMonitors(int *count) { - int monitorCount; - Monitor *m = NULL; + int monitorCount; + MonitorInfo *m = NULL; #ifdef __WIN32 monitorCount = GetSystemMetrics(SM_CMONITORS); @@ -57,7 +57,7 @@ Monitor *scanMonitors(int *count) Window wnd = DefaultRootWindow(display); XRRMonitorInfo *info = XRRGetMonitors(display, wnd, 0, &monitorCount); - m = malloc(sizeof(Monitor) * monitorCount); + m = malloc(sizeof(MonitorInfo) * monitorCount); int i = 0; while (i < monitorCount) diff --git a/src/core/main.c b/src/core/main.c index b6bf496..079cfa4 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -22,6 +22,7 @@ GtkWidget *xPosSpinBtn = NULL; GtkWidget *yPosSpinBtn = NULL; GtkWidget *widthSpinBtn = NULL; GtkWidget *heightSpinBtn = NULL; +GtkWidget *monitorNameLabel = NULL; static void reloadMonitorListBox() { @@ -36,8 +37,8 @@ static void reloadMonitorListBox() g_list_free(rows); - int monitorsCount; - Monitor *monitors; + int monitorsCount; + MonitorInfo *monitors; monitors = scanMonitors(&monitorsCount); @@ -76,6 +77,8 @@ static void activate(GtkApplication *app, gpointer userdata) yPosSpinBtn = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow_YPosSpinBtn"); widthSpinBtn = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow_WidthSpinBtn"); heightSpinBtn = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow_HeightSpinBtn"); + monitorNameLabel = + (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow_MonitorNameLabel"); gtk_window_set_application(GTK_WINDOW(mainWnd), GTK_APPLICATION(app)); gtk_window_set_application(GTK_WINDOW(exitDialog), GTK_APPLICATION(app)); diff --git a/src/core/main.h b/src/core/main.h index ccf6181..add9ac0 100644 --- a/src/core/main.h +++ b/src/core/main.h @@ -11,12 +11,14 @@ extern GtkWidget *mainWnd; extern GtkWidget *exitDialog; extern GtkWidget *wallpaperMgrWnd; extern GtkWidget *monitorWnd; +extern GtkWidget *monitorListBox; extern GtkWidget *wallpaperListBox; extern GtkWidget *wallpaperComboBox; extern GtkWidget *xPosSpinBtn; extern GtkWidget *yPosSpinBtn; extern GtkWidget *widthSpinBtn; extern GtkWidget *heightSpinBtn; +extern GtkWidget *monitorNameLabel; void runWlp(); void killWlp(); diff --git a/src/core/windowHandlers.c b/src/core/windowHandlers.c index 23be66d..82aa588 100644 --- a/src/core/windowHandlers.c +++ b/src/core/windowHandlers.c @@ -52,29 +52,50 @@ G_MODULE_EXPORT void WallpaperManagerWindowShow() G_MODULE_EXPORT void MonitorWindowClose() { gtk_widget_set_visible(monitorWnd, 0); } G_MODULE_EXPORT void MonitorWindowShow() { + // Load wallpaper list int wlpCount; WallpaperInfo *wlpList = scanWallpapers(&wlpCount); for (int i = 0; i < wlpCount; i++) { gtk_combo_box_text_insert( - GTK_COMBO_BOX_TEXT(wallpaperComboBox), 0, wlpList[i].dirPath, wlpList[i].name + GTK_COMBO_BOX_TEXT(wallpaperComboBox), 0, wlpList[i].name, wlpList[i].name ); } + gtk_combo_box_set_active(GTK_COMBO_BOX(wallpaperComboBox), 0); free(wlpList); + + // Find selected monitor name + GtkListBoxRow *listBoxRow = gtk_list_box_get_selected_row(GTK_LIST_BOX(monitorListBox)); + GList *children = gtk_container_get_children(GTK_CONTAINER(listBoxRow)); + const char *monitorName = gtk_label_get_label(GTK_LABEL(children->data)); + gtk_label_set_text(GTK_LABEL(monitorNameLabel), monitorName); + g_list_free(children); + + // Read configuration from config file + MonitorConfig mc; + if (loadMonitorConfig(monitorName, &mc)) + { + gtk_spin_button_set_value(GTK_SPIN_BUTTON(xPosSpinBtn), (gdouble)mc.wlpBounds.x); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(yPosSpinBtn), (gdouble)mc.wlpBounds.y); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(widthSpinBtn), (gdouble)mc.wlpBounds.w); + gtk_spin_button_set_value(GTK_SPIN_BUTTON(heightSpinBtn), (gdouble)mc.wlpBounds.h); + + gtk_combo_box_set_active_id(GTK_COMBO_BOX(wallpaperComboBox), mc.wlpName); + } } G_MODULE_EXPORT void MonitorWindow_ApplyBtnClick() { MonitorConfig mc; - strcpy(mc.wlpPath, gtk_combo_box_get_active_id(GTK_COMBO_BOX(wallpaperComboBox))); + strcpy(mc.wlpName, gtk_combo_box_get_active_id(GTK_COMBO_BOX(wallpaperComboBox))); mc.wlpBounds.x = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(xPosSpinBtn)); mc.wlpBounds.y = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(yPosSpinBtn)); mc.wlpBounds.w = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widthSpinBtn)); mc.wlpBounds.h = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(heightSpinBtn)); - saveMonitorConfig(&mc); + saveMonitorConfig(gtk_label_get_text(GTK_LABEL(monitorNameLabel)), &mc); killWlp(); runWlp(); diff --git a/src/window_templates/main.glade b/src/window_templates/main.glade index af1d59e..b52a7e5 100644 --- a/src/window_templates/main.glade +++ b/src/window_templates/main.glade @@ -376,7 +376,7 @@ False vertical - + True False 10 @@ -406,6 +406,7 @@ True False + 0 1 From 94a0ab040ff4e8eca4d90caacf56d6804cc4edca Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Wed, 31 Jan 2024 12:59:01 +0000 Subject: [PATCH 14/33] Loading configuration from wallpaper process --- default.cfg | 41 +--- src/common.h | 37 +++- src/common/config.c | 77 ++++++++ src/core/CMakeLists.txt | 2 +- src/core/wlp.c | 62 +++--- src/wlp/CMakeLists.txt | 34 +++- src/wlp/debug.c | 21 +- src/wlp/main.c | 176 ++++++++++++++--- src/wlp/main.h | 41 ++-- src/wlp/wallpaper.c | 242 +++++++++++++----------- src/wlp/wallpaper.h | 4 - src/wlp/window.c | 104 +++++++--- src/wlp/window.h | 4 +- wallpapers/default-fullhd/wallpaper.cfg | 19 +- 14 files changed, 566 insertions(+), 298 deletions(-) diff --git a/default.cfg b/default.cfg index 1a03de4..ab24da3 100644 --- a/default.cfg +++ b/default.cfg @@ -1,39 +1,2 @@ -# ██╗ ██╗ ██╗██████╗ -# ██║ ██║ ██║██╔══██╗ -# ██║ ██║ █╗ ██║██████╔╝ -# ██║ ██║███╗██║██╔═══╝ -# ███████╗╚███╔███╔╝██║ -# ╚══════╝ ╚══╝╚══╝ ╚═╝ - -# If You are using a compositor, or lwp window deosn't show, -# try setting this to 1 -reload_rootwindow=0 - -# Smooth movement -# Increasing this value will make the wallpaper layers move faster -smooth=8.0 - -# Monitors count -monitors=1 - -# Monitor position -monitor1_x=0 -monitor1_y=0 - -# Monitor resolution -monitor1_w=1920 -monitor1_h=1080 - -# Absolute path to the wallpaper directory -monitor1_wallpaper=/usr/local/share/lwp/wallpapers/default-fullhd - -# Wallpaper size and position relative to Your monitor -# Wallpaper resolution ratio should be the same as in original image -monitor1_wallpaper_x=0 -monitor1_wallpaper_y=0 -monitor1_wallpaper_w=1920 -monitor1_wallpaper_h=1080 - -# How many times per second should the wallpaper render -# (imprecise, but accurate enough) -target_fps=60 +draw_on_rootwindow= 0; +target_fps = 60; diff --git a/src/common.h b/src/common.h index 2d127fa..ac50ff5 100644 --- a/src/common.h +++ b/src/common.h @@ -20,6 +20,29 @@ typedef struct int h; } Bounds; +typedef struct +{ + float sensitivityX; + float sensitivityY; +} LayerConfig; + +typedef struct +{ + int set; + int repeatX; + int repeatY; + int layersCount; + LayerConfig *layerConfigs; +} WallpaperConfig; + +typedef struct +{ + char name[WALLPAPER_NAME_MAX]; + char dirPath[PATH_MAX]; + int isDefault; + WallpaperConfig config; +} WallpaperInfo; + typedef struct { int set; @@ -34,18 +57,20 @@ typedef struct MonitorConfig config; } MonitorInfo; -MonitorInfo *scanMonitors(int *count); - typedef struct { - char name[WALLPAPER_NAME_MAX]; - char dirPath[PATH_MAX]; - int isDefault; -} WallpaperInfo; + int drawOnRootWindow; + int targetFps; +} AppConfig; +MonitorInfo *scanMonitors(int *count); WallpaperInfo *scanWallpapers(int *count); void saveMonitorConfig(const char *name, MonitorConfig *mc); int loadMonitorConfig(const char *name, MonitorConfig *mc); +int loadAppConfig(AppConfig *ac); + +int loadWallpaperConfig(const char *dirName, WallpaperConfig *wc); + #endif diff --git a/src/common/config.c b/src/common/config.c index 6cac664..9530b91 100644 --- a/src/common/config.c +++ b/src/common/config.c @@ -1,13 +1,25 @@ #include #include +#include #include "../common.h" +#define CONFIG_DEFAULT 0 +#define CONFIG_USER 1 + void getMonitorConfigPath(const char *name, char *path) { sprintf(path, "%s/.config/lwp/monitors/%s.cfg", g_get_home_dir(), name); } +void getAppConfigPath(char *path, int type) +{ + if (type == CONFIG_DEFAULT) + sprintf(path, "/etc/lwp.cfg"); + else + sprintf(path, "%s/.config/lwp/lwp.cfg", g_get_home_dir()); +} + void saveMonitorConfig(const char *name, MonitorConfig *mc) { config_t cfg; @@ -68,3 +80,68 @@ int loadMonitorConfig(const char *name, MonitorConfig *mc) return 1; } + +int loadAppConfig(AppConfig *ac) +{ + config_t cfg; + config_setting_t *root, *setting; + + char path[PATH_MAX]; + getAppConfigPath(path, CONFIG_USER); + + config_init(&cfg); + if (config_read_file(&cfg, path) == CONFIG_FALSE) + { + getAppConfigPath(path, CONFIG_DEFAULT); + if (config_read_file(&cfg, path) == CONFIG_FALSE) return 0; + } + root = config_root_setting(&cfg); + + setting = config_setting_get_member(root, "draw_on_rootwindow"); + ac->drawOnRootWindow = config_setting_get_int(setting); + setting = config_setting_get_member(root, "target_fps"); + ac->targetFps = config_setting_get_int(setting); + + config_destroy(&cfg); + + return 1; +} + +int loadWallpaperConfig(const char *dirName, WallpaperConfig *wc) +{ + char path[PATH_MAX]; + sprintf(path, "%s/wallpaper.cfg", dirName); + + config_t cfg; + config_setting_t *root, *setting; + + config_init(&cfg); + if (config_read_file(&cfg, path) == CONFIG_FALSE) return 0; + root = config_root_setting(&cfg); + + setting = config_setting_get_member(root, "count"); + wc->layersCount = config_setting_get_int(setting); + setting = config_setting_get_member(root, "repeat_x"); + wc->repeatX = config_setting_get_int(setting); + setting = config_setting_get_member(root, "repeat_y"); + wc->repeatY = config_setting_get_int(setting); + + wc->layerConfigs = malloc(wc->layersCount * sizeof(LayerConfig)); + + setting = config_setting_get_member(root, "movement_x"); + float movX = config_setting_get_float(setting); + setting = config_setting_get_member(root, "movement_y"); + float movY = config_setting_get_float(setting); + + for (int i = 0; i < wc->layersCount; i++) + { + LayerConfig *lc = wc->layerConfigs + i; + + lc->sensitivityX = movX * i; + lc->sensitivityY = movY * i; + } + + config_destroy(&cfg); + + return 1; +} diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index dd4556e..1403137 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -2,9 +2,9 @@ set(_SOURCE_FILES main.c windowHandlers.c wlp.c - ../common/wallpaperScanner.c ../common/monitorScanner.c ../common/config.c + ../common/wallpaperScanner.c ) # Windows resource file diff --git a/src/core/wlp.c b/src/core/wlp.c index 0e6b032..a51aac9 100644 --- a/src/core/wlp.c +++ b/src/core/wlp.c @@ -1,8 +1,10 @@ #include "main.h" #ifdef __WIN32 -#include #include +#include +#else +#include #endif #ifdef __WIN32 @@ -15,9 +17,13 @@ static int wlpPid = 0; void killWlp() { #ifdef __WIN32 - TerminateProcess(hWlpProcess, 0); + TerminateProcess(hWlpProcess, 0); #else - if (wlpPid != 0) kill(wlpPid, SIGINT); + if (wlpPid != 0) + { + kill(wlpPid, SIGINT); + waitpid(wlpPid, NULL, 0); + } if (wlpProcess != NULL) pclose(wlpProcess); wlpPid = 0; @@ -28,40 +34,28 @@ void killWlp() void runWlp() { #ifdef __WIN32 - STARTUPINFO si; - PROCESS_INFORMATION pi; + STARTUPINFO si; + PROCESS_INFORMATION pi; - ZeroMemory(&si, sizeof(si)); - ZeroMemory(&pi, sizeof(pi)); - si.cb = sizeof(si); + ZeroMemory(&si, sizeof(si)); + ZeroMemory(&pi, sizeof(pi)); + si.cb = sizeof(si); - TCHAR path[MAX_PATH]; - GetModuleFileName(NULL, path, MAX_PATH); - path[_tcslen(path)-4] = '\0'; - _tcscat(path, TEXT("wlp.exe")); - printf("%s\n", path); + TCHAR path[MAX_PATH]; + GetModuleFileName(NULL, path, MAX_PATH); + path[_tcslen(path) - 4] = '\0'; + _tcscat(path, TEXT("wlp.exe")); + printf("%s\n", path); - if(CreateProcess( - NULL, - path, - NULL, - NULL, - FALSE, - 0, - NULL, - NULL, - &si, - &pi - )) - hWlpProcess = pi.hProcess; - else - printf("Failed to start a wallpaper subprocess"); + if (CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) + hWlpProcess = pi.hProcess; + else + printf("Failed to start a wallpaper subprocess"); - #else - wlpProcess = popen("/home/cziken/Projects/lwp/build/src/wlp/lwpwlp", "r"); - char buff[10]; - fgets(buff, sizeof(buff) - 1, wlpProcess); - wlpPid = atoi(buff); + wlpProcess = popen("/usr/local/bin/lwpwlp", "r"); + char buff[10]; + fgets(buff, sizeof(buff) - 1, wlpProcess); + wlpPid = atoi(buff); #endif -} \ No newline at end of file +} diff --git a/src/wlp/CMakeLists.txt b/src/wlp/CMakeLists.txt index 7ba99e0..0ddfdae 100644 --- a/src/wlp/CMakeLists.txt +++ b/src/wlp/CMakeLists.txt @@ -3,17 +3,41 @@ if(SDL2_RUNTIME_DIR) endif() set(_SOURCE_FILES + ../common/monitorScanner.c + ../common/config.c + ../common/wallpaperScanner.c main.c - debug.c - parser.c + debug.c wallpaper.c window.c - ) + ) + +# GLIB dependency +find_package(PkgConfig REQUIRED) +pkg_search_module(GLIB REQUIRED glib-2.0) +set(_INCLUDE_DIRS ${GLIB_INCLUDE_DIRS}) +set(_LIBS ${GLIB_LDFLAGS}) + +if (_UNAME STREQUAL "LINUX") + # X11 and Xrandr dependency + include(FindX11) + + list(APPEND _INCLUDE_DIRS ${X11_INCLUDE_DIR}) + list(APPEND _LIBS ${X11_LIBRARIES}) + + list(APPEND _INCLUDE_DIRS ${X11_Xrandr_INCLUDE_PATH}) + list(APPEND _LIBS ${X11_Xrandr_LIB}) +endif() + +# libconfig dependency +pkg_check_modules (libconfig REQUIRED libconfig) +list(APPEND _INCLUDE_DIRS ${libconfig_INCLUDE_DIRS}) +list(APPEND _LIBS ${libconfig_LINK_LIBRARIES}) # SDL2 dependency find_package(SDL2 REQUIRED CONFIG) -set(_INCLUDE_DIRS ${SDL2_INCLUDE_DIRS}) -set(_LIBS ${SDL2_LIBRARIES}) +list(APPEND _INCLUDE_DIRS ${SDL2_INCLUDE_DIRS}) +list(APPEND _LIBS ${SDL2_LIBRARIES}) if (_UNAME STREQUAL "DARWIN") # MacOSX framework dependencies diff --git a/src/wlp/debug.c b/src/wlp/debug.c index 2381d3d..f66e7ee 100644 --- a/src/wlp/debug.c +++ b/src/wlp/debug.c @@ -5,33 +5,32 @@ void lwpLog(int type, const char *str, ...) { - char *typePrefixCodes; char *typePrefix; + FILE *file = fopen("/home/cziken/.config/lwp/log", "a"); + switch (type) { case LOG_ERROR: - typePrefixCodes = "\x1b[31m\x1b[1m"; - typePrefix = "ERROR"; + typePrefix = "ERROR"; break; case LOG_INFO: - typePrefixCodes = "\x1b[34m\x1b[1m"; - typePrefix = "INFO"; + typePrefix = "INFO"; break; case LOG_WARNING: - typePrefixCodes = "\x1b[33m\x1b[1m"; - typePrefix = "WARNING"; + typePrefix = "WARNING"; break; } - printf("%s%s: \x1b[0m", typePrefixCodes, typePrefix); + fprintf(file, "%s: ", typePrefix); va_list args; va_start(args, str); - vprintf(str, args); + vfprintf(file, str, args); va_end(args); - putchar('\n'); - fflush(stdout); + fprintf(file, "\n"); + + fclose(file); } diff --git a/src/wlp/main.c b/src/wlp/main.c index d7b5842..5c11052 100644 --- a/src/wlp/main.c +++ b/src/wlp/main.c @@ -1,10 +1,8 @@ #include "main.h" #include "debug.h" -#include "parser.h" #include "platform_guard.h" #include "wallpaper.h" -#include "window.h" #ifdef __WIN32 static void initCmd() @@ -26,7 +24,7 @@ static void initCmd() // Set console title SetConsoleTitle("Layered WallPaper"); } -BOOL monitorenumproc(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM param) +BOOLmonitorenumproc(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM param) { int px = GetSystemMetrics(SM_XVIRTUALSCREEN); int py = GetSystemMetrics(SM_YVIRTUALSCREEN); @@ -47,34 +45,164 @@ BOOL monitorenumproc(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM param) return TRUE; } +/* static void scanMonitors() { lwpLog(LOG_INFO, "Scanning monitors..."); EnumDisplayMonitors(NULL, NULL, &monitorenumproc, NULL); -} +}*/ #endif -static Config cfg; +static App app; static void atExit() { - freeConfig(&cfg); - - SDL_DestroyRenderer(cfg.renderer); - SDL_DestroyWindow(cfg.window); - SDL_Quit(); + // freeConfig(&cfg); + /* + SDL_DestroyRenderer(app.renderer); + SDL_DestroyWindow(app.window); + SDL_Quit();*/ } void exitSignalHandler(int s) { + lwpLog(LOG_INFO, "Terminating..."); exit(0); } +void initWallpaper(App *app, Monitor *m, WallpaperInfo *wallpapers, int wallpapersCount) +{ + MonitorInfo *mi = &m->info; + + int foundWlp = 0; + + for (int j = 0; j < wallpapersCount; j++) + { + if (strcmp(mi->config.wlpName, wallpapers[j].name) == 0) + { + memcpy(&m->wlp.info, wallpapers + j, sizeof(WallpaperInfo)); + + if (!loadWallpaperConfig(m->wlp.info.dirPath, &m->wlp.info.config)) + { + m->wlp.info.config.set = 0; + lwpLog(LOG_WARNING, "Could not load the wallpaper config"); + } + else + { + Wallpaper *wallpaper = &m->wlp; + + lwpLog(LOG_INFO, "Initializing wallpaper %s...", wallpaper->info.name); + lwpLog(LOG_INFO, "Layers count: %d", wallpaper->info.config.layersCount); + lwpLog( + LOG_INFO, + "Repeat X Y: %d %d", + wallpaper->info.config.repeatX, + wallpaper->info.config.repeatY + ); + + m->tex = SDL_CreateTexture( + app->renderer, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_TARGET, + mi->bounds.w, + mi->bounds.h + ); + if (m->tex == NULL) + lwpLog(LOG_ERROR, "Failed creating a texture for the monitor: %s", SDL_GetError()); + + wallpaper->layers = malloc(sizeof(Layer) * wallpaper->info.config.layersCount); + wallpaper->tex = SDL_CreateTexture( + app->renderer, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_TARGET, + mi->config.wlpBounds.w, + mi->config.wlpBounds.h + ); + if (wallpaper->tex == NULL) + lwpLog(LOG_ERROR, "Failed creating a texture for the monitor: %s", SDL_GetError()); + + for (int l = 0; l < wallpaper->info.config.layersCount; l++) + { + char path[PATH_MAX]; + snprintf(path, PATH_MAX, "%s/%d.bmp", wallpaper->info.dirPath, l + 1); + + SDL_Surface *surf = SDL_LoadBMP(path); + if (!surf) lwpLog(LOG_ERROR, "File %s not found", path); + + if (l == 0) + { + wallpaper->originalW = surf->w; + wallpaper->originalH = surf->h; + } + + wallpaper->layers[l].tex = SDL_CreateTextureFromSurface(app->renderer, surf); + if (wallpaper->tex == NULL) + lwpLog(LOG_ERROR, "Failed creating a texture for the layer %d: %s", l, SDL_GetError()); + + SDL_FreeSurface(surf); + } + } + foundWlp = 1; + + break; + } + } + if (!foundWlp) lwpLog(LOG_WARNING, "Couldn't find the wallpaper. Ignoring..."); +} + +void initMonitors(App *app) +{ + MonitorInfo *monitors = scanMonitors(&app->monitorsCount); + app->monitors = malloc(sizeof(Monitor) * app->monitorsCount); + + int wallpapersCount; + WallpaperInfo *wallpapers = scanWallpapers(&wallpapersCount); + + lwpLog(LOG_INFO, "Scanner found %d monitor(s)", app->monitorsCount); + + for (int i = 0; i < app->monitorsCount; i++) + { + memcpy(&app->monitors[i].info, monitors + i, sizeof(MonitorInfo)); + app->monitors[i].tex = NULL; + + MonitorInfo *mi = &app->monitors[i].info; + + if (!loadMonitorConfig(mi->name, &mi->config)) + { + lwpLog(LOG_WARNING, "Couldn't find config file for monitor %s. Ignoring...", mi->name); + mi->config.set = 0; + } + else + { + lwpLog(LOG_INFO, "Initializing monitor %d...", i); + lwpLog(LOG_INFO, "Wallpaper: %s", mi->config.wlpName); + lwpLog( + LOG_INFO, "Bounds: %d %d %dx%d", mi->bounds.x, mi->bounds.y, mi->bounds.w, mi->bounds.h + ); + lwpLog( + LOG_INFO, + "Wallpaper destination bounds: %d %d %dx%d", + mi->config.wlpBounds.x, + mi->config.wlpBounds.y, + mi->config.wlpBounds.w, + mi->config.wlpBounds.h + ); + + initWallpaper(app, app->monitors + i, wallpapers, wallpapersCount); + } + } + + free(wallpapers); + free(monitors); +} + int main(int argc, char *argv[]) { char pidStr[10]; sprintf(pidStr, "%d\0", getpid()); fputs(pidStr, stdout); + printf("\n"); + fflush(stdout); #ifdef __WIN32 signal(SIGTERM, exitSignalHandler); @@ -88,27 +216,29 @@ int main(int argc, char *argv[]) if (argc == 2 && strcmp(argv[1], "/console") == 0) initCmd(); initTrayIcon(); - scanMonitors(); + // scanMonitors(); #endif - parseConfig(&cfg); + lwpLog(LOG_INFO, "Loading app config"); + loadAppConfig(&app.config); + lwpLog(LOG_INFO, "Initializing SDL"); SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); - initWindow(&cfg); + + lwpLog(LOG_INFO, "Initializing window"); + initWindow(&app); + + lwpLog(LOG_INFO, "Initializing monitors"); + initMonitors(&app); atexit(atExit); - cfg.renderer = - SDL_CreateRenderer(cfg.window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); - if (cfg.renderer == NULL) - { - lwpLog(LOG_ERROR, "%s", SDL_GetError()); - } - runWallpaperLoop(&cfg); + lwpLog(LOG_INFO, "Starting wallpaper loop"); + runWallpaperLoop(&app); - #ifdef __WIN32 - removeTrayIcon(); - #endif +#ifdef __WIN32 + removeTrayIcon(); +#endif return 0; } diff --git a/src/wlp/main.h b/src/wlp/main.h index 71cb43b..e799f2c 100644 --- a/src/wlp/main.h +++ b/src/wlp/main.h @@ -14,11 +14,11 @@ #include #include #elif __DARWIN -#include -#include #include #include #include +#include +#include #include #elif __LINUX #include @@ -28,40 +28,39 @@ #include #endif +#include "../common.h" + typedef struct { - float sensitivityX; - float sensitivityY; SDL_Texture *tex; } Layer; typedef struct { - char dirPath[PATH_MAX]; - int repeatX, repeatY; - int layersCount; - Layer *layers; - int originalW, originalH; - SDL_Texture *tex; + WallpaperInfo info; + int originalW; + int originalH; + SDL_Texture *tex; + Layer *layers; } Wallpaper; typedef struct { - int x, y, w, h; - int wallpaperX, wallpaperY, wallpaperW, wallpaperH; + MonitorInfo info; SDL_Texture *tex; - Wallpaper wallpaper; -} WallpaperDest; + Wallpaper wlp; +} Monitor; typedef struct { - int reloadRootWnd; - int monitorsCount; - float smooth; - int targetFPS; - WallpaperDest *monitors; + AppConfig config; + int monitorsCount; + Monitor *monitors; SDL_Window *window; SDL_Renderer *renderer; -} Config; +} App; + +void initWindow(App *app); +void runWallpaperLoop(App *app); -#endif // MAIN_H +#endif // MAIN_H diff --git a/src/wlp/wallpaper.c b/src/wlp/wallpaper.c index edec74a..1136bce 100644 --- a/src/wlp/wallpaper.c +++ b/src/wlp/wallpaper.c @@ -5,9 +5,11 @@ #endif #include "debug.h" -#include "parser.h" - -static int appplyWallpaper(Config *cfg, WallpaperDest *monitor, Wallpaper *wallpaper, const char *dirPath) +#include "main.h" +/* +static int appplyWallpaper( + App *app, MonitorInfo *monitor, Wallpaper *wallpaper, const char *dirPath +) { char path[PATH_MAX]; @@ -40,27 +42,33 @@ static int appplyWallpaper(Config *cfg, WallpaperDest *monitor, Wallpaper *wallp SDL_FreeSurface(surf); } - wallpaper->tex = - SDL_CreateTexture(cfg->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, - monitor->wallpaperW, monitor->wallpaperH); + wallpaper->tex = SDL_CreateTexture( + cfg->renderer, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_TARGET, + monitor->wallpaperW, + monitor->wallpaperH + ); - monitor->tex = SDL_CreateTexture(cfg->renderer, SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_TARGET, monitor->w, monitor->h); + monitor->tex = SDL_CreateTexture( + cfg->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, monitor->w, monitor->h + ); return 1; } -int applyWallpapers(Config *cfg) +int initWallpapers(App *app) { - for (int m = 0; m < cfg->monitorsCount; m++) - if (!appplyWallpaper(cfg, &cfg->monitors[m], &cfg->monitors[m].wallpaper, - cfg->monitors[m].wallpaper.dirPath)) + for (int m = 0; m < app->monitorsCount; m++) + if (!appplyWallpaper( + app, &cfg->monitors[m], &cfg->monitors[m].wallpaper, cfg->monitors[m].wallpaper.dirPath + )) return 0; return 1; -} - -void freeMonitor(WallpaperDest *monitor) +}*/ +/* +void freeMonitor(MonitorInfo *monitor) { SDL_DestroyTexture(monitor->tex); SDL_DestroyTexture(monitor->wallpaper.tex); @@ -70,7 +78,7 @@ void freeMonitor(WallpaperDest *monitor) SDL_DestroyTexture(monitor->wallpaper.layers[i].tex); } free(monitor->wallpaper.layers); -} +}*/ static int lerp(int a, int b, float t) { @@ -78,15 +86,9 @@ static int lerp(int a, int b, float t) return (int)((float)a + (float)t * ((float)b - (float)a)); } -void runWallpaperLoop(Config *cfg) +void runWallpaperLoop(App *app) { - int quit = 0; - - if(!applyWallpapers(cfg)) - { - lwpLog(LOG_ERROR, "Failed applying wallpapers"); - quit = 1; - } + int quit = 0; SDL_Event event; @@ -95,117 +97,141 @@ void runWallpaperLoop(Config *cfg) while (!quit) { - static int currentX = 0; - static int currentY = 0; + static int currentX = 0; + static int currentY = 0; - static int lastTicks = 0; + static int lastTicks = 0; - int ticks = SDL_GetTicks(); - float dT = (ticks - lastTicks) / 1000.0f; - lastTicks = ticks; + int ticks = SDL_GetTicks(); + float dT = (ticks - lastTicks) / 1000.0f; + lastTicks = ticks; #ifdef __WIN32 - POINT mPos; - GetCursorPos(&mPos); - mx = mPos.x - GetSystemMetrics(SM_XVIRTUALSCREEN); - my = mPos.y - GetSystemMetrics(SM_YVIRTUALSCREEN); + POINT mPos; + GetCursorPos(&mPos); + mx = mPos.x - GetSystemMetrics(SM_XVIRTUALSCREEN); + my = mPos.y - GetSystemMetrics(SM_YVIRTUALSCREEN); #else - SDL_GetGlobalMouseState(&mx, &my); + SDL_GetGlobalMouseState(&mx, &my); #endif - while (SDL_PollEvent(&event)) - if (event.type == SDL_QUIT) quit = 1; + while (SDL_PollEvent(&event)) + if (event.type == SDL_QUIT) quit = 1; + + currentX = lerp(currentX, mx, dT * 4); // 4: smooth + currentY = lerp(currentY, my, dT * 4); + + for (int m = 0; m < app->monitorsCount; m++) + { + int relativeCurrentX = currentX - app->monitors[m].info.bounds.x; + int relativeCurrentY = currentY - app->monitors[m].info.bounds.y; - currentX = lerp(currentX, mx, dT * cfg->smooth); - currentY = lerp(currentY, my, dT * cfg->smooth); + if (relativeCurrentX < 0) relativeCurrentX = 0; + if (relativeCurrentY < 0) relativeCurrentY = 0; + if (relativeCurrentX > app->monitors[m].info.bounds.w) + relativeCurrentX = app->monitors[m].info.bounds.w; + if (relativeCurrentY > app->monitors[m].info.bounds.h) + relativeCurrentY = app->monitors[m].info.bounds.h; - for (int m = 0; m < cfg->monitorsCount; m++) + if (SDL_SetRenderTarget(app->renderer, app->monitors[m].wlp.tex) != 0) { - int relativeCurrentX = currentX - cfg->monitors[m].x; - int relativeCurrentY = currentY - cfg->monitors[m].y; + lwpLog(LOG_ERROR, "Error setting the renderer target: %s", SDL_GetError()); + quit = 1; + } + SDL_RenderClear(app->renderer); - if (relativeCurrentX < 0) relativeCurrentX = 0; - if (relativeCurrentY < 0) relativeCurrentY = 0; - if (relativeCurrentX > cfg->monitors[m].w) relativeCurrentX = cfg->monitors[m].w; - if (relativeCurrentY > cfg->monitors[m].h) relativeCurrentY = cfg->monitors[m].h; + for (int i = 0; i < app->monitors[m].wlp.info.config.layersCount; i++) + { + SDL_Rect src = { + .x = 0, + .y = 0, + .w = app->monitors[m].wlp.originalW, + .h = app->monitors[m].wlp.originalH, + }; - SDL_SetRenderTarget(cfg->renderer, cfg->monitors[m].wallpaper.tex); - SDL_RenderClear(cfg->renderer); + int x = + -((relativeCurrentX - app->monitors[m].info.bounds.w / 2) * + app->monitors[m].wlp.info.config.layerConfigs[i].sensitivityX); + int y = + -((relativeCurrentY - app->monitors[m].info.bounds.h / 2) * + app->monitors[m].wlp.info.config.layerConfigs[i].sensitivityY); - for (int i = 0; i < cfg->monitors[m].wallpaper.layersCount; i++) + for (int k = -app->monitors[m].wlp.info.config.repeatY; + k <= app->monitors[m].wlp.info.config.repeatY; + k++) { - SDL_Rect src = { - .x = 0, - .y = 0, - .w = cfg->monitors[m].wallpaper.originalW, - .h = cfg->monitors[m].wallpaper.originalH, - }; - - int x = - -((relativeCurrentX - cfg->monitors[m].w / 2) * - cfg->monitors[m].wallpaper.layers[i].sensitivityX); - int y = - -((relativeCurrentY - cfg->monitors[m].h / 2) * - cfg->monitors[m].wallpaper.layers[i].sensitivityY); - - for (int k = -cfg->monitors[m].wallpaper.repeatY; k <= cfg->monitors[m].wallpaper.repeatY; - k++) + for (int j = -app->monitors[m].wlp.info.config.repeatX; + j <= app->monitors[m].wlp.info.config.repeatX; + j++) { - for (int j = -cfg->monitors[m].wallpaper.repeatX; j <= cfg->monitors[m].wallpaper.repeatX; - j++) + SDL_Rect dest = { + .x = x + j * app->monitors[m].info.config.wlpBounds.w, + .y = y + k * app->monitors[m].info.config.wlpBounds.h, + .w = app->monitors[m].info.config.wlpBounds.w, + .h = app->monitors[m].info.config.wlpBounds.h, + }; + + if (SDL_RenderCopy(app->renderer, app->monitors[m].wlp.layers[i].tex, &src, &dest) != 0) { - SDL_Rect dest = { - .x = x + j * cfg->monitors[m].wallpaperW, - .y = y + k * cfg->monitors[m].wallpaperH, - .w = cfg->monitors[m].wallpaperW, - .h = cfg->monitors[m].wallpaperH, - }; - - SDL_RenderCopy(cfg->renderer, cfg->monitors[m].wallpaper.layers[i].tex, &src, &dest); + lwpLog(LOG_ERROR, "Error rendering copy: %s", SDL_GetError()); + quit = 1; } } } + } - SDL_SetRenderTarget(cfg->renderer, cfg->monitors[m].tex); - - SDL_Rect src = { - .x = 0, - .y = 0, - .w = cfg->monitors[m].wallpaperW, - .h = cfg->monitors[m].wallpaperH, - }; - - SDL_Rect dest = { - .x = cfg->monitors[m].wallpaperX, - .y = cfg->monitors[m].wallpaperY, - .w = cfg->monitors[m].wallpaperW, - .h = cfg->monitors[m].wallpaperH, - }; + if (SDL_SetRenderTarget(app->renderer, app->monitors[m].tex) != 0) + { + lwpLog(LOG_ERROR, "Error setting the renderer target: %s", SDL_GetError()); + quit = 1; + } - SDL_RenderCopy(cfg->renderer, cfg->monitors[m].wallpaper.tex, &src, &dest); + SDL_Rect src = { + .x = 0, + .y = 0, + .w = app->monitors[m].info.config.wlpBounds.w, + .h = app->monitors[m].info.config.wlpBounds.h, + }; + + SDL_Rect dest = { + .x = app->monitors[m].info.config.wlpBounds.x, + .y = app->monitors[m].info.config.wlpBounds.y, + .w = app->monitors[m].info.config.wlpBounds.w, + .h = app->monitors[m].info.config.wlpBounds.h, + }; + + if (SDL_RenderCopy(app->renderer, app->monitors[m].wlp.tex, &src, &dest) != 0) + { + lwpLog(LOG_ERROR, "Error rendering copy: %s", SDL_GetError()); + quit = 1; + } - SDL_SetRenderTarget(cfg->renderer, NULL); + SDL_SetRenderTarget(app->renderer, NULL); - SDL_Rect finalSrc = { - .x = 0, - .y = 0, - .w = cfg->monitors[m].w, - .h = cfg->monitors[m].h, - }; + SDL_Rect finalSrc = { + .x = 0, + .y = 0, + .w = app->monitors[m].info.bounds.w, + .h = app->monitors[m].info.bounds.h, + }; - SDL_Rect finalDest = { - .x = cfg->monitors[m].x, - .y = cfg->monitors[m].y, - .w = cfg->monitors[m].w, - .h = cfg->monitors[m].h, - }; + SDL_Rect finalDest = { + .x = app->monitors[m].info.bounds.x, + .y = app->monitors[m].info.bounds.y, + .w = app->monitors[m].info.bounds.w, + .h = app->monitors[m].info.bounds.h, + }; - SDL_RenderCopy(cfg->renderer, cfg->monitors[m].tex, &finalSrc, &finalDest); + if (SDL_RenderCopy(app->renderer, app->monitors[m].tex, &finalSrc, &finalDest) != 0) + { + lwpLog(LOG_ERROR, "Error rendering copy: %s", SDL_GetError()); + quit = 1; } - SDL_RenderPresent(cfg->renderer); - SDL_Delay(1000 / cfg->targetFPS); + } + SDL_RenderPresent(app->renderer); + SDL_Delay(1000 / app->config.targetFps); #ifdef __WIN32 if (!updateTrayIcon()) quit = 1; #endif } -} \ No newline at end of file +} diff --git a/src/wlp/wallpaper.h b/src/wlp/wallpaper.h index 92b1631..4da7889 100644 --- a/src/wlp/wallpaper.h +++ b/src/wlp/wallpaper.h @@ -3,8 +3,4 @@ #include "main.h" -int applyWallpapers(Config *cfg); -void freeMonitor(WallpaperDest *monitor); -void runWallpaperLoop(Config *cfg); - #endif diff --git a/src/wlp/window.c b/src/wlp/window.c index 2a4e61f..42075c6 100644 --- a/src/wlp/window.c +++ b/src/wlp/window.c @@ -63,8 +63,12 @@ static LRESULT CALLBACK wndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lPar case WM_TRAY_ICON: if (lParam == WM_RBUTTONDOWN || lParam == WM_LBUTTONDOWN) { - int res = MessageBox(NULL, "Do You want to run Layered WallPaper with debug console?", - "Restart Layered WallPaper", MB_YESNOCANCEL | MB_ICONQUESTION); + int res = MessageBox( + NULL, + "Do You want to run Layered WallPaper with debug console?", + "Restart Layered WallPaper", + MB_YESNOCANCEL | MB_ICONQUESTION + ); TCHAR processParam = NULL; @@ -104,8 +108,20 @@ void initTrayIcon() wc.hInstance = hInstance; wc.lpszClassName = CLASS_NAME; RegisterClass(&wc); - HWND hWnd = CreateWindowEx(0, CLASS_NAME, L"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, - CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); + HWND hWnd = CreateWindowEx( + 0, + CLASS_NAME, + L"", + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + NULL, + NULL, + hInstance, + NULL + ); // Create tray icon @@ -136,14 +152,20 @@ void initWindow(Config *cfg) HWND wallpaperWorkerw = GetWindow(iconWorkerw, GW_HWNDNEXT); SetParent(hWindow, wallpaperWorkerw); - SetWindowLongPtr(hWindow, GWL_EXSTYLE, - WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | WS_EX_NOACTIVATE); + SetWindowLongPtr( + hWindow, GWL_EXSTYLE, WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | WS_EX_NOACTIVATE + ); SetWindowLongPtr(hWindow, GWL_STYLE, WS_CHILDWINDOW | WS_VISIBLE); - SetWindowPos(hWindow, NULL, 0, 0, - GetSystemMetrics(SM_CXVIRTUALSCREEN), - GetSystemMetrics(SM_CYVIRTUALSCREEN), - SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW); + SetWindowPos( + hWindow, + NULL, + 0, + 0, + GetSystemMetrics(SM_CXVIRTUALSCREEN), + GetSystemMetrics(SM_CYVIRTUALSCREEN), + SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW + ); } #elif __DARWIN @@ -165,21 +187,30 @@ void initWindow(Config *cfg) // Get shared NScfglication instance const id ns_cfg = OBJC_MSG_CLS(objc_getClass("NScfglication"), sel_getUid("sharedcfglication")); - OBJC_MSG_INT(ns_cfg, sel_getUid("setActivationPolicy:"), - 0); // NScfglicationActivationPolicyRegular + OBJC_MSG_INT( + ns_cfg, + sel_getUid("setActivationPolicy:"), + 0 + ); // NScfglicationActivationPolicyRegular // Create NSWindow const id window = ((id(*)(id, SEL, struct CGRect, int, int, int))objc_msgSend)( OBJC_MSG_CLS(objc_getClass("NSWindow"), sel_getUid("alloc")), - sel_getUid("initWithContentRect:styleMask:backing:defer:"), frameRect, + sel_getUid("initWithContentRect:styleMask:backing:defer:"), + frameRect, 0, // NSWindowStyleMaskBorderless 2, // NSBackingStoreBuffered - false); + false + ); // Set window attributes - OBJC_MSG_ID(window, sel_getUid("setTitle:"), - OBJC_MSG_CLS_CHR(objc_getClass("NSString"), sel_getUid("stringWithUTF8String:"), - "Parallax wallpaper")); + OBJC_MSG_ID( + window, + sel_getUid("setTitle:"), + OBJC_MSG_CLS_CHR( + objc_getClass("NSString"), sel_getUid("stringWithUTF8String:"), "Parallax wallpaper" + ) + ); OBJC_MSG_PTR(window, sel_getUid("makeKeyAndOrderFront:"), nil); OBJC_MSG_INT(window, sel_getUid("setLevel:"), kCGDesktopWindowLevel - 1); OBJC_MSG_INT(ns_cfg, sel_getUid("activateIgnoringOthercfgs:"), true); @@ -188,30 +219,43 @@ void initWindow(Config *cfg) cfg->window = SDL_CreateWindowFrom((void *)window); if (cfg->window == NULL) lwpLog(LOG_ERROR, "%s", SDL_GetError()); } +#endif -#elif __LINUX - -void initWindow(Config *cfg) +void initWindow(App *app) { - if (cfg->reloadRootWnd) + if (!app->config.drawOnRootWindow) { Display *display = XOpenDisplay(NULL); XCloseDisplay(display); - cfg->window = SDL_CreateWindow("Parallax wallpaper", 0, 0, DisplayWidth(display, 0), - DisplayHeight(display, 0), SDL_WINDOW_OPENGL); + app->window = SDL_CreateWindow( + "Layered WallPaper", + 0, + 0, + DisplayWidth(display, 0), + DisplayHeight(display, 0), + SDL_WINDOW_OPENGL + ); SDL_SysWMinfo wmInfo; SDL_GetVersion(&wmInfo.version); - SDL_GetWindowWMInfo(cfg->window, &wmInfo); + SDL_GetWindowWMInfo(app->window, &wmInfo); Window xWnd = wmInfo.info.x11.window; display = wmInfo.info.x11.display; Atom atomType = XInternAtom(display, "_NET_WM_WINDOW_TYPE", 0); Atom atomDesktop = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DESKTOP", 0); - XChangeProperty(display, xWnd, atomType, XA_ATOM, 32, PropModeReplace, - (const unsigned char *)&atomDesktop, 1); + XChangeProperty( + display, + xWnd, + atomType, + XA_ATOM, + 32, + PropModeReplace, + (const unsigned char *)&atomDesktop, + 1 + ); Window rootWindow = RootWindow(display, DefaultScreen(display)); @@ -223,11 +267,13 @@ void initWindow(Config *cfg) { Display *display = XOpenDisplay(NULL); Window rootWindow = RootWindow(display, DefaultScreen(display)); - cfg->window = SDL_CreateWindowFrom((void *)rootWindow); + app->window = SDL_CreateWindowFrom((void *)rootWindow); XCloseDisplay(display); } - if (cfg->window == NULL) lwpLog(LOG_ERROR, "%s", SDL_GetError()); + if (app->window == NULL) lwpLog(LOG_ERROR, "Failed to initialize window: %s", SDL_GetError()); + app->renderer = + SDL_CreateRenderer(app->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); + if (app->renderer == NULL) lwpLog(LOG_ERROR, "Failed to initialize renderer: %s", SDL_GetError()); } -#endif diff --git a/src/wlp/window.h b/src/wlp/window.h index fc4a498..feffdf4 100644 --- a/src/wlp/window.h +++ b/src/wlp/window.h @@ -4,11 +4,9 @@ #include "main.h" #include "parser.h" -void initWindow(Config *cfg); - #ifdef __WIN32 void initTrayIcon(); -int updateTrayIcon(); +int updateTrayIcon(); void removeTrayIcon(); #endif diff --git a/wallpapers/default-fullhd/wallpaper.cfg b/wallpapers/default-fullhd/wallpaper.cfg index f2939f1..6276979 100644 --- a/wallpapers/default-fullhd/wallpaper.cfg +++ b/wallpapers/default-fullhd/wallpaper.cfg @@ -1,14 +1,5 @@ -# Layers count -count=4 - -# Base movement sensitivity -# Sensitivity for each layer is [ movement_x * (i-1) ], where i is the layer index. -movement_x=0.05 -movement_y=0.05 - -# You can override base sensitivity for a specific layer like this: -# movement2_x = 0.1 - -# Set this to 1, if Your wallpaper is repeatable -repeat_x=0 -repeat_y=0 +count=4; +movement_x=0.05; +movement_y=0.05; +repeat_x=0; +repeat_y=0; From c568392b6d0b655309222abe7a9cf5191237b10f Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Wed, 31 Jan 2024 18:22:42 +0000 Subject: [PATCH 15/33] Fixed few bugs --- src/common.h | 4 ++-- src/common/config.c | 26 +++++++++++++++++++++++--- src/common/monitorScanner.c | 20 ++++++++++---------- src/common/wallpaperScanner.c | 8 +++++--- src/wlp/main.c | 4 +--- src/wlp/wallpaper.c | 3 +++ 6 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/common.h b/src/common.h index ac50ff5..9314584 100644 --- a/src/common.h +++ b/src/common.h @@ -28,7 +28,7 @@ typedef struct typedef struct { - int set; + int loaded; int repeatX; int repeatY; int layersCount; @@ -45,7 +45,7 @@ typedef struct typedef struct { - int set; + int loaded; char wlpName[WALLPAPER_NAME_MAX]; Bounds wlpBounds; } MonitorConfig; diff --git a/src/common/config.c b/src/common/config.c index 9530b91..88d77ff 100644 --- a/src/common/config.c +++ b/src/common/config.c @@ -12,6 +12,14 @@ void getMonitorConfigPath(const char *name, char *path) sprintf(path, "%s/.config/lwp/monitors/%s.cfg", g_get_home_dir(), name); } +void getWallpaperConfigPath(const char *dirName, char *path, int type) +{ + if (type == CONFIG_DEFAULT) + sprintf(path, "%s/wallpaper.cfg", dirName); + else + sprintf(path, "%s/wallpaper.cfg", dirName); +} + void getAppConfigPath(char *path, int type) { if (type == CONFIG_DEFAULT) @@ -54,6 +62,8 @@ void saveMonitorConfig(const char *name, MonitorConfig *mc) int loadMonitorConfig(const char *name, MonitorConfig *mc) { + mc->loaded = 0; + config_t cfg; config_setting_t *root, *setting; @@ -78,6 +88,7 @@ int loadMonitorConfig(const char *name, MonitorConfig *mc) config_destroy(&cfg); + mc->loaded = 1; return 1; } @@ -109,14 +120,21 @@ int loadAppConfig(AppConfig *ac) int loadWallpaperConfig(const char *dirName, WallpaperConfig *wc) { - char path[PATH_MAX]; - sprintf(path, "%s/wallpaper.cfg", dirName); + wc->loaded = 0; config_t cfg; config_setting_t *root, *setting; config_init(&cfg); - if (config_read_file(&cfg, path) == CONFIG_FALSE) return 0; + + char path[PATH_MAX]; + getWallpaperConfigPath(dirName, path, CONFIG_USER); + + if (config_read_file(&cfg, path) == CONFIG_FALSE) + { + getWallpaperConfigPath(dirName, path, CONFIG_DEFAULT); + if (config_read_file(&cfg, path) == CONFIG_FALSE) return 0; + } root = config_root_setting(&cfg); setting = config_setting_get_member(root, "count"); @@ -143,5 +161,7 @@ int loadWallpaperConfig(const char *dirName, WallpaperConfig *wc) config_destroy(&cfg); + wc->loaded = 1; + return 1; } diff --git a/src/common/monitorScanner.c b/src/common/monitorScanner.c index 0e562f3..78cd9e0 100644 --- a/src/common/monitorScanner.c +++ b/src/common/monitorScanner.c @@ -29,11 +29,11 @@ BOOL monitorenumproc(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM param) monitorEnumIndex + 1, info.dwFlags == MONITORINFOF_PRIMARY ? " (main)" : "" ); - m[monitorEnumIndex].bounds.x = info.rcWork.left; - m[monitorEnumIndex].bounds.y = info.rcWork.top; - m[monitorEnumIndex].bounds.w = info.rcWork.right - info.rcWork.left; - m[monitorEnumIndex].bounds.h = info.rcWork.bottom - info.rcWork.top; - m[monitorEnumIndex].config.set = 0; + m[monitorEnumIndex].bounds.x = info.rcWork.left; + m[monitorEnumIndex].bounds.y = info.rcWork.top; + m[monitorEnumIndex].bounds.w = info.rcWork.right - info.rcWork.left; + m[monitorEnumIndex].bounds.h = info.rcWork.bottom - info.rcWork.top; + m[monitorEnumIndex].config.loaded = 0; monitorEnumIndex++; return TRUE; @@ -63,11 +63,11 @@ MonitorInfo *scanMonitors(int *count) while (i < monitorCount) { snprintf(m[i].name, MONITOR_NAME_MAX, "%s", XGetAtomName(display, info->name)); - m[i].bounds.x = info->x; - m[i].bounds.y = info->y; - m[i].bounds.w = info->width; - m[i].bounds.h = info->height; - m[i].config.set = 0; + m[i].bounds.x = info->x; + m[i].bounds.y = info->y; + m[i].bounds.w = info->width; + m[i].bounds.h = info->height; + m[i].config.loaded = 0; info++; i++; diff --git a/src/common/wallpaperScanner.c b/src/common/wallpaperScanner.c index 2b835a2..9da8502 100644 --- a/src/common/wallpaperScanner.c +++ b/src/common/wallpaperScanner.c @@ -30,7 +30,8 @@ WallpaperInfo *scanWallpapers(int *count) { sprintf(ptr->name, "%s", filename); sprintf(ptr->dirPath, "%s/%s", SYSTEM_WALLPAPERS_PATH, filename); - ptr->isDefault = 1; + ptr->isDefault = 1; + ptr->config.loaded = 0; ptr++; } @@ -38,8 +39,9 @@ WallpaperInfo *scanWallpapers(int *count) while ((filename = g_dir_read_name(dir))) { sprintf(ptr->name, "%s", filename); - sprintf(ptr->dirPath, "%s/%s", SYSTEM_WALLPAPERS_PATH, filename); - ptr->isDefault = 0; + sprintf(ptr->dirPath, "%s/%s/%s", g_get_home_dir(), ".config/lwp/wallpapers", filename); + ptr->isDefault = 0; + ptr->config.loaded = 0; ptr++; } diff --git a/src/wlp/main.c b/src/wlp/main.c index 5c11052..454450f 100644 --- a/src/wlp/main.c +++ b/src/wlp/main.c @@ -84,7 +84,6 @@ void initWallpaper(App *app, Monitor *m, WallpaperInfo *wallpapers, int wallpape if (!loadWallpaperConfig(m->wlp.info.dirPath, &m->wlp.info.config)) { - m->wlp.info.config.set = 0; lwpLog(LOG_WARNING, "Could not load the wallpaper config"); } else @@ -150,7 +149,7 @@ void initWallpaper(App *app, Monitor *m, WallpaperInfo *wallpapers, int wallpape if (!foundWlp) lwpLog(LOG_WARNING, "Couldn't find the wallpaper. Ignoring..."); } -void initMonitors(App *app) +int initMonitors(App *app) { MonitorInfo *monitors = scanMonitors(&app->monitorsCount); app->monitors = malloc(sizeof(Monitor) * app->monitorsCount); @@ -170,7 +169,6 @@ void initMonitors(App *app) if (!loadMonitorConfig(mi->name, &mi->config)) { lwpLog(LOG_WARNING, "Couldn't find config file for monitor %s. Ignoring...", mi->name); - mi->config.set = 0; } else { diff --git a/src/wlp/wallpaper.c b/src/wlp/wallpaper.c index 1136bce..c08cfa3 100644 --- a/src/wlp/wallpaper.c +++ b/src/wlp/wallpaper.c @@ -123,6 +123,9 @@ void runWallpaperLoop(App *app) for (int m = 0; m < app->monitorsCount; m++) { + if (!app->monitors[m].info.config.loaded || !app->monitors[m].wlp.info.config.loaded) + continue; + int relativeCurrentX = currentX - app->monitors[m].info.bounds.x; int relativeCurrentY = currentY - app->monitors[m].info.bounds.y; From 740a70bbb35026612dcc32c5849f706faea0e651 Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Wed, 31 Jan 2024 18:32:05 +0000 Subject: [PATCH 16/33] Cleaned code --- src/core/main.c | 2 + src/{wlp => }/platform_guard.h | 0 src/wlp/CMakeLists.txt | 2 +- src/wlp/debug.c | 4 +- src/wlp/debug.h | 10 - src/wlp/main.c | 4 +- src/wlp/main.h | 6 + src/wlp/parser.c | 302 ------------------------------ src/wlp/parser.h | 10 - src/wlp/{wallpaper.c => render.c} | 74 -------- src/wlp/wallpaper.h | 6 - src/wlp/window.c | 1 - src/wlp/window.h | 13 -- 13 files changed, 12 insertions(+), 422 deletions(-) rename src/{wlp => }/platform_guard.h (100%) delete mode 100644 src/wlp/debug.h delete mode 100644 src/wlp/parser.c delete mode 100644 src/wlp/parser.h rename src/wlp/{wallpaper.c => render.c} (75%) delete mode 100644 src/wlp/wallpaper.h delete mode 100644 src/wlp/window.h diff --git a/src/core/main.c b/src/core/main.c index 079cfa4..4fc2f52 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1,5 +1,7 @@ #include "main.h" +#include "../platform_guard.h" + #ifdef __WIN32 #define MAIN_WINDOW_TEMPLATE_PATH "./window_templates/main.glade" #elif __DARWIN diff --git a/src/wlp/platform_guard.h b/src/platform_guard.h similarity index 100% rename from src/wlp/platform_guard.h rename to src/platform_guard.h diff --git a/src/wlp/CMakeLists.txt b/src/wlp/CMakeLists.txt index 0ddfdae..9dfcd42 100644 --- a/src/wlp/CMakeLists.txt +++ b/src/wlp/CMakeLists.txt @@ -8,7 +8,7 @@ set(_SOURCE_FILES ../common/wallpaperScanner.c main.c debug.c - wallpaper.c + render.c window.c ) diff --git a/src/wlp/debug.c b/src/wlp/debug.c index f66e7ee..5081129 100644 --- a/src/wlp/debug.c +++ b/src/wlp/debug.c @@ -1,8 +1,8 @@ -#include "debug.h" - #include #include +#include "main.h" + void lwpLog(int type, const char *str, ...) { char *typePrefix; diff --git a/src/wlp/debug.h b/src/wlp/debug.h deleted file mode 100644 index fde6f2a..0000000 --- a/src/wlp/debug.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef DEBUG_H -#define DEBUG_H - -#define LOG_ERROR 0 -#define LOG_INFO 1 -#define LOG_WARNING 2 - -void lwpLog(int type, const char *str, ...); - -#endif diff --git a/src/wlp/main.c b/src/wlp/main.c index 454450f..cf402f9 100644 --- a/src/wlp/main.c +++ b/src/wlp/main.c @@ -1,8 +1,6 @@ #include "main.h" -#include "debug.h" -#include "platform_guard.h" -#include "wallpaper.h" +#include "../platform_guard.h" #ifdef __WIN32 static void initCmd() diff --git a/src/wlp/main.h b/src/wlp/main.h index e799f2c..1d3daef 100644 --- a/src/wlp/main.h +++ b/src/wlp/main.h @@ -30,6 +30,10 @@ #include "../common.h" +#define LOG_ERROR 0 +#define LOG_INFO 1 +#define LOG_WARNING 2 + typedef struct { SDL_Texture *tex; @@ -60,6 +64,8 @@ typedef struct SDL_Renderer *renderer; } App; +void lwpLog(int type, const char *str, ...); + void initWindow(App *app); void runWallpaperLoop(App *app); diff --git a/src/wlp/parser.c b/src/wlp/parser.c deleted file mode 100644 index 26a407c..0000000 --- a/src/wlp/parser.c +++ /dev/null @@ -1,302 +0,0 @@ -#include "parser.h" - -#include -#include - -#include "debug.h" -#include "wallpaper.h" - -#define TYPE_FLOAT 0 -#define TYPE_INT 1 -#define TYPE_STR 2 - -#ifdef __WIN32 -static void getline(char **buff, size_t *buffSize, FILE *f) -{ - long int startPos = ftell(f); - int length = 1; - char c; - do - { - c = fgetc(f); - length++; - } while (c != '\n' && !feof(f)); - - if (*buffSize < length) - { - *buff = realloc(*buff, length * sizeof(char)); - *buffSize = length * sizeof(char); - } - - fseek(f, startPos, SEEK_SET); - - fgets(*buff, length - 1, f); - (*buff)[length - 1] = '\0'; - fgetc(f); -} -#endif - -static FILE *openConfigFile() -{ - FILE *f; - char userConfigPath[PATH_MAX]; - -#ifdef __WIN32 - strcpy(userConfigPath, getenv("AppData")); - strcat(userConfigPath, "\\lwp\\lwp.cfg"); -#else - char *xdgConfigHome = getenv("XDG_CONFIG_HOME"); - if (xdgConfigHome) - { - strcpy(userConfigPath, xdgConfigHome); - strcat(userConfigPath, "/lwp/lwp.cfg"); - } - else - { - struct passwd *pw = getpwuid(getuid()); - strcpy(userConfigPath, pw->pw_dir); - strcat(userConfigPath, "/.config/lwp/lwp.cfg"); - } -#endif - - f = fopen(userConfigPath, "r"); - if (!f) - { - lwpLog(LOG_INFO, "User config file not found, opening default config instead"); - char defaultConfigPath[PATH_MAX]; -#ifdef __WIN32 - GetModuleFileNameA(NULL, defaultConfigPath, PATH_MAX); - char *ptr = defaultConfigPath + strlen(defaultConfigPath) - 1; - while (*ptr != '\\') ptr--; - *ptr = '\0'; - strcat(defaultConfigPath, "\\defaultWin.cfg"); - - f = fopen(defaultConfigPath, "r"); -#elif __DARWIN - strcpy(defaultConfigPath, "/Applications/Layered_WallPaper/lwp.cfg"); - - f = fopen(defaultConfigPath, "r"); -#else - char *sysConfDir = getenv("sysconfdir"); - if (sysConfDir) - { - strcpy(defaultConfigPath, sysConfDir); - strcat(defaultConfigPath, "/lwp.cfg"); - } - else - strcpy(defaultConfigPath, "/etc/lwp.cfg"); - - f = fopen(defaultConfigPath, "r"); -#endif - if (!f) lwpLog(LOG_ERROR, "Default config file not found!"); - } - - return f; -} - -static int findLine(FILE *f, const char *name, int type, void *output) -{ - char *buff = NULL; - size_t buffSize = 0; - - int found = 0; - - fseek(f, 0, SEEK_SET); - - do - { - getline(&buff, &buffSize, f); - - if (buffSize > 0 && buff[0] != '#') - { - if (strncmp(name, buff, strlen(name)) == 0) - { - char *valuePtr = buff; - while (*valuePtr != '=' && *valuePtr != '\0') - { - valuePtr++; - } - - if (*valuePtr == '=') - { - valuePtr++; - - if (valuePtr[strlen(valuePtr) - 1] == '\n') valuePtr[strlen(valuePtr) - 1] = '\0'; - - found = 1; - - switch (type) - { - case TYPE_INT: - *(int *)output = atoi(valuePtr); - break; - case TYPE_FLOAT: - *(float *)output = atof(valuePtr); - break; - case TYPE_STR: - strcpy((char *)output, valuePtr); - break; - } - } - } - } - } while (!found && buffSize > 0 && !feof(f)); - - free(buff); - return found; -} - -int parseConfig(Config *cfg) -{ - lwpLog(LOG_INFO, "Loading config file"); - - FILE *f = openConfigFile(); - - if (!findLine(f, "monitors", TYPE_INT, &cfg->monitorsCount)) - { - lwpLog(LOG_ERROR, "Can't find line 'monitors' in config"); - return 0; - } - lwpLog(LOG_INFO, " monitors: %d", cfg->monitorsCount); - - if (!findLine(f, "smooth", TYPE_FLOAT, &cfg->smooth)) - { - lwpLog(LOG_INFO, "Can't find line 'smooth' in config, setting to default value"); - cfg->smooth = 8; - } - lwpLog(LOG_INFO, " smooth: %f", cfg->smooth); - - if (!findLine(f, "target_fps", TYPE_INT, &cfg->targetFPS)) - { - lwpLog(LOG_INFO, "Can't find line 'target_fps' in config, setting to default value"); - cfg->targetFPS = 60; - } - lwpLog(LOG_INFO, " target_fps: %d", cfg->targetFPS); - -#ifdef __LINUX - if (!findLine(f, "reload_rootwindow", TYPE_INT, &cfg->reloadRootWnd)) - { - lwpLog(LOG_ERROR, "Can't find line 'reload_rootwindow' in config"); - return 0; - } - lwpLog(LOG_INFO, " reload_rootwindow: %d", cfg->reloadRootWnd); -#endif - - cfg->monitors = malloc(cfg->monitorsCount * sizeof(WallpaperDest)); - - for (int m = 0; m < cfg->monitorsCount; m++) - { - char wallpaperPath[PATH_MAX]; - - const int PARAMS_COUNT = 9; - - const char *paramStr[] = {"wallpaper", "x", "y", "w", "h", - "wallpaper_x", "wallpaper_y", "wallpaper_w", "wallpaper_h"}; - void *outputs[] = { - wallpaperPath, - &cfg->monitors[m].x, - &cfg->monitors[m].y, - &cfg->monitors[m].w, - &cfg->monitors[m].h, - &cfg->monitors[m].wallpaperX, - &cfg->monitors[m].wallpaperY, - &cfg->monitors[m].wallpaperW, - &cfg->monitors[m].wallpaperH, - }; - - char str[100]; - - for (int p = 0; p < PARAMS_COUNT; p++) - { - snprintf(str, 100, "monitor%d_%s", m + 1, paramStr[p]); - if (!findLine(f, str, (p == 0 ? TYPE_STR : TYPE_INT), outputs[p])) - { - lwpLog(LOG_ERROR, "Can't find line '%s' in config", str); - return 0; - } - if(p > 0) - lwpLog(LOG_INFO, " %s: %d", str, *(int*)(outputs[p])); - } - -#ifdef __WIN32 - if (strlen(wallpaperPath) < 2) - { - GetModuleFileNameA(NULL, wallpaperPath, PATH_MAX); - char *ptr = wallpaperPath + strlen(wallpaperPath) - 1; - while (*ptr != '\\') ptr--; - *ptr = '\0'; - strcat(wallpaperPath, "\\wallpapers\\default-fullhd"); - } -#endif - - strncpy(cfg->monitors[m].wallpaper.dirPath, wallpaperPath, PATH_MAX); - lwpLog(LOG_INFO, " monitor%d_wallpaper: %s", m+1, wallpaperPath); - } - - fclose(f); - lwpLog(LOG_INFO, "Config file loaded"); - return 1; -} - -int parseWallpaperConfig(Wallpaper *wallpaper, const char *path) -{ - lwpLog(LOG_INFO, "Loading wallpaper config"); - FILE *f = fopen(path, "r"); - if (!f) - { - lwpLog(LOG_ERROR, "Wallpaper config file (%s) doesn't exist", path); - return 0; - } - - float defaultMovementX, defaultMovementY; - - const int PARAMS_COUNT = 5; - const char *paramStr[] = {"count", "movement_x", "movement_y", "repeat_x", "repeat_y"}; - const int types[] = {TYPE_INT, TYPE_FLOAT, TYPE_FLOAT, TYPE_INT, TYPE_INT}; - void *outputs[] = { - &wallpaper->layersCount, &defaultMovementX, &defaultMovementY, - &wallpaper->repeatX, &wallpaper->repeatY, - }; - - for (int p = 0; p < PARAMS_COUNT; p++) - { - if (!findLine(f, paramStr[p], types[p], outputs[p])) - { - lwpLog(LOG_ERROR, "Can't find line '%s' in config", paramStr[p]); - return 0; - } - - if (types[p] == TYPE_FLOAT) - lwpLog(LOG_INFO, " %s: %f", paramStr[p], *(float*)(outputs[p])); - else - lwpLog(LOG_INFO, " %s: %d", paramStr[p], *(int*)(outputs[p])); - } - wallpaper->layers = malloc(wallpaper->layersCount * sizeof(Layer)); - - lwpLog(LOG_INFO, " Each layer movements:"); - - char str[100]; - for (int l = 0; l < wallpaper->layersCount; l++) - { - snprintf(str, 100, "movement%d_x", l + 1); - if (!findLine(f, str, TYPE_FLOAT, &wallpaper->layers[l].sensitivityX)) - wallpaper->layers[l].sensitivityX = defaultMovementX * l; - lwpLog(LOG_INFO, " %s: %f", str, wallpaper->layers[l].sensitivityX); - - snprintf(str, 100, "movement%d_y", l + 1); - if (!findLine(f, str, TYPE_FLOAT, &wallpaper->layers[l].sensitivityY)) - wallpaper->layers[l].sensitivityY = defaultMovementY * l; - lwpLog(LOG_INFO, " %s: %f", str, wallpaper->layers[l].sensitivityY); - } - - fclose(f); - - lwpLog(LOG_INFO, "Wallpaper config file loaded"); - return 1; -} - -void freeConfig(Config *cfg) -{ - for (int i = 0; i < cfg->monitorsCount; i++) freeMonitor(&cfg->monitors[i]); - free(cfg->monitors); -} diff --git a/src/wlp/parser.h b/src/wlp/parser.h deleted file mode 100644 index d20603c..0000000 --- a/src/wlp/parser.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef PARSER_H -#define PARSER_H - -#include "main.h" - -int parseConfig(Config *cfg); -int parseWallpaperConfig(Wallpaper *wallpaper, const char *path); -void freeConfig(Config *cfg); - -#endif diff --git a/src/wlp/wallpaper.c b/src/wlp/render.c similarity index 75% rename from src/wlp/wallpaper.c rename to src/wlp/render.c index c08cfa3..95e27e2 100644 --- a/src/wlp/wallpaper.c +++ b/src/wlp/render.c @@ -4,81 +4,7 @@ #include #endif -#include "debug.h" #include "main.h" -/* -static int appplyWallpaper( - App *app, MonitorInfo *monitor, Wallpaper *wallpaper, const char *dirPath -) -{ - char path[PATH_MAX]; - - snprintf(path, PATH_MAX, "%s/wallpaper.cfg", dirPath); - - if (!parseWallpaperConfig(wallpaper, path)) - { - return 0; - } - - for (int i = 0; i < wallpaper->layersCount; i++) - { - snprintf(path, PATH_MAX, "%s/%d.bmp", dirPath, i + 1); - - SDL_Surface *surf = SDL_LoadBMP(path); - if (!surf) - { - lwpLog(LOG_ERROR, "File %s not found", path); - return 0; - } - - if (i == 0) - { - wallpaper->originalW = surf->w; - wallpaper->originalH = surf->h; - } - - wallpaper->layers[i].tex = SDL_CreateTextureFromSurface(cfg->renderer, surf); - - SDL_FreeSurface(surf); - } - - wallpaper->tex = SDL_CreateTexture( - cfg->renderer, - SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_TARGET, - monitor->wallpaperW, - monitor->wallpaperH - ); - - monitor->tex = SDL_CreateTexture( - cfg->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, monitor->w, monitor->h - ); - - return 1; -} - -int initWallpapers(App *app) -{ - for (int m = 0; m < app->monitorsCount; m++) - if (!appplyWallpaper( - app, &cfg->monitors[m], &cfg->monitors[m].wallpaper, cfg->monitors[m].wallpaper.dirPath - )) - return 0; - - return 1; -}*/ -/* -void freeMonitor(MonitorInfo *monitor) -{ - SDL_DestroyTexture(monitor->tex); - SDL_DestroyTexture(monitor->wallpaper.tex); - - for (int i = 0; i < monitor->wallpaper.layersCount; i++) - { - SDL_DestroyTexture(monitor->wallpaper.layers[i].tex); - } - free(monitor->wallpaper.layers); -}*/ static int lerp(int a, int b, float t) { diff --git a/src/wlp/wallpaper.h b/src/wlp/wallpaper.h deleted file mode 100644 index 4da7889..0000000 --- a/src/wlp/wallpaper.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef WALLPAPER_H -#define WALLPAPER_H - -#include "main.h" - -#endif diff --git a/src/wlp/window.c b/src/wlp/window.c index 42075c6..10a15af 100644 --- a/src/wlp/window.c +++ b/src/wlp/window.c @@ -1,4 +1,3 @@ -#include "debug.h" #include "main.h" #ifdef __WIN32 diff --git a/src/wlp/window.h b/src/wlp/window.h deleted file mode 100644 index feffdf4..0000000 --- a/src/wlp/window.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef WINDOW_H -#define WINDOW_H - -#include "main.h" -#include "parser.h" - -#ifdef __WIN32 -void initTrayIcon(); -int updateTrayIcon(); -void removeTrayIcon(); -#endif - -#endif From 91186233d5d0507a99a8be5c675a38f71c13c6ba Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Wed, 31 Jan 2024 18:41:34 +0000 Subject: [PATCH 17/33] Freeing memory at exit --- src/wlp/main.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/wlp/main.c b/src/wlp/main.c index cf402f9..c0a6b18 100644 --- a/src/wlp/main.c +++ b/src/wlp/main.c @@ -55,11 +55,24 @@ static App app; static void atExit() { - // freeConfig(&cfg); - /* - SDL_DestroyRenderer(app.renderer); - SDL_DestroyWindow(app.window); - SDL_Quit();*/ + for (int i = 0; i < app.monitorsCount; i++) + { + Monitor *m = app.monitors + i; + if (m->tex) SDL_DestroyTexture(m->tex); + if (m->wlp.tex) SDL_DestroyTexture(m->wlp.tex); + + for (int l = 0; l < m->wlp.info.config.layersCount; l++) + SDL_DestroyTexture(m->wlp.layers[l].tex); + free(m->wlp.layers); + + m++; + } + free(app.monitors); + + if (app.renderer) SDL_DestroyRenderer(app.renderer); + if (app.window) SDL_DestroyWindow(app.window); + + SDL_Quit(); } void exitSignalHandler(int s) @@ -194,6 +207,8 @@ int initMonitors(App *app) int main(int argc, char *argv[]) { + memset(&app, 0, sizeof(App)); + char pidStr[10]; sprintf(pidStr, "%d\0", getpid()); fputs(pidStr, stdout); From 17fe38616891cc573a0bfd7c0bcef8fc6bc15076 Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Wed, 31 Jan 2024 20:34:22 +0000 Subject: [PATCH 18/33] version label --- src/core/CMakeLists.txt | 2 +- src/core/main.c | 4 ++++ src/core/windowHandlers.c | 5 +++++ src/window_templates/main.glade | 3 ++- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1403137..aa7cf33 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -57,7 +57,7 @@ endif() set_target_properties(lwp PROPERTIES ENABLE_EXPORTS on) -target_compile_definitions(lwp PUBLIC __${_UNAME}) +target_compile_definitions(lwp PUBLIC __${_UNAME} PROGRAM_VERSION="${PROGRAM_VERSION}") target_include_directories(lwp PUBLIC ${_INCLUDE_DIRS}) target_link_libraries(lwp PRIVATE ${_LIBS}) diff --git a/src/core/main.c b/src/core/main.c index 4fc2f52..e8fdcf6 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -25,6 +25,7 @@ GtkWidget *yPosSpinBtn = NULL; GtkWidget *widthSpinBtn = NULL; GtkWidget *heightSpinBtn = NULL; GtkWidget *monitorNameLabel = NULL; +GtkWidget *versionLabel = NULL; static void reloadMonitorListBox() { @@ -71,6 +72,7 @@ static void activate(GtkApplication *app, gpointer userdata) wallpaperMgrWnd = (GtkWidget *)gtk_builder_get_object(builder, "WallpaperManagerWindow"); monitorWnd = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow"); monitorListBox = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow_MonitorListBox"); + versionLabel = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow_VersionLabel"); wallpaperListBox = (GtkWidget *)gtk_builder_get_object(builder, "WallpaperManagerWindow_WallpaperListBox"); wallpaperComboBox = @@ -87,6 +89,8 @@ static void activate(GtkApplication *app, gpointer userdata) gtk_window_set_application(GTK_WINDOW(wallpaperMgrWnd), GTK_APPLICATION(app)); gtk_window_set_application(GTK_WINDOW(monitorWnd), GTK_APPLICATION(app)); + gtk_label_set_text(GTK_LABEL(versionLabel), PROGRAM_VERSION); + runWlp(); } diff --git a/src/core/windowHandlers.c b/src/core/windowHandlers.c index 82aa588..bba23ab 100644 --- a/src/core/windowHandlers.c +++ b/src/core/windowHandlers.c @@ -52,6 +52,9 @@ G_MODULE_EXPORT void WallpaperManagerWindowShow() G_MODULE_EXPORT void MonitorWindowClose() { gtk_widget_set_visible(monitorWnd, 0); } G_MODULE_EXPORT void MonitorWindowShow() { + // Clear wallpaper list + gtk_combo_box_text_remove_all(GTK_COMBO_BOX_TEXT(wallpaperComboBox)); + // Load wallpaper list int wlpCount; WallpaperInfo *wlpList = scanWallpapers(&wlpCount); @@ -100,3 +103,5 @@ G_MODULE_EXPORT void MonitorWindow_ApplyBtnClick() killWlp(); runWlp(); } + +G_MODULE_EXPORT void MonitorWindow_ExitBtnClick() { gtk_widget_set_visible(monitorWnd, 0); } diff --git a/src/window_templates/main.glade b/src/window_templates/main.glade index b52a7e5..e91fcd9 100644 --- a/src/window_templates/main.glade +++ b/src/window_templates/main.glade @@ -190,7 +190,7 @@ True False - + True False 11 @@ -558,6 +558,7 @@ True True True + False From ea510237cc191244b9da2e651c8c639c7715c911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Szczerbi=C5=84ski?= Date: Wed, 7 Feb 2024 16:40:29 +0100 Subject: [PATCH 19/33] Running on Windows (but wlp not moving --- distributeDLLs.sh | 4 ++ src/common/config.c | 107 ++++++++++++++++++++-------------- src/common/monitorScanner.c | 57 ++++++++---------- src/common/wallpaperScanner.c | 71 +++++++++++++++------- src/core/main.c | 1 + src/wlp/debug.c | 17 +++++- src/wlp/main.c | 60 ------------------- src/wlp/render.c | 3 - src/wlp/window.c | 81 ++++++++++++------------- 9 files changed, 200 insertions(+), 201 deletions(-) diff --git a/distributeDLLs.sh b/distributeDLLs.sh index ccfcab3..6a8d53d 100644 --- a/distributeDLLs.sh +++ b/distributeDLLs.sh @@ -1,6 +1,9 @@ #!/bin/bash mkdir -p dlls +mkdir -p dlls/lib +mkdir -p dlls/lib/gdk-pixbuf-2.0 +mkdir -p dlls/lib/gdk-pixbuf-2.0/2.10.0 echo PS3='Choose the correct version of gdbus: ' @@ -15,5 +18,6 @@ done echo "Preparing DLLs..." ldd ./src/core/lwp.exe | grep '\/mingw.*\.dll' -o | xargs -I{} cp "{}" ./dlls/ +cp /mingw64/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache ./dlls/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache echo "Done" \ No newline at end of file diff --git a/src/common/config.c b/src/common/config.c index 88d77ff..259af27 100644 --- a/src/common/config.c +++ b/src/common/config.c @@ -5,32 +5,47 @@ #include "../common.h" #define CONFIG_DEFAULT 0 -#define CONFIG_USER 1 +#define CONFIG_USER 1 -void getMonitorConfigPath(const char *name, char *path) -{ +void getMonitorConfigPath(const char *name, char *path) { +#ifdef __WIN32 + sprintf(path, "%s\\lwp\\monitors\\%s.cfg", g_get_user_data_dir(), name); +#else sprintf(path, "%s/.config/lwp/monitors/%s.cfg", g_get_home_dir(), name); +#endif } -void getWallpaperConfigPath(const char *dirName, char *path, int type) -{ +void getWallpaperConfigPath(const char *dirName, char *path, int type) { if (type == CONFIG_DEFAULT) sprintf(path, "%s/wallpaper.cfg", dirName); else sprintf(path, "%s/wallpaper.cfg", dirName); } -void getAppConfigPath(char *path, int type) -{ +void getAppConfigPath(char *path, int type) { +#ifdef __WIN32 + if (type == CONFIG_DEFAULT) + sprintf(path, "/etc/lwp.cfg"); + else + sprintf(path, "%s\\lwp\\lwp.cfg", g_get_user_data_dir()); +#else if (type == CONFIG_DEFAULT) sprintf(path, "/etc/lwp.cfg"); else sprintf(path, "%s/.config/lwp/lwp.cfg", g_get_home_dir()); +#endif +} + +static void generateEmptyMonitorConfig(MonitorConfig *mc) { + sprintf(mc->wlpName, ""); + mc->wlpBounds.x = 0; + mc->wlpBounds.y = 0; + mc->wlpBounds.w = 1920; + mc->wlpBounds.h = 1080; } -void saveMonitorConfig(const char *name, MonitorConfig *mc) -{ - config_t cfg; +void saveMonitorConfig(const char *name, MonitorConfig *mc) { + config_t cfg; config_setting_t *root, *setting; config_init(&cfg); @@ -51,8 +66,7 @@ void saveMonitorConfig(const char *name, MonitorConfig *mc) char path[PATH_MAX]; getMonitorConfigPath(name, path); - if (!config_write_file(&cfg, path)) - { + if (!config_write_file(&cfg, path)) { fprintf(stderr, "Error while writing file.\n"); config_destroy(&cfg); } @@ -60,30 +74,36 @@ void saveMonitorConfig(const char *name, MonitorConfig *mc) config_destroy(&cfg); } -int loadMonitorConfig(const char *name, MonitorConfig *mc) -{ +int loadMonitorConfig(const char *name, MonitorConfig *mc) { mc->loaded = 0; - config_t cfg; + config_t cfg; config_setting_t *root, *setting; char path[PATH_MAX]; getMonitorConfigPath(name, path); + if (!g_file_test(path, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS)) { + generateEmptyMonitorConfig(mc); + saveMonitorConfig(name, mc); + return 1; + } + config_init(&cfg); - if (config_read_file(&cfg, path) == CONFIG_FALSE) return 0; + if (config_read_file(&cfg, path) == CONFIG_FALSE) + return 0; root = config_root_setting(&cfg); setting = config_setting_get_member(root, "wlpName"); strcpy(mc->wlpName, config_setting_get_string(setting)); - setting = config_setting_get_member(root, "x"); + setting = config_setting_get_member(root, "x"); mc->wlpBounds.x = config_setting_get_int(setting); - setting = config_setting_get_member(root, "y"); + setting = config_setting_get_member(root, "y"); mc->wlpBounds.y = config_setting_get_int(setting); - setting = config_setting_get_member(root, "w"); + setting = config_setting_get_member(root, "w"); mc->wlpBounds.w = config_setting_get_int(setting); - setting = config_setting_get_member(root, "h"); + setting = config_setting_get_member(root, "h"); mc->wlpBounds.h = config_setting_get_int(setting); config_destroy(&cfg); @@ -92,37 +112,37 @@ int loadMonitorConfig(const char *name, MonitorConfig *mc) return 1; } -int loadAppConfig(AppConfig *ac) -{ - config_t cfg; +int loadAppConfig(AppConfig *ac) { + config_t cfg; config_setting_t *root, *setting; char path[PATH_MAX]; getAppConfigPath(path, CONFIG_USER); config_init(&cfg); - if (config_read_file(&cfg, path) == CONFIG_FALSE) - { + if (config_read_file(&cfg, path) == CONFIG_FALSE) { getAppConfigPath(path, CONFIG_DEFAULT); - if (config_read_file(&cfg, path) == CONFIG_FALSE) return 0; + if (config_read_file(&cfg, path) == CONFIG_FALSE) + return 0; } root = config_root_setting(&cfg); - setting = config_setting_get_member(root, "draw_on_rootwindow"); +#ifdef __LINUX + setting = config_setting_get_member(root, "draw_on_rootwindow"); ac->drawOnRootWindow = config_setting_get_int(setting); - setting = config_setting_get_member(root, "target_fps"); - ac->targetFps = config_setting_get_int(setting); +#endif + setting = config_setting_get_member(root, "target_fps"); + ac->targetFps = config_setting_get_int(setting); config_destroy(&cfg); return 1; } -int loadWallpaperConfig(const char *dirName, WallpaperConfig *wc) -{ +int loadWallpaperConfig(const char *dirName, WallpaperConfig *wc) { wc->loaded = 0; - config_t cfg; + config_t cfg; config_setting_t *root, *setting; config_init(&cfg); @@ -130,29 +150,28 @@ int loadWallpaperConfig(const char *dirName, WallpaperConfig *wc) char path[PATH_MAX]; getWallpaperConfigPath(dirName, path, CONFIG_USER); - if (config_read_file(&cfg, path) == CONFIG_FALSE) - { + if (config_read_file(&cfg, path) == CONFIG_FALSE) { getWallpaperConfigPath(dirName, path, CONFIG_DEFAULT); - if (config_read_file(&cfg, path) == CONFIG_FALSE) return 0; + if (config_read_file(&cfg, path) == CONFIG_FALSE) + return 0; } root = config_root_setting(&cfg); - setting = config_setting_get_member(root, "count"); + setting = config_setting_get_member(root, "count"); wc->layersCount = config_setting_get_int(setting); - setting = config_setting_get_member(root, "repeat_x"); - wc->repeatX = config_setting_get_int(setting); - setting = config_setting_get_member(root, "repeat_y"); - wc->repeatY = config_setting_get_int(setting); + setting = config_setting_get_member(root, "repeat_x"); + wc->repeatX = config_setting_get_int(setting); + setting = config_setting_get_member(root, "repeat_y"); + wc->repeatY = config_setting_get_int(setting); wc->layerConfigs = malloc(wc->layersCount * sizeof(LayerConfig)); - setting = config_setting_get_member(root, "movement_x"); + setting = config_setting_get_member(root, "movement_x"); float movX = config_setting_get_float(setting); - setting = config_setting_get_member(root, "movement_y"); + setting = config_setting_get_member(root, "movement_y"); float movY = config_setting_get_float(setting); - for (int i = 0; i < wc->layersCount; i++) - { + for (int i = 0; i < wc->layersCount; i++) { LayerConfig *lc = wc->layerConfigs + i; lc->sensitivityX = movX * i; diff --git a/src/common/monitorScanner.c b/src/common/monitorScanner.c index 78cd9e0..39882a7 100644 --- a/src/common/monitorScanner.c +++ b/src/common/monitorScanner.c @@ -13,60 +13,53 @@ #ifdef __WIN32 int monitorEnumIndex = 0; -BOOL monitorenumproc(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM param) -{ +static BOOL monitorenumproc(HMONITOR monitor, HDC hdc, LPRECT rect, + LPARAM param) { MONITORINFO info; info.cbSize = sizeof(MONITORINFO); GetMonitorInfo(monitor, &info); - Monitor *m = (Monitor *)param; - - snprintf( - m[monitorEnumIndex].name, - MONITOR_NAME_MAX, - "Monitor %d%s", - monitorEnumIndex + 1, - info.dwFlags == MONITORINFOF_PRIMARY ? " (main)" : "" - ); - m[monitorEnumIndex].bounds.x = info.rcWork.left; - m[monitorEnumIndex].bounds.y = info.rcWork.top; - m[monitorEnumIndex].bounds.w = info.rcWork.right - info.rcWork.left; - m[monitorEnumIndex].bounds.h = info.rcWork.bottom - info.rcWork.top; - m[monitorEnumIndex].config.loaded = 0; + MonitorInfo *mi = (MonitorInfo *)param + monitorEnumIndex; + + snprintf(mi->name, MONITOR_NAME_MAX, "Monitor %d%s", monitorEnumIndex + 1, + info.dwFlags == MONITORINFOF_PRIMARY ? " (main)" : ""); + mi->bounds.x = info.rcWork.left; + mi->bounds.y = info.rcWork.top; + mi->bounds.w = info.rcWork.right - info.rcWork.left; + mi->bounds.h = info.rcWork.bottom - info.rcWork.top; + mi->config.loaded = 0; monitorEnumIndex++; return TRUE; } #endif -MonitorInfo *scanMonitors(int *count) -{ - int monitorCount; +MonitorInfo *scanMonitors(int *count) { MonitorInfo *m = NULL; #ifdef __WIN32 - monitorCount = GetSystemMetrics(SM_CMONITORS); - m = malloc(sizeof(Monitor) * monitorCount); - monitorEnumIndex = 0; + *count = GetSystemMetrics(SM_CMONITORS); + m = malloc(sizeof(MonitorInfo) * (*count)); + EnumDisplayMonitors(NULL, NULL, &monitorenumproc, (LPARAM)m); #else - Display *display = XOpenDisplay((getenv("DISPLAY"))); - Window wnd = DefaultRootWindow(display); - XRRMonitorInfo *info = XRRGetMonitors(display, wnd, 0, &monitorCount); + Display *display = XOpenDisplay((getenv("DISPLAY"))); + Window wnd = DefaultRootWindow(display); + XRRMonitorInfo *info = XRRGetMonitors(display, wnd, 0, &monitorCount); m = malloc(sizeof(MonitorInfo) * monitorCount); int i = 0; - while (i < monitorCount) - { - snprintf(m[i].name, MONITOR_NAME_MAX, "%s", XGetAtomName(display, info->name)); - m[i].bounds.x = info->x; - m[i].bounds.y = info->y; - m[i].bounds.w = info->width; - m[i].bounds.h = info->height; + while (i < monitorCount) { + snprintf(m[i].name, MONITOR_NAME_MAX, "%s", + XGetAtomName(display, info->name)); + m[i].bounds.x = info->x; + m[i].bounds.y = info->y; + m[i].bounds.w = info->width; + m[i].bounds.h = info->height; m[i].config.loaded = 0; info++; diff --git a/src/common/wallpaperScanner.c b/src/common/wallpaperScanner.c index 9da8502..981b128 100644 --- a/src/common/wallpaperScanner.c +++ b/src/common/wallpaperScanner.c @@ -3,44 +3,75 @@ #include "../common.h" -#define SYSTEM_WALLPAPERS_PATH "/usr/local/share/lwp/wallpapers" +#define WALLPAPERS_SYSTEM 0 +#define WALLPAPERS_USER 1 -WallpaperInfo *scanWallpapers(int *count) -{ +static void getWallpapersDirPath(char *path, int type) { +#ifdef __WIN32 + if (type == WALLPAPERS_USER) + sprintf(path, "%s\\lwp\\wallpapers", g_get_user_data_dir()); + else { + char currentDirPath[PATH_MAX]; + GetModuleFileNameA(NULL, currentDirPath, PATH_MAX); + char *ptr = currentDirPath+strlen(currentDirPath)-1; + while(*ptr != '\\') + ptr--; + *ptr = '\0'; + sprintf(path, "%s\\wallpapers", currentDirPath); + } +#else + if (type == WALLPAPERS_USER) + sprintf(path, "%s/.config/lwp/wallpapers", g_get_home_dir()); + else + sprintf(path, "/usr/local/share/lwp/wallpapers"); +#endif +} + +WallpaperInfo *scanWallpapers(int *count) { *count = 0; char userWlpPath[PATH_MAX]; + getWallpapersDirPath(userWlpPath, WALLPAPERS_USER); + char systemWlpPath[PATH_MAX]; + getWallpapersDirPath(systemWlpPath, WALLPAPERS_SYSTEM); - sprintf(userWlpPath, "%s/.config/lwp/wallpapers", g_get_home_dir()); - - GDir *dir; - GError *error; + GDir *dir; + GError *error; const gchar *filename; - dir = g_dir_open(SYSTEM_WALLPAPERS_PATH, 0, &error); - while ((g_dir_read_name(dir))) (*count)++; + dir = g_dir_open(systemWlpPath, 0, &error); + while ((g_dir_read_name(dir))) + (*count)++; dir = g_dir_open(userWlpPath, 0, &error); - while ((g_dir_read_name(dir))) (*count)++; + while ((g_dir_read_name(dir))) + (*count)++; WallpaperInfo *list = malloc(sizeof(WallpaperInfo) * (*count)); - WallpaperInfo *ptr = list; + WallpaperInfo *ptr = list; - dir = g_dir_open(SYSTEM_WALLPAPERS_PATH, 0, &error); - while ((filename = g_dir_read_name(dir))) - { + dir = g_dir_open(systemWlpPath, 0, &error); + while ((filename = g_dir_read_name(dir))) { sprintf(ptr->name, "%s", filename); - sprintf(ptr->dirPath, "%s/%s", SYSTEM_WALLPAPERS_PATH, filename); - ptr->isDefault = 1; +#ifdef __WIN32 + sprintf(ptr->dirPath, "%s\\%s", systemWlpPath, filename); + +#else + sprintf(ptr->dirPath, "%s/%s", systemWlpPath, filename); +#endif + ptr->isDefault = 1; ptr->config.loaded = 0; ptr++; } dir = g_dir_open(userWlpPath, 0, &error); - while ((filename = g_dir_read_name(dir))) - { + while ((filename = g_dir_read_name(dir))) { sprintf(ptr->name, "%s", filename); - sprintf(ptr->dirPath, "%s/%s/%s", g_get_home_dir(), ".config/lwp/wallpapers", filename); - ptr->isDefault = 0; +#ifdef __WIN32 + sprintf(ptr->dirPath, "%s\\%s", userWlpPath, filename); +#else + sprintf(ptr->dirPath, "%s/%s", userWlpPath, filename); +#endif + ptr->isDefault = 0; ptr->config.loaded = 0; ptr++; } diff --git a/src/core/main.c b/src/core/main.c index e8fdcf6..2ca6294 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -47,6 +47,7 @@ static void reloadMonitorListBox() for (int i = 0; i < monitorsCount; i++) { + printf("%s\n", monitors[i].name); GtkWidget *label = gtk_label_new(monitors[i].name); GtkWidget *row = gtk_list_box_row_new(); gtk_container_add(GTK_CONTAINER(row), label); diff --git a/src/wlp/debug.c b/src/wlp/debug.c index 5081129..d8b15c3 100644 --- a/src/wlp/debug.c +++ b/src/wlp/debug.c @@ -1,13 +1,26 @@ #include #include +#include #include "main.h" +void getLogFilePath(char *path) +{ + #ifdef __WIN32 + sprintf(path, "%s\\lwp\\lwp.log", g_get_user_data_dir()); + #else + sprintf(path, "/home/cziken/.config/lwp/log"); + #endif +} + void lwpLog(int type, const char *str, ...) { - char *typePrefix; + char path[PATH_MAX]; + getLogFilePath(path); + + FILE *file = fopen(path, "a"); - FILE *file = fopen("/home/cziken/.config/lwp/log", "a"); + char *typePrefix; switch (type) { diff --git a/src/wlp/main.c b/src/wlp/main.c index c0a6b18..a3c399a 100644 --- a/src/wlp/main.c +++ b/src/wlp/main.c @@ -2,55 +2,6 @@ #include "../platform_guard.h" -#ifdef __WIN32 -static void initCmd() -{ - // Create console - AllocConsole(); - AttachConsole(ATTACH_PARENT_PROCESS); - freopen("CONOUT$", "w", stdout); - HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD dwMode = 0; - GetConsoleMode(hOut, &dwMode); - SetConsoleMode(hOut, dwMode | 0x0004); - - // Remove closing button (because closing it closes the entire app) - HWND hwnd = GetConsoleWindow(); - HMENU hMenu = GetSystemMenu(hwnd, FALSE); - DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND); - - // Set console title - SetConsoleTitle("Layered WallPaper"); -} -BOOLmonitorenumproc(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM param) -{ - int px = GetSystemMetrics(SM_XVIRTUALSCREEN); - int py = GetSystemMetrics(SM_YVIRTUALSCREEN); - - MONITORINFO monitorInfo; - monitorInfo.cbSize = sizeof(MONITORINFO); - - GetMonitorInfo(monitor, &monitorInfo); - lwpLog( - LOG_INFO, - " Monitor: position %ldx%ld, size %ldx%ld %s", - monitorInfo.rcMonitor.left - px, - monitorInfo.rcMonitor.top - py, - monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left, - monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top, - (monitorInfo.dwFlags & MONITORINFOF_PRIMARY) == MONITORINFOF_PRIMARY ? "primary" : "" - ); - - return TRUE; -} -/* -static void scanMonitors() -{ - lwpLog(LOG_INFO, "Scanning monitors..."); - EnumDisplayMonitors(NULL, NULL, &monitorenumproc, NULL); -}*/ -#endif - static App app; static void atExit() @@ -223,13 +174,6 @@ int main(int argc, char *argv[]) lwpLog(LOG_INFO, "Starting Layered WallPaper"); -#ifdef __WIN32 - if (argc == 2 && strcmp(argv[1], "/console") == 0) initCmd(); - initTrayIcon(); - - // scanMonitors(); -#endif - lwpLog(LOG_INFO, "Loading app config"); loadAppConfig(&app.config); @@ -247,9 +191,5 @@ int main(int argc, char *argv[]) lwpLog(LOG_INFO, "Starting wallpaper loop"); runWallpaperLoop(&app); -#ifdef __WIN32 - removeTrayIcon(); -#endif - return 0; } diff --git a/src/wlp/render.c b/src/wlp/render.c index 95e27e2..baec2c4 100644 --- a/src/wlp/render.c +++ b/src/wlp/render.c @@ -159,8 +159,5 @@ void runWallpaperLoop(App *app) } SDL_RenderPresent(app->renderer); SDL_Delay(1000 / app->config.targetFps); -#ifdef __WIN32 - if (!updateTrayIcon()) quit = 1; -#endif } } diff --git a/src/wlp/window.c b/src/wlp/window.c index 10a15af..659d539 100644 --- a/src/wlp/window.c +++ b/src/wlp/window.c @@ -3,11 +3,7 @@ #ifdef __WIN32 #include #include -#ifdef _MSC_VER -#include -#else #include -#endif // _MSC_VER #endif // __WIN32 #ifdef __LINUX @@ -35,7 +31,7 @@ static BOOL CALLBACK getIconWorkerw(HWND hWnd, LPARAM lParam) } return TRUE; } - +/* #define WM_TRAY_ICON (WM_USER + 1) static NOTIFYICONDATA nid; @@ -131,41 +127,7 @@ void initTrayIcon() nid.uFlags = NIF_ICON | NIF_MESSAGE; Shell_NotifyIcon(NIM_ADD, &nid); -} - -void initWindow(Config *cfg) -{ - cfg->window = - SDL_CreateWindow("Parallax wallpaper", 0, 0, 0, 0, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); - if (cfg->window == NULL) lwpLog(LOG_ERROR, "%s", SDL_GetError()); - - SDL_SysWMinfo sysWmInfo; - SDL_VERSION(&sysWmInfo.version) - SDL_GetWindowWMInfo(cfg->window, &sysWmInfo); - HWND hWindow = sysWmInfo.info.win.window; - - HWND progman = FindWindow("Progman", NULL); - iconWorkerw = progman; - SendMessageTimeout(progman, 0x052C, NULL, NULL, SMTO_NORMAL, 1000, NULL); - if (!FindWindowEx(progman, NULL, "SHELLDLL_DefView", NULL)) EnumWindows(getIconWorkerw, NULL); - - HWND wallpaperWorkerw = GetWindow(iconWorkerw, GW_HWNDNEXT); - SetParent(hWindow, wallpaperWorkerw); - SetWindowLongPtr( - hWindow, GWL_EXSTYLE, WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | WS_EX_NOACTIVATE - ); - SetWindowLongPtr(hWindow, GWL_STYLE, WS_CHILDWINDOW | WS_VISIBLE); - - SetWindowPos( - hWindow, - NULL, - 0, - 0, - GetSystemMetrics(SM_CXVIRTUALSCREEN), - GetSystemMetrics(SM_CYVIRTUALSCREEN), - SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW - ); -} +}*/ #elif __DARWIN @@ -222,6 +184,43 @@ void initWindow(Config *cfg) void initWindow(App *app) { + #ifdef __WIN32 + + app->window = + SDL_CreateWindow("Parallax wallpaper", 0, 0, 0, 0, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); + if (app->window == NULL) lwpLog(LOG_ERROR, "%s", SDL_GetError()); + + SDL_SysWMinfo sysWmInfo; + SDL_VERSION(&sysWmInfo.version) + SDL_GetWindowWMInfo(app->window, &sysWmInfo); + HWND hWindow = sysWmInfo.info.win.window; + + HWND progman = FindWindow("Progman", NULL); + iconWorkerw = progman; + SendMessageTimeout(progman, 0x052C, NULL, NULL, SMTO_NORMAL, 1000, NULL); + if (!FindWindowEx(progman, NULL, "SHELLDLL_DefView", NULL)) EnumWindows(getIconWorkerw, NULL); + + HWND wallpaperWorkerw = GetWindow(iconWorkerw, GW_HWNDNEXT); + SetParent(hWindow, wallpaperWorkerw); + SetWindowLongPtr( + hWindow, GWL_EXSTYLE, WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | WS_EX_NOACTIVATE + ); + SetWindowLongPtr(hWindow, GWL_STYLE, WS_CHILDWINDOW | WS_VISIBLE); + + SetWindowPos( + hWindow, + NULL, + 0, + 0, + GetSystemMetrics(SM_CXVIRTUALSCREEN), + GetSystemMetrics(SM_CYVIRTUALSCREEN), + SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW + ); + + #elif __DARWIN + + #else + if (!app->config.drawOnRootWindow) { Display *display = XOpenDisplay(NULL); @@ -270,6 +269,8 @@ void initWindow(App *app) XCloseDisplay(display); } + #endif + if (app->window == NULL) lwpLog(LOG_ERROR, "Failed to initialize window: %s", SDL_GetError()); app->renderer = From 71d9672cf072209b608cb9acb7b876c05be39e76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Szczerbi=C5=84ski?= Date: Thu, 8 Feb 2024 13:08:00 +0100 Subject: [PATCH 20/33] Tray icon --- src/core/CMakeLists.txt | 7 +- src/core/main.c | 5 +- src/core/main.h | 5 + src/core/trayIcon.c | 63 ++++++++++ src/wlp/CMakeLists.txt | 6 +- src/wlp/window.c | 261 +++++++++++----------------------------- 6 files changed, 144 insertions(+), 203 deletions(-) create mode 100644 src/core/trayIcon.c diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index aa7cf33..cc0a414 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -2,6 +2,7 @@ set(_SOURCE_FILES main.c windowHandlers.c wlp.c + trayIcon.c ../common/monitorScanner.c ../common/config.c ../common/wallpaperScanner.c @@ -47,14 +48,12 @@ list(APPEND _LIBS ${libconfig_LINK_LIBRARIES}) # Main executable if (_UNAME STREQUAL "DARWIN") add_executable(lwp MACOSX_BUNDLE ${_SOURCE_FILES}) +elseif(_UNAME STREQUAL "WIN32") + add_executable(lwp WIN32 ${_SOURCE_FILES}) else() add_executable(lwp ${_SOURCE_FILES}) endif() -if(MSVC) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS") -endif() - set_target_properties(lwp PROPERTIES ENABLE_EXPORTS on) target_compile_definitions(lwp PUBLIC __${_UNAME} PROGRAM_VERSION="${PROGRAM_VERSION}") diff --git a/src/core/main.c b/src/core/main.c index 2ca6294..8514ce4 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -47,7 +47,6 @@ static void reloadMonitorListBox() for (int i = 0; i < monitorsCount; i++) { - printf("%s\n", monitors[i].name); GtkWidget *label = gtk_label_new(monitors[i].name); GtkWidget *row = gtk_list_box_row_new(); gtk_container_add(GTK_CONTAINER(row), label); @@ -104,10 +103,14 @@ int main(int argc, char *argv[]) { int status; + initTrayIcon(); + app = gtk_application_new("com.github.jszczerbinsky.lwp", G_APPLICATION_DEFAULT_FLAGS); g_signal_connect(app, "activate", G_CALLBACK(activate), NULL); status = g_application_run(G_APPLICATION(app), argc, argv); g_object_unref(app); + removeTrayIcon(); + return status; } diff --git a/src/core/main.h b/src/core/main.h index add9ac0..7b17497 100644 --- a/src/core/main.h +++ b/src/core/main.h @@ -23,4 +23,9 @@ extern GtkWidget *monitorNameLabel; void runWlp(); void killWlp(); +#ifdef __WIN32 +void initTrayIcon(); +void removeTrayIcon(); +#endif + #endif diff --git a/src/core/trayIcon.c b/src/core/trayIcon.c new file mode 100644 index 0000000..1b17ac0 --- /dev/null +++ b/src/core/trayIcon.c @@ -0,0 +1,63 @@ +#ifdef __WIN32 + +#include "main.h" + +#include +#include +#include + + +#define WM_TRAY_ICON (WM_USER + 1) + +static NOTIFYICONDATA nid; + +void removeTrayIcon() { Shell_NotifyIcon(NIM_DELETE, &nid); } + +int updateTrayIcon() { + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return 1; +} + +static LRESULT CALLBACK wndProc(HWND hwnd, UINT uMsg, WPARAM wParam, + LPARAM lParam) { + switch (uMsg) { + case WM_TRAY_ICON: + if (lParam == WM_RBUTTONDOWN || lParam == WM_LBUTTONDOWN) { + gtk_widget_set_visible(mainWnd, 1); + } + break; + } +} + +void initTrayIcon() { + // Create an invisible window to process tray icon events + + HINSTANCE hInstance = GetModuleHandle(NULL); + const TCHAR CLASS_NAME[] = TEXT("Hidden Window"); + WNDCLASS wc; + memset(&wc, 0, sizeof(WNDCLASS)); + wc.lpfnWndProc = wndProc; + wc.hInstance = hInstance; + wc.lpszClassName = CLASS_NAME; + RegisterClass(&wc); + HWND hWnd = CreateWindowEx(0, CLASS_NAME, TEXT(""), WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, NULL, NULL, hInstance, NULL); + + // Create tray icon + + nid.cbSize = sizeof(NOTIFYICONDATA); + nid.hWnd = hWnd; + nid.uCallbackMessage = WM_TRAY_ICON; + nid.hIcon = LoadIcon(hInstance, "ID"); + nid.uFlags = NIF_ICON | NIF_MESSAGE; + + Shell_NotifyIcon(NIM_ADD, &nid); +} + +#endif \ No newline at end of file diff --git a/src/wlp/CMakeLists.txt b/src/wlp/CMakeLists.txt index 9dfcd42..6e03f2d 100644 --- a/src/wlp/CMakeLists.txt +++ b/src/wlp/CMakeLists.txt @@ -56,6 +56,8 @@ option(LWP_INSTALL_LAUNCHD "Launch lwp on login (MacOSX only)" OFF) # Main executable if (_UNAME STREQUAL "DARWIN") add_executable(lwpwlp MACOSX_BUNDLE ${_SOURCE_FILES}) +elseif(_UNAME STREQUAL "WIN32") + add_executable(lwpwlp WIN32 ${_SOURCE_FILES}) else() add_executable(lwpwlp ${_SOURCE_FILES}) endif() @@ -65,10 +67,6 @@ if(_UNAME STREQUAL "WIN32") set_property(TARGET lwpwlp PROPERTY VS_DPI_AWARE "PerMonitor") endif() -if(MSVC) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS") -endif() - target_compile_definitions(lwpwlp PUBLIC __${_UNAME}) target_include_directories(lwpwlp PUBLIC ${_INCLUDE_DIRS}) target_link_libraries(lwpwlp PRIVATE ${_LIBS}) diff --git a/src/wlp/window.c b/src/wlp/window.c index 659d539..96b684d 100644 --- a/src/wlp/window.c +++ b/src/wlp/window.c @@ -1,194 +1,86 @@ #include "main.h" #ifdef __WIN32 -#include -#include #include -#endif // __WIN32 +#endif // __WIN32 #ifdef __LINUX #include #include #include -#endif //__LINUX +#endif //__LINUX #ifdef __WIN32 -static HWND iconWorkerw; -static BOOL CALLBACK getIconWorkerw(HWND hWnd, LPARAM lParam) -{ +static HWND iconWorkerw; +static BOOL CALLBACK getIconWorkerw(HWND hWnd, LPARAM lParam) { char buff[10]; GetClassName(hWnd, buff, 10); - if (strcmp(buff, "WorkerW") == 0) - { + if (strcmp(buff, "WorkerW") == 0) { HWND defView = FindWindowEx(hWnd, NULL, "SHELLDLL_DefView", NULL); - if (defView) - { + if (defView) { iconWorkerw = hWnd; return FALSE; } } return TRUE; } -/* -#define WM_TRAY_ICON (WM_USER + 1) - -static NOTIFYICONDATA nid; -static int quit = 0; - -void removeTrayIcon() { Shell_NotifyIcon(NIM_DELETE, &nid); } - -int updateTrayIcon() -{ - MSG msg; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - return !quit; -} - -static LRESULT CALLBACK wndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) - { - case WM_TRAY_ICON: - if (lParam == WM_RBUTTONDOWN || lParam == WM_LBUTTONDOWN) - { - int res = MessageBox( - NULL, - "Do You want to run Layered WallPaper with debug console?", - "Restart Layered WallPaper", - MB_YESNOCANCEL | MB_ICONQUESTION - ); - - TCHAR processParam = NULL; - - if (res != IDCANCEL) - { - TCHAR fileName[MAX_PATH]; - GetModuleFileName(NULL, fileName, MAX_PATH); - - TCHAR cmd[MAX_PATH + 10]; - _tcscpy(cmd, fileName); - - if (res == IDYES) _tcscat(cmd, " /console"); - - STARTUPINFO si; - memset(&si, 0, sizeof(STARTUPINFO)); - - PROCESS_INFORMATION pi; - - CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); - - quit = 1; - } - } - break; - } -} - -void initTrayIcon() -{ - // Create an invisible window to process tray icon events - - HINSTANCE hInstance = GetModuleHandle(NULL); - const wchar_t CLASS_NAME[] = L"Hidden Window"; - WNDCLASS wc; - memset(&wc, 0, sizeof(WNDCLASS)); - wc.lpfnWndProc = wndProc; - wc.hInstance = hInstance; - wc.lpszClassName = CLASS_NAME; - RegisterClass(&wc); - HWND hWnd = CreateWindowEx( - 0, - CLASS_NAME, - L"", - WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, - CW_USEDEFAULT, - CW_USEDEFAULT, - CW_USEDEFAULT, - NULL, - NULL, - hInstance, - NULL - ); - - // Create tray icon - - nid.cbSize = sizeof(NOTIFYICONDATA); - nid.hWnd = hWnd; - nid.uCallbackMessage = WM_TRAY_ICON; - nid.hIcon = LoadIcon(hInstance, "ID"); - nid.uFlags = NIF_ICON | NIF_MESSAGE; - - Shell_NotifyIcon(NIM_ADD, &nid); -}*/ #elif __DARWIN // Helper macros for objc runtime interaction -#define OBJC_MSG_INT ((id(*)(id, SEL, int))objc_msgSend) -#define OBJC_MSG_ID ((id(*)(id, SEL, id))objc_msgSend) -#define OBJC_MSG_PTR ((id(*)(id, SEL, void *))objc_msgSend) -#define OBJC_MSG_CLS ((id(*)(Class, SEL))objc_msgSend) +#define OBJC_MSG_INT ((id(*)(id, SEL, int))objc_msgSend) +#define OBJC_MSG_ID ((id(*)(id, SEL, id))objc_msgSend) +#define OBJC_MSG_PTR ((id(*)(id, SEL, void *))objc_msgSend) +#define OBJC_MSG_CLS ((id(*)(Class, SEL))objc_msgSend) #define OBJC_MSG_CLS_CHR ((id(*)(Class, SEL, char *))objc_msgSend) -void initWindow(Config *cfg) -{ +void initWindow(Config *cfg) { // Get main display size const CGDirectDisplayID displayID = CGMainDisplayID(); - const size_t w = CGDisplayPixelsWide(displayID); - const size_t h = CGDisplayPixelsHigh(displayID); - const struct CGRect frameRect = {0, 0, w, h}; + const size_t w = CGDisplayPixelsWide(displayID); + const size_t h = CGDisplayPixelsHigh(displayID); + const struct CGRect frameRect = {0, 0, w, h}; // Get shared NScfglication instance - const id ns_cfg = OBJC_MSG_CLS(objc_getClass("NScfglication"), sel_getUid("sharedcfglication")); - OBJC_MSG_INT( - ns_cfg, - sel_getUid("setActivationPolicy:"), - 0 - ); // NScfglicationActivationPolicyRegular + const id ns_cfg = OBJC_MSG_CLS(objc_getClass("NScfglication"), + sel_getUid("sharedcfglication")); + OBJC_MSG_INT(ns_cfg, sel_getUid("setActivationPolicy:"), + 0); // NScfglicationActivationPolicyRegular // Create NSWindow - const id window = ((id(*)(id, SEL, struct CGRect, int, int, int))objc_msgSend)( - OBJC_MSG_CLS(objc_getClass("NSWindow"), sel_getUid("alloc")), - sel_getUid("initWithContentRect:styleMask:backing:defer:"), - frameRect, - 0, // NSWindowStyleMaskBorderless - 2, // NSBackingStoreBuffered - false - ); + const id window = + ((id(*)(id, SEL, struct CGRect, int, int, int))objc_msgSend)( + OBJC_MSG_CLS(objc_getClass("NSWindow"), sel_getUid("alloc")), + sel_getUid("initWithContentRect:styleMask:backing:defer:"), frameRect, + 0, // NSWindowStyleMaskBorderless + 2, // NSBackingStoreBuffered + false); // Set window attributes - OBJC_MSG_ID( - window, - sel_getUid("setTitle:"), - OBJC_MSG_CLS_CHR( - objc_getClass("NSString"), sel_getUid("stringWithUTF8String:"), "Parallax wallpaper" - ) - ); + OBJC_MSG_ID(window, sel_getUid("setTitle:"), + OBJC_MSG_CLS_CHR(objc_getClass("NSString"), + sel_getUid("stringWithUTF8String:"), + "Parallax wallpaper")); OBJC_MSG_PTR(window, sel_getUid("makeKeyAndOrderFront:"), nil); OBJC_MSG_INT(window, sel_getUid("setLevel:"), kCGDesktopWindowLevel - 1); OBJC_MSG_INT(ns_cfg, sel_getUid("activateIgnoringOthercfgs:"), true); // Create SDL window from NSWindow cfg->window = SDL_CreateWindowFrom((void *)window); - if (cfg->window == NULL) lwpLog(LOG_ERROR, "%s", SDL_GetError()); + if (cfg->window == NULL) + lwpLog(LOG_ERROR, "%s", SDL_GetError()); } #endif -void initWindow(App *app) -{ - #ifdef __WIN32 +void initWindow(App *app) { +#ifdef __WIN32 - app->window = - SDL_CreateWindow("Parallax wallpaper", 0, 0, 0, 0, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); - if (app->window == NULL) lwpLog(LOG_ERROR, "%s", SDL_GetError()); + app->window = SDL_CreateWindow("Parallax wallpaper", 0, 0, 0, 0, + SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); + if (app->window == NULL) + lwpLog(LOG_ERROR, "%s", SDL_GetError()); SDL_SysWMinfo sysWmInfo; SDL_VERSION(&sysWmInfo.version) @@ -196,84 +88,65 @@ void initWindow(App *app) HWND hWindow = sysWmInfo.info.win.window; HWND progman = FindWindow("Progman", NULL); - iconWorkerw = progman; + iconWorkerw = progman; SendMessageTimeout(progman, 0x052C, NULL, NULL, SMTO_NORMAL, 1000, NULL); - if (!FindWindowEx(progman, NULL, "SHELLDLL_DefView", NULL)) EnumWindows(getIconWorkerw, NULL); + if (!FindWindowEx(progman, NULL, "SHELLDLL_DefView", NULL)) + EnumWindows(getIconWorkerw, NULL); HWND wallpaperWorkerw = GetWindow(iconWorkerw, GW_HWNDNEXT); SetParent(hWindow, wallpaperWorkerw); - SetWindowLongPtr( - hWindow, GWL_EXSTYLE, WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | WS_EX_NOACTIVATE - ); + SetWindowLongPtr(hWindow, GWL_EXSTYLE, + WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | + WS_EX_NOACTIVATE); SetWindowLongPtr(hWindow, GWL_STYLE, WS_CHILDWINDOW | WS_VISIBLE); - SetWindowPos( - hWindow, - NULL, - 0, - 0, - GetSystemMetrics(SM_CXVIRTUALSCREEN), - GetSystemMetrics(SM_CYVIRTUALSCREEN), - SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW - ); + SetWindowPos(hWindow, NULL, 0, 0, GetSystemMetrics(SM_CXVIRTUALSCREEN), + GetSystemMetrics(SM_CYVIRTUALSCREEN), + SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW); - #elif __DARWIN +#elif __DARWIN - #else +#else - if (!app->config.drawOnRootWindow) - { + if (!app->config.drawOnRootWindow) { Display *display = XOpenDisplay(NULL); XCloseDisplay(display); - app->window = SDL_CreateWindow( - "Layered WallPaper", - 0, - 0, - DisplayWidth(display, 0), - DisplayHeight(display, 0), - SDL_WINDOW_OPENGL - ); + app->window = + SDL_CreateWindow("Layered WallPaper", 0, 0, DisplayWidth(display, 0), + DisplayHeight(display, 0), SDL_WINDOW_OPENGL); SDL_SysWMinfo wmInfo; SDL_GetVersion(&wmInfo.version); SDL_GetWindowWMInfo(app->window, &wmInfo); Window xWnd = wmInfo.info.x11.window; - display = wmInfo.info.x11.display; + display = wmInfo.info.x11.display; - Atom atomType = XInternAtom(display, "_NET_WM_WINDOW_TYPE", 0); + Atom atomType = XInternAtom(display, "_NET_WM_WINDOW_TYPE", 0); Atom atomDesktop = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DESKTOP", 0); - XChangeProperty( - display, - xWnd, - atomType, - XA_ATOM, - 32, - PropModeReplace, - (const unsigned char *)&atomDesktop, - 1 - ); + XChangeProperty(display, xWnd, atomType, XA_ATOM, 32, PropModeReplace, + (const unsigned char *)&atomDesktop, 1); Window rootWindow = RootWindow(display, DefaultScreen(display)); XReparentWindow(display, xWnd, rootWindow, 0, 0); XSync(display, 0); - } - else - { - Display *display = XOpenDisplay(NULL); - Window rootWindow = RootWindow(display, DefaultScreen(display)); - app->window = SDL_CreateWindowFrom((void *)rootWindow); + } else { + Display *display = XOpenDisplay(NULL); + Window rootWindow = RootWindow(display, DefaultScreen(display)); + app->window = SDL_CreateWindowFrom((void *)rootWindow); XCloseDisplay(display); } - #endif +#endif - if (app->window == NULL) lwpLog(LOG_ERROR, "Failed to initialize window: %s", SDL_GetError()); + if (app->window == NULL) + lwpLog(LOG_ERROR, "Failed to initialize window: %s", SDL_GetError()); - app->renderer = - SDL_CreateRenderer(app->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); - if (app->renderer == NULL) lwpLog(LOG_ERROR, "Failed to initialize renderer: %s", SDL_GetError()); + app->renderer = SDL_CreateRenderer( + app->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); + if (app->renderer == NULL) + lwpLog(LOG_ERROR, "Failed to initialize renderer: %s", SDL_GetError()); } From c08bac599a0c249dcb808660986afc52bdc0893c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Szczerbi=C5=84ski?= Date: Fri, 16 Feb 2024 21:06:30 +0100 Subject: [PATCH 21/33] Tray icon --- src/core/trayIcon.c | 124 ++++++++++++++++++++++---------------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/src/core/trayIcon.c b/src/core/trayIcon.c index 1b17ac0..52b9764 100644 --- a/src/core/trayIcon.c +++ b/src/core/trayIcon.c @@ -1,63 +1,63 @@ -#ifdef __WIN32 - -#include "main.h" - -#include -#include -#include - - -#define WM_TRAY_ICON (WM_USER + 1) - -static NOTIFYICONDATA nid; - -void removeTrayIcon() { Shell_NotifyIcon(NIM_DELETE, &nid); } - -int updateTrayIcon() { - MSG msg; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - - return 1; -} - -static LRESULT CALLBACK wndProc(HWND hwnd, UINT uMsg, WPARAM wParam, - LPARAM lParam) { - switch (uMsg) { - case WM_TRAY_ICON: - if (lParam == WM_RBUTTONDOWN || lParam == WM_LBUTTONDOWN) { - gtk_widget_set_visible(mainWnd, 1); - } - break; - } -} - -void initTrayIcon() { - // Create an invisible window to process tray icon events - - HINSTANCE hInstance = GetModuleHandle(NULL); - const TCHAR CLASS_NAME[] = TEXT("Hidden Window"); - WNDCLASS wc; - memset(&wc, 0, sizeof(WNDCLASS)); - wc.lpfnWndProc = wndProc; - wc.hInstance = hInstance; - wc.lpszClassName = CLASS_NAME; - RegisterClass(&wc); - HWND hWnd = CreateWindowEx(0, CLASS_NAME, TEXT(""), WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - CW_USEDEFAULT, NULL, NULL, hInstance, NULL); - - // Create tray icon - - nid.cbSize = sizeof(NOTIFYICONDATA); - nid.hWnd = hWnd; - nid.uCallbackMessage = WM_TRAY_ICON; - nid.hIcon = LoadIcon(hInstance, "ID"); - nid.uFlags = NIF_ICON | NIF_MESSAGE; - - Shell_NotifyIcon(NIM_ADD, &nid); -} - +#ifdef __WIN32 + +#include "main.h" + +#include +#include +#include + + +#define WM_TRAY_ICON (WM_USER + 1) + +static NOTIFYICONDATA nid; + +void removeTrayIcon() { Shell_NotifyIcon(NIM_DELETE, &nid); } + +int updateTrayIcon() { + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return 1; +} + +static LRESULT CALLBACK wndProc(HWND hwnd, UINT uMsg, WPARAM wParam, + LPARAM lParam) { + switch (uMsg) { + case WM_TRAY_ICON: + if (lParam == WM_RBUTTONDOWN || lParam == WM_LBUTTONDOWN) { + gtk_widget_set_visible(mainWnd, 1); + } + break; + } +} + +void initTrayIcon() { + // Create an invisible window to process tray icon events + + HINSTANCE hInstance = GetModuleHandle(NULL); + const TCHAR CLASS_NAME[] = TEXT("Hidden Window"); + WNDCLASS wc; + memset(&wc, 0, sizeof(WNDCLASS)); + wc.lpfnWndProc = wndProc; + wc.hInstance = hInstance; + wc.lpszClassName = CLASS_NAME; + RegisterClass(&wc); + HWND hWnd = CreateWindowEx(0, CLASS_NAME, TEXT(""), WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, NULL, NULL, hInstance, NULL); + + // Create tray icon + + nid.cbSize = sizeof(NOTIFYICONDATA); + nid.hWnd = hWnd; + nid.uCallbackMessage = WM_TRAY_ICON; + nid.hIcon = LoadIcon(hInstance, "ID"); + nid.uFlags = NIF_ICON | NIF_MESSAGE; + + Shell_NotifyIcon(NIM_ADD, &nid); +} + #endif \ No newline at end of file From 3fc4a7ae99942959856c911bd7b4e0ea23cc3dcf Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Fri, 16 Feb 2024 22:37:00 +0000 Subject: [PATCH 22/33] Finding paths to different program components --- src/common.h | 32 ++++++++++- src/common/config.c | 97 ++++++++++++++++++---------------- src/common/monitorScanner.c | 50 ++++++++++-------- src/common/paths.c | 99 +++++++++++++++++++++++++++++++++++ src/common/wallpaperScanner.c | 58 ++++++++------------ src/core/CMakeLists.txt | 9 ++-- src/core/main.c | 15 +++++- src/wlp/CMakeLists.txt | 3 +- src/wlp/debug.c | 13 +---- 9 files changed, 257 insertions(+), 119 deletions(-) create mode 100644 src/common/paths.c diff --git a/src/common.h b/src/common.h index 9314584..8e8d092 100644 --- a/src/common.h +++ b/src/common.h @@ -12,6 +12,8 @@ #define MONITOR_NAME_MAX 100 #define WALLPAPER_NAME_MAX 100 +#define DEFAULT_LINUX_PREFIX "/usr/local" + typedef struct { int x; @@ -63,9 +65,37 @@ typedef struct int targetFps; } AppConfig; -MonitorInfo *scanMonitors(int *count); +// +// paths.c +// + +#define APP_DIR_BIN 0 +#define APP_DIR_SHARE 1 +#define APP_DIR_USER_SETTINGS 2 + +void getAppDir(char *buff, int type); + +void getMonitorCfgPath(char *buff, const char *name); +void getWlpCfgPath(char *buff, const char *dirPath); +void getAppCfgPath(char *buff); +void getLogPath(char *buff); + +// +// monitorScanner.c +// + +MonitorInfo *scanMonitors(int *count); + +// +// wallpaperScanner.c +// + WallpaperInfo *scanWallpapers(int *count); +// +// config.c +// + void saveMonitorConfig(const char *name, MonitorConfig *mc); int loadMonitorConfig(const char *name, MonitorConfig *mc); diff --git a/src/common/config.c b/src/common/config.c index 259af27..bfd2356 100644 --- a/src/common/config.c +++ b/src/common/config.c @@ -5,9 +5,9 @@ #include "../common.h" #define CONFIG_DEFAULT 0 -#define CONFIG_USER 1 +#define CONFIG_USER 1 -void getMonitorConfigPath(const char *name, char *path) { +/*void getMonitorConfigPath(const char *name, char *path) { #ifdef __WIN32 sprintf(path, "%s\\lwp\\monitors\\%s.cfg", g_get_user_data_dir(), name); #else @@ -15,11 +15,11 @@ void getMonitorConfigPath(const char *name, char *path) { #endif } -void getWallpaperConfigPath(const char *dirName, char *path, int type) { +void getWallpaperConfigPath(const char *dirPath, char *path, int type) { if (type == CONFIG_DEFAULT) - sprintf(path, "%s/wallpaper.cfg", dirName); + sprintf(path, "%s/wallpaper.cfg", dirPath); else - sprintf(path, "%s/wallpaper.cfg", dirName); + sprintf(path, "%s/wallpaper.cfg", dirPath); } void getAppConfigPath(char *path, int type) { @@ -34,9 +34,10 @@ void getAppConfigPath(char *path, int type) { else sprintf(path, "%s/.config/lwp/lwp.cfg", g_get_home_dir()); #endif -} +}*/ -static void generateEmptyMonitorConfig(MonitorConfig *mc) { +static void generateEmptyMonitorConfig(MonitorConfig *mc) +{ sprintf(mc->wlpName, ""); mc->wlpBounds.x = 0; mc->wlpBounds.y = 0; @@ -44,8 +45,9 @@ static void generateEmptyMonitorConfig(MonitorConfig *mc) { mc->wlpBounds.h = 1080; } -void saveMonitorConfig(const char *name, MonitorConfig *mc) { - config_t cfg; +void saveMonitorConfig(const char *name, MonitorConfig *mc) +{ + config_t cfg; config_setting_t *root, *setting; config_init(&cfg); @@ -64,9 +66,10 @@ void saveMonitorConfig(const char *name, MonitorConfig *mc) { config_setting_set_int(setting, mc->wlpBounds.h); char path[PATH_MAX]; - getMonitorConfigPath(name, path); + getMonitorCfgPath(path, name); - if (!config_write_file(&cfg, path)) { + if (!config_write_file(&cfg, path)) + { fprintf(stderr, "Error while writing file.\n"); config_destroy(&cfg); } @@ -74,36 +77,37 @@ void saveMonitorConfig(const char *name, MonitorConfig *mc) { config_destroy(&cfg); } -int loadMonitorConfig(const char *name, MonitorConfig *mc) { +int loadMonitorConfig(const char *name, MonitorConfig *mc) +{ mc->loaded = 0; - config_t cfg; + config_t cfg; config_setting_t *root, *setting; char path[PATH_MAX]; - getMonitorConfigPath(name, path); + getMonitorCfgPath(path, name); - if (!g_file_test(path, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS)) { + if (!g_file_test(path, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS)) + { generateEmptyMonitorConfig(mc); saveMonitorConfig(name, mc); return 1; } config_init(&cfg); - if (config_read_file(&cfg, path) == CONFIG_FALSE) - return 0; + if (config_read_file(&cfg, path) == CONFIG_FALSE) return 0; root = config_root_setting(&cfg); setting = config_setting_get_member(root, "wlpName"); strcpy(mc->wlpName, config_setting_get_string(setting)); - setting = config_setting_get_member(root, "x"); + setting = config_setting_get_member(root, "x"); mc->wlpBounds.x = config_setting_get_int(setting); - setting = config_setting_get_member(root, "y"); + setting = config_setting_get_member(root, "y"); mc->wlpBounds.y = config_setting_get_int(setting); - setting = config_setting_get_member(root, "w"); + setting = config_setting_get_member(root, "w"); mc->wlpBounds.w = config_setting_get_int(setting); - setting = config_setting_get_member(root, "h"); + setting = config_setting_get_member(root, "h"); mc->wlpBounds.h = config_setting_get_int(setting); config_destroy(&cfg); @@ -112,26 +116,27 @@ int loadMonitorConfig(const char *name, MonitorConfig *mc) { return 1; } -int loadAppConfig(AppConfig *ac) { - config_t cfg; +int loadAppConfig(AppConfig *ac) +{ + config_t cfg; config_setting_t *root, *setting; char path[PATH_MAX]; - getAppConfigPath(path, CONFIG_USER); + getAppCfgPath(path); config_init(&cfg); - if (config_read_file(&cfg, path) == CONFIG_FALSE) { - getAppConfigPath(path, CONFIG_DEFAULT); - if (config_read_file(&cfg, path) == CONFIG_FALSE) - return 0; + if (config_read_file(&cfg, path) == CONFIG_FALSE) + { + // todo: set default config values + return 0; } root = config_root_setting(&cfg); #ifdef __LINUX - setting = config_setting_get_member(root, "draw_on_rootwindow"); + setting = config_setting_get_member(root, "draw_on_rootwindow"); ac->drawOnRootWindow = config_setting_get_int(setting); #endif - setting = config_setting_get_member(root, "target_fps"); + setting = config_setting_get_member(root, "target_fps"); ac->targetFps = config_setting_get_int(setting); config_destroy(&cfg); @@ -139,39 +144,41 @@ int loadAppConfig(AppConfig *ac) { return 1; } -int loadWallpaperConfig(const char *dirName, WallpaperConfig *wc) { +int loadWallpaperConfig(const char *dirPath, WallpaperConfig *wc) +{ wc->loaded = 0; - config_t cfg; + config_t cfg; config_setting_t *root, *setting; config_init(&cfg); char path[PATH_MAX]; - getWallpaperConfigPath(dirName, path, CONFIG_USER); + getWlpCfgPath(path, dirPath); - if (config_read_file(&cfg, path) == CONFIG_FALSE) { - getWallpaperConfigPath(dirName, path, CONFIG_DEFAULT); - if (config_read_file(&cfg, path) == CONFIG_FALSE) - return 0; + if (config_read_file(&cfg, path) == CONFIG_FALSE) + { + getWlpCfgPath(path, dirPath); + if (config_read_file(&cfg, path) == CONFIG_FALSE) return 0; } root = config_root_setting(&cfg); - setting = config_setting_get_member(root, "count"); + setting = config_setting_get_member(root, "count"); wc->layersCount = config_setting_get_int(setting); - setting = config_setting_get_member(root, "repeat_x"); - wc->repeatX = config_setting_get_int(setting); - setting = config_setting_get_member(root, "repeat_y"); - wc->repeatY = config_setting_get_int(setting); + setting = config_setting_get_member(root, "repeat_x"); + wc->repeatX = config_setting_get_int(setting); + setting = config_setting_get_member(root, "repeat_y"); + wc->repeatY = config_setting_get_int(setting); wc->layerConfigs = malloc(wc->layersCount * sizeof(LayerConfig)); - setting = config_setting_get_member(root, "movement_x"); + setting = config_setting_get_member(root, "movement_x"); float movX = config_setting_get_float(setting); - setting = config_setting_get_member(root, "movement_y"); + setting = config_setting_get_member(root, "movement_y"); float movY = config_setting_get_float(setting); - for (int i = 0; i < wc->layersCount; i++) { + for (int i = 0; i < wc->layersCount; i++) + { LayerConfig *lc = wc->layerConfigs + i; lc->sensitivityX = movX * i; diff --git a/src/common/monitorScanner.c b/src/common/monitorScanner.c index 39882a7..dac0208 100644 --- a/src/common/monitorScanner.c +++ b/src/common/monitorScanner.c @@ -13,8 +13,8 @@ #ifdef __WIN32 int monitorEnumIndex = 0; -static BOOL monitorenumproc(HMONITOR monitor, HDC hdc, LPRECT rect, - LPARAM param) { +static BOOL monitorenumproc(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM param) +{ MONITORINFO info; info.cbSize = sizeof(MONITORINFO); @@ -22,12 +22,17 @@ static BOOL monitorenumproc(HMONITOR monitor, HDC hdc, LPRECT rect, MonitorInfo *mi = (MonitorInfo *)param + monitorEnumIndex; - snprintf(mi->name, MONITOR_NAME_MAX, "Monitor %d%s", monitorEnumIndex + 1, - info.dwFlags == MONITORINFOF_PRIMARY ? " (main)" : ""); - mi->bounds.x = info.rcWork.left; - mi->bounds.y = info.rcWork.top; - mi->bounds.w = info.rcWork.right - info.rcWork.left; - mi->bounds.h = info.rcWork.bottom - info.rcWork.top; + snprintf( + mi->name, + MONITOR_NAME_MAX, + "Monitor %d%s", + monitorEnumIndex + 1, + info.dwFlags == MONITORINFOF_PRIMARY ? " (main)" : "" + ); + mi->bounds.x = info.rcWork.left; + mi->bounds.y = info.rcWork.top; + mi->bounds.w = info.rcWork.right - info.rcWork.left; + mi->bounds.h = info.rcWork.bottom - info.rcWork.top; mi->config.loaded = 0; monitorEnumIndex++; @@ -35,31 +40,34 @@ static BOOL monitorenumproc(HMONITOR monitor, HDC hdc, LPRECT rect, } #endif -MonitorInfo *scanMonitors(int *count) { +MonitorInfo *scanMonitors(int *count) +{ MonitorInfo *m = NULL; #ifdef __WIN32 monitorEnumIndex = 0; - *count = GetSystemMetrics(SM_CMONITORS); - m = malloc(sizeof(MonitorInfo) * (*count)); + *count = GetSystemMetrics(SM_CMONITORS); + m = malloc(sizeof(MonitorInfo) * (*count)); EnumDisplayMonitors(NULL, NULL, &monitorenumproc, (LPARAM)m); #else - Display *display = XOpenDisplay((getenv("DISPLAY"))); - Window wnd = DefaultRootWindow(display); - XRRMonitorInfo *info = XRRGetMonitors(display, wnd, 0, &monitorCount); + int monitorCount; + + Display *display = XOpenDisplay((getenv("DISPLAY"))); + Window wnd = DefaultRootWindow(display); + XRRMonitorInfo *info = XRRGetMonitors(display, wnd, 0, &monitorCount); m = malloc(sizeof(MonitorInfo) * monitorCount); int i = 0; - while (i < monitorCount) { - snprintf(m[i].name, MONITOR_NAME_MAX, "%s", - XGetAtomName(display, info->name)); - m[i].bounds.x = info->x; - m[i].bounds.y = info->y; - m[i].bounds.w = info->width; - m[i].bounds.h = info->height; + while (i < monitorCount) + { + snprintf(m[i].name, MONITOR_NAME_MAX, "%s", XGetAtomName(display, info->name)); + m[i].bounds.x = info->x; + m[i].bounds.y = info->y; + m[i].bounds.w = info->width; + m[i].bounds.h = info->height; m[i].config.loaded = 0; info++; diff --git a/src/common/paths.c b/src/common/paths.c new file mode 100644 index 0000000..2f5c502 --- /dev/null +++ b/src/common/paths.c @@ -0,0 +1,99 @@ +#include +#include +#include +#include + +#include "../common.h" + +static void removeLastPathEntry(char *path) +{ + char *ptr = path + strlen(path) - 1; + while (*ptr != '\\') ptr--; + *ptr = '\0'; +} + +void getAppDir(char *buff, int type) +{ +#ifdef __LINUX + + char prefix[PATH_MAX]; + + char *prefixEnv = getenv("LWP_PREFIX"); + strcpy(prefix, prefixEnv ? prefixEnv : DEFAULT_LINUX_PREFIX); + +#endif + + if (type == APP_DIR_USER_SETTINGS) + { +#ifdef __LINUX + sprintf(buff, "%s/.config/lwp", g_get_home_dir()); +#elif __WIN32 + sprintf(buff, "%s\\lwp", g_get_user_data_dir()); +#endif + } + +#ifdef __LINUX + if (type == APP_DIR_BIN) + { + sprintf(buff, "%s/%s", prefix, "bin"); + } + else if (type == APP_DIR_SHARE) + { + sprintf(buff, "%s/%s", prefix, "share/lwp"); + } +#elif __WIN32 + GetModuleFileNameA(NULL, buff, PATH_MAX); + removeLastPathEntry(buff); +#elif __DARWIN +#endif +} + +void getMonitorCfgPath(char *buff, const char *name) +{ + getAppDir(buff, APP_DIR_USER_SETTINGS); + +#ifdef __WIN32 + const char *format = "%s\\monitors\\%s.cfg"; +#else + const char *format = "%s/monitors/%s.cfg"; +#endif + + sprintf(buff, format, buff, name); +} + +void getWlpCfgPath(char *buff, const char *dirPath) +{ +#ifdef __WIN32 + const char *format = "%s\\wallpaper.cfg"; +#else + const char *format = "%s/wallpaper.cfg"; +#endif + + sprintf(buff, format, dirPath); +} + +void getAppCfgPath(char *buff) +{ + getAppDir(buff, APP_DIR_USER_SETTINGS); + +#ifdef __WIN32 + const char *format = "%s\\lwp.cfg"; +#else + const char *format = "%s/lwp.cfg"; +#endif + + sprintf(buff, format, buff); +} + +void getLogPath(char *buff) +{ + getAppDir(buff, APP_DIR_USER_SETTINGS); + +#ifdef __WIN32 + const char *format = "%s\\log.txt"; +#else + const char *format = "%s/log"; +#endif + + sprintf(buff, format, buff); +} diff --git a/src/common/wallpaperScanner.c b/src/common/wallpaperScanner.c index 981b128..c45f60e 100644 --- a/src/common/wallpaperScanner.c +++ b/src/common/wallpaperScanner.c @@ -4,74 +4,62 @@ #include "../common.h" #define WALLPAPERS_SYSTEM 0 -#define WALLPAPERS_USER 1 +#define WALLPAPERS_USER 1 + +WallpaperInfo *scanWallpapers(int *count) +{ + *count = 0; -static void getWallpapersDirPath(char *path, int type) { #ifdef __WIN32 - if (type == WALLPAPERS_USER) - sprintf(path, "%s\\lwp\\wallpapers", g_get_user_data_dir()); - else { - char currentDirPath[PATH_MAX]; - GetModuleFileNameA(NULL, currentDirPath, PATH_MAX); - char *ptr = currentDirPath+strlen(currentDirPath)-1; - while(*ptr != '\\') - ptr--; - *ptr = '\0'; - sprintf(path, "%s\\wallpapers", currentDirPath); - } + const char *format = "%s\\%s"; #else - if (type == WALLPAPERS_USER) - sprintf(path, "%s/.config/lwp/wallpapers", g_get_home_dir()); - else - sprintf(path, "/usr/local/share/lwp/wallpapers"); + const char *format = "%s/%s"; #endif -} - -WallpaperInfo *scanWallpapers(int *count) { - *count = 0; char userWlpPath[PATH_MAX]; - getWallpapersDirPath(userWlpPath, WALLPAPERS_USER); + getAppDir(userWlpPath, APP_DIR_USER_SETTINGS); + sprintf(userWlpPath, format, userWlpPath, "wallpapers"); + char systemWlpPath[PATH_MAX]; - getWallpapersDirPath(systemWlpPath, WALLPAPERS_SYSTEM); + getAppDir(systemWlpPath, APP_DIR_SHARE); + sprintf(systemWlpPath, format, systemWlpPath, "wallpapers"); - GDir *dir; - GError *error; + GDir *dir; + GError *error; const gchar *filename; dir = g_dir_open(systemWlpPath, 0, &error); - while ((g_dir_read_name(dir))) - (*count)++; + while ((g_dir_read_name(dir))) (*count)++; dir = g_dir_open(userWlpPath, 0, &error); - while ((g_dir_read_name(dir))) - (*count)++; + while ((g_dir_read_name(dir))) (*count)++; WallpaperInfo *list = malloc(sizeof(WallpaperInfo) * (*count)); - WallpaperInfo *ptr = list; + WallpaperInfo *ptr = list; dir = g_dir_open(systemWlpPath, 0, &error); - while ((filename = g_dir_read_name(dir))) { + while ((filename = g_dir_read_name(dir))) + { sprintf(ptr->name, "%s", filename); #ifdef __WIN32 sprintf(ptr->dirPath, "%s\\%s", systemWlpPath, filename); - #else sprintf(ptr->dirPath, "%s/%s", systemWlpPath, filename); #endif - ptr->isDefault = 1; + ptr->isDefault = 1; ptr->config.loaded = 0; ptr++; } dir = g_dir_open(userWlpPath, 0, &error); - while ((filename = g_dir_read_name(dir))) { + while ((filename = g_dir_read_name(dir))) + { sprintf(ptr->name, "%s", filename); #ifdef __WIN32 sprintf(ptr->dirPath, "%s\\%s", userWlpPath, filename); #else sprintf(ptr->dirPath, "%s/%s", userWlpPath, filename); #endif - ptr->isDefault = 0; + ptr->isDefault = 0; ptr->config.loaded = 0; ptr++; } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index cc0a414..3d3fb22 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,12 +1,13 @@ set(_SOURCE_FILES + ../common/paths.c + ../common/config.c + ../common/wallpaperScanner.c + ../common/monitorScanner.c main.c windowHandlers.c wlp.c trayIcon.c - ../common/monitorScanner.c - ../common/config.c - ../common/wallpaperScanner.c - ) +) # Windows resource file if(_UNAME STREQUAL "WIN32") diff --git a/src/core/main.c b/src/core/main.c index 8514ce4..e50d34f 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -64,7 +64,16 @@ static void activate(GtkApplication *app, gpointer userdata) { alreadyRunning = 1; - builder = gtk_builder_new_from_file(MAIN_WINDOW_TEMPLATE_PATH); + char gladefilePath[PATH_MAX]; + getAppDir(gladefilePath, APP_DIR_SHARE); +#ifdef __WIN32 + const char *format = "%s\\window_templates\\main.glade"; +#else + const char *format = "%s/window_templates/main.glade"; +#endif + sprintf(gladefilePath, format, gladefilePath); + + builder = gtk_builder_new_from_file(gladefilePath); gtk_builder_connect_signals(builder, NULL); mainWnd = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow"); @@ -103,14 +112,18 @@ int main(int argc, char *argv[]) { int status; +#ifdef __WIN32 initTrayIcon(); +#endif app = gtk_application_new("com.github.jszczerbinsky.lwp", G_APPLICATION_DEFAULT_FLAGS); g_signal_connect(app, "activate", G_CALLBACK(activate), NULL); status = g_application_run(G_APPLICATION(app), argc, argv); g_object_unref(app); +#ifdef __WIN32 removeTrayIcon(); +#endif return status; } diff --git a/src/wlp/CMakeLists.txt b/src/wlp/CMakeLists.txt index 6e03f2d..13df9ba 100644 --- a/src/wlp/CMakeLists.txt +++ b/src/wlp/CMakeLists.txt @@ -3,8 +3,9 @@ if(SDL2_RUNTIME_DIR) endif() set(_SOURCE_FILES - ../common/monitorScanner.c + ../common/paths.c ../common/config.c + ../common/monitorScanner.c ../common/wallpaperScanner.c main.c debug.c diff --git a/src/wlp/debug.c b/src/wlp/debug.c index d8b15c3..5e51f3a 100644 --- a/src/wlp/debug.c +++ b/src/wlp/debug.c @@ -1,22 +1,13 @@ +#include #include #include -#include #include "main.h" -void getLogFilePath(char *path) -{ - #ifdef __WIN32 - sprintf(path, "%s\\lwp\\lwp.log", g_get_user_data_dir()); - #else - sprintf(path, "/home/cziken/.config/lwp/log"); - #endif -} - void lwpLog(int type, const char *str, ...) { char path[PATH_MAX]; - getLogFilePath(path); + getLogPath(path); FILE *file = fopen(path, "a"); From fd39c4052a9b6476967cf34767d2a0e13c71c02c Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Sat, 17 Feb 2024 13:49:00 +0000 Subject: [PATCH 23/33] New window template --- src/core/main.c | 38 +- src/core/windowHandlers.c | 4 +- .../assets/cross-close-svgrepo-com.svg | 7 + .../assets/github-svgrepo-com.svg | 7 + .../assets/image-photo-svgrepo-com.svg | 7 + .../assets/question-svgrepo-com.svg | 7 + .../assets/screen-monitor-svgrepo-com.svg | 7 + .../assets/settings-svgrepo-com.svg | 10 + src/window_templates/main.glade | 446 ++++++++++++++---- 9 files changed, 445 insertions(+), 88 deletions(-) create mode 100644 src/window_templates/assets/cross-close-svgrepo-com.svg create mode 100644 src/window_templates/assets/github-svgrepo-com.svg create mode 100644 src/window_templates/assets/image-photo-svgrepo-com.svg create mode 100644 src/window_templates/assets/question-svgrepo-com.svg create mode 100644 src/window_templates/assets/screen-monitor-svgrepo-com.svg create mode 100644 src/window_templates/assets/settings-svgrepo-com.svg diff --git a/src/core/main.c b/src/core/main.c index e50d34f..8121afb 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -40,6 +40,17 @@ static void reloadMonitorListBox() g_list_free(rows); + char iconPath[PATH_MAX]; + getAppDir(iconPath, APP_DIR_SHARE); +#ifdef __WIN32 + const char *format = "%s\\%s\\%s\\%s"; +#else + const char *format = "%s/%s/%s/%s"; +#endif + sprintf( + iconPath, format, iconPath, "window_templates", "assets", "screen-monitor-svgrepo-com.svg" + ); + int monitorsCount; MonitorInfo *monitors; @@ -47,11 +58,32 @@ static void reloadMonitorListBox() for (int i = 0; i < monitorsCount; i++) { - GtkWidget *label = gtk_label_new(monitors[i].name); - GtkWidget *row = gtk_list_box_row_new(); - gtk_container_add(GTK_CONTAINER(row), label); + char resStr[12]; + sprintf(resStr, "%dx%d", monitors[i].bounds.w, monitors[i].bounds.h); + + GtkWidget *nameLabel = gtk_label_new(monitors[i].name); + GtkWidget *resLabel = gtk_label_new(resStr); + GtkWidget *icon = gtk_image_new_from_file(iconPath); + + GtkWidget *labelBox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add(GTK_CONTAINER(labelBox), nameLabel); + gtk_container_add(GTK_CONTAINER(labelBox), resLabel); + gtk_box_set_child_packing(GTK_BOX(labelBox), nameLabel, 1, 1, 0, GTK_PACK_START); + + GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_container_add(GTK_CONTAINER(box), icon); + gtk_container_add(GTK_CONTAINER(box), labelBox); + gtk_box_set_child_packing(GTK_BOX(box), labelBox, 1, 1, 0, GTK_PACK_START); + + GtkWidget *row = gtk_list_box_row_new(); + gtk_container_add(GTK_CONTAINER(row), box); gtk_list_box_insert(GTK_LIST_BOX(monitorListBox), row, 0); + + char *nameBuff = malloc(sizeof(strlen(monitors[i].name))); + strcpy(nameBuff, monitors[i].name); + g_object_set_data(G_OBJECT(row), "monitor_name", (gpointer)nameBuff); + gtk_widget_show_all(row); } } diff --git a/src/core/windowHandlers.c b/src/core/windowHandlers.c index bba23ab..4e58732 100644 --- a/src/core/windowHandlers.c +++ b/src/core/windowHandlers.c @@ -71,10 +71,8 @@ G_MODULE_EXPORT void MonitorWindowShow() // Find selected monitor name GtkListBoxRow *listBoxRow = gtk_list_box_get_selected_row(GTK_LIST_BOX(monitorListBox)); - GList *children = gtk_container_get_children(GTK_CONTAINER(listBoxRow)); - const char *monitorName = gtk_label_get_label(GTK_LABEL(children->data)); + const char *monitorName = g_object_get_data(G_OBJECT(listBoxRow), "monitor_name"); gtk_label_set_text(GTK_LABEL(monitorNameLabel), monitorName); - g_list_free(children); // Read configuration from config file MonitorConfig mc; diff --git a/src/window_templates/assets/cross-close-svgrepo-com.svg b/src/window_templates/assets/cross-close-svgrepo-com.svg new file mode 100644 index 0000000..a2846fe --- /dev/null +++ b/src/window_templates/assets/cross-close-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/window_templates/assets/github-svgrepo-com.svg b/src/window_templates/assets/github-svgrepo-com.svg new file mode 100644 index 0000000..c0a813f --- /dev/null +++ b/src/window_templates/assets/github-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/window_templates/assets/image-photo-svgrepo-com.svg b/src/window_templates/assets/image-photo-svgrepo-com.svg new file mode 100644 index 0000000..a924c43 --- /dev/null +++ b/src/window_templates/assets/image-photo-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/window_templates/assets/question-svgrepo-com.svg b/src/window_templates/assets/question-svgrepo-com.svg new file mode 100644 index 0000000..b29ae56 --- /dev/null +++ b/src/window_templates/assets/question-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/window_templates/assets/screen-monitor-svgrepo-com.svg b/src/window_templates/assets/screen-monitor-svgrepo-com.svg new file mode 100644 index 0000000..439637c --- /dev/null +++ b/src/window_templates/assets/screen-monitor-svgrepo-com.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/window_templates/assets/settings-svgrepo-com.svg b/src/window_templates/assets/settings-svgrepo-com.svg new file mode 100644 index 0000000..be067f1 --- /dev/null +++ b/src/window_templates/assets/settings-svgrepo-com.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/window_templates/main.glade b/src/window_templates/main.glade index e91fcd9..c7c666d 100644 --- a/src/window_templates/main.glade +++ b/src/window_templates/main.glade @@ -2,9 +2,14 @@ + False + Closing Layered WallPaper False + 480 + 100 + assets/question-svgrepo-com.svg dialog False @@ -53,13 +58,36 @@ - + True False - Are you sure, you want to exit Layered WallPaper? + + + True + False + assets/question-svgrepo-com.svg + + + False + True + 0 + + + + + True + False + Are you sure, you want to close Layered WallPaper? + + + False + True + 1 + + - False + True True 1 @@ -74,6 +102,7 @@ False + Layered WallPaper Settings False 800 600 @@ -82,6 +111,8 @@ True False + 40 + 40 vertical @@ -98,6 +129,7 @@ center + @@ -112,6 +144,9 @@ False Enhance your desktop experiance center + + + False @@ -130,54 +165,306 @@ True False - 120 - 120 - 8 - vertical + 30 - + True False - 5 - 5 - Setup your monitors - - - + 40 + 40 + vertical + + + True + False + 5 + 15 + Setup your monitors + + + + + + False + True + 0 + + + + + True + False + + + True + True + + + True + False + + + True + False + 10 + assets/screen-monitor-svgrepo-com.svg + 6 + + + False + True + 0 + + + + + True + False + vertical + + + True + False + HDMI-1 + + + True + True + 0 + + + + + True + False + 1920x1080 + + + False + True + 1 + + + + + True + True + 1 + + + + + + + + + True + True + + + True + False + + + True + False + 10 + assets/screen-monitor-svgrepo-com.svg + 6 + + + False + True + 0 + + + + + True + False + vertical + + + True + False + HDMI-1 + + + + + + True + True + 0 + + + + + True + False + 1920x1080 + + + False + True + 1 + + + + + True + True + 1 + + + + + + + + + False + True + 1 + + + + + Edit settings + True + True + True + 10 + + + + False + True + 2 + + - False + True True 0 - + True False - 40 - 40 + center + vertical + 10 + + + True + True + True + + + + True + False + vertical + True + + + True + False + 20 + assets/image-photo-svgrepo-com.svg + 6 + + + True + True + 0 + + + + + True + False + Manage +wallpapers + center + + + False + True + end + 1 + + + + + + + False + True + 0 + + + + + True + True + True + + + + True + False + vertical + True + + + True + False + 20 + assets/cross-close-svgrepo-com.svg + 6 + + + True + True + 0 + + + + + True + False + Turn off +Layered WallPaper + center + + + False + True + end + 1 + + + + + + + False + True + 1 + + False True + end 1 - - - Edit settings - True - True - True - - - - False - True - 2 - - True @@ -189,6 +476,7 @@ True False + 20 True @@ -215,7 +503,7 @@ False True end - 2 + 1 @@ -226,52 +514,15 @@ 2 - - - True - False - center - - - Manage wallpapers - True - True - True - - - - False - True - 0 - - - - - Exit - True - True - True - - - - False - True - end - 1 - - - - - False - True - 3 - - False + Wallpaper Manager + 800 + 600 + assets/image-photo-svgrepo-com.svg @@ -368,6 +619,10 @@ False + Monitor Settings + 600 + 260 + assets/settings-svgrepo-com.svg @@ -376,16 +631,40 @@ False vertical - + True False - 10 - 10 - Monitor name - center - - - + center + + + True + False + assets/screen-monitor-svgrepo-com.svg + + + False + True + 0 + + + + + True + False + 10 + 10 + Monitor name + center + + + + + + False + True + 1 + + False @@ -538,6 +817,9 @@ False center 20 + 20 + 50 + True Apply From 26073474aabb35db3dceda581696996e89f475f3 Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Sun, 18 Feb 2024 21:15:03 +0000 Subject: [PATCH 24/33] Application settings window and wallpaper filtering --- src/common.h | 8 +- src/common/config.c | 71 +++++---- src/core/main.c | 46 +++--- src/core/main.h | 4 + src/core/windowHandlers.c | 36 +++++ src/window_templates/main.glade | 262 +++++++++++++++++++++++++++++++- src/wlp/main.c | 1 + 7 files changed, 369 insertions(+), 59 deletions(-) diff --git a/src/common.h b/src/common.h index 8e8d092..8dc09b2 100644 --- a/src/common.h +++ b/src/common.h @@ -61,8 +61,9 @@ typedef struct typedef struct { - int drawOnRootWindow; - int targetFps; + int drawOnRootWindow; + int targetFps; + char renderQuality[8]; } AppConfig; // @@ -99,7 +100,8 @@ WallpaperInfo *scanWallpapers(int *count); void saveMonitorConfig(const char *name, MonitorConfig *mc); int loadMonitorConfig(const char *name, MonitorConfig *mc); -int loadAppConfig(AppConfig *ac); +int loadAppConfig(AppConfig *ac); +void saveAppConfig(AppConfig *ac); int loadWallpaperConfig(const char *dirName, WallpaperConfig *wc); diff --git a/src/common/config.c b/src/common/config.c index bfd2356..0ede3a3 100644 --- a/src/common/config.c +++ b/src/common/config.c @@ -7,35 +7,6 @@ #define CONFIG_DEFAULT 0 #define CONFIG_USER 1 -/*void getMonitorConfigPath(const char *name, char *path) { -#ifdef __WIN32 - sprintf(path, "%s\\lwp\\monitors\\%s.cfg", g_get_user_data_dir(), name); -#else - sprintf(path, "%s/.config/lwp/monitors/%s.cfg", g_get_home_dir(), name); -#endif -} - -void getWallpaperConfigPath(const char *dirPath, char *path, int type) { - if (type == CONFIG_DEFAULT) - sprintf(path, "%s/wallpaper.cfg", dirPath); - else - sprintf(path, "%s/wallpaper.cfg", dirPath); -} - -void getAppConfigPath(char *path, int type) { -#ifdef __WIN32 - if (type == CONFIG_DEFAULT) - sprintf(path, "/etc/lwp.cfg"); - else - sprintf(path, "%s\\lwp\\lwp.cfg", g_get_user_data_dir()); -#else - if (type == CONFIG_DEFAULT) - sprintf(path, "/etc/lwp.cfg"); - else - sprintf(path, "%s/.config/lwp/lwp.cfg", g_get_home_dir()); -#endif -}*/ - static void generateEmptyMonitorConfig(MonitorConfig *mc) { sprintf(mc->wlpName, ""); @@ -116,6 +87,42 @@ int loadMonitorConfig(const char *name, MonitorConfig *mc) return 1; } +static void useDefaultAppCfg(AppConfig *ac) +{ +#ifdef __LINUX + ac->drawOnRootWindow = 0; +#endif + ac->targetFps = 60; + strcpy(ac->renderQuality, "best"); +} + +void saveAppConfig(AppConfig *ac) +{ + config_t cfg; + config_setting_t *root, *setting; + + config_init(&cfg); + root = config_root_setting(&cfg); + + setting = config_setting_add(root, "draw_on_rootwindow", CONFIG_TYPE_INT); + config_setting_set_int(setting, ac->drawOnRootWindow); + setting = config_setting_add(root, "target_fps", CONFIG_TYPE_INT); + config_setting_set_int(setting, ac->targetFps); + setting = config_setting_add(root, "render_quality", CONFIG_TYPE_STRING); + config_setting_set_string(setting, ac->renderQuality); + + char path[PATH_MAX]; + getAppCfgPath(path); + + if (!config_write_file(&cfg, path)) + { + fprintf(stderr, "Error while writing file.\n"); + config_destroy(&cfg); + } + + config_destroy(&cfg); +} + int loadAppConfig(AppConfig *ac) { config_t cfg; @@ -127,8 +134,8 @@ int loadAppConfig(AppConfig *ac) config_init(&cfg); if (config_read_file(&cfg, path) == CONFIG_FALSE) { - // todo: set default config values - return 0; + useDefaultAppCfg(ac); + return 1; } root = config_root_setting(&cfg); @@ -138,6 +145,8 @@ int loadAppConfig(AppConfig *ac) #endif setting = config_setting_get_member(root, "target_fps"); ac->targetFps = config_setting_get_int(setting); + setting = config_setting_get_member(root, "render_quality"); + strcpy(ac->renderQuality, config_setting_get_string(setting)); config_destroy(&cfg); diff --git a/src/core/main.c b/src/core/main.c index 8121afb..e901b50 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -2,30 +2,26 @@ #include "../platform_guard.h" -#ifdef __WIN32 -#define MAIN_WINDOW_TEMPLATE_PATH "./window_templates/main.glade" -#elif __DARWIN -#define MAIN_WINDOW_TEMPLATE_PATH "" -#else -#define MAIN_WINDOW_TEMPLATE_PATH "/usr/local/share/lwp/window_templates/main.glade" -#endif - GtkApplication *app = NULL; GtkBuilder *builder = NULL; -GtkWidget *mainWnd = NULL; -GtkWidget *exitDialog = NULL; -GtkWidget *wallpaperMgrWnd = NULL; -GtkWidget *monitorWnd = NULL; -GtkWidget *monitorListBox = NULL; -GtkWidget *wallpaperListBox = NULL; -GtkWidget *wallpaperComboBox = NULL; -GtkWidget *xPosSpinBtn = NULL; -GtkWidget *yPosSpinBtn = NULL; -GtkWidget *widthSpinBtn = NULL; -GtkWidget *heightSpinBtn = NULL; -GtkWidget *monitorNameLabel = NULL; -GtkWidget *versionLabel = NULL; +GtkWidget *mainWnd = NULL; +GtkWidget *exitDialog = NULL; +GtkWidget *wallpaperMgrWnd = NULL; +GtkWidget *monitorWnd = NULL; +GtkWidget *monitorListBox = NULL; +GtkWidget *wallpaperListBox = NULL; +GtkWidget *wallpaperComboBox = NULL; +GtkWidget *xPosSpinBtn = NULL; +GtkWidget *yPosSpinBtn = NULL; +GtkWidget *widthSpinBtn = NULL; +GtkWidget *heightSpinBtn = NULL; +GtkWidget *monitorNameLabel = NULL; +GtkWidget *versionLabel = NULL; +GtkWidget *appSettingsWnd = NULL; +GtkWidget *targetFpsComboBox = NULL; +GtkWidget *renderQualityComboBox = NULL; +GtkWidget *drawOnRootWndComboBox = NULL; static void reloadMonitorListBox() { @@ -112,6 +108,7 @@ static void activate(GtkApplication *app, gpointer userdata) exitDialog = (GtkWidget *)gtk_builder_get_object(builder, "ExitDialog"); wallpaperMgrWnd = (GtkWidget *)gtk_builder_get_object(builder, "WallpaperManagerWindow"); monitorWnd = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow"); + appSettingsWnd = (GtkWidget *)gtk_builder_get_object(builder, "SettingsWindow"); monitorListBox = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow_MonitorListBox"); versionLabel = (GtkWidget *)gtk_builder_get_object(builder, "MainWindow_VersionLabel"); wallpaperListBox = @@ -124,11 +121,18 @@ static void activate(GtkApplication *app, gpointer userdata) heightSpinBtn = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow_HeightSpinBtn"); monitorNameLabel = (GtkWidget *)gtk_builder_get_object(builder, "MonitorWindow_MonitorNameLabel"); + targetFpsComboBox = + (GtkWidget *)gtk_builder_get_object(builder, "SettingsWindow_TargetFpsComboBox"); + renderQualityComboBox = + (GtkWidget *)gtk_builder_get_object(builder, "SettingsWindow_TexFilteringComboBox"); + drawOnRootWndComboBox = + (GtkWidget *)gtk_builder_get_object(builder, "SettingsWindow_DrawOnRootWndComboBox"); gtk_window_set_application(GTK_WINDOW(mainWnd), GTK_APPLICATION(app)); gtk_window_set_application(GTK_WINDOW(exitDialog), GTK_APPLICATION(app)); gtk_window_set_application(GTK_WINDOW(wallpaperMgrWnd), GTK_APPLICATION(app)); gtk_window_set_application(GTK_WINDOW(monitorWnd), GTK_APPLICATION(app)); + gtk_window_set_application(GTK_WINDOW(appSettingsWnd), GTK_APPLICATION(app)); gtk_label_set_text(GTK_LABEL(versionLabel), PROGRAM_VERSION); diff --git a/src/core/main.h b/src/core/main.h index 7b17497..b775e33 100644 --- a/src/core/main.h +++ b/src/core/main.h @@ -19,6 +19,10 @@ extern GtkWidget *yPosSpinBtn; extern GtkWidget *widthSpinBtn; extern GtkWidget *heightSpinBtn; extern GtkWidget *monitorNameLabel; +extern GtkWidget *appSettingsWnd; +extern GtkWidget *targetFpsComboBox; +extern GtkWidget *renderQualityComboBox; +extern GtkWidget *drawOnRootWndComboBox; void runWlp(); void killWlp(); diff --git a/src/core/windowHandlers.c b/src/core/windowHandlers.c index 4e58732..b47a504 100644 --- a/src/core/windowHandlers.c +++ b/src/core/windowHandlers.c @@ -23,6 +23,8 @@ G_MODULE_EXPORT void MainWindow_MonitorEditBtnClick() { gtk_widget_set_visible(m G_MODULE_EXPORT void MainWindow_ExitBtnClick() { gtk_widget_set_visible(exitDialog, 1); } +G_MODULE_EXPORT void MainWindow_AppSettingsBtnClick() { gtk_widget_set_visible(appSettingsWnd, 1); } + G_MODULE_EXPORT void MainWindowClose() { gtk_widget_set_visible(mainWnd, 0); } // Wallpapaer Manager Window handlers @@ -103,3 +105,37 @@ G_MODULE_EXPORT void MonitorWindow_ApplyBtnClick() } G_MODULE_EXPORT void MonitorWindow_ExitBtnClick() { gtk_widget_set_visible(monitorWnd, 0); } + +// App Settings Window handlers + +G_MODULE_EXPORT void SettingsWindowShow() +{ + AppConfig ac; + loadAppConfig(&ac); + + char targetFpsStr[4]; + sprintf(targetFpsStr, "%d", ac.targetFps); + char drawOnRootWindowStr[2]; + sprintf(drawOnRootWindowStr, "%d", ac.drawOnRootWindow); + + gtk_combo_box_set_active_id(GTK_COMBO_BOX(renderQualityComboBox), ac.renderQuality); + gtk_combo_box_set_active_id(GTK_COMBO_BOX(targetFpsComboBox), targetFpsStr); + gtk_combo_box_set_active_id(GTK_COMBO_BOX(drawOnRootWndComboBox), drawOnRootWindowStr); +} + +G_MODULE_EXPORT void SettingsWindowClose() { gtk_widget_set_visible(appSettingsWnd, 0); } + +G_MODULE_EXPORT void SettingsWindow_ApplyBtnClick() +{ + AppConfig ac; + strcpy(ac.renderQuality, gtk_combo_box_get_active_id(GTK_COMBO_BOX(renderQualityComboBox))); + ac.targetFps = atoi(gtk_combo_box_get_active_id(GTK_COMBO_BOX(targetFpsComboBox))); + ac.drawOnRootWindow = atoi(gtk_combo_box_get_active_id(GTK_COMBO_BOX(drawOnRootWndComboBox))); + + saveAppConfig(&ac); + + killWlp(); + runWlp(); +} + +G_MODULE_EXPORT void SettingsWindow_ExitBtnClick() { gtk_widget_set_visible(appSettingsWnd, 0); } diff --git a/src/window_templates/main.glade b/src/window_templates/main.glade index c7c666d..0c9be83 100644 --- a/src/window_templates/main.glade +++ b/src/window_templates/main.glade @@ -368,7 +368,6 @@ True False vertical - True True @@ -387,6 +386,8 @@ True False + 15 + 15 Manage wallpapers center @@ -407,6 +408,57 @@ wallpapers 0 + + + True + True + True + + + + True + False + vertical + + + True + False + 20 + assets/settings-svgrepo-com.svg + 6 + + + True + True + 0 + + + + + True + False + 15 + 15 + Application +settings + center + + + False + True + end + 1 + + + + + + + False + True + 1 + + True @@ -418,7 +470,6 @@ wallpapers True False vertical - True True @@ -437,6 +488,8 @@ wallpapers True False + 15 + 15 Turn off Layered WallPaper center @@ -454,7 +507,7 @@ Layered WallPaper False True - 1 + 2 @@ -517,6 +570,207 @@ Layered WallPaper + + False + False + 500 + 250 + + + + True + False + vertical + + + + True + False + center + + + True + False + assets/settings-svgrepo-com.svg + + + False + True + 0 + + + + + True + False + 10 + 10 + Application settings + center + + + + + + False + True + 1 + + + + + False + True + 0 + + + + + + True + False + 20 + 20 + 15 + True + True + + + True + False + start + Target FPS + + + 0 + 0 + + + + + True + False + start + Texture filtering + + + 0 + 1 + + + + + True + False + + Anisotropic + Linear + Nearest-neighbour + + + + 1 + 1 + + + + + True + False + start + Draw on root window (Linux only) + + + 0 + 2 + + + + + True + False + + No + Yes + + + + 1 + 2 + + + + + True + False + + 30 fps + 60 fps + 120 fps + 144 fps + 240 fps + + + + 1 + 0 + + + + + False + True + 1 + + + + + True + False + center + 20 + 20 + 50 + True + + + Apply + True + True + True + + + + False + True + 0 + + + + + Exit + True + True + True + + + + False + True + 1 + + + + + False + True + end + 2 + + + + + False Wallpaper Manager @@ -821,7 +1075,7 @@ Layered WallPaper 50 True - + Apply True True diff --git a/src/wlp/main.c b/src/wlp/main.c index a3c399a..b7c5196 100644 --- a/src/wlp/main.c +++ b/src/wlp/main.c @@ -179,6 +179,7 @@ int main(int argc, char *argv[]) lwpLog(LOG_INFO, "Initializing SDL"); SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, app.config.renderQuality); lwpLog(LOG_INFO, "Initializing window"); initWindow(&app); From 5bf368c8b92d60ce173fc98a652fac9baf92a602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Szczerbi=C5=84ski?= <38699473+jszczerbinsky@users.noreply.github.com> Date: Sun, 18 Feb 2024 22:58:59 +0000 Subject: [PATCH 25/33] Update README.md --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 0869246..54b2e94 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,12 @@ https://user-images.githubusercontent.com/38699473/220888934-09788a6b-873c-469b-
Linux - - #### Installation steps - + + ### Dependencies - Install `SDL2` using Your package manager - If You are using `Wayland`, You also must install `XWayland` + + #### Installation steps - Download `.tar.gz` package from [releases](https://github.com/jszczerbinsky/lwp/releases/latest) - Extract the content to `/`: ```shell @@ -40,8 +41,7 @@ https://user-images.githubusercontent.com/38699473/220888934-09788a6b-873c-469b- - To make Layered WallPaper run on startup, add `lwp &` command to Your desktop enviroment `.rc` file #### Build from source instead - - Install `SDL2` using Your package manager. On some distributions `SDL2` doesn't contain development files, so it may be also necessary to install development version of `SDL2` - - If You are using `Wayland`, You also must install `XWayland` + - In some distributions `SDL2` doesn't contain development files, so it may be also necessary to install development version of `SDL2` - Install `CMake` - Clone the repository and prepare a `build` directory: @@ -69,6 +69,9 @@ https://user-images.githubusercontent.com/38699473/220888934-09788a6b-873c-469b-
macOS + + ### Dependencies + Install the dependencies (homebrew: `brew install sdl2 gtk+3 libconfig`) #### Installation steps - Download and run the installer from [releases](https://github.com/jszczerbinsky/lwp/releases/latest) @@ -77,7 +80,6 @@ https://user-images.githubusercontent.com/38699473/220888934-09788a6b-873c-469b- - To stop running Layered WallPaper on startup, run it again #### Build from source instead - - Install `SDL2` (homebrew: `brew install sdl2`) - To build this project, You need to install `cmake` (homebrew: `brew install cmake`) - Clone the repository: ```zsh @@ -269,4 +271,4 @@ 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. \ No newline at end of file +SOFTWARE. From 29912ed54e6d529be50953729fc4c055dd250d46 Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Mon, 19 Feb 2024 20:33:39 +0000 Subject: [PATCH 26/33] Creating user settings dirs at startup --- src/common.h | 2 ++ src/common/paths.c | 19 +++++++++++++++++++ src/core/main.c | 2 ++ 3 files changed, 23 insertions(+) diff --git a/src/common.h b/src/common.h index 8dc09b2..a827eac 100644 --- a/src/common.h +++ b/src/common.h @@ -81,6 +81,8 @@ void getWlpCfgPath(char *buff, const char *dirPath); void getAppCfgPath(char *buff); void getLogPath(char *buff); +void createUserDirs(); + // // monitorScanner.c // diff --git a/src/common/paths.c b/src/common/paths.c index 2f5c502..afb5e04 100644 --- a/src/common/paths.c +++ b/src/common/paths.c @@ -5,6 +5,25 @@ #include "../common.h" +void createUserDirs() +{ +#ifdef __WIN32 + const char *format = "%s\\%s"; +#else + const char *format = "%s/%s"; +#endif + + char path[PATH_MAX]; + + getAppDir(path, APP_DIR_USER_SETTINGS); + sprintf(path, format, path, "wallpapers"); + g_mkdir_with_parents(path, 484); + + getAppDir(path, APP_DIR_USER_SETTINGS); + sprintf(path, format, path, "monitors"); + g_mkdir_with_parents(path, 484); +} + static void removeLastPathEntry(char *path) { char *ptr = path + strlen(path) - 1; diff --git a/src/core/main.c b/src/core/main.c index e901b50..4be7984 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -148,6 +148,8 @@ int main(int argc, char *argv[]) { int status; + createUserDirs(); + #ifdef __WIN32 initTrayIcon(); #endif From 6445b631b9709790017cc628222e5b26150c63df Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Tue, 20 Feb 2024 14:11:17 +0000 Subject: [PATCH 27/33] G_APPLICATION_FLAGS_NONE compatibility --- src/core/main.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/main.h b/src/core/main.h index b775e33..7b9697f 100644 --- a/src/core/main.h +++ b/src/core/main.h @@ -3,6 +3,11 @@ #include +// G_APPLICATION_DEFAULT_FLAGS doesn't exist in older versions of glib +#ifndef G_APPLICATION_DEFAULT_FLAGS +#define G_APPLICATION_DEFAULT_FLAGS G_APPLICATION_FLAGS_NONE +#endif + #include "../common.h" extern GtkApplication *app; From 14a1ab76301d43b53acf37b9191eda2a5ce1c0cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Szczerbi=C5=84ski?= <38699473+jszczerbinsky@users.noreply.github.com> Date: Fri, 8 Mar 2024 17:44:29 +0000 Subject: [PATCH 28/33] Update build.yml --- .github/workflows/build.yml | 62 +++++++++++++------------------------ 1 file changed, 21 insertions(+), 41 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6a81a7e..689157a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: - name: Install cmake uses: lukka/get-cmake@v3.26.0 - name: Install dependencies - run: sudo apt install libsdl2-dev + run: sudo apt update && sudo apt install libsdl2-dev libconfig-dev libgtk-3-dev - name: Build run: mkdir -p build && cd build && cmake ../ && cmake --build . && cpack @@ -28,45 +28,25 @@ jobs: steps: - name: Checkout uses: actions/checkout@v3 - - name: Install cmake - uses: lukka/get-cmake@v3.26.0 - - name: Install MinGW - uses: egor-tensin/setup-mingw@v2 - - name: Install MSVC - uses: ilammy/msvc-dev-cmd@v1 - - name: Install wget - run: choco install wget - - name: Install dependencies` - run: | - wget -nv https://github.com/libsdl-org/SDL/releases/download/release-2.26.3/SDL2-devel-2.26.3-mingw.zip - 7z x -bd SDL2-devel-2.26.3-mingw.zip -omingw - wget -nv https://github.com/libsdl-org/SDL/releases/download/release-2.26.3/SDL2-devel-2.26.3-VC.zip - 7z x -bd SDL2-devel-2.26.3-VC.zip -omsvc - - name: Build with MSVC + - name: Install MSYS2 + uses: msys2/setup-msys2@v2 + with: + update: true + msystem: MINGW64 + install: >- + git + curl + unzip + mingw-w64-x86_64-cmake + mingw-w64-x86_64-gcc + mingw-w64-x86_64-gtk3 + mingw-w64-x86_64-SDL2 + mingw-w64-x86_64-libconfig + - name: Download SDL2 runtime + shell: msys2 {0} run: | - mkdir buildMSVC - cd buildMSVC - cmake -G "Visual Studio 17" -DSDL2_DIR=${{ github.workspace }}\msvc\SDL2-2.26.3\cmake -DSDL2_RUNTIME_DIR=${{ github.workspace }}\msvc\SDL2-2.26.3\lib\x64 ..\ - cmake --build . - cd .. - - name: Build with MinGW - run: | - mkdir buildMinGW - cd buildMinGW - cmake -G "MinGW Makefiles" -DSDL2_DIR=${{ github.workspace }}\mingw\SDL2-2.26.3\cmake -DSDL2_RUNTIME_DIR=${{ github.workspace }}\mingw\SDL2-2.26.3\x86_64-mingw32\bin -DCMAKE_BUILD_TYPE=Release ..\ - cmake --build . - - - macOS: - name: Build on MacOS - runs-on: macos-12 - - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Install cmake - uses: lukka/get-cmake@v3.26.0 - - name: Install dependencies` - run: brew install sdl2 + curl -L https://github.com/libsdl-org/SDL/releases/download/release-2.30.1/SDL2-2.30.1-win32-x64.zip > sdl2.zip + unzip sdl2.zip - name: Build - run: mkdir build && cd build && cmake ../ && cmake --build . && sudo cpack + shell: msys2 {0} + run: mkdir -p build && cd build && cmake -DSDL2_RUNTIME_DIR=${GITHUB_WORKSPACE} ../ && cmake --build . && ../distributeDLLs.sh && cpack From 878043a502fabac51e69a4dd550572b67c41f785 Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Fri, 8 Mar 2024 18:56:56 +0000 Subject: [PATCH 29/33] Hiding window on start --- src/core/main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/main.c b/src/core/main.c index 4be7984..0ecca84 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -90,8 +90,6 @@ static void activate(GtkApplication *app, gpointer userdata) if (!alreadyRunning) { - alreadyRunning = 1; - char gladefilePath[PATH_MAX]; getAppDir(gladefilePath, APP_DIR_SHARE); #ifdef __WIN32 @@ -141,7 +139,9 @@ static void activate(GtkApplication *app, gpointer userdata) reloadMonitorListBox(); - gtk_widget_set_visible(mainWnd, 1); + if (alreadyRunning) gtk_widget_set_visible(mainWnd, 1); + + alreadyRunning = 1; } int main(int argc, char *argv[]) From 8ff13a43482886b1d3d954b651f0ff9922725991 Mon Sep 17 00:00:00 2001 From: jszczerbinsky Date: Fri, 8 Mar 2024 19:29:51 +0000 Subject: [PATCH 30/33] Custom installation prefix on Linux --- CMakeLists.txt | 46 ++----------- src/common.h | 2 - src/common/paths.c | 1 - src/core/CMakeLists.txt | 9 +-- src/core/trayIcon.c | 65 +++++++++++------- src/core/wlp.c | 6 +- src/platform_guard.h | 8 +-- src/wlp/CMakeLists.txt | 16 +---- src/wlp/main.h | 7 -- src/wlp/render.c | 4 -- src/wlp/window.c | 145 +++++++++++++++++----------------------- 11 files changed, 119 insertions(+), 190 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9069ed9..78672fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,9 +6,7 @@ project(lwp LANGUAGES C) # Detect the platform -if (APPLE) - set(_UNAME "DARWIN") -elseif (MSYS OR MINGW) +if (MSYS OR MINGW) set(_UNAME "WIN32") elseif(UNIX) set(_UNAME "LINUX") @@ -40,9 +38,6 @@ set(_DEFAULT_CONFIG_FILE default.cfg) if(_UNAME STREQUAL "WIN32") set(_DEFAULT_CONFIG_FILE defaultWin.cfg) endif() -if (_UNAME STREQUAL "DARWIN") - set(_DEFAULT_CONFIG_FILE defaultMac.cfg) -endif() add_subdirectory(src/wlp) link_directories(src/wlp) @@ -60,43 +55,17 @@ if(_UNAME STREQUAL "WIN32") DESTINATION .) install(FILES LICENSE.txt DESTINATION .) - install(FILES ${_DEFAULT_CONFIG_FILE} - DESTINATION .) install(FILES ${SDL2_RUNTIME_DIR}/SDL2.dll DESTINATION .) install(FILES ${SDL2_RUNTIME_DIR}/README-SDL.txt DESTINATION .) -elseif(_UNAME STREQUAL "LINUX") +else() install(DIRECTORY wallpapers - DESTINATION usr/local/share/lwp) + DESTINATION share/lwp) install(DIRECTORY src/window_templates - DESTINATION usr/local/share/lwp) + DESTINATION share/lwp) install(FILES LICENSE.txt - DESTINATION usr/local/share/lwp) - install(FILES ${_DEFAULT_CONFIG_FILE} - TYPE SYSCONF - RENAME lwp.cfg) -else() - install(DIRECTORY wallpapers - DESTINATION Layered_WallPaper) - install(DIRECTORY src/window_templates - DESTINATION Layered_WallPaper) - install(FILES LICENSE.txt - DESTINATION Layered_WallPaper) - install(FILES ${_DEFAULT_CONFIG_FILE} - DESTINATION Layered_WallPaper - RENAME lwp.cfg) - install(FILES lwp.template.plist - DESTINATION Layered_WallPaper) - install (FILES setupPlist.command - DESTINATION Layered_WallPaper - RENAME Toggle_Autorun.command - PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) - install (FILES setupPlist.command - DESTINATION . - RENAME Toggle_Autorun.command - PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) - + DESTINATION share/lwp) endif() @@ -107,11 +76,8 @@ if (_UNAME STREQUAL "WIN32") elseif(_UNAME STREQUAL "LINUX") set(CPACK_GENERATOR TGZ) set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF) -elseif(_UNAME STREQUAL "DARWIN") - set(CPACK_GENERATOR DragNDrop) - set(CPACK_DMG_BACKGROUND_IMAGE ${CMAKE_CURRENT_SOURCE_DIR}/dmgBg.png) - set(CPACK_DMG_DS_STORE_SETUP_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/setupDmg.applescript) endif() + set(CPACK_NSIS_MUI_ICON ${CMAKE_CURRENT_SOURCE_DIR}/icon.ico) set(CPACK_NSIS_MUI_UNIICON ${CMAKE_CURRENT_SOURCE_DIR}/icon.ico) set(CPACK_NSIS_INSTALLED_ICON_NAME ${CMAKE_CURRENT_SOURCE_DIR}/icon.ico) diff --git a/src/common.h b/src/common.h index a827eac..e17526b 100644 --- a/src/common.h +++ b/src/common.h @@ -3,8 +3,6 @@ #ifdef __LINUX #include -#elif __DARWIN -#include #else #include #endif diff --git a/src/common/paths.c b/src/common/paths.c index afb5e04..e2e79b8 100644 --- a/src/common/paths.c +++ b/src/common/paths.c @@ -63,7 +63,6 @@ void getAppDir(char *buff, int type) #elif __WIN32 GetModuleFileNameA(NULL, buff, PATH_MAX); removeLastPathEntry(buff); -#elif __DARWIN #endif } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3d3fb22..b0eaa33 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -47,9 +47,7 @@ list(APPEND _INCLUDE_DIRS ${libconfig_INCLUDE_DIRS}) list(APPEND _LIBS ${libconfig_LINK_LIBRARIES}) # Main executable -if (_UNAME STREQUAL "DARWIN") - add_executable(lwp MACOSX_BUNDLE ${_SOURCE_FILES}) -elseif(_UNAME STREQUAL "WIN32") +if(_UNAME STREQUAL "WIN32") add_executable(lwp WIN32 ${_SOURCE_FILES}) else() add_executable(lwp ${_SOURCE_FILES}) @@ -67,9 +65,6 @@ if(_UNAME STREQUAL "WIN32") DESTINATION .) elseif(_UNAME STREQUAL "LINUX") install(TARGETS lwp - DESTINATION usr/local/bin) -else() - install(TARGETS lwp - DESTINATION Layered_WallPaper) + DESTINATION bin) endif() diff --git a/src/core/trayIcon.c b/src/core/trayIcon.c index 52b9764..b63cdbc 100644 --- a/src/core/trayIcon.c +++ b/src/core/trayIcon.c @@ -1,11 +1,10 @@ #ifdef __WIN32 -#include "main.h" - #include #include #include +#include "main.h" #define WM_TRAY_ICON (WM_USER + 1) @@ -13,9 +12,11 @@ static NOTIFYICONDATA nid; void removeTrayIcon() { Shell_NotifyIcon(NIM_DELETE, &nid); } -int updateTrayIcon() { +int updateTrayIcon() +{ MSG msg; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { TranslateMessage(&msg); DispatchMessage(&msg); } @@ -23,41 +24,55 @@ int updateTrayIcon() { return 1; } -static LRESULT CALLBACK wndProc(HWND hwnd, UINT uMsg, WPARAM wParam, - LPARAM lParam) { - switch (uMsg) { - case WM_TRAY_ICON: - if (lParam == WM_RBUTTONDOWN || lParam == WM_LBUTTONDOWN) { - gtk_widget_set_visible(mainWnd, 1); - } - break; +static LRESULT CALLBACK wndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_TRAY_ICON: + if (lParam == WM_RBUTTONDOWN || lParam == WM_LBUTTONDOWN) + { + gtk_widget_set_visible(mainWnd, 1); + } + break; } } -void initTrayIcon() { +void initTrayIcon() +{ // Create an invisible window to process tray icon events - HINSTANCE hInstance = GetModuleHandle(NULL); + HINSTANCE hInstance = GetModuleHandle(NULL); const TCHAR CLASS_NAME[] = TEXT("Hidden Window"); - WNDCLASS wc; + WNDCLASS wc; memset(&wc, 0, sizeof(WNDCLASS)); - wc.lpfnWndProc = wndProc; - wc.hInstance = hInstance; + wc.lpfnWndProc = wndProc; + wc.hInstance = hInstance; wc.lpszClassName = CLASS_NAME; RegisterClass(&wc); - HWND hWnd = CreateWindowEx(0, CLASS_NAME, TEXT(""), WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - CW_USEDEFAULT, NULL, NULL, hInstance, NULL); + HWND hWnd = CreateWindowEx( + 0, + CLASS_NAME, + TEXT(""), + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + CW_USEDEFAULT, + NULL, + NULL, + hInstance, + NULL + ); // Create tray icon - nid.cbSize = sizeof(NOTIFYICONDATA); - nid.hWnd = hWnd; + nid.cbSize = sizeof(NOTIFYICONDATA); + nid.hWnd = hWnd; nid.uCallbackMessage = WM_TRAY_ICON; - nid.hIcon = LoadIcon(hInstance, "ID"); - nid.uFlags = NIF_ICON | NIF_MESSAGE; + nid.hIcon = LoadIcon(hInstance, "ID"); + nid.uFlags = NIF_ICON | NIF_MESSAGE; Shell_NotifyIcon(NIM_ADD, &nid); } -#endif \ No newline at end of file +#endif diff --git a/src/core/wlp.c b/src/core/wlp.c index a51aac9..3ba9668 100644 --- a/src/core/wlp.c +++ b/src/core/wlp.c @@ -53,7 +53,11 @@ void runWlp() printf("Failed to start a wallpaper subprocess"); #else - wlpProcess = popen("/usr/local/bin/lwpwlp", "r"); + char path[PATH_MAX]; + getAppDir(path, APP_DIR_BIN); + strcat(path, "/lwpwlp"); + + wlpProcess = popen(path, "r"); char buff[10]; fgets(buff, sizeof(buff) - 1, wlpProcess); wlpPid = atoi(buff); diff --git a/src/platform_guard.h b/src/platform_guard.h index 23b647c..2b1dda8 100644 --- a/src/platform_guard.h +++ b/src/platform_guard.h @@ -2,13 +2,11 @@ #define PLATFORM_GUARD_H #ifndef __WIN32 -#ifndef __DARWIN #ifndef __LINUX #error "Unsupported platform." -#endif // __LINUX -#endif // __DARWIN -#endif // __WIN32 +#endif // __LINUX +#endif // __WIN32 -#endif // PLATFORM_GUARD_H +#endif // PLATFORM_GUARD_H diff --git a/src/wlp/CMakeLists.txt b/src/wlp/CMakeLists.txt index 13df9ba..8055a49 100644 --- a/src/wlp/CMakeLists.txt +++ b/src/wlp/CMakeLists.txt @@ -40,11 +40,6 @@ find_package(SDL2 REQUIRED CONFIG) list(APPEND _INCLUDE_DIRS ${SDL2_INCLUDE_DIRS}) list(APPEND _LIBS ${SDL2_LIBRARIES}) -if (_UNAME STREQUAL "DARWIN") - # MacOSX framework dependencies - list(APPEND _LIBS "-framework CoreGraphics" "-framework Foundation") -endif() - if (_UNAME STREQUAL "LINUX") # X11 dependency find_package(X11 REQUIRED) @@ -55,9 +50,7 @@ endif() option(LWP_INSTALL_LAUNCHD "Launch lwp on login (MacOSX only)" OFF) # Main executable -if (_UNAME STREQUAL "DARWIN") - add_executable(lwpwlp MACOSX_BUNDLE ${_SOURCE_FILES}) -elseif(_UNAME STREQUAL "WIN32") +if(_UNAME STREQUAL "WIN32") add_executable(lwpwlp WIN32 ${_SOURCE_FILES}) else() add_executable(lwpwlp ${_SOURCE_FILES}) @@ -76,11 +69,8 @@ target_link_libraries(lwpwlp PRIVATE ${_LIBS}) if(_UNAME STREQUAL "WIN32") install(TARGETS lwpwlp DESTINATION .) -elseif(_UNAME STREQUAL "LINUX") - install(TARGETS lwpwlp - DESTINATION usr/local/bin) else() - install(TARGETS lwpwlp - DESTINATION Layered_WallPaper) + install(TARGETS lwpwlp + DESTINATION bin) endif() diff --git a/src/wlp/main.h b/src/wlp/main.h index 1d3daef..cdbe86b 100644 --- a/src/wlp/main.h +++ b/src/wlp/main.h @@ -13,13 +13,6 @@ #ifdef __WIN32 #include #include -#elif __DARWIN -#include -#include -#include -#include -#include -#include #elif __LINUX #include #include diff --git a/src/wlp/render.c b/src/wlp/render.c index baec2c4..d0ddc5b 100644 --- a/src/wlp/render.c +++ b/src/wlp/render.c @@ -1,8 +1,4 @@ -#ifdef _MSC_VER -#include -#else #include -#endif #include "main.h" diff --git a/src/wlp/window.c b/src/wlp/window.c index 96b684d..c160103 100644 --- a/src/wlp/window.c +++ b/src/wlp/window.c @@ -2,24 +2,27 @@ #ifdef __WIN32 #include -#endif // __WIN32 +#endif // __WIN32 #ifdef __LINUX #include #include #include -#endif //__LINUX +#endif //__LINUX #ifdef __WIN32 -static HWND iconWorkerw; -static BOOL CALLBACK getIconWorkerw(HWND hWnd, LPARAM lParam) { +static HWND iconWorkerw; +static BOOL CALLBACK getIconWorkerw(HWND hWnd, LPARAM lParam) +{ char buff[10]; GetClassName(hWnd, buff, 10); - if (strcmp(buff, "WorkerW") == 0) { + if (strcmp(buff, "WorkerW") == 0) + { HWND defView = FindWindowEx(hWnd, NULL, "SHELLDLL_DefView", NULL); - if (defView) { + if (defView) + { iconWorkerw = hWnd; return FALSE; } @@ -27,60 +30,15 @@ static BOOL CALLBACK getIconWorkerw(HWND hWnd, LPARAM lParam) { return TRUE; } -#elif __DARWIN - -// Helper macros for objc runtime interaction -#define OBJC_MSG_INT ((id(*)(id, SEL, int))objc_msgSend) -#define OBJC_MSG_ID ((id(*)(id, SEL, id))objc_msgSend) -#define OBJC_MSG_PTR ((id(*)(id, SEL, void *))objc_msgSend) -#define OBJC_MSG_CLS ((id(*)(Class, SEL))objc_msgSend) -#define OBJC_MSG_CLS_CHR ((id(*)(Class, SEL, char *))objc_msgSend) - -void initWindow(Config *cfg) { - // Get main display size - const CGDirectDisplayID displayID = CGMainDisplayID(); - const size_t w = CGDisplayPixelsWide(displayID); - const size_t h = CGDisplayPixelsHigh(displayID); - const struct CGRect frameRect = {0, 0, w, h}; - - // Get shared NScfglication instance - const id ns_cfg = OBJC_MSG_CLS(objc_getClass("NScfglication"), - sel_getUid("sharedcfglication")); - OBJC_MSG_INT(ns_cfg, sel_getUid("setActivationPolicy:"), - 0); // NScfglicationActivationPolicyRegular - - // Create NSWindow - const id window = - ((id(*)(id, SEL, struct CGRect, int, int, int))objc_msgSend)( - OBJC_MSG_CLS(objc_getClass("NSWindow"), sel_getUid("alloc")), - sel_getUid("initWithContentRect:styleMask:backing:defer:"), frameRect, - 0, // NSWindowStyleMaskBorderless - 2, // NSBackingStoreBuffered - false); - - // Set window attributes - OBJC_MSG_ID(window, sel_getUid("setTitle:"), - OBJC_MSG_CLS_CHR(objc_getClass("NSString"), - sel_getUid("stringWithUTF8String:"), - "Parallax wallpaper")); - OBJC_MSG_PTR(window, sel_getUid("makeKeyAndOrderFront:"), nil); - OBJC_MSG_INT(window, sel_getUid("setLevel:"), kCGDesktopWindowLevel - 1); - OBJC_MSG_INT(ns_cfg, sel_getUid("activateIgnoringOthercfgs:"), true); - - // Create SDL window from NSWindow - cfg->window = SDL_CreateWindowFrom((void *)window); - if (cfg->window == NULL) - lwpLog(LOG_ERROR, "%s", SDL_GetError()); -} #endif -void initWindow(App *app) { +void initWindow(App *app) +{ #ifdef __WIN32 - app->window = SDL_CreateWindow("Parallax wallpaper", 0, 0, 0, 0, - SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); - if (app->window == NULL) - lwpLog(LOG_ERROR, "%s", SDL_GetError()); + app->window = + SDL_CreateWindow("Parallax wallpaper", 0, 0, 0, 0, SDL_WINDOW_OPENGL | SDL_WINDOW_HIDDEN); + if (app->window == NULL) lwpLog(LOG_ERROR, "%s", SDL_GetError()); SDL_SysWMinfo sysWmInfo; SDL_VERSION(&sysWmInfo.version) @@ -88,65 +46,82 @@ void initWindow(App *app) { HWND hWindow = sysWmInfo.info.win.window; HWND progman = FindWindow("Progman", NULL); - iconWorkerw = progman; + iconWorkerw = progman; SendMessageTimeout(progman, 0x052C, NULL, NULL, SMTO_NORMAL, 1000, NULL); - if (!FindWindowEx(progman, NULL, "SHELLDLL_DefView", NULL)) - EnumWindows(getIconWorkerw, NULL); + if (!FindWindowEx(progman, NULL, "SHELLDLL_DefView", NULL)) EnumWindows(getIconWorkerw, NULL); HWND wallpaperWorkerw = GetWindow(iconWorkerw, GW_HWNDNEXT); SetParent(hWindow, wallpaperWorkerw); - SetWindowLongPtr(hWindow, GWL_EXSTYLE, - WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | - WS_EX_NOACTIVATE); + SetWindowLongPtr( + hWindow, GWL_EXSTYLE, WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_RIGHTSCROLLBAR | WS_EX_NOACTIVATE + ); SetWindowLongPtr(hWindow, GWL_STYLE, WS_CHILDWINDOW | WS_VISIBLE); - SetWindowPos(hWindow, NULL, 0, 0, GetSystemMetrics(SM_CXVIRTUALSCREEN), - GetSystemMetrics(SM_CYVIRTUALSCREEN), - SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW); - -#elif __DARWIN + SetWindowPos( + hWindow, + NULL, + 0, + 0, + GetSystemMetrics(SM_CXVIRTUALSCREEN), + GetSystemMetrics(SM_CYVIRTUALSCREEN), + SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW + ); #else - if (!app->config.drawOnRootWindow) { + if (!app->config.drawOnRootWindow) + { Display *display = XOpenDisplay(NULL); XCloseDisplay(display); - app->window = - SDL_CreateWindow("Layered WallPaper", 0, 0, DisplayWidth(display, 0), - DisplayHeight(display, 0), SDL_WINDOW_OPENGL); + app->window = SDL_CreateWindow( + "Layered WallPaper", + 0, + 0, + DisplayWidth(display, 0), + DisplayHeight(display, 0), + SDL_WINDOW_OPENGL + ); SDL_SysWMinfo wmInfo; SDL_GetVersion(&wmInfo.version); SDL_GetWindowWMInfo(app->window, &wmInfo); Window xWnd = wmInfo.info.x11.window; - display = wmInfo.info.x11.display; + display = wmInfo.info.x11.display; - Atom atomType = XInternAtom(display, "_NET_WM_WINDOW_TYPE", 0); + Atom atomType = XInternAtom(display, "_NET_WM_WINDOW_TYPE", 0); Atom atomDesktop = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DESKTOP", 0); - XChangeProperty(display, xWnd, atomType, XA_ATOM, 32, PropModeReplace, - (const unsigned char *)&atomDesktop, 1); + XChangeProperty( + display, + xWnd, + atomType, + XA_ATOM, + 32, + PropModeReplace, + (const unsigned char *)&atomDesktop, + 1 + ); Window rootWindow = RootWindow(display, DefaultScreen(display)); XReparentWindow(display, xWnd, rootWindow, 0, 0); XSync(display, 0); - } else { - Display *display = XOpenDisplay(NULL); - Window rootWindow = RootWindow(display, DefaultScreen(display)); - app->window = SDL_CreateWindowFrom((void *)rootWindow); + } + else + { + Display *display = XOpenDisplay(NULL); + Window rootWindow = RootWindow(display, DefaultScreen(display)); + app->window = SDL_CreateWindowFrom((void *)rootWindow); XCloseDisplay(display); } #endif - if (app->window == NULL) - lwpLog(LOG_ERROR, "Failed to initialize window: %s", SDL_GetError()); + if (app->window == NULL) lwpLog(LOG_ERROR, "Failed to initialize window: %s", SDL_GetError()); - app->renderer = SDL_CreateRenderer( - app->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); - if (app->renderer == NULL) - lwpLog(LOG_ERROR, "Failed to initialize renderer: %s", SDL_GetError()); + app->renderer = + SDL_CreateRenderer(app->window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); + if (app->renderer == NULL) lwpLog(LOG_ERROR, "Failed to initialize renderer: %s", SDL_GetError()); } From 4e34348323811f8665fd819cea820706c036668b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Szczerbi=C5=84ski?= Date: Sat, 9 Mar 2024 16:14:12 +0100 Subject: [PATCH 31/33] Working on Windows --- src/common/config.c | 2 ++ src/common/paths.c | 7 +++++-- src/core/trayIcon.c | 2 +- src/core/windowHandlers.c | 16 ++++++++++++++-- src/core/wlp.c | 1 - src/window_templates/main.glade | 2 +- 6 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/common/config.c b/src/common/config.c index 0ede3a3..292c327 100644 --- a/src/common/config.c +++ b/src/common/config.c @@ -104,8 +104,10 @@ void saveAppConfig(AppConfig *ac) config_init(&cfg); root = config_root_setting(&cfg); +#ifdef __LINUX setting = config_setting_add(root, "draw_on_rootwindow", CONFIG_TYPE_INT); config_setting_set_int(setting, ac->drawOnRootWindow); +#endif setting = config_setting_add(root, "target_fps", CONFIG_TYPE_INT); config_setting_set_int(setting, ac->targetFps); setting = config_setting_add(root, "render_quality", CONFIG_TYPE_STRING); diff --git a/src/common/paths.c b/src/common/paths.c index e2e79b8..1eeb430 100644 --- a/src/common/paths.c +++ b/src/common/paths.c @@ -61,8 +61,11 @@ void getAppDir(char *buff, int type) sprintf(buff, "%s/%s", prefix, "share/lwp"); } #elif __WIN32 - GetModuleFileNameA(NULL, buff, PATH_MAX); - removeLastPathEntry(buff); + if (type == APP_DIR_BIN || type == APP_DIR_SHARE) + { + GetModuleFileNameA(NULL, buff, PATH_MAX); + removeLastPathEntry(buff); + } #endif } diff --git a/src/core/trayIcon.c b/src/core/trayIcon.c index b63cdbc..e4d1473 100644 --- a/src/core/trayIcon.c +++ b/src/core/trayIcon.c @@ -1,8 +1,8 @@ #ifdef __WIN32 +#include #include #include -#include #include "main.h" diff --git a/src/core/windowHandlers.c b/src/core/windowHandlers.c index b47a504..bc75cfa 100644 --- a/src/core/windowHandlers.c +++ b/src/core/windowHandlers.c @@ -16,7 +16,15 @@ G_MODULE_EXPORT void ExitDialog_Yes() G_MODULE_EXPORT void MainWindow_ManageWallpapersBtnClick() { - gtk_widget_set_visible(wallpaperMgrWnd, 1); + GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT; + GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW(mainWnd), + flags, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "This feature will be available in the future"); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + //gtk_widget_set_visible(wallpaperMgrWnd, 1); } G_MODULE_EXPORT void MainWindow_MonitorEditBtnClick() { gtk_widget_set_visible(monitorWnd, 1); } @@ -115,9 +123,13 @@ G_MODULE_EXPORT void SettingsWindowShow() char targetFpsStr[4]; sprintf(targetFpsStr, "%d", ac.targetFps); + +#ifdef __LINUX char drawOnRootWindowStr[2]; sprintf(drawOnRootWindowStr, "%d", ac.drawOnRootWindow); - +#else + char *drawOnRootWindowStr = "0"; +#endif gtk_combo_box_set_active_id(GTK_COMBO_BOX(renderQualityComboBox), ac.renderQuality); gtk_combo_box_set_active_id(GTK_COMBO_BOX(targetFpsComboBox), targetFpsStr); gtk_combo_box_set_active_id(GTK_COMBO_BOX(drawOnRootWndComboBox), drawOnRootWindowStr); diff --git a/src/core/wlp.c b/src/core/wlp.c index 3ba9668..2542599 100644 --- a/src/core/wlp.c +++ b/src/core/wlp.c @@ -45,7 +45,6 @@ void runWlp() GetModuleFileName(NULL, path, MAX_PATH); path[_tcslen(path) - 4] = '\0'; _tcscat(path, TEXT("wlp.exe")); - printf("%s\n", path); if (CreateProcess(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) hWlpProcess = pi.hProcess; diff --git a/src/window_templates/main.glade b/src/window_templates/main.glade index 0c9be83..e3147ed 100644 --- a/src/window_templates/main.glade +++ b/src/window_templates/main.glade @@ -576,12 +576,12 @@ Layered WallPaper 500 250 + True False vertical - True From 513147c2bf24361e3d659d85a2e5bd0d160baae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Szczerbi=C5=84ski?= <38699473+jszczerbinsky@users.noreply.github.com> Date: Sat, 9 Mar 2024 16:39:44 +0100 Subject: [PATCH 32/33] Update README.md --- README.md | 128 ++++++------------------------------------------------ 1 file changed, 14 insertions(+), 114 deletions(-) diff --git a/README.md b/README.md index 54b2e94..cc31888 100644 --- a/README.md +++ b/README.md @@ -32,12 +32,14 @@ https://user-images.githubusercontent.com/38699473/220888934-09788a6b-873c-469b- #### Installation steps - Download `.tar.gz` package from [releases](https://github.com/jszczerbinsky/lwp/releases/latest) - - Extract the content to `/`: + - Extract the content to `/usr/local`: ```shell - sudo tar -o -xvf [archive name].tar.gz --directory / + sudo tar -o -xvf [archive name].tar.gz --directory /usr/local ``` + - Note that if You install lwp somewhere else than `/usr/local`, You need to set `LWP_PREFIX` env before running `lwp`. - Test Layered WallPaper by running `lwp` - - Setting `reload_rootwindow=1` in config file may be necessary on some distributions for Layered WallPaper to work properly (see [configuration](#configuration)) + - Run `lwp`, then run it again to open the configuration window. Select wallpaper for each monitor. + - If You can't see any wallpaper try setting `Draw on root window` in application settings. This may be necessary on some distributions for Layered WallPaper to work properly (see [configuration](#configuration)) - To make Layered WallPaper run on startup, add `lwp &` command to Your desktop enviroment `.rc` file #### Build from source instead @@ -57,48 +59,8 @@ https://user-images.githubusercontent.com/38699473/220888934-09788a6b-873c-469b- cmake --build . cpack ``` - - Extract `.tar.gz` package - ```shell - sudo tar -o -xvf [archive name].tar.gz --directory / - ``` - - Test Layered WallPaper by running `lwp` - - Setting `reload_rootwindow=1` in config file may be necessary on some distributions for Layered WallPaper to work properly (see [configuration](#configuration)) - - To make Layered WallPaper run on startup, add `lwp &` command to Your desktop enviroment `.rc` file - - - -
- macOS - - ### Dependencies - Install the dependencies (homebrew: `brew install sdl2 gtk+3 libconfig`) + - After this `.tar.gz` package should appear. Follow the installation steps - #### Installation steps - - Download and run the installer from [releases](https://github.com/jszczerbinsky/lwp/releases/latest) - - Drag and drop Layered_WallPaper into Applications - - To make Layered WallPaper run on startup, run Toggle_Autorun.command - - To stop running Layered WallPaper on startup, run it again - - #### Build from source instead - - To build this project, You need to install `cmake` (homebrew: `brew install cmake`) - - Clone the repository: - ```zsh - git clone https://github.com/jszczerbinsky/lwp - cd lwp - - ``` - - Compile and generate installer - ```zsh - mkdir build - cd build - cmake ../ - cmake --build . - cpack -G DragNDrop - ``` - - DMG installer should appear, open it and drag Layered_WallPaper into Applications - - To make Layered WallPaper run on startup, run Toggle_Autorun.command - - To stop running Layered WallPaper on startup, run it again -
@@ -107,29 +69,18 @@ https://user-images.githubusercontent.com/38699473/220888934-09788a6b-873c-469b- #### Installation steps - Download and run the installer from [releases](https://github.com/jszczerbinsky/lwp/releases/latest) - Layered WallPaper should run immediately after the installation + - Click the tray icon on the right side of Your taskbar to show the configuration window. Set the wallpapers for each monitor #### Build from source instead To compile Layered WallPaper on Windows you need to install [MSYS2](https://www.msys2.org/). After the installation follow the guide for setting up [GTK development enviroment](https://www.gtk.org/docs/installations/windows#using-gtk-from-msys2-packages). From now on continue using MSYS2 MinGW terminal (make sure you're using `MSYS2 MINGW64`/`MSYS2 MINGW32` instead of `MSYS2`). ##### Install the remaining dependencies -There is a problem with the newest version of SDL2 on MSYS2 repository (see [issue](https://github.com/haskell-game/sdl2/issues/277)), that leads to a compilation error, thus You should install an older version of SDL2 instead. -```shell -# For 64bit: -curl -O https://repo.msys2.org/mingw/x86_64/mingw-w64-x86_64-SDL2-2.0.14-2-any.pkg.tar.zst -pacman -U mingw-w64-x86_64-SDL2-2.0.14-2-any.pkg.tar.zst - -# For 32bit: -curl -O https://repo.msys2.org/mingw/i686/mingw-w64-i686-SDL2-2.0.14-2-any.pkg.tar.zst -pacman -U mingw-w64-i686-SDL2-2.0.14-2-any.pkg.tar.zst -``` - -You also have to install cmake ```shell # For 64bit: -pacman -S mingw-w64-x86_64-cmake +pacman -S mingw-w64-x86_64-cmake mingw-w64-x86_64-SDL2 mingw-w64-x86_64-gcc mingw-w64-x86_64-gtk3 mingw-w64-x86_64-libconfig # For 32bit: -pacman -S mingw-w64-i686-cmake +pacman -S mingw-w64-i686-cmake mingw-w64-x86_i686-SDL2 mingw-w64-x86_i686-gcc mingw-w64-x86_i686-gtk3 mingw-w64-x86_i686-libconfig ``` ##### Clone the repository @@ -158,61 +109,12 @@ After this the installer should appear in the current directory. ## Configuration -#### Create a configuration file -
- Linux - - - Copy default config file to `.config/lwp/lwp.cfg`: - ```shell - mkdir ~/.config/lwp - cp /etc/lwp.cfg ~/.config/lwp/lwp.cfg - ``` - -
-
- macOS - - - Copy default config file to `~/.config/lwp/lwp.cfg`: - ```zsh - mkdir -p ~/.config/lwp - cp /opt/lwp/lwp.cfg ~/.config/lwp/ - ``` -
-
- Windows - - - Press ⊞ Win + R - - Type `%appdata%` and press `Ok` - - Create new directory and name it `lwp` - - Copy file `C:\Program Files\lwp\defaultWin.cfg` to directory created in the previous step and rename it to `lwp.cfg` - - Open `lwp.cfg` in notepad - -
+### Open configuration window +- On Linux when `lwp` is already running in the background, run `lwp` again to show the window. +- On Windows You can click the icon on the right side of the task bar. -#### Using config file - -- Do not put spaces between `=` and values -- Do not leave trailing spaces -- Comments start with `#` -- Do not put strings in quotation marks - -#### Available options: - -| Type | Name | Description | -| ------ | ------------ | ----------- | -| int | reload_rootwindow | Set this to 1 if You are using a compositor (linux only) | -| float | smooth | Smooth movement multipler | -| int | monitors | Monitors count | -| int | monitor[n]_x | Position of nth monitor in X axis | -| int | monitor[n]_y | Position of nth monitor in Y axis | -| int | monitor[n]_w | Width of nth monitor | -| int | monitor[n]_h | Height of nth monitor | -| string | monitor[n]_wallpaper | Absolute path to the wallpaper directory | -| int | monitor[n]_wallpaper_x | Position of the wallpaper relative to the monitor | -| int | monitor[n]_wallpaper_y | Position of the wallpaper relative to the monitor | -| int | monitor[n]_wallpaper_w | Wallpaper resolution | -| int | monitor[n]_wallpaper_h | Wallpaper resolution | -| int | target_fps | How many times per second should the wallpaper render (imprecise, hence "target") | +### Installing wallpapers +- To install wallpaper, copy the directory to `%LOCALAPPDATA%\lwp\wallpapers\` ## Creating Wallpapers @@ -244,8 +146,6 @@ C: | int | count | Wallpaper layers count | | float | movement_x | Mouse sensitivity in X axis | | float | movement_y | Mouse sensitivity in Y axis | -| float | movement[n]_x | Mouse sensitivity in X axis for nth layer (optional)| -| float | movement[n]_y | Mouse sensitivity in Y axis for nth layer (optional)| | int | repeat_x | Repeat the wallpaper in X axis | | int | repeat_y | Repeat the wallpaper in Y axis | From 389bceee7014e967faadfce3c47ad1a526c6904e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Szczerbi=C5=84ski?= Date: Sat, 9 Mar 2024 16:42:21 +0100 Subject: [PATCH 33/33] Removed unnecessary files --- default.cfg | 2 -- defaultMac.cfg | 36 ---------------------- defaultWin.cfg | 36 ---------------------- dmgBg.png | Bin 172756 -> 0 bytes install.bat | 72 ------------------------------------------- lwp.template.plist | 22 ------------- setupDmg.applescript | 21 ------------- setupPlist.command | 16 ---------- uninstall.bat | 26 ---------------- 9 files changed, 231 deletions(-) delete mode 100644 default.cfg delete mode 100644 defaultMac.cfg delete mode 100644 defaultWin.cfg delete mode 100644 dmgBg.png delete mode 100644 install.bat delete mode 100644 lwp.template.plist delete mode 100755 setupDmg.applescript delete mode 100755 setupPlist.command delete mode 100644 uninstall.bat diff --git a/default.cfg b/default.cfg deleted file mode 100644 index ab24da3..0000000 --- a/default.cfg +++ /dev/null @@ -1,2 +0,0 @@ -draw_on_rootwindow= 0; -target_fps = 60; diff --git a/defaultMac.cfg b/defaultMac.cfg deleted file mode 100644 index 88372be..0000000 --- a/defaultMac.cfg +++ /dev/null @@ -1,36 +0,0 @@ -# ██╗ ██╗ ██╗██████╗ -# ██║ ██║ ██║██╔══██╗ -# ██║ ██║ █╗ ██║██████╔╝ -# ██║ ██║███╗██║██╔═══╝ -# ███████╗╚███╔███╔╝██║ -# ╚══════╝ ╚══╝╚══╝ ╚═╝ - -# Smooth movement -# Increasing this value will make the wallpaper layers move faster -smooth=8.0 - -# Monitors count -monitors=1 - -# Monitor position -monitor1_x=0 -monitor1_y=0 - -# Monitor resolution -monitor1_w=1920 -monitor1_h=1080 - -# Absolute path to the wallpaper directory -# Leave the value as empty to use the default wallpaper -monitor1_wallpaper=/Applications/Layered_WallPaper/wallpapers/default-fullhd - -# Wallpaper size and position relative to Your monitor -# Wallpaper resolution ratio should be the same as in original image -monitor1_wallpaper_x=0 -monitor1_wallpaper_y=0 -monitor1_wallpaper_w=1920 -monitor1_wallpaper_h=1080 - -# How many times per second should the wallpaper render -# (imprecise, but accurate enough) -target_fps=60 diff --git a/defaultWin.cfg b/defaultWin.cfg deleted file mode 100644 index 761e0fc..0000000 --- a/defaultWin.cfg +++ /dev/null @@ -1,36 +0,0 @@ -# ██╗ ██╗ ██╗██████╗ -# ██║ ██║ ██║██╔══██╗ -# ██║ ██║ █╗ ██║██████╔╝ -# ██║ ██║███╗██║██╔═══╝ -# ███████╗╚███╔███╔╝██║ -# ╚══════╝ ╚══╝╚══╝ ╚═╝ - -# Smooth movement -# Increasing this value will make the wallpaper layers move faster -smooth=8.0 - -# Monitors count -monitors=1 - -# Monitor position -monitor1_x=0 -monitor1_y=0 - -# Monitor resolution -monitor1_w=1920 -monitor1_h=1080 - -# Absolute path to the wallpaper directory -# Leave the value as empty to use the default wallpaper -monitor1_wallpaper= - -# Wallpaper size and position relative to Your monitor -# Wallpaper resolution ratio should be the same as in original image -monitor1_wallpaper_x=0 -monitor1_wallpaper_y=0 -monitor1_wallpaper_w=1920 -monitor1_wallpaper_h=1080 - -# How many times per second should the wallpaper render -# (imprecise, but accurate enough) -target_fps=60 diff --git a/dmgBg.png b/dmgBg.png deleted file mode 100644 index 99bf59214fc3a82c8ecc28e7799d2dd6e984491c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 172756 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 !e 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 J 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 z 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 2>&1 -if not %ERRORLEVEL% equ 0 ( - echo Run me as admin - pause>nul - exit -) - -call :logo -echo. -echo If You enjoy lwp, You can give it a start on github -echo https://github.com/jszczerbinsky/lwp -echo. -echo Lwp will be installed on Your computer -echo Click any key to display the license -pause > nul - -cls -echo ________________________________________________________________________________________________________________________ -type "%~dp0\data\LICENSE.txt" -echo ________________________________________________________________________________________________________________________ -echo. -echo By continuing the installation You accept the license conditions -echo. -set /p val="Continue? (y/n) > " -if not %val%==y ( - call :logo - echo You have to accept the license to continue installation - echo Installator will exit now - echo. - echo Press any key to continue... - pause>nul - exit -) -call :logo -echo Starting installation -echo. -path=%ProgramFiles% -echo Copying files... -robocopy "%~dp0\data" "%path%\lwp" /E /NFL /NDL /NJH /NJS /nc /ns /np -echo Adding lwp to autorun... -reg add "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run" /v "lwp" /t REG_EXPAND_SZ /d "%path%\lwp\lwp.exe" /f > nul -echo Creating uninstaller... -reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\layeredWallPaper" /f > nul -reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\layeredWallPaper" /v "UninstallString" /t REG_SZ /d "cmd /c \"%path%\lwp\uninstall.bat\"" /f > nul -reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\layeredWallPaper" /v "DisplayName" /t REG_SZ /d "Layered WallPaper" /f > nul -reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\layeredWallPaper" /v "DisplayIcon" /t REG_SZ /d "%path%\lwp\lwp.exe" /f > nul -reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\layeredWallPaper" /v "Publisher" /t REG_SZ /d "Jakub Szczerbinski" /f > nul -reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\layeredWallPaper" /v "URLInfoAbout" /t REG_SZ /d "https://github.com/jszczerbinsky/lwp" /f > nul - -echo. -echo Lwp has been successfuly installed on Your computer! -start "" "%path%\lwp\lwp.exe" -pause > nul -exit - -:logo -cls -echo. -echo. -echo " __ _ ______ " -echo " / /| | / / __ \ " -echo " / / | | /| / / /_/ / " -echo " / /__| |/ |/ / ____/ " -echo " /_____/__/|__/_/ " -echo. -echo. -exit /B 0 \ No newline at end of file diff --git a/lwp.template.plist b/lwp.template.plist deleted file mode 100644 index c10c25f..0000000 --- a/lwp.template.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - Label - jszczerbinsky.lwp - - LaunchOnlyOnce - - - RunAtLoad - - - UserName - @user@ - - ProgramArguments - - @binpath@ - - - diff --git a/setupDmg.applescript b/setupDmg.applescript deleted file mode 100755 index d3bdc84..0000000 --- a/setupDmg.applescript +++ /dev/null @@ -1,21 +0,0 @@ -on run argv - tell application "Finder" - tell disk (item 1 of argv) - open - set current view of container window to icon view - set toolbar visible of container window to false - set statusbar visible of container window to false - set the bounds of container window to {400, 100, 1000, 530} - set theViewOptions to the icon view options of container window - set arrangement of theViewOptions to not arranged - set icon size of theViewOptions to 72 - set background picture of theViewOptions to file ".background:background.png" - set position of item "Applications" of container window to {450,120} - set position of item "Layered_WallPaper" of container window to {130,120} - set position of item "Toggle_Autorun.command" of container window to {260,330} - update without registering applications - delay 5 - close - end tell - end tell -end run diff --git a/setupPlist.command b/setupPlist.command deleted file mode 100755 index 143007b..0000000 --- a/setupPlist.command +++ /dev/null @@ -1,16 +0,0 @@ -clear - -if [[ ! -a ~/Library/LaunchAgents/lwp.plist ]]; then - mkdir -p ~/Library/LaunchAgents - cp /Applications/Layered_WallPaper/lwp.template.plist ~/Library/LaunchAgents/lwp.plist - sed -i "" "s/@user@/$(whoami)/g" ~/Library/LaunchAgents/lwp.plist - sed -i "" "s^@binpath@^/Applications/Layered_WallPaper/lwp.app/Contents/MacOS/lwp^g" ~/Library/LaunchAgents/lwp.plist - launchctl load ~/Library/LaunchAgents/lwp.plist - - echo Layered WallPaper has been added to autostart -else - launchctl unload ~/Library/LaunchAgents/lwp.plist - rm ~/Library/LaunchAgents/lwp.plist - - echo Layered WallPaper has been removed from autostart -fi diff --git a/uninstall.bat b/uninstall.bat deleted file mode 100644 index 794b279..0000000 --- a/uninstall.bat +++ /dev/null @@ -1,26 +0,0 @@ -@echo off -title Uninstall Layered WallPaper - -echo Are You sure, You want to remove Layered WallPaper from Your computer? -set /p val="(y/n) > " -if not %val%==y ( - exit -) - -echo Killing all running instances... -taskkill /F /IM lwp.exe -echo. - -echo Restarting explorer... -taskkill /im explorer.exe /f -start explorer.exe -echo. - -echo Removing Layered WallPaper's registry keys.. -reg delete "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\layeredWallPaper" /f > nul -reg delete "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run" /v "lwp" /f > nul -echo. - -echo Removing Layered WallPaper's files... -rmdir /S /Q "%~dp0" -exit \ No newline at end of file