--- /dev/null
+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 <curses.h>
+], [$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])])])
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],
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; }
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 <curses.h>, 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])