Compare commits

...

12 Commits

12 changed files with 91 additions and 60 deletions

27
.clang-format Normal file
View File

@@ -0,0 +1,27 @@
# clang-format is fairly limited, and these rules are approximate:
# - array initializers can get terribly mangled with clang-format 12.0,
# - sometimes it still aligns with space characters,
# - struct name NL { NL ... NL } NL name; is unachievable.
BasedOnStyle: GNU
ColumnLimit: 80
IndentWidth: 4
TabWidth: 4
UseTab: ForContinuationAndIndentation
BreakBeforeBraces: Allman
SpaceAfterCStyleCast: true
AlignAfterOpenBracket: DontAlign
AlignOperands: DontAlign
AlignConsecutiveMacros: Consecutive
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
IndentGotoLabels: false
# IncludeCategories has some potential, but it may also break the build.
# Note that the documentation says the value should be "Never".
SortIncludes: false
# This is a compromise, it generally works out aesthetically better.
BinPackArguments: false
# Unfortunately, this can't be told to align to column 40 or so.
SpacesBeforeTrailingComments: 2

2
.gitignore vendored
View File

@@ -7,3 +7,5 @@
/termo.files /termo.files
/termo.creator* /termo.creator*
/termo.includes /termo.includes
/termo.cflags
/termo.cxxflags

View File

@@ -1,21 +1,13 @@
project (termo C) cmake_minimum_required (VERSION 3.0)
cmake_minimum_required (VERSION 2.8.5) project (termo VERSION 0.1.0 LANGUAGES C)
if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC) if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -Wextra") set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -Wextra")
endif ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC) endif ()
# Version # Version
set (project_VERSION_MAJOR 0) set (project_API_VERSION ${PROJECT_VERSION_MAJOR})
set (project_VERSION_MINOR 1)
set (project_VERSION_PATCH 0)
set (project_VERSION ${project_VERSION_MAJOR})
set (project_VERSION ${project_VERSION}.${project_VERSION_MINOR})
set (project_VERSION ${project_VERSION}.${project_VERSION_PATCH})
set (project_API_VERSION ${project_VERSION_MAJOR})
# Names # Names
set (project_LIB_NAME "termo-${project_API_VERSION}") set (project_LIB_NAME "termo-${project_API_VERSION}")
@@ -55,7 +47,7 @@ if (Ncursesw_FOUND)
elseif (CURSES_FOUND) elseif (CURSES_FOUND)
include_directories (${CURSES_INCLUDE_DIR}) include_directories (${CURSES_INCLUDE_DIR})
set (curses_libraries ${CURSES_LIBRARY}) set (curses_libraries ${CURSES_LIBRARY})
endif (Ncursesw_FOUND) endif ()
if (unibilium_FOUND) if (unibilium_FOUND)
include_directories (${unibilium_INCLUDE_DIRS}) include_directories (${unibilium_INCLUDE_DIRS})
@@ -64,23 +56,29 @@ if (unibilium_FOUND)
elseif (curses_libraries) elseif (curses_libraries)
include_directories (${Ncursesw_INCLUDE_DIRS}) include_directories (${Ncursesw_INCLUDE_DIRS})
set (lib_libraries ${curses_libraries}) set (lib_libraries ${curses_libraries})
else (CURSES_FOUND) else ()
message (SEND_ERROR "Unibilium not found, Curses not found") message (SEND_ERROR "Unibilium not found, Curses not found")
endif (unibilium_FOUND) endif ()
# -liconv may or may not be a part of libc
find_library (iconv_LIBRARIES iconv)
if (iconv_LIBRARIES)
list (APPEND lib_libraries ${iconv_LIBRARIES})
endif ()
# Create the library targets # Create the library targets
add_library (termo SHARED ${lib_sources} ${lib_headers}) add_library (termo SHARED ${lib_sources} ${lib_headers})
target_link_libraries (termo ${lib_libraries}) target_link_libraries (termo ${lib_libraries})
set_target_properties (termo PROPERTIES set_target_properties (termo PROPERTIES
OUTPUT_NAME ${project_LIB_NAME} OUTPUT_NAME ${project_LIB_NAME}
VERSION ${project_VERSION} VERSION ${PROJECT_VERSION}
SOVERSION ${project_API_VERSION}) SOVERSION ${project_API_VERSION})
add_library (termo-static STATIC ${lib_sources} ${lib_headers}) add_library (termo-static STATIC ${lib_sources} ${lib_headers})
target_link_libraries (termo-static ${lib_libraries}) target_link_libraries (termo-static ${lib_libraries})
set_target_properties (termo-static PROPERTIES set_target_properties (termo-static PROPERTIES
OUTPUT_NAME ${project_LIB_NAME} OUTPUT_NAME ${project_LIB_NAME}
VERSION ${project_VERSION} VERSION ${PROJECT_VERSION}
SOVERSION ${project_API_VERSION}) SOVERSION ${project_API_VERSION})
# A fix for: relocation R_X86_64_32 against `a local symbol' can not be # A fix for: relocation R_X86_64_32 against `a local symbol' can not be
@@ -89,7 +87,7 @@ set_target_properties (termo-static PROPERTIES
# This should enable linking the static library into a shared one. # This should enable linking the static library into a shared one.
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") if (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
set_target_properties (termo-static PROPERTIES COMPILE_FLAGS "-fPIC") set_target_properties (termo-static PROPERTIES COMPILE_FLAGS "-fPIC")
endif (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") endif ()
# Demos # Demos
add_executable (demo-async EXCLUDE_FROM_ALL demo-async.c) add_executable (demo-async EXCLUDE_FROM_ALL demo-async.c)
@@ -108,7 +106,7 @@ if (glib_FOUND)
target_link_libraries (demo-glib target_link_libraries (demo-glib
termo-static ${lib_libraries} ${glib_LIBRARIES}) termo-static ${lib_libraries} ${glib_LIBRARIES})
list (APPEND demos demo-glib) list (APPEND demos demo-glib)
endif (glib_FOUND) endif ()
add_custom_target (demos DEPENDS ${demos}) add_custom_target (demos DEPENDS ${demos})
@@ -127,7 +125,7 @@ configure_file (config-version.cmake.in
install (FILES ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake install (FILES ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config.cmake
${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}-${project_VERSION}) DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}-${PROJECT_VERSION})
# Do some unit tests # Do some unit tests
option (BUILD_TESTING "Build tests" OFF) option (BUILD_TESTING "Build tests" OFF)
@@ -158,14 +156,14 @@ if (BUILD_TESTING)
add_executable (test-${name} tests/${name}.c ${test_common_sources}) add_executable (test-${name} tests/${name}.c ${test_common_sources})
target_link_libraries (test-${name} termo-static ${lib_libraries}) target_link_libraries (test-${name} termo-static ${lib_libraries})
add_test (NAME ${PROJECT_NAME}.${name} COMMAND test-${name}) add_test (NAME ${PROJECT_NAME}.${name} COMMAND test-${name})
endforeach (name) endforeach ()
endif (BUILD_TESTING) endif ()
# pkg-config # pkg-config
file (WRITE "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.pc" file (WRITE "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.pc"
"Name: ${PROJECT_NAME}\n" "Name: ${PROJECT_NAME}\n"
"Description: Terminal key input library\n" "Description: Terminal key input library\n"
"Version: ${project_VERSION}\n" "Version: ${PROJECT_VERSION}\n"
"Libs: -L${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR} -l${project_LIB_NAME}\n" "Libs: -L${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR} -l${project_LIB_NAME}\n"
"Libs.private: ${lib_libraries}\n" "Libs.private: ${lib_libraries}\n"
"Cflags: -I${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/${project_INCLUDE_NAME}\n") "Cflags: -I${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}/${project_INCLUDE_NAME}\n")
@@ -177,15 +175,12 @@ set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Terminal key input library")
set (CPACK_PACKAGE_VENDOR "Premysl Eric Janouch") set (CPACK_PACKAGE_VENDOR "Premysl Eric Janouch")
set (CPACK_PACKAGE_CONTACT "Přemysl Eric Janouch <p@janouch.name>") set (CPACK_PACKAGE_CONTACT "Přemysl Eric Janouch <p@janouch.name>")
set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
set (CPACK_PACKAGE_VERSION_MAJOR ${project_VERSION_MAJOR})
set (CPACK_PACKAGE_VERSION_MINOR ${project_VERSION_MINOR})
set (CPACK_PACKAGE_VERSION_PATCH ${project_VERSION_PATCH})
set (CPACK_GENERATOR "TGZ;ZIP") set (CPACK_GENERATOR "TGZ;ZIP")
set (CPACK_PACKAGE_FILE_NAME set (CPACK_PACKAGE_FILE_NAME
"${CMAKE_PROJECT_NAME}-${project_VERSION}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}") "${CMAKE_PROJECT_NAME}-${PROJECT_VERSION}-${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}")
set (CPACK_PACKAGE_INSTALL_DIRECTORY "${CMAKE_PROJECT_NAME}-${project_VERSION}") set (CPACK_PACKAGE_INSTALL_DIRECTORY "${CMAKE_PROJECT_NAME}-${PROJECT_VERSION}")
set (CPACK_SOURCE_GENERATOR "TGZ;ZIP") set (CPACK_SOURCE_GENERATOR "TGZ;ZIP")
set (CPACK_SOURCE_IGNORE_FILES "/\\\\.git;/build;/CMakeLists.txt.user") set (CPACK_SOURCE_IGNORE_FILES "/\\\\.git;/build;/CMakeLists.txt.user")
set (CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${project_VERSION}") set (CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${PROJECT_VERSION}")
include (CPack) include (CPack)

View File

@@ -1,5 +1,5 @@
Copyright (c) 2007-2014 Paul Evans <leonerd@leonerd.org.uk> Copyright (c) 2007-2014 Paul Evans <leonerd@leonerd.org.uk>
Copyright (c) 2014-2020 Přemysl Eric Janouch <p@janouch.name> Copyright (c) 2014-2021 Přemysl Eric Janouch <p@janouch.name>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@@ -2,20 +2,21 @@ termo
===== =====
'termo' is a library providing an alternative to ncurses' handling of terminal 'termo' is a library providing an alternative to ncurses' handling of terminal
input. ncurses does a really terrible job at that, mainly wrt. mouse support input. ncurses does a terrible job at that, mainly wrt. mouse support which was
which seems to be utterly broken. If you can drag things in a terminal even completely broken for a few years, making it impossible to drag things
application, such as in VIM, I can assure you it's not using ncurses for that. around in applications. While that one bit has been fixed, it continues to lack
(At least not with ncurses older than 6.0.) support for rxvt-unicode's 1015 mode and it's generally incapable of relaying
certain key combinations.
Since terminal I/O is really complicated and full of special cases, this project Since terminal I/O is really complicated and full of special cases, this project
doesn't aspire to also replace the output part of ncurses, but is rather doesn't aspire to also replace the output part of ncurses, but is rather
complementary to it. In the end it makes use of its terminfo library. complementary to it. In the end it makes use of its terminfo library.
The API isn't stable yet. Tell me what needs to be done so I can fix it first. The API can be considered stable. This project is in maintenance mode.
Building and Installing Building and Installing
----------------------- -----------------------
Build dependencies: cmake >= 2.8.5, pkg-config + Build dependencies: cmake >= 3.0, pkg-config +
Optional dependencies: Unibilium (alternative for curses), GLib (for the demos) Optional dependencies: Unibilium (alternative for curses), GLib (for the demos)
$ git clone https://git.janouch.name/p/termo.git $ git clone https://git.janouch.name/p/termo.git
@@ -55,7 +56,7 @@ CMake-based projects and simply import the target. No package maintainer action
is needed for you to enjoy the benefits of proper terminal input. is needed for you to enjoy the benefits of proper terminal input.
The rest is just me going silly over formatting and various unimportant stuff. The rest is just me going silly over formatting and various unimportant stuff.
Oh, and I've deleted the manpages. It needs more Doxygen. :) TBD Oh, and I've deleted the manpages.
Contributing and Support Contributing and Support
------------------------ ------------------------

2
demo.c
View File

@@ -10,7 +10,7 @@
#include "termo.h" #include "termo.h"
int int
main(int argc, char *argv[]) main (int argc, char *argv[])
{ {
TERMO_CHECK_VERSION; TERMO_CHECK_VERSION;
setlocale (LC_CTYPE, ""); setlocale (LC_CTYPE, "");

View File

@@ -250,29 +250,28 @@ load_terminfo (termo_ti_t *ti, const char *term)
else else
ti->set_mouse_string = strdup (set_mouse_string); ti->set_mouse_string = strdup (set_mouse_string);
bool have_mouse = false; // We handle 1006 and 1015 unconditionally in driver-csi.c,
if (!mouse_report_string && strstr (term, "xterm")) // and don't want to have the handling diverted by recent terminfo;
mouse_report_string = "\x1b[M"; // let's hardcode the ancient 1000 sequence locally
if (mouse_report_string) if (mouse_report_string)
{ {
have_mouse = true;
trie_node_t *node = malloc (sizeof *node); trie_node_t *node = malloc (sizeof *node);
if (!node) if (!node)
goto fail; goto fail;
node->type = TYPE_MOUSE; node->type = TYPE_MOUSE;
if (!insert_seq (ti, mouse_report_string, node)) if (!insert_seq (ti, "\x1b[M", node))
{ {
free (node); free (node);
goto fail; goto fail;
} }
} }
if (!have_mouse) if (!mouse_report_string && strstr (term, "xterm") != term)
ti->tk->guessed_mouse_proto = TERMO_MOUSE_PROTO_NONE; ti->tk->guessed_mouse_proto = TERMO_MOUSE_PROTO_NONE;
else if (strstr (term, "rxvt") == term) else if (strstr (term, "rxvt") == term)
// urxvt generally doesn't understand the SGR protocol. // urxvt didn't understand the SGR protocol until version 9.25,
// it's safest to keep using 1015.
ti->tk->guessed_mouse_proto = TERMO_MOUSE_PROTO_RXVT; ti->tk->guessed_mouse_proto = TERMO_MOUSE_PROTO_RXVT;
else else
// SGR (1006) is the superior protocol. If it's not supported by the // SGR (1006) is the superior protocol. If it's not supported by the
@@ -363,6 +362,8 @@ mouse_reset (termo_ti_t *ti)
&& write_string (ti->tk, "\x1b[?1002l") && write_string (ti->tk, "\x1b[?1002l")
&& write_string (ti->tk, "\x1b[?1003l") && write_string (ti->tk, "\x1b[?1003l")
&& write_string (ti->tk, "\x1b[?1004l")
&& write_string (ti->tk, "\x1b[?1005l") && write_string (ti->tk, "\x1b[?1005l")
&& write_string (ti->tk, "\x1b[?1006l") && write_string (ti->tk, "\x1b[?1006l")
&& write_string (ti->tk, "\x1b[?1015l"); && write_string (ti->tk, "\x1b[?1015l");
@@ -410,8 +411,12 @@ start_driver (termo_t *tk, void *info)
// Disable everything mouse-related first // Disable everything mouse-related first
if (!mouse_reset (ti)) if (!mouse_reset (ti))
return false; return false;
// Enable focus tracking opportunistically and automatically,
// as it basically doesn't have any negative consequences at all
return mouse_set_proto (ti, tk->mouse_proto, true) return mouse_set_proto (ti, tk->mouse_proto, true)
&& mouse_set_tracking_mode (ti, tk->mouse_tracking, true); && mouse_set_tracking_mode (ti, tk->mouse_tracking, true)
&& write_string (ti->tk, "\x1b[?1004h");
} }
static int static int
@@ -425,7 +430,8 @@ stop_driver (termo_t *tk, void *info)
if (tk->mouse_proto == TERMO_MOUSE_PROTO_NONE) if (tk->mouse_proto == TERMO_MOUSE_PROTO_NONE)
return true; return true;
return mouse_set_proto (ti, tk->mouse_proto, false) return mouse_set_proto (ti, tk->mouse_proto, false)
&& mouse_set_tracking_mode (ti, tk->mouse_tracking, false); && mouse_set_tracking_mode (ti, tk->mouse_tracking, false)
&& write_string (ti->tk, "\x1b[?1004l");
} }
static void * static void *

View File

@@ -1,8 +1,8 @@
#ifndef TERMO_CONFIG_H #ifndef TERMO_CONFIG_H
#define TERMO_CONFIG_H #define TERMO_CONFIG_H
#define TERMO_VERSION_MAJOR @project_VERSION_MAJOR@ #define TERMO_VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define TERMO_VERSION_MINOR @project_VERSION_MINOR@ #define TERMO_VERSION_MINOR @PROJECT_VERSION_MINOR@
#endif // ! TERMO_CONFIG_H #endif // ! TERMO_CONFIG_H

View File

@@ -906,21 +906,20 @@ peekkey (termo_t *tk, termo_key_t *key, int flags, size_t *nbytep)
switch (ret) switch (ret)
{ {
size_t halfsize;
case TERMO_RES_KEY: case TERMO_RES_KEY:
{
#ifdef DEBUG #ifdef DEBUG
print_key (tk, key); fprintf (stderr, "\n"); print_key (tk, key); fprintf (stderr, "\n");
#endif #endif
// Slide the data down to stop it running away // Slide the data down to stop it running away
size_t halfsize = tk->buffsize / 2; halfsize = tk->buffsize / 2;
if (tk->buffstart > halfsize) if (tk->buffstart > halfsize)
{ {
memcpy (tk->buffer, tk->buffer + halfsize, halfsize); memcpy (tk->buffer, tk->buffer + halfsize, halfsize);
tk->buffstart -= halfsize; tk->buffstart -= halfsize;
} }
// Fallthrough // Fall-through
}
case TERMO_RES_EOF: case TERMO_RES_EOF:
case TERMO_RES_ERROR: case TERMO_RES_ERROR:
return ret; return ret;
@@ -1857,4 +1856,3 @@ termo_keycmp (termo_t *tk,
} }
return key1.modifiers - key2.modifiers; return key1.modifiers - key2.modifiers;
} }

View File

@@ -2,7 +2,8 @@
#include "../termo.h" #include "../termo.h"
#include "taplib.h" #include "taplib.h"
int main (int argc, char *argv[]) int
main (int argc, char *argv[])
{ {
termo_t *tk; termo_t *tk;
termo_key_t key; termo_key_t key;

View File

@@ -1,7 +1,8 @@
#include "../termo.h" #include "../termo.h"
#include "taplib.h" #include "taplib.h"
int main (int argc, char *argv[]) int
main (int argc, char *argv[])
{ {
termo_t *tk; termo_t *tk;
termo_key_t key; termo_key_t key;

View File

@@ -73,7 +73,7 @@ is_str (const char *got, const char *expect, char *name)
} }
int int
exit_status(void) exit_status (void)
{ {
return g_exit_status; return g_exit_status;
} }