<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/audio/pulse/pulse_output.h"

#include <pulse/pulseaudio.h>

#include "base/message_loop.h"
#include "media/audio/audio_manager_base.h"
#include "media/audio/audio_parameters.h"
#include "media/audio/audio_util.h"
#include "media/audio/pulse/pulse_util.h"

namespace media {

using pulse::AutoPulseLock;
using pulse::WaitForOperationCompletion;

// static, pa_context_notify_cb
void PulseAudioOutputStream::ContextNotifyCallback(pa_context* c,
                                                   void* p_this) {
  PulseAudioOutputStream* stream = static_cast<PulseAudioOutputStream*>(p_this);

  // Forward unexpected failures to the AudioSourceCallback if available.  All
  // these variables are only modified under pa_threaded_mainloop_lock() so this
  // should be thread safe.
  if (c && stream->source_callback_ &&
      pa_context_get_state(c) == PA_CONTEXT_FAILED) {
    stream->source_callback_->OnError(stream, pa_context_errno(c));
  }

  pa_threaded_mainloop_signal(stream->pa_mainloop_, 0);
}

// static, pa_stream_notify_cb
void PulseAudioOutputStream::StreamNotifyCallback(pa_stream* s, void* p_this) {
  PulseAudioOutputStream* stream = static_cast<PulseAudioOutputStream*>(p_this);

  // Forward unexpected failures to the AudioSourceCallback if available.  All
  // these variables are only modified under pa_threaded_mainloop_lock() so this
  // should be thread safe.
  if (s && stream->source_callback_ &&
      pa_stream_get_state(s) == PA_STREAM_FAILED) {
    stream->source_callback_->OnError(
        stream, pa_context_errno(stream->pa_context_));
  }

  pa_threaded_mainloop_signal(stream->pa_mainloop_, 0);
}

// static, pa_stream_request_cb_t
void PulseAudioOutputStream::StreamRequestCallback(pa_stream* s, size_t len,
                                                   void* p_this) {
  // Fulfill write request; must always result in a pa_stream_write() call.
  static_cast<PulseAudioOutputStream*>(p_this)->FulfillWriteRequest(len);
}

PulseAudioOutputStream::PulseAudioOutputStream(const AudioParameters& params,
                                               AudioManagerBase* manager)
    : params_(params),
      manager_(manager),
      pa_context_(NULL),
      pa_mainloop_(NULL),
      pa_stream_(NULL),
      volume_(1.0f),
      source_callback_(NULL) {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());

  CHECK(params_.IsValid());
  audio_bus_ = AudioBus::Create(params_);
}

PulseAudioOutputStream::~PulseAudioOutputStream() {
  // All internal structures should already have been freed in Close(), which
  // calls AudioManagerBase::ReleaseOutputStream() which deletes this object.
  DCHECK(!pa_stream_);
  DCHECK(!pa_context_);
  DCHECK(!pa_mainloop_);
}

// Helper macro for Open() to avoid code spam and string bloat.
#define RETURN_ON_FAILURE(expression, message) do { \
  if (!(expression)) { \
    if (pa_context_) { \
      DLOG(ERROR) << message << "  Error: " \
                  << pa_strerror(pa_context_errno(pa_context_)); \
    } else { \
      DLOG(ERROR) << message; \
    } \
    return false; \
  } \
} while(0)

bool PulseAudioOutputStream::Open() {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());

  pa_mainloop_ = pa_threaded_mainloop_new();
  RETURN_ON_FAILURE(pa_mainloop_, "Failed to create PulseAudio main loop.");

  pa_mainloop_api* pa_mainloop_api = pa_threaded_mainloop_get_api(pa_mainloop_);
  pa_context_ = pa_context_new(pa_mainloop_api, "Chromium");
  RETURN_ON_FAILURE(pa_context_, "Failed to create PulseAudio context.");

  // A state callback must be set before calling pa_threaded_mainloop_lock() or
  // pa_threaded_mainloop_wait() calls may lead to dead lock.
  pa_context_set_state_callback(pa_context_, &ContextNotifyCallback, this);

  // Lock the main loop while setting up the context.  Failure to do so may lead
  // to crashes as the PulseAudio thread tries to run before things are ready.
  AutoPulseLock auto_lock(pa_mainloop_);

  RETURN_ON_FAILURE(pa_threaded_mainloop_start(pa_mainloop_) == 0,
                    "Failed to start PulseAudio main loop.");
  RETURN_ON_FAILURE(
      pa_context_connect(pa_context_, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) == 0,
      "Failed to connect PulseAudio context.");

  // Wait until |pa_context_| is ready.  pa_threaded_mainloop_wait() must be
  // called after pa_context_get_state() in case the context is already ready,
  // otherwise pa_threaded_mainloop_wait() will hang indefinitely.
  while (true) {
    pa_context_state_t context_state = pa_context_get_state(pa_context_);
    RETURN_ON_FAILURE(
        PA_CONTEXT_IS_GOOD(context_state), "Invalid PulseAudio context state.");
    if (context_state == PA_CONTEXT_READY)
      break;
    pa_threaded_mainloop_wait(pa_mainloop_);
  }

  // Set sample specifications.
  pa_sample_spec pa_sample_specifications;
  pa_sample_specifications.format = pulse::BitsToPASampleFormat(
      params_.bits_per_sample());
  pa_sample_specifications.rate = params_.sample_rate();
  pa_sample_specifications.channels = params_.channels();

  // Get channel mapping and open playback stream.
  pa_channel_map* map = NULL;
  pa_channel_map source_channel_map = pulse::ChannelLayoutToPAChannelMap(
      params_.channel_layout());
  if (source_channel_map.channels != 0) {
    // The source data uses a supported channel map so we will use it rather
    // than the default channel map (NULL).
    map = &source_channel_map;
  }
  pa_stream_ = pa_stream_new(
      pa_context_, "Playback", &pa_sample_specifications, map);
  RETURN_ON_FAILURE(pa_stream_, "Failed to create PulseAudio stream.");
  pa_stream_set_state_callback(pa_stream_, &StreamNotifyCallback, this);

  // Even though we start the stream corked below, PulseAudio will issue one
  // stream request after setup.  FulfillWriteRequest() must fulfill the write.
  pa_stream_set_write_callback(pa_stream_, &StreamRequestCallback, this);

  // Tell pulse audio we only want callbacks of a certain size.
  pa_buffer_attr pa_buffer_attributes;
  pa_buffer_attributes.maxlength = params_.GetBytesPerBuffer();
  pa_buffer_attributes.minreq = params_.GetBytesPerBuffer();
  pa_buffer_attributes.prebuf = params_.GetBytesPerBuffer();
  pa_buffer_attributes.tlength = params_.GetBytesPerBuffer();
  pa_buffer_attributes.fragsize = static_cast<uint32_t>(-1);

  // Connect playback stream.
  // TODO(dalecurtis): Pulse tends to want really large buffer sizes if we are
  // not using the native sample rate.  We should always open the stream with
  // PA_STREAM_FIX_RATE and ensure this is true.
  RETURN_ON_FAILURE(
      pa_stream_connect_playback(
          pa_stream_, NULL, &pa_buffer_attributes,
          static_cast<pa_stream_flags_t>(
              PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE |
              PA_STREAM_NOT_MONOTONIC | PA_STREAM_START_CORKED),
          NULL, NULL) == 0,
      "Failed to connect PulseAudio stream.");

  // Wait for the stream to be ready.
  while (true) {
    pa_stream_state_t stream_state = pa_stream_get_state(pa_stream_);
    RETURN_ON_FAILURE(
        PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state.");
    if (stream_state == PA_STREAM_READY)
      break;
    pa_threaded_mainloop_wait(pa_mainloop_);
  }

  return true;
}

#undef RETURN_ON_FAILURE

void PulseAudioOutputStream::Reset() {
  if (!pa_mainloop_) {
    DCHECK(!pa_stream_);
    DCHECK(!pa_context_);
    return;
  }

  {
    AutoPulseLock auto_lock(pa_mainloop_);

    // Close the stream.
    if (pa_stream_) {
      // Ensure all samples are played out before shutdown.
      pa_operation* operation = pa_stream_flush(
          pa_stream_, &pulse::StreamSuccessCallback, pa_mainloop_);
      WaitForOperationCompletion(pa_mainloop_, operation);

      // Release PulseAudio structures.
      pa_stream_disconnect(pa_stream_);
      pa_stream_set_write_callback(pa_stream_, NULL, NULL);
      pa_stream_set_state_callback(pa_stream_, NULL, NULL);
      pa_stream_unref(pa_stream_);
      pa_stream_ = NULL;
    }

    if (pa_context_) {
      pa_context_disconnect(pa_context_);
      pa_context_set_state_callback(pa_context_, NULL, NULL);
      pa_context_unref(pa_context_);
      pa_context_ = NULL;
    }
  }

  pa_threaded_mainloop_stop(pa_mainloop_);
  pa_threaded_mainloop_free(pa_mainloop_);
  pa_mainloop_ = NULL;
}

void PulseAudioOutputStream::Close() {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());

  Reset();

  // Signal to the manager that we're closed and can be removed.
  // This should be the last call in the function as it deletes "this".
  manager_->ReleaseOutputStream(this);
}

void PulseAudioOutputStream::FulfillWriteRequest(size_t requested_bytes) {
  CHECK_EQ(requested_bytes, static_cast<size_t>(params_.GetBytesPerBuffer()));

  int frames_filled = 0;
  if (source_callback_) {
    uint32 hardware_delay = pulse::GetHardwareLatencyInBytes(
        pa_stream_, params_.sample_rate(),
        params_.GetBytesPerFrame());
    frames_filled = source_callback_->OnMoreData(
        audio_bus_.get(), AudioBuffersState(0, hardware_delay));
  }

  // Zero any unfilled data so it plays back as silence.
  if (frames_filled < audio_bus_->frames()) {
    audio_bus_->ZeroFramesPartial(
        frames_filled, audio_bus_->frames() - frames_filled);
  }

  // PulseAudio won't always be able to provide a buffer large enough, so we may
  // need to request multiple buffers and fill them individually.
  int current_frame = 0;
  size_t bytes_remaining = requested_bytes;
  while (bytes_remaining > 0) {
    void* buffer = NULL;
    size_t bytes_to_fill = bytes_remaining;
    CHECK_GE(pa_stream_begin_write(pa_stream_, &buffer, &bytes_to_fill), 0);

    // In case PulseAudio gives us a bigger buffer than we want, cap our size.
    bytes_to_fill = std::min(
        std::min(bytes_remaining, bytes_to_fill),
        static_cast<size_t>(params_.GetBytesPerBuffer()));

    int frames_to_fill = bytes_to_fill / params_.GetBytesPerFrame();;

    // Note: If this ever changes to output raw float the data must be clipped
    // and sanitized since it may come from an untrusted source such as NaCl.
    audio_bus_->ToInterleavedPartial(
        current_frame, frames_to_fill, params_.bits_per_sample() / 8, buffer);
    media::AdjustVolume(buffer, bytes_to_fill, params_.channels(),
                        params_.bits_per_sample() / 8, volume_);

    if (pa_stream_write(pa_stream_, buffer, bytes_to_fill, NULL, 0LL,
                        PA_SEEK_RELATIVE) < 0) {
      if (source_callback_) {
        source_callback_->OnError(this, pa_context_errno(pa_context_));
      }
    }

    bytes_remaining -= bytes_to_fill;
    current_frame = frames_to_fill;
  }
}

void PulseAudioOutputStream::Start(AudioSourceCallback* callback) {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
  CHECK(callback);
  CHECK(pa_stream_);

  AutoPulseLock auto_lock(pa_mainloop_);

  // Ensure the context and stream are ready.
  if (pa_context_get_state(pa_context_) != PA_CONTEXT_READY &&
      pa_stream_get_state(pa_stream_) != PA_STREAM_READY) {
    callback->OnError(this, pa_context_errno(pa_context_));
    return;
  }

  source_callback_ = callback;

  // Uncork (resume) the stream.
  pa_operation* operation = pa_stream_cork(
      pa_stream_, 0, &pulse::StreamSuccessCallback, pa_mainloop_);
  WaitForOperationCompletion(pa_mainloop_, operation);
}

void PulseAudioOutputStream::Stop() {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());

  // Cork (pause) the stream.  Waiting for the main loop lock will ensure
  // outstanding callbacks have completed.
  AutoPulseLock auto_lock(pa_mainloop_);

  // Flush the stream prior to cork, doing so after will cause hangs.  Write
  // callbacks are suspended while inside pa_threaded_mainloop_lock() so this
  // is all thread safe.
  pa_operation* operation = pa_stream_flush(
      pa_stream_, &pulse::StreamSuccessCallback, pa_mainloop_);
  WaitForOperationCompletion(pa_mainloop_, operation);

  operation = pa_stream_cork(pa_stream_, 1, &pulse::StreamSuccessCallback,
                             pa_mainloop_);
  WaitForOperationCompletion(pa_mainloop_, operation);

  source_callback_ = NULL;
}

void PulseAudioOutputStream::SetVolume(double volume) {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());

  volume_ = static_cast<float>(volume);
}

void PulseAudioOutputStream::GetVolume(double* volume) {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());

  *volume = volume_;
}

}  // namespace media
<commit_msg>Always fully fill PulseAudio's requested buffer. Allow larger initial requests.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/audio/pulse/pulse_output.h"

#include <pulse/pulseaudio.h>

#include "base/message_loop.h"
#include "media/audio/audio_manager_base.h"
#include "media/audio/audio_parameters.h"
#include "media/audio/audio_util.h"
#include "media/audio/pulse/pulse_util.h"

namespace media {

using pulse::AutoPulseLock;
using pulse::WaitForOperationCompletion;

// static, pa_context_notify_cb
void PulseAudioOutputStream::ContextNotifyCallback(pa_context* c,
                                                   void* p_this) {
  PulseAudioOutputStream* stream = static_cast<PulseAudioOutputStream*>(p_this);

  // Forward unexpected failures to the AudioSourceCallback if available.  All
  // these variables are only modified under pa_threaded_mainloop_lock() so this
  // should be thread safe.
  if (c && stream->source_callback_ &&
      pa_context_get_state(c) == PA_CONTEXT_FAILED) {
    stream->source_callback_->OnError(stream, pa_context_errno(c));
  }

  pa_threaded_mainloop_signal(stream->pa_mainloop_, 0);
}

// static, pa_stream_notify_cb
void PulseAudioOutputStream::StreamNotifyCallback(pa_stream* s, void* p_this) {
  PulseAudioOutputStream* stream = static_cast<PulseAudioOutputStream*>(p_this);

  // Forward unexpected failures to the AudioSourceCallback if available.  All
  // these variables are only modified under pa_threaded_mainloop_lock() so this
  // should be thread safe.
  if (s && stream->source_callback_ &&
      pa_stream_get_state(s) == PA_STREAM_FAILED) {
    stream->source_callback_->OnError(
        stream, pa_context_errno(stream->pa_context_));
  }

  pa_threaded_mainloop_signal(stream->pa_mainloop_, 0);
}

// static, pa_stream_request_cb_t
void PulseAudioOutputStream::StreamRequestCallback(pa_stream* s, size_t len,
                                                   void* p_this) {
  // Fulfill write request; must always result in a pa_stream_write() call.
  static_cast<PulseAudioOutputStream*>(p_this)->FulfillWriteRequest(len);
}

PulseAudioOutputStream::PulseAudioOutputStream(const AudioParameters& params,
                                               AudioManagerBase* manager)
    : params_(params),
      manager_(manager),
      pa_context_(NULL),
      pa_mainloop_(NULL),
      pa_stream_(NULL),
      volume_(1.0f),
      source_callback_(NULL) {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());

  CHECK(params_.IsValid());
  audio_bus_ = AudioBus::Create(params_);
}

PulseAudioOutputStream::~PulseAudioOutputStream() {
  // All internal structures should already have been freed in Close(), which
  // calls AudioManagerBase::ReleaseOutputStream() which deletes this object.
  DCHECK(!pa_stream_);
  DCHECK(!pa_context_);
  DCHECK(!pa_mainloop_);
}

// Helper macro for Open() to avoid code spam and string bloat.
#define RETURN_ON_FAILURE(expression, message) do { \
  if (!(expression)) { \
    if (pa_context_) { \
      DLOG(ERROR) << message << "  Error: " \
                  << pa_strerror(pa_context_errno(pa_context_)); \
    } else { \
      DLOG(ERROR) << message; \
    } \
    return false; \
  } \
} while(0)

bool PulseAudioOutputStream::Open() {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());

  pa_mainloop_ = pa_threaded_mainloop_new();
  RETURN_ON_FAILURE(pa_mainloop_, "Failed to create PulseAudio main loop.");

  pa_mainloop_api* pa_mainloop_api = pa_threaded_mainloop_get_api(pa_mainloop_);
  pa_context_ = pa_context_new(pa_mainloop_api, "Chromium");
  RETURN_ON_FAILURE(pa_context_, "Failed to create PulseAudio context.");

  // A state callback must be set before calling pa_threaded_mainloop_lock() or
  // pa_threaded_mainloop_wait() calls may lead to dead lock.
  pa_context_set_state_callback(pa_context_, &ContextNotifyCallback, this);

  // Lock the main loop while setting up the context.  Failure to do so may lead
  // to crashes as the PulseAudio thread tries to run before things are ready.
  AutoPulseLock auto_lock(pa_mainloop_);

  RETURN_ON_FAILURE(pa_threaded_mainloop_start(pa_mainloop_) == 0,
                    "Failed to start PulseAudio main loop.");
  RETURN_ON_FAILURE(
      pa_context_connect(pa_context_, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) == 0,
      "Failed to connect PulseAudio context.");

  // Wait until |pa_context_| is ready.  pa_threaded_mainloop_wait() must be
  // called after pa_context_get_state() in case the context is already ready,
  // otherwise pa_threaded_mainloop_wait() will hang indefinitely.
  while (true) {
    pa_context_state_t context_state = pa_context_get_state(pa_context_);
    RETURN_ON_FAILURE(
        PA_CONTEXT_IS_GOOD(context_state), "Invalid PulseAudio context state.");
    if (context_state == PA_CONTEXT_READY)
      break;
    pa_threaded_mainloop_wait(pa_mainloop_);
  }

  // Set sample specifications.
  pa_sample_spec pa_sample_specifications;
  pa_sample_specifications.format = pulse::BitsToPASampleFormat(
      params_.bits_per_sample());
  pa_sample_specifications.rate = params_.sample_rate();
  pa_sample_specifications.channels = params_.channels();

  // Get channel mapping and open playback stream.
  pa_channel_map* map = NULL;
  pa_channel_map source_channel_map = pulse::ChannelLayoutToPAChannelMap(
      params_.channel_layout());
  if (source_channel_map.channels != 0) {
    // The source data uses a supported channel map so we will use it rather
    // than the default channel map (NULL).
    map = &source_channel_map;
  }
  pa_stream_ = pa_stream_new(
      pa_context_, "Playback", &pa_sample_specifications, map);
  RETURN_ON_FAILURE(pa_stream_, "Failed to create PulseAudio stream.");
  pa_stream_set_state_callback(pa_stream_, &StreamNotifyCallback, this);

  // Even though we start the stream corked below, PulseAudio will issue one
  // stream request after setup.  FulfillWriteRequest() must fulfill the write.
  pa_stream_set_write_callback(pa_stream_, &StreamRequestCallback, this);

  // Pulse is very finicky with the small buffer sizes used by Chrome.  The
  // settings below are mostly found through trial and error.  Essentially we
  // want Pulse to auto size its internal buffers, but call us back nearly every
  // |minreq| bytes.  |tlength| should be a multiple of |minreq|; too low and
  // Pulse will issue callbacks way too fast, too high and we don't get
  // callbacks frequently enough.
  pa_buffer_attr pa_buffer_attributes;
  pa_buffer_attributes.maxlength = static_cast<uint32_t>(-1);
  pa_buffer_attributes.minreq = params_.GetBytesPerBuffer();
  pa_buffer_attributes.prebuf = static_cast<uint32_t>(-1);
  pa_buffer_attributes.tlength = params_.GetBytesPerBuffer() * 3;
  pa_buffer_attributes.fragsize = static_cast<uint32_t>(-1);

  // Connect playback stream.  Like pa_buffer_attr, the pa_stream_flags have a
  // huge impact on the performance of the stream and were chosen through trial
  // and error.
  RETURN_ON_FAILURE(
      pa_stream_connect_playback(
          pa_stream_, NULL, &pa_buffer_attributes,
          static_cast<pa_stream_flags_t>(
              PA_STREAM_FIX_RATE | PA_STREAM_INTERPOLATE_TIMING |
              PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE |
              PA_STREAM_NOT_MONOTONIC | PA_STREAM_START_CORKED),
          NULL, NULL) == 0,
      "Failed to connect PulseAudio stream.");

  // Wait for the stream to be ready.
  while (true) {
    pa_stream_state_t stream_state = pa_stream_get_state(pa_stream_);
    RETURN_ON_FAILURE(
        PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state.");
    if (stream_state == PA_STREAM_READY)
      break;
    pa_threaded_mainloop_wait(pa_mainloop_);
  }

  return true;
}

#undef RETURN_ON_FAILURE

void PulseAudioOutputStream::Reset() {
  if (!pa_mainloop_) {
    DCHECK(!pa_stream_);
    DCHECK(!pa_context_);
    return;
  }

  {
    AutoPulseLock auto_lock(pa_mainloop_);

    // Close the stream.
    if (pa_stream_) {
      // Ensure all samples are played out before shutdown.
      pa_operation* operation = pa_stream_flush(
          pa_stream_, &pulse::StreamSuccessCallback, pa_mainloop_);
      WaitForOperationCompletion(pa_mainloop_, operation);

      // Release PulseAudio structures.
      pa_stream_disconnect(pa_stream_);
      pa_stream_set_write_callback(pa_stream_, NULL, NULL);
      pa_stream_set_state_callback(pa_stream_, NULL, NULL);
      pa_stream_unref(pa_stream_);
      pa_stream_ = NULL;
    }

    if (pa_context_) {
      pa_context_disconnect(pa_context_);
      pa_context_set_state_callback(pa_context_, NULL, NULL);
      pa_context_unref(pa_context_);
      pa_context_ = NULL;
    }
  }

  pa_threaded_mainloop_stop(pa_mainloop_);
  pa_threaded_mainloop_free(pa_mainloop_);
  pa_mainloop_ = NULL;
}

void PulseAudioOutputStream::Close() {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());

  Reset();

  // Signal to the manager that we're closed and can be removed.
  // This should be the last call in the function as it deletes "this".
  manager_->ReleaseOutputStream(this);
}

void PulseAudioOutputStream::FulfillWriteRequest(size_t requested_bytes) {
  int bytes_remaining = requested_bytes;
  while (bytes_remaining > 0) {
    void* buffer = NULL;
    size_t bytes_to_fill = params_.GetBytesPerBuffer();
    CHECK_GE(pa_stream_begin_write(pa_stream_, &buffer, &bytes_to_fill), 0);
    CHECK_EQ(bytes_to_fill, static_cast<size_t>(params_.GetBytesPerBuffer()));

    int frames_filled = 0;
    if (source_callback_) {
      uint32 hardware_delay = pulse::GetHardwareLatencyInBytes(
          pa_stream_, params_.sample_rate(),
          params_.GetBytesPerFrame());
      source_callback_->WaitTillDataReady();
      frames_filled = source_callback_->OnMoreData(
          audio_bus_.get(), AudioBuffersState(0, hardware_delay));
    }

    // Zero any unfilled data so it plays back as silence.
    if (frames_filled < audio_bus_->frames()) {
      audio_bus_->ZeroFramesPartial(
          frames_filled, audio_bus_->frames() - frames_filled);
    }

    // Note: If this ever changes to output raw float the data must be clipped
    // and sanitized since it may come from an untrusted source such as NaCl.
    audio_bus_->ToInterleaved(
        audio_bus_->frames(), params_.bits_per_sample() / 8, buffer);
    media::AdjustVolume(buffer, bytes_to_fill, params_.channels(),
                        params_.bits_per_sample() / 8, volume_);

    if (pa_stream_write(pa_stream_, buffer, bytes_to_fill, NULL, 0LL,
                        PA_SEEK_RELATIVE) < 0) {
      if (source_callback_) {
        source_callback_->OnError(this, pa_context_errno(pa_context_));
      }
    }

    bytes_remaining -= bytes_to_fill;
  }
}

void PulseAudioOutputStream::Start(AudioSourceCallback* callback) {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());
  CHECK(callback);
  CHECK(pa_stream_);

  AutoPulseLock auto_lock(pa_mainloop_);

  // Ensure the context and stream are ready.
  if (pa_context_get_state(pa_context_) != PA_CONTEXT_READY &&
      pa_stream_get_state(pa_stream_) != PA_STREAM_READY) {
    callback->OnError(this, pa_context_errno(pa_context_));
    return;
  }

  source_callback_ = callback;

  // Uncork (resume) the stream.
  pa_operation* operation = pa_stream_cork(
      pa_stream_, 0, &pulse::StreamSuccessCallback, pa_mainloop_);
  WaitForOperationCompletion(pa_mainloop_, operation);
}

void PulseAudioOutputStream::Stop() {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());

  // Cork (pause) the stream.  Waiting for the main loop lock will ensure
  // outstanding callbacks have completed.
  AutoPulseLock auto_lock(pa_mainloop_);

  // Set |source_callback_| to NULL so all FulfillWriteRequest() calls which may
  // occur while waiting on the flush and cork exit immediately.
  source_callback_ = NULL;

  // Flush the stream prior to cork, doing so after will cause hangs.  Write
  // callbacks are suspended while inside pa_threaded_mainloop_lock() so this
  // is all thread safe.
  pa_operation* operation = pa_stream_flush(
      pa_stream_, &pulse::StreamSuccessCallback, pa_mainloop_);
  WaitForOperationCompletion(pa_mainloop_, operation);

  operation = pa_stream_cork(pa_stream_, 1, &pulse::StreamSuccessCallback,
                             pa_mainloop_);
  WaitForOperationCompletion(pa_mainloop_, operation);
}

void PulseAudioOutputStream::SetVolume(double volume) {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());

  volume_ = static_cast<float>(volume);
}

void PulseAudioOutputStream::GetVolume(double* volume) {
  DCHECK(manager_->GetMessageLoop()->BelongsToCurrentThread());

  *volume = volume_;
}

}  // namespace media
<|endoftext|>


<commit_before>//
// Layer.cpp
//
// Copyright (c) 2001-2003 Virtual Terrain Project
// Free for all uses, see license.txt for details.
//

#include "wx/wxprec.h"

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

#include "vtdata/vtLog.h"
#include "Frame.h"
#include "WaterLayer.h"
#include "ElevLayer.h"
#include "ImageLayer.h"
#include "RoadLayer.h"
#include "StructLayer.h"
#include "VegLayer.h"
#include "TransitLayer.h"
#include "UtilityLayer.h"
#include "RawLayer.h"

wxChar *vtLayer::LayerTypeName[LAYER_TYPES] =
{
	_T("Raw"),
	_T("Elevation"),
	_T("Image"),
	_T("Road"),
	_T("Structure"),
	_T("Water"),
	_T("Vegetation"),
	_T("Transit"),
	_T("Utility")
};

wxChar *vtLayer::LayerFileExtension[LAYER_TYPES] =
{
	_T(".shp"),
	_T(".bt"),
	_T(".tif"),
	_T(".rmf"),
	_T(".vtst"),
	_T(".hyd"),
	_T(".vf"),
	_T(".xml"),
	_T(".utl")
};

void AddType(wxString &str, const wxString &filter)
{
	// Chop up the input string.  Expected form is "str1|str2|str3"
	wxString str1 = str.BeforeFirst('|');

	wxString str2 = str.AfterFirst('|');
	str2 = str2.BeforeFirst('|');

	wxString str3 = str.AfterFirst('|');
	str3 = str3.AfterFirst('|');

	// Chop up the filter string.  str3 is the wildcard part.
	wxString str4 = filter.AfterFirst('|');
	str4 = str4.BeforeFirst('|');

	// Now rebuild the string, with the filter added
	wxString output = str1 + _T("|");
	output += str2;
	if (str2.Len() > 1)
		output += _T(";");
	output += str4;
	output += _T("|");
	output += str3;
	output += filter;

	str = output;
}

//////////////////////////////////////////////////////////

vtLayer::vtLayer(LayerType type)
{
	m_type = type;
	m_bVisible = true;
	m_bModified = false;
	m_bNative = false;
}

vtLayer::~vtLayer()
{
	int foo = 1;
}

bool vtLayer::Save(const wxString &filename)
{
	if (!m_bNative)
	{
		if (!AskForSaveFilename())
			return false;
	}
	VTLOG("Saving data...");
	bool success = OnSave();
	if (success)
	{
		VTLOG("OK.\n");
		m_bNative = true;
		SetModified(false);
	}
	else
		VTLOG("Failed.\n");

	return success;
}

bool vtLayer::Load(const wxString &filename)
{
	if (filename != _T(""))
		SetLayerFilename(filename);
	bool success = OnLoad();
	if (success)
		m_bNative = true;
	return success;
}

bool vtLayer::SetVisible(bool bVisible)
{
	bool prev = m_bVisible;
	m_bVisible = bVisible;
	return prev;
}

void vtLayer::SetModified(bool bModified)
{
	bool bNeedRefresh = (m_bModified != bModified);
	m_bModified = bModified;
	if (bNeedRefresh)
		GetMainFrame()->RefreshTreeStatus();
}

void vtLayer::SetLayerFilename(const wxString2 &fname)
{
	wxString2 fname_in = fname;
	VTLOG("Setting layer filename to '%s'\n", fname_in.mb_str());
	bool bNeedRefresh = (m_wsFilename.Cmp(fname_in) != 0);
	m_wsFilename = fname_in;
	if (bNeedRefresh)
		GetMainFrame()->RefreshTreeStatus();
}

wxString vtLayer::GetSaveFileDialogFilter()
{
	wxString str;

	str = LayerTypeName[m_type];
	str += _T(" Files (*");

	wxString ext = GetFileExtension();
	str += ext;
	str += _T(")|*");
	str += ext;
	str += _T("|All Files|*.*|");

	return str;
}

bool vtLayer::AskForSaveFilename()
{
	wxString filter = GetSaveFileDialogFilter();
	wxFileDialog saveFile(NULL, _T("Save Layer"), _T(""), GetLayerFilename(),
		filter, wxSAVE | wxOVERWRITE_PROMPT);

	VTLOG("Asking user for file name\n");
	bool bResult = (saveFile.ShowModal() == wxID_OK);
	if (!bResult)
		return false;

	wxString2 name = saveFile.GetPath();
	VTLOG("Got filename: '%s'\n", name.mb_str());

	// Add file extension if user didn't specify it
	wxString2 ext = GetFileExtension();
	if (name.Len() < ext.Len() || name.Right(ext.Len()) != ext)
	{
		name += ext;
	}
	SetLayerFilename(name);
	m_bNative = true;
	return true;
}

wxString vtLayer::GetFileExtension()
{
	return (wxString) LayerFileExtension[GetType()];
}

void vtLayer::SetMessageText(const wxString &msg)
{
	GetMainFrame()->SetStatusText(msg);
}

void vtLayer::GetProjection(vtProjection &proj)
{
	// shouldn't get here.
}

void vtLayer::Offset(const DPoint2 &p)
{
	// shouldn't get here unless a layer is incapable of offset
}

/////////////////////////////////////////////////

// static
//
vtLayer *vtLayer::CreateNewLayer(LayerType ltype)
{
	vtLayer *pLayer = NULL;
	switch (ltype)
	{
	case LT_RAW:
		pLayer = new vtRawLayer();
		break;
	case LT_ELEVATION:
		pLayer = new vtElevLayer();
		break;
	case LT_IMAGE:
		pLayer = new vtImageLayer();
		break;
	case LT_ROAD:
		pLayer = new vtRoadLayer();
		break;
	case LT_STRUCTURE:
		pLayer = new vtStructureLayer();
		break;
	case LT_UTILITY:
		pLayer = new vtUtilityLayer();
		break;
	case LT_WATER:
		pLayer = new vtWaterLayer();
		break;
	case LT_VEG:
		pLayer = new vtVegLayer();
		break;
#if SUPPORT_TRANSIT
	case LT_TRANSIT:
		pLayer = new vtTransitLayer();
		break;
#endif
	}
	return pLayer;
}

////////////////////////////////////////////////

void LayerArray::DestructItems(unsigned int first, unsigned int last)
{
	for (unsigned int i = first; i <= last; ++i)
	{
		vtLayerPtr lp = GetAt(i);

		// safety check
		assert(lp->GetType() >= LT_UNKNOWN && lp->GetType() < LAYER_TYPES);

		delete lp;
	}
}

///////////////////////////////////////////////////////////////////////

DrawStyle::DrawStyle()
{
	m_LineColor.Set(0,0,0);
	m_FillColor.Set(255,255,255);
	m_bFill = false;
}

<commit_msg>vtb: fix WIN32 bug with saving layers that were imported from directories, i.e. TIGER<commit_after>//
// Layer.cpp
//
// Copyright (c) 2001-2003 Virtual Terrain Project
// Free for all uses, see license.txt for details.
//

#include "wx/wxprec.h"

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif

#include "vtdata/vtLog.h"
#include "Frame.h"
#include "WaterLayer.h"
#include "ElevLayer.h"
#include "ImageLayer.h"
#include "RoadLayer.h"
#include "StructLayer.h"
#include "VegLayer.h"
#include "TransitLayer.h"
#include "UtilityLayer.h"
#include "RawLayer.h"

wxChar *vtLayer::LayerTypeName[LAYER_TYPES] =
{
	_T("Raw"),
	_T("Elevation"),
	_T("Image"),
	_T("Road"),
	_T("Structure"),
	_T("Water"),
	_T("Vegetation"),
	_T("Transit"),
	_T("Utility")
};

wxChar *vtLayer::LayerFileExtension[LAYER_TYPES] =
{
	_T(".shp"),
	_T(".bt"),
	_T(".tif"),
	_T(".rmf"),
	_T(".vtst"),
	_T(".hyd"),
	_T(".vf"),
	_T(".xml"),
	_T(".utl")
};

void AddType(wxString &str, const wxString &filter)
{
	// Chop up the input string.  Expected form is "str1|str2|str3"
	wxString str1 = str.BeforeFirst('|');

	wxString str2 = str.AfterFirst('|');
	str2 = str2.BeforeFirst('|');

	wxString str3 = str.AfterFirst('|');
	str3 = str3.AfterFirst('|');

	// Chop up the filter string.  str3 is the wildcard part.
	wxString str4 = filter.AfterFirst('|');
	str4 = str4.BeforeFirst('|');

	// Now rebuild the string, with the filter added
	wxString output = str1 + _T("|");
	output += str2;
	if (str2.Len() > 1)
		output += _T(";");
	output += str4;
	output += _T("|");
	output += str3;
	output += filter;

	str = output;
}

//////////////////////////////////////////////////////////

vtLayer::vtLayer(LayerType type)
{
	m_type = type;
	m_bVisible = true;
	m_bModified = false;
	m_bNative = false;
}

vtLayer::~vtLayer()
{
	int foo = 1;
}

bool vtLayer::Save(const wxString &filename)
{
	if (!m_bNative)
	{
		if (!AskForSaveFilename())
			return false;
	}
	VTLOG("Saving data...");
	bool success = OnSave();
	if (success)
	{
		VTLOG("OK.\n");
		m_bNative = true;
		SetModified(false);
	}
	else
		VTLOG("Failed.\n");

	return success;
}

bool vtLayer::Load(const wxString &filename)
{
	if (filename != _T(""))
		SetLayerFilename(filename);
	bool success = OnLoad();
	if (success)
		m_bNative = true;
	return success;
}

bool vtLayer::SetVisible(bool bVisible)
{
	bool prev = m_bVisible;
	m_bVisible = bVisible;
	return prev;
}

void vtLayer::SetModified(bool bModified)
{
	bool bNeedRefresh = (m_bModified != bModified);
	m_bModified = bModified;
	if (bNeedRefresh)
		GetMainFrame()->RefreshTreeStatus();
}

void vtLayer::SetLayerFilename(const wxString2 &fname)
{
	wxString2 fname_in = fname;
	VTLOG("Setting layer filename to '%s'\n", fname_in.mb_str());
	bool bNeedRefresh = (m_wsFilename.Cmp(fname_in) != 0);
	m_wsFilename = fname_in;
	if (bNeedRefresh)
		GetMainFrame()->RefreshTreeStatus();
}

wxString vtLayer::GetSaveFileDialogFilter()
{
	wxString str;

	str = LayerTypeName[m_type];
	str += _T(" Files (*");

	wxString ext = GetFileExtension();
	str += ext;
	str += _T(")|*");
	str += ext;
	str += _T("|All Files|*.*|");

	return str;
}

bool vtLayer::AskForSaveFilename()
{
	wxString filter = GetSaveFileDialogFilter();
	wxString2 name = GetLayerFilename();
#if WIN32
	// The dumb Microsoft file dialog can have trouble with forward slashes.
	name = name.ToBackslash();
#endif
	wxFileDialog saveFile(NULL, _T("Save Layer"), _T(""), name,
		filter, wxSAVE | wxOVERWRITE_PROMPT);

	VTLOG("Asking user for file name\n");
	bool bResult = (saveFile.ShowModal() == wxID_OK);
	if (!bResult)
		return false;

	name = saveFile.GetPath();
	VTLOG("Got filename: '%s'\n", name.mb_str());

	// Add file extension if user didn't specify it
	wxString2 ext = GetFileExtension();
	if (name.Len() < ext.Len() || name.Right(ext.Len()) != ext)
	{
		name += ext;
	}
	SetLayerFilename(name);
	m_bNative = true;
	return true;
}

wxString vtLayer::GetFileExtension()
{
	return (wxString) LayerFileExtension[GetType()];
}

void vtLayer::SetMessageText(const wxString &msg)
{
	GetMainFrame()->SetStatusText(msg);
}

void vtLayer::GetProjection(vtProjection &proj)
{
	// shouldn't get here.
}

void vtLayer::Offset(const DPoint2 &p)
{
	// shouldn't get here unless a layer is incapable of offset
}

/////////////////////////////////////////////////

// static
//
vtLayer *vtLayer::CreateNewLayer(LayerType ltype)
{
	vtLayer *pLayer = NULL;
	switch (ltype)
	{
	case LT_RAW:
		pLayer = new vtRawLayer();
		break;
	case LT_ELEVATION:
		pLayer = new vtElevLayer();
		break;
	case LT_IMAGE:
		pLayer = new vtImageLayer();
		break;
	case LT_ROAD:
		pLayer = new vtRoadLayer();
		break;
	case LT_STRUCTURE:
		pLayer = new vtStructureLayer();
		break;
	case LT_UTILITY:
		pLayer = new vtUtilityLayer();
		break;
	case LT_WATER:
		pLayer = new vtWaterLayer();
		break;
	case LT_VEG:
		pLayer = new vtVegLayer();
		break;
#if SUPPORT_TRANSIT
	case LT_TRANSIT:
		pLayer = new vtTransitLayer();
		break;
#endif
	}
	return pLayer;
}

////////////////////////////////////////////////

void LayerArray::DestructItems(unsigned int first, unsigned int last)
{
	for (unsigned int i = first; i <= last; ++i)
	{
		vtLayerPtr lp = GetAt(i);

		// safety check
		assert(lp->GetType() >= LT_UNKNOWN && lp->GetType() < LAYER_TYPES);

		delete lp;
	}
}

///////////////////////////////////////////////////////////////////////

DrawStyle::DrawStyle()
{
	m_LineColor.Set(0,0,0);
	m_FillColor.Set(255,255,255);
	m_bFill = false;
}

<|endoftext|>


<commit_before>/*
 * Copyright 2020 Google LLC
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include <set>
#include <string>
#include <emscripten.h>
#include <emscripten/bind.h>
#include <emscripten/html5.h>

#include "gm/gm.h"
#include "include/core/SkBitmap.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkData.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkStream.h"
#include "include/core/SkSurface.h"
#include "include/gpu/GrContextOptions.h"
#include "include/gpu/GrDirectContext.h"
#include "include/gpu/gl/GrGLInterface.h"
#include "include/gpu/gl/GrGLTypes.h"
#include "modules/canvaskit/WasmCommon.h"
#include "src/core/SkFontMgrPriv.h"
#include "src/core/SkMD5.h"
#include "tests/Test.h"
#include "tools/HashAndEncode.h"
#include "tools/ResourceFactory.h"
#include "tools/flags/CommandLineFlags.h"
#include "tools/fonts/TestFontMgr.h"

using namespace emscripten;

/**
 * Returns a JS array of strings containing the names of the registered GMs. GMs are only registered
 * when their source is included in the "link" step, not if they are in something like libgm.a.
 * The names are also logged to the console.
 */
static JSArray ListGMs() {
    SkDebugf("Listing GMs\n");
    JSArray gms = emscripten::val::array();
    for (skiagm::GMFactory fact : skiagm::GMRegistry::Range()) {
        std::unique_ptr<skiagm::GM> gm(fact());
        SkDebugf("gm %s\n", gm->getName());
        gms.call<void>("push", std::string(gm->getName()));
    }
    return gms;
}

static std::unique_ptr<skiagm::GM> getGMWithName(std::string name) {
    for (skiagm::GMFactory fact : skiagm::GMRegistry::Range()) {
        std::unique_ptr<skiagm::GM> gm(fact());
        if (gm->getName() == name) {
            return gm;
        }
    }
    return nullptr;
}

/**
 * Sets the given WebGL context to be "current" and then creates a GrDirectContext from that
 * context.
 */
static sk_sp<GrDirectContext> MakeGrContext(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context)
{
    EMSCRIPTEN_RESULT r = emscripten_webgl_make_context_current(context);
    if (r < 0) {
        printf("failed to make webgl context current %d\n", r);
        return nullptr;
    }
    // setup GrDirectContext
    auto interface = GrGLMakeNativeInterface();
    // setup contexts
    sk_sp<GrDirectContext> dContext(GrDirectContext::MakeGL(interface));
    return dContext;
}

static std::set<std::string> gKnownDigests;

static void LoadKnownDigest(std::string md5) {
  gKnownDigests.insert(md5);
}

static std::map<std::string, sk_sp<SkData>> gResources;

static sk_sp<SkData> getResource(const char* name) {
  auto it = gResources.find(name);
  if (it == gResources.end()) {
    SkDebugf("Resource %s not found\n", name);
    return nullptr;
  }
  return it->second;
}

static void LoadResource(std::string name, WASMPointerU8 bPtr, size_t len) {
  const uint8_t* bytes = reinterpret_cast<const uint8_t*>(bPtr);
  auto data = SkData::MakeFromMalloc(bytes, len);
  gResources[name] = std::move(data);

  if (!gResourceFactory) {
    gResourceFactory = getResource;
  }
}

/**
 * Runs the given GM and returns a JS object. If the GM was successful, the object will have the
 * following properties:
 *   "png" - a Uint8Array of the PNG data extracted from the surface.
 *   "hash" - a string which is the md5 hash of the pixel contents and the metadata.
 */
static JSObject RunGM(sk_sp<GrDirectContext> ctx, std::string name) {
    JSObject result = emscripten::val::object();
    auto gm = getGMWithName(name);
    if (!gm) {
        SkDebugf("Could not find gm with name %s\n", name.c_str());
        return result;
    }
    // TODO(kjlubick) make these configurable somehow. This probably makes sense to do as function
    //   parameters.
    auto alphaType = SkAlphaType::kPremul_SkAlphaType;
    auto colorType = SkColorType::kN32_SkColorType;
    SkISize size = gm->getISize();
    SkImageInfo info = SkImageInfo::Make(size, colorType, alphaType);
    sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctx.get(),
                             SkBudgeted::kYes,
                             info, 0,
                             kBottomLeft_GrSurfaceOrigin,
                             nullptr, true));
    if (!surface) {
        SkDebugf("Could not make surface\n");
        return result;
    }
    auto canvas = surface->getCanvas();

    gm->onceBeforeDraw();
    SkString msg;
    // Based on GMSrc::draw from DM.
    auto gpuSetupResult = gm->gpuSetup(ctx.get(), canvas, &msg);
    if (gpuSetupResult == skiagm::DrawResult::kFail) {
        SkDebugf("Error with gpu setup for gm %s: %s\n", name.c_str(), msg.c_str());
        return result;
    } else if (gpuSetupResult == skiagm::DrawResult::kSkip) {
        return result;
    }

    auto drawResult = gm->draw(canvas, &msg);
    if (drawResult == skiagm::DrawResult::kFail) {
        SkDebugf("Error with gm %s: %s\n", name.c_str(), msg.c_str());
        return result;
    } else if (drawResult == skiagm::DrawResult::kSkip) {
        return result;
    }
    surface->flushAndSubmit(true);

    // Based on GPUSink::readBack
    SkBitmap bitmap;
    bitmap.allocPixels(info);
    if (!canvas->readPixels(bitmap, 0, 0)) {
        SkDebugf("Could not read pixels back\n");
        return result;
    }

    // Now we need to encode to PNG and get the md5 hash of the pixels (and colorspace and stuff).
    // This is based on Task::Run from DM.cpp
    std::unique_ptr<HashAndEncode> hashAndEncode = std::make_unique<HashAndEncode>(bitmap);
    SkString md5;
    SkMD5 hash;
    hashAndEncode->feedHash(&hash);
    SkMD5::Digest digest = hash.finish();
    for (int i = 0; i < 16; i++) {
        md5.appendf("%02x", digest.data[i]);
    }

    auto ok = gKnownDigests.find(md5.c_str());
    if (ok == gKnownDigests.end()) {
        // We only need to decode the image if it is "interesting", that is, we have not written it
        // before to disk and uploaded it to gold.
        SkDynamicMemoryWStream stream;
        // We do not need to include the keys because they are optional - they are not read by Gold.
        CommandLineFlags::StringArray empty;
        hashAndEncode->encodePNG(&stream, md5.c_str(), empty, empty);

        auto data = stream.detachAsData();

        // This is the cleanest way to create a new Uint8Array with a copy of the data that is not
        // in the WASM heap. kjlubick tried returning a pointer inside an SkData, but that lead to
        // some use after free issues. By making the copy using the JS transliteration, we don't
        // risk the SkData object being cleaned up before we make the copy.
        Uint8Array pngData = emscripten::val(
            // https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#memory-views
            typed_memory_view(data->size(), data->bytes())
        ).call<Uint8Array>("slice"); // slice with no args makes a copy of the memory view.

        result.set("png", pngData);
        gKnownDigests.emplace(md5.c_str());
    }
    result.set("hash", md5.c_str());
    return result;
}

static JSArray ListTests() {
    SkDebugf("Listing Tests\n");
    JSArray tests = emscripten::val::array();
    for (auto test : skiatest::TestRegistry::Range()) {
        SkDebugf("test %s\n", test.name);
        tests.call<void>("push", std::string(test.name));
    }
    return tests;
}

static skiatest::Test getTestWithName(std::string name, bool* ok) {
    for (auto test : skiatest::TestRegistry::Range()) {
        if (name == test.name) {
          *ok = true;
          return test;
        }
    }
    *ok = false;
    return skiatest::Test(nullptr, /*gpu*/ false, /*graphite*/ false, nullptr);
}

// Based on DM.cpp:run_test
struct WasmReporter : public skiatest::Reporter {
    WasmReporter(std::string name, JSObject result): fName(name), fResult(result){}

    void reportFailed(const skiatest::Failure& failure) override {
        SkDebugf("Test %s failed: %s\n", fName.c_str(), failure.toString().c_str());
        fResult.set("result", "failed");
        fResult.set("msg", failure.toString().c_str());
    }
    std::string fName;
    JSObject fResult;
};

/**
 * Runs the given Test and returns a JS object. If the Test was located, the object will have the
 * following properties:
 *   "result" : One of "passed", "failed", "skipped".
 *   "msg": May be non-empty on failure
 */
static JSObject RunTest(std::string name) {
    JSObject result = emscripten::val::object();
    bool ok = false;
    auto test = getTestWithName(name, &ok);
    if (!ok) {
        SkDebugf("Could not find test with name %s\n", name.c_str());
        return result;
    }
    GrContextOptions grOpts;
    if (test.needsGpu) {
        result.set("result", "passed"); // default to passing - the reporter will mark failed.
        WasmReporter reporter(name, result);
        test.modifyGrContextOptions(&grOpts);
        test.run(&reporter, grOpts);
        return result;
    }

    result.set("result", "passed"); // default to passing - the reporter will mark failed.
    WasmReporter reporter(name, result);
    test.run(&reporter, grOpts);
    return result;
}

namespace skiatest {

using ContextType = sk_gpu_test::GrContextFactory::ContextType;

// These are the supported GrContextTypeFilterFn
bool IsGLContextType(ContextType ct) {
    return GrBackendApi::kOpenGL == sk_gpu_test::GrContextFactory::ContextTypeBackend(ct);
}
bool IsRenderingGLContextType(ContextType ct) {
    return IsGLContextType(ct) && sk_gpu_test::GrContextFactory::IsRenderingContext(ct);
}
bool IsMockContextType(ContextType ct) {
    return ct == ContextType::kMock_ContextType;
}
// These are not supported
bool IsVulkanContextType(ContextType) {return false;}
bool IsMetalContextType(ContextType) {return false;}
bool IsDirect3DContextType(ContextType) {return false;}
bool IsDawnContextType(ContextType) {return false;}

void RunWithGPUTestContexts(GrContextTestFn* test, GrContextTypeFilterFn* contextTypeFilter,
                            Reporter* reporter, const GrContextOptions& options) {
    for (auto contextType : {ContextType::kGLES_ContextType, ContextType::kMock_ContextType}) {
        if (contextTypeFilter && !(*contextTypeFilter)(contextType)) {
            continue;
        }

        sk_gpu_test::GrContextFactory factory(options);
        sk_gpu_test::ContextInfo ctxInfo = factory.getContextInfo(contextType);

        REPORTER_ASSERT(reporter, ctxInfo.directContext() != nullptr);
        if (!ctxInfo.directContext()) {
            return;
        }
        ctxInfo.testContext()->makeCurrent();
        // From DMGpuTestProcs.cpp
        (*test)(reporter, ctxInfo);
        // Sync so any release/finished procs get called.
        ctxInfo.directContext()->flushAndSubmit(/*sync*/true);
    }
}
} // namespace skiatest

namespace {

// A GLtestContext that we can return from CreatePlatformGLTestContext below.
// It doesn't have to do anything WebGL-specific that I know of but we can't return
// a GLTestContext because it has pure virtual methods that need to be implemented.
class WasmWebGlTestContext : public sk_gpu_test::GLTestContext {
public:
    WasmWebGlTestContext() {}
    ~WasmWebGlTestContext() override {
        this->teardown();
    }
    // We assume WebGL only has one context and that it is always current.
    // Therefore these context related functions return null intentionally.
    // It's possible that more tests will pass if these were correctly implemented.
    std::unique_ptr<GLTestContext> makeNew() const override {
        // This is supposed to create a new GL context in a new GLTestContext.
        // Specifically for tests that do not want to re-use the existing one.
        return nullptr;
    }
    void onPlatformMakeNotCurrent() const override { }
    void onPlatformMakeCurrent() const override { }
    std::function<void()> onPlatformGetAutoContextRestore() const override {
        return nullptr;
    }
    GrGLFuncPtr onPlatformGetProcAddress(const char* procName) const override {
        return nullptr;
    }
};
} // namespace

namespace sk_gpu_test {
GLTestContext *CreatePlatformGLTestContext(GrGLStandard forcedGpuAPI,
                                           GLTestContext *shareContext) {
    return new WasmWebGlTestContext();
}
} // namespace sk_gpu_test

void Init() {
    // Use the portable fonts.
    gSkFontMgr_DefaultFactory = &ToolUtils::MakePortableFontMgr;
}

EMSCRIPTEN_BINDINGS(GMs) {
    function("Init", &Init);
    function("ListGMs", &ListGMs);
    function("ListTests", &ListTests);
    function("LoadKnownDigest", &LoadKnownDigest);
    function("_LoadResource", &LoadResource);
    function("MakeGrContext", &MakeGrContext);
    function("RunGM", &RunGM);
    function("RunTest", &RunTest);

    class_<GrDirectContext>("GrDirectContext")
        .smart_ptr<sk_sp<GrDirectContext>>("sk_sp<GrDirectContext>");
}
<commit_msg>Followup fixes to gm bindings<commit_after>/*
 * Copyright 2020 Google LLC
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include <set>
#include <string>
#include <emscripten.h>
#include <emscripten/bind.h>
#include <emscripten/html5.h>

#include "gm/gm.h"
#include "include/core/SkBitmap.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkData.h"
#include "include/core/SkImageInfo.h"
#include "include/core/SkStream.h"
#include "include/core/SkSurface.h"
#include "include/gpu/GrContextOptions.h"
#include "include/gpu/GrDirectContext.h"
#include "include/gpu/gl/GrGLInterface.h"
#include "include/gpu/gl/GrGLTypes.h"
#include "modules/canvaskit/WasmCommon.h"
#include "src/core/SkFontMgrPriv.h"
#include "src/core/SkMD5.h"
#include "tests/Test.h"
#include "tools/HashAndEncode.h"
#include "tools/ResourceFactory.h"
#include "tools/flags/CommandLineFlags.h"
#include "tools/fonts/TestFontMgr.h"

using namespace emscripten;

/**
 * Returns a JS array of strings containing the names of the registered GMs. GMs are only registered
 * when their source is included in the "link" step, not if they are in something like libgm.a.
 * The names are also logged to the console.
 */
static JSArray ListGMs() {
    SkDebugf("Listing GMs\n");
    JSArray gms = emscripten::val::array();
    for (skiagm::GMFactory fact : skiagm::GMRegistry::Range()) {
        std::unique_ptr<skiagm::GM> gm(fact());
        SkDebugf("gm %s\n", gm->getName());
        gms.call<void>("push", std::string(gm->getName()));
    }
    return gms;
}

static std::unique_ptr<skiagm::GM> getGMWithName(std::string name) {
    for (skiagm::GMFactory fact : skiagm::GMRegistry::Range()) {
        std::unique_ptr<skiagm::GM> gm(fact());
        if (gm->getName() == name) {
            return gm;
        }
    }
    return nullptr;
}

/**
 * Sets the given WebGL context to be "current" and then creates a GrDirectContext from that
 * context.
 */
static sk_sp<GrDirectContext> MakeGrContext(EMSCRIPTEN_WEBGL_CONTEXT_HANDLE context)
{
    EMSCRIPTEN_RESULT r = emscripten_webgl_make_context_current(context);
    if (r < 0) {
        printf("failed to make webgl context current %d\n", r);
        return nullptr;
    }
    // setup GrDirectContext
    auto interface = GrGLMakeNativeInterface();
    // setup contexts
    sk_sp<GrDirectContext> dContext((GrDirectContext::MakeGL(interface)));
    return dContext;
}

static std::set<std::string> gKnownDigests;

static void LoadKnownDigest(std::string md5) {
  gKnownDigests.insert(md5);
}

static std::map<std::string, sk_sp<SkData>> gResources;

static sk_sp<SkData> getResource(const char* name) {
  auto it = gResources.find(name);
  if (it == gResources.end()) {
    SkDebugf("Resource %s not found\n", name);
    return nullptr;
  }
  return it->second;
}

static void LoadResource(std::string name, WASMPointerU8 bPtr, size_t len) {
  const uint8_t* bytes = reinterpret_cast<const uint8_t*>(bPtr);
  auto data = SkData::MakeFromMalloc(bytes, len);
  gResources[name] = std::move(data);

  if (!gResourceFactory) {
    gResourceFactory = getResource;
  }
}

/**
 * Runs the given GM and returns a JS object. If the GM was successful, the object will have the
 * following properties:
 *   "png" - a Uint8Array of the PNG data extracted from the surface.
 *   "hash" - a string which is the md5 hash of the pixel contents and the metadata.
 */
static JSObject RunGM(sk_sp<GrDirectContext> ctx, std::string name) {
    JSObject result = emscripten::val::object();
    auto gm = getGMWithName(name);
    if (!gm) {
        SkDebugf("Could not find gm with name %s\n", name.c_str());
        return result;
    }
    // TODO(kjlubick) make these configurable somehow. This probably makes sense to do as function
    //   parameters.
    auto alphaType = SkAlphaType::kPremul_SkAlphaType;
    auto colorType = SkColorType::kN32_SkColorType;
    SkISize size = gm->getISize();
    SkImageInfo info = SkImageInfo::Make(size, colorType, alphaType);
    sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctx.get(),
                             SkBudgeted::kYes,
                             info, 0,
                             kBottomLeft_GrSurfaceOrigin,
                             nullptr, true));
    if (!surface) {
        SkDebugf("Could not make surface\n");
        return result;
    }
    auto canvas = surface->getCanvas();

    gm->onceBeforeDraw();
    SkString msg;
    // Based on GMSrc::draw from DM.
    auto gpuSetupResult = gm->gpuSetup(ctx.get(), canvas, &msg);
    if (gpuSetupResult == skiagm::DrawResult::kFail) {
        SkDebugf("Error with gpu setup for gm %s: %s\n", name.c_str(), msg.c_str());
        return result;
    } else if (gpuSetupResult == skiagm::DrawResult::kSkip) {
        return result;
    }

    auto drawResult = gm->draw(canvas, &msg);
    if (drawResult == skiagm::DrawResult::kFail) {
        SkDebugf("Error with gm %s: %s\n", name.c_str(), msg.c_str());
        return result;
    } else if (drawResult == skiagm::DrawResult::kSkip) {
        return result;
    }
    surface->flushAndSubmit(true);

    // Based on GPUSink::readBack
    SkBitmap bitmap;
    bitmap.allocPixels(info);
    if (!canvas->readPixels(bitmap, 0, 0)) {
        SkDebugf("Could not read pixels back\n");
        return result;
    }

    // Now we need to encode to PNG and get the md5 hash of the pixels (and colorspace and stuff).
    // This is based on Task::Run from DM.cpp
    std::unique_ptr<HashAndEncode> hashAndEncode = std::make_unique<HashAndEncode>(bitmap);
    SkString md5;
    SkMD5 hash;
    hashAndEncode->feedHash(&hash);
    SkMD5::Digest digest = hash.finish();
    for (int i = 0; i < 16; i++) {
        md5.appendf("%02x", digest.data[i]);
    }

    auto ok = gKnownDigests.find(md5.c_str());
    if (ok == gKnownDigests.end()) {
        // We only need to decode the image if it is "interesting", that is, we have not written it
        // before to disk and uploaded it to gold.
        SkDynamicMemoryWStream stream;
        // We do not need to include the keys because they are optional - they are not read by Gold.
        CommandLineFlags::StringArray empty;
        hashAndEncode->encodePNG(&stream, md5.c_str(), empty, empty);

        auto data = stream.detachAsData();

        // This is the cleanest way to create a new Uint8Array with a copy of the data that is not
        // in the WASM heap. kjlubick tried returning a pointer inside an SkData, but that lead to
        // some use after free issues. By making the copy using the JS transliteration, we don't
        // risk the SkData object being cleaned up before we make the copy.
        Uint8Array pngData = emscripten::val(
            // https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#memory-views
            typed_memory_view(data->size(), data->bytes())
        ).call<Uint8Array>("slice"); // slice with no args makes a copy of the memory view.

        result.set("png", pngData);
        gKnownDigests.emplace(md5.c_str());
    }
    result.set("hash", md5.c_str());
    return result;
}

static JSArray ListTests() {
    SkDebugf("Listing Tests\n");
    JSArray tests = emscripten::val::array();
    for (auto test : skiatest::TestRegistry::Range()) {
        SkDebugf("test %s\n", test.fName);
        tests.call<void>("push", std::string(test.fName));
    }
    return tests;
}

static skiatest::Test getTestWithName(std::string name, bool* ok) {
    for (auto test : skiatest::TestRegistry::Range()) {
        if (name == test.fName) {
          *ok = true;
          return test;
        }
    }
    *ok = false;
    return skiatest::Test(nullptr, /*gpu*/ false, /*graphite*/ false, nullptr);
}

// Based on DM.cpp:run_test
struct WasmReporter : public skiatest::Reporter {
    WasmReporter(std::string name, JSObject result): fName(name), fResult(result){}

    void reportFailed(const skiatest::Failure& failure) override {
        SkDebugf("Test %s failed: %s\n", fName.c_str(), failure.toString().c_str());
        fResult.set("result", "failed");
        fResult.set("msg", failure.toString().c_str());
    }
    std::string fName;
    JSObject fResult;
};

/**
 * Runs the given Test and returns a JS object. If the Test was located, the object will have the
 * following properties:
 *   "result" : One of "passed", "failed", "skipped".
 *   "msg": May be non-empty on failure
 */
static JSObject RunTest(std::string name) {
    JSObject result = emscripten::val::object();
    bool ok = false;
    auto test = getTestWithName(name, &ok);
    if (!ok) {
        SkDebugf("Could not find test with name %s\n", name.c_str());
        return result;
    }
    GrContextOptions grOpts;
    if (test.fNeedsGpu) {
        result.set("result", "passed"); // default to passing - the reporter will mark failed.
        WasmReporter reporter(name, result);
        test.modifyGrContextOptions(&grOpts);
        test.run(&reporter, grOpts);
        return result;
    }

    result.set("result", "passed"); // default to passing - the reporter will mark failed.
    WasmReporter reporter(name, result);
    test.run(&reporter, grOpts);
    return result;
}

namespace skiatest {

using ContextType = sk_gpu_test::GrContextFactory::ContextType;

// These are the supported GrContextTypeFilterFn
bool IsGLContextType(ContextType ct) {
    return GrBackendApi::kOpenGL == sk_gpu_test::GrContextFactory::ContextTypeBackend(ct);
}
bool IsRenderingGLContextType(ContextType ct) {
    return IsGLContextType(ct) && sk_gpu_test::GrContextFactory::IsRenderingContext(ct);
}
bool IsMockContextType(ContextType ct) {
    return ct == ContextType::kMock_ContextType;
}
// These are not supported
bool IsVulkanContextType(ContextType) {return false;}
bool IsMetalContextType(ContextType) {return false;}
bool IsDirect3DContextType(ContextType) {return false;}
bool IsDawnContextType(ContextType) {return false;}

void RunWithGPUTestContexts(GrContextTestFn* test, GrContextTypeFilterFn* contextTypeFilter,
                            Reporter* reporter, const GrContextOptions& options) {
    for (auto contextType : {ContextType::kGLES_ContextType, ContextType::kMock_ContextType}) {
        if (contextTypeFilter && !(*contextTypeFilter)(contextType)) {
            continue;
        }

        sk_gpu_test::GrContextFactory factory(options);
        sk_gpu_test::ContextInfo ctxInfo = factory.getContextInfo(contextType);

        REPORTER_ASSERT(reporter, ctxInfo.directContext() != nullptr);
        if (!ctxInfo.directContext()) {
            return;
        }
        ctxInfo.testContext()->makeCurrent();
        // From DMGpuTestProcs.cpp
        (*test)(reporter, ctxInfo);
        // Sync so any release/finished procs get called.
        ctxInfo.directContext()->flushAndSubmit(/*sync*/true);
    }
}
} // namespace skiatest

namespace {

// A GLtestContext that we can return from CreatePlatformGLTestContext below.
// It doesn't have to do anything WebGL-specific that I know of but we can't return
// a GLTestContext because it has pure virtual methods that need to be implemented.
class WasmWebGlTestContext : public sk_gpu_test::GLTestContext {
public:
    WasmWebGlTestContext() {}
    ~WasmWebGlTestContext() override {
        this->teardown();
    }
    // We assume WebGL only has one context and that it is always current.
    // Therefore these context related functions return null intentionally.
    // It's possible that more tests will pass if these were correctly implemented.
    std::unique_ptr<GLTestContext> makeNew() const override {
        // This is supposed to create a new GL context in a new GLTestContext.
        // Specifically for tests that do not want to re-use the existing one.
        return nullptr;
    }
    void onPlatformMakeNotCurrent() const override { }
    void onPlatformMakeCurrent() const override { }
    std::function<void()> onPlatformGetAutoContextRestore() const override {
        return nullptr;
    }
    GrGLFuncPtr onPlatformGetProcAddress(const char* procName) const override {
        return nullptr;
    }
};
} // namespace

namespace sk_gpu_test {
GLTestContext *CreatePlatformGLTestContext(GrGLStandard forcedGpuAPI,
                                           GLTestContext *shareContext) {
    return new WasmWebGlTestContext();
}
} // namespace sk_gpu_test

void Init() {
    // Use the portable fonts.
    gSkFontMgr_DefaultFactory = &ToolUtils::MakePortableFontMgr;
}

EMSCRIPTEN_BINDINGS(GMs) {
    function("Init", &Init);
    function("ListGMs", &ListGMs);
    function("ListTests", &ListTests);
    function("LoadKnownDigest", &LoadKnownDigest);
    function("_LoadResource", &LoadResource);
    function("MakeGrContext", &MakeGrContext);
    function("RunGM", &RunGM);
    function("RunTest", &RunTest);

    class_<GrDirectContext>("GrDirectContext")
        .smart_ptr<sk_sp<GrDirectContext>>("sk_sp<GrDirectContext>");
}
<|endoftext|>


<commit_before>/******************************************************************************
 * Copyright 2017 The Apollo Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include <iostream>

#include "ros/include/ros/ros.h"
#include "std_msgs/String.h"

#include "modules/canbus/proto/chassis.pb.h"
#include "modules/control/proto/pad_msg.pb.h"

#include "modules/common/adapters/adapter_manager.h"
#include "modules/common/log.h"
#include "modules/common/time/time.h"
#include "modules/control/common/control_gflags.h"

namespace {

using ::apollo::common::adapter::AdapterManager;
using ::apollo::common::time::Clock;

const int ROS_QUEUE_SIZE = 1;
const int RESET_COMMAND = 1;
const int AUTO_DRIVE_COMMAND = 2;

const int EMERGENCY_MODE_HOLD_TIME = 4 * 1000000;

ros::Publisher g_pad_msg_pub;

void help() {
  AINFO << "COMMAND:\n";
  AINFO << "\t0: reset to manual drive mode.";
  AINFO << "\t1: auto drive mode.";
  AINFO << "\tctrl + c: exit.";
  AINFO << "\tother: print help.";
}

void send(int cmd_type) {
  ::apollo::control::PadMessage pb;
  if (cmd_type == RESET_COMMAND) {
    pb.set_action(apollo::control::DrivingAction::RESET);
    AINFO << "sending reset action command.";
  } else if (cmd_type == AUTO_DRIVE_COMMAND) {
    pb.set_action(apollo::control::DrivingAction::START);
    AINFO << "sending start action command.";
  }
  AdapterManager::FillPadHeader("terminal", pb.mutable_header());
  AdapterManager::PublishPad(pb);
  AINFO << "send pad_message OK";
}

void on_chassis(const apollo::canbus::Chassis& chassis) {
  static bool is_first_emergency_mode = true;
  static int64_t count_start = 0;
  static bool waiting_reset = false;

  // check if chassis enter security mode, if enter, after 10s should reset to
  // manual
  if (chassis.driving_mode() == apollo::canbus::Chassis::EMERGENCY_MODE) {
    if (is_first_emergency_mode == true) {
      count_start = apollo::common::time::AsInt64<std::chrono::microseconds>(
          Clock::Now());
      is_first_emergency_mode = false;
      AINFO << "detect emergency mode.";
    } else {
      int64_t diff = apollo::common::time::AsInt64<std::chrono::microseconds>(
                         Clock::Now()) -
                     count_start;
      if (diff > EMERGENCY_MODE_HOLD_TIME) {
        count_start = apollo::common::time::AsInt64<std::chrono::microseconds>(
            Clock::Now());
        waiting_reset = true;
        // send a reset command to control
        send(RESET_COMMAND);
        AINFO << "trigger to reset emergency mode to manual mode.";
      } else {
        // nothing to do
      }
    }
  } else if (chassis.driving_mode() ==
             apollo::canbus::Chassis::COMPLETE_MANUAL) {
    if (waiting_reset) {
      is_first_emergency_mode = true;
      waiting_reset = false;
      AINFO << "emergency mode reset to manual ok.";
    }
  }
}

void* terminal_thread(void* arg) {
  int mode = 0;
  bool should_exit = false;
  while (std::cin >> mode) {
    switch (mode) {
      case 0:
        send(RESET_COMMAND);
        break;
      case 1:
        send(AUTO_DRIVE_COMMAND);
        break;
      case 9:
        should_exit = true;
        break;
      default:
        help();
        break;
    }
    if (should_exit) {
      break;
    }
  }
  return NULL;
}

}  // end of namespace

int main(int argc, char** argv) {
  google::InitGoogleLogging(argv[0]);
  FLAGS_alsologtostderr = true;
  FLAGS_v = 3;

  google::ParseCommandLineFlags(&argc, &argv, true);

  ros::init(argc, argv, "terminal");

  apollo::common::adapter::AdapterManagerConfig config;
  config.set_is_ros(true);
  {
    auto* sub_config = config.add_config();
    sub_config->set_mode(apollo::common::adapter::AdapterConfig::PUBLISH_ONLY);
    sub_config->set_type(apollo::common::adapter::AdapterConfig::PAD);
  }

  {
    auto* sub_config = config.add_config();
    sub_config->set_mode(apollo::common::adapter::AdapterConfig::RECEIVE_ONLY);
    sub_config->set_type(apollo::common::adapter::AdapterConfig::CHASSIS);
  }

  {
    auto* sub_config = config.add_config();
    sub_config->set_mode(apollo::common::adapter::AdapterConfig::RECEIVE_ONLY);
    sub_config->set_type(apollo::common::adapter::AdapterConfig::LOCALIZATION);
  }

  AdapterManager::Init(config);
  AdapterManager::SetChassisCallback(on_chassis);

  help();
  pthread_t pid;
  if (pthread_create(&pid, NULL, terminal_thread, NULL) != 0) {
    AINFO << "create terminal thread failed.";
    exit(0);
  }
  ros::spin();
  return 0;
}
<commit_msg>Use std::thread instead of pthread.<commit_after>/******************************************************************************
 * Copyright 2017 The Apollo Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include <iostream>
#include <thread>

#include "ros/include/ros/ros.h"
#include "std_msgs/String.h"

#include "modules/canbus/proto/chassis.pb.h"
#include "modules/control/proto/pad_msg.pb.h"

#include "modules/common/adapters/adapter_manager.h"
#include "modules/common/log.h"
#include "modules/common/time/time.h"
#include "modules/control/common/control_gflags.h"

namespace {

using ::apollo::common::adapter::AdapterManager;
using ::apollo::common::time::Clock;

const int ROS_QUEUE_SIZE = 1;
const int RESET_COMMAND = 1;
const int AUTO_DRIVE_COMMAND = 2;

const int EMERGENCY_MODE_HOLD_TIME = 4 * 1000000;

ros::Publisher g_pad_msg_pub;

void help() {
  AINFO << "COMMAND:\n";
  AINFO << "\t0: reset to manual drive mode.";
  AINFO << "\t1: auto drive mode.";
  AINFO << "\tctrl + c: exit.";
  AINFO << "\tother: print help.";
}

void send(int cmd_type) {
  ::apollo::control::PadMessage pb;
  if (cmd_type == RESET_COMMAND) {
    pb.set_action(apollo::control::DrivingAction::RESET);
    AINFO << "sending reset action command.";
  } else if (cmd_type == AUTO_DRIVE_COMMAND) {
    pb.set_action(apollo::control::DrivingAction::START);
    AINFO << "sending start action command.";
  }
  AdapterManager::FillPadHeader("terminal", pb.mutable_header());
  AdapterManager::PublishPad(pb);
  AINFO << "send pad_message OK";
}

void on_chassis(const apollo::canbus::Chassis& chassis) {
  static bool is_first_emergency_mode = true;
  static int64_t count_start = 0;
  static bool waiting_reset = false;

  // check if chassis enter security mode, if enter, after 10s should reset to
  // manual
  if (chassis.driving_mode() == apollo::canbus::Chassis::EMERGENCY_MODE) {
    if (is_first_emergency_mode == true) {
      count_start = apollo::common::time::AsInt64<std::chrono::microseconds>(
          Clock::Now());
      is_first_emergency_mode = false;
      AINFO << "detect emergency mode.";
    } else {
      int64_t diff = apollo::common::time::AsInt64<std::chrono::microseconds>(
                         Clock::Now()) -
                     count_start;
      if (diff > EMERGENCY_MODE_HOLD_TIME) {
        count_start = apollo::common::time::AsInt64<std::chrono::microseconds>(
            Clock::Now());
        waiting_reset = true;
        // send a reset command to control
        send(RESET_COMMAND);
        AINFO << "trigger to reset emergency mode to manual mode.";
      } else {
        // nothing to do
      }
    }
  } else if (chassis.driving_mode() ==
             apollo::canbus::Chassis::COMPLETE_MANUAL) {
    if (waiting_reset) {
      is_first_emergency_mode = true;
      waiting_reset = false;
      AINFO << "emergency mode reset to manual ok.";
    }
  }
}

void terminal_thread_func() {
  int mode = 0;
  bool should_exit = false;
  while (std::cin >> mode) {
    switch (mode) {
      case 0:
        send(RESET_COMMAND);
        break;
      case 1:
        send(AUTO_DRIVE_COMMAND);
        break;
      case 9:
        should_exit = true;
        break;
      default:
        help();
        break;
    }
    if (should_exit) {
      break;
    }
  }
}

}  // end of namespace

int main(int argc, char** argv) {
  google::InitGoogleLogging(argv[0]);
  FLAGS_alsologtostderr = true;
  FLAGS_v = 3;

  google::ParseCommandLineFlags(&argc, &argv, true);

  ros::init(argc, argv, "terminal");

  apollo::common::adapter::AdapterManagerConfig config;
  config.set_is_ros(true);
  {
    auto* sub_config = config.add_config();
    sub_config->set_mode(apollo::common::adapter::AdapterConfig::PUBLISH_ONLY);
    sub_config->set_type(apollo::common::adapter::AdapterConfig::PAD);
  }

  {
    auto* sub_config = config.add_config();
    sub_config->set_mode(apollo::common::adapter::AdapterConfig::RECEIVE_ONLY);
    sub_config->set_type(apollo::common::adapter::AdapterConfig::CHASSIS);
  }

  {
    auto* sub_config = config.add_config();
    sub_config->set_mode(apollo::common::adapter::AdapterConfig::RECEIVE_ONLY);
    sub_config->set_type(apollo::common::adapter::AdapterConfig::LOCALIZATION);
  }

  AdapterManager::Init(config);
  AdapterManager::SetChassisCallback(on_chassis);

  help();
  std::thread terminal_thread(terminal_thread_func);
  ros::spin();
  terminal_thread.join();
  return 0;
}
<|endoftext|>


<commit_before>#include "editor/xml_feature.hpp"

#include "base/assert.hpp"
#include "base/macros.hpp"
#include "base/string_utils.hpp"
#include "base/timer.hpp"

#include "std/set.hpp"
#include "std/unordered_set.hpp"

#include <boost/functional/hash.hpp>

#include "3party/pugixml/src/pugixml.hpp"

namespace
{
constexpr int const kLatLonTolerance = 7;
constexpr char const * kTimestamp = "timestamp";
constexpr char const * kOffset = "offset";
constexpr char const * kUploadTimestamp = "upload_timestamp";
constexpr char const * kUploadStatus = "upload_status";
constexpr char const * kUploadError = "upload_error";
constexpr char const * kHouseNumber = "addr:housenumber";

constexpr char const * kNodeType = "node";
constexpr char const * kWayType = "way";

pugi::xml_node FindTag(pugi::xml_document const & document, string const & key)
{
  return document.select_node(("//tag[@k='" + key + "']").data()).node();
}

ms::LatLon PointFromLatLon(pugi::xml_node const node)
{
  ms::LatLon ll;
  if (!strings::to_double(node.attribute("lat").value(), ll.lat))
  {
    MYTHROW(editor::XMLFeatureNoLatLonError,
            ("Can't parse lat attribute: " + string(node.attribute("lat").value())));
  }
 if (!strings::to_double(node.attribute("lon").value(), ll.lon))
 {
   MYTHROW(editor::XMLFeatureNoLatLonError,
           ("Can't parse lon attribute: " + string(node.attribute("lon").value())));
 }
  return ll;
}

void ValidateNode(pugi::xml_node const & node)
{
  if (!node)
    MYTHROW(editor::XMLFeatureNoNodeError, ("Document has no node"));

  // Check if point can be parsed. Throws if it's can't.
  UNUSED_VALUE(PointFromLatLon(node));

  if (!node.attribute(kTimestamp))
    MYTHROW(editor::XMLFeatureNoTimestampError, ("Node has no timestamp attribute"));
}
} // namespace

namespace editor
{

char const * const XMLFeature::kDefaultLang =
    StringUtf8Multilang::GetLangByCode(StringUtf8Multilang::DEFAULT_CODE);

XMLFeature::XMLFeature(Type const type)
{
  m_document.append_child(type == Type::Node ? kNodeType : kWayType);
}

XMLFeature::XMLFeature(string const & xml)
{
  m_document.load(xml.data());
  ValidateNode(GetRootNode());
}

XMLFeature::XMLFeature(pugi::xml_document const & xml)
{
  m_document.reset(xml);
  ValidateNode(GetRootNode());
}

XMLFeature::XMLFeature(pugi::xml_node const & xml)
{
  m_document.reset();
  m_document.append_copy(xml);
  ValidateNode(GetRootNode());
}

XMLFeature::Type XMLFeature::GetType() const
{
  return strcmp(GetRootNode().name(), "node") == 0 ? Type::Node : Type::Way;
}

void XMLFeature::Save(ostream & ost) const
{
  m_document.save(ost, "  ");
}

string XMLFeature::ToOSMString() const
{
  ostringstream ost;
  // Ugly way to wrap into <osm>..</osm> tags.
  // Unfortunately, pugi xml library doesn't allow to insert documents into other documents.
  ost << "<?xml version=\"1.0\"?>" << endl;
  ost << "<osm>" << endl;
  m_document.save(ost, "  ", pugi::format_no_declaration | pugi::format_indent);
  ost << "</osm>" << endl;
  return ost.str();
}

void XMLFeature::ApplyPatch(XMLFeature const & featureWithChanges)
{
  featureWithChanges.ForEachTag([this](string const & k, string const & v)
  {
    SetTagValue(k, v);
  });
}

ms::LatLon XMLFeature::GetCenter() const
{
  return PointFromLatLon(GetRootNode());
}

void XMLFeature::SetCenter(m2::PointD const & mercatorCenter)
{
  SetAttribute("lat", strings::to_string_dac(MercatorBounds::YToLat(mercatorCenter.y),
                                             kLatLonTolerance));
  SetAttribute("lon", strings::to_string_dac(MercatorBounds::XToLon(mercatorCenter.x),
                                             kLatLonTolerance));
}

string XMLFeature::GetName(string const & lang) const
{
  auto const suffix = (lang == kDefaultLang || lang.empty()) ? "" : ":" + lang;
  return GetTagValue(kDefaultName + suffix);
}

string XMLFeature::GetName(uint8_t const langCode) const
{
  return GetName(StringUtf8Multilang::GetLangByCode(langCode));
}

void XMLFeature::SetName(string const & name)
{
  SetName(kDefaultLang, name);
}

void XMLFeature::SetName(string const & lang, string const & name)
{
  auto const suffix = (lang == kDefaultLang || lang.empty()) ? "" : ":" + lang;
  SetTagValue(kDefaultName + suffix, name);
}

void XMLFeature::SetName(uint8_t const langCode, string const & name)
{
  SetName(StringUtf8Multilang::GetLangByCode(langCode), name);
}

string XMLFeature::GetHouse() const
{
  return GetTagValue(kHouseNumber);
}

void XMLFeature::SetHouse(string const & house)
{
  SetTagValue(kHouseNumber, house);
}

time_t XMLFeature::GetModificationTime() const
{
  return my::StringToTimestamp(GetRootNode().attribute(kTimestamp).value());
}

void XMLFeature::SetModificationTime(time_t const time)
{
  SetAttribute(kTimestamp, my::TimestampToString(time));
}

uint32_t XMLFeature::GetOffset() const
{
  // Always cast to uint32_t to avoid warnings on different platforms.
  return static_cast<uint32_t>(GetRootNode().attribute(kOffset).as_uint(0));
}

void XMLFeature::SetOffset(uint32_t featureOffset)
{
  SetAttribute(kOffset, strings::to_string(featureOffset));
}

time_t XMLFeature::GetUploadTime() const
{
  return my::StringToTimestamp(GetRootNode().attribute(kUploadTimestamp).value());
}

void XMLFeature::SetUploadTime(time_t const time)
{
  SetAttribute(kUploadTimestamp, my::TimestampToString(time));
}

string XMLFeature::GetUploadStatus() const
{
  return GetRootNode().attribute(kUploadStatus).value();
}

void XMLFeature::SetUploadStatus(string const & status)
{
  SetAttribute(kUploadStatus, status);
}

string XMLFeature::GetUploadError() const
{
  return GetRootNode().attribute(kUploadError).value();
}

void XMLFeature::SetUploadError(string const & error)
{
  SetAttribute(kUploadError, error);
}

bool XMLFeature::HasTag(string const & key) const
{
  return FindTag(m_document, key);
}

bool XMLFeature::HasAttribute(string const & key) const
{
  return GetRootNode().attribute(key.data());
}

bool XMLFeature::HasKey(string const & key) const
{
  return HasTag(key) || HasAttribute(key);
}

string XMLFeature::GetTagValue(string const & key) const
{
  auto const tag = FindTag(m_document, key);
  return tag.attribute("v").value();
}

void XMLFeature::SetTagValue(string const & key, string const value)
{
  auto tag = FindTag(m_document, key);
  if (!tag)
  {
    tag = GetRootNode().append_child("tag");
    tag.append_attribute("k") = key.data();
    tag.append_attribute("v") = value.data();
  }
  else
  {
    tag.attribute("v") = value.data();
  }
}

string XMLFeature::GetAttribute(string const & key) const
{
  return GetRootNode().attribute(key.data()).value();
}

void XMLFeature::SetAttribute(string const & key, string const & value)
{
  auto node = HasAttribute(key)
      ? GetRootNode().attribute(key.data())
      : GetRootNode().append_attribute(key.data());

  node.set_value(value.data());
}

pugi::xml_node const XMLFeature::GetRootNode() const
{
  return m_document.first_child();
}

pugi::xml_node XMLFeature::GetRootNode()
{
  return m_document.first_child();
}

bool XMLFeature::AttachToParentNode(pugi::xml_node parent) const
{
  return !parent.append_copy(GetRootNode()).empty();
}

string DebugPrint(XMLFeature const & feature)
{
  ostringstream ost;
  feature.Save(ost);
  return ost.str();
}

} // namespace editor
<commit_msg>Split way and node validation for XMLFeature.<commit_after>#include "editor/xml_feature.hpp"

#include "base/assert.hpp"
#include "base/macros.hpp"
#include "base/string_utils.hpp"
#include "base/timer.hpp"

#include "std/set.hpp"
#include "std/unordered_set.hpp"

#include <boost/functional/hash.hpp>

#include "3party/pugixml/src/pugixml.hpp"

namespace
{
constexpr int const kLatLonTolerance = 7;
constexpr char const * kTimestamp = "timestamp";
constexpr char const * kOffset = "offset";
constexpr char const * kUploadTimestamp = "upload_timestamp";
constexpr char const * kUploadStatus = "upload_status";
constexpr char const * kUploadError = "upload_error";
constexpr char const * kHouseNumber = "addr:housenumber";

constexpr char const * kNodeType = "node";
constexpr char const * kWayType = "way";

pugi::xml_node FindTag(pugi::xml_document const & document, string const & key)
{
  return document.select_node(("//tag[@k='" + key + "']").data()).node();
}

ms::LatLon PointFromLatLon(pugi::xml_node const node)
{
  ms::LatLon ll;
  if (!strings::to_double(node.attribute("lat").value(), ll.lat))
  {
    MYTHROW(editor::XMLFeatureNoLatLonError,
            ("Can't parse lat attribute: " + string(node.attribute("lat").value())));
  }
 if (!strings::to_double(node.attribute("lon").value(), ll.lon))
 {
   MYTHROW(editor::XMLFeatureNoLatLonError,
           ("Can't parse lon attribute: " + string(node.attribute("lon").value())));
 }
  return ll;
}

void ValidateNode(pugi::xml_node const & node)
{
  if (!node)
    MYTHROW(editor::XMLFeatureNoNodeError, ("Document has no node"));

  // Check if point can be parsed. Throws if it's can't.
  UNUSED_VALUE(PointFromLatLon(node));

  if (!node.attribute(kTimestamp))
    MYTHROW(editor::XMLFeatureNoTimestampError, ("Node has no timestamp attribute"));
}

void ValidateWay(pugi::xml_node const & way)
{
  if (!way)
    MYTHROW(editor::XMLFeatureNoNodeError, ("Document has no node"));

  if (!way.attribute(kTimestamp))
    MYTHROW(editor::XMLFeatureNoTimestampError, ("Way has no timestamp attribute"));
}

} // namespace

namespace editor
{

char const * const XMLFeature::kDefaultLang =
    StringUtf8Multilang::GetLangByCode(StringUtf8Multilang::DEFAULT_CODE);

XMLFeature::XMLFeature(Type const type)
{
  m_document.append_child(type == Type::Node ? kNodeType : kWayType);
}

XMLFeature::XMLFeature(string const & xml)
{
  m_document.load(xml.data());
  auto const r = GetRootNode();
  r.name() == kNodeType ? ValidateNode(r) : ValidateWay(r);
}

XMLFeature::XMLFeature(pugi::xml_document const & xml)
{
  m_document.reset(xml);
  auto const r = GetRootNode();
  r.name() == kNodeType ? ValidateNode(r) : ValidateWay(r);
}

XMLFeature::XMLFeature(pugi::xml_node const & xml)
{
  m_document.reset();
  m_document.append_copy(xml);
  auto const r = GetRootNode();
  r.name() == kNodeType ? ValidateNode(r) : ValidateWay(r);
}

XMLFeature::Type XMLFeature::GetType() const
{
  return strcmp(GetRootNode().name(), "node") == 0 ? Type::Node : Type::Way;
}

void XMLFeature::Save(ostream & ost) const
{
  m_document.save(ost, "  ");
}

string XMLFeature::ToOSMString() const
{
  ostringstream ost;
  // Ugly way to wrap into <osm>..</osm> tags.
  // Unfortunately, pugi xml library doesn't allow to insert documents into other documents.
  ost << "<?xml version=\"1.0\"?>" << endl;
  ost << "<osm>" << endl;
  m_document.save(ost, "  ", pugi::format_no_declaration | pugi::format_indent);
  ost << "</osm>" << endl;
  return ost.str();
}

void XMLFeature::ApplyPatch(XMLFeature const & featureWithChanges)
{
  featureWithChanges.ForEachTag([this](string const & k, string const & v)
  {
    SetTagValue(k, v);
  });
}

ms::LatLon XMLFeature::GetCenter() const
{
  return PointFromLatLon(GetRootNode());
}

void XMLFeature::SetCenter(m2::PointD const & mercatorCenter)
{
  SetAttribute("lat", strings::to_string_dac(MercatorBounds::YToLat(mercatorCenter.y),
                                             kLatLonTolerance));
  SetAttribute("lon", strings::to_string_dac(MercatorBounds::XToLon(mercatorCenter.x),
                                             kLatLonTolerance));
}

string XMLFeature::GetName(string const & lang) const
{
  auto const suffix = (lang == kDefaultLang || lang.empty()) ? "" : ":" + lang;
  return GetTagValue(kDefaultName + suffix);
}

string XMLFeature::GetName(uint8_t const langCode) const
{
  return GetName(StringUtf8Multilang::GetLangByCode(langCode));
}

void XMLFeature::SetName(string const & name)
{
  SetName(kDefaultLang, name);
}

void XMLFeature::SetName(string const & lang, string const & name)
{
  auto const suffix = (lang == kDefaultLang || lang.empty()) ? "" : ":" + lang;
  SetTagValue(kDefaultName + suffix, name);
}

void XMLFeature::SetName(uint8_t const langCode, string const & name)
{
  SetName(StringUtf8Multilang::GetLangByCode(langCode), name);
}

string XMLFeature::GetHouse() const
{
  return GetTagValue(kHouseNumber);
}

void XMLFeature::SetHouse(string const & house)
{
  SetTagValue(kHouseNumber, house);
}

time_t XMLFeature::GetModificationTime() const
{
  return my::StringToTimestamp(GetRootNode().attribute(kTimestamp).value());
}

void XMLFeature::SetModificationTime(time_t const time)
{
  SetAttribute(kTimestamp, my::TimestampToString(time));
}

uint32_t XMLFeature::GetOffset() const
{
  // Always cast to uint32_t to avoid warnings on different platforms.
  return static_cast<uint32_t>(GetRootNode().attribute(kOffset).as_uint(0));
}

void XMLFeature::SetOffset(uint32_t featureOffset)
{
  SetAttribute(kOffset, strings::to_string(featureOffset));
}

time_t XMLFeature::GetUploadTime() const
{
  return my::StringToTimestamp(GetRootNode().attribute(kUploadTimestamp).value());
}

void XMLFeature::SetUploadTime(time_t const time)
{
  SetAttribute(kUploadTimestamp, my::TimestampToString(time));
}

string XMLFeature::GetUploadStatus() const
{
  return GetRootNode().attribute(kUploadStatus).value();
}

void XMLFeature::SetUploadStatus(string const & status)
{
  SetAttribute(kUploadStatus, status);
}

string XMLFeature::GetUploadError() const
{
  return GetRootNode().attribute(kUploadError).value();
}

void XMLFeature::SetUploadError(string const & error)
{
  SetAttribute(kUploadError, error);
}

bool XMLFeature::HasTag(string const & key) const
{
  return FindTag(m_document, key);
}

bool XMLFeature::HasAttribute(string const & key) const
{
  return GetRootNode().attribute(key.data());
}

bool XMLFeature::HasKey(string const & key) const
{
  return HasTag(key) || HasAttribute(key);
}

string XMLFeature::GetTagValue(string const & key) const
{
  auto const tag = FindTag(m_document, key);
  return tag.attribute("v").value();
}

void XMLFeature::SetTagValue(string const & key, string const value)
{
  auto tag = FindTag(m_document, key);
  if (!tag)
  {
    tag = GetRootNode().append_child("tag");
    tag.append_attribute("k") = key.data();
    tag.append_attribute("v") = value.data();
  }
  else
  {
    tag.attribute("v") = value.data();
  }
}

string XMLFeature::GetAttribute(string const & key) const
{
  return GetRootNode().attribute(key.data()).value();
}

void XMLFeature::SetAttribute(string const & key, string const & value)
{
  auto node = HasAttribute(key)
      ? GetRootNode().attribute(key.data())
      : GetRootNode().append_attribute(key.data());

  node.set_value(value.data());
}

pugi::xml_node const XMLFeature::GetRootNode() const
{
  return m_document.first_child();
}

pugi::xml_node XMLFeature::GetRootNode()
{
  return m_document.first_child();
}

bool XMLFeature::AttachToParentNode(pugi::xml_node parent) const
{
  return !parent.append_copy(GetRootNode()).empty();
}

string DebugPrint(XMLFeature const & feature)
{
  ostringstream ost;
  feature.Save(ost);
  return ost.str();
}

} // namespace editor
<|endoftext|>


<commit_before>/*************************************************************************/
/*  path_utils.cpp                                                       */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
/*                                                                       */
/* 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 "path_utils.h"

#include "core/os/dir_access.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
#include "core/project_settings.h"

#ifdef WINDOWS_ENABLED
#define ENV_PATH_SEP ";"
#else
#define ENV_PATH_SEP ":"
#include <limits.h>
#endif

#include <stdlib.h>

String path_which(const String &p_name) {

#ifdef WINDOWS_ENABLED
	Vector<String> exts = OS::get_singleton()->get_environment("PATHEXT").split(ENV_PATH_SEP, false);
#endif
	Vector<String> env_path = OS::get_singleton()->get_environment("PATH").split(ENV_PATH_SEP, false);

	if (env_path.empty())
		return String();

	for (int i = 0; i < env_path.size(); i++) {
		String p = path_join(env_path[i], p_name);

#ifdef WINDOWS_ENABLED
		for (int j = 0; j < exts.size(); j++) {
			String p2 = p + exts[j];

			if (FileAccess::exists(p2))
				return p2;
		}
#else
		if (FileAccess::exists(p))
			return p;
#endif
	}

	return String();
}

void fix_path(const String &p_path, String &r_out) {
	r_out = p_path.replace("\\", "/");

	while (true) { // in case of using 2 or more slash
		String compare = r_out.replace("//", "/");
		if (r_out == compare)
			break;
		else
			r_out = compare;
	}
}

bool rel_path_to_abs(const String &p_existing_path, String &r_abs_path) {
#ifdef WINDOWS_ENABLED
	CharType ret[_MAX_PATH];
	if (::_wfullpath(ret, p_existing_path.c_str(), _MAX_PATH)) {
		String abspath = String(ret).replace("\\", "/");
		int pos = abspath.find(":/");
		if (pos != -1) {
			r_abs_path = abspath.substr(pos - 1, abspath.length());
		} else {
			r_abs_path = abspath;
		}
		return true;
	}
#else
	char *resolved_path = ::realpath(p_existing_path.utf8().get_data(), NULL);
	if (resolved_path) {
		String retstr;
		bool success = !retstr.parse_utf8(resolved_path);
		::free(resolved_path);
		if (success) {
			r_abs_path = retstr;
			return true;
		}
	}
#endif
	return false;
}
<commit_msg>Mono: Test Windows binaries with lowercase extension<commit_after>/*************************************************************************/
/*  path_utils.cpp                                                       */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
/*                                                                       */
/* 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 "path_utils.h"

#include "core/os/dir_access.h"
#include "core/os/file_access.h"
#include "core/os/os.h"
#include "core/project_settings.h"

#ifdef WINDOWS_ENABLED
#define ENV_PATH_SEP ";"
#else
#define ENV_PATH_SEP ":"
#include <limits.h>
#endif

#include <stdlib.h>

String path_which(const String &p_name) {

#ifdef WINDOWS_ENABLED
	Vector<String> exts = OS::get_singleton()->get_environment("PATHEXT").split(ENV_PATH_SEP, false);
#endif
	Vector<String> env_path = OS::get_singleton()->get_environment("PATH").split(ENV_PATH_SEP, false);

	if (env_path.empty())
		return String();

	for (int i = 0; i < env_path.size(); i++) {
		String p = path_join(env_path[i], p_name);

#ifdef WINDOWS_ENABLED
		for (int j = 0; j < exts.size(); j++) {
			String p2 = p + exts[j].to_lower(); // lowercase to reduce risk of case mismatch warning

			if (FileAccess::exists(p2))
				return p2;
		}
#else
		if (FileAccess::exists(p))
			return p;
#endif
	}

	return String();
}

void fix_path(const String &p_path, String &r_out) {
	r_out = p_path.replace("\\", "/");

	while (true) { // in case of using 2 or more slash
		String compare = r_out.replace("//", "/");
		if (r_out == compare)
			break;
		else
			r_out = compare;
	}
}

bool rel_path_to_abs(const String &p_existing_path, String &r_abs_path) {
#ifdef WINDOWS_ENABLED
	CharType ret[_MAX_PATH];
	if (::_wfullpath(ret, p_existing_path.c_str(), _MAX_PATH)) {
		String abspath = String(ret).replace("\\", "/");
		int pos = abspath.find(":/");
		if (pos != -1) {
			r_abs_path = abspath.substr(pos - 1, abspath.length());
		} else {
			r_abs_path = abspath;
		}
		return true;
	}
#else
	char *resolved_path = ::realpath(p_existing_path.utf8().get_data(), NULL);
	if (resolved_path) {
		String retstr;
		bool success = !retstr.parse_utf8(resolved_path);
		::free(resolved_path);
		if (success) {
			r_abs_path = retstr;
			return true;
		}
	}
#endif
	return false;
}
<|endoftext|>