From: Uwe Hermann Date: Tue, 13 Jun 2017 14:49:11 +0000 (+0200) Subject: Backport recent changes from mainline. X-Git-Tag: pulseview-0.4.1~4 X-Git-Url: https://sigrok.org/gitweb/?p=pulseview.git;a=commitdiff_plain;h=3782d8609d2f4bd66855dc4f72c0f74d9bc11c23 Backport recent changes from mainline. This includes all changes from 0adee2d93430b5fb879388e66f3bd44fd11b3b22 manual: Bump date. up to 10906507eb3d32346506a12d4387205f7c7c6e72 HACKING: Update URL to Linux kernel coding style. --- diff --git a/.gitignore b/.gitignore index ea272ee6..59d7aa2f 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,5 @@ android/src/org/kde/ # QtCreator cruft /CMakeLists.txt.user* *.cbp + +doxy diff --git a/CMakeLists.txt b/CMakeLists.txt index af39c89e..b74792de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,16 +24,27 @@ include(GNUInstallDirs) project(pulseview) +# Let AUTOMOC and AUTOUIC process GENERATED files. +if(POLICY CMP0071) + cmake_policy(SET CMP0071 NEW) +endif() + +# Only interpret if() arguments as variables or keywords when unquoted. +if(POLICY CMP0054) + cmake_policy(SET CMP0054 NEW) +endif() + list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMake") #=============================================================================== #= User Options #------------------------------------------------------------------------------- -option(DISABLE_WERROR "Build without -Werror" FALSE) +option(DISABLE_WERROR "Build without -Werror" TRUE) option(ENABLE_SIGNALS "Build with UNIX signals" TRUE) +option(ENABLE_STACKTRACE "Enable stack trace when crashing" FALSE) option(ENABLE_DECODE "Build with libsigrokdecode" TRUE) -option(ENABLE_TESTS "Enable unit tests" TRUE) +option(ENABLE_TESTS "Enable unit tests" FALSE) option(STATIC_PKGDEPS_LIBS "Statically link to (pkg-config) libraries" FALSE) if(WIN32) @@ -51,6 +62,12 @@ if(NOT CMAKE_BUILD_TYPE) FORCE) endif() +#=============================================================================== +#= Documentation +#------------------------------------------------------------------------------- + +add_subdirectory(manual) + #=============================================================================== #= Dependencies #------------------------------------------------------------------------------- @@ -58,10 +75,11 @@ endif() list(APPEND PKGDEPS glib-2.0>=2.28.0) list(APPEND PKGDEPS glibmm-2.4>=2.28.0) -list(APPEND PKGDEPS libsigrokcxx>=0.5.0) +set(LIBSR_CXX_BINDING "libsigrokcxx>=0.5.1") +list(APPEND PKGDEPS "${LIBSR_CXX_BINDING}") if(ENABLE_DECODE) - list(APPEND PKGDEPS libsigrokdecode>=0.5.0) + list(APPEND PKGDEPS libsigrokdecode>=0.5.2) endif() if(ANDROID) @@ -69,6 +87,10 @@ if(ANDROID) endif() find_package(PkgConfig) +pkg_check_modules(LIBSRCXX QUIET ${LIBSR_CXX_BINDING}) +if(NOT LIBSRCXX_FOUND OR NOT LIBSRCXX_VERSION) + message(FATAL_ERROR "libsigrok C++ bindings missing, check libsigrok's 'configure' output (missing dependencies?)") +endif() pkg_check_modules(PKGDEPS REQUIRED ${PKGDEPS}) set(CMAKE_AUTOMOC TRUE) @@ -78,7 +100,10 @@ find_package(Qt5 COMPONENTS Core Gui Widgets Svg REQUIRED) if(WIN32) # MXE workaround: Use pkg-config to find Qt5 libs. # https://github.com/mxe/mxe/issues/1642 - pkg_check_modules(QT5ALL REQUIRED Qt5Widgets Qt5Gui Qt5Svg) + # Not required (and doesn't work) on MSYS2. + if(NOT DEFINED ENV{MSYSTEM}) + pkg_check_modules(QT5ALL REQUIRED Qt5Widgets Qt5Gui Qt5Svg) + endif() endif() set(QT_LIBRARIES Qt5::Gui Qt5::Widgets Qt5::Svg) @@ -87,7 +112,12 @@ set(BOOSTCOMPS filesystem serialization system) if(ENABLE_TESTS) list(APPEND BOOSTCOMPS unit_test_framework) endif() -find_package(Boost 1.55 COMPONENTS ${BOOSTCOMPS} REQUIRED) + +if(ENABLE_STACKTRACE) + find_package(Boost 1.65.1 COMPONENTS ${BOOSTCOMPS} REQUIRED) +else() + find_package(Boost 1.55 COMPONENTS ${BOOSTCOMPS} REQUIRED) +endif() # Find the platform's thread library (needed for C++11 threads). # This will set ${CMAKE_THREAD_LIBS_INIT} to the correct, OS-specific value. @@ -153,7 +183,7 @@ memaccess_check_unaligned_le(HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS) #------------------------------------------------------------------------------- set(PV_TITLE PulseView) -set(PV_VERSION_STRING "0.4.0") +set(PV_VERSION_STRING "0.5.0") set(PV_GLIBMM_VERSION ${PKGDEPS_glibmm-2.4_VERSION}) @@ -167,6 +197,12 @@ if(NOT PV_TAG_VERSION_STRING) string(SUBSTRING "${PV_HASH}" 0 7 PV_SHORTHASH) set(PV_VERSION_STRING "${PV_VERSION_STRING}-git-${PV_SHORTHASH}") endif() + + # Non-tagged releases use the unstable manual + set(PV_MANUAL_VERSION "unstable") +else() + # Tagged releases use a fixed manual version + set(PV_MANUAL_VERSION ${PV_VERSION_STRING}) endif() if(PV_VERSION_STRING MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-[-0-9a-z]*)?$") @@ -176,7 +212,7 @@ if(PV_VERSION_STRING MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)(-[-0-9a-z]*)?$") set(PV_VERSION_SUFFIX ${CMAKE_MATCH_4}) endif() -message("-- ${PV_TITLE} version: ${PV_VERSION_STRING}") +message(STATUS "${PV_TITLE} version: ${PV_VERSION_STRING}") configure_file ( ${PROJECT_SOURCE_DIR}/config.h.in @@ -192,6 +228,7 @@ set(pulseview_SOURCES pv/application.cpp pv/devicemanager.cpp pv/globalsettings.cpp + pv/logging.cpp pv/mainwindow.cpp pv/session.cpp pv/storesession.cpp @@ -234,7 +271,6 @@ set(pulseview_SOURCES pv/views/trace/rowitem.cpp pv/views/trace/ruler.cpp pv/views/trace/signal.cpp - pv/views/trace/signalscalehandle.cpp pv/views/trace/timeitem.cpp pv/views/trace/timemarker.cpp pv/views/trace/trace.cpp @@ -251,8 +287,8 @@ set(pulseview_SOURCES pv/views/trace/viewwidget.cpp pv/views/viewbase.cpp pv/views/trace/standardbar.cpp - pv/widgets/colourbutton.cpp - pv/widgets/colourpopup.cpp + pv/widgets/colorbutton.cpp + pv/widgets/colorpopup.cpp pv/widgets/devicetoolbutton.cpp pv/widgets/exportmenu.cpp pv/widgets/importmenu.cpp @@ -265,6 +301,7 @@ set(pulseview_SOURCES # This list includes only QObject derived class headers. set(pulseview_HEADERS + pv/logging.hpp pv/globalsettings.hpp pv/mainwindow.hpp pv/session.hpp @@ -297,7 +334,6 @@ set(pulseview_HEADERS pv/views/trace/rowitem.hpp pv/views/trace/ruler.hpp pv/views/trace/signal.hpp - pv/views/trace/signalscalehandle.hpp pv/views/trace/timeitem.hpp pv/views/trace/timemarker.hpp pv/views/trace/trace.hpp @@ -310,8 +346,8 @@ set(pulseview_HEADERS pv/views/trace/viewwidget.hpp pv/views/viewbase.hpp pv/views/trace/standardbar.hpp - pv/widgets/colourbutton.hpp - pv/widgets/colourpopup.hpp + pv/widgets/colorbutton.hpp + pv/widgets/colorpopup.hpp pv/widgets/devicetoolbutton.hpp pv/widgets/exportmenu.hpp pv/widgets/importmenu.hpp @@ -334,7 +370,7 @@ endif() if(ENABLE_DECODE) list(APPEND pulseview_SOURCES pv/binding/decoder.cpp - pv/data/decoderstack.cpp + pv/data/decodesignal.cpp pv/data/decode/annotation.cpp pv/data/decode/decoder.cpp pv/data/decode/row.cpp @@ -345,7 +381,7 @@ if(ENABLE_DECODE) ) list(APPEND pulseview_HEADERS - pv/data/decoderstack.hpp + pv/data/decodesignal.hpp pv/views/trace/decodetrace.hpp pv/widgets/decodergroupbox.hpp pv/widgets/decodermenu.hpp @@ -390,6 +426,10 @@ if(ENABLE_SIGNALS) add_definitions(-DENABLE_SIGNALS) endif() +if(ENABLE_STACKTRACE) + add_definitions(-DENABLE_STACKTRACE) +endif() + #=============================================================================== #= Global Include Directories #------------------------------------------------------------------------------- @@ -430,12 +470,20 @@ endif() if(WIN32) # On Windows we need to statically link the libqsvg imageformat # plugin (and the QtSvg component) for SVG graphics/icons to work. - # We also need QWindowsIntegrationPlugin, Qt5PlatformSupport, and all - # Qt libs and their dependencies. + # We also need QWindowsIntegrationPlugin, Qt5PlatformSupport (only for + # Qt < 5.8.0), and all Qt libs and their dependencies. add_definitions(-DQT_STATICPLUGIN) list(APPEND PULSEVIEW_LINK_LIBS Qt5::QSvgPlugin) list(APPEND PULSEVIEW_LINK_LIBS Qt5::QWindowsIntegrationPlugin) - list(APPEND PULSEVIEW_LINK_LIBS -lQt5PlatformSupport ${QT5ALL_LDFLAGS}) + if(Qt5Gui_VERSION VERSION_LESS 5.8.0) + list(APPEND PULSEVIEW_LINK_LIBS -lQt5PlatformSupport) + endif() + list(APPEND PULSEVIEW_LINK_LIBS ${QT5ALL_LDFLAGS}) +endif() + +if(ENABLE_STACKTRACE) + # Needed to resolve dladdr. + list(APPEND PULSEVIEW_LINK_LIBS "-ldl") endif() if(ANDROID) @@ -450,7 +498,7 @@ endif() target_link_libraries(${PROJECT_NAME} ${PULSEVIEW_LINK_LIBS}) -if(WIN32) +if(WIN32 AND NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") # Pass -mwindows so that no "DOS box" opens when PulseView is started. set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-mwindows") endif() diff --git a/HACKING b/HACKING index d539681c..32c10ddd 100644 --- a/HACKING +++ b/HACKING @@ -5,8 +5,9 @@ HACKING Coding style ------------ -This project is programmed using the Linux kernel coding style, see -http://lxr.linux.no/linux/Documentation/CodingStyle for details. +This project is programmed using the Linux kernel coding style: + + https://www.kernel.org/doc/html/latest/process/coding-style.html Please use the same style for any code contributions, thanks! @@ -17,15 +18,16 @@ OK (in order to meet Qt/C++ related guidelines, for example). Contributions ------------- - - Patches should be sent to the development mailinglist at + - In order to contribute you should ideally clone the git repository and + let us know (preferably via IRC, or via the mailing list) from where to + pull/review your changes. You can use github.com, or any other public git + hosting site. + + - Alternatively, patches can be sent to the development mailinglist at sigrok-devel@lists.sourceforge.net (please subscribe to the list first). https://lists.sourceforge.net/lists/listinfo/sigrok-devel - - Alternatively, you can also clone the git repository and let us know - from where to pull/review your changes. You can use gitorious.org, - github.com, or any other public git hosting site. - Random notes ------------ diff --git a/INSTALL b/INSTALL index 3f883e97..edb7940d 100644 --- a/INSTALL +++ b/INSTALL @@ -22,9 +22,12 @@ Requirements - libboost-filesystem - libboost-serialization - libboost-test (optional, only needed to run the unit tests) - - libsigrokcxx >= 0.5.0 (libsigrok C++ bindings) - - libsigrokdecode >= 0.5.0 + - libboost-stacktrace (optional, only needed for debugging) + - libsigrokcxx >= 0.5.1 (libsigrok C++ bindings) + - libsigrokdecode >= 0.5.2 - libsigrokandroidutils >= 0.1.0 (optional, only needed on Android) + - asciidoctor (optional, only needed to build the HTML manual) + - asciidoctor-pdf (optional, only needed to build the PDF manual) Building and installing @@ -56,3 +59,21 @@ In order to build a source package begin with an unconfigured source tree. $ cmake .. $ make package_source + +Generating the manual +--------------------- + +To generate the HTML manual, you only need Asciidoctor. If you also want +to generate the PDF manual, you need to install asciidoctor-pdf as well +and make it available for execution: + + $ gem install --pre asciidoctor-pdf + $ export PATH=~/.gem/ruby/2.3.0/bin:$PATH + +Then, to build the PulseView manual, run: + + $ make manual + +Note: The stylesheet used is a lightly modified version of "Read The Docs" +from the Asciidoctor stylesheet factory: +https://asciidoctor.org/docs/user-manual/#stylesheet-factory diff --git a/README b/README index 613ecc5c..8608b3bf 100644 --- a/README +++ b/README @@ -44,11 +44,26 @@ is to be interpreted as Copyright (C) 2010,2011,2012,2013 Contributor Name -Icons authors and licenses --------------------------- +Resource authors and licenses +----------------------------- icons/information.svg: Bobarino https://en.wikipedia.org/wiki/File:Information.svg + License: + GFDL 1.2 or later / CC-BY-SA 3.0 + https://en.wikipedia.org/wiki/File:Information.svg#Licensing + +QDarkStyleSheet: Colin Duquesnoy + https://github.com/ColinDuquesnoy/QDarkStyleSheet + License: + CC-BY 4.0 + https://github.com/ColinDuquesnoy/QDarkStyleSheet/blob/master/LICENSE.md + +DarkStyle: Juergen Skrotzky + https://github.com/Jorgen-VikingGod/Qt-Frameless-Window-DarkStyle + License: + MIT license + https://github.com/Jorgen-VikingGod/Qt-Frameless-Window-DarkStyle#licence Mailing list diff --git a/android/loghandler.cpp b/android/loghandler.cpp index 2c6674cd..c9953a6e 100644 --- a/android/loghandler.cpp +++ b/android/loghandler.cpp @@ -23,13 +23,21 @@ #include -#include +#include #include #include "android/loghandler.hpp" namespace pv { +static sr_log_callback prev_sr_log_cb; +static void *prev_sr_log_cb_data; + +#ifdef ENABLE_DECODE +static srd_log_callback prev_srd_log_cb; +static void *prev_srd_log_cb_data; +#endif + int AndroidLogHandler::sr_callback(void *cb_data, int loglevel, const char *format, va_list args) { static const int prio[] = { @@ -40,11 +48,18 @@ int AndroidLogHandler::sr_callback(void *cb_data, int loglevel, const char *form [SR_LOG_DBG] = ANDROID_LOG_DEBUG, [SR_LOG_SPEW] = ANDROID_LOG_VERBOSE, }; + va_list args2; int ret; /* This specific log callback doesn't need the void pointer data. */ (void)cb_data; + /* Call the previously registered log callback (library's default). */ + va_copy(args2, args); + if (prev_sr_log_cb) + prev_sr_log_cb(prev_sr_log_cb_data, loglevel, format, args2); + va_end(args2); + /* Only output messages of at least the selected loglevel(s). */ if (loglevel > sr_log_loglevel_get()) return SR_OK; @@ -70,11 +85,18 @@ int AndroidLogHandler::srd_callback(void *cb_data, int loglevel, const char *for [SRD_LOG_DBG] = ANDROID_LOG_DEBUG, [SRD_LOG_SPEW] = ANDROID_LOG_VERBOSE, }; + va_list args2; int ret; /* This specific log callback doesn't need the void pointer data. */ (void)cb_data; + /* Call the previously registered log callback (library's default). */ + va_copy(args2, args); + if (prev_srd_log_cb) + prev_srd_log_cb(prev_srd_log_cb_data, loglevel, format, args2); + va_end(args2); + /* Only output messages of at least the selected loglevel(s). */ if (loglevel > srd_log_loglevel_get()) return SRD_OK; @@ -94,8 +116,10 @@ int AndroidLogHandler::srd_callback(void *cb_data, int loglevel, const char *for void AndroidLogHandler::install_callbacks() { + sr_log_callback_get(&prev_sr_log_cb, &prev_sr_log_cb_data); sr_log_callback_set(sr_callback, nullptr); #ifdef ENABLE_DECODE + srd_log_callback_get(&prev_srd_log_cb, &prev_srd_log_cb_data); srd_log_callback_set(srd_callback, nullptr); #endif } diff --git a/contrib/org.sigrok.PulseView.desktop b/contrib/org.sigrok.PulseView.desktop index 7bdfbf30..f76276e2 100644 --- a/contrib/org.sigrok.PulseView.desktop +++ b/contrib/org.sigrok.PulseView.desktop @@ -7,4 +7,4 @@ Comment=Control your logic analyzer, oscilloscope, or MSO Exec=pulseview Icon=pulseview Type=Application -MimeType=application/vnd.sigrok.session +MimeType=application/vnd.sigrok.session; diff --git a/contrib/pulseview_cross.nsi.in b/contrib/pulseview_cross.nsi.in index 13a45fe1..fdcdf49e 100644 --- a/contrib/pulseview_cross.nsi.in +++ b/contrib/pulseview_cross.nsi.in @@ -81,7 +81,7 @@ RequestExecutionLevel admin !define SHCNF_IDLIST 0 -# --- Functions --------------------------------------------------------------- +# --- Functions/Macros -------------------------------------------------------- Function register_sr_files ${registerExtension} "$INSTDIR\pulseview.exe" ".sr" "sigrok session file" @@ -90,6 +90,12 @@ Function register_sr_files System::Call 'Shell32::SHChangeNotify(i ${SHCNE_ASSOCCHANGED}, i ${SHCNF_IDLIST}, i 0, i 0)' FunctionEnd +# Inspired by http://nsis.sourceforge.net/Create_Internet_Shorcuts_during_installation +!Macro "CreateURL" "URLFile" "URLSite" "URLDesc" + WriteINIStr "$INSTDIR\${URLFile}.URL" "InternetShortcut" "URL" "${URLSite}" + CreateShortCut "$SMPROGRAMS\sigrok\PulseView\${URLFile}.lnk" "$INSTDIR\${URLFile}.url" "" \ + "$INSTDIR\pulseview.exe" 0 "SW_SHOWNORMAL" "" "${URLDesc}" +!MacroEnd # --- MUI pages --------------------------------------------------------------- @@ -177,6 +183,12 @@ Section "PulseView (required)" Section1 0 SW_SHOWNORMAL \ "" "Open-source, portable sigrok GUI" + # Create a shortcut for the PulseView application running in debug mode. + CreateShortCut "$SMPROGRAMS\sigrok\PulseView\PulseView (Debug).lnk" \ + "$INSTDIR\pulseview.exe" "-l 5" "$INSTDIR\pulseview.exe" \ + 0 SW_SHOWNORMAL \ + "" "Open-source, portable sigrok GUI (debug log level)" + # Create a shortcut for the uninstaller. CreateShortCut "$SMPROGRAMS\sigrok\PulseView\Uninstall PulseView.lnk" \ "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0 \ @@ -192,6 +204,10 @@ Section "PulseView (required)" Section1 "$INSTDIR\zadig_xp.exe" "" "$INSTDIR\zadig_xp.exe" 0 \ SW_SHOWNORMAL "" "Zadig (PulseView, Win XP)" + # Create shortcuts to the HTML and PDF manuals, respectively. + !InsertMacro "CreateURL" "PulseView HTML manual" "https://sigrok.org/doc/pulseview/@PV_MANUAL_VERSION@/manual.html" "PulseView HTML manual" + !InsertMacro "CreateURL" "PulseView PDF manual" "https://sigrok.org/doc/pulseview/@PV_MANUAL_VERSION@/manual.pdf" "PulseView PDF manual" + # Create registry keys for "Add/remove programs" in the control panel. WriteRegStr HKLM "${REGSTR}" "DisplayName" "PulseView" WriteRegStr HKLM "${REGSTR}" "UninstallString" \ @@ -267,6 +283,10 @@ Section "Uninstall" # Delete the example *.sr files. RMDir /r "$INSTDIR\examples\*" + # Delete the URL files for the manual. + Delete "$INSTDIR\PulseView HTML manual.url" + Delete "$INSTDIR\PulseView PDF manual.url" + # Delete the install directory and its sub-directories. RMDir "$INSTDIR\share" RMDir "$INSTDIR\examples" @@ -274,11 +294,16 @@ Section "Uninstall" # Delete the links from the start menu. Delete "$SMPROGRAMS\sigrok\PulseView\PulseView.lnk" + Delete "$SMPROGRAMS\sigrok\PulseView\PulseView (Debug).lnk" Delete "$SMPROGRAMS\sigrok\PulseView\Uninstall PulseView.lnk" Delete "$SMPROGRAMS\sigrok\PulseView\Zadig (PulseView).lnk" Delete "$SMPROGRAMS\sigrok\PulseView\Zadig (PulseView, Win XP).lnk" Delete "$SMPROGRAMS\sigrok\PulseView\Examples (PulseView).lnk" + # Delete the links to the manual. + Delete "$SMPROGRAMS\sigrok\PulseView\PulseView HTML manual.lnk" + Delete "$SMPROGRAMS\sigrok\PulseView\PulseView PDF manual.lnk" + # Delete the sub-directory in the start menu. RMDir "$SMPROGRAMS\sigrok\PulseView" RMDir "$SMPROGRAMS\sigrok" diff --git a/doc/pulseview.1 b/doc/pulseview.1 index ad1cf595..8289eaa8 100644 --- a/doc/pulseview.1 +++ b/doc/pulseview.1 @@ -1,4 +1,4 @@ -.TH PULSEVIEW 1 "June 6, 2017" +.TH PULSEVIEW 1 "March 30, 2018" .SH "NAME" PulseView \- Qt-based LA/scope/MSO GUI for sigrok .SH "SYNOPSIS" @@ -41,6 +41,20 @@ Show a help text and exit. .B "\-V, \-\-version" Show version information and exit. .TP +.BR "\-d, \-\-driver " +Specify the capture device to connect to. If the +.B \-\-driver +option is not supplied, PulseView attempts to re-connect to the +most recently used device, or auto-detect available devices. +.TP +.BR "\-D, \-\-dont\-scan " +Usually PulseView automatically scans all drivers to find suitable +devices during program startup. This option disables the auto-scan. +Users can either specify the +.B \-\-driver +option to pick a device at startup, or interactively scan for devices +after PulseView has finished starting up. +.TP .BR "\-i, \-\-input\-file " Load input from a file. If the .B \-\-input\-format diff --git a/icons/help-browser.png b/icons/help-browser.png new file mode 100644 index 00000000..593ecd5b Binary files /dev/null and b/icons/help-browser.png differ diff --git a/icons/media-playback-pause.png b/icons/media-playback-pause.png new file mode 100644 index 00000000..ee40fc26 Binary files /dev/null and b/icons/media-playback-pause.png differ diff --git a/icons/media-playback-start.png b/icons/media-playback-start.png new file mode 100644 index 00000000..10102d82 Binary files /dev/null and b/icons/media-playback-start.png differ diff --git a/icons/settings-general.png b/icons/settings-general.png new file mode 100644 index 00000000..4decc893 Binary files /dev/null and b/icons/settings-general.png differ diff --git a/icons/view-displaymode-last_complete_segment.svg b/icons/view-displaymode-last_complete_segment.svg new file mode 100644 index 00000000..125369e8 --- /dev/null +++ b/icons/view-displaymode-last_complete_segment.svg @@ -0,0 +1,521 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/view-displaymode-last_segment.svg b/icons/view-displaymode-last_segment.svg new file mode 100644 index 00000000..4c4bf531 --- /dev/null +++ b/icons/view-displaymode-last_segment.svg @@ -0,0 +1,521 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/view-displaymode-single_segment.svg b/icons/view-displaymode-single_segment.svg new file mode 100644 index 00000000..ed8d3ac0 --- /dev/null +++ b/icons/view-displaymode-single_segment.svg @@ -0,0 +1,530 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/zoom-original.png b/icons/zoom-original.png deleted file mode 100644 index 8ae30433..00000000 Binary files a/icons/zoom-original.png and /dev/null differ diff --git a/main.cpp b/main.cpp index ce7db5fa..a77969ea 100644 --- a/main.cpp +++ b/main.cpp @@ -22,28 +22,46 @@ #endif #include -#include - +#include #include +#include + +#include +#include #include +#include +#include +#include #include +#include + +#include "config.h" #ifdef ENABLE_SIGNALS #include "signalhandler.hpp" #endif +#ifdef ENABLE_STACKTRACE +#include +#include +#include +#endif + #include "pv/application.hpp" #include "pv/devicemanager.hpp" +#include "pv/globalsettings.hpp" +#include "pv/logging.hpp" #include "pv/mainwindow.hpp" +#include "pv/session.hpp" +#include "pv/util.hpp" + #ifdef ANDROID #include #include "android/assetreader.hpp" #include "android/loghandler.hpp" #endif -#include "config.h" - #ifdef _WIN32 #include Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin) @@ -51,9 +69,76 @@ Q_IMPORT_PLUGIN(QSvgPlugin) #endif using std::exception; +using std::ifstream; +using std::ofstream; using std::shared_ptr; using std::string; +#if ENABLE_STACKTRACE +QString stacktrace_filename; + +void signal_handler(int signum) +{ + ::signal(signum, SIG_DFL); + boost::stacktrace::safe_dump_to(stacktrace_filename.toLocal8Bit().data()); + ::raise(SIGABRT); +} + +void process_stacktrace(QString temp_path) +{ + const QString stacktrace_outfile = temp_path + "/pv_stacktrace.txt"; + + ifstream ifs(stacktrace_filename.toLocal8Bit().data()); + ofstream ofs(stacktrace_outfile.toLocal8Bit().data(), + ofstream::out | ofstream::trunc); + + boost::stacktrace::stacktrace st = + boost::stacktrace::stacktrace::from_dump(ifs); + ofs << st; + + ofs.close(); + ifs.close(); + + QFile f(stacktrace_outfile); + f.open(QFile::ReadOnly | QFile::Text); + QTextStream fs(&f); + QString stacktrace = fs.readAll(); + stacktrace = stacktrace.trimmed().replace('\n', "
"); + + qDebug() << QObject::tr("Stack trace of previous crash:"); + qDebug() << "---------------------------------------------------------"; + // Note: qDebug() prints quotation marks for QString output, so we feed it char* + qDebug() << stacktrace.toLocal8Bit().data(); + qDebug() << "---------------------------------------------------------"; + + f.close(); + + // Remove stack trace so we don't process it again the next time we run + QFile::remove(stacktrace_filename.toLocal8Bit().data()); + + // Show notification dialog if permitted + pv::GlobalSettings settings; + if (settings.value(pv::GlobalSettings::Key_Log_NotifyOfStacktrace).toBool()) { + QCheckBox *cb = new QCheckBox(QObject::tr("Don't show this message again")); + + QMessageBox msgbox; + msgbox.setText(QObject::tr("When %1 last crashed, it created a stack trace.\n" \ + "A human-readable form has been saved to disk and was written to " \ + "the log. You may access it from the settings dialog.").arg(PV_TITLE)); + msgbox.setIcon(QMessageBox::Icon::Information); + msgbox.addButton(QMessageBox::Ok); + msgbox.setCheckBox(cb); + + QObject::connect(cb, &QCheckBox::stateChanged, [](int state){ + pv::GlobalSettings settings; + settings.setValue(pv::GlobalSettings::Key_Log_NotifyOfStacktrace, + !state); }); + + msgbox.exec(); + } +} +#endif + void usage() { fprintf(stdout, @@ -66,6 +151,8 @@ void usage() "Application Options:\n" " -V, --version Show release version\n" " -l, --loglevel Set libsigrok/libsigrokdecode loglevel\n" + " -d, --driver Specify the device driver to use\n" + " -D, --dont-scan Don't auto-scan for devices, use -d spec only\n" " -i, --input-file Load input from file\n" " -I, --input-format Input format\n" " -c, --clean Don't restore previous sessions on startup\n" @@ -76,8 +163,11 @@ int main(int argc, char *argv[]) { int ret = 0; shared_ptr context; - string open_file, open_file_format; + string open_file_format, driver; + vector open_files; bool restore_sessions = true; + bool do_scan = true; + bool show_version = false; Application a(argc, argv); @@ -93,14 +183,17 @@ int main(int argc, char *argv[]) {"help", no_argument, nullptr, 'h'}, {"version", no_argument, nullptr, 'V'}, {"loglevel", required_argument, nullptr, 'l'}, + {"driver", required_argument, nullptr, 'd'}, + {"dont-scan", no_argument, nullptr, 'D'}, {"input-file", required_argument, nullptr, 'i'}, {"input-format", required_argument, nullptr, 'I'}, {"clean", no_argument, nullptr, 'c'}, + {"log-to-stdout", no_argument, nullptr, 's'}, {nullptr, 0, nullptr, 0} }; const int c = getopt_long(argc, argv, - "l:Vhc?i:I:", long_options, nullptr); + "h?VDcl:d:i:I:", long_options, nullptr); if (c == -1) break; @@ -111,13 +204,16 @@ int main(int argc, char *argv[]) return 0; case 'V': - // Print version info - fprintf(stdout, "%s %s\n", PV_TITLE, PV_VERSION_STRING); - return 0; + show_version = true; + break; case 'l': { const int loglevel = atoi(optarg); + if (loglevel < 0 || loglevel > 5) { + qDebug() << "ERROR: invalid log level spec."; + break; + } context->set_log_level(sigrok::LogLevel::get(loglevel)); #ifdef ENABLE_DECODE @@ -132,8 +228,16 @@ int main(int argc, char *argv[]) break; } + case 'd': + driver = optarg; + break; + + case 'D': + do_scan = false; + break; + case 'i': - open_file = optarg; + open_files.emplace_back(optarg); break; case 'I': @@ -145,17 +249,40 @@ int main(int argc, char *argv[]) break; } } + argc -= optind; + argv += optind; - if (argc - optind > 1) { - fprintf(stderr, "Only one file can be opened.\n"); - return 1; - } + for (int i = 0; i < argc; i++) + open_files.emplace_back(argv[i]); + + qRegisterMetaType("util::Timestamp"); + qRegisterMetaType("uint64_t"); + + // Prepare the global settings since logging needs them early on + pv::GlobalSettings settings; + settings.save_internal_defaults(); + settings.set_defaults_where_needed(); + settings.apply_theme(); - if (argc - optind == 1) - open_file = argv[argc - 1]; + pv::logging.init(); // Initialise libsigrok context = sigrok::Context::create(); + pv::Session::sr_context = context; + +#if ENABLE_STACKTRACE + QString temp_path = QStandardPaths::standardLocations( + QStandardPaths::TempLocation).at(0); + stacktrace_filename = temp_path + "/pv_stacktrace.dmp"; + qDebug() << "Stack trace file is" << stacktrace_filename; + + ::signal(SIGSEGV, &signal_handler); + ::signal(SIGABRT, &signal_handler); + + if (QFileInfo::exists(stacktrace_filename)) + process_stacktrace(temp_path); +#endif + #ifdef ANDROID context->set_resource_reader(&asset_reader); #endif @@ -172,10 +299,17 @@ int main(int argc, char *argv[]) srd_decoder_load_all(); #endif +#ifndef ENABLE_STACKTRACE try { - // Create the device manager, initialise the drivers - pv::DeviceManager device_manager(context); +#endif + + // Create the device manager, initialise the drivers + pv::DeviceManager device_manager(context, driver, do_scan); + a.collect_version_info(context); + if (show_version) { + a.print_version_info(); + } else { // Initialise the main window pv::MainWindow w(device_manager); w.show(); @@ -183,33 +317,32 @@ int main(int argc, char *argv[]) if (restore_sessions) w.restore_sessions(); - if (!open_file.empty()) - w.add_session_with_file(open_file, open_file_format); - else + if (open_files.empty()) w.add_default_session(); + else + for (string& open_file : open_files) + w.add_session_with_file(open_file, open_file_format); #ifdef ENABLE_SIGNALS if (SignalHandler::prepare_signals()) { - SignalHandler *const handler = - new SignalHandler(&w); - QObject::connect(handler, - SIGNAL(int_received()), + SignalHandler *const handler = new SignalHandler(&w); + QObject::connect(handler, SIGNAL(int_received()), &w, SLOT(close())); - QObject::connect(handler, - SIGNAL(term_received()), + QObject::connect(handler, SIGNAL(term_received()), &w, SLOT(close())); - } else { - qWarning() << - "Could not prepare signal handler."; - } + } else + qWarning() << "Could not prepare signal handler."; #endif // Run the application ret = a.exec(); + } - } catch (exception e) { - qDebug() << e.what(); +#ifndef ENABLE_STACKTRACE + } catch (exception& e) { + qDebug() << "Exception:" << e.what(); } +#endif #ifdef ENABLE_DECODE // Destroy libsigrokdecode diff --git a/manual/CMakeLists.txt b/manual/CMakeLists.txt new file mode 100644 index 00000000..c425f36b --- /dev/null +++ b/manual/CMakeLists.txt @@ -0,0 +1,87 @@ +## +## This file is part of the PulseView project. +## +## Copyright (C) 2018 Gerhard Sittig +## +## This program is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 2 of the License, or +## (at your option) any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see . +## + +cmake_minimum_required(VERSION 2.8.12) + +# External dependencies, required and optional tools. +find_program(ASCIIDOCTOR_EXECUTABLE NAMES asciidoctor) +find_program(ASCIIDOCTOR_PDF_EXECUTABLE NAMES asciidoctor-pdf) + +# Tunables. +set(STYLES_DIR "asciidoctor-stylesheet-factory/stylesheets") +set(STYLE_SHEET "readthedocs.css") + +# Input files. +set(MANUAL_SRC "${CMAKE_CURRENT_SOURCE_DIR}/manual.txt") + +# Output files, conversion results. +set(MANUAL_OUT_HTML "${CMAKE_CURRENT_BINARY_DIR}/manual.html") +set(MANUAL_OUT_PDF "${CMAKE_CURRENT_BINARY_DIR}/manual.pdf") + +# Manual related make(1) targets. +add_custom_target(manual-html + COMMAND ${ASCIIDOCTOR_EXECUTABLE} + -a stylesheet=${STYLE_SHEET} + -a stylesdir=${CMAKE_CURRENT_SOURCE_DIR}/${STYLES_DIR} + -a toc=left + --destination-dir=${CMAKE_CURRENT_BINARY_DIR} + ${MANUAL_SRC} + BYPRODUCTS ${MANUAL_OUT_HTML} + DEPENDS ${MANUAL_SRC} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating manual, HTML output" +) +if (ASCIIDOCTOR_PDF_EXECUTABLE) + add_custom_target(manual-pdf + COMMAND ${ASCIIDOCTOR_PDF_EXECUTABLE} + -a stylesheet=${STYLE_SHEET} + -a stylesdir=${CMAKE_CURRENT_SOURCE_DIR}/${STYLES_DIR} + --destination-dir=${CMAKE_CURRENT_BINARY_DIR} + ${MANUAL_SRC} + BYPRODUCTS ${MANUAL_OUT_PDF} + DEPENDS ${MANUAL_SRC} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating manual, HTML output" + ) +else () + add_custom_target(manual-pdf + COMMAND ${CMAKE_COMMAND} -E echo + "asciidoctor-pdf executable is missing, NOT generating PDF output" + DEPENDS ${MANUAL_SRC} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) +endif () +add_custom_target(manual) +add_dependencies(manual manual-html manual-pdf) + +set(MANUAL_INST_SUBDIR "share/doc/pulseview") +install( + FILES ${MANUAL_OUT_HTML} ${MANUAL_OUT_PDF} + DESTINATION ${MANUAL_INST_SUBDIR} + PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ + OPTIONAL +) +if (ASCIIDOCTOR_EXECUTABLE) + install( + DIRECTORY images + DESTINATION ${MANUAL_INST_SUBDIR} + FILE_PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ + PATTERN "*.xcf" EXCLUDE + ) +endif () diff --git a/manual/acquisition.txt b/manual/acquisition.txt new file mode 100644 index 00000000..685c9cd8 --- /dev/null +++ b/manual/acquisition.txt @@ -0,0 +1,134 @@ +== Data Acquisition + +Working with PulseView follows a pattern: + +image::pv_nodevice.png[] +<1> Open a new session +<2> Select the device you want to work with: +<3> Click "Run" to acquire signal data (waiting for a trigger first if you set one) + +When you start PulseView and no sessions are restored from the last time you used it, it will +come up with a session that has the demo device selected. That way, you can get to know the +program even when you don't have any hardware to use it with. + +=== Device Selection + +The device selector offers two methods to choose the device to use. If you click on the small +arrow on the side, you see a list of devices PulseView has recognized. If the device you want +to use it listed, you can just select it here to use it. + +image::device_selector_dropdown.png[] + +If it's not listed, you'll need to scan for it first. Since most serial port and Ethernet +devices can't be auto-detected, this is usually required for those. +To do so, either choose the "Connect to Device" option from the list or click on the button +itself. You will see the following dialog: + +image::device_selector_scan.png[] + +First, you'll need to pick a driver that you want to use. In order to do this, you'll need +to know which driver is used to talk to the device. If you're unsure, you can either try the +driver which you think may fit best or you can check the wiki. For every supported device there's +a wiki page, showing you which driver is used. + +Once the driver has been chosen, you need to select the interface. Please be aware that USB +is only usable for devices that directly communicate over USB. Devices that use USB to emulate +a serial port (like the OpenBench Logic Sniffer) will have their serial port listed in the +serial port drop-down. + +In case your device connects via Ethernet, you must supply the IP address and port. You are +also given the option to choose between raw TCP access and using the VXI protocol. VXI is an +industry standard which is mainly used in professional equipment and the device will most +likely let you know that it supports VXI. If your device however is more of a hobbyist grade +device, it's more likely that using raw TCP will be the correct choice. + +After you selected the appropriate options, clicking the scan button will make PulseView try +to connect to the device with the given settings. If successful, any device(s) found will be +shown in the list box. + +[NOTE] +When a session uses a USB device and you close Pulseview, a session with that same device +is re-opened when you start Pulseview again. Currently, this is however not the case for non-USB +devices, such as ones that connect via serial port or Ethernet. + +[NOTE] +To avoid having to manually enter the device configuration for a serial port or Ethernet +device every time you want to use it and then having to scan for it, you can also use the +command line parameter -d to have PulseView scan for it on startup. + +You may then change the device configuration and/or start the data acquisition by clicking +the "Run" button on the top left. + +When you run the acquisition, you'll notice that the newly captured data goes off-screen. +This is to improve performance and let PulseView acquire the data without bogging down your +CPU too much. If you find this inconvenient because you'd like to see what kind of data is +coming in, you have three options: + +* Enable "always perform zoom-to-fit" temporarily (see chapter "Data Analysis") +* Enable "constantly perform zoom-to-fit during acquisition" in the options +* Enable "always keep newest samples at the right edge during capture" in the options + +Which method suits you best is up for you to decide. + +=== Device Configuration + +In PulseView, the device configuration is done using these buttons: + +image::pv_device_config.png[] +<1> Device-specific settings +<2> Channel-specific settings +<3> Number of samples to capture +<4> Sample rate at which to capture the samples +<5> Per-channel trigger setting (see below) + +The values offered for those four elements depend on your device. Which settings you should choose +depends on several factors: the needs of your measurement, the device you use to capture the data +and the capabilities of your computer. + +The sample rate you choose must at least be twice that of the highest frequency you want to +capture - ideally 3 to 5 times as much so that you have some margin. That way, a jittering signal +won't ruin your measurements. + +[NOTE] +If you're using a device with a Cypress FX2 (most 8 channel / 24 MHz logic analyzers do) then you should +be aware that the 24 MHz sampling rate (12 MHz for 16 channels) can only be sustained under perfect +conditions. Usually, those devices are shipped with low-quality USB cables, impairing USB transfers as +USB traffic increases. Therefore, you can try a different USB cable if you're facing issues at higher +sample rates. If they persist, it's worth trying a different USB port as well. + +=== Triggers + +The signal labels on the left side of the view (D0, D1 and so on in the picture above) allow you to +configure certain aspects of these signals. If the device supports it then the trigger that will be +used for this signal will be among them. + +As of now, the trigger system is awaiting extension for advanced and complex trigger types, meaning +that the only triggers available to you are: + +* Trigger when the signal has a "low" level +* Trigger when the signal has a "high" level +* Trigger when the signal switches from "low" to "high" level (rising edge) +* Trigger when the signal switches from "hig" to "low" level (falling edge) +* Trigger when the signal changes level in any way (any edge) + +Once you choose a trigger, the icon for the type you chose becomes visible on the right side of the +trace view. + +When you click "Run" with a trigger configured, PulseView will wait for the device to trigger and +send data before it can show anything. There is currently no frame limit, so if the device driver +supports it, PulseView will continue arming the trigger and collecting data until you either click +"Stop" or it runs out of memory. + +=== Channel Groups + +Some devices share certain settings between a group of channels, which is why PulseView may show +the channels your device offers in groups. You can see which channels are grouped by looking at the +dark gray bar on the left. If there is none, no channels are grouped. + +Currently, the grouping is only done for your convenience and there's no direct functional impact. +This means that you're free to ungroup and group channels as you please. To do so, right-click +on the dark gray bar and select "Ungroup". +If you want to create a new group, select the signals you want to group by holding down CTRL +and clicking on the signal labels. Once you have selected the ones you want to be grouped, +right-click on one of the labels you selected and choose "Group". + diff --git a/manual/analysis.txt b/manual/analysis.txt new file mode 100644 index 00000000..7aa487b4 --- /dev/null +++ b/manual/analysis.txt @@ -0,0 +1,84 @@ +== Data Analysis + +Once you have acquired some measurement data, it's time to have a look and see what +insights you can gain from it. Usually, the first step is to look at the data as a +whole, achieved by clicking the _Zoom to Fit_ button: + +image::pv_analysis.png[] + +<1> Zoom-to-Fit button +<2> Zoom in/zoom out buttons +<3> Cursors +<4> Time scale (used to set up and show markers, see below) + +If you have located an area of interest (maybe with the help of decoders, more about +that later), you can zoom in on it using the _zoom in_/_zoom out_ buttons, using the +scroll wheel of your mouse or the pinch/expand gestures on your touch panel. + +[NOTE] +When a data capture is ongoing, the Zoom-to-Fit button stays active if you click it, +meaning that PulseView automatically fits all data to the views until either the +capture is finished or the Zoom-to-Fit button is clicked again. +If you want this feature but don't want to always have to click the button, you +can enable the "Always Zoom-to-Fit" option in the settings. + +=== Cursors and Markers + +Just looking at the signal data however is usually not sufficient. A lot of times, +you'll want to make sure that timings are honored and the bit times are like what +you'd expect. To do so, you'll want to use cursors and markers. + +In the picture above, you can enable the cursor by clicking on the cursor button. +You can move both of its boundaries around by clicking on the blue flags in the +time scale area. The area between the two boundary lines shows the time distance +and its inverse (i.e. the frequency). If you can't see it, just zoom in until it +shows. You can also move both boundaries at the same time by dragging the label +where this information is shown. + +image::pv_cursors_markers.png[] + +<1> Cursors button, showing enabled state +<2> Cursor +<3> Marker + +Markers are movable indicators that you can create wherever you like on the +time scale - just double-click on it and it'll create one for you where your +mouse cursor is at the time, or use the context menu when right-clicking on +the ruler or a signal trace. +You can click on its label and you'll have the option to change its name, or +drag it to reposition it. + +[NOTE] +For timing comparison purposes, you can also enable a vertical marker line that +follows your mouse cursor: _Settings_ -> _Views_ -> _Highlight mouse cursor_ + +[NOTE] +There is also a special kind of marker that appears for each time the data +acquisition device has triggered. It cannot be moved and appears as a vertical +dashed line. + +=== Special-Purpose Decoders + +There are some decoders available that analyze the data instead of decoding it. +You can make use of them to examine various properties of the signals that are +of interest to you. + +Their names are: + +* Counter - counts pulses and/or groups of pulses (i.e. words) +* Guess bitrate - guesses the bitrate when using a serial protocol +* Jitter - determines the jitter (variance) of a signal +* Timing - shows the time passing between the chosen signal edges + +=== Other Features + +Trace Views also allow you to maximize the viewing area by minimizing the area +occupied by the label area on the left. To do this, simply position the mouse +cursor at the right edge of the label area (or left edge of the viewing area). +Your mouse cursor will change shape and you now can drag the border. + +This way, you can give signals long, expressive names without clogging up the +view area. + +Also, you can create multiple views by clicking on the "New View" button on +the very left of the toolbar. Those can be rearranged as you wish. diff --git a/manual/asciidoctor-stylesheet-factory/LICENSE b/manual/asciidoctor-stylesheet-factory/LICENSE new file mode 100644 index 00000000..5f4d18e5 --- /dev/null +++ b/manual/asciidoctor-stylesheet-factory/LICENSE @@ -0,0 +1,45 @@ +Asciidoctor styles +------------------ + +Copyright (c) 2013 Dan Allen + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +Other licensed work +------------------- + +- Foundation 4 by Zurb, on which the themes are built, is licensed under the + Apache License, v2.0: + + http://apache.org/licenses/LICENSE-2.0 + http://foundation.zurb.com + +- The riak theme is derived from the Riak documentation theme by Basho, + licensed under the Creative Commons Attribution 3.0 Unported License: + + http://creativecommons.org/licenses/by/3.0/us + http://docs.basho.org + +- The iconic theme is inspired by O'Reilly typography and Atlas manual. + + http://oreilly.com diff --git a/manual/asciidoctor-stylesheet-factory/stylesheets/readthedocs.css b/manual/asciidoctor-stylesheet-factory/stylesheets/readthedocs.css new file mode 100644 index 00000000..7adc8d11 --- /dev/null +++ b/manual/asciidoctor-stylesheet-factory/stylesheets/readthedocs.css @@ -0,0 +1,727 @@ +/*! normalize.css v2.1.2 | MIT License | git.io/normalize */ +/* ========================================================================== HTML5 display definitions ========================================================================== */ +/** Correct `block` display not defined in IE 8/9. */ +article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { display: block; } + +/** Correct `inline-block` display not defined in IE 8/9. */ +audio, canvas, video { display: inline-block; } + +/** Prevent modern browsers from displaying `audio` without controls. Remove excess height in iOS 5 devices. */ +audio:not([controls]) { display: none; height: 0; } + +/** Address `[hidden]` styling not present in IE 8/9. Hide the `template` element in IE, Safari, and Firefox < 22. */ +[hidden], template { display: none; } + +script { display: none !important; } + +/* ========================================================================== Base ========================================================================== */ +/** 1. Set default font family to sans-serif. 2. Prevent iOS text size adjust after orientation change, without disabling user zoom. */ +html { font-family: sans-serif; /* 1 */ -ms-text-size-adjust: 100%; /* 2 */ -webkit-text-size-adjust: 100%; /* 2 */ } + +/** Remove default margin. */ +body { margin: 0; } + +/* ========================================================================== Links ========================================================================== */ +/** Remove the gray background color from active links in IE 10. */ +a { background: transparent; } + +/** Address `outline` inconsistency between Chrome and other browsers. */ +a:focus { outline: thin dotted; } + +/** Improve readability when focused and also mouse hovered in all browsers. */ +a:active, a:hover { outline: 0; } + +/* ========================================================================== Typography ========================================================================== */ +/** Address variable `h1` font-size and margin within `section` and `article` contexts in Firefox 4+, Safari 5, and Chrome. */ +h1 { font-size: 2em; margin: 0.67em 0; } + +/** Address styling not present in IE 8/9, Safari 5, and Chrome. */ +abbr[title] { border-bottom: 1px dotted; } + +/** Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome. */ +b, strong { font-weight: bold; } + +/** Address styling not present in Safari 5 and Chrome. */ +dfn { font-style: italic; } + +/** Address differences between Firefox and other browsers. */ +hr { -moz-box-sizing: content-box; box-sizing: content-box; height: 0; } + +/** Address styling not present in IE 8/9. */ +mark { background: #ff0; color: #000; } + +/** Correct font family set oddly in Safari 5 and Chrome. */ +code, kbd, pre, samp { font-family: monospace, serif; font-size: 1em; } + +/** Improve readability of pre-formatted text in all browsers. */ +pre { white-space: pre-wrap; } + +/** Set consistent quote types. */ +q { quotes: "\201C" "\201D" "\2018" "\2019"; } + +/** Address inconsistent and variable font size in all browsers. */ +small { font-size: 80%; } + +/** Prevent `sub` and `sup` affecting `line-height` in all browsers. */ +sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } + +sup { top: -0.5em; } + +sub { bottom: -0.25em; } + +/* ========================================================================== Embedded content ========================================================================== */ +/** Remove border when inside `a` element in IE 8/9. */ +img { border: 0; } + +/** Correct overflow displayed oddly in IE 9. */ +svg:not(:root) { overflow: hidden; } + +/* ========================================================================== Figures ========================================================================== */ +/** Address margin not present in IE 8/9 and Safari 5. */ +figure { margin: 0; } + +/* ========================================================================== Forms ========================================================================== */ +/** Define consistent border, margin, and padding. */ +fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; } + +/** 1. Correct `color` not being inherited in IE 8/9. 2. Remove padding so people aren't caught out if they zero out fieldsets. */ +legend { border: 0; /* 1 */ padding: 0; /* 2 */ } + +/** 1. Correct font family not being inherited in all browsers. 2. Correct font size not being inherited in all browsers. 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. */ +button, input, select, textarea { font-family: inherit; /* 1 */ font-size: 100%; /* 2 */ margin: 0; /* 3 */ } + +/** Address Firefox 4+ setting `line-height` on `input` using `!important` in the UA stylesheet. */ +button, input { line-height: normal; } + +/** Address inconsistent `text-transform` inheritance for `button` and `select`. All other form control elements do not inherit `text-transform` values. Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. Correct `select` style inheritance in Firefox 4+ and Opera. */ +button, select { text-transform: none; } + +/** 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. 2. Correct inability to style clickable `input` types in iOS. 3. Improve usability and consistency of cursor style between image-type `input` and others. */ +button, html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ } + +/** Re-set default cursor for disabled elements. */ +button[disabled], html input[disabled] { cursor: default; } + +/** 1. Address box sizing set to `content-box` in IE 8/9. 2. Remove excess padding in IE 8/9. */ +input[type="checkbox"], input[type="radio"] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ } + +/** 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome (include `-moz` to future-proof). */ +input[type="search"] { -webkit-appearance: textfield; /* 1 */ -moz-box-sizing: content-box; -webkit-box-sizing: content-box; /* 2 */ box-sizing: content-box; } + +/** Remove inner padding and search cancel button in Safari 5 and Chrome on OS X. */ +input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } + +/** Remove inner padding and border in Firefox 4+. */ +button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } + +/** 1. Remove default vertical scrollbar in IE 8/9. 2. Improve readability and alignment in all browsers. */ +textarea { overflow: auto; /* 1 */ vertical-align: top; /* 2 */ } + +/* ========================================================================== Tables ========================================================================== */ +/** Remove most spacing between table cells. */ +table { border-collapse: collapse; border-spacing: 0; } + +meta.foundation-mq-small { font-family: "only screen and (min-width: 768px)"; width: 768px; } + +meta.foundation-mq-medium { font-family: "only screen and (min-width:1280px)"; width: 1280px; } + +meta.foundation-mq-large { font-family: "only screen and (min-width:1440px)"; width: 1440px; } + +*, *:before, *:after { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; } + +html, body { font-size: 100%; } + +body { background: #fff; color: #222; padding: 0; margin: 0; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-weight: normal; font-style: normal; line-height: 1; position: relative; cursor: auto; } + +a:hover { cursor: pointer; } + +img, object, embed { max-width: 100%; height: auto; } + +object, embed { height: 100%; } + +img { -ms-interpolation-mode: bicubic; } + +#map_canvas img, #map_canvas embed, #map_canvas object, .map_canvas img, .map_canvas embed, .map_canvas object { max-width: none !important; } + +.left { float: left !important; } + +.right { float: right !important; } + +.text-left { text-align: left !important; } + +.text-right { text-align: right !important; } + +.text-center { text-align: center !important; } + +.text-justify { text-align: justify !important; } + +.hide { display: none; } + +.antialiased { -webkit-font-smoothing: antialiased; } + +img { display: inline-block; vertical-align: middle; } + +textarea { height: auto; min-height: 50px; } + +select { width: 100%; } + +object, svg { display: inline-block; vertical-align: middle; } + +.center { margin-left: auto; margin-right: auto; } + +.stretch { width: 100%; } + +p.lead { font-size: 1.21875em; line-height: 1.6; } + +.subheader, .admonitionblock td.content > .title, .audioblock > .title, .exampleblock > .title, .imageblock > .title, .listingblock > .title, .literalblock > .title, .stemblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, table.tableblock > .title, .verseblock > .title, .videoblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title { line-height: 1.4; color: #6c818f; font-weight: 300; margin-top: 0.2em; margin-bottom: 0.5em; } + +/* Typography resets */ +div, dl, dt, dd, ul, ol, li, h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6, pre, form, p, blockquote, th, td { margin: 0; padding: 0; direction: ltr; } + +/* Default Link Styles */ +a { color: #444; text-decoration: underline; line-height: inherit; } +a:hover, a:focus { color: #111; } +a img { border: none; } + +/* Default paragraph styles */ +p { font-family: inherit; font-weight: normal; font-size: 1em; line-height: 1.5; margin-bottom: 1.25em; text-rendering: optimizeLegibility; } +p aside { font-size: 0.875em; line-height: 1.35; font-style: italic; } + +/* Default header styles */ +h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { font-family: ff-meta-web-pro-1, ff-meta-web-pro-2, Arial, "Helvetica Neue", sans-serif; font-weight: bold; font-style: normal; color: #465158; text-rendering: optimizeLegibility; margin-top: 1em; margin-bottom: 0.5em; line-height: 1.2125em; } +h1 small, h2 small, h3 small, #toctitle small, .sidebarblock > .content > .title small, h4 small, h5 small, h6 small { font-size: 60%; color: #909ea7; line-height: 0; } + +h1 { font-size: 2.125em; } + +h2 { font-size: 1.6875em; } + +h3, #toctitle, .sidebarblock > .content > .title { font-size: 1.375em; } + +h4 { font-size: 1.125em; } + +h5 { font-size: 1.125em; } + +h6 { font-size: 1em; } + +hr { border: solid #ddd; border-width: 1px 0 0; clear: both; margin: 1.25em 0 1.1875em; height: 0; } + +/* Helpful Typography Defaults */ +em, i { font-style: italic; line-height: inherit; } + +strong, b { font-weight: bold; line-height: inherit; } + +small { font-size: 60%; line-height: inherit; } + +code { font-family: "Consolas", "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", monospace; font-weight: normal; color: #444; } + +/* Lists */ +ul, ol, dl { font-size: 1em; line-height: 1.5; margin-bottom: 1.25em; list-style-position: outside; font-family: inherit; } + +ul, ol { margin-left: 0; } +ul.no-bullet, ol.no-bullet { margin-left: 0; } + +/* Unordered Lists */ +ul li ul, ul li ol { margin-left: 1.25em; margin-bottom: 0; font-size: 1em; /* Override nested font-size change */ } +ul.square li ul, ul.circle li ul, ul.disc li ul { list-style: inherit; } +ul.square { list-style-type: square; } +ul.circle { list-style-type: circle; } +ul.disc { list-style-type: disc; } +ul.no-bullet { list-style: none; } + +/* Ordered Lists */ +ol li ul, ol li ol { margin-left: 1.25em; margin-bottom: 0; } + +/* Definition Lists */ +dl dt { margin-bottom: 0.3em; font-weight: bold; } +dl dd { margin-bottom: 0.75em; } + +/* Abbreviations */ +abbr, acronym { text-transform: uppercase; font-size: 90%; color: #000; border-bottom: 1px dotted #ddd; cursor: help; } + +abbr { text-transform: none; } + +/* Blockquotes */ +blockquote { margin: 0 0 1.25em; padding: 0.5625em 1.25em 0 1.1875em; border-left: 1px solid #ddd; } +blockquote cite { display: block; font-size: 0.8125em; color: #748590; } +blockquote cite:before { content: "\2014 \0020"; } +blockquote cite a, blockquote cite a:visited { color: #748590; } + +blockquote, blockquote p { line-height: 1.5; color: #909ea7; } + +/* Microformats */ +.vcard { display: inline-block; margin: 0 0 1.25em 0; border: 1px solid #ddd; padding: 0.625em 0.75em; } +.vcard li { margin: 0; display: block; } +.vcard .fn { font-weight: bold; font-size: 0.9375em; } + +.vevent .summary { font-weight: bold; } +.vevent abbr { cursor: auto; text-decoration: none; font-weight: bold; border: none; padding: 0 0.0625em; } + +@media only screen and (min-width: 768px) { h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { line-height: 1.4; } + h1 { font-size: 2.75em; } + h2 { font-size: 2.3125em; } + h3, #toctitle, .sidebarblock > .content > .title { font-size: 1.6875em; } + h4 { font-size: 1.4375em; } } +/* Tables */ +table { background: #fff; margin-bottom: 1.25em; border: solid 0 #ddd; } +table thead, table tfoot { background: none; font-weight: bold; } +table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td { padding: 1px 8px 1px 5px; font-size: 1em; color: #222; text-align: left; } +table tr th, table tr td { padding: 1px 8px 1px 5px; font-size: 1em; color: #222; } +table tr.even, table tr.alt, table tr:nth-of-type(even) { background: none; } +table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td { display: table-cell; line-height: 1.5; } + +body { -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; tab-size: 4; } + +h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { line-height: 1.4; } + +.clearfix:before, .clearfix:after, .float-group:before, .float-group:after { content: " "; display: table; } +.clearfix:after, .float-group:after { clear: both; } + +*:not(pre) > code { font-size: 0.95em; font-style: normal !important; letter-spacing: 0; padding: 0; background-color: #f2f2f2; -webkit-border-radius: 6px; border-radius: 6px; line-height: inherit; word-wrap: break-word; } +*:not(pre) > code.nobreak { word-wrap: normal; } +*:not(pre) > code.nowrap { white-space: nowrap; } + +pre, pre > code { line-height: 1.2; color: inherit; font-family: "Consolas", "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", monospace; font-weight: normal; } + +em em { font-style: normal; } + +strong strong { font-weight: normal; } + +.keyseq { color: #333333; } + +kbd { font-family: "Consolas", "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", monospace; display: inline-block; color: #000; font-size: 0.65em; line-height: 1.45; background-color: #f7f7f7; border: 1px solid #ccc; -webkit-border-radius: 3px; border-radius: 3px; -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 0.1em white inset; -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 0.1em white inset; box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 0.1em white inset; margin: 0 0.15em; padding: 0.2em 0.5em; vertical-align: middle; position: relative; top: -0.1em; white-space: nowrap; } + +.keyseq kbd:first-child { margin-left: 0; } + +.keyseq kbd:last-child { margin-right: 0; } + +.menuseq, .menuref { color: #000; } + +.menuseq b:not(.caret), .menuref { font-weight: inherit; } + +.menuseq { word-spacing: -0.02em; } +.menuseq b.caret { font-size: 1.25em; line-height: 0.8; } +.menuseq i.caret { font-weight: bold; text-align: center; width: 0.45em; } + +b.button:before, b.button:after { position: relative; top: -1px; font-weight: normal; } + +b.button:before { content: "["; padding: 0 3px 0 2px; } + +b.button:after { content: "]"; padding: 0 2px 0 3px; } + +p a > code:hover { color: #373737; } + +#header, #content, #footnotes, #footer { width: 100%; margin-left: auto; margin-right: auto; margin-top: 0; margin-bottom: 0; max-width: 62.5em; *zoom: 1; position: relative; padding-left: 0.9375em; padding-right: 0.9375em; } +#header:before, #header:after, #content:before, #content:after, #footnotes:before, #footnotes:after, #footer:before, #footer:after { content: " "; display: table; } +#header:after, #content:after, #footnotes:after, #footer:after { clear: both; } + +#content { margin-top: 1.25em; } + +#content:before { content: none; } + +#header > h1:first-child { color: #111; margin-top: 2.25rem; margin-bottom: 0; } +#header > h1:first-child + #toc { margin-top: 8px; border-top: 1px solid #ddd; } +#header > h1:only-child, body.toc2 #header > h1:nth-last-child(2) { border-bottom: 1px solid #ddd; padding-bottom: 8px; } +#header .details { border-bottom: 1px solid #ddd; line-height: 1.45; padding-top: 0.25em; padding-bottom: 0.25em; padding-left: 0.25em; color: #748590; display: -ms-flexbox; display: -webkit-flex; display: flex; -ms-flex-flow: row wrap; -webkit-flex-flow: row wrap; flex-flow: row wrap; } +#header .details span:first-child { margin-left: -0.125em; } +#header .details span.email a { color: #909ea7; } +#header .details br { display: none; } +#header .details br + span:before { content: "\00a0\2013\00a0"; } +#header .details br + span.author:before { content: "\00a0\22c5\00a0"; color: #909ea7; } +#header .details br + span#revremark:before { content: "\00a0|\00a0"; } +#header #revnumber { text-transform: capitalize; } +#header #revnumber:after { content: "\00a0"; } + +#content > h1:first-child:not([class]) { color: #111; border-bottom: 1px solid #ddd; padding-bottom: 8px; margin-top: 0; padding-top: 1rem; margin-bottom: 1.25rem; } + +#toc { border-bottom: 1px solid #ddd; padding-bottom: 0.5em; } +#toc > ul { margin-left: 0.125em; } +#toc ul.sectlevel0 > li > a { font-style: italic; } +#toc ul.sectlevel0 ul.sectlevel1 { margin: 0.5em 0; } +#toc ul { font-family: ff-meta-web-pro-1, ff-meta-web-pro-2, Arial, "Helvetica Neue", sans-serif; list-style-type: none; } +#toc li { line-height: 1.3334; margin-top: 0.3334em; } +#toc a { text-decoration: none; } +#toc a:active { text-decoration: underline; } + +#toctitle { color: #6c818f; font-size: 1.2em; } + +@media only screen and (min-width: 768px) { #toctitle { font-size: 1.375em; } + body.toc2 { padding-left: 15em; padding-right: 0; } + #toc.toc2 { margin-top: 0 !important; background-color: #f2f2f2; position: fixed; width: 15em; left: 0; top: 0; border-right: 1px solid #ddd; border-top-width: 0 !important; border-bottom-width: 0 !important; z-index: 1000; padding: 1.25em 1em; height: 100%; overflow: auto; } + #toc.toc2 #toctitle { margin-top: 0; margin-bottom: 0.8rem; font-size: 1.2em; } + #toc.toc2 > ul { font-size: 0.9em; margin-bottom: 0; } + #toc.toc2 ul ul { margin-left: 0; padding-left: 1em; } + #toc.toc2 ul.sectlevel0 ul.sectlevel1 { padding-left: 0; margin-top: 0.5em; margin-bottom: 0.5em; } + body.toc2.toc-right { padding-left: 0; padding-right: 15em; } + body.toc2.toc-right #toc.toc2 { border-right-width: 0; border-left: 1px solid #ddd; left: auto; right: 0; } } +@media only screen and (min-width: 1280px) { body.toc2 { padding-left: 20em; padding-right: 0; } + #toc.toc2 { width: 20em; } + #toc.toc2 #toctitle { font-size: 1.375em; } + #toc.toc2 > ul { font-size: 0.95em; } + #toc.toc2 ul ul { padding-left: 1.25em; } + body.toc2.toc-right { padding-left: 0; padding-right: 20em; } } +#content #toc { border-style: solid; border-width: 1px; border-color: #d9d9d9; margin-bottom: 1.25em; padding: 1.25em; background: #f2f2f2; -webkit-border-radius: 6px; border-radius: 6px; } +#content #toc > :first-child { margin-top: 0; } +#content #toc > :last-child { margin-bottom: 0; } + +#footer { max-width: 100%; background-color: #000; padding: 1.25em; } + +#footer-text { color: white; line-height: 1.35; } + +#content { margin-bottom: 0.625em; } + +.sect1 { padding-bottom: 0.625em; } + +@media only screen and (min-width: 768px) { #content { margin-bottom: 1.25em; } + .sect1 { padding-bottom: 1.25em; } } +.sect1:last-child { padding-bottom: 0; } + +.sect1 + .sect1 { border-top: 1px solid #ddd; } + +#content h1 > a.anchor, h2 > a.anchor, h3 > a.anchor, #toctitle > a.anchor, .sidebarblock > .content > .title > a.anchor, h4 > a.anchor, h5 > a.anchor, h6 > a.anchor { position: absolute; z-index: 1001; width: 1.5ex; margin-left: -1.5ex; display: block; text-decoration: none !important; visibility: hidden; text-align: center; font-weight: normal; } +#content h1 > a.anchor:before, h2 > a.anchor:before, h3 > a.anchor:before, #toctitle > a.anchor:before, .sidebarblock > .content > .title > a.anchor:before, h4 > a.anchor:before, h5 > a.anchor:before, h6 > a.anchor:before { content: "\00A7"; font-size: 0.85em; display: block; padding-top: 0.1em; } +#content h1:hover > a.anchor, #content h1 > a.anchor:hover, h2:hover > a.anchor, h2 > a.anchor:hover, h3:hover > a.anchor, #toctitle:hover > a.anchor, .sidebarblock > .content > .title:hover > a.anchor, h3 > a.anchor:hover, #toctitle > a.anchor:hover, .sidebarblock > .content > .title > a.anchor:hover, h4:hover > a.anchor, h4 > a.anchor:hover, h5:hover > a.anchor, h5 > a.anchor:hover, h6:hover > a.anchor, h6 > a.anchor:hover { visibility: visible; } +#content h1 > a.link, h2 > a.link, h3 > a.link, #toctitle > a.link, .sidebarblock > .content > .title > a.link, h4 > a.link, h5 > a.link, h6 > a.link { color: #465158; text-decoration: none; } +#content h1 > a.link:hover, h2 > a.link:hover, h3 > a.link:hover, #toctitle > a.link:hover, .sidebarblock > .content > .title > a.link:hover, h4 > a.link:hover, h5 > a.link:hover, h6 > a.link:hover { color: #3b444a; } + +.audioblock, .imageblock, .literalblock, .listingblock, .stemblock, .videoblock { margin-bottom: 1.25em; } + +.admonitionblock td.content > .title, .audioblock > .title, .exampleblock > .title, .imageblock > .title, .listingblock > .title, .literalblock > .title, .stemblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, table.tableblock > .title, .verseblock > .title, .videoblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title { text-rendering: optimizeLegibility; text-align: left; } + +table.tableblock.fit-content > caption.title { white-space: nowrap; width: 0; } + +.paragraph.lead > p, #preamble > .sectionbody > [class="paragraph"]:first-of-type p { font-size: 1.21875em; line-height: 1.6; color: #111; } + +table.tableblock #preamble > .sectionbody > [class="paragraph"]:first-of-type p { font-size: inherit; } + +.admonitionblock > table { border-collapse: separate; border: 0; background: none; width: 100%; } +.admonitionblock > table td.icon { text-align: center; width: 80px; } +.admonitionblock > table td.icon img { max-width: none; } +.admonitionblock > table td.icon .title { font-weight: bold; font-family: ff-meta-web-pro-1, ff-meta-web-pro-2, Arial, "Helvetica Neue", sans-serif; text-transform: uppercase; } +.admonitionblock > table td.content { padding-left: 1.125em; padding-right: 1.25em; border-left: 1px solid #ddd; color: #748590; } +.admonitionblock > table td.content > :last-child > :last-child { margin-bottom: 0; } + +.exampleblock > .content { border-style: solid; border-width: 1px; border-color: #e6e6e6; margin-bottom: 1.25em; padding: 1.25em; background: #fff; -webkit-border-radius: 6px; border-radius: 6px; } +.exampleblock > .content > :first-child { margin-top: 0; } +.exampleblock > .content > :last-child { margin-bottom: 0; } + +.sidebarblock { border-style: solid; border-width: 1px; border-color: #d9d9d9; margin-bottom: 1.25em; padding: 1.25em; background: #f2f2f2; -webkit-border-radius: 6px; border-radius: 6px; } +.sidebarblock > :first-child { margin-top: 0; } +.sidebarblock > :last-child { margin-bottom: 0; } +.sidebarblock > .content > .title { color: #6c818f; margin-top: 0; } + +.exampleblock > .content > :last-child > :last-child, .exampleblock > .content .olist > ol > li:last-child > :last-child, .exampleblock > .content .ulist > ul > li:last-child > :last-child, .exampleblock > .content .qlist > ol > li:last-child > :last-child, .sidebarblock > .content > :last-child > :last-child, .sidebarblock > .content .olist > ol > li:last-child > :last-child, .sidebarblock > .content .ulist > ul > li:last-child > :last-child, .sidebarblock > .content .qlist > ol > li:last-child > :last-child { margin-bottom: 0; } + +.literalblock pre, .listingblock pre:not(.highlight), .listingblock pre[class="highlight"], .listingblock pre[class^="highlight "], .listingblock pre.CodeRay, .listingblock pre.prettyprint { background: #eee; } +.sidebarblock .literalblock pre, .sidebarblock .listingblock pre:not(.highlight), .sidebarblock .listingblock pre[class="highlight"], .sidebarblock .listingblock pre[class^="highlight "], .sidebarblock .listingblock pre.CodeRay, .sidebarblock .listingblock pre.prettyprint { background: #f2f1f1; } + +.literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { border: 1px solid #ccc; -webkit-border-radius: 6px; border-radius: 6px; word-wrap: break-word; padding: 0.5em; font-size: 0.8125em; } +.literalblock pre.nowrap, .literalblock pre[class].nowrap, .listingblock pre.nowrap, .listingblock pre[class].nowrap { overflow-x: auto; white-space: pre; word-wrap: normal; } +@media only screen and (min-width: 768px) { .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { font-size: 0.90625em; } } +@media only screen and (min-width: 1280px) { .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { font-size: 1em; } } + +.literalblock.output pre { color: #eee; background-color: inherit; } + +.listingblock pre.highlightjs { padding: 0; } +.listingblock pre.highlightjs > code { padding: 0.5em; -webkit-border-radius: 6px; border-radius: 6px; } + +.listingblock > .content { position: relative; } + +.listingblock code[data-lang]:before { display: none; content: attr(data-lang); position: absolute; font-size: 0.75em; top: 0.425rem; right: 0.5rem; line-height: 1; text-transform: uppercase; color: #999; } + +.listingblock:hover code[data-lang]:before { display: block; } + +.listingblock.terminal pre .command:before { content: attr(data-prompt); padding-right: 0.5em; color: #999; } + +.listingblock.terminal pre .command:not([data-prompt]):before { content: "$"; } + +table.pyhltable { border-collapse: separate; border: 0; margin-bottom: 0; background: none; } + +table.pyhltable td { vertical-align: top; padding-top: 0; padding-bottom: 0; line-height: 1.2; } + +table.pyhltable td.code { padding-left: .75em; padding-right: 0; } + +pre.pygments .lineno, table.pyhltable td:not(.code) { color: #999; padding-left: 0; padding-right: .5em; border-right: 1px solid #ddd; } + +pre.pygments .lineno { display: inline-block; margin-right: .25em; } + +table.pyhltable .linenodiv { background: none !important; padding-right: 0 !important; } + +.quoteblock { margin: 0 1em 1.25em 1.5em; display: table; } +.quoteblock > .title { margin-left: -1.5em; margin-bottom: 0.75em; } +.quoteblock blockquote, .quoteblock blockquote p { color: #909ea7; font-size: 1.15rem; line-height: 1.75; word-spacing: 0.1em; letter-spacing: 0; font-style: italic; text-align: justify; } +.quoteblock blockquote { margin: 0; padding: 0; border: 0; } +.quoteblock blockquote:before { content: "\201c"; float: left; font-size: 2.75em; font-weight: bold; line-height: 0.6em; margin-left: -0.6em; color: #6c818f; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); } +.quoteblock blockquote > .paragraph:last-child p { margin-bottom: 0; } +.quoteblock .attribution { margin-top: 0.5em; margin-right: 0.5ex; text-align: right; } +.quoteblock .quoteblock { margin-left: 0; margin-right: 0; padding: 0.5em 0; border-left: 3px solid #748590; } +.quoteblock .quoteblock blockquote { padding: 0 0 0 0.75em; } +.quoteblock .quoteblock blockquote:before { display: none; } + +.verseblock { margin: 0 1em 1.25em 1em; } +.verseblock pre { font-family: "Open Sans", "DejaVu Sans", sans; font-size: 1.15rem; color: #909ea7; font-weight: 300; text-rendering: optimizeLegibility; } +.verseblock pre strong { font-weight: 400; } +.verseblock .attribution { margin-top: 1.25rem; margin-left: 0.5ex; } + +.quoteblock .attribution, .verseblock .attribution { font-size: 0.8125em; line-height: 1.45; font-style: italic; } +.quoteblock .attribution br, .verseblock .attribution br { display: none; } +.quoteblock .attribution cite, .verseblock .attribution cite { display: block; letter-spacing: -0.025em; color: #748590; } + +.quoteblock.abstract { margin: 0 1em 1.25em 1em; display: block; } +.quoteblock.abstract > .title { margin: 0 0 0.375em 0; font-size: 1.15em; text-align: center; } +.quoteblock.abstract blockquote, .quoteblock.abstract blockquote p { word-spacing: 0; line-height: 1.6; } +.quoteblock.abstract blockquote:before, .quoteblock.abstract p:before { display: none; } + +table.tableblock { max-width: 100%; border-collapse: separate; } + +p.tableblock:last-child { margin-bottom: 0; } + +td.tableblock > .content { margin-bottom: -1.25em; } + +table.tableblock, th.tableblock, td.tableblock { border: 0 solid #ddd; } + +table.grid-all > thead > tr > .tableblock, table.grid-all > tbody > tr > .tableblock { border-width: 0 0 0 0; } + +table.grid-all > tfoot > tr > .tableblock { border-width: 0 0 0 0; } + +table.grid-cols > * > tr > .tableblock { border-width: 0 0 0 0; } + +table.grid-rows > thead > tr > .tableblock, table.grid-rows > tbody > tr > .tableblock { border-width: 0 0 0 0; } + +table.grid-rows > tfoot > tr > .tableblock { border-width: 0 0 0 0; } + +table.grid-all > * > tr > .tableblock:last-child, table.grid-cols > * > tr > .tableblock:last-child { border-right-width: 0; } + +table.grid-all > tbody > tr:last-child > .tableblock, table.grid-all > thead:last-child > tr > .tableblock, table.grid-rows > tbody > tr:last-child > .tableblock, table.grid-rows > thead:last-child > tr > .tableblock { border-bottom-width: 0; } + +table.frame-all { border-width: 0; } + +table.frame-sides { border-width: 0 0; } + +table.frame-topbot, table.frame-ends { border-width: 0 0; } + +table.stripes-all tr, table.stripes-odd tr:nth-of-type(odd) { background: none; } + +table.stripes-none tr, table.stripes-odd tr:nth-of-type(even) { background: none; } + +th.halign-left, td.halign-left { text-align: left; } + +th.halign-right, td.halign-right { text-align: right; } + +th.halign-center, td.halign-center { text-align: center; } + +th.valign-top, td.valign-top { vertical-align: top; } + +th.valign-bottom, td.valign-bottom { vertical-align: bottom; } + +th.valign-middle, td.valign-middle { vertical-align: middle; } + +table thead th, table tfoot th { font-weight: bold; } + +tbody tr th { display: table-cell; line-height: 1.5; background: none; } + +tbody tr th, tbody tr th p, tfoot tr th, tfoot tr th p { color: #222; font-weight: bold; } + +p.tableblock > code:only-child { background: none; padding: 0; } + +p.tableblock { font-size: 1em; } + +td > div.verse { white-space: pre; } + +ol { margin-left: 0.25em; } + +ul li ol { margin-left: 0; } + +dl dd { margin-left: 1.125em; } + +dl dd:last-child, dl dd:last-child > :last-child { margin-bottom: 0; } + +ol > li p, ul > li p, ul dd, ol dd, .olist .olist, .ulist .ulist, .ulist .olist, .olist .ulist { margin-bottom: 0.625em; } + +ul.checklist, ul.none, ol.none, ul.no-bullet, ol.no-bullet, ol.unnumbered, ul.unstyled, ol.unstyled { list-style-type: none; } + +ul.no-bullet, ol.no-bullet, ol.unnumbered { margin-left: 0.625em; } + +ul.unstyled, ol.unstyled { margin-left: 0; } + +ul.checklist { margin-left: 0.625em; } + +ul.checklist li > p:first-child > .fa-square-o:first-child, ul.checklist li > p:first-child > .fa-check-square-o:first-child { width: 1.25em; font-size: 0.8em; position: relative; bottom: 0.125em; } + +ul.checklist li > p:first-child > input[type="checkbox"]:first-child { margin-right: 0.25em; } + +ul.inline { display: -ms-flexbox; display: -webkit-box; display: flex; -ms-flex-flow: row wrap; -webkit-flex-flow: row wrap; flex-flow: row wrap; list-style: none; margin: 0 0 0.625em -1.25em; } + +ul.inline > li { margin-left: 1.25em; } + +.unstyled dl dt { font-weight: normal; font-style: normal; } + +ol.arabic { list-style-type: decimal; } + +ol.decimal { list-style-type: decimal-leading-zero; } + +ol.loweralpha { list-style-type: lower-alpha; } + +ol.upperalpha { list-style-type: upper-alpha; } + +ol.lowerroman { list-style-type: lower-roman; } + +ol.upperroman { list-style-type: upper-roman; } + +ol.lowergreek { list-style-type: lower-greek; } + +.hdlist > table, .colist > table { border: 0; background: none; } +.hdlist > table > tbody > tr, .colist > table > tbody > tr { background: none; } + +td.hdlist1, td.hdlist2 { vertical-align: top; padding: 0 0.625em; } + +td.hdlist1 { font-weight: bold; padding-bottom: 1.25em; } + +.literalblock + .colist, .listingblock + .colist { margin-top: -0.5em; } + +.colist td:not([class]):first-child { padding: 0.4em 0.75em 0 0.75em; line-height: 1; vertical-align: top; } +.colist td:not([class]):first-child img { max-width: none; } +.colist td:not([class]):last-child { padding: 0.25em 0; } + +.thumb, .th { line-height: 0; display: inline-block; border: solid 4px #fff; -webkit-box-shadow: 0 0 0 1px #ddd; box-shadow: 0 0 0 1px #ddd; } + +.imageblock.left { margin: 0.25em 0.625em 1.25em 0; } +.imageblock.right { margin: 0.25em 0 1.25em 0.625em; } +.imageblock > .title { margin-bottom: 0; } +.imageblock.thumb, .imageblock.th { border-width: 6px; } +.imageblock.thumb > .title, .imageblock.th > .title { padding: 0 0.125em; } + +.image.left, .image.right { margin-top: 0.25em; margin-bottom: 0.25em; display: inline-block; line-height: 0; } +.image.left { margin-right: 0.625em; } +.image.right { margin-left: 0.625em; } + +a.image { text-decoration: none; display: inline-block; } +a.image object { pointer-events: none; } + +sup.footnote, sup.footnoteref { font-size: 0.875em; position: static; vertical-align: super; } +sup.footnote a, sup.footnoteref a { text-decoration: none; } +sup.footnote a:active, sup.footnoteref a:active { text-decoration: underline; } + +#footnotes { padding-top: 0.75em; padding-bottom: 0.75em; margin-bottom: 0.625em; } +#footnotes hr { width: 20%; min-width: 6.25em; margin: -0.25em 0 0.75em 0; border-width: 1px 0 0 0; } +#footnotes .footnote { padding: 0 0.375em 0 0.225em; line-height: 1.3334; font-size: 0.875em; margin-left: 1.2em; margin-bottom: 0.2em; } +#footnotes .footnote a:first-of-type { font-weight: bold; text-decoration: none; margin-left: -1.05em; } +#footnotes .footnote:last-of-type { margin-bottom: 0; } +#content #footnotes { margin-top: -0.625em; margin-bottom: 0; padding: 0.75em 0; } + +.gist .file-data > table { border: 0; background: #fff; width: 100%; margin-bottom: 0; } +.gist .file-data > table td.line-data { width: 99%; } + +div.unbreakable { page-break-inside: avoid; } + +.big { font-size: larger; } + +.small { font-size: smaller; } + +.underline { text-decoration: underline; } + +.overline { text-decoration: overline; } + +.line-through { text-decoration: line-through; } + +.aqua { color: #00bfbf; } + +.aqua-background { background-color: #00fafa; } + +.black { color: black; } + +.black-background { background-color: black; } + +.blue { color: #0000bf; } + +.blue-background { background-color: #0000fa; } + +.fuchsia { color: #bf00bf; } + +.fuchsia-background { background-color: #fa00fa; } + +.gray { color: #606060; } + +.gray-background { background-color: #7d7d7d; } + +.green { color: #006000; } + +.green-background { background-color: #007d00; } + +.lime { color: #00bf00; } + +.lime-background { background-color: #00fa00; } + +.maroon { color: #600000; } + +.maroon-background { background-color: #7d0000; } + +.navy { color: #000060; } + +.navy-background { background-color: #00007d; } + +.olive { color: #606000; } + +.olive-background { background-color: #7d7d00; } + +.purple { color: #600060; } + +.purple-background { background-color: #7d007d; } + +.red { color: #bf0000; } + +.red-background { background-color: #fa0000; } + +.silver { color: #909090; } + +.silver-background { background-color: #bcbcbc; } + +.teal { color: #006060; } + +.teal-background { background-color: #007d7d; } + +.white { color: #bfbfbf; } + +.white-background { background-color: #fafafa; } + +.yellow { color: #bfbf00; } + +.yellow-background { background-color: #fafa00; } + +span.icon > .fa { cursor: default; } +a span.icon > .fa { cursor: inherit; } + +.admonitionblock td.icon [class^="fa icon-"] { font-size: 2.5em; text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); cursor: default; } +.admonitionblock td.icon .icon-note:before { content: "\f05a"; color: #333333; } +.admonitionblock td.icon .icon-tip:before { content: "\f0eb"; text-shadow: 1px 1px 2px rgba(155, 155, 0, 0.8); color: #111; } +.admonitionblock td.icon .icon-warning:before { content: "\f071"; color: #bf6900; } +.admonitionblock td.icon .icon-caution:before { content: "\f06d"; color: #bf3400; } +.admonitionblock td.icon .icon-important:before { content: "\f06a"; color: #bf0000; } + +.conum[data-value] { display: inline-block; color: #fff !important; background-color: #000; -webkit-border-radius: 100px; border-radius: 100px; text-align: center; font-size: 0.75em; width: 1.67em; height: 1.67em; line-height: 1.67em; font-family: "Open Sans", "DejaVu Sans", sans-serif; font-style: normal; font-weight: bold; } +.conum[data-value] * { color: #fff !important; } +.conum[data-value] + b { display: none; } +.conum[data-value]:after { content: attr(data-value); } +pre .conum[data-value] { position: relative; top: -0.125em; } + +b.conum * { color: inherit !important; } + +.conum:not([data-value]):empty { display: none; } + +h4 { color: #6c818f; } + +.literalblock > .content > pre, .listingblock > .content > pre { -webkit-border-radius: 6px; border-radius: 6px; margin-left: 2em; margin-right: 2em; } + +.admonitionblock { margin-left: 2em; margin-right: 2em; } +.admonitionblock > table { border: 1px solid #607f90; border-top-width: 1.5em; background-color: #f0f9ff; border-collapse: separate; -webkit-border-radius: 0; border-radius: 0; } +.admonitionblock > table td.icon { padding-top: .5em; padding-bottom: .5em; } +.admonitionblock > table td.content { padding: .5em 1em; color: #000; font-size: .9em; border-left: none; } + +.sidebarblock { background-color: #e8ecef; border-color: #ccc; } +.sidebarblock > .content > .title { color: #444; } + +table.tableblock.grid-all { border-collapse: collapse; -webkit-border-radius: 0; border-radius: 0; } +table.tableblock.grid-all th.tableblock, table.tableblock.grid-all td.tableblock { border-bottom: 1px solid #aaa; } + +#footer { background-color: #465158; padding: 2em; } + +#footer-text { color: #eee; font-size: 0.8em; text-align: center; } diff --git a/manual/cli.txt b/manual/cli.txt new file mode 100644 index 00000000..001b6e9f --- /dev/null +++ b/manual/cli.txt @@ -0,0 +1,42 @@ +== Command-line Interface + +Even though PulseView has a graphical user interface, there is also a command-line interface that +you can make use of. It is meant to provide functions for convenience and debug purposes, not to +replace the user interface itself. + +Running + + pulseview -h + +gives you a list of these functions. + +Since PulseView can't automatically scan for devices connected to a COM port (ttySx on Linux) or +Ethernet, you can tell it to look for a specific device using the -d or --driver parameter. Its +usage is the same as for sigrok-cli. For example: + + pulseview -d lecroy-xstream:conn=vxi/192.168.178.20/111 + +Also, just as with sigrok-cli, you can specify -i / --input-file and -I / --input-format to open +a file on startup. Without -I, it is assumed that the file is in the native sigrok format (.sr). +You can also specify more than one file but they are all expected to be in the same format then. +Example: + + pulseview -i data.csv -I csv:samplerate=3000000 + +The remaining parameters are mostly for debug purposes: + + -V / --version Shows the release version + -l / --loglevel Sets the libsigrok/libsigrokdecode log level (max is 5) + -D / --dont-scan Don't auto-scan for devices + -c / --clean Don't restore previous sessions on startup + +Of these, -D / --dont-scan can be useful when PulseView gets stuck during the startup device scan. +No such scan will be performed then, allowing the program to start up but you'll have to scan for +your acquisition device(s) manually before you can use them. + +Another potentially useful option is -c / --clean, which can be used when PulseView doesn't start +up and you don't know what could cause this. + +Thus, the combination of both parameters can be seen as some kind of "safe mode" for PulseView: + + pulseview -c -D diff --git a/manual/decoders.txt b/manual/decoders.txt new file mode 100644 index 00000000..d2ca15ab --- /dev/null +++ b/manual/decoders.txt @@ -0,0 +1,140 @@ +== Decoders + +Protocol decoders are one of the key elements of PulseView's functionality. They take +input data that you acquired and process it in a way that results in a (hopefully) much +easier to understand representation of that same data. + +In its simplest form, a protocol decoder (PD) converts a group of 1-bit signals into a +stream of n-bit events. This is exactly what the parallel PD does: it takes for example +8 logic channels and treats them like an 8-bit parallel bus, emitting annotations that +show the current state of the bus at any point in time. + +=== Basic Operation + +Another one of the protocol decoders available to you is the I²C decoder. It takes the +two I²C signals SCL and SDA (serial clock / serial data) and shows you the details of +the I²C communication without the need to evaluate the signal bit by bit yourself. + +As an example, let's have look at one of the sample .sr files we keep around for +validation of the PD code base: https://sigrok.org/gitweb/?p=sigrok-dumps.git;a=blob_plain;f=i2c/rtc_dallas_ds1307/rtc_ds1307_200khz.sr;hb=HEAD[rtc_ds1307_200khz.sr]. +It contains the capture of an I²C master interacting with a https://www.maximintegrated.com/en/products/digital/real-time-clocks/DS1307.html[Dallas DS1307 I²C Real-Time Clock] +where the master repeatedly sets and queries the time of day. After loading and using +"zoom-to-fit", it looks similar to this: + +image::pv_decoders_1.png[] + +Adding the I²C decoder by clicking on ➊ and selecting I²C from the list adds +a new decode signal to the view. PulseView tries to match existing signals to the signals +that the newly added protocol decoder needs to function, which is what happened here - +SCL and SDA have been automatically assigned and the PD has decoded the communication with +the default parameters. If you need to change the signal assignment or change the decoding +parameters, you can click on ➋ to do so. + +When you zoom in, you now see that PulseView has decoded the I²C messages and displays +these annotations as part of the decode signal (note that we have zoomed in so far that +PulseView shows you the individual samples): + +image::pv_decoders_2.png[] + +This is already very useful, and a massive improvement over counting out pulses on an +oscilloscope screen. However, sigrok allows us to go one step further with the use of +so-called stacked decoders. + +=== Decoder Stacking + +To add a stacked decoder we open the settings of the decode signal, go to the _Stack +Decoder_ menu ➊, and select the DS1307 decoder: + +image::pv_decoders_3.png[] + +With the stacked decoder added, we can now see that PulseView has decoded the meaning +of the I²C commands, so that we don't need to bother searching the reference manual. +In this view, we can see that the I²C packet was a command to read the date and time, +which was then reported to be 10.03.2013 23:35:30. + +There are all kinds of stacked decoders available, but keep in mind that they're not +shown in the decoder menu. Stacked decoders require a lower-level decoder first before +they become stackable. Most of the time, they require either the UART, I²C or SPI decoder. + +You can check the https://sigrok.org/wiki/Protocol_decoders[List of Protocol Decoders] +to see which protocol decoders have been created already. + +=== Using Decoders on Analog Signals + +If you're capturing data using an oscilloscope or import analog signal data from a file, +you'll quickly notice that protocol decoders don't give you the option to select analog +channels as inputs. That is because as of now, decoders only work on logic signals. You +can however convert analog signals into logic signals by choosing a conversion setting +from the signal setting popup. + +image::pv_conversion_a2l.png[] + +Here, A1 has been converted using a threshold (with default settings) and A2 has been +converted using a Schmitt-Trigger emulation (also with default settings). Additionally, +the conversion threshold display mode has been set to _Background_ in the _Views_ settings +dialog. This way, you can tell how PulseView decided to change the logic signal level +as you can now visually understand where the ranges for high and low are placed. + +Aside from the default conversion threshold(s), you can choose from a few common presets +or enter custom values as well. They take the form "0.0V" and "0.0V/0.0V", respectively. + +=== Troubleshooting + +In case a protocol decoder doesn't provide the expected result, there are several things +you can check. + +The first check you should perform is whether the time unit in the ruler +is given as "sa". This is short for "samples" and means that the device didn't provide +a sample rate and so PulseView has no way of showing a time scale in seconds or +fractions thereof. While some decoders can run without timing information, or only +optionally make use of the time scale, others may not be able to interpret the +input data since timing information is an essential part of the very protocol. + +Another issue to remain aware of is that decoders need enough samples per protocol step +to reliably interpret the information. In typical cases the minimum sample rate should +be four to five times the rate of the fastest activity in the protocol. + +If a protocol decoder runs but shows you annotations that don't seem to make any sense, +it's worth double-checking the decoder settings. One common source of error is the +baud rate. For example, the CAN protocol decoder doesn't know what baud rate +is used on the bus that you captured, so it could be that a different baud rate is used +than the one you set. Also, if this is still not the reason for the malfunction, it's +worth checking whether any of the signals have been captured inverted. Again using the +CAN bus as an example, the decoder will decode the signal just fine if it's inverted but +it'll show data even when the signal looks "idle". + +When a protocol decoder stops execution because of an unmet constraint (required input +not connected, essential parameter not specified) or a bug in the decoder itself, you +will be presented a static red message in the protocol decoder's display area. +In that case, you check the log output in the settings menu. There you'll find the Python +error description which you can use to either adjust the configuration, +or debug the decoder (and let us know of the fix) or you can copy that information and +file a bug report so that we can fix it. + +Further helpful knowledge and explanations on logic analyzers can be found in our +https://sigrok.org/wiki/FAQ#Where_can_I_learn_more_about_logic_analyzers.3F["Learn about logic analyzers" FAQ item]. + +=== Exporting Annotations + +If you want to postprocess annotations that were generated by a protocol decoder, you +can do so by right-clicking into the area of the decode signal (not on the signal label +on the left). You are shown several export methods to choose from, with the last one +being only available if the cursor is enabled. + +After you chose a method that suits your needs, you are prompted for a file to export +the annotations to. The contents of the file very much depend on the option you chose +but also on the annotation export format string that you can define in the _Decoders_ +menu of the settings dialog. If the default output isn't useful to you, you can +customize it there. + +=== Creating a Protocol Decoder + +Protocol decoders are written in Python and can be created using nothing more than a +text editor. You, too, can write one! + +To find out how to go about it, please see our https://sigrok.org/wiki/Protocol_decoder_HOWTO[Protocol Decoder How-To] +and the https://sigrok.org/wiki/Protocol_decoder_API[Protocol Decoder API Reference]. + +If you do write one, we'd appreciate if you'd contribute to our project so that everyone +can benefit from your work. + diff --git a/manual/images/_callouts.xcf b/manual/images/_callouts.xcf new file mode 100644 index 00000000..c7123159 Binary files /dev/null and b/manual/images/_callouts.xcf differ diff --git a/manual/images/device_selector.png b/manual/images/device_selector.png new file mode 100644 index 00000000..2bba96b3 Binary files /dev/null and b/manual/images/device_selector.png differ diff --git a/manual/images/device_selector_dropdown.png b/manual/images/device_selector_dropdown.png new file mode 100644 index 00000000..08c73992 Binary files /dev/null and b/manual/images/device_selector_dropdown.png differ diff --git a/manual/images/device_selector_scan.png b/manual/images/device_selector_scan.png new file mode 100644 index 00000000..0cbe54bc Binary files /dev/null and b/manual/images/device_selector_scan.png differ diff --git a/manual/images/pv_after_startup.png b/manual/images/pv_after_startup.png new file mode 100644 index 00000000..22a0fcc1 Binary files /dev/null and b/manual/images/pv_after_startup.png differ diff --git a/manual/images/pv_after_startup.xcf b/manual/images/pv_after_startup.xcf new file mode 100644 index 00000000..e81a4a58 Binary files /dev/null and b/manual/images/pv_after_startup.xcf differ diff --git a/manual/images/pv_analysis.png b/manual/images/pv_analysis.png new file mode 100644 index 00000000..215d1483 Binary files /dev/null and b/manual/images/pv_analysis.png differ diff --git a/manual/images/pv_analysis.xcf b/manual/images/pv_analysis.xcf new file mode 100644 index 00000000..93b7859f Binary files /dev/null and b/manual/images/pv_analysis.xcf differ diff --git a/manual/images/pv_conversion_a2l.png b/manual/images/pv_conversion_a2l.png new file mode 100644 index 00000000..b804fef6 Binary files /dev/null and b/manual/images/pv_conversion_a2l.png differ diff --git a/manual/images/pv_cursors_markers.png b/manual/images/pv_cursors_markers.png new file mode 100644 index 00000000..fc8552c4 Binary files /dev/null and b/manual/images/pv_cursors_markers.png differ diff --git a/manual/images/pv_cursors_markers.xcf b/manual/images/pv_cursors_markers.xcf new file mode 100644 index 00000000..3748545a Binary files /dev/null and b/manual/images/pv_cursors_markers.xcf differ diff --git a/manual/images/pv_decoders_1.png b/manual/images/pv_decoders_1.png new file mode 100644 index 00000000..ac33defe Binary files /dev/null and b/manual/images/pv_decoders_1.png differ diff --git a/manual/images/pv_decoders_1.xcf b/manual/images/pv_decoders_1.xcf new file mode 100644 index 00000000..19fc127d Binary files /dev/null and b/manual/images/pv_decoders_1.xcf differ diff --git a/manual/images/pv_decoders_2.png b/manual/images/pv_decoders_2.png new file mode 100644 index 00000000..4c856724 Binary files /dev/null and b/manual/images/pv_decoders_2.png differ diff --git a/manual/images/pv_decoders_3.png b/manual/images/pv_decoders_3.png new file mode 100644 index 00000000..153cfdd0 Binary files /dev/null and b/manual/images/pv_decoders_3.png differ diff --git a/manual/images/pv_decoders_3.xcf b/manual/images/pv_decoders_3.xcf new file mode 100644 index 00000000..f570f129 Binary files /dev/null and b/manual/images/pv_decoders_3.xcf differ diff --git a/manual/images/pv_decoders_4.png b/manual/images/pv_decoders_4.png new file mode 100644 index 00000000..bb8d863b Binary files /dev/null and b/manual/images/pv_decoders_4.png differ diff --git a/manual/images/pv_device_config.png b/manual/images/pv_device_config.png new file mode 100644 index 00000000..ee7694b2 Binary files /dev/null and b/manual/images/pv_device_config.png differ diff --git a/manual/images/pv_device_config.xcf b/manual/images/pv_device_config.xcf new file mode 100644 index 00000000..0e7252d7 Binary files /dev/null and b/manual/images/pv_device_config.xcf differ diff --git a/manual/images/pv_import.png b/manual/images/pv_import.png new file mode 100644 index 00000000..645e3193 Binary files /dev/null and b/manual/images/pv_import.png differ diff --git a/manual/images/pv_nodevice.png b/manual/images/pv_nodevice.png new file mode 100644 index 00000000..a5db293f Binary files /dev/null and b/manual/images/pv_nodevice.png differ diff --git a/manual/images/top_bar.png b/manual/images/top_bar.png new file mode 100644 index 00000000..5aeae70f Binary files /dev/null and b/manual/images/top_bar.png differ diff --git a/manual/import_export.txt b/manual/import_export.txt new file mode 100644 index 00000000..2e5c8b22 --- /dev/null +++ b/manual/import_export.txt @@ -0,0 +1,34 @@ +== Data Import/Export + +In order to facilitate versatile use of the sigrok suite, libsigrok allows users to import +and export data from files in various formats - some of them as generic as possible, others +very specific. For a list and details, make sure to check https://sigrok.org/wiki/Input_output_formats[the wiki]. + +=== Import + +The first step to importing data from a file is to know what format the data in the file is +encoded in. There are common, not-so-common and outright exotic ways to represent data and sigrok +tries to suit as many needs as it can. To see which formats your version of PulseView supports, +just click on the small arrow next to the _Open_ button: + +image::pv_import.png[] + +After choosing the format that you want to use, PulseView will ask for the file name to open. +Once you picked the file, you may be asked to specify the details of the format, if the input +module requires them. + +For example, the VCD import will ask you for these: + +* Compress idle periods: Compress idle periods longer than the specified value (default 0) +* Downsampling factor: Downsample, i.e. divide the samplerate by the specified factor (default 1) +* Number of logic channels: The number of (logic) channels in the data (default 0) +* Skip samples until timestamp: Skip samples until the specified timestamp; < 0: Skip until first timestamp listed; 0: Don't skip (default -1) + +The detailed description of each item can also be seen when clicking on the help icon on the right +or hovering your mouse over it. A click on _OK_ then loads the data from the selected file and you +can work with it. + +=== Export + +Export works just the same as the import: clicking on the small arrow next to the _Save_ button +brings up the export menu. Simply choose the format you want to use and proceed. diff --git a/manual/installation.txt b/manual/installation.txt new file mode 100644 index 00000000..047333ed --- /dev/null +++ b/manual/installation.txt @@ -0,0 +1,100 @@ +[[installation,Installation]] +== Installation + +PulseView can be run on Linux, Windows, Mac OS X or Android. For some platforms, we provide binary +packages, for others we provide installers and for others we provide AppImage containers that +you can run without the need to install anything. Check the https://sigrok.org/wiki/Downloads[sigrok download page] +to see which option is available for your platform. + +=== Linux + +On Linux, the usual way to install PulseView is to install the packages provided by your distro's +package manager. However, sometimes only outdated packages are made available to you. In that case, +you have two options: + +. https://sigrok.org/wiki/Downloads[Download] and use the AppImage which contains all required files and needs no installation: ++ +-- +[listing, subs="normal"] +chmod u+x PulseView-NIGHTLY-x86_64.AppImage +./PulseView-NIGHTLY-x86_64.AppImage + +Please be aware, however, that the AppImages are built every night, so they always contain +the latest development changes. While we do try to keep the code base in a working state, it is sometimes +unavoidable to introduce bugs that show up in the nightly builds. If you encounter something that is +odd to you, please download and install the latest nightly and check if the issue still exists. If it +does, feel free to https://sigrok.org/bugzilla/[file a bug]. + +No system files are changed, so if you decide that you no longer want to use PulseView, simply +delete the AppImage. If you also want the stored settings gone, delete ~/.config/sigrok as well. +-- + +. Uninstall any sigrok packages from your package manager and build PulseView from source: ++ +-- +[listing, subs="normal"] +_[install dependencies https://sigrok.org/wiki/Linux#Building[as listed on the wiki]]_ +mkdir ~/sr +cd ~/sr +wget 'https://sigrok.org/gitweb/?p=sigrok-util.git;a=blob_plain;f=cross-compile/linux/sigrok-cross-linux' -O sigrok-cross-linux +chmod u+x sigrok-cross-linux +./sigrok-cross-linux +export LD_LIBRARY_PATH=~/sr/lib +~/sr/bin/pulseview + +No system files are changed, so if you decide that you no longer want to use PulseView, simply +delete the ~/sr directory. If you also want the stored settings gone, delete ~/.config/sigrok +as well. +-- + +[WARNING] +-- +If you don't install the PulseView distro packages (as is the case when using the AppImage or building +from source), PulseView will not be able to access USB and serial port devices unless it's run as root. +Since programs shouldn't be run as root unless absolutely necessary, we provide udev configuration files +that allows PulseView access to those devices without being root. + +Here's how you install them: +[listing, subs="normal"] +sudo bash +cd /etc/udev/rules.d/ +wget 'https://sigrok.org/gitweb/?p=libsigrok.git;a=blob_plain;f=contrib/60-libsigrok.rules' -O 60-libsigrok.rules +wget 'https://sigrok.org/gitweb/?p=libsigrok.git;a=blob_plain;f=contrib/61-libsigrok-plugdev.rules' -O 61-libsigrok-plugdev.rules +wget 'https://sigrok.org/gitweb/?p=libsigrok.git;a=blob_plain;f=contrib/61-libsigrok-uaccess.rules' -O 61-libsigrok-uaccess.rules +sudo udevadm control --reload-rules +-- + +=== Windows + +We offer installers for PulseView that contain everything you need to get started. Simply download +them from the https://sigrok.org/wiki/Downloads[sigrok download page] and run them as any other Windows +installer. +Please be aware, however, that the Windows installers are built every night, so they always contain +the latest development changes. While we do try to keep the code base in a working state, it is sometimes +unavoidable to introduce bugs that show up in the nightly builds. If you encounter something that is +odd to you, please download and install the latest nightly and check if the issue still exists. If it +does, feel free to https://sigrok.org/bugzilla/[file a bug]. + +After installation, you will find a program called Zadig in the start menu. By default, certain devices +recognized by Windows will have drivers installed for them that PulseView cannot use. The purpose of +Zadig is to let you change the driver Windows uses for a particular device - for most devices you'll need +to choose WinUSB to use them with PulseView or the original proprietary Windows driver to use it with whatever +other software you access the device with. More details are available https://sigrok.org/wiki/Windows[in the wiki]. + +In case your device doesn't show up in PulseView and you can't find it with a scan either (see next +chapter), check with Zadig whether the correct driver is assigned for the device. + +=== Mac OS X + +We offer DMG installers for PulseView that contain everything you need to get started. Simply download +them from the https://sigrok.org/wiki/Downloads[sigrok download page] and run them. + +Please be aware, however, that the DMG installers are built every night, so they always contain +the latest development changes. While we do try to keep the code base in a working state, it is sometimes +unavoidable to introduce bugs that show up in the nightly builds. If you encounter something that is +odd to you, please download and install the latest nightly and check if the issue still exists. If it +does, feel free to https://sigrok.org/bugzilla/[file a bug]. + +No system files are changed, so if you decide that you no longer want to use PulseView, simply +delete the DMG file. If you also want the stored settings gone, delete +~/Library/Preferences/pulseview.plist as well. diff --git a/manual/license.txt b/manual/license.txt new file mode 100644 index 00000000..34ac5804 --- /dev/null +++ b/manual/license.txt @@ -0,0 +1,3 @@ +== License + +This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. diff --git a/manual/manual.txt b/manual/manual.txt new file mode 100644 index 00000000..c4a31277 --- /dev/null +++ b/manual/manual.txt @@ -0,0 +1,28 @@ +PulseView User Manual +===================== +unreleased development snapshot, dated 2018-10-29 +:doctype: book +:imagesdir: ./images +:sectnums: +:toc: +:toclevels: 2 +:icons: font +:figure-caption: Image + +ifdef::ebook-format[:leveloffset: -1] + +include::license.txt[] + +include::overview.txt[] + +include::installation.txt[] + +include::acquisition.txt[] + +include::analysis.txt[] + +include::decoders.txt[] + +include::import_export.txt[] + +include::cli.txt[] diff --git a/manual/overview.txt b/manual/overview.txt new file mode 100644 index 00000000..bfd87f85 --- /dev/null +++ b/manual/overview.txt @@ -0,0 +1,40 @@ +[[overview,Overview]] +== Overview + +PulseView is a graphical frontend for the libsigrok and libsigrokdecode libraries, permitting +access to a wide range of devices and protocol decoders to let you record, analyze, process +and export analog and logic data. It is part of the sigrok suite, just like the libraries that +it makes use of. + +The sigrok suite needs some kind of hardware to interface to the signals you want to examine. +We found that most people are currently using logic analyzers based on the http://www.cypress.com/products/ez-usb-fx2lp[Cypress FX2 microcontroller]. +With http://sigrok.org/wiki/Fx2lafw[fx2lafw], sigrok's open source runtime firmware, any device +containing an FX2 can become a powerful streaming logic analyzer. +A variety of compatible low cost chinese made logic analyzer products are available for as little +as $5. These can easily be found by searching for _24MHz Logic Analyzer_. There are also barebone +Cypress FX2 boards such as the Lcsoft Mini Board, which can usually be found by searching for +_Cypress FX2 Board_ or similar. + +In addition, a good set of https://sigrok.org/wiki/Probe_comparison[quality probe hooks] is recommended. + +Aside from FX2-based logic analyzers, sigrok also supports FX2-based oscilloscopes such as the +https://sigrok.org/wiki/Hantek_6022BE[Hantek 6022BE], non-FX2 devices like the +https://sigrok.org/wiki/Openbench_Logic_Sniffer[Openbench Logic Sniffer] or devices that make use +of the SCPI protocol, as all reasonably modern oscilloscopes do (Rigol DS1054z, LeCroy WaveRunner, +Yokogawa DLM and similar). + +Please be aware however, that PulseView currently only supports devices that can either work as an +https://sigrok.org/wiki/Supported_hardware#Oscilloscopes[oscilloscope], +a https://sigrok.org/wiki/Supported_hardware#Logic_analyzers[logic analyzer] or +a https://sigrok.org/wiki/Supported_hardware#Mixed-signal_devices[mixed-signal device]. This +means that multimeters in particular are currently only usable with either https://sigrok.org/wiki/Sigrok-cli[sigrok-cli] +or https://sigrok.org/wiki/Sigrok-meter[sigrok-meter]. + +image::pv_after_startup.png[] + +The PulseView user interface is geared towards navigation and analysis of captured waveforms, so +the most space is by default used up by the main trace view. From here, you can access the most +often used features. + +Before we dive deeper into how to accomplish things, let's make PulseView available on your +system first. diff --git a/pulseview.qrc b/pulseview.qrc index 5b6d0983..33e36100 100644 --- a/pulseview.qrc +++ b/pulseview.qrc @@ -1,5 +1,5 @@ - + icons/add-decoder.svg icons/application-exit.png icons/channels.svg @@ -9,9 +9,13 @@ icons/document-new.png icons/document-open.png icons/document-save-as.png + icons/help-browser.png icons/information.svg + icons/media-playback-pause.png + icons/media-playback-start.png icons/menu.svg icons/preferences-system.png + icons/settings-general.png icons/settings-views.svg icons/pulseview.png icons/pulseview.svg @@ -30,10 +34,84 @@ icons/trigger-marker-rising.svg icons/trigger-none.svg icons/trigger-rising.svg + icons/view-displaymode-last_complete_segment.svg + icons/view-displaymode-last_segment.svg + icons/view-displaymode-single_segment.svg icons/window-new.png icons/zoom-fit-best.png icons/zoom-in.png - icons/zoom-original.png icons/zoom-out.png + + + + themes/qdarkstyle/style.qss + themes/qdarkstyle/rc/up_arrow_disabled.png + themes/qdarkstyle/rc/Hmovetoolbar.png + themes/qdarkstyle/rc/stylesheet-branch-end.png + themes/qdarkstyle/rc/branch_closed-on.png + themes/qdarkstyle/rc/stylesheet-vline.png + themes/qdarkstyle/rc/branch_closed.png + themes/qdarkstyle/rc/branch_open-on.png + themes/qdarkstyle/rc/transparent.png + themes/qdarkstyle/rc/right_arrow_disabled.png + themes/qdarkstyle/rc/sizegrip.png + themes/qdarkstyle/rc/close.png + themes/qdarkstyle/rc/close-hover.png + themes/qdarkstyle/rc/close-pressed.png + themes/qdarkstyle/rc/down_arrow.png + themes/qdarkstyle/rc/Vmovetoolbar.png + themes/qdarkstyle/rc/left_arrow.png + themes/qdarkstyle/rc/stylesheet-branch-more.png + themes/qdarkstyle/rc/up_arrow.png + themes/qdarkstyle/rc/right_arrow.png + themes/qdarkstyle/rc/left_arrow_disabled.png + themes/qdarkstyle/rc/Hsepartoolbar.png + themes/qdarkstyle/rc/branch_open.png + themes/qdarkstyle/rc/Vsepartoolbar.png + themes/qdarkstyle/rc/down_arrow_disabled.png + themes/qdarkstyle/rc/undock.png + themes/qdarkstyle/rc/checkbox_checked_disabled.png + themes/qdarkstyle/rc/checkbox_checked_focus.png + themes/qdarkstyle/rc/checkbox_checked.png + themes/qdarkstyle/rc/checkbox_indeterminate.png + themes/qdarkstyle/rc/checkbox_indeterminate_focus.png + themes/qdarkstyle/rc/checkbox_unchecked_disabled.png + themes/qdarkstyle/rc/checkbox_unchecked_focus.png + themes/qdarkstyle/rc/checkbox_unchecked.png + themes/qdarkstyle/rc/radio_checked_disabled.png + themes/qdarkstyle/rc/radio_checked_focus.png + themes/qdarkstyle/rc/radio_checked.png + themes/qdarkstyle/rc/radio_unchecked_disabled.png + themes/qdarkstyle/rc/radio_unchecked_focus.png + themes/qdarkstyle/rc/radio_unchecked.png + + + + + themes/darkstyle/darkstyle.qss + themes/darkstyle/icon_close.png + themes/darkstyle/icon_restore.png + themes/darkstyle/icon_undock.png + themes/darkstyle/icon_branch_closed.png + themes/darkstyle/icon_branch_end.png + themes/darkstyle/icon_branch_more.png + themes/darkstyle/icon_branch_open.png + themes/darkstyle/icon_vline.png + themes/darkstyle/icon_checkbox_checked.png + themes/darkstyle/icon_checkbox_indeterminate.png + themes/darkstyle/icon_checkbox_unchecked.png + themes/darkstyle/icon_checkbox_checked_pressed.png + themes/darkstyle/icon_checkbox_indeterminate_pressed.png + themes/darkstyle/icon_checkbox_unchecked_pressed.png + themes/darkstyle/icon_checkbox_checked_disabled.png + themes/darkstyle/icon_checkbox_indeterminate_disabled.png + themes/darkstyle/icon_checkbox_unchecked_disabled.png + themes/darkstyle/icon_radiobutton_checked.png + themes/darkstyle/icon_radiobutton_unchecked.png + themes/darkstyle/icon_radiobutton_checked_pressed.png + themes/darkstyle/icon_radiobutton_unchecked_pressed.png + themes/darkstyle/icon_radiobutton_checked_disabled.png + themes/darkstyle/icon_radiobutton_unchecked_disabled.png + diff --git a/pv/application.cpp b/pv/application.cpp index 383b5c23..5a6e28a1 100644 --- a/pv/application.cpp +++ b/pv/application.cpp @@ -21,10 +21,35 @@ #include "config.h" #include +#include -using std::cerr; +#include + +#include + +#ifdef ENABLE_STACKTRACE +#include +#endif + +#ifdef ENABLE_DECODE +#include +#endif + +using std::cout; using std::endl; using std::exception; +using std::shared_ptr; + +#ifdef ENABLE_DECODE +static gint sort_pds(gconstpointer a, gconstpointer b) +{ + const struct srd_decoder *sda, *sdb; + + sda = (const struct srd_decoder *)a; + sdb = (const struct srd_decoder *)b; + return strcmp(sda->id, sdb->id); +} +#endif Application::Application(int &argc, char* argv[]) : QApplication(argc, argv) @@ -35,13 +60,186 @@ Application::Application(int &argc, char* argv[]) : setOrganizationDomain("sigrok.org"); } +void Application::collect_version_info(shared_ptr context) +{ + // Library versions and features + version_info_.emplace_back(applicationName(), applicationVersion()); + version_info_.emplace_back("Qt", qVersion()); + version_info_.emplace_back("glibmm", PV_GLIBMM_VERSION); + version_info_.emplace_back("Boost", BOOST_LIB_VERSION); + + version_info_.emplace_back("libsigrok", QString("%1/%2 (rt: %3/%4)") + .arg(SR_PACKAGE_VERSION_STRING, SR_LIB_VERSION_STRING, + sr_package_version_string_get(), sr_lib_version_string_get())); + + GSList *l_orig = sr_buildinfo_libs_get(); + for (GSList *l = l_orig; l; l = l->next) { + GSList *m = (GSList *)l->data; + const char *lib = (const char *)m->data; + const char *version = (const char *)m->next->data; + version_info_.emplace_back(QString(" - %1").arg(QString(lib)), QString(version)); + g_slist_free_full(m, g_free); + } + g_slist_free(l_orig); + + char *host = sr_buildinfo_host_get(); + version_info_.emplace_back(" - Host", QString(host)); + g_free(host); + + char *scpi_backends = sr_buildinfo_scpi_backends_get(); + version_info_.emplace_back(" - SCPI backends", QString(scpi_backends)); + g_free(scpi_backends); + +#ifdef ENABLE_DECODE + struct srd_decoder *dec; + + version_info_.emplace_back("libsigrokdecode", QString("%1/%2 (rt: %3/%4)") + .arg(SRD_PACKAGE_VERSION_STRING, SRD_LIB_VERSION_STRING, + srd_package_version_string_get(), srd_lib_version_string_get())); + + l_orig = srd_buildinfo_libs_get(); + for (GSList *l = l_orig; l; l = l->next) { + GSList *m = (GSList *)l->data; + const char *lib = (const char *)m->data; + const char *version = (const char *)m->next->data; + version_info_.emplace_back(QString(" - %1").arg(QString(lib)), QString(version)); + g_slist_free_full(m, g_free); + } + g_slist_free(l_orig); + + host = srd_buildinfo_host_get(); + version_info_.emplace_back(" - Host", QString(host)); + g_free(host); +#endif + + // Firmware paths + l_orig = sr_resourcepaths_get(SR_RESOURCE_FIRMWARE); + for (GSList *l = l_orig; l; l = l->next) + fw_path_list_.emplace_back((char*)l->data); + g_slist_free_full(l_orig, g_free); + + // PD paths +#ifdef ENABLE_DECODE + l_orig = srd_searchpaths_get(); + for (GSList *l = l_orig; l; l = l->next) + pd_path_list_.emplace_back((char*)l->data); + g_slist_free_full(l_orig, g_free); +#endif + + // Device drivers + for (auto& entry : context->drivers()) + driver_list_.emplace_back(QString::fromUtf8(entry.first.c_str()), + QString::fromUtf8(entry.second->long_name().c_str())); + + // Input formats + for (auto& entry : context->input_formats()) + input_format_list_.emplace_back(QString::fromUtf8(entry.first.c_str()), + QString::fromUtf8(entry.second->description().c_str())); + + // Output formats + for (auto& entry : context->output_formats()) + output_format_list_.emplace_back(QString::fromUtf8(entry.first.c_str()), + QString::fromUtf8(entry.second->description().c_str())); + + // Protocol decoders +#ifdef ENABLE_DECODE + GSList *sl = g_slist_copy((GSList *)srd_decoder_list()); + sl = g_slist_sort(sl, sort_pds); + for (const GSList *l = sl; l; l = l->next) { + dec = (struct srd_decoder *)l->data; + pd_list_.emplace_back(QString::fromUtf8(dec->id), + QString::fromUtf8(dec->longname)); + } + g_slist_free(sl); +#endif +} + +void Application::print_version_info() +{ + cout << PV_TITLE << " " << PV_VERSION_STRING << endl; + + cout << endl << "Libraries and features:" << endl; + for (pair& entry : version_info_) + cout << " " << entry.first.toStdString() << " " << entry.second.toStdString() << endl; + + cout << endl << "Firmware search paths:" << endl; + for (QString& entry : fw_path_list_) + cout << " " << entry.toStdString() << endl; + + cout << endl << "Protocol decoder search paths:" << endl; + for (QString& entry : pd_path_list_) + cout << " " << entry.toStdString() << endl; + + cout << endl << "Supported hardware drivers:" << endl; + for (pair& entry : driver_list_) + cout << " " << entry.first.leftJustified(21, ' ').toStdString() << + entry.second.toStdString() << endl; + + cout << endl << "Supported input formats:" << endl; + for (pair& entry : input_format_list_) + cout << " " << entry.first.leftJustified(21, ' ').toStdString() << + entry.second.toStdString() << endl; + + cout << endl << "Supported output formats:" << endl; + for (pair& entry : output_format_list_) + cout << " " << entry.first.leftJustified(21, ' ').toStdString() << + entry.second.toStdString() << endl; + +#ifdef ENABLE_DECODE + cout << endl << "Supported protocol decoders:" << endl; + for (pair& entry : pd_list_) + cout << " " << entry.first.leftJustified(21, ' ').toStdString() << + entry.second.toStdString() << endl; +#endif +} + +vector< pair > Application::get_version_info() const +{ + return version_info_; +} + +vector Application::get_fw_path_list() const +{ + return fw_path_list_; +} + +vector Application::get_pd_path_list() const +{ + return pd_path_list_; +} + +vector< pair > Application::get_driver_list() const +{ + return driver_list_; +} + +vector< pair > Application::get_input_format_list() const +{ + return input_format_list_; +} + +vector< pair > Application::get_output_format_list() const +{ + return output_format_list_; +} + +vector< pair > Application::get_pd_list() const +{ + return pd_list_; +} + bool Application::notify(QObject *receiver, QEvent *event) { try { return QApplication::notify(receiver, event); } catch (exception& e) { - cerr << "Caught exception: " << e.what() << endl; + qDebug().nospace() << "Caught exception of type " << \ + typeid(e).name() << " (" << e.what() << ")"; +#ifdef ENABLE_STACKTRACE + throw e; +#else exit(1); +#endif return false; } } diff --git a/pv/application.hpp b/pv/application.hpp index 7059c02a..c618f80d 100644 --- a/pv/application.hpp +++ b/pv/application.hpp @@ -20,14 +20,44 @@ #ifndef PULSEVIEW_PV_APPLICATION_HPP #define PULSEVIEW_PV_APPLICATION_HPP +#include + #include +#include + +using std::shared_ptr; +using std::pair; +using std::vector; + class Application : public QApplication { + Q_OBJECT + public: Application(int &argc, char* argv[]); + + void collect_version_info(shared_ptr context); + void print_version_info(); + + vector< pair > get_version_info() const; + vector get_fw_path_list() const; + vector get_pd_path_list() const; + vector< pair > get_driver_list() const; + vector< pair > get_input_format_list() const; + vector< pair > get_output_format_list() const; + vector< pair > get_pd_list() const; + private: bool notify(QObject *receiver, QEvent *event); + + vector< pair > version_info_; + vector fw_path_list_; + vector pd_path_list_; + vector< pair > driver_list_; + vector< pair > input_format_list_; + vector< pair > output_format_list_; + vector< pair > pd_list_; }; #endif // PULSEVIEW_PV_APPLICATION_HPP diff --git a/pv/binding/binding.cpp b/pv/binding/binding.cpp index ae4f2943..9735e146 100644 --- a/pv/binding/binding.cpp +++ b/pv/binding/binding.cpp @@ -20,7 +20,10 @@ #include #include +#include +#include #include +#include #include @@ -46,28 +49,56 @@ void Binding::commit() } } -void Binding::add_properties_to_form(QFormLayout *layout, - bool auto_commit) const +void Binding::add_properties_to_form(QFormLayout *layout, bool auto_commit) { assert(layout); + help_labels_.clear(); + for (shared_ptr p : properties_) { assert(p); - QWidget *const widget = p->get_widget(layout->parentWidget(), - auto_commit); + QWidget *widget; + QLabel *help_lbl = nullptr; + + if (p->desc().isEmpty()) { + widget = p->get_widget(layout->parentWidget(), auto_commit); + } else { + QPushButton *help_btn = new QPushButton(); + help_btn->setFlat(true); + help_btn->setIcon(QIcon(":/icons/help-browser.png")); + help_btn->setToolTip(p->desc()); + connect(help_btn, SIGNAL(clicked(bool)), + this, SLOT(on_help_clicked())); + + QHBoxLayout *layout = new QHBoxLayout(); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(p->get_widget(layout->parentWidget(), auto_commit)); + layout->addWidget(help_btn, 0, Qt::AlignRight); + + widget = new QWidget(); + widget->setLayout(layout); + + help_lbl = new QLabel(p->desc()); + help_lbl->setVisible(false); + help_lbl->setWordWrap(true); + help_labels_[help_btn] = help_lbl; + } + if (p->labeled_widget()) { layout->addRow(widget); } else { auto *lbl = new QLabel(p->name()); - lbl->setToolTip(p->desc()); + lbl->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); layout->addRow(lbl, widget); } + + if (help_lbl) + layout->addRow(help_lbl); } } -QWidget* Binding::get_property_form(QWidget *parent, - bool auto_commit) const +QWidget* Binding::get_property_form(QWidget *parent, bool auto_commit) { QWidget *const form = new QWidget(parent); QFormLayout *const layout = new QFormLayout(form); @@ -76,6 +107,14 @@ QWidget* Binding::get_property_form(QWidget *parent, return form; } +void Binding::update_property_widgets() +{ + for (shared_ptr p : properties_) { + assert(p); + p->update_widget(); + } +} + QString Binding::print_gvariant(Glib::VariantBase gvar) { QString s; @@ -84,13 +123,21 @@ QString Binding::print_gvariant(Glib::VariantBase gvar) s = QString::fromStdString("(null)"); else if (gvar.is_of_type(Glib::VariantType("s"))) s = QString::fromStdString( - Glib::VariantBase::cast_dynamic>( - gvar).get()); + Glib::VariantBase::cast_dynamic>(gvar).get()); else s = QString::fromStdString(gvar.print()); return s; } +void Binding::on_help_clicked() +{ + QPushButton *btn = qobject_cast(QObject::sender()); + assert(btn); + + QLabel *lbl = help_labels_.at(btn); + lbl->setVisible(!lbl->isVisible()); +} + } // namespace binding } // namespace pv diff --git a/pv/binding/binding.hpp b/pv/binding/binding.hpp index e672ff17..5580a0b0 100644 --- a/pv/binding/binding.hpp +++ b/pv/binding/binding.hpp @@ -26,15 +26,19 @@ G_GNUC_BEGIN_IGNORE_DEPRECATIONS #include G_GNUC_END_IGNORE_DEPRECATIONS +#include #include #include +#include #include +using std::map; using std::shared_ptr; using std::vector; class QFormLayout; +class QLabel; class QWidget; namespace pv { @@ -45,23 +49,29 @@ class Property; namespace binding { -class Binding +class Binding: public QObject { + Q_OBJECT + public: const vector< shared_ptr >& properties(); void commit(); - void add_properties_to_form(QFormLayout *layout, - bool auto_commit = false) const; + void add_properties_to_form(QFormLayout *layout, bool auto_commit = false); + + QWidget* get_property_form(QWidget *parent, bool auto_commit = false); - QWidget* get_property_form(QWidget *parent, - bool auto_commit = false) const; + void update_property_widgets(); static QString print_gvariant(Glib::VariantBase gvar); +protected Q_SLOTS: + void on_help_clicked(); + protected: vector< shared_ptr > properties_; + map help_labels_; }; } // namespace binding diff --git a/pv/binding/decoder.cpp b/pv/binding/decoder.cpp index f2e2e1d5..f51f9077 100644 --- a/pv/binding/decoder.cpp +++ b/pv/binding/decoder.cpp @@ -24,14 +24,13 @@ #include #include -#include +#include #include #include #include #include using boost::none; -using std::make_pair; using std::map; using std::pair; using std::shared_ptr; @@ -48,9 +47,9 @@ namespace pv { namespace binding { Decoder::Decoder( - shared_ptr decoder_stack, + shared_ptr decode_signal, shared_ptr decoder) : - decoder_stack_(decoder_stack), + decode_signal_(decode_signal), decoder_(decoder) { assert(decoder_); @@ -97,7 +96,7 @@ shared_ptr Decoder::bind_enum( vector< pair > values; for (GSList *l = option->values; l; l = l->next) { Glib::VariantBase var = Glib::VariantBase((GVariant*)l->data, true); - values.push_back(make_pair(var, print_gvariant(var))); + values.emplace_back(var, print_gvariant(var)); } return shared_ptr(new Enum(name, desc, values, getter, setter)); @@ -137,8 +136,8 @@ void Decoder::setter(const char *id, Glib::VariantBase value) assert(decoder_); decoder_->set_option(id, value.gobj()); - assert(decoder_stack_); - decoder_stack_->begin_decode(); + assert(decode_signal_); + decode_signal_->begin_decode(); } } // namespace binding diff --git a/pv/binding/decoder.hpp b/pv/binding/decoder.hpp index bf670403..a93a057f 100644 --- a/pv/binding/decoder.hpp +++ b/pv/binding/decoder.hpp @@ -31,7 +31,7 @@ struct srd_decoder_option; namespace pv { namespace data { -class DecoderStack; +class DecodeSignal; namespace decode { class Decoder; } @@ -42,7 +42,7 @@ namespace binding { class Decoder : public Binding { public: - Decoder(shared_ptr decoder_stack, + Decoder(shared_ptr decode_signal, shared_ptr decoder); private: @@ -55,7 +55,7 @@ private: void setter(const char *id, Glib::VariantBase value); private: - shared_ptr decoder_stack_; + shared_ptr decode_signal_; shared_ptr decoder_; }; diff --git a/pv/binding/device.cpp b/pv/binding/device.cpp index 20082db2..37c9d43d 100644 --- a/pv/binding/device.cpp +++ b/pv/binding/device.cpp @@ -32,7 +32,6 @@ using boost::optional; using std::function; -using std::make_pair; using std::pair; using std::set; using std::shared_ptr; @@ -69,7 +68,7 @@ Device::Device(shared_ptr configurable) : string name_str; try { name_str = key->description(); - } catch (Error e) { + } catch (Error& e) { name_str = key->name(); } @@ -98,6 +97,8 @@ Device::Device(shared_ptr configurable) : case SR_CONF_TRIGGER_SLOPE: case SR_CONF_COUPLING: case SR_CONF_CLOCK_EDGE: + case SR_CONF_DATA_SOURCE: + case SR_CONF_EXTERNAL_CLOCK_SOURCE: bind_enum(name, "", key, capabilities, get, set); break; @@ -105,6 +106,7 @@ Device::Device(shared_ptr configurable) : case SR_CONF_EXTERNAL_CLOCK: case SR_CONF_RLE: case SR_CONF_POWER_OFF: + case SR_CONF_AVERAGING: bind_bool(name, "", get, set); break; @@ -127,6 +129,13 @@ Device::Device(shared_ptr configurable) : bind_int(name, "", "", pair(1, 500), get, set); break; + case SR_CONF_AVG_SAMPLES: + if (capabilities.count(Capability::LIST)) + bind_enum(name, "", key, capabilities, get, set, print_averages); + else + bind_int(name, "", "", pair(0, INT32_MAX), get, set); + break; + default: break; } @@ -157,7 +166,7 @@ void Device::bind_enum(const QString &name, const QString &desc, vector< pair > values; while ((iter.next_value(gvar))) - values.push_back(make_pair(gvar, printer(gvar))); + values.emplace_back(gvar, printer(gvar)); properties_.push_back(shared_ptr(new Enum(name, desc, values, getter, setter))); @@ -206,5 +215,12 @@ QString Device::print_probe_factor(Glib::VariantBase gvar) return QString("%1x").arg(factor); } +QString Device::print_averages(Glib::VariantBase gvar) +{ + uint64_t avg; + avg = g_variant_get_uint64(gvar.gobj()); + return QString("%1").arg(avg); +} + } // namespace binding } // namespace pv diff --git a/pv/binding/device.hpp b/pv/binding/device.hpp index dcef9470..9f5daf57 100644 --- a/pv/binding/device.hpp +++ b/pv/binding/device.hpp @@ -20,6 +20,8 @@ #ifndef PULSEVIEW_PV_BINDING_DEVICE_HPP #define PULSEVIEW_PV_BINDING_DEVICE_HPP +#include + #include #include @@ -40,7 +42,7 @@ namespace pv { namespace binding { -class Device : public QObject, public Binding +class Device : public Binding { Q_OBJECT @@ -66,6 +68,7 @@ private: static QString print_vdiv(Glib::VariantBase gvar); static QString print_voltage_threshold(Glib::VariantBase gvar); static QString print_probe_factor(Glib::VariantBase gvar); + static QString print_averages(Glib::VariantBase gvar); protected: shared_ptr configurable_; diff --git a/pv/binding/inputoutput.cpp b/pv/binding/inputoutput.cpp index 9d2299af..f9a061c7 100644 --- a/pv/binding/inputoutput.cpp +++ b/pv/binding/inputoutput.cpp @@ -35,7 +35,6 @@ using boost::none; -using std::make_pair; using std::map; using std::pair; using std::shared_ptr; @@ -60,12 +59,13 @@ namespace binding { InputOutput::InputOutput( const map> &options) { - for (pair> o : options) { + for (const pair>& o : options) { const shared_ptr