# DWN - Desktop Window Manager # Simple Makefile - just run 'make' to build! # Compiler settings CC = gcc CFLAGS = -Wall -Wextra -Wpedantic -Wshadow -O2 -I./include CFLAGS += -fstack-protector-strong -D_FORTIFY_SOURCE=2 LDFLAGS = -lX11 -lXext -lXinerama -lXrandr -lXft -lfontconfig -ldbus-1 -lcurl -lm -lpthread -lXtst -lXi -ldl # Directories SRC_DIR = src INC_DIR = include BUILD_DIR = build BIN_DIR = bin # Find all source files automatically (including subdirectories) SRCS = $(shell find $(SRC_DIR) -name '*.c' -type f) OBJS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRCS)) DEPS = $(OBJS:.o=.d) # Output binary TARGET = $(BIN_DIR)/dwn # Installation paths PREFIX ?= /usr/local BINDIR = $(PREFIX)/bin DATADIR = $(PREFIX)/share SYSCONFDIR = /etc # Get pkg-config flags (with error handling) PKG_CFLAGS := $(shell pkg-config --cflags x11 xext xinerama xrandr xft fontconfig dbus-1 xtst xi libpng tesseract lept 2>/dev/null) PKG_LIBS := $(shell pkg-config --libs x11 xext xinerama xrandr xft fontconfig dbus-1 xtst xi libpng tesseract lept 2>/dev/null) # Use pkg-config if available ifneq ($(PKG_LIBS),) LDFLAGS = $(PKG_LIBS) -lcurl -lm -lpthread endif ifneq ($(PKG_CFLAGS),) CFLAGS += $(PKG_CFLAGS) endif # ============================================================================= # MAIN TARGETS # ============================================================================= .PHONY: all help clean install uninstall debug sanitize run test deps check-deps # Default target - show help if first time, otherwise build all: check-deps $(TARGET) @echo "" @echo "Build successful! Binary created at: $(TARGET)" @echo "" @echo "Next steps:" @echo " make run - Test DWN in a window (safe, doesn't affect your desktop)" @echo " make install - Install DWN system-wide" @echo "" # Show help help: @echo "==============================================" @echo " DWN - Desktop Window Manager" @echo "==============================================" @echo "" @echo "QUICK START:" @echo " make deps - Install required dependencies (Ubuntu/Debian)" @echo " make - Build DWN" @echo " make run - Test DWN in a safe window" @echo " make install - Install to your system" @echo "" @echo "ALL COMMANDS:" @echo " make - Build DWN (release version)" @echo " make debug - Build with debug symbols" @echo " make sanitize - Build with address/UB sanitizers" @echo " make run - Test in Xephyr window (safe!)" @echo " make install - Install to $(BINDIR)" @echo " make uninstall- Remove from system" @echo " make clean - Remove build files" @echo " make deps - Install dependencies (needs sudo)" @echo " make help - Show this help" @echo "" @echo "API TESTING:" @echo " make test - Run API integration tests" @echo " make test-quick - Run quick tests (skip OCR)" @echo " make test-coverage- Run tests with coverage report" @echo "" @echo "AFTER INSTALL:" @echo " 1. Log out of your current session" @echo " 2. At login screen, select 'DWN' as your session" @echo " 3. Log in and enjoy!" @echo "" # ============================================================================= # BUILD TARGETS # ============================================================================= # Build with debug symbols debug: CFLAGS += -g -DDEBUG debug: clean $(TARGET) @echo "Debug build complete!" # Build with address and undefined behavior sanitizers sanitize: CFLAGS += -g -fsanitize=address,undefined -fno-omit-frame-pointer sanitize: LDFLAGS += -fsanitize=address,undefined sanitize: clean $(TARGET) @echo "Sanitizer build complete!" @echo "Run with: ASAN_OPTIONS=detect_leaks=1 ./$(TARGET)" # Link all object files into final binary $(TARGET): $(OBJS) | $(BIN_DIR) @echo "Linking..." @$(CC) $(OBJS) -o $@ $(LDFLAGS) # Compile each source file $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR) @echo "Compiling $<..." @mkdir -p $(dir $@) @$(CC) $(CFLAGS) -MMD -MP -c $< -o $@ # Create build directory $(BUILD_DIR): @mkdir -p $(BUILD_DIR) # Create bin directory $(BIN_DIR): @mkdir -p $(BIN_DIR) # Include dependency files -include $(DEPS) # ============================================================================= # INSTALL / UNINSTALL # ============================================================================= install: $(TARGET) @echo "Installing DWN..." @install -Dm755 $(TARGET) $(DESTDIR)$(BINDIR)/dwn @install -Dm644 examples/dwn.desktop $(DESTDIR)$(DATADIR)/xsessions/dwn.desktop @mkdir -p $(DESTDIR)$(SYSCONFDIR)/dwn @install -Dm644 config/config.example $(DESTDIR)$(SYSCONFDIR)/dwn/config.example @echo "" @echo "==============================================" @echo " Installation complete!" @echo "==============================================" @echo "" @echo "To configure DWN, run:" @echo " mkdir -p ~/.config/dwn" @echo " cp $(SYSCONFDIR)/dwn/config.example ~/.config/dwn/config" @echo "" @echo "Then log out and select 'DWN' at the login screen!" @echo "" uninstall: @echo "Removing DWN..." @rm -f $(DESTDIR)$(BINDIR)/dwn @rm -f $(DESTDIR)$(DATADIR)/xsessions/dwn.desktop @rm -rf $(DESTDIR)$(SYSCONFDIR)/dwn @echo "DWN has been uninstalled." # ============================================================================= # DEVELOPMENT / TESTING # ============================================================================= # Test DWN in a nested X server (safe - doesn't touch your desktop) run: $(TARGET) @echo "Starting DWN in test mode..." @echo "(Press Alt+Shift+Q inside the window to quit)" Xephyr :1 -screen 1280x720 & sleep 1 DISPLAY=:1 $(TARGET) # Clean build files clean: @echo "Cleaning build files..." @rm -rf $(BUILD_DIR) $(BIN_DIR) @echo "Clean complete." # ============================================================================= # DEPENDENCY MANAGEMENT # ============================================================================= # Check if dependencies are installed check-deps: @command -v pkg-config >/dev/null 2>&1 || { \ echo "ERROR: pkg-config is not installed!"; \ echo "Run: make deps"; \ exit 1; \ } @pkg-config --exists x11 2>/dev/null || { \ echo "ERROR: X11 development libraries not found!"; \ echo "Run: make deps"; \ exit 1; \ } @pkg-config --exists dbus-1 2>/dev/null || { \ echo "ERROR: D-Bus development libraries not found!"; \ echo "Run: make deps"; \ exit 1; \ } # Install dependencies (Ubuntu/Debian) deps: @echo "Installing dependencies..." @echo "This requires sudo (administrator) access." @echo "" @if command -v apt >/dev/null 2>&1; then \ sudo apt update && sudo apt install -y \ build-essential \ pkg-config \ libx11-dev \ libxext-dev \ libxinerama-dev \ libxrandr-dev \ libxft-dev \ libxtst-dev \ libfontconfig1-dev \ libdbus-1-dev \ libcurl4-openssl-dev \ libpng-dev \ libtesseract-dev \ libleptonica-dev \ tesseract-ocr \ tesseract-ocr-eng \ xserver-xephyr \ dmenu; \ echo ""; \ echo "Dependencies installed successfully!"; \ echo "Now run: make"; \ elif command -v dnf >/dev/null 2>&1; then \ sudo dnf install -y \ gcc make \ pkg-config \ libX11-devel \ libXext-devel \ libXinerama-devel \ libXrandr-devel \ libXtst-devel \ dbus-devel \ libcurl-devel \ libpng-devel \ tesseract-devel \ leptonica-devel \ tesseract-langpack-eng \ xorg-x11-server-Xephyr \ dmenu; \ echo ""; \ echo "Dependencies installed successfully!"; \ echo "Now run: make"; \ elif command -v pacman >/dev/null 2>&1; then \ sudo pacman -S --needed \ base-devel \ pkg-config \ libx11 \ libxext \ libxinerama \ libxrandr \ libxtst \ dbus \ curl \ libpng \ tesseract \ tesseract-data-eng \ leptonica \ xorg-server-xephyr \ dmenu; \ echo ""; \ echo "Dependencies installed successfully!"; \ echo "Now run: make"; \ else \ echo "ERROR: Could not detect package manager!"; \ echo "Please install these packages manually:"; \ echo " - GCC and Make"; \ echo " - pkg-config"; \ echo " - X11, Xext, Xinerama, Xrandr, Xtst development libraries"; \ echo " - D-Bus development library"; \ echo " - libcurl development library"; \ echo " - Xephyr (for testing)"; \ echo " - dmenu (for app launcher)"; \ exit 1; \ fi # ============================================================================= # API INTEGRATION TESTS # ============================================================================= TEST_PORT ?= 18777 TEST_DISPLAY ?= :99 .PHONY: test test-quick test-coverage test-isolated test: @echo "Running API integration tests..." @echo "Note: DWN must be running with API enabled (port 8777)" @cd tests && python -m pytest -v test-quick: @echo "Running quick API tests (skipping OCR)..." @cd tests && python -m pytest -v -x --ignore=test_ocr_commands.py test-coverage: @echo "Running API tests with coverage..." @cd tests && python -m pytest --cov=. --cov-report=term-missing test-isolated: $(TARGET) @echo "Starting isolated DWN test instance on port $(TEST_PORT)..." @-kill $$(cat /tmp/dwn_test_dwn.pid 2>/dev/null) 2>/dev/null; true @-kill $$(cat /tmp/dwn_test_xephyr.pid 2>/dev/null) 2>/dev/null; true @rm -f /tmp/dwn_test_xephyr.pid /tmp/dwn_test_dwn.pid /tmp/dwn_test.log @sleep 1 @Xephyr $(TEST_DISPLAY) -screen 1280x720 2>/dev/null & echo $$! > /tmp/dwn_test_xephyr.pid @sleep 2 @DISPLAY=$(TEST_DISPLAY) $(TARGET) -p $(TEST_PORT) > /tmp/dwn_test.log 2>&1 & echo $$! > /tmp/dwn_test_dwn.pid @sleep 3 @for i in 1 2 3 4 5; do \ if curl -s http://localhost:$(TEST_PORT)/api/status >/dev/null 2>&1; then \ echo "DWN API ready on port $(TEST_PORT)"; \ break; \ fi; \ echo "Waiting for DWN API... ($$i/5)"; \ sleep 1; \ done @if ! curl -s http://localhost:$(TEST_PORT)/api/status >/dev/null 2>&1; then \ echo "ERROR: DWN API not responding. Check /tmp/dwn_test.log"; \ cat /tmp/dwn_test.log 2>/dev/null | tail -50; \ kill $$(cat /tmp/dwn_test_dwn.pid 2>/dev/null) 2>/dev/null || true; \ kill $$(cat /tmp/dwn_test_xephyr.pid 2>/dev/null) 2>/dev/null || true; \ exit 1; \ fi @DWN_TEST_PORT=$(TEST_PORT) python -m pytest tests/ -v; \ EXIT_CODE=$$?; \ kill $$(cat /tmp/dwn_test_dwn.pid 2>/dev/null) 2>/dev/null || true; \ kill $$(cat /tmp/dwn_test_xephyr.pid 2>/dev/null) 2>/dev/null || true; \ rm -f /tmp/dwn_test_xephyr.pid /tmp/dwn_test_dwn.pid; \ exit $$EXIT_CODE # ============================================================================= # CODE QUALITY (for developers) # ============================================================================= .PHONY: format check format: @echo "Formatting code..." @clang-format -i $(SRC_DIR)/*.c $(INC_DIR)/*.h 2>/dev/null || \ echo "Note: clang-format not installed (optional)" check: @echo "Running static analysis..." @cppcheck --enable=all --inconclusive -I$(INC_DIR) $(SRC_DIR) 2>/dev/null || \ echo "Note: cppcheck not installed (optional)"