diff --git a/include/SFML/Audio/Sound.hpp b/include/SFML/Audio/Sound.hpp index 74b88269f..6c346ba02 100644 --- a/include/SFML/Audio/Sound.hpp +++ b/include/SFML/Audio/Sound.hpp @@ -144,7 +144,9 @@ public : /// \brief Change the current playing position of the sound /// /// The playing position can be changed when the sound is - /// either paused or playing. + /// either paused or playing. Changing the playing position + /// when the sound is stopped has no effect, since playing + /// the sound would reset its position. /// /// \param timeOffset New playing position, from the beginning of the sound /// diff --git a/include/SFML/Audio/SoundStream.hpp b/include/SFML/Audio/SoundStream.hpp index c66daf350..348f562a6 100644 --- a/include/SFML/Audio/SoundStream.hpp +++ b/include/SFML/Audio/SoundStream.hpp @@ -32,6 +32,7 @@ #include #include #include +#include #include @@ -65,8 +66,8 @@ public : /// \brief Start or resume playing the audio stream /// /// This function starts the stream if it was stopped, resumes - /// it if it was paused, and restarts it from beginning if it - /// was it already playing. + /// it if it was paused, and restarts it from the beginning if + /// it was already playing. /// This function uses its own thread so that it doesn't block /// the rest of the program while the stream is played. /// @@ -131,7 +132,9 @@ public : /// \brief Change the current playing position of the stream /// /// The playing position can be changed when the stream is - /// either paused or playing. + /// either paused or playing. Changing the playing position + /// when the stream is stopped has no effect, since playing + /// the stream would reset its position. /// /// \param timeOffset New playing position, from the beginning of the stream /// @@ -285,6 +288,8 @@ private : // Member data //////////////////////////////////////////////////////////// Thread m_thread; ///< Thread running the background tasks + mutable Mutex m_threadMutex; ///< Thread mutex + Status m_threadStartState; ///< State the thread starts in (Playing, Paused, Stopped) bool m_isStreaming; ///< Streaming state (true = playing, false = stopped) unsigned int m_buffers[BufferCount]; ///< Sound buffers used to store temporary audio data unsigned int m_channelCount; ///< Number of channels (1 = mono, 2 = stereo, ...) diff --git a/include/SFML/Network/Ftp.hpp b/include/SFML/Network/Ftp.hpp index 1b43078f6..a9a677ce6 100644 --- a/include/SFML/Network/Ftp.hpp +++ b/include/SFML/Network/Ftp.hpp @@ -229,7 +229,7 @@ public : /// \param data Data containing the raw listing /// //////////////////////////////////////////////////////////// - ListingResponse(const Response& response, const std::vector& data); + ListingResponse(const Response& response, const std::string& data); //////////////////////////////////////////////////////////// /// \brief Return the array of directory/file names @@ -452,6 +452,9 @@ public : /// current working directory of the server, and the local /// destination path is relative to the current directory /// of your application. + /// If a file with the same filename as the distant file + /// already exists in the local destination path, it will + /// be overwritten. /// /// \param remoteFile Filename of the distant file to download /// \param localPath The directory in which to put the file on the local computer diff --git a/include/SFML/Window/Window.hpp b/include/SFML/Window/Window.hpp index fbff7f9a6..4aa191fe9 100644 --- a/include/SFML/Window/Window.hpp +++ b/include/SFML/Window/Window.hpp @@ -306,7 +306,9 @@ public : /// /// \param width Icon's width, in pixels /// \param height Icon's height, in pixels - /// \param pixels Pointer to the array of pixels in memory + /// \param pixels Pointer to the array of pixels in memory. The + /// pixels are copied, so you need not keep the + /// source alive after calling this function. /// /// \see setTitle /// diff --git a/src/SFML/Audio/SoundStream.cpp b/src/SFML/Audio/SoundStream.cpp index 972bdbf9d..eb84f25f0 100644 --- a/src/SFML/Audio/SoundStream.cpp +++ b/src/SFML/Audio/SoundStream.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #ifdef _MSC_VER #pragma warning(disable : 4355) // 'this' used in base member initializer list @@ -41,6 +42,8 @@ namespace sf //////////////////////////////////////////////////////////// SoundStream::SoundStream() : m_thread (&SoundStream::streamData, this), +m_threadMutex (), +m_threadStartState(Stopped), m_isStreaming (false), m_channelCount (0), m_sampleRate (0), @@ -56,7 +59,15 @@ m_samplesProcessed(0) SoundStream::~SoundStream() { // Stop the sound if it was playing - stop(); + + // Request the thread to terminate + { + Lock lock(m_threadMutex); + m_isStreaming = false; + } + + // Wait for the thread to terminate + m_thread.wait(); } @@ -89,12 +100,30 @@ void SoundStream::play() return; } - // If the sound is already playing (probably paused), just resume it - if (m_isStreaming) + bool isStreaming = false; + Status threadStartState = Stopped; + { + Lock lock(m_threadMutex); + + isStreaming = m_isStreaming; + threadStartState = m_threadStartState; + } + + + if (isStreaming && (threadStartState == Paused)) + { + // If the sound is paused, resume it + Lock lock(m_threadMutex); + m_threadStartState = Playing; alCheck(alSourcePlay(m_source)); return; } + else if (isStreaming && (threadStartState == Playing)) + { + // If the sound is playing, stop it and continue as if it was stopped + stop(); + } // Move to the beginning onSeek(Time::Zero); @@ -102,6 +131,7 @@ void SoundStream::play() // Start updating the stream in a separate thread to avoid blocking the application m_samplesProcessed = 0; m_isStreaming = true; + m_threadStartState = Playing; m_thread.launch(); } @@ -109,6 +139,16 @@ void SoundStream::play() //////////////////////////////////////////////////////////// void SoundStream::pause() { + // Handle pause() being called before the thread has started + { + Lock lock(m_threadMutex); + + if (!m_isStreaming) + return; + + m_threadStartState = Paused; + } + alCheck(alSourcePause(m_source)); } @@ -116,9 +156,20 @@ void SoundStream::pause() //////////////////////////////////////////////////////////// void SoundStream::stop() { + // Request the thread to terminate + { + Lock lock(m_threadMutex); + m_isStreaming = false; + } + // Wait for the thread to terminate - m_isStreaming = false; m_thread.wait(); + + // Move to the beginning + onSeek(Time::Zero); + + // Reset the playing position + m_samplesProcessed = 0; } @@ -142,8 +193,13 @@ SoundStream::Status SoundStream::getStatus() const Status status = SoundSource::getStatus(); // To compensate for the lag between play() and alSourceplay() - if ((status == Stopped) && m_isStreaming) - status = Playing; + if (status == Stopped) + { + Lock lock(m_threadMutex); + + if (m_isStreaming) + status = m_threadStartState; + } return status; } @@ -152,6 +208,9 @@ SoundStream::Status SoundStream::getStatus() const //////////////////////////////////////////////////////////// void SoundStream::setPlayingOffset(Time timeOffset) { + // Get old playing status + Status oldStatus = getStatus(); + // Stop the stream stop(); @@ -160,7 +219,12 @@ void SoundStream::setPlayingOffset(Time timeOffset) // Restart streaming m_samplesProcessed = static_cast(timeOffset.asSeconds() * m_sampleRate * m_channelCount); + + if (oldStatus == Stopped) + return; + m_isStreaming = true; + m_threadStartState = oldStatus; m_thread.launch(); } @@ -199,19 +263,42 @@ bool SoundStream::getLoop() const //////////////////////////////////////////////////////////// void SoundStream::streamData() { - // Create the buffers - alCheck(alGenBuffers(BufferCount, m_buffers)); - for (int i = 0; i < BufferCount; ++i) - m_endBuffers[i] = false; + bool requestStop = false; - // Fill the queue - bool requestStop = fillQueue(); - - // Play the sound - alCheck(alSourcePlay(m_source)); - - while (m_isStreaming) { + Lock lock(m_threadMutex); + + // Check if the thread was launched Stopped + if (m_threadStartState == Stopped) + { + m_isStreaming = false; + return; + } + + // Create the buffers + alCheck(alGenBuffers(BufferCount, m_buffers)); + for (int i = 0; i < BufferCount; ++i) + m_endBuffers[i] = false; + + // Fill the queue + requestStop = fillQueue(); + + // Play the sound + alCheck(alSourcePlay(m_source)); + + // Check if the thread was launched Paused + if (m_threadStartState == Paused) + alCheck(alSourcePause(m_source)); + } + + for (;;) + { + { + Lock lock(m_threadMutex); + if (!m_isStreaming) + break; + } + // The stream has been interrupted! if (SoundSource::getStatus() == Stopped) { @@ -223,6 +310,7 @@ void SoundStream::streamData() else { // End streaming + Lock lock(m_threadMutex); m_isStreaming = false; } } @@ -266,6 +354,7 @@ void SoundStream::streamData() << "and initialize() has been called correctly" << std::endl; // Abort streaming (exit main loop) + Lock lock(m_threadMutex); m_isStreaming = false; requestStop = true; break; diff --git a/src/SFML/Network/Ftp.cpp b/src/SFML/Network/Ftp.cpp index e847e780c..ddaad4084 100644 --- a/src/SFML/Network/Ftp.cpp +++ b/src/SFML/Network/Ftp.cpp @@ -27,11 +27,13 @@ //////////////////////////////////////////////////////////// #include #include +#include #include #include #include #include #include +#include namespace sf @@ -48,10 +50,10 @@ public : Ftp::Response open(Ftp::TransferMode mode); //////////////////////////////////////////////////////////// - void send(const std::vector& data); + void send(std::istream& stream); //////////////////////////////////////////////////////////// - void receive(std::vector& data); + void receive(std::ostream& stream); private : @@ -115,17 +117,16 @@ const std::string& Ftp::DirectoryResponse::getDirectory() const //////////////////////////////////////////////////////////// -Ftp::ListingResponse::ListingResponse(const Ftp::Response& response, const std::vector& data) : +Ftp::ListingResponse::ListingResponse(const Ftp::Response& response, const std::string& data) : Ftp::Response(response) { if (isOk()) { // Fill the array of strings - std::string paths(data.begin(), data.end()); std::string::size_type lastPos = 0; - for (std::string::size_type pos = paths.find("\r\n"); pos != std::string::npos; pos = paths.find("\r\n", lastPos)) + for (std::string::size_type pos = data.find("\r\n"); pos != std::string::npos; pos = data.find("\r\n", lastPos)) { - m_listing.push_back(paths.substr(lastPos, pos - lastPos)); + m_listing.push_back(data.substr(lastPos, pos - lastPos)); lastPos = pos + 2; } } @@ -206,7 +207,7 @@ Ftp::DirectoryResponse Ftp::getWorkingDirectory() Ftp::ListingResponse Ftp::getDirectoryListing(const std::string& directory) { // Open a data channel on default port (20) using ASCII transfer mode - std::vector directoryData; + std::ostringstream directoryData; DataChannel data(*this); Response response = data.open(Ascii); if (response.isOk()) @@ -223,7 +224,7 @@ Ftp::ListingResponse Ftp::getDirectoryListing(const std::string& directory) } } - return ListingResponse(response, directoryData); + return ListingResponse(response, directoryData.str()); } @@ -285,33 +286,34 @@ Ftp::Response Ftp::download(const std::string& remoteFile, const std::string& lo response = sendCommand("RETR", remoteFile); if (response.isOk()) { + // Extract the filename from the file path + std::string filename = remoteFile; + std::string::size_type pos = filename.find_last_of("/\\"); + if (pos != std::string::npos) + filename = filename.substr(pos + 1); + + // Make sure the destination path ends with a slash + std::string path = localPath; + if (!path.empty() && (path[path.size() - 1] != '\\') && (path[path.size() - 1] != '/')) + path += "/"; + + // Create the file and truncate it if necessary + std::ofstream file((path + filename).c_str(), std::ios_base::binary | std::ios_base::trunc); + if (!file) + return Response(Response::InvalidFile); + // Receive the file data - std::vector fileData; - data.receive(fileData); + data.receive(file); + + // Close the file + file.close(); // Get the response from the server response = getResponse(); - if (response.isOk()) - { - // Extract the filename from the file path - std::string filename = remoteFile; - std::string::size_type pos = filename.find_last_of("/\\"); - if (pos != std::string::npos) - filename = filename.substr(pos + 1); - // Make sure the destination path ends with a slash - std::string path = localPath; - if (!path.empty() && (path[path.size() - 1] != '\\') && (path[path.size() - 1] != '/')) - path += "/"; - - // Create the file and copy the received data into it - std::ofstream file((path + filename).c_str(), std::ios_base::binary); - if (!file) - return Response(Response::InvalidFile); - - if (!fileData.empty()) - file.write(&fileData[0], static_cast(fileData.size())); - } + // If the download was unsuccessful, delete the partial file + if (!response.isOk()) + std::remove((path + filename).c_str()); } } @@ -327,13 +329,6 @@ Ftp::Response Ftp::upload(const std::string& localFile, const std::string& remot if (!file) return Response(Response::InvalidFile); - file.seekg(0, std::ios::end); - std::size_t length = static_cast(file.tellg()); - file.seekg(0, std::ios::beg); - std::vector fileData(length); - if (length > 0) - file.read(&fileData[0], static_cast(length)); - // Extract the filename from the file path std::string filename = localFile; std::string::size_type pos = filename.find_last_of("/\\"); @@ -355,7 +350,7 @@ Ftp::Response Ftp::upload(const std::string& localFile, const std::string& remot if (response.isOk()) { // Send the file data - data.send(fileData); + data.send(file); // Get the response from the server response = getResponse(); @@ -584,15 +579,20 @@ Ftp::Response Ftp::DataChannel::open(Ftp::TransferMode mode) //////////////////////////////////////////////////////////// -void Ftp::DataChannel::receive(std::vector& data) +void Ftp::DataChannel::receive(std::ostream& stream) { // Receive data - data.clear(); char buffer[1024]; std::size_t received; while (m_dataSocket.receive(buffer, sizeof(buffer), received) == Socket::Done) { - std::copy(buffer, buffer + received, std::back_inserter(data)); + stream.write(buffer, static_cast(received)); + + if (!stream.good()) + { + err() << "FTP Error: Writing to the file has failed" << std::endl; + break; + } } // Close the data socket @@ -601,11 +601,37 @@ void Ftp::DataChannel::receive(std::vector& data) //////////////////////////////////////////////////////////// -void Ftp::DataChannel::send(const std::vector& data) +void Ftp::DataChannel::send(std::istream& stream) { // Send data - if (!data.empty()) - m_dataSocket.send(&data[0], data.size()); + char buffer[1024]; + std::size_t count; + + for (;;) + { + // read some data from the stream + stream.read(buffer, sizeof(buffer)); + + if (!stream.good() && !stream.eof()) + { + err() << "FTP Error: Reading from the file has failed" << std::endl; + break; + } + + count = stream.gcount(); + + if (count > 0) + { + // we could read more data from the stream: send them + if (m_dataSocket.send(buffer, count) != Socket::Done) + break; + } + else + { + // no more data: exit the loop + break; + } + } // Close the data socket m_dataSocket.disconnect(); diff --git a/src/SFML/Window/EglContext.cpp b/src/SFML/Window/EglContext.cpp index aa751ba80..d18396f12 100644 --- a/src/SFML/Window/EglContext.cpp +++ b/src/SFML/Window/EglContext.cpp @@ -180,7 +180,8 @@ bool EglContext::makeCurrent() //////////////////////////////////////////////////////////// void EglContext::display() { - eglCheck(eglSwapBuffers(m_display, m_surface)); + if (m_surface != EGL_NO_SURFACE) + eglCheck(eglSwapBuffers(m_display, m_surface)); }