From ca706c21c01cb6648fa5f4dce752ee5aaf6d5e9c Mon Sep 17 00:00:00 2001 From: trim21 Date: Fri, 29 May 2026 14:13:06 +0800 Subject: [PATCH] feat: add --without-ncurses option for daemon-only builds Add a dummy curses stub header that provides all ncurses types, macros, and no-op function stubs. When configured with --without-ncurses, the build uses this stub instead of linking to the real ncurses library. This allows rtorrent to be built for daemon-only usage (e.g. with ruTorrent or Flood) without requiring ncurses to be installed. Closes #1613 --- configure.ac | 33 +++++++++--- src/Makefile.am | 4 ++ src/display/attributes.h | 4 +- src/display/color_map.h | 4 ++ src/display/curses_stub.cc | 6 +++ src/display/curses_stub.h | 107 +++++++++++++++++++++++++++++++++++++ 6 files changed, 151 insertions(+), 7 deletions(-) create mode 100644 src/display/curses_stub.cc create mode 100644 src/display/curses_stub.h diff --git a/configure.ac b/configure.ac index 83b7d78e..153298a6 100644 --- a/configure.ac +++ b/configure.ac @@ -41,10 +41,20 @@ AC_ARG_ENABLE(execinfo, ]) AX_PTHREAD([], AC_MSG_ERROR([requires pthread])) -AX_WITH_CURSES -if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then - AC_MSG_ERROR([requires either NcursesW or Ncurses library]) +AC_ARG_WITH([ncurses], + [AS_HELP_STRING([--without-ncurses], [build without ncurses (daemon-only mode)])], + [with_ncurses=$withval], + [with_ncurses=yes]) + +if test "x$with_ncurses" != xno; then + AX_WITH_CURSES + + if test "x$ax_cv_ncursesw" != xyes && test "x$ax_cv_ncurses" != xyes; then + AC_MSG_ERROR([requires either NcursesW or Ncurses library]) + fi +else + AC_DEFINE([HAVE_NO_NCURSES], [1], [Define to 1 if building without ncurses]) fi PKG_CHECK_MODULES([CPPUNIT], [cppunit],, [no_cppunit="yes"]) @@ -70,9 +80,20 @@ AC_DEFINE(USER_AGENT, [std::string(PACKAGE "/" VERSION)], Http user agent) dnl Only update global build variables immediately before generating the output, dnl to avoid affecting the global build environment for other autoconf checks. -LIBS="$PTHREAD_LIBS $CURSES_LIB $CURSES_LIBS $ZLIB_LIBS $DEPENDENCIES_LIBS $LIBS" -CFLAGS="$CFLAGS $PTHREAD_CFLAGS $CURSES_CFLAGS $ZLIB_CFLAGS $DEPENDENCIES_CFLAGS" -CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $CURSES_CFLAGS $ZLIB_CFLAGS $DEPENDENCIES_CFLAGS" +if test "x$with_ncurses" != xno; then + LIBS="$PTHREAD_LIBS $CURSES_LIB $CURSES_LIBS $ZLIB_LIBS $DEPENDENCIES_LIBS $LIBS" +else + LIBS="$PTHREAD_LIBS $ZLIB_LIBS $DEPENDENCIES_LIBS $LIBS" +fi +if test "x$with_ncurses" != xno; then + CFLAGS="$CFLAGS $PTHREAD_CFLAGS $CURSES_CFLAGS $ZLIB_CFLAGS $DEPENDENCIES_CFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $CURSES_CFLAGS $ZLIB_CFLAGS $DEPENDENCIES_CFLAGS" +else + CFLAGS="$CFLAGS $PTHREAD_CFLAGS $ZLIB_CFLAGS $DEPENDENCIES_CFLAGS" + CXXFLAGS="$CXXFLAGS $PTHREAD_CFLAGS $ZLIB_CFLAGS $DEPENDENCIES_CFLAGS" +fi + +AM_CONDITIONAL([NO_NCURSES], [test "x$with_ncurses" = xno]) TORRENT_CHECK_CACHELINE TORRENT_CHECK_POPCOUNT diff --git a/src/Makefile.am b/src/Makefile.am index 76018dbf..147a1997 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -205,4 +205,8 @@ libsub_root_a_SOURCES = \ signal_handler.h +if NO_NCURSES +libsub_root_a_SOURCES += display/curses_stub.cc display/curses_stub.h +endif + AM_CPPFLAGS = -I$(srcdir) -I$(top_srcdir) -DPACKAGE_DATADIR=\"$(pkgdatadir)\" diff --git a/src/display/attributes.h b/src/display/attributes.h index 5a170443..115a88eb 100644 --- a/src/display/attributes.h +++ b/src/display/attributes.h @@ -4,7 +4,9 @@ #include #include -#if defined(HAVE_NCURSESW_CURSES_H) +#if defined(HAVE_NO_NCURSES) +#include "curses_stub.h" +#elif defined(HAVE_NCURSESW_CURSES_H) #include #elif defined(HAVE_NCURSESW_H) #include diff --git a/src/display/color_map.h b/src/display/color_map.h index d91133ab..18b458e8 100644 --- a/src/display/color_map.h +++ b/src/display/color_map.h @@ -4,7 +4,11 @@ #include #include +#if defined(HAVE_NO_NCURSES) +#include "curses_stub.h" +#else #include +#endif namespace display { diff --git a/src/display/curses_stub.cc b/src/display/curses_stub.cc new file mode 100644 index 00000000..696bbcdf --- /dev/null +++ b/src/display/curses_stub.cc @@ -0,0 +1,6 @@ +// Dummy ncurses stub: provides the stdscr symbol expected by the codebase. + +#include "curses_stub.h" + +// Define stdscr as a null pointer; all ncurses calls are no-ops in this stub. +WINDOW* stdscr = nullptr; diff --git a/src/display/curses_stub.h b/src/display/curses_stub.h new file mode 100644 index 00000000..9a5c467b --- /dev/null +++ b/src/display/curses_stub.h @@ -0,0 +1,107 @@ +// Dummy ncurses stub header for building rtorrent without ncurses. +// Provides types, macros, and no-op function stubs so the codebase +// compiles without a real curses library. All display functions become +// no-ops; use with system.daemon.set = true. + +#ifndef RTORRENT_DISPLAY_CURSES_STUB_H +#define RTORRENT_DISPLAY_CURSES_STUB_H + +#include +#include +#include + +// Opaque window type +typedef void* WINDOW; + +// Character type used by curses +typedef unsigned int chtype; +typedef unsigned int attr_t; + +// The standard screen +extern WINDOW* stdscr; + +// Boolean constants and return codes +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef ERR +#define ERR (-1) +#endif + +// Attribute constants +#define A_NORMAL 0 +#define A_STANDOUT (1U << 8) +#define A_UNDERLINE (1U << 9) +#define A_REVERSE (1U << 10) +#define A_BLINK (1U << 11) +#define A_DIM (1U << 12) +#define A_BOLD (1U << 13) + +// Color support +#define COLORS 0 +#define COLOR_BLACK 0 +#define COLOR_RED 1 +#define COLOR_GREEN 2 +#define COLOR_YELLOW 3 +#define COLOR_BLUE 4 +#define COLOR_MAGENTA 5 +#define COLOR_CYAN 6 +#define COLOR_WHITE 7 + +#define COLOR_PAIR(n) 0 + +// Key constants +#define KEY_DOWN 0x102 +#define KEY_UP 0x103 +#define KEY_LEFT 0x104 +#define KEY_RIGHT 0x105 +#define KEY_HOME 0x106 +#define KEY_BACKSPACE 0x107 +#define KEY_DC 0x14a +#define KEY_ENTER 0x157 +#define KEY_NPAGE 0x152 +#define KEY_PPAGE 0x153 +#define KEY_END 0x168 +#define KEY_MOUSE 0x199 +#define KEY_RESIZE 0x19a + +// Stubbed functions +inline WINDOW* initscr() { return nullptr; } +inline int endwin() { return 0; } +inline WINDOW* newwin(int, int, int, int) { return nullptr; } +inline int delwin(WINDOW*) { return 0; } +inline int wresize(WINDOW*, int, int) { return 0; } +inline int mvwin(WINDOW*, int, int) { return 0; } +inline int wnoutrefresh(WINDOW*) { return 0; } +inline int redrawwin(WINDOW*) { return 0; } +inline int werase(WINDOW*) { return 0; } +inline int wmove(WINDOW*, int, int) { return 0; } +inline int vw_printw(WINDOW*, char*, va_list) { return 0; } +inline int waddch(WINDOW*, const chtype) { return 0; } +inline int mvwaddch(WINDOW*, int, int, const chtype) { return 0; } +inline int mvwchgat(WINDOW*, int, int, int, attr_t, short, const void*) { return 0; } +inline int wattrset(WINDOW*, int) { return 0; } +inline int wattr_get(WINDOW*, attr_t*, short*, void*) { return 0; } +inline int wattr_set(WINDOW*, attr_t, short, void*) { return 0; } +inline int doupdate() { return 0; } +inline int start_color() { return 0; } +inline int use_default_colors() { return 0; } +inline int raw() { return 0; } +inline int noraw() { return 0; } +inline int noecho() { return 0; } +inline int nodelay(WINDOW*, bool) { return 0; } +inline int keypad(WINDOW*, bool) { return 0; } +inline int curs_set(int) { return 0; } +inline int init_pair(short, short, short) { return 0; } +inline int pair_content(short, short*, short*) { return 0; } +inline int resizeterm(int, int) { return 0; } +inline int getch() { return ERR; } + +// getyx / getmaxyx are macros in real ncurses; stub them as no-ops +#define getyx(w, y, x) do { (y) = 0; (x) = 0; } while (0) +#define getmaxyx(w, y, x) do { (y) = 24; (x) = 80; } while (0) + +#endif // RTORRENT_DISPLAY_CURSES_STUB_H