diff --git a/CHANGELOG b/CHANGELOG index 6062f26..cdc3f17 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,53 @@ -qtermwidget-0.7.1 / 2016-12-21 +qtermwidget-0.8.0 / 2017-10-21 ============================== + * FIX: #46 fix vertical font truncation + * bump versions + * Really fallback to /bin/sh when $SHELL is missing or invalid + * README: don't recommend building from source + * Improve README + * Don't export github templates + * Support REP escape sequence defined in ECMA-48, section 8.3.103 + * Fix build issue related to utmpx in Mac OSX Sierra + * Remove the deprecation notice + * Handle DECSCUSR signals + * Copied issue template + * Update building instructions + * Require Qt 5.6+ + * This commit allows the consumer of qtermwidget to capture the (#111) + * Allow the terminal display to be smaller than the size hint (#123) + * Backport Vt102 emulation fixes (#113) + * Backport the default.keytab from Konsole + * Fixes (#122) + * Updated README, Added support for PyQT 5.7 + * Fix memory leak in hotspot (URLs & emails) detection + * Adds superbuild support + * Use target_compile_definitions() instead of add_definitions() + * Update find_package() documentation + * Use the lxqt_create_pkgconfig_file + * Improve lxqt_translate_ts() use + * Adds COMPONENT to the install files + * Renames test app to example. Make it work + * Drop include_directories() for in tree dirs + * Use the CMake Targets way + * Pack Utf8Proc stuff + * Adds export header + * Use LXQtCompilerSettings + * Packs compile definitions + * Adds package version file + * Removes Qt4 stuff + * Add translation mechanism + * Use const iterators when possible. + * Enable strict iterators for debug builds + * TerminalDisplay: Make resizing "Size" translatable + * Exposes receivedData signal to users of QTermWidget + * Exposes sessions autoClose property to QTermWidget + +0.7.1 / 2016-12-21 +================== + + * Release 0.7.1: Update changelog * Bump patch version (#105) * Added a modified Breeze color scheme (#104) * Accept hex color strings as well (#101) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7502ee1..c5930c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,54 +3,44 @@ cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR) project(qtermwidget) include(GNUInstallDirs) +include(GenerateExportHeader) +include(CMakePackageConfigHelpers) include(CheckFunctionExists) -option(BUILD_TEST "Build test application. Default OFF." OFF) +set(REQUIRED_QT_VERSION "5.6") +set(LXQTBT_MINIMUM_VERSION "0.4.0") + +option(UPDATE_TRANSLATIONS "Update source translation translations/*.ts files" OFF) +option(BUILD_EXAMPLE "Build example application. Default OFF." OFF) # just change version for releases set(QTERMWIDGET_VERSION_MAJOR "0") -set(QTERMWIDGET_VERSION_MINOR "7") -set(QTERMWIDGET_VERSION_PATCH "1") +set(QTERMWIDGET_VERSION_MINOR "8") +set(QTERMWIDGET_VERSION_PATCH "0") set(QTERMWIDGET_VERSION "${QTERMWIDGET_VERSION_MAJOR}.${QTERMWIDGET_VERSION_MINOR}.${QTERMWIDGET_VERSION_PATCH}") # additional cmake files -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake") if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() -include(CheckCXXCompilerFlag) -CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) -CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) -if(COMPILER_SUPPORTS_CXX11) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") -elseif(COMPILER_SUPPORTS_CXX0X) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") -else() - message(FATAL "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. C++11 support is required") -endif() - -include_directories( - "${CMAKE_SOURCE_DIR}/lib" - "${CMAKE_BINARY_DIR}/lib" - "${CMAKE_BINARY_DIR}" -) -add_definitions(-Wall) +set(CMAKE_INCLUDE_CURRENT_DIR ON) +find_package(Qt5Widgets "${REQUIRED_QT_VERSION}" REQUIRED) +find_package(Qt5LinguistTools "${REQUIRED_QT_VERSION}" REQUIRED) +find_package(lxqt-build-tools ${LXQTBT_MINIMUM_VERSION} REQUIRED) +include(LXQtTranslateTs) +include(LXQtCompilerSettings NO_POLICY_SCOPE) +include(LXQtCreatePkgConfigFile) set(QTERMWIDGET_LIBRARY_NAME qtermwidget5) -include(qtermwidget5_use) option(USE_UTF8PROC "Use libutf8proc for better Unicode support. Default OFF" OFF) if(USE_UTF8PROC) - find_package(Utf8Proc) -endif() - -if (UTF8PROC_FOUND) - add_definitions(-DHAVE_UTF8PROC) - include_directories("${UTF8PROC_INCLUDE_DIRS}") + find_package(Utf8Proc REQUIRED) endif() # main library @@ -105,86 +95,200 @@ set(UI # for distribution set(HDRS_DISTRIB lib/qtermwidget.h + lib/Emulation.h lib/Filter.h ) # dirs set(KB_LAYOUT_DIR "${CMAKE_INSTALL_FULL_DATADIR}/${QTERMWIDGET_LIBRARY_NAME}/kb-layouts") message(STATUS "Keyboard layouts will be installed in: ${KB_LAYOUT_DIR}") -add_definitions(-DKB_LAYOUT_DIR="${KB_LAYOUT_DIR}") set(COLORSCHEMES_DIR "${CMAKE_INSTALL_FULL_DATADIR}/${QTERMWIDGET_LIBRARY_NAME}/color-schemes") message(STATUS "Color schemes will be installed in: ${COLORSCHEMES_DIR}" ) -add_definitions(-DCOLORSCHEMES_DIR="${COLORSCHEMES_DIR}") + +set(TRANSLATIONS_DIR "${CMAKE_INSTALL_FULL_DATADIR}/${QTERMWIDGET_LIBRARY_NAME}/translations") +message(STATUS "Translations will be installed in: ${TRANSLATIONS_DIR}") set(QTERMWIDGET_INCLUDE_DIR "${CMAKE_INSTALL_FULL_INCLUDEDIR}/${QTERMWIDGET_LIBRARY_NAME}") -#| Defines -add_definitions(-DHAVE_POSIX_OPENPT -DHAVE_SYS_TIME_H) -if(APPLE) - add_definitions(-DHAVE_UTMPX -D_UTMPX_COMPAT) -endif() CHECK_FUNCTION_EXISTS(updwtmpx HAVE_UPDWTMPX) -if(HAVE_UPDWTMPX) - add_definitions(-DHAVE_UPDWTMPX) -endif() qt5_wrap_cpp(MOCS ${HDRS}) qt5_wrap_ui(UI_SRCS ${UI}) -set(PKG_CONFIG_REQ "Qt5Core, Qt5Xml, Qt5Widgets") +set(PKG_CONFIG_REQ "Qt5Widgets") -add_library(${QTERMWIDGET_LIBRARY_NAME} SHARED ${SRCS} ${MOCS} ${UI_SRCS}) -target_link_libraries(${QTERMWIDGET_LIBRARY_NAME} ${QTERMWIDGET_QT_LIBRARIES}) +lxqt_translate_ts(QTERMWIDGET_QM + TRANSLATION_DIR "lib/translations" + UPDATE_TRANSLATIONS + ${UPDATE_TRANSLATIONS} + SOURCES + ${SRCS} ${HDRS} ${UI} + PULL_TRANSLATIONS + ${PULL_TRANSLATIONS} + CLEAN_TRANSLATIONS + ${CLEAN_TRANSLATIONS} + TRANSLATIONS_REPO + ${TRANSLATIONS_REPO} + TRANSLATIONS_REFSPEC + ${TRANSLATIONS_REFSPEC} + INSTALL_DIR + ${TRANSLATIONS_DIR} + COMPONENT + Runtime +) + +add_library(${QTERMWIDGET_LIBRARY_NAME} SHARED ${SRCS} ${MOCS} ${UI_SRCS} ${QTERMWIDGET_QM}) +target_link_libraries(${QTERMWIDGET_LIBRARY_NAME} Qt5::Widgets) set_target_properties( ${QTERMWIDGET_LIBRARY_NAME} PROPERTIES SOVERSION ${QTERMWIDGET_VERSION_MAJOR} VERSION ${QTERMWIDGET_VERSION} ) + + +if(APPLE) + target_compile_definitions(${QTERMWIDGET_LIBRARY_NAME} + PRIVATE + "HAVE_UTMPX" + "UTMPX_COMPAT" + ) +endif() + +if(HAVE_UPDWTMPX) + target_compile_definitions(${QTERMWIDGET_LIBRARY_NAME} + PRIVATE + "HAVE_UPDWTMPX" + ) +endif() + if (UTF8PROC_FOUND) - target_link_libraries(${QTERMWIDGET_LIBRARY_NAME} ${UTF8PROC_LIBRARIES}) + target_compile_definitions(${QTERMWIDGET_LIBRARY_NAME} + PRIVATE + "HAVE_UTF8PROC" + ) + target_include_directories(${QTERMWIDGET_LIBRARY_NAME} + INTERFACE + ${UTF8PROC_INCLUDE_DIRS} + ) + target_link_libraries(${QTERMWIDGET_LIBRARY_NAME} + ${UTF8PROC_LIBRARIES} + ) + string(APPEND PKG_CONFIG_REQ ", libutf8proc") endif() + if(APPLE) set (CMAKE_SKIP_RPATH 1) # this is a must to load the lib correctly set_target_properties(${QTERMWIDGET_LIBRARY_NAME} PROPERTIES INSTALL_NAME_DIR ${CMAKE_INSTALL_FULL_LIBDIR}) endif() -install(TARGETS ${QTERMWIDGET_LIBRARY_NAME} DESTINATION "${CMAKE_INSTALL_LIBDIR}") -install(FILES ${HDRS_DISTRIB} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${QTERMWIDGET_LIBRARY_NAME}") +target_compile_definitions(${QTERMWIDGET_LIBRARY_NAME} + PRIVATE + "KB_LAYOUT_DIR=\"${KB_LAYOUT_DIR}\"" + "COLORSCHEMES_DIR=\"${COLORSCHEMES_DIR}\"" + "TRANSLATIONS_DIR=\"${TRANSLATIONS_DIR}\"" + "HAVE_POSIX_OPENPT" + "HAVE_SYS_TIME_H" +) + + +generate_export_header(${QTERMWIDGET_LIBRARY_NAME} + EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/lib/qtermwidget_export.h" + EXPORT_MACRO_NAME QTERMWIDGET_EXPORT +) + +target_include_directories(${QTERMWIDGET_LIBRARY_NAME} + PUBLIC + "$" + "$" + INTERFACE + "$" + "$" +) + +write_basic_package_version_file( + "${CMAKE_BINARY_DIR}/${QTERMWIDGET_LIBRARY_NAME}-config-version.cmake" + VERSION ${QTERMWIDGET_VERSION} + COMPATIBILITY AnyNewerVersion +) + +install(FILES + "${CMAKE_BINARY_DIR}/${QTERMWIDGET_LIBRARY_NAME}-config-version.cmake" + DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${QTERMWIDGET_LIBRARY_NAME}" + COMPONENT Devel +) + +install(EXPORT + "${QTERMWIDGET_LIBRARY_NAME}-targets" + DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${QTERMWIDGET_LIBRARY_NAME}" + COMPONENT Devel +) + +install(FILES + ${HDRS_DISTRIB} "${CMAKE_CURRENT_BINARY_DIR}/lib/qtermwidget_export.h" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${QTERMWIDGET_LIBRARY_NAME}" + COMPONENT Devel +) # keyboard layouts -install(DIRECTORY lib/kb-layouts/ DESTINATION "${KB_LAYOUT_DIR}" FILES_MATCHING PATTERN "*.keytab" ) +install(DIRECTORY + lib/kb-layouts/ + DESTINATION "${KB_LAYOUT_DIR}" + COMPONENT Runtime + FILES_MATCHING PATTERN "*.keytab" +) # color schemes -install(DIRECTORY lib/color-schemes/ DESTINATION "${COLORSCHEMES_DIR}" FILES_MATCHING PATTERN "*.*schem*") +install(DIRECTORY + lib/color-schemes/ + DESTINATION "${COLORSCHEMES_DIR}" + COMPONENT Runtime + FILES_MATCHING PATTERN "*.*schem*" +) -include(create_pkgconfig_file) -create_pkgconfig_file(${QTERMWIDGET_LIBRARY_NAME} - "QTermWidget library for Qt ${QTERMWIDGET_VERSION_MAJOR}.x" - ${PKG_CONFIG_REQ} - ${QTERMWIDGET_LIBRARY_NAME} - ${QTERMWIDGET_VERSION} +lxqt_create_pkgconfig_file( + PACKAGE_NAME ${QTERMWIDGET_LIBRARY_NAME} + DESCRIPTIVE_NAME ${QTERMWIDGET_LIBRARY_NAME} + DESCRIPTION "QTermWidget library for Qt ${QTERMWIDGET_VERSION_MAJOR}.x" + INCLUDEDIRS ${QTERMWIDGET_LIBRARY_NAME} + LIBS ${QTERMWIDGET_LIBRARY_NAME} + REQUIRES ${PKG_CONFIG_REQ} + VERSION ${QTERMWIDGET_VERSION} + INSTALL + COMPONENT Devel ) configure_file( - "${CMAKE_SOURCE_DIR}/cmake/${QTERMWIDGET_LIBRARY_NAME}-config.cmake.in" + "${PROJECT_SOURCE_DIR}/cmake/${QTERMWIDGET_LIBRARY_NAME}-config.cmake.in" "${CMAKE_BINARY_DIR}/${QTERMWIDGET_LIBRARY_NAME}-config.cmake" @ONLY ) + install(FILES "${CMAKE_BINARY_DIR}/${QTERMWIDGET_LIBRARY_NAME}-config.cmake" - "${CMAKE_SOURCE_DIR}/cmake/${QTERMWIDGET_LIBRARY_NAME}_use.cmake" DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${QTERMWIDGET_LIBRARY_NAME}" + COMPONENT Devel +) + +install(TARGETS ${QTERMWIDGET_LIBRARY_NAME} + DESTINATION "${CMAKE_INSTALL_LIBDIR}" + EXPORT "${QTERMWIDGET_LIBRARY_NAME}-targets" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + PUBLIC_HEADER + COMPONENT Runtime +) + +export(TARGETS ${QTERMWIDGET_LIBRARY_NAME} + FILE "${CMAKE_BINARY_DIR}/${QTERMWIDGET_LIBRARY_NAME}-targets.cmake" + EXPORT_LINK_INTERFACE_LIBRARIES ) # end of main library -# test application -if(BUILD_TEST) - set(TEST_SRC src/main.cpp) - add_executable(test ${TEST_SRC}) - add_dependencies(test ${QTERMWIDGET_LIBRARY_NAME}) - link_directories(${CMAKE_BINARY_DIR}) - target_link_libraries(test ${QTERMWIDGET_QT_LIBRARIES} ${QTERMWIDGET_LIBRARY_NAME} util) -endif (BUILD_TEST) -# end of test application +# example application +if(BUILD_EXAMPLE) + set(EXAMPLE_SRC example/main.cpp) + add_executable(example ${EXAMPLE_SRC}) + target_link_libraries(example ${QTERMWIDGET_LIBRARY_NAME}) +endif() +# end of example application CONFIGURE_FILE( diff --git a/README.md b/README.md index d2dafdf..ce35585 100644 --- a/README.md +++ b/README.md @@ -2,27 +2,27 @@ ## Overview -A terminal emulator widget for Qt 5. +A terminal emulator widget for Qt 5. -QTermWidget is an open-source project originally based on KDE4 Konsole application, but it took its own direction later. -The main goal of this project is to provide a unicode-enabled, embeddable Qt widget for using as a built-in console (or terminal emulation widget). +QTermWidget is an open-source project originally based on the KDE4 Konsole application, but it took its own direction later on. +The main goal of this project is to provide a unicode-enabled, embeddable Qt widget for using as a built-in console (or terminal emulation widget). -It is compatible with BSD, Linux and OS X. +It is compatible with BSD, Linux and OS X. -This project is licensed under the terms of the [GPLv2](https://www.gnu.org/licenses/gpl-2.0.en.html) or any later version. See the LICENSE file for the full text of the license. +This project is licensed under the terms of the [GPLv2](https://www.gnu.org/licenses/gpl-2.0.en.html) or any later version. See the LICENSE file for the full text of the license. ## Installation ### Compiling sources -The only runtime dependency is qtbase ≥ 5.4. -In order to build CMake ≥ 3.0 is needed as well as optionally Git to pull latest VCS checkouts. +The only runtime dependency is qtbase ≥ 5.6. +In order to build CMake ≥ 3.0.2 and [lxqt-build-tools](https://github.com/lxde/lxqt-build-tools/) >= 0.3 are needed as well as Git to pull translations and optionally latest VCS checkouts. -Code configuration is handled by CMake. Building out of source is strongly recommended. CMake variable `CMAKE_INSTALL_PREFIX` will normally have to be set to `/usr`, depending on the way library paths are dealt with on 64bit systems variables like `CMAKE_INSTALL_LIBDIR` may have to be set as well. +Code configuration is handled by CMake. CMake variable `CMAKE_INSTALL_PREFIX` will normally have to be set to `/usr`, depending on the way library paths are dealt with on 64bit systems variables like `CMAKE_INSTALL_LIBDIR` may have to be set as well. -To build run `make`, to install `make install` which accepts variable `DESTDIR` as usual. +To build run `make`, to install `make install` which accepts variable `DESTDIR` as usual. ### Binary packages -The library is provided by all major Linux distributions like Arch Linux, Debian, Fedora and openSUSE. +The library is provided by all major Linux distributions like Arch Linux, Debian, Fedora and openSUSE. Just use the distributions' package managers to search for string `qtermwidget`. diff --git a/cmake/create_pkgconfig_file.cmake b/cmake/create_pkgconfig_file.cmake deleted file mode 100644 index c3e775b..0000000 --- a/cmake/create_pkgconfig_file.cmake +++ /dev/null @@ -1,29 +0,0 @@ -# -# Write a pkg-config pc file for given "name" with "decription" -# Arguments: -# name: a library name (withoud "lib" prefix and "so" suffixes -# desc: a desription string -# requires: required libraries -# include_rel_dir: include directory, relative to includedir -# version: package version -# -macro (create_pkgconfig_file name desc requires include_rel_dir version) - set(_pkgfname "${CMAKE_CURRENT_BINARY_DIR}/${name}.pc") - message(STATUS "${name}: writing pkgconfig file ${_pkgfname}") - - file(WRITE "${_pkgfname}" - "prefix=${CMAKE_INSTALL_PREFIX}\n" - "libdir=\${prefix}/${CMAKE_INSTALL_LIBDIR}\n" - "includedir=\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}\n" - "\n" - "Name: ${name}\n" - "Description: ${desc}\n" - "Version: ${version}\n" - "Requires: ${requires}\n" - "Libs: -L\${libdir} -l${name}\n" - "Cflags: -I\${includedir} -I\${includedir}/${include_rel_dir}\n" - "\n" - ) - - install(FILES ${_pkgfname} DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) -endmacro() diff --git a/cmake/qtermwidget4_use.cmake b/cmake/qtermwidget4_use.cmake deleted file mode 100644 index 5f2d732..0000000 --- a/cmake/qtermwidget4_use.cmake +++ /dev/null @@ -1,8 +0,0 @@ - -find_package(Qt4 REQUIRED QUIET) -include(${QT_USE_FILE}) - -set(QTERMWIDGET_QT_LIBRARIES ${QT_LIBRARIES}) - -include_directories(${QTERMWIDGET_INCLUDE_DIRS}) - diff --git a/cmake/qtermwidget5-config.cmake.in b/cmake/qtermwidget5-config.cmake.in index 83295e4..db62356 100644 --- a/cmake/qtermwidget5-config.cmake.in +++ b/cmake/qtermwidget5-config.cmake.in @@ -1,41 +1,20 @@ -# - Find the QTermWidget include and library dirs and define a some macros -# -# The module defines the following variables -# QTERMWIDGET_FOUND - Set to TRUE if all of the above has been found -# -# QTERMWIDGET_INCLUDE_DIR - The QTermWidget include directory -# -# QTERMWIDGET_INCLUDE_DIRS - The QTermWidget include directory -# -# QTERMWIDGET_LIBRARIES - The libraries needed to use QTermWidget -# -# QTERMWIDGET_USE_FILE - The variable QTERMWIDGET_USE_FILE is set which is the path -# to a CMake file that can be included to compile qtermwidget -# applications and libraries. It sets up the compilation -# environment for include directories and populates a -# QTERMWIDGET_LIBRARIES variable. -# -# QTERMWIDGET_QT_LIBRARIES - The Qt libraries needed by QTermWidget +# - Find the QTermWidget include and library # # Typical usage: -# find_package(QTERMWIDGET5) +# find_package(QTermWidget5 REQUIRED) # -# include(${QTERMWIDGET_USE_FILE}) # add_executable(foo main.cpp) -# target_link_libraries(foo ${QTERMWIDGET_QT_LIBRARIES} ${QTERMWIDGET_LIBRARIES}) - -set(QTERMWIDGET_INCLUDE_DIR @QTERMWIDGET_INCLUDE_DIR@) -set(QTERMWIDGET_LIBRARY @QTERMWIDGET_LIBRARY_NAME@) - -set(QTERMWIDGET_LIBRARIES ${QTERMWIDGET_LIBRARY}) -set(QTERMWIDGET_INCLUDE_DIRS "${QTERMWIDGET_INCLUDE_DIR}") +# target_link_libraries(foo qtermwidget5) -set(QTERMWIDGET_USE_FILE "${CMAKE_CURRENT_LIST_DIR}/qtermwidget5_use.cmake") -set(QTERMWIDGET_FOUND 1) +@PACKAGE_INIT@ -set(QTERMWIDGET_VERSION_MAJOR @QTERMWIDGET_VERSION_MAJOR@) -set(QTERMWIDGET_VERSION_MINOR @QTERMWIDGET_VERSION_MINOR@) -set(QTERMWIDGET_VERSION_PATCH @QTERMWIDGET_VERSION_PATCH@) -set(QTERMWIDGET_VERSION @QTERMWIDGET_VERSION@) +if (CMAKE_VERSION VERSION_LESS 3.0.2) + message(FATAL_ERROR \"qtermwidget requires at least CMake version 3.0.2\") +endif() -mark_as_advanced(QTERMWIDGET_LIBRARY QTERMWIDGET_INCLUDE_DIR) +if (NOT TARGET @QTERMWIDGET_LIBRARY_NAME@) + if (POLICY CMP0024) + cmake_policy(SET CMP0024 NEW) + endif() + include("${CMAKE_CURRENT_LIST_DIR}/@QTERMWIDGET_LIBRARY_NAME@-targets.cmake") +endif() diff --git a/cmake/qtermwidget5_use.cmake b/cmake/qtermwidget5_use.cmake deleted file mode 100644 index 3db35fa..0000000 --- a/cmake/qtermwidget5_use.cmake +++ /dev/null @@ -1,9 +0,0 @@ -find_package(Qt5Widgets REQUIRED) - -include_directories(${Qt5Widgets_INCLUDE_DIRS}) -add_definitions(${Qt5Core_DEFINITIONS}) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}") -set(QTERMWIDGET_QT_LIBRARIES ${Qt5Widgets_LIBRARIES}) - -include_directories(${QTERMWIDGET_INCLUDE_DIRS}) - diff --git a/src/README b/example/README similarity index 100% rename from src/README rename to example/README diff --git a/src/main.cpp b/example/main.cpp similarity index 100% rename from src/main.cpp rename to example/main.cpp diff --git a/lib/Character.h b/lib/Character.h index 536cd63..0777a7e 100644 --- a/lib/Character.h +++ b/lib/Character.h @@ -48,6 +48,10 @@ static const int LINE_DOUBLEHEIGHT = (1 << 2); #define RE_ITALIC (1 << 4) #define RE_CURSOR (1 << 5) #define RE_EXTENDED_CHAR (1 << 6) +#define RE_FAINT (1 << 7) +#define RE_STRIKEOUT (1 << 8) +#define RE_CONCEAL (1 << 9) +#define RE_OVERLINE (1 << 10) /** * A single character in the terminal which consists of a unicode character diff --git a/lib/Emulation.cpp b/lib/Emulation.cpp index 42155ba..2dfb956 100644 --- a/lib/Emulation.cpp +++ b/lib/Emulation.cpp @@ -70,6 +70,11 @@ Emulation::Emulation() : SLOT(usesMouseChanged(bool))); connect(this , SIGNAL(programBracketedPasteModeChanged(bool)) , SLOT(bracketedPasteModeChanged(bool))); + + connect(this, &Emulation::cursorChanged, [this] (KeyboardCursorShape cursorShape, bool blinkingCursorEnabled) { + emit titleChanged( 50, QString("CursorShape=%1;BlinkingCursorEnabled=%2") + .arg(static_cast(cursorShape)).arg(blinkingCursorEnabled) ); + }); } bool Emulation::programUsesMouse() const diff --git a/lib/Emulation.h b/lib/Emulation.h index 57802d9..77623ce 100644 --- a/lib/Emulation.h +++ b/lib/Emulation.h @@ -126,6 +126,26 @@ Q_OBJECT public: + /** + * This enum describes the available shapes for the keyboard cursor. + * See setKeyboardCursorShape() + */ + enum class KeyboardCursorShape { + /** A rectangular block which covers the entire area of the cursor character. */ + BlockCursor = 0, + /** + * A single flat line which occupies the space at the bottom of the cursor + * character's area. + */ + UnderlineCursor = 1, + /** + * An cursor shaped like the capital letter 'I', similar to the IBeam + * cursor used in Qt/KDE text editors. + */ + IBeamCursor = 2 + }; + + /** Constructs a new terminal emulation */ Emulation(); ~Emulation(); @@ -415,6 +435,15 @@ signals: */ void flowControlKeyPressed(bool suspendKeyPressed); + /** + * Emitted when the cursor shape or its blinking state is changed via + * DECSCUSR sequences. + * + * @param cursorShape One of 3 possible values in KeyboardCursorShape enum + * @param blinkingCursorEnabled Whether to enable blinking or not + */ + void cursorChanged(KeyboardCursorShape cursorShape, bool blinkingCursorEnabled); + protected: virtual void setMode(int mode) = 0; virtual void resetMode(int mode) = 0; diff --git a/lib/Filter.cpp b/lib/Filter.cpp index 5ca7bee..611fb6c 100644 --- a/lib/Filter.cpp +++ b/lib/Filter.cpp @@ -26,6 +26,7 @@ // Qt #include #include +#include #include #include #include @@ -194,6 +195,7 @@ Filter::~Filter() } void Filter::reset() { + qDeleteAll(_hotspotList); _hotspots.clear(); _hotspotList.clear(); } @@ -416,7 +418,7 @@ UrlFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endCol UrlFilter::HotSpot::UrlType UrlFilter::HotSpot::urlType() const { - QString url = capturedTexts().first(); + QString url = capturedTexts().constFirst(); if ( FullUrlRegExp.exactMatch(url) ) return StandardUrl; @@ -428,7 +430,7 @@ UrlFilter::HotSpot::UrlType UrlFilter::HotSpot::urlType() const void UrlFilter::HotSpot::activate(const QString& actionName) { - QString url = capturedTexts().first(); + QString url = capturedTexts().constFirst(); const UrlType kind = urlType(); diff --git a/lib/KeyboardTranslator.cpp b/lib/KeyboardTranslator.cpp index 856fadb..68657a7 100644 --- a/lib/KeyboardTranslator.cpp +++ b/lib/KeyboardTranslator.cpp @@ -321,6 +321,10 @@ bool KeyboardTranslatorReader::parseAsCommand(const QString& text,KeyboardTransl command = KeyboardTranslator::ScrollLineDownCommand; else if ( text.compare("scrolllock",Qt::CaseInsensitive) == 0 ) command = KeyboardTranslator::ScrollLockCommand; + else if ( text.compare("scrolluptotop",Qt::CaseInsensitive) == 0) + command = KeyboardTranslator::ScrollUpToTopCommand; + else if ( text.compare("scrolldowntobottom",Qt::CaseInsensitive) == 0) + command = KeyboardTranslator::ScrollDownToBottomCommand; else return false; @@ -543,27 +547,27 @@ QList KeyboardTranslatorReader::tokenize(const if ( title.exactMatch(text) ) { Token titleToken = { Token::TitleKeyword , QString() }; - Token textToken = { Token::TitleText , title.capturedTexts()[1] }; + Token textToken = { Token::TitleText , title.capturedTexts().at(1) }; list << titleToken << textToken; } else if ( key.exactMatch(text) ) { Token keyToken = { Token::KeyKeyword , QString() }; - Token sequenceToken = { Token::KeySequence , key.capturedTexts()[1].remove(' ') }; + Token sequenceToken = { Token::KeySequence , key.capturedTexts().value(1).remove(' ') }; list << keyToken << sequenceToken; - if ( key.capturedTexts()[3].isEmpty() ) + if ( key.capturedTexts().at(3).isEmpty() ) { // capturedTexts()[2] is a command - Token commandToken = { Token::Command , key.capturedTexts()[2] }; + Token commandToken = { Token::Command , key.capturedTexts().at(2) }; list << commandToken; } else { // capturedTexts()[3] is the output string - Token outputToken = { Token::OutputText , key.capturedTexts()[3] }; + Token outputToken = { Token::OutputText , key.capturedTexts().at(3) }; list << outputToken; } } @@ -785,6 +789,10 @@ QString KeyboardTranslator::Entry::resultToString(bool expandWildCards,Qt::Keybo return "ScrollLineDown"; else if ( _command == ScrollLockCommand ) return "ScrollLock"; + else if (_command == ScrollUpToTopCommand) + return "ScrollUpToTop"; + else if (_command == ScrollDownToBottomCommand) + return "ScrollDownToBottom"; return QString(); } @@ -852,10 +860,11 @@ void KeyboardTranslator::removeEntry(const Entry& entry) } KeyboardTranslator::Entry KeyboardTranslator::findEntry(int keyCode, Qt::KeyboardModifiers modifiers, States state) const { - foreach(const Entry& entry, _entries.values(keyCode)) + for (auto it = _entries.cbegin(), end = _entries.cend(); it != end; ++it) { - if ( entry.matches(keyCode,modifiers,state) ) - return entry; + if (it.key() == keyCode) + if ( it.value().matches(keyCode,modifiers,state) ) + return *it; } return Entry(); // entry not found } diff --git a/lib/KeyboardTranslator.h b/lib/KeyboardTranslator.h index 37efc10..a9614e4 100644 --- a/lib/KeyboardTranslator.h +++ b/lib/KeyboardTranslator.h @@ -111,8 +111,12 @@ public: ScrollLineDownCommand = 16, /** Toggles scroll lock mode */ ScrollLockCommand = 32, + /** Scroll the terminal display up to the start of history */ + ScrollUpToTopCommand = 64, + /** Scroll the terminal display down to the end of history */ + ScrollDownToBottomCommand = 128, /** Echos the operating system specific erase character. */ - EraseCommand = 64 + EraseCommand = 256 }; Q_DECLARE_FLAGS(Commands,Command) diff --git a/lib/Screen.cpp b/lib/Screen.cpp index fa03652..6104764 100644 --- a/lib/Screen.cpp +++ b/lib/Screen.cpp @@ -226,6 +226,28 @@ void Screen::insertChars(int n) screenLines[cuY].resize(columns); } +void Screen::repeatChars(int count) + //=REP +{ + if (count == 0) + { + count = 1; + } + /** + * From ECMA-48 version 5, section 8.3.103 + * If the character preceding REP is a control function or part of a + * control function, the effect of REP is not defined by this Standard. + * + * So, a "normal" program should always use REP immediately after a visible + * character (those other than escape sequences). So, lastDrawnChar can be + * safely used. + */ + for (int i = 0; i < count; i++) + { + displayCharacter(lastDrawnChar); + } +} + void Screen::deleteLines(int n) { if (n == 0) n = 1; // Default @@ -663,6 +685,8 @@ void Screen::displayCharacter(unsigned short c) currentChar.backgroundColor = effectiveBackground; currentChar.rendition = effectiveRendition; + lastDrawnChar = c; + int i = 0; int newCursorX = cuX + w--; while(w) diff --git a/lib/Screen.h b/lib/Screen.h index 6316ad4..ea526ac 100644 --- a/lib/Screen.h +++ b/lib/Screen.h @@ -197,6 +197,11 @@ public: * If @p n is 0 then one character is inserted. */ void insertChars(int n); + /** + * Repeat the preceeding graphic character @count times, including SPACE. + * If @count is 0 then the character is repeated once. + */ + void repeatChars(int count); /** * Removes @p n lines beginning from the current cursor position. * The position of the cursor is not altered. @@ -667,6 +672,9 @@ private: // last position where we added a character int lastPos; + // used in REP (repeating char) + unsigned short lastDrawnChar; + static Character defaultChar; }; diff --git a/lib/Session.cpp b/lib/Session.cpp index fb7ca67..08cb197 100644 --- a/lib/Session.cpp +++ b/lib/Session.cpp @@ -99,6 +99,8 @@ Session::Session(QObject* parent) : this, SLOT(onEmulationSizeChange(QSize))); connect(_emulation, SIGNAL(imageSizeChanged(int, int)), this, SLOT(onViewSizeChange(int, int))); + connect(_emulation, &Vt102Emulation::cursorChanged, + this, &Session::cursorChanged); //connect teletype to emulation backend _shellProcess->setUtf8Mode(_emulation->utf8()); @@ -256,8 +258,10 @@ void Session::run() // here we expect full path. If there is no fullpath let's expect it's // a custom shell (eg. python, etc.) available in the PATH. - if (exec.startsWith("/")) + if (exec.startsWith("/") || exec.isEmpty()) { + const QString defaultShell{"/bin/sh"}; + QFile excheck(exec); if ( exec.isEmpty() || !excheck.exists() ) { exec = getenv("SHELL"); @@ -265,7 +269,8 @@ void Session::run() excheck.setFileName(exec); if ( exec.isEmpty() || !excheck.exists() ) { - exec = "/bin/sh"; + qWarning() << "Neither default shell nor $SHELL is set to a correct path. Fallback to" << defaultShell; + exec = defaultShell; } } diff --git a/lib/Session.h b/lib/Session.h index 1a68f1d..9aa8026 100644 --- a/lib/Session.h +++ b/lib/Session.h @@ -28,6 +28,7 @@ #include #include +#include "Emulation.h" #include "History.h" class KProcess; @@ -477,6 +478,11 @@ signals: */ void flowControlEnabledChanged(bool enabled); + /** + * Broker for Emulation::cursorChanged() signal + */ + void cursorChanged(Emulation::KeyboardCursorShape cursorShape, bool blinkingCursorEnabled); + void silence(); void activity(); diff --git a/lib/ShellCommand.cpp b/lib/ShellCommand.cpp index 7440305..210285c 100644 --- a/lib/ShellCommand.cpp +++ b/lib/ShellCommand.cpp @@ -96,8 +96,9 @@ QStringList ShellCommand::expand(const QStringList & items) { QStringList result; - foreach( QString item , items ) - result << expand(item); + foreach(const QString &item, items ) { + result << expand(item); + } return result; } diff --git a/lib/TerminalDisplay.cpp b/lib/TerminalDisplay.cpp index b396954..3e93f13 100644 --- a/lib/TerminalDisplay.cpp +++ b/lib/TerminalDisplay.cpp @@ -241,9 +241,32 @@ void TerminalDisplay::fontChange(const QFont&) emit changedFontMetricSignal( _fontHeight, _fontWidth ); propagateSize(); + + // We will run paint event testing procedure. + // Although this operation will destory the orignal content, + // the content will be drawn again after the test. + _drawTextTestFlag = true; update(); } +void TerminalDisplay::calDrawTextAdditionHeight(QPainter& painter) +{ + QRect test_rect, feedback_rect; + test_rect.setRect(1, 1, _fontWidth * 4, _fontHeight); + painter.drawText(test_rect, Qt::AlignBottom, LTR_OVERRIDE_CHAR + QString("Mq"), &feedback_rect); + + //qDebug() << "test_rect:" << test_rect << "feeback_rect:" << feedback_rect; + + _drawTextAdditionHeight = (feedback_rect.height() - _fontHeight) / 2; + if(_drawTextAdditionHeight < 0) { + _drawTextAdditionHeight = 0; + } + + // update the original content + _drawTextTestFlag = false; + update(); +} + void TerminalDisplay::setVTFont(const QFont& f) { QFont font = f; @@ -334,9 +357,13 @@ TerminalDisplay::TerminalDisplay(QWidget *parent) ,_colorsInverted(false) ,_blendColor(qRgba(0,0,0,0xff)) ,_filterChain(new TerminalImageFilterChain()) -,_cursorShape(QTermWidget::BlockCursor) +,_cursorShape(Emulation::KeyboardCursorShape::BlockCursor) ,mMotionAfterPasting(NoMoveScreenWindow) { + // variables for draw text + _drawTextAdditionHeight = 0; + _drawTextTestFlag = false; + // terminal applications are not designed with Right-To-Left in mind, // so the layout is forced to Left-To-Right setLayoutDirection(Qt::LeftToRight); @@ -739,7 +766,7 @@ void TerminalDisplay::drawCursor(QPainter& painter, else painter.setPen(foregroundColor); - if ( _cursorShape == QTermWidget::BlockCursor ) + if ( _cursorShape == Emulation::KeyboardCursorShape::BlockCursor ) { // draw the cursor outline, adjusting the area so that // it is draw entirely inside 'rect' @@ -761,12 +788,12 @@ void TerminalDisplay::drawCursor(QPainter& painter, } } } - else if ( _cursorShape == QTermWidget::UnderlineCursor ) + else if ( _cursorShape == Emulation::KeyboardCursorShape::UnderlineCursor ) painter.drawLine(cursorRect.left(), cursorRect.bottom(), cursorRect.right(), cursorRect.bottom()); - else if ( _cursorShape == QTermWidget::IBeamCursor ) + else if ( _cursorShape == Emulation::KeyboardCursorShape::IBeamCursor ) painter.drawLine(cursorRect.left(), cursorRect.top(), cursorRect.left(), @@ -783,23 +810,30 @@ void TerminalDisplay::drawCharacters(QPainter& painter, { // don't draw text which is currently blinking if ( _blinking && (style->rendition & RE_BLINK) ) + return; + + // don't draw concealed characters + if (style->rendition & RE_CONCEAL) return; // setup bold and underline - bool useBold; - ColorEntry::FontWeight weight = style->fontWeight(_colorTable); - if (weight == ColorEntry::UseCurrentFormat) - useBold = ((style->rendition & RE_BOLD) && _boldIntense) || font().bold(); - else - useBold = (weight == ColorEntry::Bold) ? true : false; - bool useUnderline = style->rendition & RE_UNDERLINE || font().underline(); + bool useBold = ((style->rendition & RE_BOLD) && _boldIntense) || font().bold(); + const bool useUnderline = style->rendition & RE_UNDERLINE || font().underline(); + const bool useItalic = style->rendition & RE_ITALIC || font().italic(); + const bool useStrikeOut = style->rendition & RE_STRIKEOUT || font().strikeOut(); + const bool useOverline = style->rendition & RE_OVERLINE || font().overline(); QFont font = painter.font(); if ( font.bold() != useBold - || font.underline() != useUnderline ) - { + || font.underline() != useUnderline + || font.italic() != useItalic + || font.strikeOut() != useStrikeOut + || font.overline() != useOverline) { font.setBold(useBold); font.setUnderline(useUnderline); + font.setItalic(useItalic); + font.setStrikeOut(useStrikeOut); + font.setOverline(useOverline); painter.setFont(font); } @@ -818,16 +852,21 @@ void TerminalDisplay::drawCharacters(QPainter& painter, drawLineCharString(painter,rect.x(),rect.y(),text,style); else { - // the drawText(rect,flags,string) overload is used here with null flags - // instead of drawText(rect,string) because the (rect,string) overload causes - // the application's default layout direction to be used instead of - // the widget-specific layout direction, which should always be - // Qt::LeftToRight for this widget - // This was discussed in: http://lists.kde.org/?t=120552223600002&r=1&w=2 - if (_bidiEnabled) - painter.drawText(rect,0,text); - else - painter.drawText(rect, Qt::AlignBottom, LTR_OVERRIDE_CHAR + text); + // Force using LTR as the document layout for the terminal area, because + // there is no use cases for RTL emulator and RTL terminal application. + // + // This still allows RTL characters to be rendered in the RTL way. + painter.setLayoutDirection(Qt::LeftToRight); + + if (_bidiEnabled) { + painter.drawText(rect.x(), rect.y() + _fontAscent + _lineSpacing, text); + } else { + { + QRect drawRect(rect.topLeft(), rect.size()); + drawRect.setHeight(rect.height() + _drawTextAdditionHeight); + painter.drawText(drawRect, Qt::AlignBottom, LTR_OVERRIDE_CHAR + text); + } + } } } @@ -1241,8 +1280,9 @@ void TerminalDisplay::showResizeNotification() } if (!_resizeWidget) { - _resizeWidget = new QLabel("Size: XXX x XXX", this); - _resizeWidget->setMinimumWidth(_resizeWidget->fontMetrics().width("Size: XXX x XXX")); + const QString label = tr("Size: XXX x XXX"); + _resizeWidget = new QLabel(label, this); + _resizeWidget->setMinimumWidth(_resizeWidget->fontMetrics().width(label)); _resizeWidget->setMinimumHeight(_resizeWidget->sizeHint().height()); _resizeWidget->setAlignment(Qt::AlignCenter); @@ -1252,8 +1292,7 @@ void TerminalDisplay::showResizeNotification() _resizeTimer->setSingleShot(true); connect(_resizeTimer, SIGNAL(timeout()), _resizeWidget, SLOT(hide())); } - QString sizeStr = QString("Size: %1 x %2").arg(_columns).arg(_lines); - _resizeWidget->setText(sizeStr); + _resizeWidget->setText(tr("Size: %1 x %2").arg(_columns).arg(_lines)); _resizeWidget->move((width()-_resizeWidget->width())/2, (height()-_resizeWidget->height())/2+20); _resizeWidget->show(); @@ -1332,14 +1371,21 @@ void TerminalDisplay::paintEvent( QPaintEvent* pe ) paint.fillRect(contentsRect(), background); } - foreach (const QRect &rect, (pe->region() & contentsRect()).rects()) + if(_drawTextTestFlag) + { + calDrawTextAdditionHeight(paint); + } + else { - drawBackground(paint,rect,palette().background().color(), - true /* use opacity setting */); - drawContents(paint, rect); + foreach (const QRect &rect, (pe->region() & contentsRect()).rects()) + { + drawBackground(paint,rect,palette().background().color(), + true /* use opacity setting */); + drawContents(paint, rect); + } + drawInputMethodPreeditString(paint,preeditRect()); + paintFilters(paint); } - drawInputMethodPreeditString(paint,preeditRect()); - paintFilters(paint); } QPoint TerminalDisplay::cursorPosition() const diff --git a/lib/TerminalDisplay.h b/lib/TerminalDisplay.h index 8968119..ea0dc27 100644 --- a/lib/TerminalDisplay.h +++ b/lib/TerminalDisplay.h @@ -677,6 +677,8 @@ private: void paintFilters(QPainter& painter); + void calDrawTextAdditionHeight(QPainter& painter); + // returns a region covering all of the areas of the widget which contain // a hotspot QRegion hotSpotRegion() const; @@ -702,6 +704,8 @@ private: int _fontWidth; // width int _fontAscent; // ascend bool _boldIntense; // Whether intense colors should be rendered with bold font + int _drawTextAdditionHeight; // additional height to prevent font trancation + bool _drawTextTestFlag; // indicate it is a testing or not int _leftMargin; // offset int _topMargin; // offset diff --git a/lib/Vt102Emulation.cpp b/lib/Vt102Emulation.cpp index f3bf1f9..077ad9f 100644 --- a/lib/Vt102Emulation.cpp +++ b/lib/Vt102Emulation.cpp @@ -133,6 +133,7 @@ void Vt102Emulation::reset() - ESC_DE - Escape codes of the form C - CSI_PN - Escape codes of the form '[' {Pn} ';' {Pn} C - CSI_PS - Escape codes of the form '[' {Pn} ';' ... C + - CSI_PS_SP - Escape codes of the form '[' {Pn} ';' ... {Space} C - CSI_PR - Escape codes of the form '[' '?' {Pn} ';' ... C - CSI_PE - Escape codes of the form '[' '!' {Pn} ';' ... C - VT52 - VT52 escape codes @@ -160,6 +161,7 @@ void Vt102Emulation::reset() #define TY_CSI_PS(A,N) TY_CONSTRUCT(5,A,N) #define TY_CSI_PN(A ) TY_CONSTRUCT(6,A,0) #define TY_CSI_PR(A,N) TY_CONSTRUCT(7,A,N) +#define TY_CSI_PS_SP(A,N) TY_CONSTRUCT(11,A,N) #define TY_VT52(A) TY_CONSTRUCT(8,A,0) #define TY_CSI_PG(A) TY_CONSTRUCT(9,A,0) @@ -203,7 +205,6 @@ void Vt102Emulation::addToCurrentToken(int cc) } // Character Class flags used while decoding - #define CTL 1 // Control character #define CHR 2 // Printable character #define CPN 4 // TODO: Document me @@ -223,7 +224,7 @@ void Vt102Emulation::initTokenizer() charClass[i] |= CTL; for(i = 32;i < 256; ++i) charClass[i] |= CHR; - for(s = (quint8*)"@ABCDGHILMPSTXZcdfry"; *s; ++s) + for(s = (quint8*)"@ABCDGHILMPSTXZbcdfry"; *s; ++s) charClass[*s] |= CPN; // resize = \e[8;;t for(s = (quint8*)"t"; *s; ++s) @@ -266,17 +267,19 @@ void Vt102Emulation::initTokenizer() #define epp( ) (p >= 3 && s[2] == '?') #define epe( ) (p >= 3 && s[2] == '!') #define egt( ) (p >= 3 && s[2] == '>') +#define esp( ) (p == 4 && s[3] == ' ') #define Xpe (tokenBufferPos >= 2 && tokenBuffer[1] == ']') -#define Xte (Xpe && cc == 7 ) +#define Xte (Xpe && (cc == 7 || cc == 33)) #define ces(C) (cc < 256 && (charClass[cc] & (C)) == (C) && !Xte) -#define ESC 27 #define CNTL(c) ((c)-'@') +#define ESC 27 +#define DEL 127 // process an incoming unicode character void Vt102Emulation::receiveChar(int cc) { - if (cc == 127) + if (cc == DEL) return; //VT100: ignore. if (ces(CTL)) @@ -314,6 +317,12 @@ void Vt102Emulation::receiveChar(int cc) if (les(3,1,SCS)) { processToken( TY_ESC_CS(s[1],s[2]), 0, 0); resetTokenizer(); return; } if (lec(3,1,'#')) { processToken( TY_ESC_DE(s[2]), 0, 0); resetTokenizer(); return; } if (eps( CPN)) { processToken( TY_CSI_PN(cc), argv[0],argv[1]); resetTokenizer(); return; } + if (esp( )) { return; } + if (lec(5, 4, 'q') && s[3] == ' ') { + processToken( TY_CSI_PS_SP(cc, argv[0]), argv[0], 0); + resetTokenizer(); + return; + } // resize = \e[8;;t if (eps(CPS)) @@ -531,7 +540,7 @@ void Vt102Emulation::processToken(int token, int p, int q) case TY_ESC_DE('8' ) : _currentScreen->helpAlign ( ); break; // resize = \e[8;;t - case TY_CSI_PS('t', 8) : setImageSize( q /* columns */, p /* lines */ ); + case TY_CSI_PS('t', 8) : setImageSize( p /*lines */, q /* columns */ ); emit imageResizeRequest(QSize(q, p)); break; @@ -557,18 +566,27 @@ void Vt102Emulation::processToken(int token, int p, int q) case TY_CSI_PS('m', 0) : _currentScreen->setDefaultRendition ( ); break; case TY_CSI_PS('m', 1) : _currentScreen-> setRendition (RE_BOLD ); break; //VT100 + case TY_CSI_PS('m', 2) : _currentScreen-> setRendition (RE_FAINT ); break; case TY_CSI_PS('m', 3) : _currentScreen-> setRendition (RE_ITALIC ); break; //VT100 case TY_CSI_PS('m', 4) : _currentScreen-> setRendition (RE_UNDERLINE); break; //VT100 case TY_CSI_PS('m', 5) : _currentScreen-> setRendition (RE_BLINK ); break; //VT100 case TY_CSI_PS('m', 7) : _currentScreen-> setRendition (RE_REVERSE ); break; + case TY_CSI_PS('m', 8) : _currentScreen-> setRendition (RE_CONCEAL ); break; + case TY_CSI_PS('m', 9) : _currentScreen-> setRendition (RE_STRIKEOUT); break; + case TY_CSI_PS('m', 53) : _currentScreen-> setRendition (RE_OVERLINE ); break; case TY_CSI_PS('m', 10) : /* IGNORED: mapping related */ break; //LINUX case TY_CSI_PS('m', 11) : /* IGNORED: mapping related */ break; //LINUX case TY_CSI_PS('m', 12) : /* IGNORED: mapping related */ break; //LINUX - case TY_CSI_PS('m', 22) : _currentScreen->resetRendition (RE_BOLD ); break; + case TY_CSI_PS('m', 21) : _currentScreen->resetRendition (RE_BOLD ); break; + case TY_CSI_PS('m', 22) : _currentScreen->resetRendition (RE_BOLD ); + _currentScreen->resetRendition (RE_FAINT ); break; case TY_CSI_PS('m', 23) : _currentScreen->resetRendition (RE_ITALIC ); break; //VT100 case TY_CSI_PS('m', 24) : _currentScreen->resetRendition (RE_UNDERLINE); break; case TY_CSI_PS('m', 25) : _currentScreen->resetRendition (RE_BLINK ); break; case TY_CSI_PS('m', 27) : _currentScreen->resetRendition (RE_REVERSE ); break; + case TY_CSI_PS('m', 28) : _currentScreen->resetRendition (RE_CONCEAL ); break; + case TY_CSI_PS('m', 29) : _currentScreen->resetRendition (RE_STRIKEOUT); break; + case TY_CSI_PS('m', 55) : _currentScreen->resetRendition (RE_OVERLINE ); break; case TY_CSI_PS('m', 30) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 0); break; case TY_CSI_PS('m', 31) : _currentScreen->setForeColor (COLOR_SPACE_SYSTEM, 1); break; @@ -624,6 +642,14 @@ void Vt102Emulation::processToken(int token, int p, int q) case TY_CSI_PS('x', 0) : reportTerminalParms ( 2); break; //VT100 case TY_CSI_PS('x', 1) : reportTerminalParms ( 3); break; //VT100 + case TY_CSI_PS_SP('q', 0) : /* fall through */ + case TY_CSI_PS_SP('q', 1) : emit cursorChanged(KeyboardCursorShape::BlockCursor, true ); break; + case TY_CSI_PS_SP('q', 2) : emit cursorChanged(KeyboardCursorShape::BlockCursor, false); break; + case TY_CSI_PS_SP('q', 3) : emit cursorChanged(KeyboardCursorShape::UnderlineCursor, true ); break; + case TY_CSI_PS_SP('q', 4) : emit cursorChanged(KeyboardCursorShape::UnderlineCursor, false); break; + case TY_CSI_PS_SP('q', 5) : emit cursorChanged(KeyboardCursorShape::IBeamCursor, true ); break; + case TY_CSI_PS_SP('q', 6) : emit cursorChanged(KeyboardCursorShape::IBeamCursor, false); break; + case TY_CSI_PN('@' ) : _currentScreen->insertChars (p ); break; case TY_CSI_PN('A' ) : _currentScreen->cursorUp (p ); break; //VT100 case TY_CSI_PN('B' ) : _currentScreen->cursorDown (p ); break; //VT100 @@ -641,6 +667,7 @@ void Vt102Emulation::processToken(int token, int p, int q) case TY_CSI_PN('T' ) : _currentScreen->scrollDown (p ); break; case TY_CSI_PN('X' ) : _currentScreen->eraseChars (p ); break; case TY_CSI_PN('Z' ) : _currentScreen->backtab (p ); break; + case TY_CSI_PN('b' ) : _currentScreen->repeatChars (p ); break; case TY_CSI_PN('c' ) : reportTerminalType ( ); break; //VT100 case TY_CSI_PN('d' ) : _currentScreen->setCursorY (p ); break; //LINUX case TY_CSI_PN('f' ) : _currentScreen->setCursorYX (p, q); break; //VT100 @@ -885,12 +912,12 @@ void Vt102Emulation::reportAnswerBack() /*! `cx',`cy' are 1-based. - `eventType' indicates the button pressed (0-2) - or a general mouse release (3). + `cb' indicates the button pressed or released (0-2) or scroll event (4-5). eventType represents the kind of mouse action that occurred: - 0 = Mouse button press or release + 0 = Mouse button press 1 = Mouse drag + 2 = Mouse button release */ void Vt102Emulation::sendMouseEvent( int cb, int cx, int cy , int eventType ) @@ -905,10 +932,31 @@ void Vt102Emulation::sendMouseEvent( int cb, int cx, int cy , int eventType ) //Mouse motion handling if ((getMode(MODE_Mouse1002) || getMode(MODE_Mouse1003)) && eventType == 1) - cb += 0x20; //add 32 to signify motion event + cb += 0x20; //add 32 to signify motion event + + char command[32]; + command[0] = '\0'; + // Check the extensions in decreasing order of preference. Encoding the release event above assumes that 1006 comes first. + if (getMode(MODE_Mouse1006)) { + snprintf(command, sizeof(command), "\033[<%d;%d;%d%c", cb, cx, cy, eventType == 2 ? 'm' : 'M'); + } else if (getMode(MODE_Mouse1015)) { + snprintf(command, sizeof(command), "\033[%d;%d;%dM", cb + 0x20, cx, cy); + } else if (getMode(MODE_Mouse1005)) { + if (cx <= 2015 && cy <= 2015) { + // The xterm extension uses UTF-8 (up to 2 bytes) to encode + // coordinate+32, no matter what the locale is. We could easily + // convert manually, but QString can also do it for us. + QChar coords[2]; + coords[0] = cx + 0x20; + coords[1] = cy + 0x20; + QString coordsStr = QString(coords, 2); + QByteArray utf8 = coordsStr.toUtf8(); + snprintf(command, sizeof(command), "\033[M%c%s", cb + 0x20, utf8.constData()); + } + } else if (cx <= 223 && cy <= 223) { + snprintf(command, sizeof(command), "\033[M%c%c%c", cb + 0x20, cx + 0x20, cy + 0x20); + } - char command[20]; - sprintf(command,"\033[M%c%c%c",cb+0x20,cx+0x20,cy+0x20); sendString(command); } @@ -965,10 +1013,15 @@ void Vt102Emulation::sendKeyEvent( QKeyEvent* event ) // check flow control state if (modifiers & Qt::ControlModifier) { - if (event->key() == Qt::Key_S) + switch (event->key()) { + case Qt::Key_S: emit flowControlKeyPressed(true); - else if (event->key() == Qt::Key_Q) + break; + case Qt::Key_Q: + case Qt::Key_C: // cancel flow control emit flowControlKeyPressed(false); + break; + } } // lookup key binding @@ -987,6 +1040,7 @@ void Vt102Emulation::sendKeyEvent( QKeyEvent* event ) // (unless there is an entry defined for this particular combination // in the keyboard modifier) bool wantsAltModifier = entry.modifiers() & entry.modifierMask() & Qt::AltModifier; + bool wantsMetaModifier = entry.modifiers() & entry.modifierMask() & Qt::MetaModifier; bool wantsAnyModifier = entry.state() & entry.stateMask() & KeyboardTranslator::AnyModifierState; @@ -995,6 +1049,11 @@ void Vt102Emulation::sendKeyEvent( QKeyEvent* event ) { textToSend.prepend("\033"); } + if ( modifiers & Qt::MetaModifier && !(wantsMetaModifier || wantsAnyModifier) + && !event->text().isEmpty() ) + { + textToSend.prepend("\030@s"); + } if ( entry.command() != KeyboardTranslator::NoCommand ) { @@ -1169,6 +1228,9 @@ void Vt102Emulation::resetModes() resetMode(MODE_Mouse1001); saveMode(MODE_Mouse1001); resetMode(MODE_Mouse1002); saveMode(MODE_Mouse1002); resetMode(MODE_Mouse1003); saveMode(MODE_Mouse1003); + resetMode(MODE_Mouse1005); saveMode(MODE_Mouse1005); + resetMode(MODE_Mouse1006); saveMode(MODE_Mouse1006); + resetMode(MODE_Mouse1015); saveMode(MODE_Mouse1015); resetMode(MODE_BracketedPaste); saveMode(MODE_BracketedPaste); resetMode(MODE_AppScreen); saveMode(MODE_AppScreen); @@ -1268,7 +1330,7 @@ char Vt102Emulation::eraseChar() const 0, 0); if ( entry.text().count() > 0 ) - return entry.text()[0]; + return entry.text().at(0); else return '\b'; } diff --git a/lib/Vt102Emulation.h b/lib/Vt102Emulation.h index af07675..22a9104 100644 --- a/lib/Vt102Emulation.h +++ b/lib/Vt102Emulation.h @@ -133,7 +133,7 @@ private: void resetModes(); void resetTokenizer(); - #define MAX_TOKEN_LENGTH 80 + #define MAX_TOKEN_LENGTH 256 // Max length of tokens (e.g. window title) void addToCurrentToken(int cc); int tokenBuffer[MAX_TOKEN_LENGTH]; //FIXME: overflow? int tokenBufferPos; @@ -153,6 +153,7 @@ private: void processToken(int code, int p, int q); void processWindowAttributeChange(); + void requestWindowAttribute(int); void reportTerminalType(); void reportSecondaryAttributes(); diff --git a/lib/kb-layouts/default.keytab b/lib/kb-layouts/default.keytab index aebd8cf..6e09e44 100644 --- a/lib/kb-layouts/default.keytab +++ b/lib/kb-layouts/default.keytab @@ -62,6 +62,11 @@ key Down -Shift+AnyMod+Ansi : "\E[1;*B" key Right -Shift+AnyMod+Ansi : "\E[1;*C" key Left -Shift+AnyMod+Ansi : "\E[1;*D" +key Up +Shift+AppScreen : "\E[1;*A" +key Down +Shift+AppScreen : "\E[1;*B" +key Left +Shift+AppScreen : "\E[1;*D" +key Right +Shift+AppScreen : "\E[1;*C" + # Keypad keys with NumLock ON # (see "Numeric Keypad" section at http://www.nw.com/nw/WWW/products/wizcon/vt100.html ) # @@ -99,10 +104,10 @@ key End +AppCuKeys+KeyPad : "\EOF" key Home -AppCuKeys+KeyPad : "\E[H" key End -AppCuKeys+KeyPad : "\E[F" -key Insert +KeyPad : "\E[2~" -key Delete +KeyPad : "\E[3~" -key Prior -Shift+KeyPad : "\E[5~" -key Next -Shift+KeyPad : "\E[6~" +key Insert +KeyPad : "\E[2~" +key Delete +KeyPad : "\E[3~" +key PgUp -Shift+KeyPad : "\E[5~" +key PgDown -Shift+KeyPad : "\E[6~" # other grey PC keys @@ -121,10 +126,10 @@ key Delete -AnyMod : "\E[3~" key Insert +AnyMod : "\E[2;*~" key Delete +AnyMod : "\E[3;*~" -key Prior -Shift-AnyMod : "\E[5~" -key Next -Shift-AnyMod : "\E[6~" -key Prior -Shift+AnyMod : "\E[5;*~" -key Next -Shift+AnyMod : "\E[6;*~" +key PgUp -Shift-AnyMod : "\E[5~" +key PgDown -Shift-AnyMod : "\E[6~" +key PgUp -Shift+AnyMod : "\E[5;*~" +key PgDown -Shift+AnyMod : "\E[6;*~" # Function keys key F1 -AnyMod : "\EOP" @@ -160,10 +165,12 @@ key Space +Control : "\x00" # Some keys are used by konsole to cause operations. # The scroll* operations refer to the history buffer. -key Up +Shift-AppScreen : scrollLineUp -key Prior +Shift-AppScreen : scrollPageUp -key Down +Shift-AppScreen : scrollLineDown -key Next +Shift-AppScreen : scrollPageDown +key Up +Shift-AppScreen : scrollLineUp +key PgUp +Shift-AppScreen : scrollPageUp +key Home +Shift-AppScreen : scrollUpToTop +key Down +Shift-AppScreen : scrollLineDown +key PgDown +Shift-AppScreen : scrollPageDown +key End +Shift-AppScreen : scrollDownToBottom key ScrollLock : scrollLock diff --git a/lib/kb-layouts/historic/vt100.keytab b/lib/kb-layouts/historic/vt100.keytab index dec49ba..dc79b5f 100644 --- a/lib/kb-layouts/historic/vt100.keytab +++ b/lib/kb-layouts/historic/vt100.keytab @@ -102,9 +102,9 @@ key F12 : "\E[24~" key Home : "\E[H" key End : "\E[F" -key Prior -Shift : "\E[5~" -key Next -Shift : "\E[6~" -key Insert-Shift : "\E[2~" +key PgUp -Shift : "\E[5~" +key PgDown -Shift : "\E[6~" +key Insert -Shift : "\E[2~" # Keypad-Enter. See comment on Return above. @@ -115,10 +115,10 @@ key Space +Control : "\x00" # some of keys are used by konsole. -key Up +Shift : scrollLineUp -key Prior +Shift : scrollPageUp -key Down +Shift : scrollLineDown -key Next +Shift : scrollPageDown +key Up +Shift : scrollLineUp +key PgUp +Shift : scrollPageUp +key Down +Shift : scrollLineDown +key PgDown +Shift : scrollPageDown key ScrollLock : scrollLock diff --git a/lib/kb-layouts/historic/x11r5.keytab b/lib/kb-layouts/historic/x11r5.keytab index 75ba06e..e17da0d 100644 --- a/lib/kb-layouts/historic/x11r5.keytab +++ b/lib/kb-layouts/historic/x11r5.keytab @@ -36,8 +36,8 @@ key Home : "\E[1~" key Insert-Shift : "\E[2~" key Delete : "\E[3~" key End : "\E[4~" -key Prior -Shift : "\E[5~" -key Next -Shift : "\E[6~" +key PgUp -Shift : "\E[5~" +key PgDown -Shift : "\E[6~" # function keys @@ -61,10 +61,10 @@ key Space +Control : "\x00" # Some keys are used by konsole to cause operations. # The scroll* operations refer to the history buffer. -key Up +Shift : scrollLineUp -key Prior +Shift : scrollPageUp -key Down +Shift : scrollLineDown -key Next +Shift : scrollPageDown +key Up +Shift : scrollLineUp +key PgUp +Shift : scrollPageUp +key Down +Shift : scrollLineDown +key PgDown +Shift : scrollPageDown key ScrollLock : scrollLock diff --git a/lib/kb-layouts/linux.keytab b/lib/kb-layouts/linux.keytab index 94a39fb..eefed09 100644 --- a/lib/kb-layouts/linux.keytab +++ b/lib/kb-layouts/linux.keytab @@ -108,9 +108,9 @@ key F12 : "\E[24~" key Home : "\E[1~" key End : "\E[4~" -key Prior -Shift : "\E[5~" -key Next -Shift : "\E[6~" -key Insert-Shift : "\E[2~" +key PgUp -Shift : "\E[5~" +key PgDown -Shift : "\E[6~" +key Insert -Shift : "\E[2~" # Keypad-Enter. See comment on Return above. @@ -121,10 +121,10 @@ key Space +Control : "\x00" # some of keys are used by konsole. -key Up +Shift : scrollLineUp -key Prior +Shift : scrollPageUp -key Down +Shift : scrollLineDown -key Next +Shift : scrollPageDown +key Up +Shift : scrollLineUp +key PgUp +Shift : scrollPageUp +key Down +Shift : scrollLineDown +key PgDown +Shift : scrollPageDown key ScrollLock : scrollLock diff --git a/lib/kb-layouts/macbook.keytab b/lib/kb-layouts/macbook.keytab index adbc784..71e61ae 100644 --- a/lib/kb-layouts/macbook.keytab +++ b/lib/kb-layouts/macbook.keytab @@ -99,10 +99,10 @@ key Delete -AnyMod : "\E[3~" key Insert +AnyMod : "\E[2;*~" key Delete +AnyMod : "\E[3;*~" -key Prior -Shift-AnyMod : "\E[5~" -key Next -Shift-AnyMod : "\E[6~" -key Prior -Shift+AnyMod : "\E[5;*~" -key Next -Shift+AnyMod : "\E[6;*~" +key PgUp -Shift-AnyMod : "\E[5~" +key PgDown -Shift-AnyMod : "\E[6~" +key PgUp -Shift+AnyMod : "\E[5;*~" +key PgDown -Shift+AnyMod : "\E[6;*~" # Function keys #key F1 -AnyMod : "\EOP" @@ -160,10 +160,10 @@ key Space +Control : "\x00" # Some keys are used by konsole to cause operations. # The scroll* operations refer to the history buffer. -key Up +Shift-AppScreen : scrollLineUp -key Prior +Shift-AppScreen : scrollPageUp -key Down +Shift-AppScreen : scrollLineDown -key Next +Shift-AppScreen : scrollPageDown +key Up +Shift-AppScreen : scrollLineUp +key PgUp +Shift-AppScreen : scrollPageUp +key Down +Shift-AppScreen : scrollLineDown +key PgDown +Shift-AppScreen : scrollPageDown #key Up +Shift : scrollLineUp #key Prior +Shift : scrollPageUp diff --git a/lib/kb-layouts/solaris.keytab b/lib/kb-layouts/solaris.keytab index 0739edf..30e8b8f 100644 --- a/lib/kb-layouts/solaris.keytab +++ b/lib/kb-layouts/solaris.keytab @@ -72,8 +72,8 @@ key Home : "\E[1~" key Insert-Shift : "\E[2~" key Delete : "\E[3~" key End : "\E[4~" -key Prior -Shift : "\E[5~" -key Next -Shift : "\E[6~" +key PgUp -Shift : "\E[5~" +key PgDown -Shift : "\E[6~" # function keys @@ -99,10 +99,10 @@ key Space +Control : "\x00" #key Left +Shift : prevSession #key Right +Shift : nextSession -key Up +Shift : scrollLineUp -key Prior +Shift : scrollPageUp -key Down +Shift : scrollLineDown -key Next +Shift : scrollPageDown +key Up +Shift : scrollLineUp +key PgUp +Shift : scrollPageUp +key Down +Shift : scrollLineDown +key PgDown +Shift : scrollPageDown #key Insert+Shift : emitSelection # keypad characters are not offered differently by Qt. diff --git a/lib/kb-layouts/vt420pc.keytab b/lib/kb-layouts/vt420pc.keytab index ee6aa9a..7ccb88b 100644 --- a/lib/kb-layouts/vt420pc.keytab +++ b/lib/kb-layouts/vt420pc.keytab @@ -138,9 +138,9 @@ key F12+Shift : "\E[24;2~" key Home : "\E[H" key End : "\E[F" -key Prior -Shift : "\E[5~" -key Next -Shift : "\E[6~" -key Insert-Shift : "\E[2~" +key PgUp -Shift : "\E[5~" +key PgDown -Shift : "\E[6~" +key Insert -Shift : "\E[2~" # Keypad-Enter. See comment on Return above. @@ -151,10 +151,10 @@ key Space +Control : "\x00" # some of keys are used by konsole. -key Up +Shift : scrollLineUp -key Prior +Shift : scrollPageUp -key Down +Shift : scrollLineDown -key Next +Shift : scrollPageDown +key Up +Shift : scrollLineUp +key PgUp +Shift : scrollPageUp +key Down +Shift : scrollLineDown +key PgDown +Shift : scrollPageDown key ScrollLock : scrollLock diff --git a/lib/kpty.cpp b/lib/kpty.cpp index fbcdb6c..0f3348e 100644 --- a/lib/kpty.cpp +++ b/lib/kpty.cpp @@ -503,7 +503,11 @@ void KPty::login(const char * user, const char * remotehost) // note: strncpy without terminators _is_ correct here. man 4 utmp if (user) { +# ifdef HAVE_UTMPX + strncpy(l_struct.ut_user, user, sizeof(l_struct.ut_user)); +# else strncpy(l_struct.ut_name, user, sizeof(l_struct.ut_name)); +# endif } if (remotehost) { @@ -614,7 +618,11 @@ void KPty::logout() setutent(); if ((ut = getutline(&l_struct))) { # endif +# ifdef HAVE_UTMPX + memset(ut->ut_user, 0, sizeof(*ut->ut_user)); +# else memset(ut->ut_name, 0, sizeof(*ut->ut_name)); +# endif memset(ut->ut_host, 0, sizeof(*ut->ut_host)); # ifdef HAVE_STRUCT_UTMP_UT_SYSLEN ut->ut_syslen = 0; diff --git a/lib/kptydevice.h b/lib/kptydevice.h index 0fccd62..c398646 100644 --- a/lib/kptydevice.h +++ b/lib/kptydevice.h @@ -278,7 +278,7 @@ public: { int index = 0; int start = head; - QLinkedList::ConstIterator it = buffers.begin(); + QLinkedList::ConstIterator it = buffers.constBegin(); forever { if (!maxLength) return index; diff --git a/lib/qtermwidget.cpp b/lib/qtermwidget.cpp index 21f0a0a..e3fa23d 100644 --- a/lib/qtermwidget.cpp +++ b/lib/qtermwidget.cpp @@ -246,8 +246,28 @@ void QTermWidget::init(int startnow) m_layout->setMargin(0); setLayout(m_layout); + // translations + // First check $XDG_DATA_DIRS. This follows the implementation in libqtxdg + QString d = QFile::decodeName(qgetenv("XDG_DATA_DIRS")); + QStringList dirs = d.split(QLatin1Char(':'), QString::SkipEmptyParts); + if (dirs.isEmpty()) { + dirs.append(QString::fromLatin1("/usr/local/share")); + dirs.append(QString::fromLatin1("/usr/share")); + } + dirs.append(QFile::decodeName(TRANSLATIONS_DIR)); + + m_translator = new QTranslator(this); + + for (const QString& dir : dirs) { + qDebug() << "Trying to load translation file from dir" << dir; + if (m_translator->load(QLocale::system(), "qtermwidget", "_", dir)) { + qApp->installTranslator(m_translator); + qDebug() << "Translations found in" << dir; + break; + } + } + m_impl = new TermWidgetImpl(this); - m_impl->m_terminalDisplay->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); m_layout->addWidget(m_impl->m_terminalDisplay); connect(m_impl->m_session, SIGNAL(bellRequest(QString)), m_impl->m_terminalDisplay, SLOT(bell(QString))); @@ -255,6 +275,8 @@ void QTermWidget::init(int startnow) connect(m_impl->m_session, SIGNAL(activity()), this, SIGNAL(activity())); connect(m_impl->m_session, SIGNAL(silence()), this, SIGNAL(silence())); + connect(m_impl->m_session, &Session::profileChangeCommandReceived, this, &QTermWidget::profileChanged); + connect(m_impl->m_session, &Session::receivedData, this, &QTermWidget::receivedData); // That's OK, FilterChain's dtor takes care of UrlFilter. UrlFilter *urlFilter = new UrlFilter(); @@ -296,13 +318,14 @@ void QTermWidget::init(int startnow) m_searchBar->setFont(font); setScrollBarPosition(NoScrollBar); - setKeyboardCursorShape(BlockCursor); + setKeyboardCursorShape(Emulation::KeyboardCursorShape::BlockCursor); m_impl->m_session->addView(m_impl->m_terminalDisplay); connect(m_impl->m_session, SIGNAL(resizeRequest(QSize)), this, SLOT(setSize(QSize))); connect(m_impl->m_session, SIGNAL(finished()), this, SLOT(sessionFinished())); connect(m_impl->m_session, &Session::titleChanged, this, &QTermWidget::titleChanged); + connect(m_impl->m_session, &Session::cursorChanged, this, &QTermWidget::cursorChanged); } @@ -668,6 +691,13 @@ void QTermWidget::setKeyboardCursorShape(KeyboardCursorShape shape) m_impl->m_terminalDisplay->setKeyboardCursorShape(shape); } +void QTermWidget::setBlinkingCursor(bool blink) +{ + if (!m_impl->m_terminalDisplay) + return; + m_impl->m_terminalDisplay->setBlinkingCursor(blink); +} + QString QTermWidget::title() const { QString title = m_impl->m_session->userTitle(); @@ -688,3 +718,15 @@ bool QTermWidget::isTitleChanged() const { return m_impl->m_session->isTitleChanged(); } + +void QTermWidget::setAutoClose(bool autoClose) +{ + m_impl->m_session->setAutoClose(autoClose); +} + +void QTermWidget::cursorChanged(Konsole::Emulation::KeyboardCursorShape cursorShape, bool blinkingCursorEnabled) +{ + // TODO: A switch to enable/disable DECSCUSR? + setKeyboardCursorShape(cursorShape); + setBlinkingCursor(blinkingCursorEnabled); +} diff --git a/lib/qtermwidget.h b/lib/qtermwidget.h index e486f58..ce10c45 100644 --- a/lib/qtermwidget.h +++ b/lib/qtermwidget.h @@ -20,15 +20,18 @@ #ifndef _Q_TERM_WIDGET #define _Q_TERM_WIDGET +#include #include +#include "Emulation.h" #include "Filter.h" +#include "qtermwidget_export.h" class QVBoxLayout; struct TermWidgetImpl; class SearchBar; class QUrl; -class QTermWidget : public QWidget { +class QTERMWIDGET_EXPORT QTermWidget : public QWidget { Q_OBJECT public: @@ -44,24 +47,7 @@ public: ScrollBarRight = 2 }; - /** - * This enum describes the available shapes for the keyboard cursor. - * See setKeyboardCursorShape() - */ - enum KeyboardCursorShape { - /** A rectangular block which covers the entire area of the cursor character. */ - BlockCursor = 0, - /** - * A single flat line which occupies the space at the bottom of the cursor - * character's area. - */ - UnderlineCursor = 1, - /** - * An cursor shaped like the capital letter 'I', similar to the IBeam - * cursor used in Qt/KDE text editors. - */ - IBeamCursor = 2 - }; + using KeyboardCursorShape = Konsole::Emulation::KeyboardCursorShape; //Creation of widget QTermWidget(int startnow, // 1 = start shell programm immediatelly @@ -211,6 +197,15 @@ public: */ void setKeyboardCursorShape(KeyboardCursorShape shape); + void setBlinkingCursor(bool blink); + + + /** + * Automatically close the terminal session after the shell process exits or + * keep it running. + */ + void setAutoClose(bool); + QString title() const; QString icon() const; @@ -240,8 +235,16 @@ signals: */ void sendData(const char *,int); + void profileChanged(const QString & profile); + void titleChanged(); + /** + * Signals that we received new data from the process running in the + * terminal emulator + */ + void receivedData(const QString &text); + public slots: // Copy selection to clipboard void copyClipboard(); @@ -282,6 +285,11 @@ private slots: void findPrevious(); void matchFound(int startColumn, int startLine, int endColumn, int endLine); void noMatchFound(); + /** + * Emulation::cursorChanged() signal propogates to here and QTermWidget + * sends the specified cursor states to the terminal display + */ + void cursorChanged(Konsole::Emulation::KeyboardCursorShape cursorShape, bool blinkingCursorEnabled); private: void search(bool forwards, bool next); @@ -290,6 +298,7 @@ private: TermWidgetImpl * m_impl; SearchBar* m_searchBar; QVBoxLayout *m_layout; + QTranslator *m_translator; }; diff --git a/lib/tools.cpp b/lib/tools.cpp index d7054ea..1269e40 100644 --- a/lib/tools.cpp +++ b/lib/tools.cpp @@ -92,7 +92,7 @@ const QStringList get_color_schemes_dirs() rval << (QCoreApplication::applicationDirPath() + "/../Resources/color-schemes/"); } #endif - for (const QString& custom_dir : custom_color_schemes_dirs) + for (const QString& custom_dir : const_cast(custom_color_schemes_dirs)) { d.setPath(custom_dir); if (d.exists()) diff --git a/pyqt/README.md b/pyqt/README.md index 71e8758..4f45e97 100644 --- a/pyqt/README.md +++ b/pyqt/README.md @@ -4,31 +4,33 @@ PyQt5 Bindings for QTermWidget INSTALL: ------------ -####1. Download QTermWidget -> https://github.com/lxde/qtermwidget -####2. Compile and install it: - $ mkdir build && cd build +####1. Download, compile and install QTermWidget: + $ git clone https://github.com/lxde/qtermwidget.git + $ cd qtermwidget && mkdir build && cd build $ cmake .. $ make $ sudo make install If `make install` command will not work just copy the `qtermwidget.so*` files to /usr/lib directory. -####3. Install PyQt5 and PyQt5-devel if not yet installed. -####4. Configure, compile and install bindings. Execute in terminal in the qtermwidget bindings folder: - - $ python config.py +####2. Install PyQt5 and PyQt5-devel if not yet installed. +####3. Configure, compile and install Python bindings. Execute in terminal in the qtermwidget bindings folder: + $ cd pyqt/ + $ QT_SELECT=5 python config.py $ make $ sudo make install -####5. You can run ./test.py to test the installed module. +####4. You can run ./test.py to test the installed module. ABOUT: --------- +Curently maintained by: +- Pawel Koston + Based on previous PyQt4 bindings by: - Piotr "Riklaunim" Maliński , - Alexander Slesarev - PyQt5 QTermWidget Bindings License: GPL3 diff --git a/pyqt/config.py b/pyqt/config.py index 70ad215..5925213 100755 --- a/pyqt/config.py +++ b/pyqt/config.py @@ -5,66 +5,74 @@ import os import site import pprint from distutils import sysconfig -import pyqtconfig from PyQt5 import QtCore import PyQt5 + class Configuration(sipconfig.Configuration): - """The class that represents PyQt configuration values. - """ - def getEnv(self,name, default): - return os.environ.get(name) or default - - def __init__(self): - qtconfig = subprocess.check_output(["/usr/lib64/qt5/bin/qmake", "-query"], universal_newlines=True) - qtconfig = dict(x.split(":", 1) for x in qtconfig.splitlines()) - - self.pyQtIncludePath = self.getEnv('PYQT_INCLUDE_PATH','/usr/share/sip/PyQt5' ) - - pyqtconfig = { - "pyqt_config_args": "--confirm-license -v "+str(self.pyQtIncludePath)+" --qsci-api -q /usr/lib64/qt5/bin/qmake", - "pyqt_version": QtCore.PYQT_VERSION, - "pyqt_version_str": QtCore.PYQT_VERSION_STR, - "pyqt_bin_dir": PyQt5.__path__[0], - "pyqt_mod_dir": PyQt5.__path__[0], - "pyqt_sip_dir": str(self.pyQtIncludePath), - "pyqt_modules": "QtCore QtGui QtWidgets", #... and many more - "pyqt_sip_flags": QtCore.PYQT_CONFIGURATION['sip_flags'], - "qt_version": QtCore.QT_VERSION, - "qt_edition": "free", - "qt_winconfig": "shared", - "qt_framework": 0, - "qt_threaded": 1, - "qt_dir": qtconfig['QT_INSTALL_PREFIX'], - "qt_data_dir": qtconfig['QT_INSTALL_DATA'], - "qt_archdata_dir": qtconfig['QT_INSTALL_DATA'], - "qt_inc_dir": qtconfig['QT_INSTALL_HEADERS'], - "qt_lib_dir": qtconfig['QT_INSTALL_LIBS'] - } - - macros = sipconfig._default_macros.copy() - macros['INCDIR_QT'] = qtconfig['QT_INSTALL_HEADERS'] - macros['LIBDIR_QT'] = qtconfig['QT_INSTALL_LIBS'] - macros['MOC'] = os.path.join(qtconfig['QT_INSTALL_BINS'], 'moc') - - sipconfig.Configuration.__init__(self, [pyqtconfig]) - self.set_build_macros(macros) - - -## The name of the SIP build file generated by SIP and used by the build system. + """The class that represents PyQt configuration values. + """ + + def getEnv(self, name, default): + return os.environ.get(name) or default + + def __init__(self): + qmake_bin = subprocess.check_output( + ["which", "qmake"], universal_newlines=True).strip(' \t\n\r') + qtconfig = subprocess.check_output( + [qmake_bin, "-query"], universal_newlines=True) + qtconfig = dict(x.split(":", 1) for x in qtconfig.splitlines()) + + self.pyQtIncludePath = self.getEnv( + 'PYQT_INCLUDE_PATH', '/usr/share/sip/PyQt5') + + pyqtconfig = { + "pyqt_config_args": "--confirm-license -v " + str(self.pyQtIncludePath) + " --qsci-api -q " + qmake_bin, + "pyqt_version": QtCore.PYQT_VERSION, + "pyqt_version_str": QtCore.PYQT_VERSION_STR, + "pyqt_bin_dir": PyQt5.__path__[0], + "pyqt_mod_dir": PyQt5.__path__[0], + "pyqt_sip_dir": str(self.pyQtIncludePath), + "pyqt_modules": "QtCore QtGui QtWidgets", # ... and many more + "pyqt_sip_flags": QtCore.PYQT_CONFIGURATION['sip_flags'], + "qt_version": QtCore.QT_VERSION, + "qt_edition": "free", + "qt_winconfig": "shared", + "qt_framework": 0, + "qt_threaded": 1, + "qt_dir": qtconfig['QT_INSTALL_PREFIX'], + "qt_data_dir": qtconfig['QT_INSTALL_DATA'], + "qt_archdata_dir": qtconfig['QT_INSTALL_DATA'], + "qt_inc_dir": qtconfig['QT_INSTALL_HEADERS'], + "qt_lib_dir": qtconfig['QT_INSTALL_LIBS'] + } + + macros = sipconfig._default_macros.copy() + macros['INCDIR_QT'] = qtconfig['QT_INSTALL_HEADERS'] + macros['LIBDIR_QT'] = qtconfig['QT_INSTALL_LIBS'] + macros['MOC'] = os.path.join(qtconfig['QT_INSTALL_BINS'], 'moc') + + sipconfig.Configuration.__init__(self, [pyqtconfig]) + self.set_build_macros(macros) + + +# The name of the SIP build file generated by SIP and used by the build system. build_file = "qtermwidget.sbf" # Get the SIP configuration information. config = Configuration() # Run SIP to generate the build_file -os.system(" ".join([config.sip_bin, '-I' , str(config.pyQtIncludePath), str(config.pyqt_sip_flags), "-b", build_file,"-o", "-c", ". " " qtermwidget.sip"])) +os.system(" ".join([config.sip_bin, '-I', str(config.pyQtIncludePath), str( + config.pyqt_sip_flags), "-b", build_file, "-o", "-c", ". " " qtermwidget.sip"])) installs = [] -installs.append(["qtermwidget.sip", os.path.join(config.pyqt_sip_dir,"qtermwidget")]) +installs.append(["qtermwidget.sip", os.path.join( + config.pyqt_sip_dir, "qtermwidget")]) installs.append(["qtermwidgetconfig.py", config.pyqt_mod_dir]) -makefile = sipconfig.SIPModuleMakefile( configuration = config, build_file = build_file, installs = installs, qt=["QtCore" ,"QtGui", "QtWidgets"] ) +makefile = sipconfig.SIPModuleMakefile( + configuration=config, build_file=build_file, installs=installs, qt=["QtCore", "QtGui", "QtWidgets"]) # Add the library we are wrapping. The name doesn't include any platform # specific prefixes or extensions (e.g. the "lib" prefix on UNIX, or the @@ -73,20 +81,23 @@ makefile.extra_lib_dirs.append("../lib/") makefile.extra_lib_dirs.append("..") makefile.extra_libs = ["qtermwidget5"] +# Support for C++11 +makefile.extra_cxxflags.append('-std=c++11') + # Generate the Makefile itself. makefile.generate() content = { - # Publish where the SIP specifications for this module will be - # installed. - "qtermwidget_sip_dir": config.pyqt_sip_dir, - - # Publish the set of SIP flags needed by this module. As these are the - # same flags needed by the qt module we could leave it out, but this - # allows us to change the flags at a later date without breaking - # scripts that import the configuration module. - "qtermwidget_sip_flags": config.pyqt_sip_flags - } + # Publish where the SIP specifications for this module will be + # installed. + "qtermwidget_sip_dir": config.pyqt_sip_dir, + + # Publish the set of SIP flags needed by this module. As these are the + # same flags needed by the qt module we could leave it out, but this + # allows us to change the flags at a later date without breaking + # scripts that import the configuration module. + "qtermwidget_sip_flags": config.pyqt_sip_flags +} # This creates the qtermwidgetconfig.py module from the qtermwidgetconfig.py.in # template and the dictionary. diff --git a/pyqt/qtermwidget.sip b/pyqt/qtermwidget.sip index 51a4f80..444e2f8 100644 --- a/pyqt/qtermwidget.sip +++ b/pyqt/qtermwidget.sip @@ -1,8 +1,6 @@ %Module QTermWidget - - %Import QtGui/QtGuimod.sip %Import QtCore/QtCoremod.sip %Import QtWidgets/QtWidgetsmod.sip @@ -21,9 +19,16 @@ public: ScrollBarRight=2 }; + enum KeyboardCursorShape + { + BlockCursor=0, + UnderlineCursor=1, + IBeamCursor=2 + }; + QTermWidget(int startnow = 1, QWidget *parent = 0); ~QTermWidget(); - + void startTerminalTeletype(); QSize sizeHint() const; void startShellProgram(); int getShellPID(); @@ -35,10 +40,11 @@ public: void setShellProgram(const QString & progname); void setWorkingDirectory(const QString & dir); QString workingDirectory(); - void setArgs(QStringList &args); + void setArgs(QStringList & args); void setTextCodec(QTextCodec *codec); void setColorScheme(const QString & name); static QStringList availableColorSchemes(); + static void addCustomColorSchemeDir(const QString& custom_dir); void setHistorySize(int lines); void setScrollBarPosition(ScrollBarPosition); void scrollToEnd(); @@ -59,28 +65,48 @@ public: void setMonitorActivity(bool); void setMonitorSilence(bool); void setSilenceTimeout(int seconds); + int getPtySlaveFd() const; + void setKeyboardCursorShape(KeyboardCursorShape shape); + void setAutoClose(bool); + QString title() const; + QString icon() const; signals: void finished(); void copyAvailable(bool); void termGetFocus(); void termLostFocus(); void termKeyPressed(QKeyEvent *); - void urlActivated(const QUrl&); + void urlActivated(const QUrl&, bool fromContextMenu); void bell(const QString& message); void activity(); void silence(); + void sendData(const char *,int); + void titleChanged(); + void receivedData(const QString &text); + void profileChanged(const QString & profile); public slots: void copyClipboard(); void pasteClipboard(); void pasteSelection(); void zoomIn(); void zoomOut(); + void setSize(const QSize &); void setKeyBindings(const QString & kb); void clear(); void toggleShowSearchBar(); - void setSize(const QSize&); protected: - void resizeEvent(QResizeEvent *e); + virtual void resizeEvent(QResizeEvent *); +protected slots: + void sessionFinished(); + void selectionChanged(bool textSelected); private: - void *createTermWidget(int startnow, void *parent); + void search(bool forwards, bool next); + void setZoom(int step); + void init(int startnow); +private slots: + void find(); + void findNext(); + void findPrevious(); + void matchFound(int startColumn, int startLine, int endColumn, int endLine); + void noMatchFound(); };