# HG changeset patch # User Chris Cannam # Date 1580222053 0 # Node ID 1cd161242250838dd87384ff55b59d4a23f30269 # Parent 07a8793a0388daa40307b80a744ce9bd7f6860de# Parent 0bc6caf87658eb1e4b00a1b0511c1e9b7b7e5560 Merge diff -r 07a8793a0388 -r 1cd161242250 base/DataExportOptions.h --- a/base/DataExportOptions.h Tue Jan 28 14:32:34 2020 +0000 +++ b/base/DataExportOptions.h Tue Jan 28 14:34:13 2020 +0000 @@ -18,8 +18,36 @@ enum DataExportOption { DataExportDefaults = 0x0, + + /** + * Export sparse event-based models as if they were dense models, + * writing an event at every interval of the model's + * resolution. Where no event is present in the actual model, a + * constant "fill event" is interpolated instead. + */ DataExportFillGaps = 0x1, - DataExportOmitLevels = 0x2, + + /** + * Omit the level attribute from exported events. + */ + DataExportOmitLevel = 0x2, + + /** + * Always include a timestamp in the first column. Otherwise + * timestamps will only be included in sparse models. + */ + DataExportAlwaysIncludeTimestamp = 0x4, + + /** + * Use sample frames rather than seconds for time and duration + * values. + */ + DataExportWriteTimeInFrames = 0x8, + + /** + * Write a header row before any data rows. + */ + DataExportIncludeHeader = 0x10 }; typedef int DataExportOptions; diff -r 07a8793a0388 -r 1cd161242250 base/Event.h --- a/base/Event.h Tue Jan 28 14:32:34 2020 +0000 +++ b/base/Event.h Tue Jan 28 14:34:13 2020 +0000 @@ -366,25 +366,71 @@ return n; } + QString getDelimitedDataHeaderLine(QString delimiter, + DataExportOptions opts, + ExportNameOptions nameOpts) const { + + QStringList list; + + // These are considered API rather than human-readable text - + // they shouldn't be translated + + if (opts & DataExportWriteTimeInFrames) { + list << "frame"; + } else { + list << "time"; + } + + if (m_haveValue) { + list << nameOpts.valueAttributeName; + } + + if (m_haveDuration) { + list << "duration"; + } + + if (m_haveLevel) { + if (!(opts & DataExportOmitLevel)) { + list << nameOpts.levelAttributeName; + } + } + + if (m_uri != "") { + list << nameOpts.uriAttributeName; + } + + list << "label"; + + return list.join(delimiter).toUpper(); + } + QString toDelimitedDataString(QString delimiter, DataExportOptions opts, sv_samplerate_t sampleRate) const { QStringList list; - list << RealTime::frame2RealTime(m_frame, sampleRate) - .toString().c_str(); + if (opts & DataExportWriteTimeInFrames) { + list << QString("%1").arg(m_frame); + } else { + list << RealTime::frame2RealTime(m_frame, sampleRate) + .toString().c_str(); + } if (m_haveValue) { list << QString("%1").arg(m_value); } if (m_haveDuration) { - list << RealTime::frame2RealTime(m_duration, sampleRate) - .toString().c_str(); + if (opts & DataExportWriteTimeInFrames) { + list << QString("%1").arg(m_duration); + } else { + list << RealTime::frame2RealTime(m_duration, sampleRate) + .toString().c_str(); + } } if (m_haveLevel) { - if (!(opts & DataExportOmitLevels)) { + if (!(opts & DataExportOmitLevel)) { list << QString("%1").arg(m_level); } } diff -r 07a8793a0388 -r 1cd161242250 base/EventSeries.cpp --- a/base/EventSeries.cpp Tue Jan 28 14:32:34 2020 +0000 +++ b/base/EventSeries.cpp Tue Jan 28 14:34:13 2020 +0000 @@ -598,6 +598,20 @@ } QString +EventSeries::getDelimitedDataHeaderLine(QString delimiter, + DataExportOptions opts, + Event::ExportNameOptions nopts) const +{ + if (m_events.empty()) { + return QString(); + } else { + return m_events.begin()->getDelimitedDataHeaderLine(delimiter, + opts, + nopts); + } +} + +QString EventSeries::toDelimitedDataString(QString delimiter, DataExportOptions options, sv_frame_t startFrame, diff -r 07a8793a0388 -r 1cd161242250 base/EventSeries.h --- a/base/EventSeries.h Tue Jan 28 14:32:34 2020 +0000 +++ b/base/EventSeries.h Tue Jan 28 14:34:13 2020 +0000 @@ -221,6 +221,14 @@ Event::ExportNameOptions) const; /** + * Emit a label for each column that would be written by + * toDelimitedDataString, separated by the given delimiter. + */ + QString getDelimitedDataHeaderLine(QString delimiter, + DataExportOptions options, + Event::ExportNameOptions) const; + + /** * Emit events starting within the given range to a delimited * (e.g. comma-separated) data format. */ diff -r 07a8793a0388 -r 1cd161242250 base/Selection.cpp --- a/base/Selection.cpp Tue Jan 28 14:32:34 2020 +0000 +++ b/base/Selection.cpp Tue Jan 28 14:34:13 2020 +0000 @@ -227,3 +227,15 @@ stream << indent << "\n"; } +QString +MultiSelection::toString() const +{ + QStringList list; + for (SelectionList::iterator i = m_selections.begin(); + i != m_selections.end(); ++i) { + list << QString("(%1,%2)") + .arg(i->getStartFrame()) + .arg(i->getEndFrame()); + } + return "(" + list.join(",") + ")"; +} diff -r 07a8793a0388 -r 1cd161242250 base/Selection.h --- a/base/Selection.h Tue Jan 28 14:32:34 2020 +0000 +++ b/base/Selection.h Tue Jan 28 14:34:13 2020 +0000 @@ -75,18 +75,22 @@ void clearSelections(); void getExtents(sv_frame_t &startFrame, sv_frame_t &endFrame) const; - + /** * Return the selection that contains a given frame. * If defaultToFollowing is true, and if the frame is not in a * selected area, return the next selection after the given frame. * Return the empty selection if no appropriate selection is found. */ - Selection getContainingSelection(sv_frame_t frame, bool defaultToFollowing) const; + Selection getContainingSelection(sv_frame_t frame, + bool defaultToFollowing) const; void toXml(QTextStream &stream, QString indent = "", - QString extraAttributes = "") const override; + QString extraAttributes = "") const override; + // Debug only, not interop + QString toString() const; + protected: SelectionList m_selections; }; diff -r 07a8793a0388 -r 1cd161242250 data/fileio/CSVFileWriter.cpp --- a/data/fileio/CSVFileWriter.cpp Tue Jan 28 14:32:34 2020 +0000 +++ b/data/fileio/CSVFileWriter.cpp Tue Jan 28 14:34:13 2020 +0000 @@ -85,6 +85,11 @@ QTextStream out(&file); + if (m_options & DataExportIncludeHeader) { + out << m_model->getDelimitedDataHeaderLine(m_delimiter, m_options) + << endl; + } + sv_frame_t blockSize = 65536; if (m_model->isSparse()) { diff -r 07a8793a0388 -r 1cd161242250 data/fileio/MIDIFileReader.cpp --- a/data/fileio/MIDIFileReader.cpp Tue Jan 28 14:32:34 2020 +0000 +++ b/data/fileio/MIDIFileReader.cpp Tue Jan 28 14:34:13 2020 +0000 @@ -934,7 +934,7 @@ } if (!model) { - model = new NoteModel(m_mainModelSampleRate, 1, 0.0, 0.0, false); + model = new NoteModel(m_mainModelSampleRate, 1, false); model->setValueQuantization(1.0); model->setObjectName(QFileInfo(m_path).fileName()); } diff -r 07a8793a0388 -r 1cd161242250 data/fileio/MIDIFileWriter.h --- a/data/fileio/MIDIFileWriter.h Tue Jan 28 14:32:34 2020 +0000 +++ b/data/fileio/MIDIFileWriter.h Tue Jan 28 14:34:13 2020 +0000 @@ -48,12 +48,12 @@ const NoteExportable *exportable, sv_samplerate_t sampleRate, // used to convert exportable sample timings float tempo = 120.f); - virtual ~MIDIFileWriter(); + ~MIDIFileWriter(); - virtual bool isOK() const; - virtual QString getError() const; + bool isOK() const; + QString getError() const; - virtual void write(); + void write(); protected: typedef std::vector MIDITrack; diff -r 07a8793a0388 -r 1cd161242250 data/model/AlignmentModel.h --- a/data/model/AlignmentModel.h Tue Jan 28 14:32:34 2020 +0000 +++ b/data/model/AlignmentModel.h Tue Jan 28 14:34:13 2020 +0000 @@ -77,6 +77,10 @@ QString indent = "", QString extraAttributes = "") const override; + QString getDelimitedDataHeaderLine(QString, DataExportOptions) const override { + return ""; + } + QString toDelimitedDataString(QString, DataExportOptions, sv_frame_t, sv_frame_t) const override { return ""; diff -r 07a8793a0388 -r 1cd161242250 data/model/BasicCompressedDenseThreeDimensionalModel.cpp --- a/data/model/BasicCompressedDenseThreeDimensionalModel.cpp Tue Jan 28 14:32:34 2020 +0000 +++ b/data/model/BasicCompressedDenseThreeDimensionalModel.cpp Tue Jan 28 14:34:13 2020 +0000 @@ -499,6 +499,17 @@ } QString +BasicCompressedDenseThreeDimensionalModel::getDelimitedDataHeaderLine(QString delimiter, + DataExportOptions) const +{ + QStringList list; + for (int i = 0; i < m_yBinCount; ++i) { + list << QString("Bin%1").arg(i+1); + } + return list.join(delimiter); +} + +QString BasicCompressedDenseThreeDimensionalModel::toDelimitedDataString(QString delimiter, DataExportOptions, sv_frame_t startFrame, diff -r 07a8793a0388 -r 1cd161242250 data/model/BasicCompressedDenseThreeDimensionalModel.h --- a/data/model/BasicCompressedDenseThreeDimensionalModel.h Tue Jan 28 14:32:34 2020 +0000 +++ b/data/model/BasicCompressedDenseThreeDimensionalModel.h Tue Jan 28 14:34:13 2020 +0000 @@ -181,6 +181,9 @@ QString getTypeName() const override { return tr("Editable Dense 3-D"); } + QString getDelimitedDataHeaderLine(QString delimiter, + DataExportOptions opts) const override; + QString toDelimitedDataString(QString delimiter, DataExportOptions options, sv_frame_t startFrame, diff -r 07a8793a0388 -r 1cd161242250 data/model/BoxModel.h --- a/data/model/BoxModel.h Tue Jan 28 14:32:34 2020 +0000 +++ b/data/model/BoxModel.h Tue Jan 28 14:34:13 2020 +0000 @@ -348,8 +348,26 @@ m_events.toXml(out, indent, QString("dimensions=\"2\""), options); } + QString getDelimitedDataHeaderLine(QString delimiter, + DataExportOptions opts) const override { + QStringList list; + + // These are considered API rather than human-readable text - + // they shouldn't be translated + + if (opts & DataExportWriteTimeInFrames) { + list << "startframe" << "endframe"; + } else { + list << "start" << "end"; + } + + list << "extent start" << "extent end" << "label"; + + return list.join(delimiter).toUpper(); + } + QString toDelimitedDataString(QString delimiter, - DataExportOptions, + DataExportOptions opts, sv_frame_t startFrame, sv_frame_t duration) const override { @@ -363,14 +381,25 @@ QStringList list; - list << RealTime::frame2RealTime - (e.getFrame(), getSampleRate()) - .toString().c_str() - << RealTime::frame2RealTime - (e.getFrame() + e.getDuration(), getSampleRate()) - .toString().c_str() - << QString("%1").arg(e.getValue()) - << QString("%1").arg(e.getValue() + fabsf(e.getLevel())); + if (opts & DataExportWriteTimeInFrames) { + + list << QString("%1").arg(e.getFrame()); + list << QString("%1").arg(e.getFrame() + e.getDuration()); + + } else { + + list << RealTime::frame2RealTime + (e.getFrame(), getSampleRate()) + .toString().c_str(); + + list << RealTime::frame2RealTime + (e.getFrame() + e.getDuration(), getSampleRate()) + .toString().c_str(); + } + + list << QString("%1").arg(e.getValue()); + + list << QString("%1").arg(e.getValue() + fabsf(e.getLevel())); if (e.getLabel() != "") { list << e.getLabel(); diff -r 07a8793a0388 -r 1cd161242250 data/model/Dense3DModelPeakCache.h --- a/data/model/Dense3DModelPeakCache.h Tue Jan 28 14:32:34 2020 +0000 +++ b/data/model/Dense3DModelPeakCache.h Tue Jan 28 14:34:13 2020 +0000 @@ -119,6 +119,10 @@ return source ? source->getCompletion() : 100; } + QString getDelimitedDataHeaderLine(QString, DataExportOptions) const override { + return ""; + } + QString toDelimitedDataString(QString, DataExportOptions, sv_frame_t, sv_frame_t) const override { return ""; diff -r 07a8793a0388 -r 1cd161242250 data/model/DenseTimeValueModel.cpp --- a/data/model/DenseTimeValueModel.cpp Tue Jan 28 14:32:34 2020 +0000 +++ b/data/model/DenseTimeValueModel.cpp Tue Jan 28 14:34:13 2020 +0000 @@ -16,7 +16,19 @@ #include "DenseTimeValueModel.h" #include - + +QString +DenseTimeValueModel::getDelimitedDataHeaderLine(QString delimiter, + DataExportOptions) const +{ + int ch = getChannelCount(); + QStringList list; + for (int i = 0; i < ch; ++i) { + list << QString("Channel%1").arg(i+1); + } + return list.join(delimiter); +} + QString DenseTimeValueModel::toDelimitedDataString(QString delimiter, DataExportOptions, diff -r 07a8793a0388 -r 1cd161242250 data/model/DenseTimeValueModel.h --- a/data/model/DenseTimeValueModel.h Tue Jan 28 14:32:34 2020 +0000 +++ b/data/model/DenseTimeValueModel.h Tue Jan 28 14:34:13 2020 +0000 @@ -82,6 +82,9 @@ bool canPlay() const override { return true; } QString getDefaultPlayClipId() const override { return ""; } + QString getDelimitedDataHeaderLine(QString delimiter, + DataExportOptions options) const override; + QString toDelimitedDataString(QString delimiter, DataExportOptions options, sv_frame_t startFrame, diff -r 07a8793a0388 -r 1cd161242250 data/model/EditableDenseThreeDimensionalModel.cpp --- a/data/model/EditableDenseThreeDimensionalModel.cpp Tue Jan 28 14:32:34 2020 +0000 +++ b/data/model/EditableDenseThreeDimensionalModel.cpp Tue Jan 28 14:34:13 2020 +0000 @@ -352,6 +352,17 @@ } QString +EditableDenseThreeDimensionalModel::getDelimitedDataHeaderLine(QString delimiter, + DataExportOptions) const +{ + QStringList list; + for (int i = 0; i < m_yBinCount; ++i) { + list << QString("Bin%1").arg(i+1); + } + return list.join(delimiter); +} + +QString EditableDenseThreeDimensionalModel::toDelimitedDataString(QString delimiter, DataExportOptions, sv_frame_t startFrame, diff -r 07a8793a0388 -r 1cd161242250 data/model/EditableDenseThreeDimensionalModel.h --- a/data/model/EditableDenseThreeDimensionalModel.h Tue Jan 28 14:32:34 2020 +0000 +++ b/data/model/EditableDenseThreeDimensionalModel.h Tue Jan 28 14:34:13 2020 +0000 @@ -172,6 +172,9 @@ QString getTypeName() const override { return tr("Editable Dense 3-D"); } + QString getDelimitedDataHeaderLine(QString delimiter, + DataExportOptions opts) const override; + QString toDelimitedDataString(QString delimiter, DataExportOptions options, sv_frame_t startFrame, diff -r 07a8793a0388 -r 1cd161242250 data/model/FFTModel.h --- a/data/model/FFTModel.h Tue Jan 28 14:32:34 2020 +0000 +++ b/data/model/FFTModel.h Tue Jan 28 14:34:13 2020 +0000 @@ -99,6 +99,9 @@ float getBinValue(int n) const override; QString getBinName(int n) const override; + QString getDelimitedDataHeaderLine(QString, DataExportOptions) const override { + return ""; + } QString toDelimitedDataString(QString, DataExportOptions, sv_frame_t, sv_frame_t) const override { return ""; diff -r 07a8793a0388 -r 1cd161242250 data/model/ImageModel.h --- a/data/model/ImageModel.h Tue Jan 28 14:32:34 2020 +0000 +++ b/data/model/ImageModel.h Tue Jan 28 14:34:13 2020 +0000 @@ -275,6 +275,16 @@ m_events.toXml(out, indent, QString("dimensions=\"1\""), options); } + QString getDelimitedDataHeaderLine(QString delimiter, + DataExportOptions options) const override { + Event::ExportNameOptions nameOpts; + nameOpts.uriAttributeName = "image"; + + return m_events.getDelimitedDataHeaderLine(delimiter, + options, + nameOpts); + } + QString toDelimitedDataString(QString delimiter, DataExportOptions options, sv_frame_t startFrame, diff -r 07a8793a0388 -r 1cd161242250 data/model/Model.h --- a/data/model/Model.h Tue Jan 28 14:32:34 2020 +0000 +++ b/data/model/Model.h Tue Jan 28 14:34:13 2020 +0000 @@ -274,6 +274,17 @@ QString indent = "", QString extraAttributes = "") const override; + /** + * Emit a label for each column that would be written by + * toDelimitedDataString, separated by the given delimiter. + */ + virtual QString getDelimitedDataHeaderLine(QString delimiter, + DataExportOptions options) const = 0; + + /** + * Emit the contents of the model within the given range to a + * delimited (e.g. comma-separated) data format. + */ virtual QString toDelimitedDataString(QString delimiter, DataExportOptions options, sv_frame_t startFrame, diff -r 07a8793a0388 -r 1cd161242250 data/model/NoteModel.h --- a/data/model/NoteModel.h Tue Jan 28 14:32:34 2020 +0000 +++ b/data/model/NoteModel.h Tue Jan 28 14:34:13 2020 +0000 @@ -197,6 +197,9 @@ return m_events.getNearestEventMatching (startSearchAt, predicate, direction, found); } + int getIndexForEvent(const Event &e) { + return m_events.getIndexForEvent(e); + } /** * Editing methods. @@ -405,6 +408,13 @@ m_events.toXml(out, indent, QString("dimensions=\"3\"")); } + QString getDelimitedDataHeaderLine(QString delimiter, + DataExportOptions options) const override { + return m_events.getDelimitedDataHeaderLine(delimiter, + options, + Event::ExportNameOptions()); + } + QString toDelimitedDataString(QString delimiter, DataExportOptions options, sv_frame_t startFrame, diff -r 07a8793a0388 -r 1cd161242250 data/model/RegionModel.h --- a/data/model/RegionModel.h Tue Jan 28 14:32:34 2020 +0000 +++ b/data/model/RegionModel.h Tue Jan 28 14:34:13 2020 +0000 @@ -340,6 +340,13 @@ m_events.toXml(out, indent, QString("dimensions=\"3\"")); } + QString getDelimitedDataHeaderLine(QString delimiter, + DataExportOptions options) const override { + return m_events.getDelimitedDataHeaderLine(delimiter, + options, + Event::ExportNameOptions()); + } + QString toDelimitedDataString(QString delimiter, DataExportOptions options, sv_frame_t startFrame, diff -r 07a8793a0388 -r 1cd161242250 data/model/SparseOneDimensionalModel.h --- a/data/model/SparseOneDimensionalModel.h Tue Jan 28 14:32:34 2020 +0000 +++ b/data/model/SparseOneDimensionalModel.h Tue Jan 28 14:34:13 2020 +0000 @@ -308,6 +308,13 @@ m_events.toXml(out, indent, QString("dimensions=\"1\"")); } + QString getDelimitedDataHeaderLine(QString delimiter, + DataExportOptions options) const override { + return m_events.getDelimitedDataHeaderLine(delimiter, + options, + Event::ExportNameOptions()); + } + QString toDelimitedDataString(QString delimiter, DataExportOptions options, sv_frame_t startFrame, diff -r 07a8793a0388 -r 1cd161242250 data/model/SparseTimeValueModel.h --- a/data/model/SparseTimeValueModel.h Tue Jan 28 14:32:34 2020 +0000 +++ b/data/model/SparseTimeValueModel.h Tue Jan 28 14:34:13 2020 +0000 @@ -347,6 +347,13 @@ m_events.toXml(out, indent, QString("dimensions=\"2\"")); } + QString getDelimitedDataHeaderLine(QString delimiter, + DataExportOptions options) const override { + return m_events.getDelimitedDataHeaderLine(delimiter, + options, + Event::ExportNameOptions()); + } + QString toDelimitedDataString(QString delimiter, DataExportOptions options, sv_frame_t startFrame, diff -r 07a8793a0388 -r 1cd161242250 data/model/TextModel.h --- a/data/model/TextModel.h Tue Jan 28 14:32:34 2020 +0000 +++ b/data/model/TextModel.h Tue Jan 28 14:34:13 2020 +0000 @@ -278,6 +278,15 @@ m_events.toXml(out, indent, QString("dimensions=\"2\""), options); } + QString getDelimitedDataHeaderLine(QString delimiter, + DataExportOptions options) const override { + Event::ExportNameOptions nameOpts; + nameOpts.valueAttributeName = "height"; + return m_events.getDelimitedDataHeaderLine(delimiter, + options, + nameOpts); + } + QString toDelimitedDataString(QString delimiter, DataExportOptions options, sv_frame_t startFrame, diff -r 07a8793a0388 -r 1cd161242250 data/osc/OSCQueue.cpp --- a/data/osc/OSCQueue.cpp Tue Jan 28 14:32:34 2020 +0000 +++ b/data/osc/OSCQueue.cpp Tue Jan 28 14:34:13 2020 +0000 @@ -34,8 +34,8 @@ void OSCQueue::oscError(int num, const char *msg, const char *path) { - cerr << "ERROR: OSCQueue::oscError: liblo server error " << num - << " in path " << path << ": " << msg << endl; + SVCERR << "ERROR: OSCQueue::oscError: liblo server error " << num + << " in path " << path << ": " << msg << endl; } int @@ -175,8 +175,7 @@ OSCMessage *message = m_buffer.readOne(); OSCMessage rmessage = *message; delete message; - SVDEBUG << "OSCQueue::readMessage: In thread " - << QThread::currentThreadId() << ": message follows:\n" + SVDEBUG << "OSCQueue::readMessage[" << QThread::currentThreadId() << "]: " << rmessage.toString() << endl; return rmessage; } diff -r 07a8793a0388 -r 1cd161242250 system/System.cpp --- a/system/System.cpp Tue Jan 28 14:32:34 2020 +0000 +++ b/system/System.cpp Tue Jan 28 14:34:13 2020 +0000 @@ -27,6 +27,10 @@ #include #endif +#ifdef _MSC_VER +#include +#endif + #ifdef __APPLE__ #include #include @@ -334,6 +338,39 @@ } } +bool +OSReportsDarkThemeActive() +{ + SVCERR << "OSReportsDarkThemeActive() called" << endl; +#ifdef _MSC_VER + using namespace winrt::Windows::UI::ViewManagement; + UISettings settings; + auto background = settings.GetColorValue(UIColorType::Background); + if (int(background.R) + int(background.G) + int(background.B) < 384) { + return true; + } +#endif + return false; +} + +bool +OSQueryAccentColour(int &r, int &g, int &b) +{ + SVCERR << "OSQueryAccentColour() called" << endl; +#ifdef _MSC_VER + using namespace winrt::Windows::UI::ViewManagement; + bool dark = OSReportsDarkThemeActive(); + UISettings settings; + auto accent = settings.GetColorValue + (dark ? UIColorType::AccentLight1 : UIColorType::Accent); + r = accent.R; + g = accent.G; + b = accent.B; + return true; +#endif + return false; +} + double mod(double x, double y) { return x - (y * floor(x / y)); } float modf(float x, float y) { return x - (y * floorf(x / y)); } diff -r 07a8793a0388 -r 1cd161242250 system/System.h --- a/system/System.h Tue Jan 28 14:32:34 2020 +0000 +++ b/system/System.h Tue Jan 28 14:34:13 2020 +0000 @@ -163,6 +163,17 @@ // if unknown. (Hence signed return type) extern ssize_t GetDiscSpaceMBAvailable(const char *path); +// Return true if the OS desktop is set to use a dark mode +// theme. Return false if it is set to a light theme or if the theme +// is unknown. +extern bool OSReportsDarkThemeActive(); + +// Return true if the OS desktop reports an accent colour to go with +// the current theme; if so, also return by reference the r, g, and b +// components of the colour (range 0-255). Return false if we can't +// query such a thing. +extern bool OSQueryAccentColour(int &r, int &g, int &b); + extern void StoreStartupLocale(); extern void RestoreStartupLocale();