Compare commits

...

42 Commits

Author SHA1 Message Date
f9498a667f Touch Support for Ardour Fader 2024-10-02 00:17:07 +02:00
00c6aed6cf Touch Support for Ardour Button 2024-10-01 23:16:13 +02:00
bd89fb54c0 Note to grab 2024-10-01 23:16:13 +02:00
f10f5b35d1 Humble beginnings of implementing Touch API for YDK/Windows 2024-10-01 23:16:13 +02:00
5446eae928 Implement YDK Touch API for Linux/X11 2024-10-01 23:16:13 +02:00
0910fe1a6b Define YDK/YTKMM Touch Event API 2024-10-01 23:16:13 +02:00
adc9d9e0af
VST3: work around plugins that do not heed ContextInfo::kSendCount
see also c5618f01d6
2024-10-01 23:03:19 +02:00
John Emmas
07c79ce92c Small changes to make pthread_utils.cc buildable again with MSVC 2024-10-01 20:28:16 +01:00
44b2377e72
VST3: Fix possible deadlock when using PSL extension for sends
see also c5618f01d6
2024-10-01 19:24:36 +02:00
4fc4a2ca09
Pick a sane default for "insert time" 2024-10-01 05:48:49 +02:00
ca7ac7027b
VST3: add more debug messages for PSL extensions 2024-10-01 01:21:35 +02:00
22a2cb0624
Ignore inactive routes for pre-roll sub-cycles 2024-09-30 23:36:36 +02:00
1aad6805b3
Fix count-in/preroll recording offsets
Notably `Route::process_output_buffers` uses

```
  output_latency = speed * _output_latency;
```

here, speed already needs to be non-zero during count-in
and pre-roll.
2024-09-30 23:36:06 +02:00
dab22a7c70
Explicitly set Windows Process Scheduling Class 2024-09-30 19:57:53 +02:00
ed437afda7
Fix thread priorities for Windows builds 2024-09-30 19:07:53 +02:00
11f71a3297
Fix playback of sessions with low sample-rate
previously, _chunksize =
```
  minimum_disk_read_bytes / sizeof (Sample)
```

can become larger then actual allocated ringbuffer:

```
  audio_playback_buffer_seconds * sample-rate
```

In which case the buffer was never filled.
The disk writer has similar issues
2024-09-29 18:25:33 +02:00
d60e0e7ade
Fix sample-rate display rounding for 22.05kHz
Thanks to Colin Fletcher
2024-09-29 18:07:59 +02:00
c6ef4c3545
Fix interaction with snapshot list
* Select snapshot before showing relevant context menu
* Ignore clicks on empty space (no path)
2024-09-28 21:40:11 +02:00
46f61d7662
Fix and simplify Playlist::fade_range for multiple ranges 2024-09-28 21:16:55 +02:00
6b5582deef
Small code cleanup and simplification 2024-09-28 21:16:01 +02:00
28605b5351
Fix Audio region fade property mapping
This fixes an issue when undoing region fades.
Particularly but not limited to the following:

1. select a region and split it in the middle
2. switch to range tool. make a range selection across the split
3. Edit > Fade > Fade range seection  (or press `/`)
4. Undo

Previously the fade-out of the earlier region was not undone.
2024-09-28 21:15:57 +02:00
1d921dec0b
Debug thread sched_priority 2024-09-28 14:47:52 +02:00
2014faaeca
Fix BaseUI thread priority 2024-09-28 14:45:05 +02:00
dd4a1a6d73
Set thread priority relative to backend
This also removed direct calls to backend real_time_priority
for good measure.
2024-09-28 04:15:39 +02:00
72deb74c58
Unconditionally check for mlock
Prepare for `is_realtime` API removal. All backend
except for Dummy and sometimes JACK are realtime anyway.
2024-09-28 04:15:35 +02:00
395833e4f8
Always use PBD API for thread priorities 2024-09-28 03:58:48 +02:00
0b5a197f76
NO-OP: whitespace 2024-09-28 03:16:57 +02:00
d089f38481
Use config variable to set IOTask thread policy 2024-09-28 03:16:54 +02:00
dcd79f3135
Debug Backend thread creation 2024-09-28 03:16:50 +02:00
aeb4f925c6
PBD pthreads: allow unlimited stacksize 2024-09-28 03:16:45 +02:00
e8c67408bb
Impose stack limit on backend threads (freewheeling, MIDI poll) 2024-09-28 03:16:42 +02:00
e8445d13ec
Require thread name to be passed to PBD::Thread and store thread 2024-09-28 03:16:30 +02:00
88a24ae8e5
Let PBD::Thread set thread-name 2024-09-28 03:16:07 +02:00
538a8cbccc
Consolidate calls to `pthread_create' (2/2) 2024-09-28 03:16:02 +02:00
6a741689d1
Consolidate calls to `pthread_create' (1/2) 2024-09-28 03:15:48 +02:00
8d3ebde60e
Distinguish Threads and ThreadName debugging
This also allows for -DThread to enable both.

We celebrate the 128th debug bit and look forward to
the next 64!
2024-09-28 03:15:36 +02:00
c4fdd5356c
Enable debugging for stored threads 2024-09-28 03:14:16 +02:00
301777e7fe
Remove cruft 2024-09-27 18:03:11 +02:00
71a3161252
Error handling for 3751d20ce 2024-09-27 17:20:42 +02:00
3751d20ce9
Use dedicated thread to start export timespans #9798
This addresses an issue with creating a one-shot thread
directly from the realtime callback, which can be problematic
on Windows.
2024-09-27 06:00:15 +02:00
d8e4e7b259
Fix calculating intersection of waveform rectangle
see also 68eb63e0c8
2024-09-27 02:54:35 +02:00
9945d7721f
Allow to investigate I/O thread policy
see
https://discourse.ardour.org/t/ardour-8-7-unable-to-connect-to-audio-backend/110774/10
https://discourse.ardour.org/t/ardour-8-7-x-run-issues/110767/2
2024-09-27 01:41:40 +02:00
91 changed files with 1084 additions and 423 deletions

View File

@ -758,7 +758,7 @@ ARDOUR_UI::check_memory_locking ()
XMLNode* memory_warning_node = Config->instant_xml (X_("no-memory-warning"));
if (AudioEngine::instance()->is_realtime() && memory_warning_node == 0) {
if (memory_warning_node == 0) {
struct rlimit limits;
int64_t ram;

View File

@ -84,6 +84,10 @@ EditorSnapshots::button_press (GdkEventButton* ev)
int cx;
int cy;
_snapshot_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, col, cx, cy);
if (!path) {
return false;
}
_snapshot_display.get_selection()->select (path);
Gtk::TreeModel::iterator iter = _snapshot_model->get_iter (path);
if (iter) {
Gtk::TreeModel::Row row = *iter;
@ -97,6 +101,9 @@ EditorSnapshots::button_press (GdkEventButton* ev)
int cy;
string snap_name;
_snapshot_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, col, cx, cy);
if (!path) {
return false;
}
Gtk::TreeModel::iterator iter = _snapshot_model->get_iter (path);
if (iter) {
Gtk::TreeModel::Row row = *iter;

View File

@ -89,7 +89,7 @@ InsertRemoveTimeDialog::InsertRemoveTimeDialog (PublicEditor& e, bool remove)
_intersected_combo.append (_("stay in position"));
_intersected_combo.append (_("move"));
_intersected_combo.append (_("be split"));
_intersected_combo.set_active (0);
_intersected_combo.set_active (2);
table->attach (_intersected_combo, 1, 2, 2, 3);
}

View File

@ -26,7 +26,6 @@
#include <libgen.h>
#include <assert.h>
#include <pthread.h>
#include <signal.h>
#include <glib.h>
#include <glibmm/timer.h>
@ -42,7 +41,6 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/time.h>
struct ERect{
@ -319,9 +317,8 @@ any Xevents to all the UI callbacks plugins 'may' have registered on their
windows, that is if they don't manage their own UIs **/
static void*
gui_event_loop (void* ptr)
gui_event_loop (void*)
{
pthread_set_name ("LXVSTEventLoop");
VSTState* vstfx;
int LXVST_sched_timer_interval = 40; //ms, 25fps
XEvent event;
@ -509,20 +506,6 @@ int vstfx_init (void* ptr)
assert (gui_state == -1);
pthread_mutex_init (&plugin_mutex, NULL);
int thread_create_result;
pthread_attr_t thread_attributes;
/*Init the attribs to defaults*/
pthread_attr_init (&thread_attributes);
/*Make sure the thread is joinable - this should be the default anyway -
so we can join to it on vstfx_exit*/
pthread_attr_setdetachstate (&thread_attributes, PTHREAD_CREATE_JOINABLE);
/*This is where we need to open a connection to X, and start the GUI thread*/
/*Open our connection to X - all linuxVST plugin UIs handled by the LXVST engine
@ -546,7 +529,7 @@ int vstfx_init (void* ptr)
/*Create the thread - use default attrs for now, don't think we need anything special*/
thread_create_result = pthread_create (&LXVST_gui_event_thread, &thread_attributes, gui_event_loop, NULL);
int thread_create_result = pthread_create_and_store ("LXVSTEventLoop", &LXVST_gui_event_thread, gui_event_loop, NULL, 0);
if (thread_create_result != 0)
{

View File

@ -97,7 +97,6 @@ build_query_string (ArdourCurl::HttpGet const & h)
static void*
_pingback (void *arg)
{
pthread_set_name ("Pingback");
ArdourCurl::HttpGet h;
//initialize curl
@ -177,7 +176,7 @@ void pingback (const string& version, const string& announce_path)
ping_call* cm = new ping_call (version, announce_path);
pthread_t thread;
pthread_create_and_store ("pingback", &thread, _pingback, cm);
pthread_create_and_store ("Pingback", &thread, _pingback, cm);
}
}

View File

@ -1310,7 +1310,6 @@ RouteTimeAxisView::find_next_region_boundary (timepos_t const & pos, int32_t dir
void
RouteTimeAxisView::fade_range (TimeSelection& selection)
{
std::shared_ptr<Playlist> what_we_got;
std::shared_ptr<Track> tr = track ();
std::shared_ptr<Playlist> playlist;
@ -1328,11 +1327,7 @@ RouteTimeAxisView::fade_range (TimeSelection& selection)
playlist->fade_range (time);
vector<Command*> cmds;
playlist->rdiff (cmds);
_session->add_commands (cmds);
_session->add_command (new StatefulDiffCommand (playlist));
playlist->rdiff_and_add_command (_session);
}
void

View File

@ -132,7 +132,7 @@ StripSilenceDialog::StripSilenceDialog (Session* s, list<RegionView*> const & v)
/* Create a thread which runs while the dialogue is open to compute the silence regions */
Completed.connect (_completed_connection, invalidator(*this), boost::bind (&StripSilenceDialog::update, this), gui_context ());
_thread_should_finish = false;
pthread_create (&_thread, 0, StripSilenceDialog::_detection_thread_work, this);
pthread_create_and_store ("SilenceDetect", &_thread, StripSilenceDialog::_detection_thread_work, this, 0);
signal_response().connect(sigc::mem_fun (*this, &StripSilenceDialog::finished));
}
@ -248,7 +248,6 @@ void *
StripSilenceDialog::_detection_thread_work (void* arg)
{
StripSilenceDialog* d = reinterpret_cast<StripSilenceDialog*> (arg);
pthread_set_name ("SilenceDetect");
return d->detection_thread_work ();
}

View File

@ -770,7 +770,9 @@ string
ARDOUR_UI_UTILS::rate_as_string (float r)
{
char buf[32];
if (fmod (r, 1000.0f)) {
if (fmod (r, 100.0f)) {
snprintf (buf, sizeof (buf), "%.2f kHz", r / 1000.0);
} else if (fmod (r, 1000.0f)) {
snprintf (buf, sizeof (buf), "%.1f kHz", r / 1000.0);
} else {
snprintf (buf, sizeof (buf), "%.0f kHz", r / 1000.0);

View File

@ -274,6 +274,8 @@ VideoImageFrame::http_get (samplepos_t fn) {
queued_request=false;
req_video_frame_number=fn;
pthread_mutex_unlock(&queue_lock);
/* do not use `pbd pthread_create_and_store' here.
* The request thread uses aync cancellation */
int rv = pthread_create(&thread_id_tt, NULL, http_get_thread, this);
thread_active=true;
if (rv) {

View File

@ -164,7 +164,7 @@ public:
/** Return true if the backed is JACK */
virtual bool is_jack () const { return false; }
virtual int client_real_time_priority () { return PBD_RT_PRI_PROC; }
virtual int client_real_time_priority () { return 0; }
/* Discovering devices and parameters */

View File

@ -115,9 +115,6 @@ class LIBARDOUR_API AudioEngine : public PortManager, public SessionHandlePtr
void request_device_list_update();
void launch_device_control_app();
int client_real_time_priority ();
bool is_realtime() const;
// for the user which hold state_lock to check if reset operation is pending
bool is_reset_requested() const { return _hw_reset_request_count.load(); }

View File

@ -30,6 +30,7 @@
#include <boost/operators.hpp>
#include "pbd/gstdio_compat.h"
#include "pbd/pthread_utils.h"
#include "ardour/export_pointers.h"
#include "ardour/session.h"
@ -146,7 +147,13 @@ class LIBARDOUR_API ExportHandler : public ExportElementFactory, public sigc::tr
/* Timespan management */
static void* start_timespan_bg (void*);
void timespan_thread_wakeup ();
static void* _timespan_thread_run (void*);
PBD::Thread* _timespan_thread;
std::atomic<int> _timespan_thread_active;
pthread_mutex_t _timespan_mutex;
pthread_cond_t _timespan_cond;
int start_timespan ();
int process_timespan (samplecnt_t samples);

View File

@ -223,6 +223,7 @@ CONFIG_VARIABLE (bool, allow_special_bus_removal, "allow-special-bus-removal", f
CONFIG_VARIABLE (int32_t, processor_usage, "processor-usage", -1)
CONFIG_VARIABLE (int32_t, cpu_dma_latency, "cpu-dma-latency", -1) /* >=0 to enable */
CONFIG_VARIABLE (int32_t, io_thread_count, "io-thread-count", -2)
CONFIG_VARIABLE (int32_t, io_thread_policy, "io-thread-policy", 0)
CONFIG_VARIABLE (gain_t, max_gain, "max-gain", 2.0) /* +6.0dB */
CONFIG_VARIABLE (uint32_t, max_recent_sessions, "max-recent-sessions", 10)
CONFIG_VARIABLE (uint32_t, max_recent_templates, "max-recent-templates", 10)

View File

@ -553,7 +553,7 @@ public:
std::shared_ptr<AutomationControl> mapped_control (enum WellKnownCtrl, uint32_t band = 0) const;
std::shared_ptr<ReadOnlyControl> mapped_output (enum WellKnownData) const;
std::shared_ptr<AutomationControl> send_level_controllable (uint32_t n) const;
std::shared_ptr<AutomationControl> send_level_controllable (uint32_t n, bool locked = false) const;
std::shared_ptr<AutomationControl> send_enable_controllable (uint32_t n) const;
std::shared_ptr<AutomationControl> send_pan_azimuth_controllable (uint32_t n) const;
std::shared_ptr<AutomationControl> send_pan_azimuth_enable_controllable (uint32_t n) const;

View File

@ -811,7 +811,7 @@ public:
double engine_speed() const { return _engine_speed; }
double actual_speed() const;
double transport_speed() const;
double transport_speed (bool incl_preroll = false) const;
/** @return true if the transport state (TFSM) is stopped */
bool transport_stopped() const;
/** @return true if the transport state (TFSM) is stopped or stopping */

View File

@ -158,7 +158,7 @@ class LIBARDOUR_API Stripable : public SessionObject,
* In Ardour, these are user-created sends that connect to user-created
* Aux busses.
*/
virtual std::shared_ptr<AutomationControl> send_level_controllable (uint32_t n) const = 0;
virtual std::shared_ptr<AutomationControl> send_level_controllable (uint32_t n, bool locked = false) const = 0;
virtual std::shared_ptr<AutomationControl> send_enable_controllable (uint32_t n) const = 0;
virtual std::shared_ptr<AutomationControl> send_pan_azimuth_controllable (uint32_t n) const = 0;
virtual std::shared_ptr<AutomationControl> send_pan_azimuth_enable_controllable (uint32_t n) const = 0;

View File

@ -134,7 +134,7 @@ class LIBARDOUR_API VCA : public Stripable,
uint32_t eq_band_cnt () const { return 0; }
std::string eq_band_name (uint32_t) const { return std::string(); }
std::shared_ptr<AutomationControl> send_level_controllable (uint32_t n) const { return std::shared_ptr<AutomationControl>(); }
std::shared_ptr<AutomationControl> send_level_controllable (uint32_t n, bool locked = false) const { return std::shared_ptr<AutomationControl>(); }
std::shared_ptr<AutomationControl> send_enable_controllable (uint32_t n) const { return std::shared_ptr<AutomationControl>(); }
std::shared_ptr<AutomationControl> send_pan_azimuth_controllable (uint32_t n) const { return std::shared_ptr<AutomationControl>(); }
std::shared_ptr<AutomationControl> send_pan_azimuth_enable_controllable (uint32_t n) const { return std::shared_ptr<AutomationControl>(); }

View File

@ -359,6 +359,10 @@ private:
bool _no_kMono;
/* work around yabridge threading */
bool _restart_component_is_synced;
/* work around PSL calls during set_owner,
* while the route holds a processor lock
*/
std::atomic<bool> _in_set_owner;
};
} // namespace Steinberg

View File

@ -678,7 +678,6 @@ void
AudioEngine::do_reset_backend()
{
SessionEvent::create_per_thread_pool (X_("Backend reset processing thread"), 1024);
pthread_set_name ("EngineWatchdog");
Glib::Threads::Mutex::Lock guard (_reset_request_lock);
@ -739,7 +738,6 @@ void
AudioEngine::do_devicelist_update()
{
SessionEvent::create_per_thread_pool (X_("Device list update processing thread"), 512);
pthread_set_name ("DeviceList");
Glib::Threads::Mutex::Lock guard (_devicelist_update_lock);
@ -769,13 +767,13 @@ AudioEngine::start_hw_event_processing()
if (_hw_reset_event_thread == 0) {
_hw_reset_request_count.store (0);
_stop_hw_reset_processing.store (0);
_hw_reset_event_thread = PBD::Thread::create (boost::bind (&AudioEngine::do_reset_backend, this));
_hw_reset_event_thread = PBD::Thread::create (boost::bind (&AudioEngine::do_reset_backend, this), "EngineWatchdog");
}
if (_hw_devicelist_update_thread == 0) {
_hw_devicelist_update_count.store (0);
_stop_hw_devicelist_processing.store (0);
_hw_devicelist_update_thread = PBD::Thread::create (boost::bind (&AudioEngine::do_devicelist_update, this));
_hw_devicelist_update_thread = PBD::Thread::create (boost::bind (&AudioEngine::do_devicelist_update, this), "DeviceList");
}
}
@ -1069,6 +1067,12 @@ AudioEngine::start (bool for_latency)
return -1;
}
if (_backend->is_realtime ()) {
pbd_set_engine_rt_priority (_backend->client_real_time_priority ());
} else {
pbd_set_engine_rt_priority (0);
}
_running = true;
if (_session) {
@ -1192,38 +1196,6 @@ AudioEngine::get_dsp_load() const
return _backend->dsp_load ();
}
bool
AudioEngine::is_realtime() const
{
if (!_backend) {
return false;
}
return _backend->is_realtime();
}
int
AudioEngine::client_real_time_priority ()
{
if (!_backend) {
assert (0);
return PBD_RT_PRI_PROC;
}
if (!_backend->is_realtime ()) {
/* this is only an issue with the Dummy backend.
* - with JACK, we require rt permissions.
* - with ALSA/PulseAudio this can only happen if rt permissions
* are n/a. Other attempts to get rt will fail likewise.
*
* perhaps:
* TODO: use is_realtime () ? PBD_SCHED_FIFO : PBD_SCHED_OTHER
*/
return PBD_RT_PRI_PROC; // XXX
}
return _backend->client_real_time_priority();
}
void
AudioEngine::transport_start ()
{

View File

@ -234,9 +234,9 @@ AudioRegion::register_properties ()
, _fade_before_fx (Properties::fade_before_fx, other->_fade_before_fx) \
, _scale_amplitude (Properties::scale_amplitude, other->_scale_amplitude) \
, _fade_in (Properties::fade_in, std::shared_ptr<AutomationList> (new AutomationList (*other->_fade_in.val()))) \
, _inverse_fade_in (Properties::fade_in, std::shared_ptr<AutomationList> (new AutomationList (*other->_inverse_fade_in.val()))) \
, _fade_out (Properties::fade_in, std::shared_ptr<AutomationList> (new AutomationList (*other->_fade_out.val()))) \
, _inverse_fade_out (Properties::fade_in, std::shared_ptr<AutomationList> (new AutomationList (*other->_inverse_fade_out.val())))
, _inverse_fade_in (Properties::inverse_fade_in, std::shared_ptr<AutomationList> (new AutomationList (*other->_inverse_fade_in.val()))) \
, _fade_out (Properties::fade_out, std::shared_ptr<AutomationList> (new AutomationList (*other->_fade_out.val()))) \
, _inverse_fade_out (Properties::inverse_fade_out, std::shared_ptr<AutomationList> (new AutomationList (*other->_inverse_fade_out.val())))
/* a Session will reset these to its chosen defaults by calling AudioRegion::set_default_fade() */
void

View File

@ -423,10 +423,6 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, samplecnt_t npeaks, samplepos
* least large enough for all the data in the audio file. if it
* is too short, assume that a crash or other error truncated
* it, and rebuild it from scratch.
*
* XXX this may not work for destructive recording, but we
* might decided to get rid of that anyway.
*
*/
const off_t expected_file_size = (_length.samples() / (double) samples_per_file_peak) * sizeof (PeakData);
@ -591,10 +587,6 @@ AudioSource::read_peaks_with_fpp (PeakData *peaks, samplecnt_t npeaks, samplepos
samplecnt_t nvisual_peaks = 0;
uint32_t i = 0;
/* handle the case where the initial visual peak is on a pixel boundary */
//current_stored_peak = min (current_stored_peak, stored_peak_before_next_visual_peak);
/* open ... close during out: handling */
off_t map_off = (uint32_t) (current_stored_peak) * sizeof(PeakData);

View File

@ -201,8 +201,7 @@ AutomationWatch::timer ()
void
AutomationWatch::thread ()
{
pbd_set_thread_priority (pthread_self(), PBD_SCHED_FIFO, AudioEngine::instance()->client_real_time_priority() - 2); // XXX
pthread_set_name ("AutomationWatch");
pbd_set_thread_priority (pthread_self(), PBD_SCHED_FIFO, PBD_RT_PRI_CTRL);
while (_run_thread) {
Glib::usleep ((gulong) floor (Config->get_automation_interval_msecs() * 1000)); // TODO use pthread_cond_timedwait on _run_thread
timer ();
@ -224,7 +223,7 @@ AutomationWatch::set_session (Session* s)
if (_session) {
_run_thread = true;
_thread = PBD::Thread::create (boost::bind (&AutomationWatch::thread, this));
_thread = PBD::Thread::create (boost::bind (&AutomationWatch::thread, this), "AutomationWatch");
_session->TransportStateChange.connect_same_thread (transport_connection, boost::bind (&AutomationWatch::transport_state_change, this));
}

View File

@ -141,7 +141,7 @@ Butler::start_thread ()
should_run = false;
if (pthread_create_and_store ("disk butler", &thread, _thread_work, this)) {
if (pthread_create_and_store ("butler", &thread, _thread_work, this)) {
error << _("Session: could not create butler thread") << endmsg;
return -1;
}
@ -171,7 +171,6 @@ void*
Butler::_thread_work (void* arg)
{
SessionEvent::create_per_thread_pool ("butler events", 4096);
pthread_set_name (X_("butler"));
/* get thread buffers for RegionFx */
ARDOUR::ProcessThread* pt = new ProcessThread ();
pt->get_buffers ();

View File

@ -185,7 +185,7 @@ ControlProtocolManager::set_session (Session* s)
usb_hotplug_cb, this,
&_hpcp)) {
_hotplug_thread_run = true;
if (pthread_create (&_hotplug_thread, NULL, usb_hotplug_thread, this)) {
if (pthread_create_and_store ("Ctrl USB Hotplug", &_hotplug_thread, usb_hotplug_thread, this, 0)) {
_hotplug_thread_run = false;
}
}

View File

@ -169,7 +169,7 @@ Convolution::restart ()
}
if (rv == 0) {
rv = _convproc.start_process (pbd_absolute_rt_priority (PBD_SCHED_FIFO, AudioEngine::instance ()->client_real_time_priority () - 1), PBD_SCHED_FIFO);
rv = _convproc.start_process (pbd_absolute_rt_priority (PBD_SCHED_FIFO, PBD_RT_PRI_PROC), PBD_SCHED_FIFO);
}
assert (rv == 0); // bail out in debug builds

View File

@ -113,8 +113,9 @@ DiskReader::ReaderChannelInfo::resize_preloop (samplecnt_t bufsize)
int
DiskReader::add_channel_to (std::shared_ptr<ChannelList> c, uint32_t how_many)
{
samplecnt_t bufsz = std::max<samplecnt_t> (_chunk_samples * 2, _session.butler ()->audio_playback_buffer_size ());
while (how_many--) {
c->push_back (new ReaderChannelInfo (_session.butler ()->audio_playback_buffer_size (), loop_fade_length));
c->push_back (new ReaderChannelInfo (bufsz, loop_fade_length));
DEBUG_TRACE (DEBUG::DiskIO, string_compose ("'%1': new reader channel, write space = %2 read = %3\n",
name (),
c->back ()->rbuf->write_space (),
@ -236,8 +237,10 @@ DiskReader::adjust_buffering ()
{
std::shared_ptr<ChannelList const> c = channels.reader ();
samplecnt_t bufsz = std::max<samplecnt_t> (_chunk_samples * 2, _session.butler ()->audio_playback_buffer_size ());
for (auto const& chan : *c) {
chan->resize (_session.butler ()->audio_playback_buffer_size ());
chan->resize (bufsz);
}
}

View File

@ -109,8 +109,9 @@ DiskWriter::WriterChannelInfo::resize (samplecnt_t bufsize)
int
DiskWriter::add_channel_to (std::shared_ptr<ChannelList> c, uint32_t how_many)
{
samplecnt_t bufsz = std::max<samplecnt_t> (_chunk_samples * 2, _session.butler()->audio_capture_buffer_size());
while (how_many--) {
c->push_back (new WriterChannelInfo (_session.butler()->audio_capture_buffer_size()));
c->push_back (new WriterChannelInfo (bufsz));
DEBUG_TRACE (DEBUG::DiskIO, string_compose ("%1: new writer channel, write space = %2 read = %3\n",
name(),
c->back()->wbuf->write_space(),
@ -1386,8 +1387,9 @@ DiskWriter::adjust_buffering ()
{
std::shared_ptr<ChannelList const> c = channels.reader();
samplecnt_t bufsz = std::max<samplecnt_t> (_chunk_samples * 2, _session.butler()->audio_capture_buffer_size());
for (auto const chan : *c) {
chan->resize (_session.butler()->audio_capture_buffer_size());
chan->resize (bufsz);
}
}

View File

@ -121,6 +121,15 @@ ExportHandler::ExportHandler (Session & session)
, cue_tracknum (0)
, cue_indexnum (0)
{
pthread_mutex_init (&_timespan_mutex, 0);
pthread_cond_init (&_timespan_cond, 0);
_timespan_thread_active.store (1);
_timespan_thread = PBD::Thread::create (boost::bind (_timespan_thread_run, this), "ExportHandler");
if (!_timespan_thread) {
_timespan_thread_active.store (0);
fatal << "Cannot create export handler helper thread" << endmsg;
abort(); /* NOTREACHED*/
}
}
ExportHandler::~ExportHandler ()
@ -130,6 +139,51 @@ ExportHandler::~ExportHandler ()
session.surround_master ()->surround_return ()->finalize_export ();
}
graph_builder->cleanup (export_status->aborted () );
pthread_mutex_lock (&_timespan_mutex);
_timespan_thread_active.store (0);
pthread_cond_signal (&_timespan_cond);
pthread_mutex_unlock (&_timespan_mutex);
_timespan_thread->join ();
pthread_cond_destroy (&_timespan_cond);
pthread_mutex_destroy (&_timespan_mutex);
}
void*
ExportHandler::_timespan_thread_run (void* me)
{
ExportHandler* self = static_cast<ExportHandler*> (me);
SessionEvent::create_per_thread_pool ("ExportHandler", 512);
PBD::notify_event_loops_about_thread_creation (pthread_self(), "ExportHandler", 512);
pthread_mutex_lock (&self->_timespan_mutex);
while (self->_timespan_thread_active.load ()) {
pthread_cond_wait (&self->_timespan_cond, &self->_timespan_mutex);
if (!self->_timespan_thread_active.load ()) {
break;
} else {
Temporal::TempoMap::fetch ();
self->process_connection.disconnect ();
Glib::Threads::Mutex::Lock l (self->export_status->lock());
DiskReader::allocate_working_buffers ();
self->start_timespan ();
DiskReader::free_working_buffers ();
}
}
pthread_mutex_unlock (&self->_timespan_mutex);
return 0;
}
void
ExportHandler::timespan_thread_wakeup ()
{
if (pthread_mutex_trylock (&_timespan_mutex) == 0) {
pthread_cond_signal (&_timespan_cond);
pthread_mutex_unlock (&_timespan_mutex);
}
}
/** Add an export to the `to-do' list */
@ -372,22 +426,6 @@ ExportHandler::command_output(std::string output, size_t size)
info << output << endmsg;
}
void*
ExportHandler::start_timespan_bg (void* eh)
{
char name[64];
snprintf (name, 64, "Export-TS-%p", (void*)DEBUG_THREAD_SELF);
pthread_set_name (name);
ExportHandler* self = static_cast<ExportHandler*> (eh);
self->process_connection.disconnect ();
Glib::Threads::Mutex::Lock l (self->export_status->lock());
SessionEvent::create_per_thread_pool (name, 512);
DiskReader::allocate_working_buffers ();
self->start_timespan ();
DiskReader::free_working_buffers ();
return 0;
}
void
ExportHandler::finish_timespan ()
{
@ -543,9 +581,7 @@ ExportHandler::finish_timespan ()
/* finish timespan is called in freewheeling rt-context,
* we cannot start a new export from here */
assert (AudioEngine::instance()->freewheeling ());
pthread_t tid;
pthread_create (&tid, NULL, ExportHandler::start_timespan_bg, this);
pthread_detach (tid);
timespan_thread_wakeup ();
}
void

View File

@ -815,7 +815,8 @@ ARDOUR::init_post_engine (uint32_t start_cnt)
}
}
BaseUI::set_thread_priority (pbd_absolute_rt_priority (PBD_SCHED_FIFO, AudioEngine::instance()->client_real_time_priority () - 2));
/* set/update thread priority relative to backend's [jack_]client_real_time_priority */
BaseUI::set_thread_priority (PBD_RT_PRI_CTRL);
TransportMasterManager::instance ().restart ();
}

View File

@ -32,6 +32,7 @@
#include "ardour/disk_reader.h"
#include "ardour/io_tasklist.h"
#include "ardour/process_thread.h"
#include "ardour/rc_configuration.h"
#include "ardour/session_event.h"
#include "pbd/i18n.h"
@ -50,35 +51,41 @@ IOTaskList::IOTaskList (uint32_t n_threads)
return;
}
pthread_attr_t attr;
struct sched_param parm;
parm.sched_priority = pbd_absolute_rt_priority (SCHED_RR, pbd_pthread_priority (THREAD_IO));
bool use_rt;
int policy;
switch (Config->get_io_thread_policy ()) {
case 1:
use_rt = true;
policy = SCHED_FIFO;
break;
case 2:
use_rt = true;
policy = SCHED_RR;
default:
use_rt = false;
policy = SCHED_OTHER;
break;
}
pthread_attr_init (&attr);
#ifdef PLATFORM_WINDOWS
pthread_attr_setschedpolicy (&attr, SCHED_OTHER);
#else
pthread_attr_setschedpolicy (&attr, SCHED_RR);
policy = SCHED_OTHER;
#endif
pthread_attr_setschedparam (&attr, &parm);
pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
pthread_attr_setinheritsched (&attr, PTHREAD_EXPLICIT_SCHED);
DEBUG_TRACE (PBD::DEBUG::IOTaskList, string_compose ("IOTaskList starting %1 threads with priority = %2\n", _n_threads, parm.sched_priority));
DEBUG_TRACE (PBD::DEBUG::IOTaskList, string_compose ("IOTaskList starting %1 threads with sched policy = %2\n", _n_threads, policy));
_workers.resize (_n_threads);
for (uint32_t i = 0; i < _n_threads; ++i) {
if (pthread_create (&_workers[i], &attr, &_worker_thread, this)) {
if (pthread_create (&_workers[i], NULL, &_worker_thread, this)) {
if (!use_rt || pbd_realtime_pthread_create (policy, THREAD_IO, 0, &_workers[i], &_worker_thread, this)) {
if (use_rt && i == 0) {
PBD::warning << _("IOTaskList: cannot acquire realtime permissions.") << endmsg;
}
if (pbd_pthread_create (0, &_workers[i], &_worker_thread, this)) {
std::cerr << "Failed to start IOTaskList thread\n";
throw failed_constructor ();
}
if (i == 0) {
PBD::warning << _("IOTaskList: cannot acquire realtime permissions.") << endmsg;
}
}
}
pthread_attr_destroy (&attr);
}
IOTaskList::~IOTaskList ()
@ -139,8 +146,8 @@ IOTaskList::_worker_thread (void* me)
#ifdef HAVE_IOPRIO
/* compare to Butler::_thread_work */
// ioprio_set (IOPRIO_WHO_PROCESS, 0 /*calling thread*/, IOPRIO_PRIO_VALUE (IOPRIO_CLASS_RT, 4))
syscall (SYS_ioprio_set, 1, 0, (1 << 13) | 4);
// ioprio_set (IOPRIO_WHO_PROCESS, 0 /*calling thread*/, IOPRIO_PRIO_VALUE (IOPRIO_CLASS_RT, 4))
syscall (SYS_ioprio_set, 1, 0, (1 << 13) | 4);
#endif
self->io_thread ();

View File

@ -607,7 +607,7 @@ LV2Plugin::init(const void* c_plugin, samplecnt_t rate)
static const int32_t _min_block_length = 1; // may happen during split-cycles
static const int32_t _max_block_length = 8192; // max possible (with all engines and during export)
static const int32_t rt_policy = PBD_SCHED_FIFO;
static const int32_t rt_priority = pbd_absolute_rt_priority (PBD_SCHED_FIFO, AudioEngine::instance()->client_real_time_priority () - 1);
static const int32_t rt_priority = pbd_absolute_rt_priority (PBD_SCHED_FIFO, PBD_RT_PRI_PROC);
static const int32_t hw_concurrency = how_many_dsp_threads ();
/* Consider updating max-block-size whenever the buffersize changes.
* It requires re-instantiating the plugin (which is a non-realtime operation),

View File

@ -275,7 +275,6 @@ MidiPatchManager::load_midnams ()
*/
PBD::notify_event_loops_about_thread_creation (pthread_self(), "midi-patch-manager", 8);
pthread_set_name ("MIDNAMLoader");
{
PBD::Unwinder<bool> npc (no_patch_changed_messages, true);
@ -291,7 +290,7 @@ MidiPatchManager::load_midnams ()
void
MidiPatchManager::load_midnams_in_thread ()
{
_midnam_load_thread = PBD::Thread::create (boost::bind (&MidiPatchManager::load_midnams, this));
_midnam_load_thread = PBD::Thread::create (boost::bind (&MidiPatchManager::load_midnams, this), "MIDNAMLoader");
}
void

View File

@ -3413,17 +3413,14 @@ Playlist::fade_range (list<TimelineRange>& ranges)
{
ThawList thawlist;
RegionReadLock rlock (this);
for (list<TimelineRange>::iterator r = ranges.begin(); r != ranges.end(); ) {
list<TimelineRange>::iterator tmpr = r;
++tmpr;
for (RegionList::const_iterator i = regions.begin (); i != regions.end ();) {
RegionList::const_iterator tmpi = i;
++tmpi;
thawlist.add (*i);
(*i)->fade_range ((*r).start().samples(), (*r).end().samples());
i = tmpi;
/* add regions only once, not for each range */
for (auto const& r : regions) {
thawlist.add (r);
}
for (auto const& t: ranges) {
for (auto const& r : regions) {
r->fade_range (t.start().samples(), t.end().samples());
}
r = tmpr;
}
rlock.release ();
thawlist.release ();

View File

@ -410,7 +410,7 @@ Route::process_output_buffers (BufferSet& bufs,
_pannable->automation_run (start_sample, nframes);
}
const int speed = (is_auditioner() ? 1 : _session.transport_speed ());
const int speed = (is_auditioner() ? 1 : _session.transport_speed (true));
assert (speed == -1 || speed == 0 || speed == 1);
const samplecnt_t output_latency = speed * _output_latency;
@ -4050,7 +4050,7 @@ Route::latency_preroll (pframes_t nframes, samplepos_t& start_sample, samplepos_
return nframes;
}
if (!_disk_reader) {
if (_session.transport_speed() < 0) {
if (_session.transport_speed (true) < 0) {
start_sample += latency_preroll;
end_sample += latency_preroll;
} else {
@ -4060,12 +4060,12 @@ Route::latency_preroll (pframes_t nframes, samplepos_t& start_sample, samplepos_
return nframes;
}
if (latency_preroll > playback_latency ()) {
if (latency_preroll >= playback_latency ()) {
no_roll_unlocked (nframes, start_sample - latency_preroll, end_sample - latency_preroll, false);
return 0;
}
if (_session.transport_speed() < 0) {
if (_session.transport_speed (true) < 0) {
start_sample += latency_preroll;
end_sample += latency_preroll;
} else {
@ -5927,8 +5927,15 @@ Route::send_pan_azimuth_controllable (uint32_t n) const
}
std::shared_ptr<AutomationControl>
Route::send_level_controllable (uint32_t n) const
Route::send_level_controllable (uint32_t n, bool locked) const
{
if (locked) {
/* calling thread has a WriterLock (_processor_lock)
* we cannot call nth_send()
*/
return std::shared_ptr<AutomationControl>();
}
std::shared_ptr<Send> s = std::dynamic_pointer_cast<Send>(nth_send (n));
if (s) {
return s->gain_control ();

View File

@ -7845,8 +7845,10 @@ Session::auto_connect_thread_start ()
lx.release ();
_ac_thread_active.store (1);
if (pthread_create (&_auto_connect_thread, NULL, auto_connect_thread, this)) {
if (pthread_create_and_store ("AutoConnect", &_auto_connect_thread, auto_connect_thread, this, 0)) {
_ac_thread_active.store (0);
fatal << "Cannot create 'session auto connect' thread" << endmsg;
abort(); /* NOTREACHED*/
}
}
@ -7881,9 +7883,7 @@ void *
Session::auto_connect_thread (void *arg)
{
Session *s = static_cast<Session *>(arg);
pthread_set_name (X_("autoconnect"));
s->auto_connect_thread_run ();
pthread_exit (0);
return 0;
}

View File

@ -348,6 +348,9 @@ Session::calc_preroll_subcycle (samplecnt_t ns) const
{
std::shared_ptr<RouteList const> r = routes.reader ();
for (auto const& i : *r) {
if (!i->active ()) {
continue;
}
samplecnt_t route_offset = i->playback_latency ();
if (_remaining_latency_preroll > route_offset + ns) {
/* route will no-roll for complete pre-roll cycle */
@ -1199,7 +1202,7 @@ Session::emit_thread_start ()
}
_rt_thread_active = true;
if (pthread_create (&_rt_emit_thread, NULL, emit_thread, this)) {
if (pthread_create_and_store ("SessionSignals", &_rt_emit_thread, emit_thread, this, 0)) {
_rt_thread_active = false;
}
}
@ -1225,9 +1228,7 @@ void *
Session::emit_thread (void *arg)
{
Session *s = static_cast<Session *>(arg);
pthread_set_name ("SessionSignals");
s->emit_thread_run ();
pthread_exit (0);
return 0;
}

View File

@ -2142,11 +2142,14 @@ Session::transport_will_roll_forwards () const
}
double
Session::transport_speed() const
Session::transport_speed (bool incl_preroll) const
{
if (_transport_fsm->transport_speed() != _transport_fsm->transport_speed()) {
// cerr << "\n\n!!TS " << _transport_fsm->transport_speed() << " TFSM::speed " << _transport_fsm->transport_speed() << " via " << _transport_fsm->current_state() << endl;
}
if (incl_preroll) {
return _transport_fsm->transport_speed();
}
return _count_in_samples > 0 ? 0. : _transport_fsm->transport_speed();
}

View File

@ -65,8 +65,6 @@ static void
peak_thread_work ()
{
SessionEvent::create_per_thread_pool (X_("PeakFile Builder "), 64);
pthread_set_name ("PeakFileBuilder");
while (true) {
SourceFactory::peak_building_lock.lock ();
@ -119,7 +117,7 @@ SourceFactory::init ()
}
peak_thread_run = true;
for (int n = 0; n < 2; ++n) {
peak_thread_pool.push_back (PBD::Thread::create (&peak_thread_work));
peak_thread_pool.push_back (PBD::Thread::create (&peak_thread_work, string_compose ("PeakFileBuilder-%1", n)));
}
}

View File

@ -4879,7 +4879,7 @@ TriggerBoxThread::TriggerBoxThread ()
: requests (1024)
, _xthread (true)
{
if (pthread_create_and_store ("triggerbox thread", &thread, _thread_work, this)) {
if (pthread_create_and_store ("TriggerBox Worker", &thread, _thread_work, this)) {
error << _("Session: could not create triggerbox thread") << endmsg;
throw failed_constructor ();
}
@ -4897,15 +4897,12 @@ void *
TriggerBoxThread::_thread_work (void* arg)
{
SessionEvent::create_per_thread_pool ("tbthread events", 4096);
pthread_set_name (X_("tbthread"));
return ((TriggerBoxThread *) arg)->thread_work ();
}
void *
TriggerBoxThread::thread_work ()
{
pthread_set_name (X_("Trigger Worker"));
while (true) {
char msg;

View File

@ -1195,6 +1195,7 @@ VST3PI::VST3PI (std::shared_ptr<ARDOUR::VST3PluginModule> m, std::string unique_
, _rpc_queue (RouteProcessorChange::NoProcessorChange, false)
, _no_kMono (false)
, _restart_component_is_synced (false)
, _in_set_owner (false)
{
using namespace std;
IPluginFactory* factory = m->factory ();
@ -1835,9 +1836,13 @@ VST3PI::set_owner (SessionObject* o)
return;
}
_in_set_owner.store (true);
if (!setup_psl_info_handler ()) {
setup_info_listener ();
}
_in_set_owner.store (false);
}
void
@ -2809,7 +2814,7 @@ VST3PI::automation_state_changed (uint32_t port, AutoState s, std::weak_ptr<Auto
/* ****************************************************************************/
static std::shared_ptr<AutomationControl>
lookup_ac (SessionObject* o, FIDString id)
lookup_ac (SessionObject* o, FIDString id, bool locked = false)
{
Stripable* s = dynamic_cast<Stripable*> (o);
if (!s) {
@ -2842,8 +2847,8 @@ lookup_ac (SessionObject* o, FIDString id)
* recurive locks (deadlock, or double unlock crash).
*/
int send_id = atoi (id + strlen (ContextInfo::kSendLevel));
if (s->send_enable_controllable (send_id)) {
return s->send_level_controllable (send_id);
if (send_id >=0 && s->send_enable_controllable (send_id)) {
return s->send_level_controllable (send_id, locked);
}
#endif
}
@ -2964,12 +2969,13 @@ VST3PI::getContextInfoValue (double& value, FIDString id)
if (0 == strcmp (id, ContextInfo::kMaxVolume)) {
value = s->gain_control ()->upper ();
} else if (0 == strcmp (id, ContextInfo::kMaxSendLevel)) {
value = 2.0; // Config->get_max_gain();
#ifdef MIXBUS
if (s->send_level_controllable (0)) {
if (s->send_enable_controllable (0)) {
assert (s->send_level_controllable (0));
value = s->send_level_controllable (0)->upper (); // pow (10.0, .05 * 15.0);
}
#endif
value = 2.0; // Config->get_max_gain();
} else if (0 == strcmp (id, ContextInfo::kVolume)) {
std::shared_ptr<AutomationControl> ac = s->gain_control ();
value = ac->get_value (); // gain coefficient 0..2 (1.0 = 0dB)
@ -2983,11 +2989,12 @@ VST3PI::getContextInfoValue (double& value, FIDString id)
value = 0.5; // center
}
} else if (0 == strncmp (id, ContextInfo::kSendLevel, strlen (ContextInfo::kSendLevel))) {
std::shared_ptr<AutomationControl> ac = lookup_ac (_owner, id);
std::shared_ptr<AutomationControl> ac = lookup_ac (_owner, id, _in_set_owner.load ());
if (ac) {
value = ac->get_value (); // gain cofficient
psl_subscribe_to (ac, id);
} else {
value = 0;
DEBUG_TRACE (DEBUG::VST3Callbacks, string_compose ("VST3PI::getContextInfoValue<double> invalid AC %1\n", id));
return kInvalidArgument; // send index out of bounds
}
@ -3023,14 +3030,14 @@ VST3PI::setContextInfoValue (FIDString id, double value)
ac->set_value (ac->interface_to_internal (value, true), PBD::Controllable::NoGroup);
}
} else if (0 == strncmp (id, ContextInfo::kSendLevel, strlen (ContextInfo::kSendLevel))) {
std::shared_ptr<AutomationControl> ac = lookup_ac (_owner, id);
std::shared_ptr<AutomationControl> ac = lookup_ac (_owner, id, _in_set_owner.load ());
if (ac) {
ac->set_value (value, Controllable::NoGroup);
} else {
return kInvalidArgument; // send index out of bounds
}
} else {
DEBUG_TRACE (DEBUG::VST3Callbacks, "VST3PI::setContextInfoValue<double>: unsupported ID\n");
DEBUG_TRACE (DEBUG::VST3Callbacks, string_compose ("VST3PI::setContextInfoValue<double>: unsupported ID %1\n", id));
return kInvalidArgument;
}
return kResultOk;
@ -3071,7 +3078,7 @@ VST3PI::setContextInfoValue (FIDString id, int32 value)
s->session ().set_control (ac, value != 0 ? 1 : 0, Controllable::NoGroup);
}
} else {
DEBUG_TRACE (DEBUG::VST3Callbacks, "VST3PI::setContextInfoValue<int>: unsupported ID\n");
DEBUG_TRACE (DEBUG::VST3Callbacks, string_compose ("VST3PI::setContextInfoValue<int>: unsupported ID %1\n", id));
return kNotImplemented;
}
return kResultOk;
@ -3101,6 +3108,7 @@ VST3PI::beginEditContextInfoValue (FIDString id)
}
std::shared_ptr<AutomationControl> ac = lookup_ac (_owner, id);
if (!ac) {
DEBUG_TRACE (DEBUG::VST3Callbacks, string_compose ("VST3PI::beginEditContextInfoValue %1 -- invalid AC\n", id));
return kInvalidArgument;
}
DEBUG_TRACE (DEBUG::VST3Callbacks, string_compose ("VST3PI::beginEditContextInfoValue %1\n", id));
@ -3117,6 +3125,7 @@ VST3PI::endEditContextInfoValue (FIDString id)
}
std::shared_ptr<AutomationControl> ac = lookup_ac (_owner, id);
if (!ac) {
DEBUG_TRACE (DEBUG::VST3Callbacks, string_compose ("VST3PI::endEditContextInfoValue %1 -- invalid AC\n", id));
return kInvalidArgument;
}
DEBUG_TRACE (DEBUG::VST3Callbacks, string_compose ("VST3PI::endEditContextInfoValue %1\n", id));

View File

@ -41,7 +41,7 @@ Worker::Worker(Workee* workee, uint32_t ring_size, bool threaded)
, _synchronous(!threaded)
{
if (threaded) {
_thread = PBD::Thread::create (boost::bind (&Worker::run, this));
_thread = PBD::Thread::create (boost::bind (&Worker::run, this), "LV2Worker");
}
}
@ -137,8 +137,6 @@ Worker::emit_responses()
void
Worker::run()
{
pthread_set_name ("LV2Worker");
void* buf = NULL;
size_t buf_size = 0;
while (true) {

View File

@ -136,9 +136,7 @@ class TmpFileRt
static void * _disk_thread (void *arg)
{
TmpFileRt *d = static_cast<TmpFileRt *>(arg);
pthread_set_name ("ExportDiskIO");
d->disk_thread ();
pthread_exit (0);
return 0;
}
@ -158,7 +156,7 @@ class TmpFileRt
pthread_mutex_init (&_disk_thread_lock, 0);
pthread_cond_init (&_data_ready, 0);
if (pthread_create (&_thread_id, NULL, _disk_thread, this)) {
if (pthread_create_and_store ("ExportDiskIO", &_thread_id, _disk_thread, this, 0)) {
_capture = false;
if (SndfileWriter<T>::throw_level (ThrowStrict)) {
throw Exception (*this, "Cannot create export disk writer");

View File

@ -1385,7 +1385,7 @@ AlsaAudioBackend::midi_device_thread ()
bool
AlsaAudioBackend::listen_for_midi_device_changes ()
{
if (pthread_create (&_midi_device_thread_id, NULL, _midi_device_thread, this)) {
if (pbd_pthread_create (PBD_RT_STACKSIZE_HELP, &_midi_device_thread_id, _midi_device_thread, this)) {
return false;
}
return true;

View File

@ -605,7 +605,7 @@ CoreAudioBackend::_start (bool for_latency_measurement)
return PortReconnectError;
}
if (pthread_create (&_freeewheel_thread, NULL, pthread_freewheel, this))
if (pbd_pthread_create (PBD_RT_STACKSIZE_PROC, &_freeewheel_thread, pthread_freewheel, this))
{
PBD::error << _("CoreAudioBackend: failed to create process thread.") << endmsg;
delete _pcmio; _pcmio = 0;

View File

@ -856,7 +856,7 @@ static void* freewheel_thread(void* arg)
bool
PortAudioBackend::start_freewheel_process_thread ()
{
if (pthread_create(&_pthread_freewheel, NULL, freewheel_thread, this)) {
if (pbd_pthread_create (PBD_RT_STACKSIZE_PROC, &_pthread_freewheel, freewheel_thread, this)) {
DEBUG_AUDIO("Failed to create main audio thread\n");
return false;
}

View File

@ -50,7 +50,7 @@ using namespace PBD;
using namespace Glib;
uint64_t BaseUI::rt_bit = 1;
int BaseUI::_thread_priority = PBD_RT_PRI_PROC - 1;
int BaseUI::_thread_priority = PBD_RT_PRI_CTRL;
BaseUI::RequestType BaseUI::CallSlot = BaseUI::new_request_type();
BaseUI::RequestType BaseUI::Quit = BaseUI::new_request_type();
@ -94,7 +94,6 @@ BaseUI::set_thread_priority () const
void
BaseUI::main_thread ()
{
pthread_set_name (string_compose ("UI:%1", event_loop_name ()).c_str ());
DEBUG_TRACE (DEBUG::EventLoop, string_compose ("%1: event loop running in thread %2\n", event_loop_name(), pthread_name()));
set_event_loop_for_thread (this);
thread_init ();
@ -122,7 +121,7 @@ BaseUI::run ()
attach_request_source ();
Glib::Threads::Mutex::Lock lm (_run_lock);
_run_loop_thread = PBD::Thread::create (boost::bind (&BaseUI::main_thread, this));
_run_loop_thread = PBD::Thread::create (boost::bind (&BaseUI::main_thread, this), string_compose ("UI:%1", event_loop_name ()));
_running.wait (_run_lock);
}

View File

@ -61,6 +61,7 @@ DebugBits PBD::DEBUG::Configuration = PBD::new_debug_bit ("configuration");
DebugBits PBD::DEBUG::UndoHistory = PBD::new_debug_bit ("undohistory");
DebugBits PBD::DEBUG::Timing = PBD::new_debug_bit ("timing");
DebugBits PBD::DEBUG::Threads = PBD::new_debug_bit ("threads");
DebugBits PBD::DEBUG::ThreadName = PBD::new_debug_bit ("threadname");
DebugBits PBD::DEBUG::Locale = PBD::new_debug_bit ("locale");
DebugBits PBD::DEBUG::StringConvert = PBD::new_debug_bit ("stringconvert");
DebugBits PBD::DEBUG::DebugTimestamps = PBD::new_debug_bit ("debugtimestamps");
@ -103,7 +104,7 @@ PBD::new_debug_bit (const char* name)
void
PBD::debug_only_print (const char* prefix, string str)
{
if ((PBD::debug_bits & DEBUG::Threads).any()) {
if ((PBD::debug_bits & DEBUG::ThreadName).any()) {
printf ("0x%lx (%s) ", (intptr_t) DEBUG_THREAD_SELF, pthread_name());
}

View File

@ -98,7 +98,7 @@ Downloader::start ()
_cancel = false;
_status = 0; /* unknown at this point */
return 0 != (thread = PBD::Thread::create (boost::bind (&Downloader::download, this)));
return 0 != (thread = PBD::Thread::create (boost::bind (&Downloader::download, this), "Downloader"));
}
void

View File

@ -59,7 +59,6 @@ write_callback (void* buffer, size_t size, size_t nmemb, void* d)
static void*
get_url (void* arg)
{
pthread_set_name ("FileArchiveURL");
FileArchive::Request* r = (FileArchive::Request*) arg;
CURL* curl;
@ -328,7 +327,10 @@ std::vector<std::string>
FileArchive::contents_url ()
{
_req.mp.reset ();
pthread_create (&_tid, NULL, get_url, (void*)&_req);
if (pthread_create_and_store ("FileArchiveHTTP", &_tid, get_url, (void*)&_req, 0)) {
return std::vector<std::string> ();
}
struct archive* a = setup_archive ();
archive_read_open (a, (void*)&_req.mp, NULL, ar_read, NULL);
@ -359,7 +361,9 @@ int
FileArchive::extract_url ()
{
_req.mp.reset ();
pthread_create (&_tid, NULL, get_url, (void*)&_req);
if (pthread_create_and_store ("FileArchiveHTTP", &_tid, get_url, (void*)&_req)) {
return -1;
}
struct archive* a = setup_archive ();
archive_read_open (a, (void*)&_req.mp, NULL, ar_read, NULL);
int rv = do_extract (a);

View File

@ -45,7 +45,7 @@ Inflater::~Inflater ()
int
Inflater::start ()
{
return 0 != (thread = PBD::Thread::create (boost::bind (&Inflater::threaded_inflate, this)));
return 0 != (thread = PBD::Thread::create (boost::bind (&Inflater::threaded_inflate, this), "Inflater"));
}
void

View File

@ -39,7 +39,7 @@
namespace PBD {
typedef std::bitset<128> DebugBits;
typedef std::bitset<192> DebugBits;
LIBPBD_API extern DebugBits debug_bits;
LIBPBD_API DebugBits new_debug_bit (const char* name);
@ -64,6 +64,7 @@ namespace PBD {
LIBPBD_API extern DebugBits UndoHistory;
LIBPBD_API extern DebugBits Timing;
LIBPBD_API extern DebugBits Threads;
LIBPBD_API extern DebugBits ThreadName;
LIBPBD_API extern DebugBits Locale;
LIBPBD_API extern DebugBits StringConvert;
LIBPBD_API extern DebugBits DebugTimestamps;

View File

@ -57,6 +57,7 @@
# define PBD_RT_PRI_MAIN pbd_pthread_priority (THREAD_MAIN)
# define PBD_RT_PRI_MIDI pbd_pthread_priority (THREAD_MIDI)
# define PBD_RT_PRI_PROC pbd_pthread_priority (THREAD_PROC)
# define PBD_RT_PRI_CTRL pbd_pthread_priority (THREAD_CTRL)
LIBPBD_API int pthread_create_and_store (std::string name, pthread_t *thread, void * (*start_routine)(void *), void * arg, uint32_t stacklimit = 0x80000 /*512kB*/);
LIBPBD_API void pthread_cancel_one (pthread_t thread);
@ -65,10 +66,13 @@ LIBPBD_API void pthread_kill_all (int signum);
LIBPBD_API const char* pthread_name ();
LIBPBD_API void pthread_set_name (const char* name);
LIBPBD_API void pbd_set_engine_rt_priority (int);
enum PBDThreadClass {
THREAD_MAIN, // main audio I/O thread
THREAD_MIDI, // MIDI I/O threads
THREAD_PROC, // realtime worker
THREAD_CTRL, // Automation watch, BaseUI
THREAD_IO // non-realtime I/O
};
@ -88,7 +92,7 @@ LIBPBD_API int pbd_realtime_pthread_create (
void *arg);
LIBPBD_API int pbd_absolute_rt_priority (int policy, int priority);
LIBPBD_API int pbd_set_thread_priority (pthread_t, const int policy, int priority);
LIBPBD_API int pbd_set_thread_priority (pthread_t, int policy, int priority);
LIBPBD_API bool pbd_mach_set_realtime_policy (pthread_t thread_id, double period_ns, bool main);
namespace PBD {
@ -97,7 +101,7 @@ namespace PBD {
class LIBPBD_API Thread {
public:
static Thread* create (boost::function<void ()> const&, std::string const& name = "");
static Thread* create (boost::function<void ()> const&, std::string const& name);
static Thread* self ();
void join ();
bool caller_is_self () const;

View File

@ -1,7 +1,7 @@
/*
* Copyright (C) 2002-2015 Paul Davis <paul@linuxaudiosystems.com>
* Copyright (C) 2007-2009 David Robillard <d@drobilla.net>
* Copyright (C) 2015-2018 Robin Gareus <robin@gareus.org>
* Copyright (C) 2015-2024 Robin Gareus <robin@gareus.org>
*
* 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
@ -23,11 +23,17 @@
#include <stdint.h>
#include <string>
#ifdef PLATFORM_WINDOWS
#include <windows.h>
#endif
#if !defined PLATFORM_WINDOWS && defined __GLIBC__
#include <climits>
#include <dlfcn.h>
#endif
#include "pbd/compose.h"
#include "pbd/debug.h"
#include "pbd/failed_constructor.h"
#include "pbd/pthread_utils.h"
@ -37,6 +43,8 @@ DECLARE_DEFAULT_COMPARISONS (pthread_t) // Needed for 'DECLARE_DEFAULT_COMPARISO
// if the type of object being contained has no appropriate comparison operators
// defined (specifically, if operators '<' and '==' are undefined). This seems
// to be the case with ptw32 'pthread_t' which is a simple struct.
#define pthread_gethandle pthread_getw32threadhandle_np
#endif
#ifdef __APPLE__
@ -47,10 +55,54 @@ DECLARE_DEFAULT_COMPARISONS (pthread_t) // Needed for 'DECLARE_DEFAULT_COMPARISO
using namespace std;
typedef std::list<pthread_t> ThreadMap;
static ThreadMap all_threads;
static pthread_mutex_t thread_map_lock = PTHREAD_MUTEX_INITIALIZER;
static Glib::Threads::Private<char> thread_name (free);
typedef std::map<pthread_t, std::string> ThreadMap;
static ThreadMap all_threads;
static pthread_mutex_t thread_map_lock = PTHREAD_MUTEX_INITIALIZER;
static Glib::Threads::Private<char> thread_name (free);
static int base_priority_relative_to_max = -20;
#ifdef PLATFORM_WINDOWS
static
std::string GetLastErrorAsString()
{
DWORD err = ::GetLastError();
if(err == 0) {
return std::string ();
}
LPSTR buf = nullptr;
size_t size = FormatMessageA (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, err, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buf, 0, NULL);
std::string rv (buf, size);
LocalFree (buf);
return rv;
}
static bool
set_win_set_realtime_policy (pthread_t thread, int priority)
{
if (priority < 12) {
return false;
}
bool ok = false;
if (SetPriorityClass (GetCurrentProcess (), 0x00000100 /* REALTIME_PRIORITY_CLASS */)) {
/* see https://learn.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities */
ok = SetThreadPriority (pthread_gethandle (thread), priority);
DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Using Windows RT thread class. set priority: %1\n", ok ? "OK" : GetLastErrorAsString ()));
} else {
DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Cannot use Windows RT thread class: %1\n", GetLastErrorAsString ()));
ok = SetPriorityClass (GetCurrentProcess (), 0x00000080 /* HIGH_PRIORITY_CLASS */);
DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Using Windows high priority thread class: %1\n", ok ? "OK" : GetLastErrorAsString ()));
if (ok) {
ok = SetThreadPriority (pthread_gethandle (thread), priority);
DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Set Windows high thread priority: %1\n", ok ? "OK" : GetLastErrorAsString ()));
}
}
return ok;
}
#endif
namespace PBD
{
@ -72,148 +124,6 @@ PBD::notify_event_loops_about_thread_creation (pthread_t thread, const std::stri
ThreadCreatedWithRequestSize (thread, emitting_thread_name, request_count);
}
struct ThreadStartWithName {
void* (*thread_work) (void*);
void* arg;
std::string name;
ThreadStartWithName (void* (*f) (void*), void* a, const std::string& s)
: thread_work (f)
, arg (a)
, name (s)
{}
};
static void*
fake_thread_start (void* arg)
{
ThreadStartWithName* ts = (ThreadStartWithName*)arg;
void* (*thread_work) (void*) = ts->thread_work;
void* thread_arg = ts->arg;
/* name will be deleted by the default handler for GStaticPrivate, when the thread exits */
pthread_set_name (ts->name.c_str ());
/* we don't need this object anymore */
delete ts;
/* actually run the thread's work function */
void* ret = thread_work (thread_arg);
/* cleanup */
pthread_mutex_lock (&thread_map_lock);
for (ThreadMap::iterator i = all_threads.begin (); i != all_threads.end (); ++i) {
if (pthread_equal ((*i), pthread_self ())) {
all_threads.erase (i);
break;
}
}
pthread_mutex_unlock (&thread_map_lock);
/* done */
return ret;
}
int
pthread_create_and_store (string name, pthread_t* thread, void* (*start_routine) (void*), void* arg, uint32_t stacklimit)
{
pthread_attr_t default_attr;
int ret;
/* set default stack size to sensible default for memlocking */
pthread_attr_init (&default_attr);
if (stacklimit > 0) {
pthread_attr_setstacksize (&default_attr, stacklimit);
}
ThreadStartWithName* ts = new ThreadStartWithName (start_routine, arg, name);
if ((ret = pthread_create (thread, &default_attr, fake_thread_start, ts)) == 0) {
pthread_mutex_lock (&thread_map_lock);
all_threads.push_back (*thread);
pthread_mutex_unlock (&thread_map_lock);
}
pthread_attr_destroy (&default_attr);
return ret;
}
void
pthread_set_name (const char* str)
{
/* copy string and delete it when exiting */
thread_name.set (strdup (str)); // leaks
#if !defined PTW32_VERSION && defined _GNU_SOURCE
/* set public thread name, up to 16 chars */
char ptn[16];
memset (ptn, 0, 16);
strncpy (ptn, str, 15);
pthread_setname_np (pthread_self (), ptn);
#endif
}
const char*
pthread_name ()
{
const char* str = thread_name.get ();
if (str) {
return str;
}
return "unknown";
}
void
pthread_kill_all (int signum)
{
pthread_mutex_lock (&thread_map_lock);
for (ThreadMap::iterator i = all_threads.begin (); i != all_threads.end (); ++i) {
if (!pthread_equal ((*i), pthread_self ())) {
pthread_kill ((*i), signum);
}
}
all_threads.clear ();
pthread_mutex_unlock (&thread_map_lock);
}
void
pthread_cancel_all ()
{
pthread_mutex_lock (&thread_map_lock);
for (ThreadMap::iterator i = all_threads.begin (); i != all_threads.end ();) {
ThreadMap::iterator nxt = i;
++nxt;
if (!pthread_equal ((*i), pthread_self ())) {
pthread_cancel ((*i));
}
i = nxt;
}
all_threads.clear ();
pthread_mutex_unlock (&thread_map_lock);
}
void
pthread_cancel_one (pthread_t thread)
{
pthread_mutex_lock (&thread_map_lock);
for (ThreadMap::iterator i = all_threads.begin (); i != all_threads.end (); ++i) {
if (pthread_equal ((*i), thread)) {
all_threads.erase (i);
break;
}
}
pthread_cancel (thread);
pthread_mutex_unlock (&thread_map_lock);
}
static size_t
pbd_stack_size ()
{
@ -250,6 +160,148 @@ pbd_stack_size ()
return rv;
}
struct ThreadStartWithName {
void* (*thread_work) (void*);
void* arg;
std::string name;
ThreadStartWithName (void* (*f) (void*), void* a, const std::string& s)
: thread_work (f)
, arg (a)
, name (s)
{}
};
static void*
fake_thread_start (void* arg)
{
ThreadStartWithName* ts = (ThreadStartWithName*)arg;
void* (*thread_work) (void*) = ts->thread_work;
void* thread_arg = ts->arg;
/* name will be deleted by the default handler for GStaticPrivate, when the thread exits */
pthread_set_name (ts->name.c_str ());
DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Started: '%1'\n", ts->name));
/* we don't need this object anymore */
delete ts;
/* actually run the thread's work function */
void* ret = thread_work (thread_arg);
/* cleanup */
pthread_mutex_lock (&thread_map_lock);
for (auto const& t : all_threads) {
if (pthread_equal (t.first, pthread_self ())) {
DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Terminated: '%1'\n", t.second));
all_threads.erase (t.first);
break;
}
}
pthread_mutex_unlock (&thread_map_lock);
/* done */
return ret;
}
int
pthread_create_and_store (string name, pthread_t* thread, void* (*start_routine) (void*), void* arg, uint32_t stacklimit)
{
pthread_attr_t default_attr;
int ret;
/* set default stack size to sensible default for memlocking */
pthread_attr_init (&default_attr);
if (stacklimit > 0) {
pthread_attr_setstacksize (&default_attr, stacklimit + pbd_stack_size ());
}
ThreadStartWithName* ts = new ThreadStartWithName (start_routine, arg, name);
if ((ret = pthread_create (thread, &default_attr, fake_thread_start, ts)) == 0) {
pthread_mutex_lock (&thread_map_lock);
all_threads[*thread] = name;
pthread_mutex_unlock (&thread_map_lock);
}
pthread_attr_destroy (&default_attr);
return ret;
}
void
pthread_set_name (const char* str)
{
/* copy string and delete it when exiting */
thread_name.set (strdup (str)); // leaks
#if !defined PTW32_VERSION && defined _GNU_SOURCE
/* set public thread name, up to 16 chars */
char ptn[16];
memset (ptn, 0, 16);
strncpy (ptn, str, 15);
pthread_setname_np (pthread_self (), ptn);
#endif
}
const char*
pthread_name ()
{
const char* str = thread_name.get ();
if (str) {
return str;
}
return "unknown";
}
void
pthread_kill_all (int signum)
{
pthread_mutex_lock (&thread_map_lock);
for (auto const& t : all_threads) {
if (pthread_equal (t.first, pthread_self ())) {
continue;
}
DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Kill: '%1'\n", t.second));
pthread_kill (t.first, signum);
}
all_threads.clear ();
pthread_mutex_unlock (&thread_map_lock);
}
void
pthread_cancel_all ()
{
pthread_mutex_lock (&thread_map_lock);
for (auto const& t : all_threads) {
if (pthread_equal (t.first, pthread_self ())) {
continue;
}
DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Cancel: '%1'\n", t.second));
pthread_cancel (t.first);
}
all_threads.clear ();
pthread_mutex_unlock (&thread_map_lock);
}
void
pthread_cancel_one (pthread_t thread)
{
pthread_mutex_lock (&thread_map_lock);
for (auto const& t : all_threads) {
if (pthread_equal (t.first, thread)) {
all_threads.erase (t.first);
break;
}
}
pthread_cancel (thread);
pthread_mutex_unlock (&thread_map_lock);
}
int
pbd_pthread_create (
const size_t stacksize,
@ -261,12 +313,28 @@ pbd_pthread_create (
pthread_attr_t attr;
pthread_attr_init (&attr);
pthread_attr_setstacksize (&attr, stacksize + pbd_stack_size ());
if (stacksize > 0) {
pthread_attr_setstacksize (&attr, stacksize + pbd_stack_size ());
}
DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Start Non-RT Thread stacksize = 0x%1%2\n", std::hex, stacksize));
rv = pthread_create (thread, &attr, start_routine, arg);
pthread_attr_destroy (&attr);
return rv;
}
void
pbd_set_engine_rt_priority (int p)
{
/* this is mainly for JACK's benefit */
const int p_max = sched_get_priority_max (SCHED_FIFO);
const int p_min = sched_get_priority_min (SCHED_FIFO);
if (p <= 0 || p <= p_min + 10 || p > p_max) {
base_priority_relative_to_max = -20;
} else {
base_priority_relative_to_max = p - p_max;
}
}
int
pbd_pthread_priority (PBDThreadClass which)
{
@ -274,24 +342,22 @@ pbd_pthread_priority (PBDThreadClass which)
#ifdef PLATFORM_WINDOWS
switch (which) {
case THREAD_MAIN:
return -1;
return -1; // THREAD_PRIORITY_TIME_CRITICAL (15)
case THREAD_MIDI:
return -2;
default:
case THREAD_PROC:
return -2;
case THREAD_CTRL:
default:
return -14; // THREAD_PRIORITY_HIGHEST (2)
case THREAD_IO:
/* https://github.com/mingw-w64/mingw-w64/blob/master/mingw-w64-libraries/winpthreads/src/sched.c
* -> THREAD_PRIORITY_HIGHEST
*/
return -13;
/* https://github.com/mingw-w64/mingw-w64/blob/master/mingw-w64-libraries/winpthreads/src/sched.c */
return -15; // THREAD_PRIORITY_ABOVE_NORMAL (1)
}
#else
int base = -20;
int base = base_priority_relative_to_max;
const char* p = getenv ("ARDOUR_SCHED_PRI");
if (p && *p) {
base = atoi (p);
if (base > -5 && base < 5) {
if (base > -5 || base < -85) {
base = -20;
}
}
@ -304,6 +370,8 @@ pbd_pthread_priority (PBDThreadClass which)
default:
case THREAD_PROC:
return base - 2;
case THREAD_CTRL:
return base - 3;
case THREAD_IO:
return base - 10;
}
@ -314,26 +382,15 @@ int
pbd_absolute_rt_priority (int policy, int priority)
{
/* POSIX requires a spread of at least 32 steps between min..max */
const int p_min = sched_get_priority_min (policy); // Linux: 1
const int p_max = sched_get_priority_max (policy); // Linux: 99
const int p_min = sched_get_priority_min (policy); // Linux: 1 Windows -15
const int p_max = sched_get_priority_max (policy); // Linux: 99 Windows +15
if (priority == 0) {
assert (0);
priority = (p_min + p_max) / 2;
} else if (priority > 0) {
/* value relative to minium */
priority += p_min - 1;
} else {
/* value relative maximum */
priority += p_max + 1;
}
/* priority is relative to the max */
assert (priority < 0);
priority += p_max + 1;
if (priority > p_max) {
priority = p_max;
}
if (priority < p_min) {
priority = p_min;
}
priority = std::min (p_max, priority);
priority = std::max (p_min, priority);
return priority;
}
@ -356,19 +413,42 @@ pbd_realtime_pthread_create (
pthread_attr_setschedparam (&attr, &parm);
pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
pthread_attr_setinheritsched (&attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setstacksize (&attr, stacksize + pbd_stack_size ());
if (stacksize > 0) {
pthread_attr_setstacksize (&attr, stacksize + pbd_stack_size ());
}
DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Start Realtime Thread policy = %1 priority = %2 stacksize = 0x%3%4\n", policy, parm.sched_priority, std::hex, stacksize));
rv = pthread_create (thread, &attr, start_routine, arg);
pthread_attr_destroy (&attr);
#ifdef PLATFORM_WINDOWS
if (0 == rv && thread && parm.sched_priority >= 12) {
set_win_set_realtime_policy (*thread, parm.sched_priority);
}
#endif
return rv;
}
int
pbd_set_thread_priority (pthread_t thread, const int policy, int priority)
pbd_set_thread_priority (pthread_t thread, int policy, int priority)
{
#if defined PLATFORM_WINDOWS
policy = SCHED_OTHER;
#endif
struct sched_param param;
memset (&param, 0, sizeof (param));
param.sched_priority = pbd_absolute_rt_priority (policy, priority);
DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Change '%1' to policy = %2 priority = %3\n", pthread_name(), policy, param.sched_priority));
#ifdef PLATFORM_WINDOWS
if (is_pthread_active (thread) && param.sched_priority >= 12) {
if (set_win_set_realtime_policy (thread, param.sched_priority)) {
return 0;
}
}
#endif
return pthread_setschedparam (thread, SCHED_FIFO, &param);
}
@ -484,6 +564,12 @@ PBD::Thread::Thread (boost::function<void ()> const& slot, std::string const& na
if (pthread_create (&_t, &thread_attributes, _run, this)) {
throw failed_constructor ();
}
if (_joinable) {
pthread_mutex_lock (&thread_map_lock);
all_threads[_t] = name;
pthread_mutex_unlock (&thread_map_lock);
}
}
void*
@ -492,8 +578,22 @@ PBD::Thread::_run (void* arg) {
if (!self->_name.empty ()) {
pthread_set_name (self->_name.c_str ());
}
DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Started: '%1'\n", self->_name));
self->_slot ();
/* cleanup */
pthread_mutex_lock (&thread_map_lock);
for (auto const& t : all_threads) {
if (pthread_equal (t.first, pthread_self ())) {
DEBUG_TRACE (PBD::DEBUG::Threads, string_compose ("Terminated: '%1'\n", t.second));
all_threads.erase (t.first);
break;
}
}
pthread_mutex_unlock (&thread_map_lock);
pthread_exit (0);
return 0;
}

View File

@ -278,9 +278,7 @@ SystemExec::~SystemExec ()
static void*
interposer_thread (void *arg) {
SystemExec *sex = static_cast<SystemExec *>(arg);
pthread_set_name ("ExecStdOut");
sex->output_interposer();
pthread_exit(0);
return 0;
}
@ -522,7 +520,7 @@ SystemExec::start (StdErrMode stderr_mode, const char * /*vfork_exec_wrapper*/)
return -1;
}
int rv = pthread_create (&thread_id_tt, NULL, interposer_thread, this);
int rv = pthread_create_and_store ("ExecStdOut", &thread_id_tt, interposer_thread, this, 0);
thread_active=true;
if (rv) {
thread_active=false;
@ -560,7 +558,6 @@ SystemExec::output_interposer()
ReadStdout(rv, bytesRead); /* EMIT SIGNAL */
}
Terminated(); /* EMIT SIGNAL */
pthread_exit(0);
}
void
@ -808,7 +805,7 @@ SystemExec::start (StdErrMode stderr_mode, const char *vfork_exec_wrapper)
close_fd (pout[1]);
close_fd (pin[0]);
int rv = pthread_create (&thread_id_tt, NULL, interposer_thread, this);
int rv = pthread_create_and_store ("ExecStdOut", &thread_id_tt, interposer_thread, this, 0);
thread_active=true;
if (rv) {
@ -917,7 +914,6 @@ again:
ReadStdout (rv, r); /* EMIT SIGNAL */
}
Terminated (); /* EMIT SIGNAL */
pthread_exit (0);
}
void

View File

@ -258,8 +258,6 @@ CC121::stop ()
void
CC121::thread_init ()
{
pthread_set_name (event_loop_name().c_str());
PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);

View File

@ -238,8 +238,6 @@ void
ContourDesignControlProtocol::thread_init ()
{
DEBUG_TRACE (DEBUG::ContourDesignControl, "thread_init()\n");
pthread_set_name (X_("contourdesign"));
PBD::notify_event_loops_about_thread_creation (pthread_self (), X_("contourdesign"), 2048);
ARDOUR::SessionEvent::create_per_thread_pool (X_("contourdesign"), 128);

View File

@ -283,8 +283,6 @@ FaderPort8::stop ()
void
FaderPort8::thread_init ()
{
pthread_set_name (event_loop_name().c_str());
PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);

View File

@ -308,8 +308,6 @@ GenericMidiControlProtocol::stop ()
void
GenericMidiControlProtocol::thread_init ()
{
pthread_set_name (event_loop_name().c_str());
PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);

View File

@ -665,8 +665,6 @@ void LaunchControlXL::handle_midi_note_off_message(MIDI::Parser & parser, MIDI::
void
LaunchControlXL::thread_init ()
{
pthread_set_name (event_loop_name().c_str());
PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);

View File

@ -194,8 +194,6 @@ MackieControlProtocol::~MackieControlProtocol()
void
MackieControlProtocol::thread_init ()
{
pthread_set_name (event_loop_name().c_str());
PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);

View File

@ -307,16 +307,9 @@ Maschine2::stop ()
void
Maschine2::thread_init ()
{
pthread_set_name (event_loop_name().c_str());
ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 1024);
PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 1024);
struct sched_param rtparam;
memset (&rtparam, 0, sizeof (rtparam));
rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
if (pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam) != 0) {
// do we care? not particularly.
}
set_thread_priority ();
}
void

View File

@ -284,8 +284,6 @@ OSC::start ()
void
OSC::thread_init ()
{
pthread_set_name (event_loop_name().c_str());
if (_osc_unix_server) {
Glib::RefPtr<IOSource> src = IOSource::create (lo_server_get_socket_fd (_osc_unix_server), IO_IN|IO_HUP|IO_ERR);
src->connect (sigc::bind (sigc::mem_fun (*this, &OSC::osc_input_handler), _osc_unix_server));

View File

@ -174,8 +174,6 @@ US2400Protocol::~US2400Protocol()
void
US2400Protocol::thread_init ()
{
pthread_set_name (event_loop_name().c_str());
PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);

View File

@ -76,7 +76,6 @@ ArdourWebsockets::set_active (bool yn)
void
ArdourWebsockets::thread_init ()
{
pthread_set_name (event_loop_name ().c_str ());
PBD::notify_event_loops_about_thread_creation (pthread_self (), event_loop_name (), 2048);
SessionEvent::create_per_thread_pool (event_loop_name (), 128);
}

View File

@ -154,8 +154,6 @@ WiimoteControlProtocol::thread_init ()
{
DEBUG_TRACE (DEBUG::WiimoteControl, "WiimoteControlProtocol::thread_init init\n");
pthread_set_name (X_("wiimote"));
// allow to make requests to the GUI and RT thread(s)
PBD::notify_event_loops_about_thread_creation (pthread_self (), X_("wiimote"), 2048);
BasicUI::register_thread ("wiimote");

View File

@ -229,6 +229,9 @@ gdk_event_mask_get_type (void)
{ GDK_PROXIMITY_OUT_MASK, "GDK_PROXIMITY_OUT_MASK", "proximity-out-mask" },
{ GDK_SUBSTRUCTURE_MASK, "GDK_SUBSTRUCTURE_MASK", "substructure-mask" },
{ GDK_SCROLL_MASK, "GDK_SCROLL_MASK", "scroll-mask" },
{ GDK_TOUCH_BEGIN_MASK, "GDK_TOUCH_BEGIN_MASK", "touch-begin-mask" },
{ GDK_TOUCH_UPDATE_MASK, "GDK_TOUCH_UPDATE_MASK", "touch-update-mask" },
{ GDK_TOUCH_END_MASK, "GDK_TOUCH_END_MASK", "touch-end-mask" },
{ GDK_ALL_EVENTS_MASK, "GDK_ALL_EVENTS_MASK", "all-events-mask" },
{ 0, NULL, NULL }
};

View File

@ -387,6 +387,14 @@ gdk_event_new (GdkEventType type)
new_event->button.x_root = 0.;
new_event->button.y_root = 0.;
break;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
new_event->touch.x = 0.;
new_event->touch.y = 0.;
new_event->touch.x_root = 0.;
new_event->touch.y_root = 0.;
break;
case GDK_SCROLL:
new_event->scroll.x = 0.;
new_event->scroll.y = 0.;
@ -596,6 +604,10 @@ gdk_event_get_time (const GdkEvent *event)
case GDK_3BUTTON_PRESS:
case GDK_BUTTON_RELEASE:
return event->button.time;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
return event->touch.time;
case GDK_SCROLL:
return event->scroll.time;
case GDK_KEY_PRESS:
@ -674,6 +686,11 @@ gdk_event_get_state (const GdkEvent *event,
case GDK_BUTTON_RELEASE:
*state = event->button.state;
return TRUE;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
*state = event->touch.state;
return TRUE;
case GDK_SCROLL:
*state = event->scroll.state;
return TRUE;
@ -770,6 +787,12 @@ gdk_event_get_coords (const GdkEvent *event,
x = event->motion.x;
y = event->motion.y;
break;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
x = event->touch.x;
y = event->touch.y;
break;
default:
fetched = FALSE;
break;
@ -820,6 +843,12 @@ gdk_event_get_root_coords (const GdkEvent *event,
x = event->button.x_root;
y = event->button.y_root;
break;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
x = event->touch.x_root;
y = event->touch.y_root;
break;
case GDK_ENTER_NOTIFY:
case GDK_LEAVE_NOTIFY:
x = event->crossing.x_root;

View File

@ -9887,6 +9887,9 @@ static const guint type_masks[] = {
0, /* GDK_OWNER_CHANGE = 34 */
0, /* GDK_GRAB_BROKEN = 35 */
0, /* GDK_DAMAGE = 36 */
GDK_TOUCH_BEGIN_MASK, /* GDK_TOUCH_BEGIN = 37 */
GDK_TOUCH_UPDATE_MASK, /* GDK_TOUCH_UPDATE = 38 */
GDK_TOUCH_END_MASK, /* GDK_TOUCH_END = 39 */
};
G_STATIC_ASSERT (G_N_ELEMENTS (type_masks) == GDK_EVENT_LAST);
@ -9929,6 +9932,14 @@ is_motion_type (GdkEventType type)
type == GDK_LEAVE_NOTIFY;
}
static gboolean
is_touch_type (GdkEventType type)
{
return type == GDK_TOUCH_BEGIN ||
type == GDK_TOUCH_UPDATE ||
type == GDK_TOUCH_END;
}
static GdkWindowObject *
find_common_ancestor (GdkWindowObject *win1,
GdkWindowObject *win2)
@ -10828,6 +10839,8 @@ proxy_button_event (GdkEvent *source_event,
_gdk_display_pointer_grab_update (display, serial);
}
// TODO pointer grab per touch event (use g hash table)
pointer_window = get_pointer_window (display, toplevel_window,
toplevel_x, toplevel_y,
serial);
@ -10873,6 +10886,19 @@ proxy_button_event (GdkEvent *source_event,
event->scroll.delta_y = source_event->scroll.delta_y;
return TRUE;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
convert_toplevel_coords_to_window (event_win,
toplevel_x, toplevel_y,
&event->touch.x, &event->touch.y);
event->touch.sequence = source_event->touch.sequence;
event->touch.deviceid = source_event->touch.deviceid;
event->touch.x_root = source_event->touch.x_root;
event->touch.y_root = source_event->touch.y_root;
event->touch.state = state;
return TRUE;
default:
return FALSE;
}
@ -11057,7 +11083,8 @@ _gdk_windowing_got_event (GdkDisplay *display,
return;
if (!(is_button_type (event->type) ||
is_motion_type (event->type)) ||
is_motion_type (event->type) ||
is_touch_type (event->type)) ||
event_private->window_type == GDK_WINDOW_ROOT)
return;
@ -11147,6 +11174,9 @@ _gdk_windowing_got_event (GdkDisplay *display,
else if (is_button_type (event->type))
unlink_event = proxy_button_event (event,
serial);
else if (is_touch_type (event->type))
unlink_event = proxy_button_event (event,
serial);
if (event->type == GDK_BUTTON_RELEASE &&
!event->any.send_event)

View File

@ -811,6 +811,10 @@ _gdk_win32_print_event (const GdkEvent *event)
CASE (GDK_SETTING);
CASE (GDK_OWNER_CHANGE);
CASE (GDK_GRAB_BROKEN);
CASE (GDK_TOUCH_BEGIN);
CASE (GDK_TOUCH_UPDATE);
CASE (GDK_TOUCH_END);
CASE (GDK_TOUCH_CANCEL);
#undef CASE
default: g_assert_not_reached ();
}
@ -935,6 +939,7 @@ _gdk_win32_print_event (const GdkEvent *event)
g_print ("%s: %s",
_gdk_win32_window_state_to_string (event->window_state.changed_mask),
_gdk_win32_window_state_to_string (event->window_state.new_window_state));
break;
case GDK_SETTING:
g_print ("%s: %s",
(event->setting.action == GDK_SETTING_ACTION_NEW ? "NEW" :
@ -942,11 +947,23 @@ _gdk_win32_print_event (const GdkEvent *event)
(event->setting.action == GDK_SETTING_ACTION_DELETED ? "DELETED" :
"???"))),
(event->setting.name ? event->setting.name : "NULL"));
break;
case GDK_GRAB_BROKEN:
g_print ("%s %s %p",
(event->grab_broken.keyboard ? "KEYBOARD" : "POINTER"),
(event->grab_broken.implicit ? "IMPLICIT" : "EXPLICIT"),
(event->grab_broken.grab_window ? GDK_WINDOW_HWND (event->grab_broken.grab_window) : 0));
break;
case GDK_TOUCH_BEGIN:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
g_print ("Touch %s %d (%.4g,%.4g) (%.4g,%.4g) ",
(event->any.type == GDK_TOUCH_BEGIN) ? "Begin" : (event->any.type == GDK_TOUCH_END) ? "End" : "Update",
event->touch.sequence,
event->touch.x, event->touch.y,
event->touch.x_root, event->touch.y_root);
case GDK_TOUCH_CANCEL:
g_print ("Touch Cancel\n");
default:
/* Nothing */
break;
@ -2681,6 +2698,13 @@ gdk_event_translate (MSG *msg,
return_val = TRUE;
break;
case WM_POINTERDOWN:
break;
case WM_POINTERUP:
break;
case WM_POINTERUPDATE:
break;
case WM_NCMOUSEMOVE:
GDK_NOTE (EVENTS,
g_print (" (%d,%d)",

View File

@ -164,6 +164,7 @@ def configure(conf):
conf.check_cc(header_name='X11/extensions/Xinerama.h',
lib='Xinerama', uselib_store="XINERAMA", define_name='HAVE_XFREE_XINERAMA',
execute = False, mandatory=False)
autowaf.check_pkg(conf, 'xi', uselib_store='XINPUT2', mandatory=False, atleast_version='1.7.10')
def build(bld):
if not bld.is_defined('YTK'):
@ -202,7 +203,7 @@ def build(bld):
obj.ldflags = '-limm32 -lole32 -lgdi32 -lcomdlg32 -lwinspool -lcomctl32 -luuid'
else:
obj.source = libydk_sources + libydk_x11_sources
obj.uselib += ' GIO-UNIX X11 XEXT XINERAMA RANDR'
obj.uselib += ' GIO-UNIX X11 XEXT XINERAMA RANDR DL'
obj.includes += ['ydk/x11/gdk', 'ydk/gdk/x11']
obj.export_includes += ['ydk/gdk']
obj.export_includes += ['ydk/x11']

View File

@ -65,6 +65,9 @@
#include <X11/extensions/Xrandr.h>
#endif
#ifdef HAVE_XINPUT2
#include <dlfcn.h>
#endif
static void gdk_display_x11_dispose (GObject *object);
static void gdk_display_x11_finalize (GObject *object);
@ -864,6 +867,13 @@ gdk_display_x11_finalize (GObject *object)
g_hash_table_destroy (display_x11->atom_from_virtual);
g_hash_table_destroy (display_x11->atom_to_virtual);
/* Multitouch */
#ifdef HAVE_XINPUT2
if (display_x11->xi.libxi)
dlclose (display_x11->xi.libxi);
g_hash_table_destroy (display_x11->touch_devices);
#endif
/* Leader Window */
XDestroyWindow (display_x11->xdisplay, display_x11->leader_window);

View File

@ -62,6 +62,11 @@
#include <X11/extensions/Xrandr.h>
#endif
#ifdef HAVE_XINPUT2
#include <dlfcn.h>
#include <X11/extensions/XInput2.h>
#endif
#include <X11/Xatom.h>
typedef struct _GdkIOClosure GdkIOClosure;
@ -237,6 +242,64 @@ _gdk_events_init (GdkDisplay *display)
gdk_atom_intern_static_string ("WM_PROTOCOLS"),
gdk_wm_protocols_filter,
NULL);
#ifdef HAVE_XINPUT2
void* lxi = dlopen ("libXi.so", RTLD_NOW | RTLD_GLOBAL);
if (lxi)
{
display_x11->xi.XISelectEvents = dlsym (lxi, "XISelectEvents");
display_x11->xi.XIQueryDevice = dlsym (lxi, "XIQueryDevice");
display_x11->xi.XIFreeDeviceInfo = dlsym (lxi, "XIFreeDeviceInfo");
if (display_x11->xi.XISelectEvents && display_x11->xi.XIQueryDevice && display_x11->xi.XIFreeDeviceInfo)
{
display_x11->xi.libxi = lxi;
}
else
{
dlclose (lxi);
}
}
int firstevent, firsterror;
if (display_x11->xi.libxi && XQueryExtension (display_x11->xdisplay, "XInputExtension", &display_x11->xid_opcode, &firstevent, &firsterror))
{
printf ("CHECK FOR TOUCHSCREENs\n");
int n_devices;
XIDeviceInfo* info;
info = display_x11->xi.XIQueryDevice (display_x11->xdisplay, XIAllDevices, &n_devices);
for (int i = 0; i < n_devices; ++i) {
XIDeviceInfo* dev = &info[i];
if (!dev->enabled) {
continue;
}
if (!(dev->use == XISlavePointer || dev->use == XIFloatingSlave)) {
continue;
}
gboolean direct_touch = FALSE;
XITouchClassInfo* classInfo;
for (int j = 0; j < dev->num_classes; ++j) {
classInfo = (XITouchClassInfo*)(dev->classes[j]);
if (classInfo->type == XITouchClass && ((XITouchClassInfo *)dev->classes[j])->mode == XIDirectTouch) {
direct_touch = TRUE;
}
}
if (direct_touch) {
printf ("XI: touch-device id=%d name='%s' ntouch: %d\n", dev->deviceid, dev->name, classInfo->num_touches);
if (!display_x11->touch_devices) {
display_x11->touch_devices = g_hash_table_new (g_direct_hash, NULL);
}
g_hash_table_insert (display_x11->touch_devices, GUINT_TO_POINTER (dev->deviceid), GUINT_TO_POINTER(1));
}
}
display_x11->xi.XIFreeDeviceInfo (info);
}
#endif
}
void
@ -1233,7 +1296,7 @@ gdk_event_translate (GdkDisplay *display,
event->button.state = (GdkModifierType) xevent->xbutton.state;
event->button.button = xevent->xbutton.button;
event->button.device = display->core_pointer;
if (!set_screen_from_root (display, event, xevent->xbutton.root))
{
return_val = FALSE;
@ -2124,6 +2187,9 @@ gdk_event_translate (GdkDisplay *display,
break;
default:
if (xevent->xcookie.type == GenericEvent) {
XGetEventData (display_x11->xdisplay, &xevent->xcookie);
}
#ifdef HAVE_XKB
if (xevent->type == display_x11->xkb_event_type)
{
@ -2175,6 +2241,46 @@ gdk_event_translate (GdkDisplay *display,
}
else
#endif
#ifdef HAVE_XINPUT2
if (xevent->xcookie.type == GenericEvent && xevent->xcookie.extension == display_x11->xid_opcode
&& display_x11->touch_devices && g_hash_table_lookup (display_x11->touch_devices, GUINT_TO_POINTER (((XIDeviceEvent *)xevent->xcookie.data)->deviceid)))
{
XIDeviceEvent *xev = (XIDeviceEvent *) xevent->xcookie.data;
printf ("TOUCH dev=%d src=%d | dt: %u flags: %x\n", xev->deviceid, xev->sourceid, xev->detail, xev->flags);
window = gdk_window_lookup_for_display (display, xev->event);
g_object_ref (window);
event->touch.window = window;
event->touch.time = xev->time;
event->touch.x = xev->event_x;
event->touch.y = xev->event_y;
event->touch.x_root = xev->root_x;
event->touch.y_root = xev->root_y;
event->touch.state = 0 ; (GdkModifierType) xevent->xbutton.state;
event->touch.sequence = xev->detail;
event->touch.deviceid = xev->deviceid;
switch (xevent->xcookie.evtype) {
case XI_TouchBegin:
event->touch.type = GDK_TOUCH_BEGIN;
break;
case XI_TouchUpdate:
event->touch.type = GDK_TOUCH_UPDATE;
break;
case XI_TouchEnd:
event->touch.type = GDK_TOUCH_END;
break;
default:
return FALSE;
break;
}
if (!set_screen_from_root (display, event, xev->root)) {
return_val = FALSE;
}
}
else
#endif
#if defined(HAVE_XCOMPOSITE) && defined (HAVE_XDAMAGE) && defined (HAVE_XFIXES)
if (display_x11->have_xdamage && window_private && window_private->composited &&
xevent->type == display_x11->xdamage_event_base + XDamageNotify &&

View File

@ -71,6 +71,10 @@
#include <X11/extensions/Xdamage.h>
#endif
#ifdef HAVE_XINPUT2
#include <X11/extensions/XInput2.h>
#endif
const int _gdk_event_mask_table[21] =
{
ExposureMask,
@ -797,6 +801,24 @@ _gdk_window_impl_new (GdkWindow *window,
g_object_ref (window);
_gdk_xid_table_insert (screen_x11->display, &draw_impl->xid, window);
#ifdef HAVE_XINPUT2
GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (gdk_drawable_get_display (window));
if (display_x11->touch_devices)
{
XIEventMask evmask;
unsigned char mask[XIMaskLen(XI_LASTEVENT)] = { 0 };
evmask.deviceid = XIAllDevices;
evmask.mask_len = sizeof(mask);
evmask.mask = mask;
XISetMask(evmask.mask, XI_TouchBegin);
XISetMask(evmask.mask, XI_TouchUpdate);
XISetMask(evmask.mask, XI_TouchEnd);
display_x11->xi.XISelectEvents (xdisplay, xid, &evmask, 1);
}
#endif
switch (GDK_WINDOW_TYPE (private))
{
case GDK_WINDOW_DIALOG:

View File

@ -50,6 +50,7 @@ typedef struct _GdkEventNoExpose GdkEventNoExpose;
typedef struct _GdkEventVisibility GdkEventVisibility;
typedef struct _GdkEventMotion GdkEventMotion;
typedef struct _GdkEventButton GdkEventButton;
typedef struct _GdkEventTouch GdkEventTouch;
typedef struct _GdkEventScroll GdkEventScroll;
typedef struct _GdkEventKey GdkEventKey;
typedef struct _GdkEventFocus GdkEventFocus;
@ -152,6 +153,9 @@ typedef enum
GDK_OWNER_CHANGE = 34,
GDK_GRAB_BROKEN = 35,
GDK_DAMAGE = 36,
GDK_TOUCH_BEGIN = 37,
GDK_TOUCH_UPDATE = 38,
GDK_TOUCH_END = 39,
GDK_EVENT_LAST /* helper variable for decls */
} GdkEventType;
@ -181,6 +185,9 @@ typedef enum
GDK_PROXIMITY_OUT_MASK = 1 << 19,
GDK_SUBSTRUCTURE_MASK = 1 << 20,
GDK_SCROLL_MASK = 1 << 21,
GDK_TOUCH_BEGIN_MASK = 1 << 22,
GDK_TOUCH_UPDATE_MASK = 1 << 23,
GDK_TOUCH_END_MASK = 1 << 24,
GDK_ALL_EVENTS_MASK = 0x3FFFFE
} GdkEventMask;
@ -325,6 +332,20 @@ struct _GdkEventButton
gdouble x_root, y_root;
};
struct _GdkEventTouch
{
GdkEventType type;
GdkWindow *window;
gint8 send_event;
guint32 time;
gdouble x;
gdouble y;
guint state;
guint sequence;
guint deviceid;
gdouble x_root, y_root;
};
struct _GdkEventScroll
{
GdkEventType type;
@ -500,6 +521,7 @@ union _GdkEvent
GdkEventVisibility visibility;
GdkEventMotion motion;
GdkEventButton button;
GdkEventTouch touch;
GdkEventScroll scroll;
GdkEventKey key;
GdkEventCrossing crossing;

View File

@ -36,6 +36,7 @@
G_BEGIN_DECLS
typedef struct _GdkDisplayX11 GdkDisplayX11;
typedef struct _GdkXInput2Fn GdkXInput2Fn;
typedef struct _GdkDisplayX11Class GdkDisplayX11Class;
#define GDK_TYPE_DISPLAY_X11 (_gdk_display_x11_get_type())
@ -52,6 +53,19 @@ typedef enum
GDK_YES
} GdkTristate;
#ifdef HAVE_XINPUT2
#include <X11/extensions/XInput2.h>
struct _GdkXInput2Fn
{
int (*XISelectEvents)(Display*, Window, XIEventMask*, int);
XIDeviceInfo* (*XIQueryDevice)(Display*, int, int*);
void (*XIFreeDeviceInfo)(XIDeviceInfo*);
void* libxi;
};
#endif
struct _GdkDisplayX11
{
GdkDisplay parent_instance;
@ -155,6 +169,12 @@ struct _GdkDisplayX11
/* The offscreen window that has the pointer in it (if any) */
GdkWindow *active_offscreen_window;
#ifdef HAVE_XINPUT2
int xid_opcode;
GHashTable* touch_devices;
GdkXInput2Fn xi;
#endif
};
struct _GdkDisplayX11Class

View File

@ -92,6 +92,9 @@ enum EventType
OWNER_CHANGE,
GRAB_BROKEN,
DAMAGE,
TOUCH_BEGIN,
TOUCH_UPDATE,
TOUCH_END,
EVENT_LAST
};

View File

@ -91,6 +91,9 @@ enum EventMask
PROXIMITY_OUT_MASK = 1 << 19,
SUBSTRUCTURE_MASK = 1 << 20,
SCROLL_MASK = 1 << 21,
TOUCH_BEGIN_MASK = 1 << 22,
TOUCH_UPDATE_MASK = 1 << 23,
TOUCH_END_MASK = 1 << 24,
ALL_EVENTS_MASK = 0x3FFFFE
};

View File

@ -1599,6 +1599,7 @@ gtk_main_do_event (GdkEvent *event)
case GDK_BUTTON_PRESS:
case GDK_2BUTTON_PRESS:
case GDK_3BUTTON_PRESS:
case GDK_TOUCH_BEGIN:
gtk_propagate_event (grab_widget, event);
break;
@ -1636,6 +1637,8 @@ gtk_main_do_event (GdkEvent *event)
/* else fall through */
case GDK_MOTION_NOTIFY:
case GDK_BUTTON_RELEASE:
case GDK_TOUCH_UPDATE:
case GDK_TOUCH_END:
case GDK_PROXIMITY_IN:
case GDK_PROXIMITY_OUT:
gtk_propagate_event (grab_widget, event);

View File

@ -197,6 +197,9 @@ enum {
KEYNAV_FAILED,
DRAG_FAILED,
DAMAGE_EVENT,
TOUCH_BEGIN,
TOUCH_UPDATE,
TOUCH_END,
LAST_SIGNAL
};
@ -537,6 +540,9 @@ gtk_widget_class_init (GtkWidgetClass *klass)
klass->drag_drop = NULL;
klass->drag_data_received = NULL;
klass->screen_changed = NULL;
klass->touch_begin_event = NULL;
klass->touch_update_event = NULL;
klass->touch_end_event = NULL;
klass->can_activate_accel = gtk_widget_real_can_activate_accel;
klass->grab_broken_event = NULL;
klass->query_tooltip = gtk_widget_real_query_tooltip;
@ -2377,6 +2383,39 @@ gtk_widget_class_init (GtkWidgetClass *klass)
G_TYPE_NONE, 1,
GDK_TYPE_SCREEN);
/* YTK Touch */
widget_signals[TOUCH_BEGIN] =
g_signal_new (I_("touch-start"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkWidgetClass, touch_begin_event),
_gtk_boolean_handled_accumulator, NULL,
_gtk_marshal_BOOLEAN__BOXED,
G_TYPE_BOOLEAN, 1,
GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
widget_signals[TOUCH_UPDATE] =
g_signal_new (I_("touch-update"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkWidgetClass, touch_update_event),
_gtk_boolean_handled_accumulator, NULL,
_gtk_marshal_BOOLEAN__BOXED,
G_TYPE_BOOLEAN, 1,
GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
widget_signals[TOUCH_END] =
g_signal_new (I_("touch-end"),
G_TYPE_FROM_CLASS (gobject_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (GtkWidgetClass, touch_end_event),
_gtk_boolean_handled_accumulator, NULL,
_gtk_marshal_BOOLEAN__BOXED,
G_TYPE_BOOLEAN, 1,
GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
/**
* GtkWidget::can-activate-accel:
* @widget: the object which received the signal
@ -5001,6 +5040,15 @@ gtk_widget_event_internal (GtkWidget *widget,
case GDK_DAMAGE:
signal_num = DAMAGE_EVENT;
break;
case GDK_TOUCH_BEGIN:
signal_num = TOUCH_BEGIN;
break;
case GDK_TOUCH_END:
signal_num = TOUCH_END;
break;
case GDK_TOUCH_UPDATE:
signal_num = TOUCH_UPDATE;
break;
default:
g_warning ("gtk_widget_event(): unhandled event type: %d", event->type);
signal_num = -1;

View File

@ -726,6 +726,12 @@ struct _GtkWidgetClass
GdkEventAny *event);
gboolean (* window_state_event) (GtkWidget *widget,
GdkEventWindowState *event);
gboolean (* touch_begin_event) (GtkWidget *widget,
GdkEventTouch *event);
gboolean (* touch_update_event) (GtkWidget *widget,
GdkEventTouch *event);
gboolean (* touch_end_event) (GtkWidget *widget,
GdkEventTouch *event);
/* selection */
void (* selection_get) (GtkWidget *widget,

View File

@ -3395,6 +3395,9 @@ void Widget_Class::class_init_function(void* g_class, void* class_data)
klass->drag_motion = &drag_motion_callback;
klass->drag_drop = &drag_drop_callback;
klass->drag_data_received = &drag_data_received_callback;
klass->touch_begin_event = &touch_begin_callback;
klass->touch_update_event = &touch_update_callback;
klass->touch_end_event = &touch_end_callback;
#ifdef GTKMM_ATKMM_ENABLED
#ifndef GTKMM_DISABLE_DEPRECATED
@ -4344,6 +4347,120 @@ gboolean Widget_Class::button_release_event_callback(GtkWidget* self, GdkEventBu
typedef gboolean RType;
return RType();
}
gboolean Widget_Class::touch_begin_callback(GtkWidget* self, GdkEventTouch* p0)
{
Glib::ObjectBase *const obj_base = static_cast<Glib::ObjectBase*>(
Glib::ObjectBase::_get_current_wrapper((GObject*)self));
// Non-gtkmmproc-generated custom classes implicitly call the default
// Glib::ObjectBase constructor, which sets is_derived_. But gtkmmproc-
// generated classes can use this optimisation, which avoids the unnecessary
// parameter conversions if there is no possibility of the virtual function
// being overridden:
if(obj_base && obj_base->is_derived_())
{
CppObjectType *const obj = dynamic_cast<CppObjectType* const>(obj_base);
if(obj) // This can be NULL during destruction.
{
try // Trap C++ exceptions which would normally be lost because this is a C callback.
{
// Call the virtual member method, which derived classes might override.
return static_cast<int>(obj->on_touch_begin_event(p0));
}
catch(...)
{
Glib::exception_handlers_invoke();
}
}
}
BaseClassType *const base = static_cast<BaseClassType*>(
g_type_class_peek_parent(G_OBJECT_GET_CLASS(self)) // Get the parent class of the object class (The original underlying C class).
);
// Call the original underlying C function:
if(base && base->touch_begin_event)
return (*base->touch_begin_event)(self, p0);
typedef gboolean RType;
return RType();
}
gboolean Widget_Class::touch_update_callback(GtkWidget* self, GdkEventTouch* p0)
{
Glib::ObjectBase *const obj_base = static_cast<Glib::ObjectBase*>(
Glib::ObjectBase::_get_current_wrapper((GObject*)self));
// Non-gtkmmproc-generated custom classes implicitly call the default
// Glib::ObjectBase constructor, which sets is_derived_. But gtkmmproc-
// generated classes can use this optimisation, which avoids the unnecessary
// parameter conversions if there is no possibility of the virtual function
// being overridden:
if(obj_base && obj_base->is_derived_())
{
CppObjectType *const obj = dynamic_cast<CppObjectType* const>(obj_base);
if(obj) // This can be NULL during destruction.
{
try // Trap C++ exceptions which would normally be lost because this is a C callback.
{
// Call the virtual member method, which derived classes might override.
return static_cast<int>(obj->on_touch_update_event(p0));
}
catch(...)
{
Glib::exception_handlers_invoke();
}
}
}
BaseClassType *const base = static_cast<BaseClassType*>(
g_type_class_peek_parent(G_OBJECT_GET_CLASS(self)) // Get the parent class of the object class (The original underlying C class).
);
// Call the original underlying C function:
if(base && base->touch_update_event)
return (*base->touch_update_event)(self, p0);
typedef gboolean RType;
return RType();
}
gboolean Widget_Class::touch_end_callback(GtkWidget* self, GdkEventTouch* p0)
{
Glib::ObjectBase *const obj_base = static_cast<Glib::ObjectBase*>(
Glib::ObjectBase::_get_current_wrapper((GObject*)self));
// Non-gtkmmproc-generated custom classes implicitly call the default
// Glib::ObjectBase constructor, which sets is_derived_. But gtkmmproc-
// generated classes can use this optimisation, which avoids the unnecessary
// parameter conversions if there is no possibility of the virtual function
// being overridden:
if(obj_base && obj_base->is_derived_())
{
CppObjectType *const obj = dynamic_cast<CppObjectType* const>(obj_base);
if(obj) // This can be NULL during destruction.
{
try // Trap C++ exceptions which would normally be lost because this is a C callback.
{
// Call the virtual member method, which derived classes might override.
return static_cast<int>(obj->on_touch_end_event(p0));
}
catch(...)
{
Glib::exception_handlers_invoke();
}
}
}
BaseClassType *const base = static_cast<BaseClassType*>(
g_type_class_peek_parent(G_OBJECT_GET_CLASS(self)) // Get the parent class of the object class (The original underlying C class).
);
// Call the original underlying C function:
if(base && base->touch_end_event)
return (*base->touch_end_event)(self, p0);
typedef gboolean RType;
return RType();
}
gboolean Widget_Class::scroll_event_callback(GtkWidget* self, GdkEventScroll* p0)
{
Glib::ObjectBase *const obj_base = static_cast<Glib::ObjectBase*>(
@ -7516,6 +7633,42 @@ bool Gtk::Widget::on_button_release_event(GdkEventButton* event)
typedef bool RType;
return RType();
}
bool Gtk::Widget::on_touch_begin_event(GdkEventTouch* event)
{
BaseClassType *const base = static_cast<BaseClassType*>(
g_type_class_peek_parent(G_OBJECT_GET_CLASS(gobject_)) // Get the parent class of the object class (The original underlying C class).
);
if(base && base->touch_begin_event)
return (*base->touch_begin_event)(gobj(),event);
typedef bool RType;
return RType();
}
bool Gtk::Widget::on_touch_update_event(GdkEventTouch* event)
{
BaseClassType *const base = static_cast<BaseClassType*>(
g_type_class_peek_parent(G_OBJECT_GET_CLASS(gobject_)) // Get the parent class of the object class (The original underlying C class).
);
if(base && base->touch_update_event)
return (*base->touch_update_event)(gobj(),event);
typedef bool RType;
return RType();
}
bool Gtk::Widget::on_touch_end_event(GdkEventTouch* event)
{
BaseClassType *const base = static_cast<BaseClassType*>(
g_type_class_peek_parent(G_OBJECT_GET_CLASS(gobject_)) // Get the parent class of the object class (The original underlying C class).
);
if(base && base->touch_end_event)
return (*base->touch_end_event)(gobj(),event);
typedef bool RType;
return RType();
}
bool Gtk::Widget::on_scroll_event(GdkEventScroll* event)
{
BaseClassType *const base = static_cast<BaseClassType*>(

View File

@ -90,6 +90,9 @@ protected:
static gboolean drag_motion_callback(GtkWidget* self, GdkDragContext* p0, gint p1, gint p2, guint p3);
static gboolean drag_drop_callback(GtkWidget* self, GdkDragContext* p0, gint p1, gint p2, guint p3);
static void drag_data_received_callback(GtkWidget* self, GdkDragContext* p0, gint p1, gint p2, GtkSelectionData* p3, guint p4, guint p5);
static gboolean touch_begin_callback(GtkWidget*, GdkEventTouch*);
static gboolean touch_update_callback(GtkWidget*, GdkEventTouch*);
static gboolean touch_end_callback(GtkWidget*, GdkEventTouch*);
#ifdef GTKMM_ATKMM_ENABLED
#ifndef GTKMM_DISABLE_DEPRECATED

View File

@ -490,6 +490,9 @@ protected:
virtual bool on_drag_drop(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, guint time);
/// This is a default handler for the signal signal_drag_data_received().
virtual void on_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const SelectionData& selection_data, guint info, guint time);
virtual bool on_touch_begin_event(GdkEventTouch* event);
virtual bool on_touch_update_event(GdkEventTouch* event);
virtual bool on_touch_end_event(GdkEventTouch* event);
#ifdef GTKMM_ATKMM_ENABLED
/// This is a default handler for the signal signal_get_accessible().
virtual Glib::RefPtr<Atk::Object> on_get_accessible();

View File

@ -309,6 +309,11 @@ WaveView::get_item_and_draw_rect_in_window_coords (Rect const& canvas_rect, Rect
double const width = region_length() / _props->samples_per_pixel;
item_rect = item_to_window (Rect (0.0, 0.0, width, _props->height), false);
item_rect.x0 = floor (item_rect.x0);
item_rect.x1 = ceil (item_rect.x1);
item_rect.y0 = round (item_rect.y0);
item_rect.y1 = round (item_rect.y1);
/* Now lets get the intersection with the area we've been asked to draw */
draw_rect = item_rect.intersection (canvas_rect);
@ -317,12 +322,6 @@ WaveView::get_item_and_draw_rect_in_window_coords (Rect const& canvas_rect, Rect
// No intersection with drawing area
return false;
}
item_rect.x0 = floor (item_rect.x0);
item_rect.x1 = ceil (item_rect.x1);
item_rect.y0 = round (item_rect.y0);
item_rect.y1 = round (item_rect.y1);
/* draw_rect now defines the rectangle we need to update/render the waveview
* into, in window coordinate space.
*

View File

@ -405,7 +405,7 @@ WaveViewDrawingThread::start ()
{
assert (!_thread);
_thread = PBD::Thread::create (&WaveViewThreads::thread_proc);
_thread = PBD::Thread::create (&WaveViewThreads::thread_proc, "WaveViewDrawing");
}
void
@ -477,8 +477,6 @@ WaveViewThreads::thread_proc ()
void
WaveViewThreads::_thread_proc ()
{
pthread_set_name ("WaveViewDrawing");
while (true) {
_queue_mutex.lock ();

View File

@ -103,6 +103,7 @@ ArdourButton::ArdourButton (Element e, bool toggle)
UIConfigurationBase::instance().ColorsChanged.connect (sigc::mem_fun (*this, &ArdourButton::color_handler));
/* This is not provided by gtkmm */
signal_grab_broken_event().connect (sigc::mem_fun (*this, &ArdourButton::on_grab_broken_event));
add_events (Gdk::TOUCH_BEGIN_MASK | Gdk::TOUCH_END_MASK);
}
ArdourButton::ArdourButton (const std::string& str, Element e, bool toggle)
@ -154,6 +155,7 @@ ArdourButton::ArdourButton (const std::string& str, Element e, bool toggle)
UIConfigurationBase::instance().DPIReset.connect (sigc::mem_fun (*this, &ArdourButton::on_name_changed));
/* This is not provided by gtkmm */
signal_grab_broken_event().connect (sigc::mem_fun (*this, &ArdourButton::on_grab_broken_event));
add_events (Gdk::TOUCH_BEGIN_MASK | Gdk::TOUCH_END_MASK);
}
ArdourButton::~ArdourButton()
@ -943,6 +945,42 @@ ArdourButton::set_led_left (bool yn)
_led_left = yn;
}
bool
ArdourButton::on_touch_begin_event (GdkEventTouch *ev)
{
printf ("ArdourButton::on_touch_begin_event finger %d\n", ev->sequence);
focus_handler (this);
CairoWidget::set_dirty ();
if (!_act_on_release) {
if (_action) {
_action->activate ();
} else if (_auto_toggle) {
set_active (!get_active ());
signal_clicked ();
}
}
return true;
}
bool
ArdourButton::on_touch_end_event (GdkEventTouch *ev)
{
printf ("ArdourButton::on_touch_end_event finger: %d\n", ev->sequence);
CairoWidget::set_dirty ();
if (_act_on_release && _auto_toggle && !_action) {
set_active (!get_active ());
}
signal_clicked ();
if (_act_on_release && _action) {
_action->activate ();
}
return true;
}
bool
ArdourButton::on_button_press_event (GdkEventButton *ev)
{

View File

@ -61,6 +61,8 @@ ArdourFader::ArdourFader (Gtk::Adjustment& adj, int orientation, int fader_lengt
{
update_unity_position ();
add_events (Gdk::TOUCH_UPDATE_MASK);
if (_orien == VERT) {
CairoWidget::set_size_request(_girth, _span);
} else {
@ -464,6 +466,18 @@ ArdourFader::on_motion_notify_event (GdkEventMotion* ev)
return true;
}
bool
ArdourFader::on_touch_update_event (GdkEventTouch* ev)
{
GdkEventMotion mev;
mev.window = ev->window;
mev.time = ev->time;
mev.x = ev->x;
mev.y = ev->y;
mev.state = 0;
return ArdourFader::on_motion_notify_event (&mev);
}
/** @return pixel offset of the current value from the right or bottom of the fader */
int
ArdourFader::display_span ()

View File

@ -41,6 +41,8 @@ FaderWidget::FaderWidget (Gtk::Adjustment& adj, int orien)
| Gdk::SCROLL_MASK
| Gdk::ENTER_NOTIFY_MASK
| Gdk::LEAVE_NOTIFY_MASK
| Gdk::TOUCH_BEGIN_MASK
| Gdk::TOUCH_END_MASK
);
_adjustment.signal_value_changed().connect (mem_fun (*this, &FaderWidget::adjustment_changed));
@ -100,6 +102,29 @@ FaderWidget::on_button_press_event (GdkEventButton* ev)
return (_tweaks & NoButtonForward) ? true : false;
}
bool
FaderWidget::on_touch_begin_event (GdkEventTouch *ev)
{
StartGesture (0);
_grab_loc = (_orien == VERT) ? ev->y : ev->x;
_grab_start = (_orien == VERT) ? ev->y : ev->x;
_grab_window = ev->window;
_dragging = true;
return true;
}
bool
FaderWidget::on_touch_end_event (GdkEventTouch *ev)
{
if (!_dragging) {
return true;
}
_dragging = false;
StopGesture (0);
return true;
}
bool
FaderWidget::on_enter_notify_event (GdkEventCrossing*)
{

View File

@ -124,6 +124,8 @@ class LIBWIDGETS_API ArdourButton : public CairoWidget , public Gtkmm2ext::Activ
bool on_button_press_event (GdkEventButton*);
bool on_button_release_event (GdkEventButton*);
bool on_touch_begin_event (GdkEventTouch*);
bool on_touch_end_event (GdkEventTouch*);
void set_image (const Glib::RefPtr<Gdk::Pixbuf>&);

View File

@ -55,6 +55,7 @@ protected:
void render (Cairo::RefPtr<Cairo::Context> const&, cairo_rectangle_t*);
bool on_motion_notify_event (GdkEventMotion*);
bool on_touch_update_event (GdkEventTouch*);
void on_state_changed (Gtk::StateType);
void on_style_changed (const Glib::RefPtr<Gtk::Style>&);

View File

@ -62,6 +62,8 @@ public:
protected:
bool on_button_press_event (GdkEventButton*);
bool on_button_release_event (GdkEventButton*);
bool on_touch_begin_event (GdkEventTouch*);
bool on_touch_end_event (GdkEventTouch*);
bool on_enter_notify_event (GdkEventCrossing* ev);
bool on_leave_notify_event (GdkEventCrossing* ev);
bool on_scroll_event (GdkEventScroll* ev);