Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,15 @@ option(ENABLE_STACKTRACE "Enable stack trace when crashing" FALSE)
option(ENABLE_DECODE "Build with libsigrokdecode" TRUE)
option(ENABLE_FLOW "Build with libsigrokflow" FALSE)
option(ENABLE_TESTS "Enable unit tests" FALSE)
option(STATIC_PKGDEPS_LIBS "Statically link to (pkg-config) libraries" FALSE)
if(WIN32)
set(PKGDEPS_STATIC_DEFAULT TRUE)
else()
set(PKGDEPS_STATIC_DEFAULT FALSE)
endif()
option(STATIC_PKGDEPS_LIBS "Statically link to (pkg-config) libraries" ${PKGDEPS_STATIC_DEFAULT})
option(ENABLE_TS_UPDATE "Update .ts source files (Qt l10n)" FALSE)

if(WIN32)
# On Windows/MinGW we need to statically link to libraries.
# This option is user configurable, but enable it by default on win32.
set(STATIC_PKGDEPS_LIBS TRUE)

# Windows does not support UNIX signals.
set(ENABLE_SIGNALS FALSE)
endif()
Expand Down Expand Up @@ -337,6 +338,7 @@ set(pulseview_SOURCES
pv/mainwindow.cpp
pv/metadata_obj.cpp
pv/session.cpp
pv/triggersettings.cpp
pv/storesession.cpp
pv/util.cpp
pv/binding/binding.cpp
Expand Down Expand Up @@ -515,7 +517,7 @@ endif()

if(WIN32)
# Use the sigrok icon for the pulseview.exe executable.
set(CMAKE_RC_COMPILE_OBJECT "${CMAKE_RC_COMPILER} -O coff -I${CMAKE_CURRENT_SOURCE_DIR} <SOURCE> <OBJECT>")
set(CMAKE_RC_COMPILE_OBJECT "\"${CMAKE_RC_COMPILER}\" -O coff -I\"${CMAKE_CURRENT_SOURCE_DIR}\" <SOURCE> <OBJECT>")
enable_language(RC)
list(APPEND pulseview_SOURCES pulseviewico.rc)
endif()
Expand Down Expand Up @@ -631,7 +633,12 @@ else()
list(APPEND PULSEVIEW_LINK_LIBS ${PKGDEPS_LIBRARIES})
endif()

if(WIN32)
if(Qt5_FOUND)
get_target_property(QT_CORE_LIBRARY_TYPE Qt5::Core TYPE)
else()
get_target_property(QT_CORE_LIBRARY_TYPE Qt6::Core TYPE)
endif()
if(WIN32 AND QT_CORE_LIBRARY_TYPE STREQUAL "STATIC_LIBRARY")
# 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 (only for
Expand Down
6 changes: 5 additions & 1 deletion main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,8 @@ int main(int argc, char *argv[])
qDebug() << "ERROR: invalid log level spec.";
break;
}
context->set_log_level(sigrok::LogLevel::get(loglevel));
// No Context exists yet; libsigrok's log level is global.
sr_log_loglevel_set(loglevel);

#ifdef ENABLE_DECODE
srd_log_loglevel_set(loglevel);
Expand Down Expand Up @@ -376,5 +377,8 @@ int main(int argc, char *argv[])

} while (false);

// Unregister while the global settings registry is still alive.
// The static logger destructor must not access a destroyed vector.
pv::logging.shutdown();
return ret;
}
61 changes: 58 additions & 3 deletions pv/binding/device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "device.hpp"

#include <pv/prop/bool.hpp>
#include <pv/prop/double.hpp>
#include <pv/prop/enum.hpp>
#include <pv/prop/int.hpp>

Expand All @@ -44,6 +45,7 @@ using sigrok::ConfigKey;
using sigrok::Error;

using pv::prop::Bool;
using pv::prop::Double;
using pv::prop::Enum;
using pv::prop::Int;
using pv::prop::Property;
Expand Down Expand Up @@ -76,7 +78,8 @@ Device::Device(shared_ptr<sigrok::Configurable> configurable) :
if ((key->id() == SR_CONF_CONTINUOUS) || (key->id() == SR_CONF_TRIGGER_MATCH) ||
(key->id() == SR_CONF_CONN) || (key->id() == SR_CONF_SERIALCOMM) || (key->id() == SR_CONF_NUM_LOGIC_CHANNELS) ||
(key->id() == SR_CONF_NUM_ANALOG_CHANNELS) || (key->id() == SR_CONF_SESSIONFILE) || (key->id() == SR_CONF_CAPTUREFILE) ||
(key->id() == SR_CONF_CAPTURE_UNITSIZE))
(key->id() == SR_CONF_CAPTURE_UNITSIZE) || (key->id() == SR_CONF_CAPTURE_STATUS) ||
(key->id() == SR_CONF_CAPTURE_ERROR))
continue;

qDebug() << QString(tr("Note for device developers: Ignoring device configuration capability '%1' " \
Expand All @@ -92,14 +95,42 @@ Device::Device(shared_ptr<sigrok::Configurable> configurable) :
};

switch (key->id()) {
case SR_CONF_ENABLED:
properties_.push_back(shared_ptr<Property>(new Bool(
descr, "", get, set, true)));
break;

case SR_CONF_OUTPUT_FREQUENCY:
bind_double_range(descr, " Hz", key, capabilities, get, set);
break;

case SR_CONF_DUTY_CYCLE:
bind_double_range(descr, " %", key, capabilities, get, set);
break;

case SR_CONF_SAMPLERATE:
// Sample rate values are not bound because they are shown
// in the MainBar
break;

case SR_CONF_CAPTURE_RATIO:
bind_int(descr, "", "%", pair<int64_t, int64_t>(0, 100), get, set);
case SR_CONF_CAPTURE_RATIO: {
pair<int64_t, int64_t> limits(0, 100);
if (capabilities.count(Capability::LIST)) {
try {
auto range = configurable_->config_list(key);
if (g_variant_is_of_type(range.gobj(), G_VARIANT_TYPE("(tt)"))) {
uint64_t minimum, maximum;
g_variant_get(range.gobj(), "(tt)", &minimum, &maximum);
if (minimum <= maximum && maximum <= 100)
limits = pair<int64_t, int64_t>(minimum, maximum);
}
} catch (const Error &e) {
qWarning() << "Cannot query capture ratio range" << e.what();
}
}
bind_int(descr, "", "%", limits, get, set);
break;
}

case SR_CONF_LIMIT_FRAMES:
// Value 0 means that there is no limit
Expand Down Expand Up @@ -204,6 +235,30 @@ void Device::bind_int(const QString &name, const QString &desc, QString suffix,
range, getter, setter, special_value_text)));
}

void Device::bind_double_range(const QString &name, const QString &suffix,
const ConfigKey *key, set<const Capability *> capabilities,
Property::Getter getter, Property::Setter setter)
{
if (!capabilities.count(Capability::LIST))
return;
try {
auto range = configurable_->config_list(key);
if (!g_variant_is_of_type(range.gobj(), G_VARIANT_TYPE("ad")) ||
g_variant_n_children(range.gobj()) != 3)
return;
double minimum, maximum, step;
g_variant_get_child(range.gobj(), 0, "d", &minimum);
g_variant_get_child(range.gobj(), 1, "d", &maximum);
g_variant_get_child(range.gobj(), 2, "d", &step);
if (minimum > maximum || step <= 0)
return;
properties_.push_back(std::make_shared<Double>(name, "", step >= 1 ? 0 : 6,
suffix, pair<double, double>(minimum, maximum), step, getter, setter));
} catch (const Error &e) {
qWarning() << "Cannot query generator range" << name << e.what();
}
}

QString Device::print_timebase(Glib::VariantBase gvar)
{
uint64_t p, q;
Expand Down
3 changes: 3 additions & 0 deletions pv/binding/device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ class Device : public Binding
private:
void bind_bool(const QString &name, const QString &desc,
prop::Property::Getter getter, prop::Property::Setter setter);
void bind_double_range(const QString &name, const QString &suffix,
const sigrok::ConfigKey *key, set<const sigrok::Capability *> capabilities,
prop::Property::Getter getter, prop::Property::Setter setter);
void bind_enum(const QString &name, const QString &desc,
const sigrok::ConfigKey *key,
set<const sigrok::Capability *> capabilities,
Expand Down
3 changes: 2 additions & 1 deletion pv/data/segment.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#define PULSEVIEW_PV_DATA_SEGMENT_HPP

#include "pv/util.hpp"
#include "segmentmutex.hpp"

#include <atomic>
#include <memory>
Expand Down Expand Up @@ -100,7 +101,7 @@ class Segment : public QObject
uint64_t get_iterator_valid_length(SegmentDataIterator* it);

uint32_t segment_id_;
mutable recursive_mutex mutex_;
mutable SegmentMutex mutex_;
deque<uint8_t*> data_chunks_;
uint8_t* current_chunk_;
uint64_t used_samples_, unused_samples_;
Expand Down
41 changes: 41 additions & 0 deletions pv/data/segmentmutex.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef PULSEVIEW_PV_DATA_SEGMENTMUTEX_HPP
#define PULSEVIEW_PV_DATA_SEGMENTMUTEX_HPP

#include <exception>
#include <mutex>
#include <type_traits>

namespace pv {
namespace data {

/* In MinGW's statically initialized libstdc++ mutex branch, the default
* destructor is trivial. Winpthreads lazily allocates an event on first lock,
* which still needs pthread_mutex_destroy. Restrict that extra destruction to
* this implementation; other platforms retain standard destruction.
* Segment lock guards release their locks before member destruction. */
class SegmentMutex final : public std::recursive_mutex {
public:
SegmentMutex() = default;
~SegmentMutex() noexcept
{
#if defined(_WIN32) && defined(__GLIBCXX__) && defined(__GTHREAD_RECURSIVE_MUTEX_INIT)
destroy_if_trivial(std::is_trivially_destructible<std::recursive_mutex>());
#endif
}

private:
#if defined(_WIN32) && defined(__GLIBCXX__) && defined(__GTHREAD_RECURSIVE_MUTEX_INIT)
void destroy_if_trivial(std::true_type) noexcept
{
if (__gthread_recursive_mutex_destroy(native_handle()) != 0)
std::terminate();
}
void destroy_if_trivial(std::false_type) noexcept {}
#endif
};

} // namespace data
} // namespace pv

#endif
9 changes: 9 additions & 0 deletions pv/logging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ static void *prev_srd_log_cb_data;

Logging::~Logging()
{
shutdown();
}

void Logging::shutdown()
{
if (!initialized_)
return;
qInstallMessageHandler(nullptr);
if (prev_sr_log_cb)
sr_log_callback_set(prev_sr_log_cb, prev_sr_log_cb_data);
Expand All @@ -64,6 +71,7 @@ Logging::~Logging()
#endif

GlobalSettings::remove_change_handler(this);
initialized_ = false;
}

void Logging::init()
Expand All @@ -84,6 +92,7 @@ void Logging::init()
#endif

GlobalSettings::add_change_handler(this);
initialized_ = true;
}

int Logging::get_log_level() const
Expand Down
2 changes: 2 additions & 0 deletions pv/logging.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class Logging : public QObject, public GlobalSettingsInterface
public:
~Logging();
void init();
void shutdown();

int get_log_level() const;
void set_log_level(int level);
Expand All @@ -73,6 +74,7 @@ class Logging : public QObject, public GlobalSettingsInterface
void logged_text(QString s);

private:
bool initialized_ = false;
int buffer_size_;
QStringList buffer_;
mutable mutex log_mutex_;
Expand Down
26 changes: 21 additions & 5 deletions pv/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,13 @@ MainWindow::~MainWindow()
sub_windows_.clear();
}

void MainWindow::show_session_error(const QString text, const QString info_text)
void MainWindow::show_session_error(const QString text, const QString info_text, QWidget *parent)
{
// TODO Emulate noquote()
qDebug() << "Notifying user of session error: " << text << "; " << info_text;

QMessageBox msg;
QMessageBox msg(parent ? parent : QApplication::activeWindow());
msg.setWindowTitle(text);
msg.setText(text + "\n\n" + info_text);
msg.setStandardButtons(QMessageBox::Ok);
msg.setIcon(QMessageBox::Warning);
Expand Down Expand Up @@ -596,6 +597,13 @@ void MainWindow::update_acq_button(Session *session)
run_stop_button_->setIcon(*icons[state]);
run_stop_button_->setText((state == pv::Session::Stopped) ?
run_caption : tr("Stop"));
const bool reconnect_needed = session && session->device_needs_reconnect();
const bool stopping = session && session->stop_requested();
run_stop_button_->setEnabled(!reconnect_needed && !stopping);
run_stop_shortcut_->setEnabled(!reconnect_needed && !stopping);
run_stop_button_->setToolTip(reconnect_needed ?
tr("Device disconnected. Reconnect USB, then use Connect to Device, Scan, and select the analyzer again.") :
(stopping ? tr("Stopping acquisition...") : tr("Start/Stop Acquisition")));
}

void MainWindow::save_ui_settings()
Expand Down Expand Up @@ -670,6 +678,8 @@ bool MainWindow::restoreState(const QByteArray &state, int version)

void MainWindow::on_run_stop_clicked()
{
if (!run_stop_button_->isEnabled())
return;
GlobalSettings settings;
bool all_sessions = settings.value(GlobalSettings::Key_General_StartAllSessions).toBool();

Expand All @@ -694,9 +704,10 @@ void MainWindow::on_run_stop_clicked()

for (shared_ptr<Session> s : hw_sessions)
if (any_running)
s->stop_capture();
s->request_stop_capture();
else
s->start_capture([&](QString message) {Q_EMIT session_error_raised("Capture failed", message);});
update_acq_button(last_focused_session_.get());
} else {

shared_ptr<Session> session = last_focused_session_;
Expand All @@ -710,7 +721,12 @@ void MainWindow::on_run_stop_clicked()
break;
case Session::AwaitingTrigger:
case Session::Running:
session->stop_capture();
try {
session->request_stop_capture();
} catch (sigrok::Error& error) {
Q_EMIT session_error_raised(tr("Capture failed"), error.what());
}
update_acq_button(session.get());
break;
}
}
Expand Down Expand Up @@ -985,7 +1001,7 @@ void MainWindow::on_close_current_tab()
}

void MainWindow::on_session_error_raised(const QString text, const QString info_text) {
MainWindow::show_session_error(text, info_text);
MainWindow::show_session_error(text, info_text, this);
}

} // namespace pv
2 changes: 1 addition & 1 deletion pv/mainwindow.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class MainWindow : public QMainWindow

~MainWindow();

static void show_session_error(const QString text, const QString info_text);
static void show_session_error(const QString text, const QString info_text, QWidget *parent = nullptr);

shared_ptr<views::ViewBase> get_active_view() const;

Expand Down
4 changes: 4 additions & 0 deletions pv/popups/deviceoptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ DeviceOptions::DeviceOptions(shared_ptr<Device> device, QWidget *parent) :
setLayout(&layout_);

layout_.addWidget(binding_.get_property_form(this, true));
connect(&binding_, &pv::binding::Device::config_changed, this, [this]() {
// A mode change can make an existing setting unavailable.
binding_.update_property_widgets();
});
}

pv::binding::Device& DeviceOptions::binding()
Expand Down
Loading