From 4d796dabafdee54a7b726013416730efaafa905e Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Mon, 6 Jun 2022 23:56:03 -0400 Subject: [PATCH] Add some curses-related feature tests. Add helper macros to check the existence of specific functions in the curses library, and also a helper to check for mouse support functions. --- m4/curses-compat.m4 | 93 ++++++++++++++++++++++++++++++ m4/curses.m4 | 3 +- t/libdummy.c | 9 +++ tests/libs.at | 134 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 m4/curses-compat.m4 diff --git a/m4/curses-compat.m4 b/m4/curses-compat.m4 new file mode 100644 index 0000000..c445a67 --- /dev/null +++ b/m4/curses-compat.m4 @@ -0,0 +1,93 @@ +dnl Copyright © 2022 Nick Bowler +dnl +dnl Macros for probing specific curses library behaviour. +dnl +dnl License WTFPL2: Do What The Fuck You Want To Public License, version 2. +dnl This is free software: you are free to do what the fuck you want to. +dnl There is NO WARRANTY, to the extent permitted by law. + +dnl DX_CHECK_CURSES_FUNC(function, [args], [prologue]) +dnl +dnl Simple yes/no check for whether the curses library provides the specified +dnl function. If it does, the macro HAVE_CURSES_func macro is defined to 1, +dnl where func is the specified function name in capital letters. The cache +dnl variable dx_cv_curses_have_func is set to "yes" if the function is +dnl detected, or "no" otherwise. +dnl +dnl The second argument is the argument list (without its enclosing +dnl parentheses) used to construct a syntactically valid function call. +dnl Typically most arguments can simply be 0. +dnl +dnl The third argument is additional C code to be placed before the function +dnl call within main. This can include declarations and statements. +AC_DEFUN([DX_CHECK_CURSES_FUNC], +[_DX_CURSES_LINKTEST([supports $1], [$1], [$3; $1($2)])]) + +dnl DX_CHECK_CURSES_GETMOUSE_NCURSES +dnl +dnl Check if the curses library provides the getmouse function with an +dnl ncurses-compatible interface. Some other curses libraries also have +dnl a function named getmouse but with a totally different interface. +dnl +dnl If so, the macro HAVE_CURSES_GETMOUSE_NCURSES is defined to 1 and the +dnl cache variable dx_cv_curses_have_getmouse_ncurses is set to "yes". +dnl Otherwise the cache variable is set to "no". +AC_DEFUN([DX_CHECK_CURSES_GETMOUSE_NCURSES], +[_DX_CURSES_LINKTEST([has ncurses-like getmouse], [getmouse_ncurses], +[MEVENT ev; { extern int getmouse(MEVENT *); getmouse(&ev); } getmouse(&ev)])]) + +dnl DX_CHECK_CURSES_MOUSE_SUPPORT +dnl +dnl Probe a set of mouse-related curses functions to figure out what is +dnl needed for basic mouse support. This is implemented using the preceding +dnl macros and sets the same outputs. +dnl +dnl Additionally, the HAVE_CURSES_MOUSE_SUPPORT macro is defined to 1 if: +dnl +dnl - one of mouse_set or mousemask is found, and +dnl - one of ncurses-style getmouse or request_mouse_pos is found. +dnl +dnl Currently, this probes the following functions, in order: +dnl +dnl - mouse_set +dnl - mousemask +dnl - mouseinterval +dnl - getmouse (with ncurses interface) +dnl - request_mouse_pos +dnl +dnl To save time, not all functions are probed; only the minimum required to +dnl determine that curses does (or does not) have usable mouse support. As a +dnl result, libraries that support multiple interfaces such as pdcurses might +dnl not have every supported function reflected in the configuration macros. +AC_DEFUN([DX_CHECK_CURSES_MOUSE_SUPPORT], +[DX_CHECK_CURSES_FUNC([mouse_set], [BUTTON1_PRESSED]) +AS_IF([test x"$dx_cv_curses_have_mouse_set" != x"yes"], + [DX_CHECK_CURSES_FUNC([mousemask], [BUTTON1_PRESSED,0])]) +AS_CASE([$dx_cv_curses_have_mouse_set$dx_cv_curses_have_mousemask], +[*yes*], [DX_CHECK_CURSES_FUNC([mouseinterval], [0]) +DX_CHECK_CURSES_GETMOUSE_NCURSES +AS_IF([test x"$dx_cv_curses_have_getmouse_ncurses" != x"yes"], + [DX_CHECK_CURSES_FUNC([request_mouse_pos], [], + [switch (BUTTON_STATUS(1)) case BUTTON_PRESSED:;])]) +AS_CASE( +[$dx_cv_curses_have_getmouse_ncurses$dx_cv_curses_have_request_mouse_pos], + [*yes*], [AC_DEFINE([HAVE_CURSES_MOUSE_SUPPORT], [1], + [Define to 1 if curses has usable mouse support.])])])]) + +dnl _DX_CURSES_LINKTEST(message, key, body) +dnl +dnl Internal macro common to the tests above. "message" is used to construct +dnl the configure output and config.h template description; "key" is used to +dnl construct macro and cache variable names; "body" is the C function body +dnl to compile. +AC_DEFUN([_DX_CURSES_LINKTEST], +[AC_CACHE_CHECK([whether curses $1], [AS_TR_SH([dx_cv_curses_have_$2])], +[_dx_save_CFLAGS=$CFLAGS _dx_save_LIBS=$LIBS +CFLAGS="$CFLAGS $CURSES_CFLAGS" LIBS="$CURSES_LIBS" +AC_LINK_IFELSE([AC_LANG_PROGRAM([#include +], [$3;])], [AS_TR_SH([dx_cv_curses_have_$2])=yes], +[AS_TR_SH([dx_cv_curses_have_$2])=no]) +CFLAGS=$_dx_save_CFLAGS LIBS=$_dx_save_LIBS]) +AS_IF([test x"$AS_TR_SH([dx_cv_curses_have_$2])" = x"yes"], + [AC_DEFINE(AS_TR_CPP([HAVE_CURSES_$2]), [1], + [Define to 1 if your curses library $1])])]) diff --git a/m4/curses.m4 b/m4/curses.m4 index f03711c..57aa76e 100644 --- a/m4/curses.m4 +++ b/m4/curses.m4 @@ -5,7 +5,8 @@ dnl This is free software: you are free to do what the fuck you want to. dnl There is NO WARRANTY, to the extent permitted by law. AC_DEFUN([DX_LIB_CURSES], -[DX_LIB_SETUP([curses])dnl +[AC_BEFORE([$0], [_DX_CURSES_LINKTEST])dnl +DX_LIB_SETUP([curses])dnl DX_LIB_PKGCONFIG_FLAGS([ncurses]) AC_MSG_CHECKING([for curses]) DX_LIB_SEARCH_LINK([curses], diff --git a/t/libdummy.c b/t/libdummy.c index dfa2c7d..3e40d13 100644 --- a/t/libdummy.c +++ b/t/libdummy.c @@ -5,3 +5,12 @@ void discid_free(void) {} void curs_set(int x) {} void g_get_prgname(void) {} + +int mouse_set(unsigned long mask) { return 0; } +int request_mouse_pos(void) { return 0; } +unsigned getmouse(void) { return 0; } +unsigned long mousemask(unsigned long a, unsigned long *b) { return 0; } + +struct MEVENT; +int getmouse_nc_(struct MEVENT *mev) { return 0; } +int getmouse_bogus_(void *p) { return 0; } diff --git a/tests/libs.at b/tests/libs.at index 89ecf67..1d327cc 100644 --- a/tests/libs.at +++ b/tests/libs.at @@ -141,6 +141,140 @@ AT_CHECK_UNQUOTED([cat test], [0], [no AT_CLEANUP +AT_SETUP([DX_CHECK_CURSES_GETMOUSE_NCURSES]) +AT_KEYWORDS([lib curses ncurses]) + +mkdir ncurses sysv bogus +AT_DATA([ncurses/curses.h], +[[typedef struct MEVENT { int x; } MEVENT; +int getmouse_nc_(MEVENT *); +#define getmouse getmouse_nc_ +]]) + +AT_DATA([sysv/curses.h], +[[unsigned getmouse(void); +]]) + +AT_DATA([bogus/curses.h], +[[typedef int MEVENT; +int getmouse_bogus_(void *); +#define getmouse getmouse_bogus_ +]]) + +AT_DATA([test.in], +[[@dx_cv_curses_have_getmouse_ncurses@ +]]) + +TEST_CONFIGURE_AC([[AC_CONFIG_HEADERS([config.h]) +DX_CHECK_CURSES_GETMOUSE_NCURSES +AC_SUBST([dx_cv_curses_have_getmouse_ncurses]) +AC_CONFIG_FILES([test]) +]]) +TEST_AUTORECONF + +TEST_CONFIGURE([CURSES_CFLAGS=-Incurses CURSES_LIBS="$builddir/t/libdummy.a"]) +AT_CHECK([cat test && grep HAVE_CURSES config.h], [0], [yes +#define HAVE_CURSES_GETMOUSE_NCURSES 1 +]) + +TEST_CONFIGURE([CURSES_CFLAGS=-Isysv CURSES_LIBS="$builddir/t/libdummy.a"]) +AT_CHECK([cat test && grep HAVE_CURSES config.h], [0], [no +/* #undef HAVE_CURSES_GETMOUSE_NCURSES */ +]) + +TEST_CONFIGURE([CURSES_CFLAGS=-Ibogus CURSES_LIBS="$builddir/t/libdummy.a"]) +AT_CHECK([cat test && grep HAVE_CURSES config.h], [0], [no +/* #undef HAVE_CURSES_GETMOUSE_NCURSES */ +]) + +AT_CLEANUP + +AT_SETUP([DX_CHECK_CURSES_MOUSE_SUPPORT]) +AT_KEYWORDS([lib curses ncurses]) + +mkdir hp ncurses sysv + +# HP-UX 11 curses contains definitions of mouse functions in the library but +# is missing declarations and associated macros in , although they +# are present in the alternate "cur_colr" implementation. +# +# Regardless, neither implementation seems to have working mouse support with +# any X terminal emulator so for now no workarounds are provided and the checks +# are expected to return "no". +AT_DATA([hp/curses.h], +[[int mouse_set(unsigned long); +int request_mouse_pos(void); +]]) + +# Ncurses-like mouse functions. +AT_DATA([ncurses/curses.h], +[[unsigned long mousemask(unsigned long, unsigned long *); +typedef int MEVENT; +int getmouse_nc_(MEVENT *); +#define getmouse getmouse_nc_ +#define BUTTON1_RELEASED 1L +#define BUTTON1_PRESSED 2L + +/* rename this to avoid false positives in the test */ +#define mouse_set mouse_set_bogus_ +]]) + +# SysV-like mouse funcitons. +AT_DATA([sysv/curses.h], +[[int mouse_set(unsigned long); +int request_mouse_pos(void); + +#define BUTTON_STATUS(x) 0 +#define BUTTON_RELEASED 0 +#define BUTTON_PRESSED 1 + +#define BUTTON1_RELEASED 1L +#define BUTTON1_PRESSED 2L +]]) + +AT_DATA([test.in], +[[@dx_cv_curses_have_mouse_set@ +@dx_cv_curses_have_mousemask@ +@dx_cv_curses_have_getmouse_ncurses@ +@dx_cv_curses_have_request_mouse_pos@ +]]) + +TEST_CONFIGURE_AC([[AC_CONFIG_HEADERS([config.h]) +DX_CHECK_CURSES_MOUSE_SUPPORT +AC_SUBST([dx_cv_curses_have_mouse_set]) +AC_SUBST([dx_cv_curses_have_mousemask]) +AC_SUBST([dx_cv_curses_have_request_mouse_pos]) +AC_SUBST([dx_cv_curses_have_getmouse_ncurses]) +AC_CONFIG_FILES([test]) +]]) +TEST_AUTORECONF + +TEST_CONFIGURE([CURSES_CFLAGS=-Ihp CURSES_LIBS="$builddir/t/libdummy.a"]) +AT_CHECK([cat test && grep CURSES_MOUSE_SUPPORT config.h], [0], [no +no + + +/* #undef HAVE_CURSES_MOUSE_SUPPORT */ +]) + +TEST_CONFIGURE([CURSES_CFLAGS=-Incurses CURSES_LIBS="$builddir/t/libdummy.a"]) +AT_CHECK([cat test && grep CURSES_MOUSE_SUPPORT config.h], [0], [no +yes +yes + +#define HAVE_CURSES_MOUSE_SUPPORT 1 +]) + +TEST_CONFIGURE([CURSES_CFLAGS=-Isysv CURSES_LIBS="$builddir/t/libdummy.a"]) +AT_CHECK([cat test && grep CURSES_MOUSE_SUPPORT config.h], [0], [yes + +no +yes +#define HAVE_CURSES_MOUSE_SUPPORT 1 +]) + +AT_CLEANUP + AT_SETUP([GLib GNU inline workaround]) TEST_DUMMY_PKGCONFIG([-I.], [$builddir/t/libdummy.a]) -- 2.43.2