<commit_before>/*
 *  sounddlg.cpp  -  sound file selection and configuration dialog
 *  Program:  kalarm
 *  Copyright © 2005-2007 by David Jarvie <software@astrojar.org.uk>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "kalarm.h"

#include <QLabel>
#include <QDir>
#include <QGroupBox>
#include <QApplication>
#include <QVBoxLayout>
#include <QGridLayout>
#include <QShowEvent>
#include <QResizeEvent>

#include <klocale.h>
#include <kstandarddirs.h>
#include <kiconloader.h>
#include <khbox.h>
#include <kio/netaccess.h>
#include <phonon/audioplayer.h>

#include "checkbox.h"
#include "functions.h"
#include "lineedit.h"
#include "pushbutton.h"
#include "slider.h"
#include "soundpicker.h"
#include "spinbox.h"
#include "sounddlg.moc"

// Collect these widget labels together to ensure consistent wording and
// translations across different modules.
QString SoundDlg::i18n_SetVolume()   { return i18n("Set volume"); }
QString SoundDlg::i18n_v_SetVolume() { return i18n("Set &volume"); }
QString SoundDlg::i18n_Repeat()      { return i18n("Repeat"); }
QString SoundDlg::i18n_p_Repeat()    { return i18n("Re&peat"); }

static const char SOUND_DIALOG_NAME[] = "SoundDialog";

SoundDlg::SoundDlg(const QString& file, float volume, float fadeVolume, int fadeSeconds, bool repeat,
                   const QString& caption, QWidget* parent)
	: KDialog(parent),
	  mPlayer(0),
	  mReadOnly(false)
{
	QWidget* page = new QWidget(this);
	setMainWidget(page);
	setCaption(caption);
	setButtons(Ok|Cancel);
	setDefaultButton(Ok);
	connect(this, SIGNAL(okClicked()), SLOT(slotOk()));

	QVBoxLayout* layout = new QVBoxLayout(page);
	layout->setMargin(0);
	layout->setSpacing(spacingHint());

	// File play button
	KHBox* box = new KHBox(page);
	box->setMargin(0);
	layout->addWidget(box);
	mFilePlay = new QPushButton(box);
	mFilePlay->setIcon(SmallIcon("media-playback-start"));
	mFilePlay->setFixedSize(mFilePlay->sizeHint());
	connect(mFilePlay, SIGNAL(clicked()), SLOT(playSound()));
	mFilePlay->setToolTip(i18n("Test the sound"));
	mFilePlay->setWhatsThis(i18n("Play the selected sound file."));

	// File name edit box
	mFileEdit = new LineEdit(LineEdit::Url, box);
	mFileEdit->setAcceptDrops(true);
	mFileEdit->setWhatsThis(i18n("Enter the name or URL of a sound file to play."));

	// File browse button
	mFileBrowseButton = new PushButton(box);
	mFileBrowseButton->setIcon(SmallIcon("document-open"));
	mFileBrowseButton->setFixedSize(mFileBrowseButton->sizeHint());
	connect(mFileBrowseButton, SIGNAL(clicked()), SLOT(slotPickFile()));
	mFileBrowseButton->setToolTip(i18n("Choose a file"));
	mFileBrowseButton->setWhatsThis(i18n("Select a sound file to play."));

	// Sound repetition checkbox
	mRepeatCheckbox = new CheckBox(i18n_p_Repeat(), page);
	mRepeatCheckbox->setFixedSize(mRepeatCheckbox->sizeHint());
	mRepeatCheckbox->setWhatsThis(i18n("If checked, the sound file will be played repeatedly for as long as the message is displayed."));
	layout->addWidget(mRepeatCheckbox);

	// Volume
	QGroupBox* group = new QGroupBox(i18n("Volume"), page);
	layout->addWidget(group);
	QGridLayout* grid = new QGridLayout(group);
	grid->setMargin(marginHint());
	grid->setSpacing(spacingHint());
	grid->setColumnStretch(2, 1);
	int indentWidth = 3 * KDialog::spacingHint();
	grid->setColumnMinimumWidth(0, indentWidth);
	grid->setColumnMinimumWidth(1, indentWidth);

	// 'Set volume' checkbox
	box = new KHBox(group);
	box->setMargin(0);
	box->setSpacing(spacingHint());
	grid->addWidget(box, 1, 0, 1, 3);
	mVolumeCheckbox = new CheckBox(i18n_v_SetVolume(), box);
	mVolumeCheckbox->setFixedSize(mVolumeCheckbox->sizeHint());
	connect(mVolumeCheckbox, SIGNAL(toggled(bool)), SLOT(slotVolumeToggled(bool)));
	mVolumeCheckbox->setWhatsThis(i18n("Select to choose the volume for playing the sound file."));

	// Volume slider
	mVolumeSlider = new Slider(0, 100, 10, Qt::Horizontal, box);
	mVolumeSlider->setTickPosition(QSlider::TicksBelow);
	mVolumeSlider->setTickInterval(10);
	mVolumeSlider->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
	mVolumeSlider->setWhatsThis(i18n("Choose the volume for playing the sound file."));
	mVolumeCheckbox->setFocusWidget(mVolumeSlider);

	// Fade checkbox
	mFadeCheckbox = new CheckBox(i18n("Fade"), group);
	mFadeCheckbox->setFixedSize(mFadeCheckbox->sizeHint());
	connect(mFadeCheckbox, SIGNAL(toggled(bool)), SLOT(slotFadeToggled(bool)));
	mFadeCheckbox->setWhatsThis(i18n("Select to fade the volume when the sound file first starts to play."));
	grid->addWidget(mFadeCheckbox, 2, 1, 1, 2, Qt::AlignLeft);

	// Fade time
	mFadeBox = new KHBox(group);
	mFadeBox->setMargin(0);
	mFadeBox->setSpacing(spacingHint());
	grid->addWidget(mFadeBox, 3, 2, Qt::AlignLeft);
	QLabel* label = new QLabel(i18nc("Time period over which to fade the sound", "Fade time:"), mFadeBox);
	label->setFixedSize(label->sizeHint());
	mFadeTime = new SpinBox(1, 999, mFadeBox);
	mFadeTime->setSingleShiftStep(10);
	mFadeTime->setFixedSize(mFadeTime->sizeHint());
	label->setBuddy(mFadeTime);
	label = new QLabel(i18n("seconds"), mFadeBox);
	label->setFixedSize(label->sizeHint());
	mFadeBox->setWhatsThis(i18n("Enter how many seconds to fade the sound before reaching the set volume."));

	// Fade slider
	mFadeVolumeBox = new KHBox(group);
	mFadeVolumeBox->setMargin(0);
	mFadeVolumeBox->setSpacing(spacingHint());
	grid->addWidget(mFadeVolumeBox, 4, 2);
	label = new QLabel(i18n("Initial volume:"), mFadeVolumeBox);
	label->setFixedSize(label->sizeHint());
	mFadeSlider = new Slider(0, 100, 10, Qt::Horizontal, mFadeVolumeBox);
	mFadeSlider->setTickPosition(QSlider::TicksBelow);
	mFadeSlider->setTickInterval(10);
	mFadeSlider->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
	label->setBuddy(mFadeSlider);
	mFadeVolumeBox->setWhatsThis(i18n("Choose the initial volume for playing the sound file."));

	// Restore the dialogue size from last time
	QSize s;
	if (KAlarm::readConfigWindowSize(SOUND_DIALOG_NAME, s))
		resize(s);

	// Initialise the control values
	mFileEdit->setText(file);
	mRepeatCheckbox->setChecked(repeat);
	mVolumeCheckbox->setChecked(volume >= 0);
	mVolumeSlider->setValue(volume >= 0 ? static_cast<int>(volume*100) : 100);
	mFadeCheckbox->setChecked(fadeVolume >= 0);
	mFadeSlider->setValue(fadeVolume >= 0 ? static_cast<int>(fadeVolume*100) : 100);
	mFadeTime->setValue(fadeSeconds);
	slotVolumeToggled(volume >= 0);
}

SoundDlg::~SoundDlg()
{
	delete mPlayer;   // this stops playing if not already stopped
	mPlayer = 0;
}

/******************************************************************************
 * Set the read-only status of the dialogue.
 */
void SoundDlg::setReadOnly(bool readOnly)
{
	if (readOnly != mReadOnly)
	{
		mFileEdit->setReadOnly(readOnly);
		mFileBrowseButton->setReadOnly(readOnly);
		mRepeatCheckbox->setReadOnly(readOnly);
		mVolumeCheckbox->setReadOnly(readOnly);
		mVolumeSlider->setReadOnly(readOnly);
		mFadeCheckbox->setReadOnly(readOnly);
		mFadeTime->setReadOnly(readOnly);
		mFadeSlider->setReadOnly(readOnly);
		mReadOnly = readOnly;
	}
}

/******************************************************************************
 * Return the entered repetition and volume settings:
 * 'volume' is in range 0 - 1, or < 0 if volume is not to be set.
 * 'fadeVolume is similar, with 'fadeTime' set to the fade interval in seconds.
 * Reply = whether to repeat or not.
 */
bool SoundDlg::getSettings(float& volume, float& fadeVolume, int& fadeSeconds) const
{
	volume = mVolumeCheckbox->isChecked() ? (float)mVolumeSlider->value() / 100 : -1;
	if (mFadeCheckbox->isChecked())
	{
		fadeVolume  = (float)mFadeSlider->value() / 100;
		fadeSeconds = mFadeTime->value();
	}
	else
	{
		fadeVolume  = -1;
		fadeSeconds = 0;
	}
	return mRepeatCheckbox->isChecked();
}

/******************************************************************************
*  Called when the dialog's size has changed.
*  Records the new size in the config file.
*/
void SoundDlg::resizeEvent(QResizeEvent* re)
{
	if (isVisible())
		KAlarm::writeConfigWindowSize(SOUND_DIALOG_NAME, re->size());
	mVolumeSlider->resize(mFadeSlider->size());
	KDialog::resizeEvent(re);
}

void SoundDlg::showEvent(QShowEvent* se)
{
	mVolumeSlider->resize(mFadeSlider->size());
	KDialog::showEvent(se);
}

/******************************************************************************
*  Called when the OK button is clicked.
*/
void SoundDlg::slotOk()
{
	if (mReadOnly)
		reject();
	if (!checkFile())
		return;
	accept();
}

/******************************************************************************
 * Called when the file browser button is clicked.
 */
void SoundDlg::slotPickFile()
{
	QString url = SoundPicker::browseFile(mDefaultDir, mFileEdit->text());
	if (!url.isEmpty())
		mFileEdit->setText(url);
}

/******************************************************************************
* Called when the file play or stop button is clicked.
*/
void SoundDlg::playSound()
{
	if (mPlayer)
	{
		// The file is currently playing. Stop it.
		playFinished();
		return;
	}
	if (!checkFile())
		return;
	mPlayer = new Phonon::AudioPlayer(Phonon::NotificationCategory);
	connect(mPlayer, SIGNAL(finished()), SLOT(playFinished()));
	mFilePlay->setIcon(SmallIcon("media-playback-stop"));   // change the play button to a stop button
	mPlayer->play(mUrl);
}

/******************************************************************************
* Called when playing the file has completed, or to stop playing.
*/
void SoundDlg::playFinished()
{
	delete mPlayer;   // this stops playing if not already stopped
	mPlayer = 0;
	mFilePlay->setIcon(SmallIcon("media-playback-start"));
}

/******************************************************************************
* Check whether the specified sound file exists.
*/
bool SoundDlg::checkFile()
{
	QString file = mFileEdit->text();
	mUrl = KUrl(file);
	if (KIO::NetAccess::exists(mUrl, true, this))
		return true;
	if (mUrl.isLocalFile()  &&  !file.startsWith(QLatin1String("/")))
	{
		// It's a relative path.
		// Find the first sound resource that contains files.
		QStringList soundDirs = KGlobal::dirs()->resourceDirs("sound");
		if (!soundDirs.isEmpty())
		{
			QDir dir;
			dir.setFilter(QDir::Files | QDir::Readable);
			for (int i = 0, end = soundDirs.count();  i < end;  ++i)
			{
				dir = soundDirs[i];
				if (dir.isReadable() && dir.count() > 2)
				{
					mUrl.setPath(soundDirs[i]);
					mUrl.addPath(file);
					if (KIO::NetAccess::exists(mUrl, true, this))
						return true;
				}
			}
		}
		mUrl.setPath(QDir::homePath());
		mUrl.addPath(file);
		if (KIO::NetAccess::exists(mUrl, true, this))
			return true;
	}
	KMessageBox::sorry(this, i18n("File not found"));
	mUrl.clear();
	return false;
}

/******************************************************************************
 * Called when the Set Volume checkbox is toggled.
 */
void SoundDlg::slotVolumeToggled(bool on)
{
	mVolumeSlider->setEnabled(on);
	mFadeCheckbox->setEnabled(on);
	slotFadeToggled(on  &&  mFadeCheckbox->isChecked());
}

/******************************************************************************
 * Called when the Fade checkbox is toggled.
 */
void SoundDlg::slotFadeToggled(bool on)
{
	mFadeBox->setEnabled(on);
	mFadeVolumeBox->setEnabled(on);
}
<commit_msg>Make tooltips correspond to button function<commit_after>/*
 *  sounddlg.cpp  -  sound file selection and configuration dialog
 *  Program:  kalarm
 *  Copyright © 2005-2007 by David Jarvie <software@astrojar.org.uk>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include "kalarm.h"

#include <QLabel>
#include <QDir>
#include <QGroupBox>
#include <QApplication>
#include <QVBoxLayout>
#include <QGridLayout>
#include <QShowEvent>
#include <QResizeEvent>

#include <klocale.h>
#include <kstandarddirs.h>
#include <kiconloader.h>
#include <khbox.h>
#include <kio/netaccess.h>
#include <phonon/audioplayer.h>

#include "checkbox.h"
#include "functions.h"
#include "lineedit.h"
#include "pushbutton.h"
#include "slider.h"
#include "soundpicker.h"
#include "spinbox.h"
#include "sounddlg.moc"

// Collect these widget labels together to ensure consistent wording and
// translations across different modules.
QString SoundDlg::i18n_SetVolume()   { return i18n("Set volume"); }
QString SoundDlg::i18n_v_SetVolume() { return i18n("Set &volume"); }
QString SoundDlg::i18n_Repeat()      { return i18n("Repeat"); }
QString SoundDlg::i18n_p_Repeat()    { return i18n("Re&peat"); }

static const char SOUND_DIALOG_NAME[] = "SoundDialog";

SoundDlg::SoundDlg(const QString& file, float volume, float fadeVolume, int fadeSeconds, bool repeat,
                   const QString& caption, QWidget* parent)
	: KDialog(parent),
	  mPlayer(0),
	  mReadOnly(false)
{
	QWidget* page = new QWidget(this);
	setMainWidget(page);
	setCaption(caption);
	setButtons(Ok|Cancel);
	setDefaultButton(Ok);
	connect(this, SIGNAL(okClicked()), SLOT(slotOk()));

	QVBoxLayout* layout = new QVBoxLayout(page);
	layout->setMargin(0);
	layout->setSpacing(spacingHint());

	// File play button
	KHBox* box = new KHBox(page);
	box->setMargin(0);
	layout->addWidget(box);
	mFilePlay = new QPushButton(box);
	mFilePlay->setIcon(SmallIcon("media-playback-start"));
	mFilePlay->setFixedSize(mFilePlay->sizeHint());
	connect(mFilePlay, SIGNAL(clicked()), SLOT(playSound()));
	mFilePlay->setToolTip(i18n("Test the sound"));
	mFilePlay->setWhatsThis(i18n("Play the selected sound file."));

	// File name edit box
	mFileEdit = new LineEdit(LineEdit::Url, box);
	mFileEdit->setAcceptDrops(true);
	mFileEdit->setWhatsThis(i18n("Enter the name or URL of a sound file to play."));

	// File browse button
	mFileBrowseButton = new PushButton(box);
	mFileBrowseButton->setIcon(SmallIcon("document-open"));
	mFileBrowseButton->setFixedSize(mFileBrowseButton->sizeHint());
	connect(mFileBrowseButton, SIGNAL(clicked()), SLOT(slotPickFile()));
	mFileBrowseButton->setToolTip(i18n("Choose a file"));
	mFileBrowseButton->setWhatsThis(i18n("Select a sound file to play."));

	// Sound repetition checkbox
	mRepeatCheckbox = new CheckBox(i18n_p_Repeat(), page);
	mRepeatCheckbox->setFixedSize(mRepeatCheckbox->sizeHint());
	mRepeatCheckbox->setWhatsThis(i18n("If checked, the sound file will be played repeatedly for as long as the message is displayed."));
	layout->addWidget(mRepeatCheckbox);

	// Volume
	QGroupBox* group = new QGroupBox(i18n("Volume"), page);
	layout->addWidget(group);
	QGridLayout* grid = new QGridLayout(group);
	grid->setMargin(marginHint());
	grid->setSpacing(spacingHint());
	grid->setColumnStretch(2, 1);
	int indentWidth = 3 * KDialog::spacingHint();
	grid->setColumnMinimumWidth(0, indentWidth);
	grid->setColumnMinimumWidth(1, indentWidth);

	// 'Set volume' checkbox
	box = new KHBox(group);
	box->setMargin(0);
	box->setSpacing(spacingHint());
	grid->addWidget(box, 1, 0, 1, 3);
	mVolumeCheckbox = new CheckBox(i18n_v_SetVolume(), box);
	mVolumeCheckbox->setFixedSize(mVolumeCheckbox->sizeHint());
	connect(mVolumeCheckbox, SIGNAL(toggled(bool)), SLOT(slotVolumeToggled(bool)));
	mVolumeCheckbox->setWhatsThis(i18n("Select to choose the volume for playing the sound file."));

	// Volume slider
	mVolumeSlider = new Slider(0, 100, 10, Qt::Horizontal, box);
	mVolumeSlider->setTickPosition(QSlider::TicksBelow);
	mVolumeSlider->setTickInterval(10);
	mVolumeSlider->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
	mVolumeSlider->setWhatsThis(i18n("Choose the volume for playing the sound file."));
	mVolumeCheckbox->setFocusWidget(mVolumeSlider);

	// Fade checkbox
	mFadeCheckbox = new CheckBox(i18n("Fade"), group);
	mFadeCheckbox->setFixedSize(mFadeCheckbox->sizeHint());
	connect(mFadeCheckbox, SIGNAL(toggled(bool)), SLOT(slotFadeToggled(bool)));
	mFadeCheckbox->setWhatsThis(i18n("Select to fade the volume when the sound file first starts to play."));
	grid->addWidget(mFadeCheckbox, 2, 1, 1, 2, Qt::AlignLeft);

	// Fade time
	mFadeBox = new KHBox(group);
	mFadeBox->setMargin(0);
	mFadeBox->setSpacing(spacingHint());
	grid->addWidget(mFadeBox, 3, 2, Qt::AlignLeft);
	QLabel* label = new QLabel(i18nc("Time period over which to fade the sound", "Fade time:"), mFadeBox);
	label->setFixedSize(label->sizeHint());
	mFadeTime = new SpinBox(1, 999, mFadeBox);
	mFadeTime->setSingleShiftStep(10);
	mFadeTime->setFixedSize(mFadeTime->sizeHint());
	label->setBuddy(mFadeTime);
	label = new QLabel(i18n("seconds"), mFadeBox);
	label->setFixedSize(label->sizeHint());
	mFadeBox->setWhatsThis(i18n("Enter how many seconds to fade the sound before reaching the set volume."));

	// Fade slider
	mFadeVolumeBox = new KHBox(group);
	mFadeVolumeBox->setMargin(0);
	mFadeVolumeBox->setSpacing(spacingHint());
	grid->addWidget(mFadeVolumeBox, 4, 2);
	label = new QLabel(i18n("Initial volume:"), mFadeVolumeBox);
	label->setFixedSize(label->sizeHint());
	mFadeSlider = new Slider(0, 100, 10, Qt::Horizontal, mFadeVolumeBox);
	mFadeSlider->setTickPosition(QSlider::TicksBelow);
	mFadeSlider->setTickInterval(10);
	mFadeSlider->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed));
	label->setBuddy(mFadeSlider);
	mFadeVolumeBox->setWhatsThis(i18n("Choose the initial volume for playing the sound file."));

	// Restore the dialogue size from last time
	QSize s;
	if (KAlarm::readConfigWindowSize(SOUND_DIALOG_NAME, s))
		resize(s);

	// Initialise the control values
	mFileEdit->setText(file);
	mRepeatCheckbox->setChecked(repeat);
	mVolumeCheckbox->setChecked(volume >= 0);
	mVolumeSlider->setValue(volume >= 0 ? static_cast<int>(volume*100) : 100);
	mFadeCheckbox->setChecked(fadeVolume >= 0);
	mFadeSlider->setValue(fadeVolume >= 0 ? static_cast<int>(fadeVolume*100) : 100);
	mFadeTime->setValue(fadeSeconds);
	slotVolumeToggled(volume >= 0);
}

SoundDlg::~SoundDlg()
{
	delete mPlayer;   // this stops playing if not already stopped
	mPlayer = 0;
}

/******************************************************************************
 * Set the read-only status of the dialogue.
 */
void SoundDlg::setReadOnly(bool readOnly)
{
	if (readOnly != mReadOnly)
	{
		mFileEdit->setReadOnly(readOnly);
		mFileBrowseButton->setReadOnly(readOnly);
		mRepeatCheckbox->setReadOnly(readOnly);
		mVolumeCheckbox->setReadOnly(readOnly);
		mVolumeSlider->setReadOnly(readOnly);
		mFadeCheckbox->setReadOnly(readOnly);
		mFadeTime->setReadOnly(readOnly);
		mFadeSlider->setReadOnly(readOnly);
		mReadOnly = readOnly;
	}
}

/******************************************************************************
 * Return the entered repetition and volume settings:
 * 'volume' is in range 0 - 1, or < 0 if volume is not to be set.
 * 'fadeVolume is similar, with 'fadeTime' set to the fade interval in seconds.
 * Reply = whether to repeat or not.
 */
bool SoundDlg::getSettings(float& volume, float& fadeVolume, int& fadeSeconds) const
{
	volume = mVolumeCheckbox->isChecked() ? (float)mVolumeSlider->value() / 100 : -1;
	if (mFadeCheckbox->isChecked())
	{
		fadeVolume  = (float)mFadeSlider->value() / 100;
		fadeSeconds = mFadeTime->value();
	}
	else
	{
		fadeVolume  = -1;
		fadeSeconds = 0;
	}
	return mRepeatCheckbox->isChecked();
}

/******************************************************************************
*  Called when the dialog's size has changed.
*  Records the new size in the config file.
*/
void SoundDlg::resizeEvent(QResizeEvent* re)
{
	if (isVisible())
		KAlarm::writeConfigWindowSize(SOUND_DIALOG_NAME, re->size());
	mVolumeSlider->resize(mFadeSlider->size());
	KDialog::resizeEvent(re);
}

void SoundDlg::showEvent(QShowEvent* se)
{
	mVolumeSlider->resize(mFadeSlider->size());
	KDialog::showEvent(se);
}

/******************************************************************************
*  Called when the OK button is clicked.
*/
void SoundDlg::slotOk()
{
	if (mReadOnly)
		reject();
	if (!checkFile())
		return;
	accept();
}

/******************************************************************************
 * Called when the file browser button is clicked.
 */
void SoundDlg::slotPickFile()
{
	QString url = SoundPicker::browseFile(mDefaultDir, mFileEdit->text());
	if (!url.isEmpty())
		mFileEdit->setText(url);
}

/******************************************************************************
* Called when the file play or stop button is clicked.
*/
void SoundDlg::playSound()
{
	if (mPlayer)
	{
		// The file is currently playing. Stop it.
		playFinished();
		return;
	}
	if (!checkFile())
		return;
	mPlayer = new Phonon::AudioPlayer(Phonon::NotificationCategory);
	connect(mPlayer, SIGNAL(finished()), SLOT(playFinished()));
	mFilePlay->setIcon(SmallIcon("media-playback-stop"));   // change the play button to a stop button
	mFilePlay->setToolTip(i18n("Stop sound"));
	mFilePlay->setWhatsThis(i18n("Stop playing the sound"));
	mPlayer->play(mUrl);
}

/******************************************************************************
* Called when playing the file has completed, or to stop playing.
*/
void SoundDlg::playFinished()
{
	delete mPlayer;   // this stops playing if not already stopped
	mPlayer = 0;
	mFilePlay->setIcon(SmallIcon("media-playback-start"));
	mFilePlay->setToolTip(i18n("Test the sound"));
	mFilePlay->setWhatsThis(i18n("Play the selected sound file."));
}

/******************************************************************************
* Check whether the specified sound file exists.
*/
bool SoundDlg::checkFile()
{
	QString file = mFileEdit->text();
	mUrl = KUrl(file);
	if (KIO::NetAccess::exists(mUrl, true, this))
		return true;
	if (mUrl.isLocalFile()  &&  !file.startsWith(QLatin1String("/")))
	{
		// It's a relative path.
		// Find the first sound resource that contains files.
		QStringList soundDirs = KGlobal::dirs()->resourceDirs("sound");
		if (!soundDirs.isEmpty())
		{
			QDir dir;
			dir.setFilter(QDir::Files | QDir::Readable);
			for (int i = 0, end = soundDirs.count();  i < end;  ++i)
			{
				dir = soundDirs[i];
				if (dir.isReadable() && dir.count() > 2)
				{
					mUrl.setPath(soundDirs[i]);
					mUrl.addPath(file);
					if (KIO::NetAccess::exists(mUrl, true, this))
						return true;
				}
			}
		}
		mUrl.setPath(QDir::homePath());
		mUrl.addPath(file);
		if (KIO::NetAccess::exists(mUrl, true, this))
			return true;
	}
	KMessageBox::sorry(this, i18n("File not found"));
	mUrl.clear();
	return false;
}

/******************************************************************************
 * Called when the Set Volume checkbox is toggled.
 */
void SoundDlg::slotVolumeToggled(bool on)
{
	mVolumeSlider->setEnabled(on);
	mFadeCheckbox->setEnabled(on);
	slotFadeToggled(on  &&  mFadeCheckbox->isChecked());
}

/******************************************************************************
 * Called when the Fade checkbox is toggled.
 */
void SoundDlg::slotFadeToggled(bool on)
{
	mFadeBox->setEnabled(on);
	mFadeVolumeBox->setEnabled(on);
}
<|endoftext|>


<commit_before>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Baldur Karlsson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "CaptureContext.h"
#include <QApplication>
#include <QDir>
#include <QFileInfo>
#include <QLabel>
#include <QMenu>
#include <QMessageBox>
#include <QMetaObject>
#include <QProgressDialog>
#include <QStandardPaths>
#include <QTimer>
#include "Windows/MainWindow.h"
#include "QRDUtils.h"

CaptureContext::CaptureContext(QString paramFilename, QString remoteHost, uint32_t remoteIdent,
                               bool temp, PersistantConfig &cfg)
    : Config(cfg)
{
  m_LogLoaded = false;
  m_LoadInProgress = false;

  m_EventID = 0;

  memset(&m_APIProps, 0, sizeof(m_APIProps));

  qApp->setApplicationVersion(RENDERDOC_GetVersionString());

  m_MainWindow = new MainWindow(this);
  m_MainWindow->show();

  if(!paramFilename.isEmpty())
  {
    QFileInfo fi(paramFilename);

    m_MainWindow->LoadFromFilename(paramFilename);
  }
}

CaptureContext::~CaptureContext()
{
  m_Renderer.CloseThread();
  delete m_MainWindow;
}

bool CaptureContext::isRunning()
{
  return m_MainWindow && m_MainWindow->isVisible();
}

QString CaptureContext::ConfigFile(const QString &filename)
{
  QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);

  QDir dir(path);
  if(!dir.exists())
    dir.mkdir(".");

  return QDir::cleanPath(dir.absoluteFilePath(filename));
}

QString CaptureContext::TempLogFilename(QString appname)
{
  QString folder = Config.TemporaryCaptureDirectory;

  QDir dir(folder);

  if(folder == "" || !dir.exists())
    folder = QDir::tempPath();

  return dir.absoluteFilePath(
      appname + "_" + QDateTime::currentDateTimeUtc().toString("yyyy.MM.dd_HH.mm.ss") + ".rdc");
}

void CaptureContext::LoadLogfile(const QString &logFile, const QString &origFilename,
                                 bool temporary, bool local)
{
  m_Progress = new QProgressDialog(QString("Loading Log"), QString(), 0, 1000, m_MainWindow);
  m_Progress->setWindowTitle("Please Wait");
  m_Progress->setWindowFlags(Qt::CustomizeWindowHint | Qt::Dialog | Qt::WindowTitleHint);
  m_Progress->setWindowIcon(QIcon());
  m_Progress->setMinimumSize(QSize(250, 0));
  m_Progress->setMaximumSize(QSize(250, 10000));
  m_Progress->setCancelButton(NULL);
  m_Progress->setMinimumDuration(0);
  m_Progress->setWindowModality(Qt::ApplicationModal);
  m_Progress->setValue(0);

  QLabel *label = new QLabel(m_Progress);

  label->setText(QString("Loading Log: %1").arg(origFilename));
  label->setAlignment(Qt::AlignCenter);
  label->setWordWrap(true);

  m_Progress->setLabel(label);

  LambdaThread *thread = new LambdaThread([this, logFile, origFilename, temporary, local]() {
    LoadLogfileThreaded(logFile, origFilename, temporary, local);

    GUIInvoke::call([this, origFilename]() {
      delete m_Progress;
      m_Progress = NULL;
    });
  });
  thread->selfDelete(true);
  thread->start();
}

void CaptureContext::LoadLogfileThreaded(const QString &logFile, const QString &origFilename,
                                         bool temporary, bool local)
{
  QFileInfo fi(ConfigFile("UI.config"));

  m_LogFile = origFilename;

  m_LogLocal = local;

  m_LoadInProgress = true;

  if(fi.exists())
    Config.Serialize(fi.absoluteFilePath());

  float loadProgress = 0.0f;
  float postloadProgress = 0.0f;

  QSemaphore progressThread(1);

  LambdaThread progressTickerThread([this, &progressThread, &loadProgress, &postloadProgress]() {
    while(progressThread.available())
    {
      QThread::msleep(30);

      float val = 0.8f * loadProgress + 0.19f * postloadProgress + 0.01f;

      GUIInvoke::call([this, val]() {
        m_Progress->setValue(val * 1000);
        m_MainWindow->setProgress(val);
      });
    }
    GUIInvoke::call([this]() { m_Progress->setValue(1000); });
  });
  progressTickerThread.start();

  // this function call will block until the log is either loaded, or there's some failure
  m_Renderer.OpenCapture(logFile, &loadProgress);

  // if the renderer isn't running, we hit a failure case so display an error message
  if(!m_Renderer.IsRunning())
  {
    QString errmsg = "Unknown error message";
    errmsg = ToQStr(m_Renderer.GetCreateStatus());

    progressThread.acquire();
    progressTickerThread.wait();

    RDDialog::critical(NULL, "Error opening log",
                       QString("%1\nFailed to open logfile for replay: %2.\n\n"
                               "Check diagnostic log in Help menu for more details.")
                           .arg(logFile)
                           .arg(errmsg));

    GUIInvoke::call([this]() {
      m_Progress->setValue(1000);
      m_MainWindow->setProgress(-1.0f);
      m_Progress->hide();
    });

    m_LoadInProgress = false;

    return;
  }

  if(!temporary)
  {
    PersistantConfig::AddRecentFile(Config.RecentLogFiles, origFilename, 10);

    if(fi.exists())
      Config.Serialize(fi.absoluteFilePath());
  }

  m_EventID = 0;

  // fetch initial data like drawcalls, textures and buffers
  m_Renderer.BlockInvoke([this, &postloadProgress](IReplayRenderer *r) {
    r->GetFrameInfo(&m_FrameInfo);

    m_APIProps = r->GetAPIProperties();

    postloadProgress = 0.2f;

    r->GetDrawcalls(&m_Drawcalls);

    postloadProgress = 0.4f;

    r->GetSupportedWindowSystems(&m_WinSystems);

#if defined(RENDERDOC_PLATFORM_WIN32)
    m_CurWinSystem = eWindowingSystem_Win32;
#elif defined(RENDERDOC_PLATFORM_LINUX)
    m_CurWinSystem = eWindowingSystem_Xlib;

    // prefer XCB, if supported
    for(WindowingSystem sys : m_WinSystems)
    {
      if(sys == eWindowingSystem_XCB)
      {
        m_CurWinSystem = eWindowingSystem_XCB;
        break;
      }
    }

    if(m_CurWinSystem == eWindowingSystem_XCB)
      m_XCBConnection = QX11Info::connection();
    else
      m_X11Display = QX11Info::display();
#endif

    r->GetBuffers(&m_BufferList);
    for(FetchBuffer &b : m_BufferList)
      m_Buffers[b.ID] = &b;

    postloadProgress = 0.8f;

    r->GetTextures(&m_TextureList);
    for(FetchTexture &t : m_TextureList)
      m_Textures[t.ID] = &t;

    postloadProgress = 0.9f;

    r->GetD3D11PipelineState(&CurD3D11PipelineState);
    r->GetD3D12PipelineState(&CurD3D12PipelineState);
    r->GetGLPipelineState(&CurGLPipelineState);
    r->GetVulkanPipelineState(&CurVulkanPipelineState);
    CurPipelineState.SetStates(m_APIProps, &CurD3D11PipelineState, &CurD3D12PipelineState,
                               &CurGLPipelineState, &CurVulkanPipelineState);

    UnreadMessageCount = 0;
    AddMessages(m_FrameInfo.debugMessages);

    postloadProgress = 1.0f;
  });

  QThread::msleep(20);

  QDateTime today = QDateTime::currentDateTimeUtc();
  QDateTime compare = today.addDays(-21);

  if(compare > Config.DegradedLog_LastUpdate && m_APIProps.degraded)
  {
    Config.DegradedLog_LastUpdate = today;

    RDDialog::critical(
        NULL, "Degraded support of log",
        QString(
            "%1\nThis log opened with degraded support - "
            "this could mean missing hardware support caused a fallback to software rendering.\n\n"
            "This warning will not appear every time this happens, "
            "check debug errors/warnings window for more details.")
            .arg(origFilename));
  }

  m_LogLoaded = true;

  progressThread.acquire();
  progressTickerThread.wait();

  QVector<ILogViewerForm *> logviewers(m_LogViewers);

  GUIInvoke::blockcall([&logviewers]() {
    // notify all the registers log viewers that a log has been loaded
    for(ILogViewerForm *logviewer : logviewers)
    {
      if(logviewer)
        logviewer->OnLogfileLoaded();
    }
  });

  m_LoadInProgress = false;

  GUIInvoke::call([this]() {
    m_Progress->setValue(1000);
    m_MainWindow->setProgress(1.0f);
    m_Progress->hide();
  });
}

void CaptureContext::CloseLogfile()
{
  if(!m_LogLoaded)
    return;

  m_LogFile = "";

  m_Renderer.CloseThread();

  memset(&m_APIProps, 0, sizeof(m_APIProps));
  memset(&m_FrameInfo, 0, sizeof(m_FrameInfo));
  m_Buffers.clear();
  m_BufferList.clear();
  m_Textures.clear();
  m_TextureList.clear();

  CurD3D11PipelineState = D3D11PipelineState();
  CurD3D12PipelineState = D3D12PipelineState();
  CurGLPipelineState = GLPipelineState();
  CurVulkanPipelineState = VulkanPipelineState();
  CurPipelineState.SetStates(m_APIProps, NULL, NULL, NULL, NULL);

  DebugMessages.clear();
  UnreadMessageCount = 0;

  m_LogLoaded = false;

  QVector<ILogViewerForm *> logviewers(m_LogViewers);

  for(ILogViewerForm *logviewer : logviewers)
  {
    if(logviewer)
      logviewer->OnLogfileClosed();
  }
}

void CaptureContext::SetEventID(ILogViewerForm *exclude, uint32_t eventID, bool force)
{
  m_EventID = eventID;

  m_Renderer.BlockInvoke([this, eventID, force](IReplayRenderer *r) {
    r->SetFrameEvent(eventID, force);
    r->GetD3D11PipelineState(&CurD3D11PipelineState);
    r->GetD3D12PipelineState(&CurD3D12PipelineState);
    r->GetGLPipelineState(&CurGLPipelineState);
    r->GetVulkanPipelineState(&CurVulkanPipelineState);
    CurPipelineState.SetStates(m_APIProps, &CurD3D11PipelineState, &CurD3D12PipelineState,
                               &CurGLPipelineState, &CurVulkanPipelineState);
  });

  for(ILogViewerForm *logviewer : m_LogViewers)
  {
    if(logviewer == exclude)
      continue;

    logviewer->OnEventSelected(eventID);
  }
}

void *CaptureContext::FillWindowingData(WId widget)
{
#if defined(WIN32)

  return (void *)widget;

#elif defined(RENDERDOC_PLATFORM_LINUX)

  static XCBWindowData xcb = {
      m_XCBConnection, (xcb_window_t)widget,
  };

  static XlibWindowData xlib = {m_X11Display, (Drawable)widget};

  if(m_CurWinSystem == eWindowingSystem_XCB)
    return &xcb;
  else
    return &xlib;

#else

#error "Unknown platform"

#endif
}
<commit_msg>Assign the temp path properly when fetching it as a default<commit_after>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Baldur Karlsson
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 ******************************************************************************/

#include "CaptureContext.h"
#include <QApplication>
#include <QDir>
#include <QFileInfo>
#include <QLabel>
#include <QMenu>
#include <QMessageBox>
#include <QMetaObject>
#include <QProgressDialog>
#include <QStandardPaths>
#include <QTimer>
#include "Windows/MainWindow.h"
#include "QRDUtils.h"

CaptureContext::CaptureContext(QString paramFilename, QString remoteHost, uint32_t remoteIdent,
                               bool temp, PersistantConfig &cfg)
    : Config(cfg)
{
  m_LogLoaded = false;
  m_LoadInProgress = false;

  m_EventID = 0;

  memset(&m_APIProps, 0, sizeof(m_APIProps));

  qApp->setApplicationVersion(RENDERDOC_GetVersionString());

  m_MainWindow = new MainWindow(this);
  m_MainWindow->show();

  if(!paramFilename.isEmpty())
  {
    QFileInfo fi(paramFilename);

    m_MainWindow->LoadFromFilename(paramFilename);
  }
}

CaptureContext::~CaptureContext()
{
  m_Renderer.CloseThread();
  delete m_MainWindow;
}

bool CaptureContext::isRunning()
{
  return m_MainWindow && m_MainWindow->isVisible();
}

QString CaptureContext::ConfigFile(const QString &filename)
{
  QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);

  QDir dir(path);
  if(!dir.exists())
    dir.mkdir(".");

  return QDir::cleanPath(dir.absoluteFilePath(filename));
}

QString CaptureContext::TempLogFilename(QString appname)
{
  QString folder = Config.TemporaryCaptureDirectory;

  QDir dir(folder);

  if(folder == "" || !dir.exists())
    dir = QDir(QDir::tempPath());

  return dir.absoluteFilePath(
      appname + "_" + QDateTime::currentDateTimeUtc().toString("yyyy.MM.dd_HH.mm.ss") + ".rdc");
}

void CaptureContext::LoadLogfile(const QString &logFile, const QString &origFilename,
                                 bool temporary, bool local)
{
  m_Progress = new QProgressDialog(QString("Loading Log"), QString(), 0, 1000, m_MainWindow);
  m_Progress->setWindowTitle("Please Wait");
  m_Progress->setWindowFlags(Qt::CustomizeWindowHint | Qt::Dialog | Qt::WindowTitleHint);
  m_Progress->setWindowIcon(QIcon());
  m_Progress->setMinimumSize(QSize(250, 0));
  m_Progress->setMaximumSize(QSize(250, 10000));
  m_Progress->setCancelButton(NULL);
  m_Progress->setMinimumDuration(0);
  m_Progress->setWindowModality(Qt::ApplicationModal);
  m_Progress->setValue(0);

  QLabel *label = new QLabel(m_Progress);

  label->setText(QString("Loading Log: %1").arg(origFilename));
  label->setAlignment(Qt::AlignCenter);
  label->setWordWrap(true);

  m_Progress->setLabel(label);

  LambdaThread *thread = new LambdaThread([this, logFile, origFilename, temporary, local]() {
    LoadLogfileThreaded(logFile, origFilename, temporary, local);

    GUIInvoke::call([this, origFilename]() {
      delete m_Progress;
      m_Progress = NULL;
    });
  });
  thread->selfDelete(true);
  thread->start();
}

void CaptureContext::LoadLogfileThreaded(const QString &logFile, const QString &origFilename,
                                         bool temporary, bool local)
{
  QFileInfo fi(ConfigFile("UI.config"));

  m_LogFile = origFilename;

  m_LogLocal = local;

  m_LoadInProgress = true;

  if(fi.exists())
    Config.Serialize(fi.absoluteFilePath());

  float loadProgress = 0.0f;
  float postloadProgress = 0.0f;

  QSemaphore progressThread(1);

  LambdaThread progressTickerThread([this, &progressThread, &loadProgress, &postloadProgress]() {
    while(progressThread.available())
    {
      QThread::msleep(30);

      float val = 0.8f * loadProgress + 0.19f * postloadProgress + 0.01f;

      GUIInvoke::call([this, val]() {
        m_Progress->setValue(val * 1000);
        m_MainWindow->setProgress(val);
      });
    }
    GUIInvoke::call([this]() { m_Progress->setValue(1000); });
  });
  progressTickerThread.start();

  // this function call will block until the log is either loaded, or there's some failure
  m_Renderer.OpenCapture(logFile, &loadProgress);

  // if the renderer isn't running, we hit a failure case so display an error message
  if(!m_Renderer.IsRunning())
  {
    QString errmsg = "Unknown error message";
    errmsg = ToQStr(m_Renderer.GetCreateStatus());

    progressThread.acquire();
    progressTickerThread.wait();

    RDDialog::critical(NULL, "Error opening log",
                       QString("%1\nFailed to open logfile for replay: %2.\n\n"
                               "Check diagnostic log in Help menu for more details.")
                           .arg(logFile)
                           .arg(errmsg));

    GUIInvoke::call([this]() {
      m_Progress->setValue(1000);
      m_MainWindow->setProgress(-1.0f);
      m_Progress->hide();
    });

    m_LoadInProgress = false;

    return;
  }

  if(!temporary)
  {
    PersistantConfig::AddRecentFile(Config.RecentLogFiles, origFilename, 10);

    if(fi.exists())
      Config.Serialize(fi.absoluteFilePath());
  }

  m_EventID = 0;

  // fetch initial data like drawcalls, textures and buffers
  m_Renderer.BlockInvoke([this, &postloadProgress](IReplayRenderer *r) {
    r->GetFrameInfo(&m_FrameInfo);

    m_APIProps = r->GetAPIProperties();

    postloadProgress = 0.2f;

    r->GetDrawcalls(&m_Drawcalls);

    postloadProgress = 0.4f;

    r->GetSupportedWindowSystems(&m_WinSystems);

#if defined(RENDERDOC_PLATFORM_WIN32)
    m_CurWinSystem = eWindowingSystem_Win32;
#elif defined(RENDERDOC_PLATFORM_LINUX)
    m_CurWinSystem = eWindowingSystem_Xlib;

    // prefer XCB, if supported
    for(WindowingSystem sys : m_WinSystems)
    {
      if(sys == eWindowingSystem_XCB)
      {
        m_CurWinSystem = eWindowingSystem_XCB;
        break;
      }
    }

    if(m_CurWinSystem == eWindowingSystem_XCB)
      m_XCBConnection = QX11Info::connection();
    else
      m_X11Display = QX11Info::display();
#endif

    r->GetBuffers(&m_BufferList);
    for(FetchBuffer &b : m_BufferList)
      m_Buffers[b.ID] = &b;

    postloadProgress = 0.8f;

    r->GetTextures(&m_TextureList);
    for(FetchTexture &t : m_TextureList)
      m_Textures[t.ID] = &t;

    postloadProgress = 0.9f;

    r->GetD3D11PipelineState(&CurD3D11PipelineState);
    r->GetD3D12PipelineState(&CurD3D12PipelineState);
    r->GetGLPipelineState(&CurGLPipelineState);
    r->GetVulkanPipelineState(&CurVulkanPipelineState);
    CurPipelineState.SetStates(m_APIProps, &CurD3D11PipelineState, &CurD3D12PipelineState,
                               &CurGLPipelineState, &CurVulkanPipelineState);

    UnreadMessageCount = 0;
    AddMessages(m_FrameInfo.debugMessages);

    postloadProgress = 1.0f;
  });

  QThread::msleep(20);

  QDateTime today = QDateTime::currentDateTimeUtc();
  QDateTime compare = today.addDays(-21);

  if(compare > Config.DegradedLog_LastUpdate && m_APIProps.degraded)
  {
    Config.DegradedLog_LastUpdate = today;

    RDDialog::critical(
        NULL, "Degraded support of log",
        QString(
            "%1\nThis log opened with degraded support - "
            "this could mean missing hardware support caused a fallback to software rendering.\n\n"
            "This warning will not appear every time this happens, "
            "check debug errors/warnings window for more details.")
            .arg(origFilename));
  }

  m_LogLoaded = true;

  progressThread.acquire();
  progressTickerThread.wait();

  QVector<ILogViewerForm *> logviewers(m_LogViewers);

  GUIInvoke::blockcall([&logviewers]() {
    // notify all the registers log viewers that a log has been loaded
    for(ILogViewerForm *logviewer : logviewers)
    {
      if(logviewer)
        logviewer->OnLogfileLoaded();
    }
  });

  m_LoadInProgress = false;

  GUIInvoke::call([this]() {
    m_Progress->setValue(1000);
    m_MainWindow->setProgress(1.0f);
    m_Progress->hide();
  });
}

void CaptureContext::CloseLogfile()
{
  if(!m_LogLoaded)
    return;

  m_LogFile = "";

  m_Renderer.CloseThread();

  memset(&m_APIProps, 0, sizeof(m_APIProps));
  memset(&m_FrameInfo, 0, sizeof(m_FrameInfo));
  m_Buffers.clear();
  m_BufferList.clear();
  m_Textures.clear();
  m_TextureList.clear();

  CurD3D11PipelineState = D3D11PipelineState();
  CurD3D12PipelineState = D3D12PipelineState();
  CurGLPipelineState = GLPipelineState();
  CurVulkanPipelineState = VulkanPipelineState();
  CurPipelineState.SetStates(m_APIProps, NULL, NULL, NULL, NULL);

  DebugMessages.clear();
  UnreadMessageCount = 0;

  m_LogLoaded = false;

  QVector<ILogViewerForm *> logviewers(m_LogViewers);

  for(ILogViewerForm *logviewer : logviewers)
  {
    if(logviewer)
      logviewer->OnLogfileClosed();
  }
}

void CaptureContext::SetEventID(ILogViewerForm *exclude, uint32_t eventID, bool force)
{
  m_EventID = eventID;

  m_Renderer.BlockInvoke([this, eventID, force](IReplayRenderer *r) {
    r->SetFrameEvent(eventID, force);
    r->GetD3D11PipelineState(&CurD3D11PipelineState);
    r->GetD3D12PipelineState(&CurD3D12PipelineState);
    r->GetGLPipelineState(&CurGLPipelineState);
    r->GetVulkanPipelineState(&CurVulkanPipelineState);
    CurPipelineState.SetStates(m_APIProps, &CurD3D11PipelineState, &CurD3D12PipelineState,
                               &CurGLPipelineState, &CurVulkanPipelineState);
  });

  for(ILogViewerForm *logviewer : m_LogViewers)
  {
    if(logviewer == exclude)
      continue;

    logviewer->OnEventSelected(eventID);
  }
}

void *CaptureContext::FillWindowingData(WId widget)
{
#if defined(WIN32)

  return (void *)widget;

#elif defined(RENDERDOC_PLATFORM_LINUX)

  static XCBWindowData xcb = {
      m_XCBConnection, (xcb_window_t)widget,
  };

  static XlibWindowData xlib = {m_X11Display, (Drawable)widget};

  if(m_CurWinSystem == eWindowingSystem_XCB)
    return &xcb;
  else
    return &xlib;

#else

#error "Unknown platform"

#endif
}
<|endoftext|>


<commit_before>/// \file blocked_gzip_input_stream.cpp
///
/// Unit tests for BlockedGzipInputStream

#include "../blocked_gzip_input_stream.hpp"
#include "../blocked_gzip_output_stream.hpp"
#include "../hfile_cppstream.hpp"
#include "catch.hpp"

#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/gzip_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>

#include <htslib/hfile.h>

#include <sstream>

#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

namespace vg {
namespace unittest {
using namespace std;
using namespace vg::stream;

// We have a tiny function to get virtual offsets, based on the block's start
// offset in the file, and the offset in the block
static int64_t vo(size_t block_start, size_t offset) {
    return (block_start << 16) | (0xFFFF & offset);
}

TEST_CASE("a BlockedGzipInputStream can read from a stringstream", "[bgzip]") {
    stringstream datastream;

    const string TO_COMPRESS = "It's cheap and it's ethical... well, it's ethical... well, it's magical really";

    {
        // Write some data in
        BlockedGzipOutputStream bgzip_out(datastream);
        ::google::protobuf::io::CodedOutputStream coded_out(&bgzip_out);
        coded_out.WriteString(TO_COMPRESS);
        bgzip_out.EndFile();
    }

    // Stringstreams track put and get positions separately. So we will read from the beginning.
    REQUIRE(datastream.tellg() == 0);

    // Now try and read it back
    BlockedGzipInputStream bgzip_in(datastream);

    SECTION("data can be read the first time through, from the start") {

        REQUIRE(bgzip_in.Tell() == vo(0, 0));

        const char* buffer;
        int buffer_size;
        REQUIRE(bgzip_in.Next((const void**)&buffer, &buffer_size));

        int block = 0;
        int good_through = 0;

        do {

            // Check each block we read out of the stream

            for (size_t i = 0; i < buffer_size; i++) {
                // Check all the characters
                REQUIRE(buffer[i] == TO_COMPRESS[good_through + i]);
            }

            good_through += buffer_size;
            block++;

        } while (bgzip_in.Next((const void**)&buffer, &buffer_size));

        REQUIRE(good_through == TO_COMPRESS.size());
    }

    SECTION("data can be seeked into") {

        // Make sure we started at the start
        REQUIRE(bgzip_in.Tell() == vo(0, 0));

        // Go somewhere else and make sure we get there
        REQUIRE(bgzip_in.Seek(vo(0, 10)));
        REQUIRE(bgzip_in.Tell() == vo(0, 10));

        const char* buffer;
        int buffer_size = 0;
        while(buffer_size == 0) {
            // Fish for data until we get some
            REQUIRE(bgzip_in.Next((const void**)&buffer, &buffer_size));
        }

        // Make sure we got the right data
        REQUIRE(buffer[0] == TO_COMPRESS[10]);

        SECTION("seek can seek back") {

            REQUIRE(bgzip_in.Seek(vo(0, 0)));
            REQUIRE(bgzip_in.Tell() == vo(0, 0));

            buffer_size = 0;
            while(buffer_size == 0) {
                // Fish for data until we get some
                REQUIRE(bgzip_in.Next((const void**)&buffer, &buffer_size));
            }

            // Make sure we got the right data
            REQUIRE(buffer[0] == TO_COMPRESS[0]);

        }

        SECTION("skip works") {
            REQUIRE(bgzip_in.Seek(vo(0, 0)));
            bgzip_in.Skip(5);
            REQUIRE(bgzip_in.Tell() == vo(0, 5));

            buffer_size = 0;
            while(buffer_size == 0) {
                // Fish for data until we get some
                REQUIRE(bgzip_in.Next((const void**)&buffer, &buffer_size));
            }

            REQUIRE(buffer[0] == TO_COMPRESS[5]);
        }

    }

}

TEST_CASE("a BlockedGzipInputStream can read non-blocked gzip-compressed data", "[bgzip]") {
    stringstream datastream;

    const string TO_COMPRESS = "But wait, it comes with a warranty for a week, and that's respectable";

    {
        // Write some data in
        ::google::protobuf::io::OstreamOutputStream raw_out(&datastream);
        ::google::protobuf::io::GzipOutputStream gzip_out(&raw_out);
        ::google::protobuf::io::CodedOutputStream coded_out(&gzip_out);
        coded_out.WriteString(TO_COMPRESS);
    }

    // Stringstreams track put and get positions separately. So we will read from the beginning.
    REQUIRE(datastream.tellg() == 0);

    // Now try and read it back
    BlockedGzipInputStream bgzip_in(datastream);

    SECTION("data can be read the first time through, from the start") {

        // We can't seek
        REQUIRE(bgzip_in.Tell() == -1);

        const char* buffer;
        int buffer_size;
        REQUIRE(bgzip_in.Next((const void**)&buffer, &buffer_size));

        int block = 0;
        int good_through = 0;

        do {

            // Check each block we read out of the stream

            // We still can't seek
            REQUIRE(bgzip_in.Tell() == -1);

            for (size_t i = 0; i < buffer_size; i++) {
                // Check all the characters
                REQUIRE(buffer[i] == TO_COMPRESS[good_through + i]);
            }

            good_through += buffer_size;
            block++;

        } while (bgzip_in.Next((const void**)&buffer, &buffer_size));

        REQUIRE(good_through == TO_COMPRESS.size());
    }

    SECTION("seeking refuses to work") {
        REQUIRE(bgzip_in.Tell() == -1);
        REQUIRE(!bgzip_in.Seek(vo(0, 10)));
        REQUIRE(bgzip_in.Tell() == -1);

        const char* buffer;
        int buffer_size = 0;
        while(buffer_size == 0) {
            // Fish for data until we get some
            REQUIRE(bgzip_in.Next((const void**)&buffer, &buffer_size));
        }

        // Make sure we got the right data, even though we tried to seek
        REQUIRE(buffer[0] == TO_COMPRESS[0]);
    }

    SECTION("Skip works") {
        REQUIRE(bgzip_in.Tell() == -1);
        REQUIRE(bgzip_in.Skip(10));
        REQUIRE(bgzip_in.Tell() == -1);

        const char* buffer;
        int buffer_size = 0;
        while(buffer_size == 0) {
            // Fish for data until we get some
            REQUIRE(bgzip_in.Next((const void**)&buffer, &buffer_size));
        }

        // Make sure we got the right data that we skipped to
        REQUIRE(buffer[0] == TO_COMPRESS[10]);
    }

}

TEST_CASE("a BlockedGzipInputStream does not hang on truncated gzip-compressed data", "[bgzip]") {
    stringstream datastream;

    const string TO_COMPRESS = "Now all the parts are running, sparks are spilling out the gears";

    {
        // Write some data in
        ::google::protobuf::io::OstreamOutputStream raw_out(&datastream);
        ::google::protobuf::io::GzipOutputStream gzip_out(&raw_out);
        ::google::protobuf::io::CodedOutputStream coded_out(&gzip_out);
        coded_out.WriteString(TO_COMPRESS);
    }

    string data = datastream.str();
    data = data.substr(0, data.size() / 2);
    datastream = stringstream(data);

    BlockedGzipInputStream bgzip_in(datastream);

    char* buffer;
    int size;

    // This is going to complain about truncation. Don't listen to it.
    // Send stderr to /dev/null
    int discard = open("/dev/null", O_WRONLY);
    int real_stderr = dup(2);
    dup2(discard, 2);

    while(bgzip_in.Next((const void**)&buffer, &size)) {
        // Read off all the data
    }

    bool can_read_more = bgzip_in.Next((const void**)&buffer, &size);

    // Send stderr back to stderr
    dup2(real_stderr, 2);
    close(discard);
    close(real_stderr);

    REQUIRE(can_read_more == false);

}

TEST_CASE("a BlockedGzipInputStream can read large amounts of non-blocked compressed data", "[bgzip]") {
    stringstream datastream;

    {
        // Write some data in
        ::google::protobuf::io::OstreamOutputStream raw_out(&datastream);
        ::google::protobuf::io::GzipOutputStream gzip_out(&raw_out);
        ::google::protobuf::io::CodedOutputStream coded_out(&gzip_out);

        for (uint32_t i = 0; i < 1000000; i++) {
            // Generate ~4 MB of data
            coded_out.WriteLittleEndian32(i);
        }

    }

    // Stringstreams track put and get positions separately. So we will read from the beginning.
    REQUIRE(datastream.tellg() == 0);

    // Now try and read it back
    BlockedGzipInputStream bgzip_in(datastream);

    SECTION("it can be read straight through") {
        ::google::protobuf::io::CodedInputStream coded_in(&bgzip_in);

        uint32_t expected = 0;
        uint32_t found;

        while(coded_in.ReadLittleEndian32(&found)) {
            REQUIRE(found == expected);
            expected++;
        }

        REQUIRE(expected == 1000000);
    }

    SECTION("it can be read with multiple CodedInputStreams") {
        uint32_t expected = 0;
        uint32_t found;

        while(true) {
            ::google::protobuf::io::CodedInputStream coded_in(&bgzip_in);

            if (!coded_in.ReadLittleEndian32(&found)) {
                break;
            }

            REQUIRE(found == expected);
            expected++;
        }

        REQUIRE(expected == 1000000);
    }

}

TEST_CASE("a BlockedGzipInputStream can read concatenated streams of non-blocked compressed data", "[bgzip]") {
    stringstream datastream;

    for (size_t stream = 0; stream < 10; stream++) {
        // Write some data in
        ::google::protobuf::io::OstreamOutputStream raw_out(&datastream);
        ::google::protobuf::io::GzipOutputStream gzip_out(&raw_out);
        ::google::protobuf::io::CodedOutputStream coded_out(&gzip_out);

        for (uint32_t i = 0; i < 100; i++) {
            // Each stream will have 100 entries
            coded_out.WriteLittleEndian32(i);
        }

    }

    // Stringstreams track put and get positions separately. So we will read from the beginning.
    REQUIRE(datastream.tellg() == 0);

    // Now try and read it back
    BlockedGzipInputStream bgzip_in(datastream);

    ::google::protobuf::io::CodedInputStream coded_in(&bgzip_in);

    uint32_t expected = 0;
    uint32_t found;
    size_t seen_count = 0;

    while(coded_in.ReadLittleEndian32(&found)) {
        if (expected == 100) {
            expected = 0;
        }
        REQUIRE(found == expected);
        expected++;
        seen_count++;
    }

    REQUIRE(expected == 100);
    REQUIRE(seen_count == 100 * 10);
}

TEST_CASE("a BlockedGzipInputStream can read large amounts of uncompressed data", "[bgzip]") {
    stringstream datastream;

    {
        // Write some data in
        ::google::protobuf::io::OstreamOutputStream raw_out(&datastream);
        ::google::protobuf::io::CodedOutputStream coded_out(&raw_out);

        for (uint32_t i = 0; i < 1000000; i++) {
            // Generate ~4 MB of data
            coded_out.WriteLittleEndian32(i);
        }

    }

    // Stringstreams track put and get positions separately. So we will read from the beginning.
    REQUIRE(datastream.tellg() == 0);

    // Now try and read it back
    BlockedGzipInputStream bgzip_in(datastream);
    ::google::protobuf::io::CodedInputStream coded_in(&bgzip_in);

    uint32_t expected = 0;
    uint32_t found;

    while(coded_in.ReadLittleEndian32(&found)) {
        REQUIRE(found == expected);
        expected++;
    }

    REQUIRE(expected == 1000000);

}

TEST_CASE("a BlockedGzipInputStream can read large amounts of blocked compressed data", "[bgzip]") {
    stringstream datastream;

    {
        // Write some data in
        BlockedGzipOutputStream bgzip_out(datastream);
        ::google::protobuf::io::CodedOutputStream coded_out(&bgzip_out);

        for (uint32_t i = 0; i < 1000000; i++) {
            // Generate ~4 MB of data
            coded_out.WriteLittleEndian32(i);
        }
        bgzip_out.EndFile();

    }

    // Stringstreams track put and get positions separately. So we will read from the beginning.
    REQUIRE(datastream.tellg() == 0);

    // Now try and read it back
    BlockedGzipInputStream bgzip_in(datastream);
    ::google::protobuf::io::CodedInputStream coded_in(&bgzip_in);

    uint32_t expected = 0;
    uint32_t found;

    while(coded_in.ReadLittleEndian32(&found)) {
        REQUIRE(found == expected);
        expected++;
    }

    REQUIRE(expected == 1000000);

}

}

}
<commit_msg>Cut the number of assertions in the new tests<commit_after>/// \file blocked_gzip_input_stream.cpp
///
/// Unit tests for BlockedGzipInputStream

#include "../blocked_gzip_input_stream.hpp"
#include "../blocked_gzip_output_stream.hpp"
#include "../hfile_cppstream.hpp"
#include "catch.hpp"

#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/gzip_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>

#include <htslib/hfile.h>

#include <sstream>

#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

namespace vg {
namespace unittest {
using namespace std;
using namespace vg::stream;

// We have a tiny function to get virtual offsets, based on the block's start
// offset in the file, and the offset in the block
static int64_t vo(size_t block_start, size_t offset) {
    return (block_start << 16) | (0xFFFF & offset);
}

TEST_CASE("a BlockedGzipInputStream can read from a stringstream", "[bgzip]") {
    stringstream datastream;

    const string TO_COMPRESS = "It's cheap and it's ethical... well, it's ethical... well, it's magical really";

    {
        // Write some data in
        BlockedGzipOutputStream bgzip_out(datastream);
        ::google::protobuf::io::CodedOutputStream coded_out(&bgzip_out);
        coded_out.WriteString(TO_COMPRESS);
        bgzip_out.EndFile();
    }

    // Stringstreams track put and get positions separately. So we will read from the beginning.
    REQUIRE(datastream.tellg() == 0);

    // Now try and read it back
    BlockedGzipInputStream bgzip_in(datastream);

    SECTION("data can be read the first time through, from the start") {

        REQUIRE(bgzip_in.Tell() == vo(0, 0));

        const char* buffer;
        int buffer_size;
        REQUIRE(bgzip_in.Next((const void**)&buffer, &buffer_size));

        int block = 0;
        int good_through = 0;

        do {

            // Check each block we read out of the stream

            for (size_t i = 0; i < buffer_size; i++) {
                // Check all the characters
                REQUIRE(buffer[i] == TO_COMPRESS[good_through + i]);
            }

            good_through += buffer_size;
            block++;

        } while (bgzip_in.Next((const void**)&buffer, &buffer_size));

        REQUIRE(good_through == TO_COMPRESS.size());
    }

    SECTION("data can be seeked into") {

        // Make sure we started at the start
        REQUIRE(bgzip_in.Tell() == vo(0, 0));

        // Go somewhere else and make sure we get there
        REQUIRE(bgzip_in.Seek(vo(0, 10)));
        REQUIRE(bgzip_in.Tell() == vo(0, 10));

        const char* buffer;
        int buffer_size = 0;
        while(buffer_size == 0) {
            // Fish for data until we get some
            REQUIRE(bgzip_in.Next((const void**)&buffer, &buffer_size));
        }

        // Make sure we got the right data
        REQUIRE(buffer[0] == TO_COMPRESS[10]);

        SECTION("seek can seek back") {

            REQUIRE(bgzip_in.Seek(vo(0, 0)));
            REQUIRE(bgzip_in.Tell() == vo(0, 0));

            buffer_size = 0;
            while(buffer_size == 0) {
                // Fish for data until we get some
                REQUIRE(bgzip_in.Next((const void**)&buffer, &buffer_size));
            }

            // Make sure we got the right data
            REQUIRE(buffer[0] == TO_COMPRESS[0]);

        }

        SECTION("skip works") {
            REQUIRE(bgzip_in.Seek(vo(0, 0)));
            bgzip_in.Skip(5);
            REQUIRE(bgzip_in.Tell() == vo(0, 5));

            buffer_size = 0;
            while(buffer_size == 0) {
                // Fish for data until we get some
                REQUIRE(bgzip_in.Next((const void**)&buffer, &buffer_size));
            }

            REQUIRE(buffer[0] == TO_COMPRESS[5]);
        }

    }

}

TEST_CASE("a BlockedGzipInputStream can read non-blocked gzip-compressed data", "[bgzip]") {
    stringstream datastream;

    const string TO_COMPRESS = "But wait, it comes with a warranty for a week, and that's respectable";

    {
        // Write some data in
        ::google::protobuf::io::OstreamOutputStream raw_out(&datastream);
        ::google::protobuf::io::GzipOutputStream gzip_out(&raw_out);
        ::google::protobuf::io::CodedOutputStream coded_out(&gzip_out);
        coded_out.WriteString(TO_COMPRESS);
    }

    // Stringstreams track put and get positions separately. So we will read from the beginning.
    REQUIRE(datastream.tellg() == 0);

    // Now try and read it back
    BlockedGzipInputStream bgzip_in(datastream);

    SECTION("data can be read the first time through, from the start") {

        // We can't seek
        REQUIRE(bgzip_in.Tell() == -1);

        const char* buffer;
        int buffer_size;
        REQUIRE(bgzip_in.Next((const void**)&buffer, &buffer_size));

        int block = 0;
        int good_through = 0;

        do {

            // Check each block we read out of the stream

            // We still can't seek
            REQUIRE(bgzip_in.Tell() == -1);

            for (size_t i = 0; i < buffer_size; i++) {
                // Check all the characters
                REQUIRE(buffer[i] == TO_COMPRESS[good_through + i]);
            }

            good_through += buffer_size;
            block++;

        } while (bgzip_in.Next((const void**)&buffer, &buffer_size));

        REQUIRE(good_through == TO_COMPRESS.size());
    }

    SECTION("seeking refuses to work") {
        REQUIRE(bgzip_in.Tell() == -1);
        REQUIRE(!bgzip_in.Seek(vo(0, 10)));
        REQUIRE(bgzip_in.Tell() == -1);

        const char* buffer;
        int buffer_size = 0;
        while(buffer_size == 0) {
            // Fish for data until we get some
            REQUIRE(bgzip_in.Next((const void**)&buffer, &buffer_size));
        }

        // Make sure we got the right data, even though we tried to seek
        REQUIRE(buffer[0] == TO_COMPRESS[0]);
    }

    SECTION("Skip works") {
        REQUIRE(bgzip_in.Tell() == -1);
        REQUIRE(bgzip_in.Skip(10));
        REQUIRE(bgzip_in.Tell() == -1);

        const char* buffer;
        int buffer_size = 0;
        while(buffer_size == 0) {
            // Fish for data until we get some
            REQUIRE(bgzip_in.Next((const void**)&buffer, &buffer_size));
        }

        // Make sure we got the right data that we skipped to
        REQUIRE(buffer[0] == TO_COMPRESS[10]);
    }

}

TEST_CASE("a BlockedGzipInputStream does not hang on truncated gzip-compressed data", "[bgzip]") {
    stringstream datastream;

    const string TO_COMPRESS = "Now all the parts are running, sparks are spilling out the gears";

    {
        // Write some data in
        ::google::protobuf::io::OstreamOutputStream raw_out(&datastream);
        ::google::protobuf::io::GzipOutputStream gzip_out(&raw_out);
        ::google::protobuf::io::CodedOutputStream coded_out(&gzip_out);
        coded_out.WriteString(TO_COMPRESS);
    }

    string data = datastream.str();
    data = data.substr(0, data.size() / 2);
    datastream = stringstream(data);

    BlockedGzipInputStream bgzip_in(datastream);

    char* buffer;
    int size;

    // This is going to complain about truncation. Don't listen to it.
    // Send stderr to /dev/null
    int discard = open("/dev/null", O_WRONLY);
    int real_stderr = dup(2);
    dup2(discard, 2);

    while(bgzip_in.Next((const void**)&buffer, &size)) {
        // Read off all the data
    }

    bool can_read_more = bgzip_in.Next((const void**)&buffer, &size);

    // Send stderr back to stderr
    dup2(real_stderr, 2);
    close(discard);
    close(real_stderr);

    REQUIRE(can_read_more == false);

}

TEST_CASE("a BlockedGzipInputStream can read large amounts of non-blocked compressed data", "[bgzip]") {
    stringstream datastream;

    {
        // Write some data in
        ::google::protobuf::io::OstreamOutputStream raw_out(&datastream);
        ::google::protobuf::io::GzipOutputStream gzip_out(&raw_out);
        ::google::protobuf::io::CodedOutputStream coded_out(&gzip_out);

        for (uint32_t i = 0; i < 1000000; i++) {
            // Generate ~4 MB of data
            coded_out.WriteLittleEndian32(i);
        }

    }

    // Stringstreams track put and get positions separately. So we will read from the beginning.
    REQUIRE(datastream.tellg() == 0);

    // Now try and read it back
    BlockedGzipInputStream bgzip_in(datastream);

    SECTION("it can be read straight through") {
        ::google::protobuf::io::CodedInputStream coded_in(&bgzip_in);

        uint32_t expected = 0;
        uint32_t found;

        while(coded_in.ReadLittleEndian32(&found)) {
            if (expected % 1000 == 1) {
                REQUIRE(found == expected);
            }
            expected++;
        }

        REQUIRE(expected == 1000000);
    }

    SECTION("it can be read with multiple CodedInputStreams") {
        uint32_t expected = 0;
        uint32_t found;

        while(true) {
            ::google::protobuf::io::CodedInputStream coded_in(&bgzip_in);

            if (!coded_in.ReadLittleEndian32(&found)) {
                break;
            }

            if (expected % 1000 == 1) {
                REQUIRE(found == expected);
            }
            expected++;
        }

        REQUIRE(expected == 1000000);
    }

}

TEST_CASE("a BlockedGzipInputStream can read concatenated streams of non-blocked compressed data", "[bgzip]") {
    stringstream datastream;

    for (size_t stream = 0; stream < 10; stream++) {
        // Write some data in
        ::google::protobuf::io::OstreamOutputStream raw_out(&datastream);
        ::google::protobuf::io::GzipOutputStream gzip_out(&raw_out);
        ::google::protobuf::io::CodedOutputStream coded_out(&gzip_out);

        for (uint32_t i = 0; i < 100; i++) {
            // Each stream will have 100 entries
            coded_out.WriteLittleEndian32(i);
        }

    }

    // Stringstreams track put and get positions separately. So we will read from the beginning.
    REQUIRE(datastream.tellg() == 0);

    // Now try and read it back
    BlockedGzipInputStream bgzip_in(datastream);

    ::google::protobuf::io::CodedInputStream coded_in(&bgzip_in);

    uint32_t expected = 0;
    uint32_t found;
    size_t seen_count = 0;

    while(coded_in.ReadLittleEndian32(&found)) {
        if (expected == 100) {
            expected = 0;
        }

        if (expected % 10 == 1) {
            REQUIRE(found == expected);
        }
        expected++;
        seen_count++;
    }

    REQUIRE(expected == 100);
    REQUIRE(seen_count == 100 * 10);
}

TEST_CASE("a BlockedGzipInputStream can read large amounts of uncompressed data", "[bgzip]") {
    stringstream datastream;

    {
        // Write some data in
        ::google::protobuf::io::OstreamOutputStream raw_out(&datastream);
        ::google::protobuf::io::CodedOutputStream coded_out(&raw_out);

        for (uint32_t i = 0; i < 1000000; i++) {
            // Generate ~4 MB of data
            coded_out.WriteLittleEndian32(i);
        }

    }

    // Stringstreams track put and get positions separately. So we will read from the beginning.
    REQUIRE(datastream.tellg() == 0);

    // Now try and read it back
    BlockedGzipInputStream bgzip_in(datastream);
    ::google::protobuf::io::CodedInputStream coded_in(&bgzip_in);

    uint32_t expected = 0;
    uint32_t found;

    while(coded_in.ReadLittleEndian32(&found)) {
        if (expected % 1000 == 1) {
            REQUIRE(found == expected);
        }
        expected++;
    }

    REQUIRE(expected == 1000000);

}

TEST_CASE("a BlockedGzipInputStream can read large amounts of blocked compressed data", "[bgzip]") {
    stringstream datastream;

    {
        // Write some data in
        BlockedGzipOutputStream bgzip_out(datastream);
        ::google::protobuf::io::CodedOutputStream coded_out(&bgzip_out);

        for (uint32_t i = 0; i < 1000000; i++) {
            // Generate ~4 MB of data
            coded_out.WriteLittleEndian32(i);
        }
        bgzip_out.EndFile();

    }

    // Stringstreams track put and get positions separately. So we will read from the beginning.
    REQUIRE(datastream.tellg() == 0);

    // Now try and read it back
    BlockedGzipInputStream bgzip_in(datastream);
    ::google::protobuf::io::CodedInputStream coded_in(&bgzip_in);

    uint32_t expected = 0;
    uint32_t found;

    while(coded_in.ReadLittleEndian32(&found)) {
        if (expected % 1000 == 1) {
            REQUIRE(found == expected);
        }
        expected++;
    }

    REQUIRE(expected == 1000000);

}

}

}
<|endoftext|>


<commit_before>#ifndef SIMDIFY_VEC3
#define SIMDIFY_VEC3

#include "simdify.hpp"
#include "util/inline.hpp"

#define INL SIMDIFY_FORCE_INLINE

namespace simd {

    template <typename T>
    struct basic_vec3 {
        using mm_t = typename T::mm_t;
        using fp_t = typename T::fp_t;
        using bitmask_t = typename T::bitmask_t;

        T x, y, z;

        INL basic_vec3() = default;
        INL basic_vec3(const basic_vec3& other) = default;
        INL basic_vec3& operator=(const basic_vec3& other) = default;

        INL basic_vec3(const T& tx, const T& ty, const T& tz) :
            x(tx), y(ty), z(tz) {}

        template <typename S>
        INL basic_vec3(const S& other) : x(other.x), y(other.y), z(other.z) {}

        template <typename S>
        INL basic_vec3& operator=(const S& other) {
            x = T(other.x); y = T(other.y); z = T(other.z);
            return *this;
        }

        INL const T min() const { return min(x, min(y, z)); }
        INL const T max() const { return max(x, max(y, z)); }
    };

    template <typename T>
    INL const basic_vec3<T> cross(const basic_vec3<T>& l, const basic_vec3<T>& r) {
        return{ l.y*r.z - l.z*r.y, l.z*r.x - l.x*r.z, l.x*r.y - l.y*r.x };
    }

    template <typename T>
    INL const T dot(const basic_vec3<T>& l, const basic_vec3<T>& r) {
        return l.x*r.x + l.y*r.y + l.z*r.z;
    }

    // bitwise not with lazy evaluation
    template <typename T>
    struct bitwise_not<basic_vec3<T>> {
        using tp = basic_vec3<T>;
        tp neg;
        INL explicit bitwise_not(const tp& r) : neg(r) {}
        INL operator const tp() const { return{ ~neg.x, ~neg.y, ~neg.z }; }
    };

    template <typename T>
    INL const basic_vec3<T> operator&(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x & r.x, l.y & r.y, l.z & r.z }; }
    INL const basic_vec3<T> operator|(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x | r.x, l.y | r.y, l.z | r.z }; }
    INL const basic_vec3<T> operator^(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x ^ r.x, l.y ^ r.y, l.z ^ r.z }; }
    INL const bitwise_not<basic_vec3<T>> operator~(const basic_vec3<T>& l) { return bitwise_not<basic_vec3<T>>(l); }
    INL const basic_vec3<T> operator<(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x < r.x, l.y < r.y, l.z < r.z }; }
    INL const basic_vec3<T> operator>(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x > r.x, l.y > r.y, l.z > r.z }; }
    INL const basic_vec3<T> operator<=(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x <= r.x, l.y <= r.y, l.z <= r.z }; }
    INL const basic_vec3<T> operator>=(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x >= r.x, l.y >= r.y, l.z >= r.z }; }
    INL const basic_vec3<T> operator==(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x == r.x, l.y == r.y, l.z == r.z }; }
    INL const basic_vec3<T> operator!=(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x != r.x, l.y != r.y, l.z != r.z }; }
    INL const basic_vec3<T> operator+(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x + r.x, l.y + r.y, l.z + r.z }; }
    INL const basic_vec3<T> operator-(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x - r.x, l.y - r.y, l.z - r.z }; }
    INL const basic_vec3<T> operator*(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x * r.x, l.y * r.y, l.z * r.z }; }
    INL const basic_vec3<T> operator/(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x / r.x, l.y / r.y, l.z / r.z }; }

    INL const basic_vec3<T> min(const basic_vec3<T>& l, const basic_vec3<T>& r) {
        return{ min(l.x, r.x), min(l.y, r.y), min(l.z, r.z) };
    }

    INL const basic_vec3<T> max(const basic_vec3<T>& l, const basic_vec3<T>& r) {
        return{ max(l.x, r.x), max(l.y, r.y), max(l.z, r.z) };
    }

    using vec3 = basic_vec3<simd_t>;
}

#undef INL

#endif // SIMDIFY_VEC3<commit_msg>vec3::min, ::max removed; their usage too situational, semantics not obvious<commit_after>#ifndef SIMDIFY_VEC3
#define SIMDIFY_VEC3

#include "simdify.hpp"
#include "util/inline.hpp"

#define INL SIMDIFY_FORCE_INLINE

namespace simd {

    template <typename T>
    struct basic_vec3 {
        using mm_t = typename T::mm_t;
        using fp_t = typename T::fp_t;
        using bitmask_t = typename T::bitmask_t;

        T x, y, z;

        INL basic_vec3() = default;
        INL basic_vec3(const basic_vec3& other) = default;
        INL basic_vec3& operator=(const basic_vec3& other) = default;

        INL basic_vec3(const T& tx, const T& ty, const T& tz) :
            x(tx), y(ty), z(tz) {}

        template <typename S>
        INL basic_vec3(const S& other) : x(other.x), y(other.y), z(other.z) {}

        template <typename S>
        INL basic_vec3& operator=(const S& other) {
            x = T(other.x); y = T(other.y); z = T(other.z);
            return *this;
        }
    };

    template <typename T>
    INL const basic_vec3<T> cross(const basic_vec3<T>& l, const basic_vec3<T>& r) {
        return{ l.y*r.z - l.z*r.y, l.z*r.x - l.x*r.z, l.x*r.y - l.y*r.x };
    }

    template <typename T>
    INL const T dot(const basic_vec3<T>& l, const basic_vec3<T>& r) {
        return l.x*r.x + l.y*r.y + l.z*r.z;
    }

    // bitwise not with lazy evaluation
    template <typename T>
    struct bitwise_not<basic_vec3<T>> {
        using tp = basic_vec3<T>;
        tp neg;
        INL explicit bitwise_not(const tp& r) : neg(r) {}
        INL operator const tp() const { return{ ~neg.x, ~neg.y, ~neg.z }; }
    };

    template <typename T>
    INL const basic_vec3<T> operator&(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x & r.x, l.y & r.y, l.z & r.z }; }
    INL const basic_vec3<T> operator|(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x | r.x, l.y | r.y, l.z | r.z }; }
    INL const basic_vec3<T> operator^(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x ^ r.x, l.y ^ r.y, l.z ^ r.z }; }
    INL const bitwise_not<basic_vec3<T>> operator~(const basic_vec3<T>& l) { return bitwise_not<basic_vec3<T>>(l); }
    INL const basic_vec3<T> operator<(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x < r.x, l.y < r.y, l.z < r.z }; }
    INL const basic_vec3<T> operator>(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x > r.x, l.y > r.y, l.z > r.z }; }
    INL const basic_vec3<T> operator<=(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x <= r.x, l.y <= r.y, l.z <= r.z }; }
    INL const basic_vec3<T> operator>=(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x >= r.x, l.y >= r.y, l.z >= r.z }; }
    INL const basic_vec3<T> operator==(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x == r.x, l.y == r.y, l.z == r.z }; }
    INL const basic_vec3<T> operator!=(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x != r.x, l.y != r.y, l.z != r.z }; }
    INL const basic_vec3<T> operator+(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x + r.x, l.y + r.y, l.z + r.z }; }
    INL const basic_vec3<T> operator-(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x - r.x, l.y - r.y, l.z - r.z }; }
    INL const basic_vec3<T> operator*(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x * r.x, l.y * r.y, l.z * r.z }; }
    INL const basic_vec3<T> operator/(const basic_vec3<T>& l, const basic_vec3<T>& r) { return{ l.x / r.x, l.y / r.y, l.z / r.z }; }

    INL const basic_vec3<T> min(const basic_vec3<T>& l, const basic_vec3<T>& r) {
        return{ min(l.x, r.x), min(l.y, r.y), min(l.z, r.z) };
    }

    INL const basic_vec3<T> max(const basic_vec3<T>& l, const basic_vec3<T>& r) {
        return{ max(l.x, r.x), max(l.y, r.y), max(l.z, r.z) };
    }

    using vec3 = basic_vec3<simd_t>;
}

#undef INL

#endif // SIMDIFY_VEC3<|endoftext|>


<commit_before>//Copyright (c) 2013 Mike Pedersen
//See the file license.txt for copying permission.

#ifndef COHERENT_FRACTAL_HPP
#define COHERENT_FRACTAL_HPP

#include <cassert>
#include <Eigen/Core>

namespace coherent
{
	/// Calculates the sum of several noise functions (called octaves) with
	/// rising frequency and decreasing amplitude. The range of the return type
	/// will be the same as the noise functions (usually [0,1]).
	///
	/// @param begin the beginning of the list of noise functions
	/// @param end the end of the list of noise functions
	/// @param lacunarity the proportional change in frequency (a value of 2 will
	/// give each octave double the frequency of the one before it).
	/// @param persistence is the the proportional change in amplitude (a value of 0.5 will give each octave half the amplitude of the one before it). The function will return NaN if persistence is exactly 1.0.
	/// @param position the position of the noise value to return.
	template <typename Iterator, typename Derived>
	typename Derived::Scalar fractal(Iterator begin,
	                                 Iterator end,
	                                 typename Derived::Scalar lacunarity,
	                                 typename Derived::Scalar persistence,
	                                 const Eigen::MatrixBase<Derived>& position)
	{
		EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived);

		typename Derived::Scalar sum = 0, per = 1, lac = 1;
		for (Iterator i = begin; i != end; i++, per *= persistence, lac *= lacunarity)
			sum += (*i)(position * lac) * per;

		//Apply scaling factor
		return sum * (persistence - 1) / (per - 1);
	}

	/// Calculates the sum of several noise functions (called octaves) with
	/// rising frequency and decreasing amplitude, multiplied by the previous
	/// octave.
	///
	/// @param begin the beginning of the list of noise functions
	/// @param end the end of the list of noise functions
	/// @param lacunarity the proportional change in frequency (a value of 2 will give each octave double the frequency of the one before it).
	/// @param persistence is the the proportional change in amplitude (a value of 0.5 will give each octave half the amplitude of the one before it). The function will return NaN if persistence is exactly 1.0.
	/// @param position the position of the noise value to return.
	template <typename Iterator, typename Derived>s
	typename Derived::Scalar fractal_feedback(Iterator begin,
	                                          Iterator end,
	                                          typename Derived::Scalar lacunarity,
	                                          typename Derived::Scalar persistence,
	                                          const Eigen::MatrixBase<Derived>& position)
	{
		EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived);

		typename Derived::Scalar sum = 0, per = 1, lac = 1, prev = 1;
		for (Iterator i = begin; i != end; i++, per *= persistence, lac *= lacunarity)
		{
			typename Derived::Scalar current = (*i)(position * lac) * per;
			sum += current * prev;
			prev = current;
		}

		//Apply scaling factor
		return sum * (persistence - 1) / (per - 1);
	}
}
#endif
<commit_msg>Update fractal.hpp<commit_after>//Copyright (c) 2013 Mike Pedersen
//See the file license.txt for copying permission.

#ifndef COHERENT_FRACTAL_HPP
#define COHERENT_FRACTAL_HPP

#include <cassert>
#include <Eigen/Core>

namespace coherent
{
	/// Calculates the sum of several noise functions (called octaves) with
	/// rising frequency and decreasing amplitude. The range of the return type
	/// will be the same as the noise functions (usually [0,1]).
	///
	/// @param begin the beginning of the list of noise functions
	/// @param end the end of the list of noise functions
	/// @param lacunarity the proportional change in frequency (a value of 2 will
	/// give each octave double the frequency of the one before it).
	/// @param persistence is the the proportional change in amplitude (a value of 0.5 will give each octave half the amplitude of the one before it). The function will return NaN if persistence is exactly 1.0.
	/// @param position the position of the noise value to return.
	template <typename Iterator, typename Derived>
	typename Derived::Scalar fractal(Iterator begin,
	                                 Iterator end,
	                                 typename Derived::Scalar lacunarity,
	                                 typename Derived::Scalar persistence,
	                                 const Eigen::MatrixBase<Derived>& position)
	{
		EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived);

		typename Derived::Scalar sum = 0, per = 1, lac = 1;
		for (Iterator i = begin; i != end; i++, per *= persistence, lac *= lacunarity)
			sum += (*i)(position * lac) * per;

		//Apply scaling factor
		return sum * (persistence - 1) / (per - 1);
	}

	/// Calculates the sum of several noise functions (called octaves) with
	/// rising frequency and decreasing amplitude, multiplied by the previous
	/// octave.
	///
	/// @param begin the beginning of the list of noise functions
	/// @param end the end of the list of noise functions
	/// @param lacunarity the proportional change in frequency (a value of 2 will give each octave double the frequency of the one before it).
	/// @param persistence is the the proportional change in amplitude (a value of 0.5 will give each octave half the amplitude of the one before it). The function will return NaN if persistence is exactly 1.0.
	/// @param position the position of the noise value to return.
	template <typename Iterator, typename Derived>
	typename Derived::Scalar fractal_feedback(Iterator begin,
	                                          Iterator end,
	                                          typename Derived::Scalar lacunarity,
	                                          typename Derived::Scalar persistence,
	                                          const Eigen::MatrixBase<Derived>& position)
	{
		EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived);

		typename Derived::Scalar sum = 0, per = 1, lac = 1, prev = 1;
		for (Iterator i = begin; i != end; i++, per *= persistence, lac *= lacunarity)
		{
			typename Derived::Scalar current = (*i)(position * lac) * per;
			sum += current * prev;
			prev = current;
		}

		//Apply scaling factor
		return sum * (persistence - 1) / (per - 1);
	}
}
#endif
<|endoftext|>