<commit_before>/****************************************************************************
 *
 *   Copyright (c) 2018 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/
/**
 * @file FlightTaskManual.cpp
 */

#include "FlightTaskManual.hpp"
#include <mathlib/mathlib.h>
#include <float.h>

using namespace matrix;

bool FlightTaskManual::initializeSubscriptions(SubscriptionArray &subscription_array)
{
	if (!FlightTask::initializeSubscriptions(subscription_array)) {
		return false;
	}

	if (!subscription_array.get(ORB_ID(manual_control_setpoint), _sub_manual_control_setpoint)) {
		return false;
	}

	return true;
}

bool FlightTaskManual::updateInitialize()
{
	bool ret = FlightTask::updateInitialize();
	const bool sticks_available = _evaluateSticks();

	if (_sticks_data_required) {
		ret = ret && sticks_available;
	}

	return ret;
}

bool FlightTaskManual::activate()
{
	bool ret = FlightTask::activate();

	if (_sticks_data_required) {
		// need valid stick inputs
		ret = ret && PX4_ISFINITE(_sticks(0))
		      && PX4_ISFINITE(_sticks(1))
		      && PX4_ISFINITE(_sticks(2))
		      && PX4_ISFINITE(_sticks(3));
	}

	return ret;
}

bool FlightTaskManual::_evaluateSticks()
{
	/* Sticks are rescaled linearly and exponentially to [-1,1] */
	if ((_time_stamp_current - _sub_manual_control_setpoint->get().timestamp) < _timeout) {

		/* Linear scale  */
		_sticks(0) = _sub_manual_control_setpoint->get().x; /* NED x, "pitch" [-1,1] */
		_sticks(1) = _sub_manual_control_setpoint->get().y; /* NED y, "roll" [-1,1] */
		_sticks(2) = -(_sub_manual_control_setpoint->get().z - 0.5f) * 2.f; /* NED z, "thrust" resacaled from [0,1] to [-1,1] */
		_sticks(3) = _sub_manual_control_setpoint->get().r; /* "yaw" [-1,1] */

		/* Exponential scale */
		_sticks_expo(0) = math::expo_deadzone(_sticks(0), _xy_vel_man_expo.get(), _stick_dz.get());
		_sticks_expo(1) = math::expo_deadzone(_sticks(1), _xy_vel_man_expo.get(), _stick_dz.get());
		_sticks_expo(2) = math::expo_deadzone(_sticks(2), _z_vel_man_expo.get(), _stick_dz.get());
		_sticks_expo(3) = math::expo_deadzone(_sticks(3), _yaw_expo.get(), _stick_dz.get());

		// Only switch the landing gear up if the user switched from gear down to gear up.
		// If the user had the switch in the gear up position and took off ignore it
		// until he toggles the switch to avoid retracting the gear immediately on takeoff.
		int8_t gear_switch = _sub_manual_control_setpoint->get().gear_switch;

		if (!_constraints.landing_gear) {
			if (gear_switch == manual_control_setpoint_s::SWITCH_POS_OFF) {
				_applyGearSwitch(gear_switch);
			}

		} else {
			_applyGearSwitch(gear_switch);
		}

		return true;

	} else {
		/* Timeout: set all sticks to zero */
		_sticks.zero();
		_sticks_expo.zero();
		_constraints.landing_gear = vehicle_constraints_s::GEAR_KEEP;
		return false;
	}
}

void FlightTaskManual::_applyGearSwitch(uint8_t gswitch)
{
	if (gswitch == manual_control_setpoint_s::SWITCH_POS_OFF) {
		_constraints.landing_gear = vehicle_constraints_s::GEAR_DOWN;
	}

	if (gswitch == manual_control_setpoint_s::SWITCH_POS_ON) {
		_constraints.landing_gear = vehicle_constraints_s::GEAR_UP;
	}
}
<commit_msg>FlightTaskManual: sticks not to be finite activation method not needed<commit_after>/****************************************************************************
 *
 *   Copyright (c) 2018 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/
/**
 * @file FlightTaskManual.cpp
 */

#include "FlightTaskManual.hpp"
#include <mathlib/mathlib.h>
#include <float.h>

using namespace matrix;

bool FlightTaskManual::initializeSubscriptions(SubscriptionArray &subscription_array)
{
	if (!FlightTask::initializeSubscriptions(subscription_array)) {
		return false;
	}

	if (!subscription_array.get(ORB_ID(manual_control_setpoint), _sub_manual_control_setpoint)) {
		return false;
	}

	return true;
}

bool FlightTaskManual::updateInitialize()
{
	bool ret = FlightTask::updateInitialize();
	const bool sticks_available = _evaluateSticks();

	if (_sticks_data_required) {
		ret = ret && sticks_available;
	}

	return ret;
}

bool FlightTaskManual::_evaluateSticks()
{
	/* Sticks are rescaled linearly and exponentially to [-1,1] */
	if ((_time_stamp_current - _sub_manual_control_setpoint->get().timestamp) < _timeout) {

		/* Linear scale  */
		_sticks(0) = _sub_manual_control_setpoint->get().x; /* NED x, "pitch" [-1,1] */
		_sticks(1) = _sub_manual_control_setpoint->get().y; /* NED y, "roll" [-1,1] */
		_sticks(2) = -(_sub_manual_control_setpoint->get().z - 0.5f) * 2.f; /* NED z, "thrust" resacaled from [0,1] to [-1,1] */
		_sticks(3) = _sub_manual_control_setpoint->get().r; /* "yaw" [-1,1] */

		/* Exponential scale */
		_sticks_expo(0) = math::expo_deadzone(_sticks(0), _xy_vel_man_expo.get(), _stick_dz.get());
		_sticks_expo(1) = math::expo_deadzone(_sticks(1), _xy_vel_man_expo.get(), _stick_dz.get());
		_sticks_expo(2) = math::expo_deadzone(_sticks(2), _z_vel_man_expo.get(), _stick_dz.get());
		_sticks_expo(3) = math::expo_deadzone(_sticks(3), _yaw_expo.get(), _stick_dz.get());

		// Only switch the landing gear up if the user switched from gear down to gear up.
		// If the user had the switch in the gear up position and took off ignore it
		// until he toggles the switch to avoid retracting the gear immediately on takeoff.
		int8_t gear_switch = _sub_manual_control_setpoint->get().gear_switch;

		if (!_constraints.landing_gear) {
			if (gear_switch == manual_control_setpoint_s::SWITCH_POS_OFF) {
				_applyGearSwitch(gear_switch);
			}

		} else {
			_applyGearSwitch(gear_switch);
		}

		// valid stick inputs are required
		const bool valid_sticks =  PX4_ISFINITE(_sticks(0))
					   && PX4_ISFINITE(_sticks(1))
					   && PX4_ISFINITE(_sticks(2))
					   && PX4_ISFINITE(_sticks(3));

		return valid_sticks;

	} else {
		/* Timeout: set all sticks to zero */
		_sticks.zero();
		_sticks_expo.zero();
		_constraints.landing_gear = vehicle_constraints_s::GEAR_KEEP;
		return false;
	}
}

void FlightTaskManual::_applyGearSwitch(uint8_t gswitch)
{
	if (gswitch == manual_control_setpoint_s::SWITCH_POS_OFF) {
		_constraints.landing_gear = vehicle_constraints_s::GEAR_DOWN;
	}

	if (gswitch == manual_control_setpoint_s::SWITCH_POS_ON) {
		_constraints.landing_gear = vehicle_constraints_s::GEAR_UP;
	}
}
<|endoftext|>


<commit_before>/****************************************************************************
 *
 *   Copyright (c) 2017 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/**
 * @file FlightTaskManual.hpp
 *
 * Linear and exponential map from stick inputs to range -1 and 1.
 *
 */

#pragma once

#include "FlightTask.hpp"
#include <uORB/topics/manual_control_setpoint.h>
#include <platforms/px4_defines.h>

class FlightTaskManual : public FlightTask
{
public:
	FlightTaskManual(control::SuperBlock *parent, const char *name);

	virtual ~FlightTaskManual() = default;

	bool initializeSubscriptions(SubscriptionArray &subscription_array) override;

	bool activate() override;

	bool applyCommandParameters(const vehicle_command_s &command) override { return FlightTask::applyCommandParameters(command); };

	bool updateInitialize() override;

protected:

	bool _sticks_data_required = true; /**< let inherited task-class define if it depends on stick data */
	matrix::Vector<float, 4> _sticks; /**< unmodified manual stick inputs */
	matrix::Vector3f _sticks_expo; /**< modified manual sticks using expo function*/
	control::BlockParamFloat _stick_dz; /**< 0-deadzone around the center for the sticks */

private:

	uORB::Subscription<manual_control_setpoint_s> *_sub_manual_control_setpoint{nullptr};

	control::BlockParamFloat _xy_vel_man_expo; /**< ratio of exponential curve for stick input in xy direction */
	control::BlockParamFloat _z_vel_man_expo; /**< ratio of exponential curve for stick input in z direction */

	bool _evaluateSticks(); /**< checks and sets stick inputs */
};
<commit_msg>FlightTaskManual: remove #include px4_defines.h<commit_after>/****************************************************************************
 *
 *   Copyright (c) 2017 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name PX4 nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************/

/**
 * @file FlightTaskManual.hpp
 *
 * Linear and exponential map from stick inputs to range -1 and 1.
 *
 */

#pragma once

#include "FlightTask.hpp"
#include <uORB/topics/manual_control_setpoint.h>

class FlightTaskManual : public FlightTask
{
public:
	FlightTaskManual(control::SuperBlock *parent, const char *name);

	virtual ~FlightTaskManual() = default;

	bool initializeSubscriptions(SubscriptionArray &subscription_array) override;

	bool activate() override;

	bool applyCommandParameters(const vehicle_command_s &command) override { return FlightTask::applyCommandParameters(command); };

	bool updateInitialize() override;

protected:

	bool _sticks_data_required = true; /**< let inherited task-class define if it depends on stick data */
	matrix::Vector<float, 4> _sticks; /**< unmodified manual stick inputs */
	matrix::Vector3f _sticks_expo; /**< modified manual sticks using expo function*/
	control::BlockParamFloat _stick_dz; /**< 0-deadzone around the center for the sticks */

private:

	uORB::Subscription<manual_control_setpoint_s> *_sub_manual_control_setpoint{nullptr};

	control::BlockParamFloat _xy_vel_man_expo; /**< ratio of exponential curve for stick input in xy direction */
	control::BlockParamFloat _z_vel_man_expo; /**< ratio of exponential curve for stick input in z direction */

	bool _evaluateSticks(); /**< checks and sets stick inputs */
};
<|endoftext|>


<commit_before>/*
 * Copyright 2007 Stephen Liu
 * For license terms, see the file COPYING along with this library.
 */

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#include "spporting.hpp"

#include "spioutils.hpp"

void SP_IOUtils :: inetNtoa( in_addr * addr, char * ip, int size )
{
#if defined (linux) || defined (__sgi) || defined (__hpux) || defined (__FreeBSD__)
	const unsigned char *p = ( const unsigned char *) addr;
	snprintf( ip, size, "%i.%i.%i.%i", p[0], p[1], p[2], p[3] );
#else
	snprintf( ip, size, "%i.%i.%i.%i", addr->s_net, addr->s_host, addr->s_lh, addr->s_impno );
#endif
}

int SP_IOUtils :: setNonblock( int fd )
{
#ifdef WIN32
	unsigned long nonblocking = 1;
	ioctlsocket( fd, FIONBIO, (unsigned long*) &nonblocking );
#else
	int flags;

	flags = fcntl( fd, F_GETFL );
	if( flags < 0 ) return flags;

	flags |= O_NONBLOCK;
	if( fcntl( fd, F_SETFL, flags ) < 0 ) return -1;
#endif

	return 0;
}

int SP_IOUtils :: setBlock( int fd )
{
#ifdef WIN32

	unsigned long nonblocking = 0;
	ioctlsocket( fd, FIONBIO, (unsigned long*) &nonblocking );

#else

	int flags;

	flags = fcntl( fd, F_GETFL );
	if( flags < 0 ) return flags;

	flags &= ~O_NONBLOCK;
	if( fcntl( fd, F_SETFL, flags ) < 0 ) return -1;
#endif

	return 0;
}

int SP_IOUtils :: tcpListen( const char * ip, int port, int * fd, int blocking )
{
	int ret = 0;

	int listenFd = socket( AF_INET, SOCK_STREAM, 0 );
	if( listenFd < 0 ) {
		sp_syslog( LOG_WARNING, "listen failed, errno %d, %s", errno, strerror( errno ) );
		ret = -1;
	}

	if( 0 == ret && 0 == blocking ) {
		if( setNonblock( listenFd ) < 0 ) {
			sp_syslog( LOG_WARNING, "failed to set socket to non-blocking" );
			ret = -1;
		}
	}

	if( 0 == ret ) {
		int flags = 1;
		if( setsockopt( listenFd, SOL_SOCKET, SO_REUSEADDR, (char*)&flags, sizeof( flags ) ) < 0 ) {
			sp_syslog( LOG_WARNING, "failed to set setsock to reuseaddr" );
			ret = -1;
		}
		if( setsockopt( listenFd, IPPROTO_TCP, TCP_NODELAY, (char*)&flags, sizeof(flags) ) < 0 ) {
			sp_syslog( LOG_WARNING, "failed to set socket to nodelay" );
			ret = -1;
		}
	}

	struct sockaddr_in addr;

	if( 0 == ret ) {
		memset( &addr, 0, sizeof( addr ) );
		addr.sin_family = AF_INET;
		addr.sin_port = htons( port );

		addr.sin_addr.s_addr = INADDR_ANY;
		if( '\0' != *ip ) {
			if( 0 == sp_inet_aton( ip, &addr.sin_addr ) ) {
				sp_syslog( LOG_WARNING, "failed to convert %s to inet_addr", ip );
				ret = -1;
			}
		}
	}

	if( 0 == ret ) {
		if( bind( listenFd, (struct sockaddr*)&addr, sizeof( addr ) ) < 0 ) {
			sp_syslog( LOG_WARNING, "bind failed, errno %d, %s", errno, strerror( errno ) );
			ret = -1;
		}
	}

	if( 0 == ret ) {
		if( ::listen( listenFd, 1024 ) < 0 ) {
			sp_syslog( LOG_WARNING, "listen failed, errno %d, %s", errno, strerror( errno ) );
			ret = -1;
		}
	}

	if( 0 != ret && listenFd >= 0 ) sp_close( listenFd );

	if( 0 == ret ) {
		* fd = listenFd;
		sp_syslog( LOG_NOTICE, "Listen on port [%d]", port );
	}

	return ret;
}

<commit_msg>porting to MacOS<commit_after>/*
 * Copyright 2007 Stephen Liu
 * For license terms, see the file COPYING along with this library.
 */

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

#include "spporting.hpp"

#include "spioutils.hpp"

void SP_IOUtils :: inetNtoa( in_addr * addr, char * ip, int size )
{
#if defined (linux) || defined (__sgi) || defined (__hpux) \
		|| defined (__FreeBSD__) || defined (__APPLE__)
	const unsigned char *p = ( const unsigned char *) addr;
	snprintf( ip, size, "%i.%i.%i.%i", p[0], p[1], p[2], p[3] );
#else
	snprintf( ip, size, "%i.%i.%i.%i", addr->s_net, addr->s_host, addr->s_lh, addr->s_impno );
#endif
}

int SP_IOUtils :: setNonblock( int fd )
{
#ifdef WIN32
	unsigned long nonblocking = 1;
	ioctlsocket( fd, FIONBIO, (unsigned long*) &nonblocking );
#else
	int flags;

	flags = fcntl( fd, F_GETFL );
	if( flags < 0 ) return flags;

	flags |= O_NONBLOCK;
	if( fcntl( fd, F_SETFL, flags ) < 0 ) return -1;
#endif

	return 0;
}

int SP_IOUtils :: setBlock( int fd )
{
#ifdef WIN32

	unsigned long nonblocking = 0;
	ioctlsocket( fd, FIONBIO, (unsigned long*) &nonblocking );

#else

	int flags;

	flags = fcntl( fd, F_GETFL );
	if( flags < 0 ) return flags;

	flags &= ~O_NONBLOCK;
	if( fcntl( fd, F_SETFL, flags ) < 0 ) return -1;
#endif

	return 0;
}

int SP_IOUtils :: tcpListen( const char * ip, int port, int * fd, int blocking )
{
	int ret = 0;

	int listenFd = socket( AF_INET, SOCK_STREAM, 0 );
	if( listenFd < 0 ) {
		sp_syslog( LOG_WARNING, "listen failed, errno %d, %s", errno, strerror( errno ) );
		ret = -1;
	}

	if( 0 == ret && 0 == blocking ) {
		if( setNonblock( listenFd ) < 0 ) {
			sp_syslog( LOG_WARNING, "failed to set socket to non-blocking" );
			ret = -1;
		}
	}

	if( 0 == ret ) {
		int flags = 1;
		if( setsockopt( listenFd, SOL_SOCKET, SO_REUSEADDR, (char*)&flags, sizeof( flags ) ) < 0 ) {
			sp_syslog( LOG_WARNING, "failed to set setsock to reuseaddr" );
			ret = -1;
		}
		if( setsockopt( listenFd, IPPROTO_TCP, TCP_NODELAY, (char*)&flags, sizeof(flags) ) < 0 ) {
			sp_syslog( LOG_WARNING, "failed to set socket to nodelay" );
			ret = -1;
		}
	}

	struct sockaddr_in addr;

	if( 0 == ret ) {
		memset( &addr, 0, sizeof( addr ) );
		addr.sin_family = AF_INET;
		addr.sin_port = htons( port );

		addr.sin_addr.s_addr = INADDR_ANY;
		if( '\0' != *ip ) {
			if( 0 == sp_inet_aton( ip, &addr.sin_addr ) ) {
				sp_syslog( LOG_WARNING, "failed to convert %s to inet_addr", ip );
				ret = -1;
			}
		}
	}

	if( 0 == ret ) {
		if( bind( listenFd, (struct sockaddr*)&addr, sizeof( addr ) ) < 0 ) {
			sp_syslog( LOG_WARNING, "bind failed, errno %d, %s", errno, strerror( errno ) );
			ret = -1;
		}
	}

	if( 0 == ret ) {
		if( ::listen( listenFd, 1024 ) < 0 ) {
			sp_syslog( LOG_WARNING, "listen failed, errno %d, %s", errno, strerror( errno ) );
			ret = -1;
		}
	}

	if( 0 != ret && listenFd >= 0 ) sp_close( listenFd );

	if( 0 == ret ) {
		* fd = listenFd;
		sp_syslog( LOG_NOTICE, "Listen on port [%d]", port );
	}

	return ret;
}

<|endoftext|>


<commit_before>//===- SymbolTable.cpp ----------------------------------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "Config.h"
#include "Driver.h"
#include "Error.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "lld/Core/Parallel.h"
#include "llvm/LTO/LTOCodeGenerator.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include <utility>

using namespace llvm;

namespace lld {
namespace coff {

void SymbolTable::addFile(std::unique_ptr<InputFile> FileP) {
  InputFile *File = FileP.get();
  Files.push_back(std::move(FileP));
  if (auto *F = dyn_cast<ArchiveFile>(File)) {
    ArchiveQueue.push_back(
        std::async(std::launch::async, [=]() { F->parse(); return F; }));
    return;
  }
  ObjectQueue.push_back(
      std::async(std::launch::async, [=]() { File->parse(); return File; }));
  if (auto *F = dyn_cast<ObjectFile>(File)) {
    ObjectFiles.push_back(F);
  } else if (auto *F = dyn_cast<BitcodeFile>(File)) {
    BitcodeFiles.push_back(F);
  } else {
    ImportFiles.push_back(cast<ImportFile>(File));
  }
}

void SymbolTable::step() {
  if (queueEmpty())
    return;
  readObjects();
  readArchives();
}

void SymbolTable::run() {
  while (!queueEmpty())
    step();
}

void SymbolTable::readArchives() {
  if (ArchiveQueue.empty())
    return;

  // Add lazy symbols to the symbol table. Lazy symbols that conflict
  // with existing undefined symbols are accumulated in LazySyms.
  std::vector<Symbol *> LazySyms;
  for (std::future<ArchiveFile *> &Future : ArchiveQueue) {
    ArchiveFile *File = Future.get();
    if (Config->Verbose)
      llvm::outs() << "Reading " << File->getShortName() << "\n";
    for (Lazy &Sym : File->getLazySymbols())
      addLazy(&Sym, &LazySyms);
  }
  ArchiveQueue.clear();

  // Add archive member files to ObjectQueue that should resolve
  // existing undefined symbols.
  for (Symbol *Sym : LazySyms)
    addMemberFile(cast<Lazy>(Sym->Body));
}

void SymbolTable::readObjects() {
  if (ObjectQueue.empty())
    return;

  // Add defined and undefined symbols to the symbol table.
  std::vector<StringRef> Directives;
  for (size_t I = 0; I < ObjectQueue.size(); ++I) {
    InputFile *File = ObjectQueue[I].get();
    if (Config->Verbose)
      llvm::outs() << "Reading " << File->getShortName() << "\n";
    // Adding symbols may add more files to ObjectQueue
    // (but not to ArchiveQueue).
    for (SymbolBody *Sym : File->getSymbols())
      if (Sym->isExternal())
        addSymbol(Sym);
    StringRef S = File->getDirectives();
    if (!S.empty()) {
      Directives.push_back(S);
      if (Config->Verbose)
        llvm::outs() << "Directives: " << File->getShortName()
                     << ": " << S << "\n";
    }
  }
  ObjectQueue.clear();

  // Parse directive sections. This may add files to
  // ArchiveQueue and ObjectQueue.
  for (StringRef S : Directives)
    Driver->parseDirectives(S);
}

bool SymbolTable::queueEmpty() {
  return ArchiveQueue.empty() && ObjectQueue.empty();
}

void SymbolTable::reportRemainingUndefines(bool Resolve) {
  llvm::SmallPtrSet<SymbolBody *, 8> Undefs;
  for (auto &I : Symtab) {
    Symbol *Sym = I.second;
    auto *Undef = dyn_cast<Undefined>(Sym->Body);
    if (!Undef)
      continue;
    StringRef Name = Undef->getName();
    // A weak alias may have been resolved, so check for that.
    if (Defined *D = Undef->getWeakAlias()) {
      if (Resolve)
        Sym->Body = D;
      continue;
    }
    // If we can resolve a symbol by removing __imp_ prefix, do that.
    // This odd rule is for compatibility with MSVC linker.
    if (Name.startswith("__imp_")) {
      Symbol *Imp = find(Name.substr(strlen("__imp_")));
      if (Imp && isa<Defined>(Imp->Body)) {
        if (!Resolve)
          continue;
        auto *D = cast<Defined>(Imp->Body);
        auto *S = new (Alloc) DefinedLocalImport(Name, D);
        LocalImportChunks.push_back(S->getChunk());
        Sym->Body = S;
        continue;
      }
    }
    // Remaining undefined symbols are not fatal if /force is specified.
    // They are replaced with dummy defined symbols.
    if (Config->Force && Resolve)
      Sym->Body = new (Alloc) DefinedAbsolute(Name, 0);
    Undefs.insert(Sym->Body);
  }
  if (Undefs.empty())
    return;
  for (Undefined *U : Config->GCRoot)
    if (Undefs.count(U->repl()))
      llvm::errs() << "<root>: undefined symbol: " << U->getName() << "\n";
  for (std::unique_ptr<InputFile> &File : Files)
    if (!isa<ArchiveFile>(File.get()))
      for (SymbolBody *Sym : File->getSymbols())
        if (Undefs.count(Sym->repl()))
          llvm::errs() << File->getShortName() << ": undefined symbol: "
                       << Sym->getName() << "\n";
  if (!Config->Force)
    error("Link failed");
}

void SymbolTable::addLazy(Lazy *New, std::vector<Symbol *> *Accum) {
  Symbol *Sym = insert(New);
  if (Sym->Body == New)
    return;
  SymbolBody *Existing = Sym->Body;
  if (isa<Defined>(Existing))
    return;
  if (Lazy *L = dyn_cast<Lazy>(Existing))
    if (L->getFileIndex() < New->getFileIndex())
      return;
  Sym->Body = New;
  New->setBackref(Sym);
  if (isa<Undefined>(Existing))
    Accum->push_back(Sym);
}

void SymbolTable::addSymbol(SymbolBody *New) {
  // Find an existing symbol or create and insert a new one.
  assert(isa<Defined>(New) || isa<Undefined>(New));
  Symbol *Sym = insert(New);
  if (Sym->Body == New)
    return;
  SymbolBody *Existing = Sym->Body;

  // If we have an undefined symbol and a lazy symbol,
  // let the lazy symbol to read a member file.
  if (auto *L = dyn_cast<Lazy>(Existing)) {
    // Undefined symbols with weak aliases need not to be resolved,
    // since they would be replaced with weak aliases if they remain
    // undefined.
    if (auto *U = dyn_cast<Undefined>(New)) {
      if (!U->WeakAlias) {
        addMemberFile(L);
        return;
      }
    }
    Sym->Body = New;
    return;
  }

  // compare() returns -1, 0, or 1 if the lhs symbol is less preferable,
  // equivalent (conflicting), or more preferable, respectively.
  int Comp = Existing->compare(New);
  if (Comp == 0)
    error(Twine("duplicate symbol: ") + Existing->getDebugName() + " and " +
          New->getDebugName());
  if (Comp < 0)
    Sym->Body = New;
}

Symbol *SymbolTable::insert(SymbolBody *New) {
  Symbol *&Sym = Symtab[New->getName()];
  if (Sym) {
    New->setBackref(Sym);
    return Sym;
  }
  Sym = new (Alloc) Symbol(New);
  New->setBackref(Sym);
  return Sym;
}

// Reads an archive member file pointed by a given symbol.
void SymbolTable::addMemberFile(Lazy *Body) {
  std::unique_ptr<InputFile> File = Body->getMember();

  // getMember returns an empty buffer if the member was already
  // read from the library.
  if (!File)
    return;
  if (Config->Verbose)
    llvm::outs() << "Loaded " << File->getShortName() << " for "
                 << Body->getName() << "\n";
  addFile(std::move(File));
}

std::vector<Chunk *> SymbolTable::getChunks() {
  std::vector<Chunk *> Res;
  for (ObjectFile *File : ObjectFiles) {
    std::vector<Chunk *> &V = File->getChunks();
    Res.insert(Res.end(), V.begin(), V.end());
  }
  return Res;
}

Symbol *SymbolTable::find(StringRef Name) {
  auto It = Symtab.find(Name);
  if (It == Symtab.end())
    return nullptr;
  return It->second;
}

Symbol *SymbolTable::findUnderscore(StringRef Name) {
  if (Config->Machine == I386)
    return find(("_" + Name).str());
  return find(Name);
}

StringRef SymbolTable::findByPrefix(StringRef Prefix) {
  for (auto Pair : Symtab) {
    StringRef Name = Pair.first;
    if (Name.startswith(Prefix))
      return Name;
  }
  return "";
}

StringRef SymbolTable::findMangle(StringRef Name) {
  if (Symbol *Sym = find(Name))
    if (!isa<Undefined>(Sym->Body))
      return Name;
  if (Config->Machine != I386)
    return findByPrefix(("?" + Name + "@@Y").str());
  if (!Name.startswith("_"))
    return "";
  // Search for x86 C function.
  StringRef S = findByPrefix((Name + "@").str());
  if (!S.empty())
    return S;
  // Search for x86 C++ non-member function.
  return findByPrefix(("?" + Name.substr(1) + "@@Y").str());
}

void SymbolTable::mangleMaybe(Undefined *U) {
  if (U->WeakAlias)
    return;
  if (!isa<Undefined>(U->repl()))
    return;
  StringRef Alias = findMangle(U->getName());
  if (!Alias.empty())
    U->WeakAlias = addUndefined(Alias);
}

Undefined *SymbolTable::addUndefined(StringRef Name) {
  auto *New = new (Alloc) Undefined(Name);
  addSymbol(New);
  if (auto *U = dyn_cast<Undefined>(New->repl()))
    return U;
  return New;
}

DefinedRelative *SymbolTable::addRelative(StringRef Name, uint64_t VA) {
  auto *New = new (Alloc) DefinedRelative(Name, VA);
  addSymbol(New);
  return New;
}

DefinedAbsolute *SymbolTable::addAbsolute(StringRef Name, uint64_t VA) {
  auto *New = new (Alloc) DefinedAbsolute(Name, VA);
  addSymbol(New);
  return New;
}

void SymbolTable::printMap(llvm::raw_ostream &OS) {
  for (ObjectFile *File : ObjectFiles) {
    OS << File->getShortName() << ":\n";
    for (SymbolBody *Body : File->getSymbols())
      if (auto *R = dyn_cast<DefinedRegular>(Body))
        if (R->getChunk()->isLive())
          OS << Twine::utohexstr(Config->ImageBase + R->getRVA())
             << " " << R->getName() << "\n";
  }
}

void SymbolTable::addCombinedLTOObject(ObjectFile *Obj) {
  for (SymbolBody *Body : Obj->getSymbols()) {
    if (!Body->isExternal())
      continue;
    // We should not see any new undefined symbols at this point, but we'll
    // diagnose them later in reportRemainingUndefines().
    StringRef Name = Body->getName();
    Symbol *Sym = insert(Body);

    if (isa<DefinedBitcode>(Sym->Body)) {
      Sym->Body = Body;
      continue;
    }
    if (auto *L = dyn_cast<Lazy>(Sym->Body)) {
      // We may see new references to runtime library symbols such as __chkstk
      // here. These symbols must be wholly defined in non-bitcode files.
      addMemberFile(L);
      continue;
    }
    SymbolBody *Existing = Sym->Body;
    int Comp = Existing->compare(Body);
    if (Comp == 0)
      error(Twine("LTO: unexpected duplicate symbol: ") + Name);
    if (Comp < 0)
      Sym->Body = Body;
  }
}

void SymbolTable::addCombinedLTOObjects() {
  if (BitcodeFiles.empty())
    return;

  // Diagnose any undefined symbols early, but do not resolve weak externals,
  // as resolution breaks the invariant that each Symbol points to a unique
  // SymbolBody, which we rely on to replace DefinedBitcode symbols correctly.
  reportRemainingUndefines(/*Resolve=*/false);

  // Create an object file and add it to the symbol table by replacing any
  // DefinedBitcode symbols with the definitions in the object file.
  LTOCodeGenerator CG;
  CG.setOptLevel(Config->LTOOptLevel);
  std::vector<ObjectFile *> Objs = createLTOObjects(&CG);

  for (ObjectFile *Obj : Objs)
    addCombinedLTOObject(Obj);

  size_t NumBitcodeFiles = BitcodeFiles.size();
  run();
  if (BitcodeFiles.size() != NumBitcodeFiles)
    error("LTO: late loaded symbol created new bitcode reference");
}

// Combine and compile bitcode files and then return the result
// as a vector of regular COFF object files.
std::vector<ObjectFile *> SymbolTable::createLTOObjects(LTOCodeGenerator *CG) {
  // All symbols referenced by non-bitcode objects must be preserved.
  for (ObjectFile *File : ObjectFiles)
    for (SymbolBody *Body : File->getSymbols())
      if (auto *S = dyn_cast<DefinedBitcode>(Body->repl()))
        CG->addMustPreserveSymbol(S->getName());

  // Likewise for bitcode symbols which we initially resolved to non-bitcode.
  for (BitcodeFile *File : BitcodeFiles)
    for (SymbolBody *Body : File->getSymbols())
      if (isa<DefinedBitcode>(Body) && !isa<DefinedBitcode>(Body->repl()))
        CG->addMustPreserveSymbol(Body->getName());

  // Likewise for other symbols that must be preserved.
  for (Undefined *U : Config->GCRoot) {
    if (auto *S = dyn_cast<DefinedBitcode>(U->repl()))
      CG->addMustPreserveSymbol(S->getName());
    else if (auto *S = dyn_cast_or_null<DefinedBitcode>(U->getWeakAlias()))
      CG->addMustPreserveSymbol(S->getName());
  }

  CG->setModule(BitcodeFiles[0]->takeModule());
  for (unsigned I = 1, E = BitcodeFiles.size(); I != E; ++I)
    CG->addModule(BitcodeFiles[I]->getModule());

  bool DisableVerify = true;
#ifdef NDEBUG
  DisableVerify = false;
#endif
  std::string ErrMsg;
  if (!CG->optimize(DisableVerify, false, false, false, ErrMsg))
    error(ErrMsg);

  Objs.resize(Config->LTOJobs);
  // Use std::list to avoid invalidation of pointers in OSPtrs.
  std::list<raw_svector_ostream> OSs;
  std::vector<raw_pwrite_stream *> OSPtrs;
  for (SmallVector<char, 0> &Obj : Objs) {
    OSs.emplace_back(Obj);
    OSPtrs.push_back(&OSs.back());
  }

  if (!CG->compileOptimized(OSPtrs, ErrMsg))
    error(ErrMsg);

  std::vector<ObjectFile *> ObjFiles;
  for (SmallVector<char, 0> &Obj : Objs) {
    auto *ObjFile = new ObjectFile(
        MemoryBufferRef(StringRef(Obj.data(), Obj.size()), "<LTO object>"));
    Files.emplace_back(ObjFile);
    ObjectFiles.push_back(ObjFile);
    ObjFile->parse();
    ObjFiles.push_back(ObjFile);
  }

  return ObjFiles;
}

} // namespace coff
} // namespace lld
<commit_msg>COFF: Do not call std::async with std::launch::async if multithreading is disabled.<commit_after>//===- SymbolTable.cpp ----------------------------------------------------===//
//
//                             The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "Config.h"
#include "Driver.h"
#include "Error.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "lld/Core/Parallel.h"
#include "llvm/LTO/LTOCodeGenerator.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include <utility>

using namespace llvm;

namespace lld {
namespace coff {

void SymbolTable::addFile(std::unique_ptr<InputFile> FileP) {
#if LLVM_ENABLE_THREADS
  std::launch Policy = std::launch::async;
#else
  std::launch Policy = std::launch::deferred;
#endif

  InputFile *File = FileP.get();
  Files.push_back(std::move(FileP));
  if (auto *F = dyn_cast<ArchiveFile>(File)) {
    ArchiveQueue.push_back(
        std::async(Policy, [=]() { F->parse(); return F; }));
    return;
  }
  ObjectQueue.push_back(
      std::async(Policy, [=]() { File->parse(); return File; }));
  if (auto *F = dyn_cast<ObjectFile>(File)) {
    ObjectFiles.push_back(F);
  } else if (auto *F = dyn_cast<BitcodeFile>(File)) {
    BitcodeFiles.push_back(F);
  } else {
    ImportFiles.push_back(cast<ImportFile>(File));
  }
}

void SymbolTable::step() {
  if (queueEmpty())
    return;
  readObjects();
  readArchives();
}

void SymbolTable::run() {
  while (!queueEmpty())
    step();
}

void SymbolTable::readArchives() {
  if (ArchiveQueue.empty())
    return;

  // Add lazy symbols to the symbol table. Lazy symbols that conflict
  // with existing undefined symbols are accumulated in LazySyms.
  std::vector<Symbol *> LazySyms;
  for (std::future<ArchiveFile *> &Future : ArchiveQueue) {
    ArchiveFile *File = Future.get();
    if (Config->Verbose)
      llvm::outs() << "Reading " << File->getShortName() << "\n";
    for (Lazy &Sym : File->getLazySymbols())
      addLazy(&Sym, &LazySyms);
  }
  ArchiveQueue.clear();

  // Add archive member files to ObjectQueue that should resolve
  // existing undefined symbols.
  for (Symbol *Sym : LazySyms)
    addMemberFile(cast<Lazy>(Sym->Body));
}

void SymbolTable::readObjects() {
  if (ObjectQueue.empty())
    return;

  // Add defined and undefined symbols to the symbol table.
  std::vector<StringRef> Directives;
  for (size_t I = 0; I < ObjectQueue.size(); ++I) {
    InputFile *File = ObjectQueue[I].get();
    if (Config->Verbose)
      llvm::outs() << "Reading " << File->getShortName() << "\n";
    // Adding symbols may add more files to ObjectQueue
    // (but not to ArchiveQueue).
    for (SymbolBody *Sym : File->getSymbols())
      if (Sym->isExternal())
        addSymbol(Sym);
    StringRef S = File->getDirectives();
    if (!S.empty()) {
      Directives.push_back(S);
      if (Config->Verbose)
        llvm::outs() << "Directives: " << File->getShortName()
                     << ": " << S << "\n";
    }
  }
  ObjectQueue.clear();

  // Parse directive sections. This may add files to
  // ArchiveQueue and ObjectQueue.
  for (StringRef S : Directives)
    Driver->parseDirectives(S);
}

bool SymbolTable::queueEmpty() {
  return ArchiveQueue.empty() && ObjectQueue.empty();
}

void SymbolTable::reportRemainingUndefines(bool Resolve) {
  llvm::SmallPtrSet<SymbolBody *, 8> Undefs;
  for (auto &I : Symtab) {
    Symbol *Sym = I.second;
    auto *Undef = dyn_cast<Undefined>(Sym->Body);
    if (!Undef)
      continue;
    StringRef Name = Undef->getName();
    // A weak alias may have been resolved, so check for that.
    if (Defined *D = Undef->getWeakAlias()) {
      if (Resolve)
        Sym->Body = D;
      continue;
    }
    // If we can resolve a symbol by removing __imp_ prefix, do that.
    // This odd rule is for compatibility with MSVC linker.
    if (Name.startswith("__imp_")) {
      Symbol *Imp = find(Name.substr(strlen("__imp_")));
      if (Imp && isa<Defined>(Imp->Body)) {
        if (!Resolve)
          continue;
        auto *D = cast<Defined>(Imp->Body);
        auto *S = new (Alloc) DefinedLocalImport(Name, D);
        LocalImportChunks.push_back(S->getChunk());
        Sym->Body = S;
        continue;
      }
    }
    // Remaining undefined symbols are not fatal if /force is specified.
    // They are replaced with dummy defined symbols.
    if (Config->Force && Resolve)
      Sym->Body = new (Alloc) DefinedAbsolute(Name, 0);
    Undefs.insert(Sym->Body);
  }
  if (Undefs.empty())
    return;
  for (Undefined *U : Config->GCRoot)
    if (Undefs.count(U->repl()))
      llvm::errs() << "<root>: undefined symbol: " << U->getName() << "\n";
  for (std::unique_ptr<InputFile> &File : Files)
    if (!isa<ArchiveFile>(File.get()))
      for (SymbolBody *Sym : File->getSymbols())
        if (Undefs.count(Sym->repl()))
          llvm::errs() << File->getShortName() << ": undefined symbol: "
                       << Sym->getName() << "\n";
  if (!Config->Force)
    error("Link failed");
}

void SymbolTable::addLazy(Lazy *New, std::vector<Symbol *> *Accum) {
  Symbol *Sym = insert(New);
  if (Sym->Body == New)
    return;
  SymbolBody *Existing = Sym->Body;
  if (isa<Defined>(Existing))
    return;
  if (Lazy *L = dyn_cast<Lazy>(Existing))
    if (L->getFileIndex() < New->getFileIndex())
      return;
  Sym->Body = New;
  New->setBackref(Sym);
  if (isa<Undefined>(Existing))
    Accum->push_back(Sym);
}

void SymbolTable::addSymbol(SymbolBody *New) {
  // Find an existing symbol or create and insert a new one.
  assert(isa<Defined>(New) || isa<Undefined>(New));
  Symbol *Sym = insert(New);
  if (Sym->Body == New)
    return;
  SymbolBody *Existing = Sym->Body;

  // If we have an undefined symbol and a lazy symbol,
  // let the lazy symbol to read a member file.
  if (auto *L = dyn_cast<Lazy>(Existing)) {
    // Undefined symbols with weak aliases need not to be resolved,
    // since they would be replaced with weak aliases if they remain
    // undefined.
    if (auto *U = dyn_cast<Undefined>(New)) {
      if (!U->WeakAlias) {
        addMemberFile(L);
        return;
      }
    }
    Sym->Body = New;
    return;
  }

  // compare() returns -1, 0, or 1 if the lhs symbol is less preferable,
  // equivalent (conflicting), or more preferable, respectively.
  int Comp = Existing->compare(New);
  if (Comp == 0)
    error(Twine("duplicate symbol: ") + Existing->getDebugName() + " and " +
          New->getDebugName());
  if (Comp < 0)
    Sym->Body = New;
}

Symbol *SymbolTable::insert(SymbolBody *New) {
  Symbol *&Sym = Symtab[New->getName()];
  if (Sym) {
    New->setBackref(Sym);
    return Sym;
  }
  Sym = new (Alloc) Symbol(New);
  New->setBackref(Sym);
  return Sym;
}

// Reads an archive member file pointed by a given symbol.
void SymbolTable::addMemberFile(Lazy *Body) {
  std::unique_ptr<InputFile> File = Body->getMember();

  // getMember returns an empty buffer if the member was already
  // read from the library.
  if (!File)
    return;
  if (Config->Verbose)
    llvm::outs() << "Loaded " << File->getShortName() << " for "
                 << Body->getName() << "\n";
  addFile(std::move(File));
}

std::vector<Chunk *> SymbolTable::getChunks() {
  std::vector<Chunk *> Res;
  for (ObjectFile *File : ObjectFiles) {
    std::vector<Chunk *> &V = File->getChunks();
    Res.insert(Res.end(), V.begin(), V.end());
  }
  return Res;
}

Symbol *SymbolTable::find(StringRef Name) {
  auto It = Symtab.find(Name);
  if (It == Symtab.end())
    return nullptr;
  return It->second;
}

Symbol *SymbolTable::findUnderscore(StringRef Name) {
  if (Config->Machine == I386)
    return find(("_" + Name).str());
  return find(Name);
}

StringRef SymbolTable::findByPrefix(StringRef Prefix) {
  for (auto Pair : Symtab) {
    StringRef Name = Pair.first;
    if (Name.startswith(Prefix))
      return Name;
  }
  return "";
}

StringRef SymbolTable::findMangle(StringRef Name) {
  if (Symbol *Sym = find(Name))
    if (!isa<Undefined>(Sym->Body))
      return Name;
  if (Config->Machine != I386)
    return findByPrefix(("?" + Name + "@@Y").str());
  if (!Name.startswith("_"))
    return "";
  // Search for x86 C function.
  StringRef S = findByPrefix((Name + "@").str());
  if (!S.empty())
    return S;
  // Search for x86 C++ non-member function.
  return findByPrefix(("?" + Name.substr(1) + "@@Y").str());
}

void SymbolTable::mangleMaybe(Undefined *U) {
  if (U->WeakAlias)
    return;
  if (!isa<Undefined>(U->repl()))
    return;
  StringRef Alias = findMangle(U->getName());
  if (!Alias.empty())
    U->WeakAlias = addUndefined(Alias);
}

Undefined *SymbolTable::addUndefined(StringRef Name) {
  auto *New = new (Alloc) Undefined(Name);
  addSymbol(New);
  if (auto *U = dyn_cast<Undefined>(New->repl()))
    return U;
  return New;
}

DefinedRelative *SymbolTable::addRelative(StringRef Name, uint64_t VA) {
  auto *New = new (Alloc) DefinedRelative(Name, VA);
  addSymbol(New);
  return New;
}

DefinedAbsolute *SymbolTable::addAbsolute(StringRef Name, uint64_t VA) {
  auto *New = new (Alloc) DefinedAbsolute(Name, VA);
  addSymbol(New);
  return New;
}

void SymbolTable::printMap(llvm::raw_ostream &OS) {
  for (ObjectFile *File : ObjectFiles) {
    OS << File->getShortName() << ":\n";
    for (SymbolBody *Body : File->getSymbols())
      if (auto *R = dyn_cast<DefinedRegular>(Body))
        if (R->getChunk()->isLive())
          OS << Twine::utohexstr(Config->ImageBase + R->getRVA())
             << " " << R->getName() << "\n";
  }
}

void SymbolTable::addCombinedLTOObject(ObjectFile *Obj) {
  for (SymbolBody *Body : Obj->getSymbols()) {
    if (!Body->isExternal())
      continue;
    // We should not see any new undefined symbols at this point, but we'll
    // diagnose them later in reportRemainingUndefines().
    StringRef Name = Body->getName();
    Symbol *Sym = insert(Body);

    if (isa<DefinedBitcode>(Sym->Body)) {
      Sym->Body = Body;
      continue;
    }
    if (auto *L = dyn_cast<Lazy>(Sym->Body)) {
      // We may see new references to runtime library symbols such as __chkstk
      // here. These symbols must be wholly defined in non-bitcode files.
      addMemberFile(L);
      continue;
    }
    SymbolBody *Existing = Sym->Body;
    int Comp = Existing->compare(Body);
    if (Comp == 0)
      error(Twine("LTO: unexpected duplicate symbol: ") + Name);
    if (Comp < 0)
      Sym->Body = Body;
  }
}

void SymbolTable::addCombinedLTOObjects() {
  if (BitcodeFiles.empty())
    return;

  // Diagnose any undefined symbols early, but do not resolve weak externals,
  // as resolution breaks the invariant that each Symbol points to a unique
  // SymbolBody, which we rely on to replace DefinedBitcode symbols correctly.
  reportRemainingUndefines(/*Resolve=*/false);

  // Create an object file and add it to the symbol table by replacing any
  // DefinedBitcode symbols with the definitions in the object file.
  LTOCodeGenerator CG;
  CG.setOptLevel(Config->LTOOptLevel);
  std::vector<ObjectFile *> Objs = createLTOObjects(&CG);

  for (ObjectFile *Obj : Objs)
    addCombinedLTOObject(Obj);

  size_t NumBitcodeFiles = BitcodeFiles.size();
  run();
  if (BitcodeFiles.size() != NumBitcodeFiles)
    error("LTO: late loaded symbol created new bitcode reference");
}

// Combine and compile bitcode files and then return the result
// as a vector of regular COFF object files.
std::vector<ObjectFile *> SymbolTable::createLTOObjects(LTOCodeGenerator *CG) {
  // All symbols referenced by non-bitcode objects must be preserved.
  for (ObjectFile *File : ObjectFiles)
    for (SymbolBody *Body : File->getSymbols())
      if (auto *S = dyn_cast<DefinedBitcode>(Body->repl()))
        CG->addMustPreserveSymbol(S->getName());

  // Likewise for bitcode symbols which we initially resolved to non-bitcode.
  for (BitcodeFile *File : BitcodeFiles)
    for (SymbolBody *Body : File->getSymbols())
      if (isa<DefinedBitcode>(Body) && !isa<DefinedBitcode>(Body->repl()))
        CG->addMustPreserveSymbol(Body->getName());

  // Likewise for other symbols that must be preserved.
  for (Undefined *U : Config->GCRoot) {
    if (auto *S = dyn_cast<DefinedBitcode>(U->repl()))
      CG->addMustPreserveSymbol(S->getName());
    else if (auto *S = dyn_cast_or_null<DefinedBitcode>(U->getWeakAlias()))
      CG->addMustPreserveSymbol(S->getName());
  }

  CG->setModule(BitcodeFiles[0]->takeModule());
  for (unsigned I = 1, E = BitcodeFiles.size(); I != E; ++I)
    CG->addModule(BitcodeFiles[I]->getModule());

  bool DisableVerify = true;
#ifdef NDEBUG
  DisableVerify = false;
#endif
  std::string ErrMsg;
  if (!CG->optimize(DisableVerify, false, false, false, ErrMsg))
    error(ErrMsg);

  Objs.resize(Config->LTOJobs);
  // Use std::list to avoid invalidation of pointers in OSPtrs.
  std::list<raw_svector_ostream> OSs;
  std::vector<raw_pwrite_stream *> OSPtrs;
  for (SmallVector<char, 0> &Obj : Objs) {
    OSs.emplace_back(Obj);
    OSPtrs.push_back(&OSs.back());
  }

  if (!CG->compileOptimized(OSPtrs, ErrMsg))
    error(ErrMsg);

  std::vector<ObjectFile *> ObjFiles;
  for (SmallVector<char, 0> &Obj : Objs) {
    auto *ObjFile = new ObjectFile(
        MemoryBufferRef(StringRef(Obj.data(), Obj.size()), "<LTO object>"));
    Files.emplace_back(ObjFile);
    ObjectFiles.push_back(ObjFile);
    ObjFile->parse();
    ObjFiles.push_back(ObjFile);
  }

  return ObjFiles;
}

} // namespace coff
} // namespace lld
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
// Name:      shell.cpp
// Purpose:   Implementation of class wxExSTCShell
// Author:    Anton van Wezenbeek
// Copyright: (c) 2012 Anton van Wezenbeek
////////////////////////////////////////////////////////////////////////////////

#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <numeric>
#include <functional>
#include <wx/config.h>
#include <wx/tokenzr.h>
#include <wx/extension/shell.h>
#include <wx/extension/defs.h> // for ID_SHELL_COMMAND
#include <wx/extension/util.h>

#if wxUSE_GUI

BEGIN_EVENT_TABLE(wxExSTCShell, wxExSTC)
  EVT_CHAR(wxExSTCShell::OnChar)
  EVT_KEY_DOWN(wxExSTCShell::OnKey)
  EVT_STC_CHARADDED(wxID_ANY, wxExSTCShell::OnStyledText)
END_EVENT_TABLE()

wxExSTCShell::wxExSTCShell(
  wxWindow* parent,
  const wxString& prompt,
  const wxString& command_end,
  bool echo,
  int commands_save_in_config,
  const wxString& lexer,
  long menu_flags,
  wxWindowID id,
  const wxPoint& pos,
  const wxSize& size,
  long style)
  : wxExSTC(
      parent,
      wxEmptyString,
      STC_WIN_NO_INDICATOR,
      wxEmptyString, // title
      menu_flags,
      id,
      pos,
      size,
      style)
  , m_Command(wxEmptyString)
  , m_CommandEnd((command_end == wxEmptyString ? GetEOL(): command_end))
  , m_CommandStartPosition(0)
  , m_Echo(echo)
  // take a char that is not likely to appear inside commands
  , m_CommandsInConfigDelimiter(wxUniChar(0x03))
  , m_CommandsSaveInConfig(commands_save_in_config)
  , m_Prompt(prompt)
  , m_Handler(parent)
  , m_Enabled(true)
{
  // Override defaults from config.
  SetEdgeMode(wxSTC_EDGE_NONE);
  ResetMargins(false); // do not reset divider margin
  SetName("SHELL");

  // Start with a prompt.
  Prompt();

  if (m_CommandsSaveInConfig > 0)
  {
    // Get all previous commands.
    wxStringTokenizer tkz(wxConfigBase::Get()->Read("Shell"),
      m_CommandsInConfigDelimiter);

    while (tkz.HasMoreTokens())
    {
      const wxString val = tkz.GetNextToken();
      m_Commands.push_front(val);
    }
  }

  // Take care that m_CommandsIterator is valid.
  m_CommandsIterator = m_Commands.end();

  EnableShell(true);

  SetLexer(lexer);
}

wxExSTCShell::~wxExSTCShell()
{
  if (m_CommandsSaveInConfig > 0)
  {
    wxString values;
    int items = 0;

    for (
#ifdef wxExUSE_CPP0X
      auto it = m_Commands.rbegin();
#else
      std::list < wxString >::reverse_iterator it = m_Commands.rbegin();
#endif
      it != m_Commands.rend() && items < m_CommandsSaveInConfig;
      ++it)
    {
      values += *it + m_CommandsInConfigDelimiter;
      items++;
    }

    wxConfigBase::Get()->Write("Shell", values);
  }
}

void wxExSTCShell::AddText(const wxString& text)
{
  wxExSTC::AddText(text);
  EnsureCaretVisible();
  m_CommandStartPosition = GetCurrentPos();
}

void wxExSTCShell::EnableShell(bool enabled)
{
  m_Enabled = enabled;

  if (!m_Enabled)
  {
    // A disabled shell follows STC vi mode.
    GetVi().Use(wxConfigBase::Get()->ReadBool(_("vi mode"), true));
  }
  else
  {
    // An enabled shell does not use vi mode.
    GetVi().Use(false);
  }
}

void wxExSTCShell::Expand()
{
  wxDir dir(wxGetCwd());
  wxString filename;
  const wxString word(m_Command.AfterLast(' '));

  if (dir.GetFirst(&filename, word + "*"))
  {
    const wxString expansion = filename.Mid(word.length());

    AddText(expansion);
    m_Command += expansion;
  }
}

const wxString wxExSTCShell::GetCommand() const
{
  if (!m_Commands.empty())
  {
    return m_Commands.back();
  }
  else
  {
    return wxEmptyString;
  }
}

const wxString wxExSTCShell::GetHistory() const
{
  return accumulate(m_Commands.begin(), m_Commands.end(), wxString());
}

void wxExSTCShell::KeepCommand()
{
  m_Commands.remove(m_Command);
  m_Commands.push_back(m_Command);
}

// No longer used, for the moment.
void wxExSTCShell::OnCommand(wxCommandEvent& command)
{
  if (!m_Enabled)
  {
    command.Skip();
    return;
  }

  switch (command.GetId())
  {
    default:
      wxFAIL;
      break;
  }
}

void wxExSTCShell::OnChar(wxKeyEvent& event)
{
  if (m_Enabled)
  {
    ProcessChar(event.GetKeyCode());
  }

  if (m_Echo)
  {
    event.Skip();
  }
}

void wxExSTCShell::OnKey(wxKeyEvent& event)
{
  if (!m_Enabled)
  {
    event.Skip();
    return;
  }

  const int key = event.GetKeyCode();

  if (key == WXK_RETURN || key == WXK_TAB)
  {
    ProcessChar(key);
  }
  // Up or down key pressed, and at the end of document.
  else if ((key == WXK_UP || key == WXK_DOWN) &&
            GetCurrentPos() == GetTextLength())
  {
    ShowCommand(key);
  }
  // Home key pressed.
  else if (key == WXK_HOME)
  {
    Home();

    const wxString line = GetLine(GetCurrentLine());

    if (line.StartsWith(m_Prompt))
    {
      GotoPos(GetCurrentPos() + m_Prompt.length());
    }
  }
  // Ctrl-Q pressed, used to stop processing.
  // Ctrl-C pressed and no text selected (otherwise copy), also used to stop processing.
  else if (
    event.GetModifiers() == wxMOD_CONTROL &&
   ( key == 'Q' ||
    (key == 'C' && GetSelectedText().empty())))
  {
    wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, ID_SHELL_COMMAND_STOP);
    wxPostEvent(m_Handler, event);
  }
  // Ctrl-V pressed, used for pasting as well.
  else if (event.GetModifiers() == wxMOD_CONTROL && key == 'V')
  {
    Paste();
  }
  // Backspace or delete key pressed.
  else if (key == WXK_BACK || key == WXK_DELETE)
  {
    if (GetCurrentPos() <= m_CommandStartPosition)
    {
      // Ignore, so do nothing.
    }
    else
    {
      // Allow.
      ProcessChar(key);
      if (m_Echo) event.Skip();
    }
  }
  // The rest.
  else
  {
    // If we enter regular text and not already building a command, first goto end.
    if (event.GetModifiers() == wxMOD_NONE &&
        key < WXK_START &&
        GetCurrentPos() < m_CommandStartPosition)
    {
      DocumentEnd();
    }

    m_CommandsIterator = m_Commands.end();

    if (m_Echo) event.Skip();
  }
}

void wxExSTCShell::OnStyledText(wxStyledTextEvent& event)
{
  if (!m_Enabled)
  {
    event.Skip();
    return;
  }

  // do nothing, keep event from sent to wxExSTC.
}

void wxExSTCShell::Paste()
{
  if (!CanPaste())
  {
    return;
  }

  // Take care that we cannot paste somewhere inside.
  if (GetCurrentPos() < m_CommandStartPosition)
  {
    DocumentEnd();
  }

  wxExSTC::Paste();

  m_Command += wxExClipboardGet();
}

void wxExSTCShell::ProcessChar(int key)
{
  // No need to check m_Enabled, already done by calling this method.

  if (key == WXK_RETURN)
  {
    if (m_Command.empty())
    {
      wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, ID_SHELL_COMMAND);
      event.SetString(m_Command);
      wxPostEvent(m_Handler, event);
    }
    else if (
      m_CommandEnd == GetEOL() ||
      m_Command.EndsWith(m_CommandEnd))
    {
      // We have a command.
      EmptyUndoBuffer();

      // History command.
      if (m_Command == wxString("history") +
         (m_CommandEnd == GetEOL() ? wxString(wxEmptyString): m_CommandEnd))
      {
        KeepCommand();
        ShowHistory();
        Prompt();
      }
      // !.. command, get it from history.
      else if (m_Command.StartsWith("!"))
      {
        if (SetCommandFromHistory(m_Command.substr(1)))
        {
          AppendText(GetEOL() + m_Command);

          // We don't keep the command, so commands are not rearranged and
          // repeatingly calling !5 always gives the same command, just as bash does.

          wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, ID_SHELL_COMMAND);
          event.SetString(m_Command);
          wxPostEvent(m_Handler, event);
        }
        else
        {
          Prompt(GetEOL() + m_Command + ": " + _("event not found"));
        }
      }
      // Other command, send to parent.
      else
      {
        KeepCommand();
        wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, ID_SHELL_COMMAND);
        event.SetString(m_Command);
        wxPostEvent(m_Handler, event);
      }

      m_Command.clear();
    }

    m_CommandsIterator = m_Commands.end();
  }
  else if (key == WXK_BACK)
  {
    // Delete the key at current position (-1  because of WXK_BACK).
    const int index = GetCurrentPos() - m_CommandStartPosition - 1;

    if (index >= 0 && index < m_Command.length() && m_Command.length() > 0)
    {
      m_Command.erase(index, 1);
    }
  }
  else if (key == WXK_TAB)
  {
    Expand();
  }
  else
  {
    // Insert the key at current position.
    const int index = GetCurrentPos() - m_CommandStartPosition;

    if (index >= 0 && index < m_Command.size())
    {
      m_Command.insert(index, wxChar(key));
    }
    else
    {
      m_Command += wxChar(key);
    }
  }
}

bool wxExSTCShell::Prompt(const wxString& text, bool add_eol)
{
  if (!m_Enabled)
  {
    return false;
  }

  if (!text.empty())
  {
    AppendText(text);
  }

  if (!m_Prompt.empty())
  {
    if (GetTextLength() > 0 && add_eol)
    {
      AppendText(GetEOL());
    }

    AppendText(m_Prompt);
  }

  DocumentEnd();

  m_CommandStartPosition = GetCurrentPos();

  EmptyUndoBuffer();

  return true;
}

bool wxExSTCShell::SetCommandFromHistory(const wxString& short_command)
{
  const int no_asked_for = atoi(short_command.c_str());

  if (no_asked_for > 0)
  {
    int no = 1;

    for (
#ifdef wxExUSE_CPP0X
      auto it = m_Commands.begin();
#else
      std::list < wxString >::iterator it = m_Commands.begin();
#endif
      it != m_Commands.end();
      ++it)
    {
      if (no == no_asked_for)
      {
        m_Command = *it;
        return true;
      }

      no++;
    }
  }
  else
  {
    wxString short_command_check;

    if (m_CommandEnd == GetEOL())
    {
      short_command_check = short_command;
    }
    else
    {
      short_command_check =
        short_command.substr(
          0,
          short_command.length() - m_CommandEnd.length());
    }

    for (
#ifdef wxExUSE_CPP0X
      auto it = m_Commands.rbegin();
#else
      std::list < wxString >::reverse_iterator it = m_Commands.rbegin();
#endif
      it != m_Commands.rend();
      ++it)
    {
      const wxString command = *it;

      if (command.StartsWith(short_command_check))
      {
        m_Command = command;
        return true;
      }
    }
  }

  return false;
}

bool wxExSTCShell::SetPrompt(const wxString& prompt, bool do_prompt)
{
  if (!m_Enabled)
  {
    return false;
  }

  m_Prompt = prompt;

  if (do_prompt)
  {
    Prompt();
  }

  return true;
}

void wxExSTCShell::ShowCommand(int key)
{
  SetTargetStart(m_CommandStartPosition);
  SetTargetEnd(GetTextLength());

  if (key == WXK_UP)
  {
    if (m_CommandsIterator != m_Commands.begin())
    {
      m_CommandsIterator--;
    }
  }
  else
  {
    if (m_CommandsIterator != m_Commands.end())
    {
      m_CommandsIterator++;
    }
  }

  if (m_CommandsIterator != m_Commands.end())
  {
    m_Command = *m_CommandsIterator;
    ReplaceTarget(m_Command);
  }

  DocumentEnd();
}

void wxExSTCShell::ShowHistory()
{
  int command_no = 1;

  for (
#ifdef wxExUSE_CPP0X
    auto it = m_Commands.begin();
#else
    std::list < wxString >::iterator it = m_Commands.begin();
#endif
    it != m_Commands.end();
    ++it)
  {
    const wxString command = *it;

    AppendText(wxString::Format("\n%d %s",
      command_no++,
      command.c_str()));
  }
}

#endif // wxUSE_GUI
<commit_msg>fixed using backspace after tab expand<commit_after>////////////////////////////////////////////////////////////////////////////////
// Name:      shell.cpp
// Purpose:   Implementation of class wxExSTCShell
// Author:    Anton van Wezenbeek
// Copyright: (c) 2012 Anton van Wezenbeek
////////////////////////////////////////////////////////////////////////////////

#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/wx.h>
#endif
#include <numeric>
#include <functional>
#include <wx/config.h>
#include <wx/tokenzr.h>
#include <wx/extension/shell.h>
#include <wx/extension/defs.h> // for ID_SHELL_COMMAND
#include <wx/extension/util.h>

#if wxUSE_GUI

BEGIN_EVENT_TABLE(wxExSTCShell, wxExSTC)
  EVT_CHAR(wxExSTCShell::OnChar)
  EVT_KEY_DOWN(wxExSTCShell::OnKey)
  EVT_STC_CHARADDED(wxID_ANY, wxExSTCShell::OnStyledText)
END_EVENT_TABLE()

wxExSTCShell::wxExSTCShell(
  wxWindow* parent,
  const wxString& prompt,
  const wxString& command_end,
  bool echo,
  int commands_save_in_config,
  const wxString& lexer,
  long menu_flags,
  wxWindowID id,
  const wxPoint& pos,
  const wxSize& size,
  long style)
  : wxExSTC(
      parent,
      wxEmptyString,
      STC_WIN_NO_INDICATOR,
      wxEmptyString, // title
      menu_flags,
      id,
      pos,
      size,
      style)
  , m_Command(wxEmptyString)
  , m_CommandEnd((command_end == wxEmptyString ? GetEOL(): command_end))
  , m_CommandStartPosition(0)
  , m_Echo(echo)
  // take a char that is not likely to appear inside commands
  , m_CommandsInConfigDelimiter(wxUniChar(0x03))
  , m_CommandsSaveInConfig(commands_save_in_config)
  , m_Prompt(prompt)
  , m_Handler(parent)
  , m_Enabled(true)
{
  // Override defaults from config.
  SetEdgeMode(wxSTC_EDGE_NONE);
  ResetMargins(false); // do not reset divider margin
  SetName("SHELL");

  // Start with a prompt.
  Prompt();

  if (m_CommandsSaveInConfig > 0)
  {
    // Get all previous commands.
    wxStringTokenizer tkz(wxConfigBase::Get()->Read("Shell"),
      m_CommandsInConfigDelimiter);

    while (tkz.HasMoreTokens())
    {
      const wxString val = tkz.GetNextToken();
      m_Commands.push_front(val);
    }
  }

  // Take care that m_CommandsIterator is valid.
  m_CommandsIterator = m_Commands.end();

  EnableShell(true);

  SetLexer(lexer);
}

wxExSTCShell::~wxExSTCShell()
{
  if (m_CommandsSaveInConfig > 0)
  {
    wxString values;
    int items = 0;

    for (
#ifdef wxExUSE_CPP0X
      auto it = m_Commands.rbegin();
#else
      std::list < wxString >::reverse_iterator it = m_Commands.rbegin();
#endif
      it != m_Commands.rend() && items < m_CommandsSaveInConfig;
      ++it)
    {
      values += *it + m_CommandsInConfigDelimiter;
      items++;
    }

    wxConfigBase::Get()->Write("Shell", values);
  }
}

void wxExSTCShell::AddText(const wxString& text)
{
  wxExSTC::AddText(text);
  EnsureCaretVisible();
  m_CommandStartPosition = GetCurrentPos();
}

void wxExSTCShell::EnableShell(bool enabled)
{
  m_Enabled = enabled;

  if (!m_Enabled)
  {
    // A disabled shell follows STC vi mode.
    GetVi().Use(wxConfigBase::Get()->ReadBool(_("vi mode"), true));
  }
  else
  {
    // An enabled shell does not use vi mode.
    GetVi().Use(false);
  }
}

void wxExSTCShell::Expand()
{
  wxDir dir(wxGetCwd());
  wxString filename;
  const wxString word(m_Command.AfterLast(' '));

  if (dir.GetFirst(&filename, word + "*"))
  {
    const wxString expansion = filename.Mid(word.length());

    // We cannot use our AddText, as command start pos
    // should not be changed.
    wxExSTC::AddText(expansion);
    m_Command += expansion;
  }
}

const wxString wxExSTCShell::GetCommand() const
{
  if (!m_Commands.empty())
  {
    return m_Commands.back();
  }
  else
  {
    return wxEmptyString;
  }
}

const wxString wxExSTCShell::GetHistory() const
{
  return accumulate(m_Commands.begin(), m_Commands.end(), wxString());
}

void wxExSTCShell::KeepCommand()
{
  m_Commands.remove(m_Command);
  m_Commands.push_back(m_Command);
}

// No longer used, for the moment.
void wxExSTCShell::OnCommand(wxCommandEvent& command)
{
  if (!m_Enabled)
  {
    command.Skip();
    return;
  }

  switch (command.GetId())
  {
    default:
      wxFAIL;
      break;
  }
}

void wxExSTCShell::OnChar(wxKeyEvent& event)
{
  if (m_Enabled)
  {
    ProcessChar(event.GetKeyCode());
  }

  if (m_Echo)
  {
    event.Skip();
  }
}

void wxExSTCShell::OnKey(wxKeyEvent& event)
{
  if (!m_Enabled)
  {
    event.Skip();
    return;
  }

  const int key = event.GetKeyCode();

  if (key == WXK_RETURN || key == WXK_TAB)
  {
    ProcessChar(key);
  }
  // Up or down key pressed, and at the end of document.
  else if ((key == WXK_UP || key == WXK_DOWN) &&
            GetCurrentPos() == GetTextLength())
  {
    ShowCommand(key);
  }
  // Home key pressed.
  else if (key == WXK_HOME)
  {
    Home();

    const wxString line = GetLine(GetCurrentLine());

    if (line.StartsWith(m_Prompt))
    {
      GotoPos(GetCurrentPos() + m_Prompt.length());
    }
  }
  // Ctrl-Q pressed, used to stop processing.
  // Ctrl-C pressed and no text selected (otherwise copy), also used to stop processing.
  else if (
    event.GetModifiers() == wxMOD_CONTROL &&
   ( key == 'Q' ||
    (key == 'C' && GetSelectedText().empty())))
  {
    wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, ID_SHELL_COMMAND_STOP);
    wxPostEvent(m_Handler, event);
  }
  // Ctrl-V pressed, used for pasting as well.
  else if (event.GetModifiers() == wxMOD_CONTROL && key == 'V')
  {
    Paste();
  }
  // Backspace or delete key pressed.
  else if (key == WXK_BACK || key == WXK_DELETE)
  {
    if (GetCurrentPos() <= m_CommandStartPosition)
    {
      // Ignore, so do nothing.
    }
    else
    {
      // Allow.
      ProcessChar(key);
      if (m_Echo) event.Skip();
    }
  }
  // The rest.
  else
  {
    // If we enter regular text and not already building a command, first goto end.
    if (event.GetModifiers() == wxMOD_NONE &&
        key < WXK_START &&
        GetCurrentPos() < m_CommandStartPosition)
    {
      DocumentEnd();
    }

    m_CommandsIterator = m_Commands.end();

    if (m_Echo) event.Skip();
  }
}

void wxExSTCShell::OnStyledText(wxStyledTextEvent& event)
{
  if (!m_Enabled)
  {
    event.Skip();
    return;
  }

  // do nothing, keep event from sent to wxExSTC.
}

void wxExSTCShell::Paste()
{
  if (!CanPaste())
  {
    return;
  }

  // Take care that we cannot paste somewhere inside.
  if (GetCurrentPos() < m_CommandStartPosition)
  {
    DocumentEnd();
  }

  wxExSTC::Paste();

  m_Command += wxExClipboardGet();
}

void wxExSTCShell::ProcessChar(int key)
{
  // No need to check m_Enabled, already done by calling this method.

  if (key == WXK_RETURN)
  {
    if (m_Command.empty())
    {
      wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, ID_SHELL_COMMAND);
      event.SetString(m_Command);
      wxPostEvent(m_Handler, event);
    }
    else if (
      m_CommandEnd == GetEOL() ||
      m_Command.EndsWith(m_CommandEnd))
    {
      // We have a command.
      EmptyUndoBuffer();

      // History command.
      if (m_Command == wxString("history") +
         (m_CommandEnd == GetEOL() ? wxString(wxEmptyString): m_CommandEnd))
      {
        KeepCommand();
        ShowHistory();
        Prompt();
      }
      // !.. command, get it from history.
      else if (m_Command.StartsWith("!"))
      {
        if (SetCommandFromHistory(m_Command.substr(1)))
        {
          AppendText(GetEOL() + m_Command);

          // We don't keep the command, so commands are not rearranged and
          // repeatingly calling !5 always gives the same command, just as bash does.

          wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, ID_SHELL_COMMAND);
          event.SetString(m_Command);
          wxPostEvent(m_Handler, event);
        }
        else
        {
          Prompt(GetEOL() + m_Command + ": " + _("event not found"));
        }
      }
      // Other command, send to parent.
      else
      {
        KeepCommand();
        wxCommandEvent event(wxEVT_COMMAND_MENU_SELECTED, ID_SHELL_COMMAND);
        event.SetString(m_Command);
        wxPostEvent(m_Handler, event);
      }

      m_Command.clear();
    }

    m_CommandsIterator = m_Commands.end();
  }
  else if (key == WXK_BACK)
  {
    // Delete the key at current position (-1  because of WXK_BACK).
    const int index = GetCurrentPos() - m_CommandStartPosition - 1;

    if (index >= 0 && index < m_Command.length() && m_Command.length() > 0)
    {
      m_Command.erase(index, 1);
    }
  }
  else if (key == WXK_TAB)
  {
    Expand();
  }
  else
  {
    // Insert the key at current position.
    const int index = GetCurrentPos() - m_CommandStartPosition;

    if (index >= 0 && index < m_Command.size())
    {
      m_Command.insert(index, wxChar(key));
    }
    else
    {
      m_Command += wxChar(key);
    }
  }
}

bool wxExSTCShell::Prompt(const wxString& text, bool add_eol)
{
  if (!m_Enabled)
  {
    return false;
  }

  if (!text.empty())
  {
    AppendText(text);
  }

  if (!m_Prompt.empty())
  {
    if (GetTextLength() > 0 && add_eol)
    {
      AppendText(GetEOL());
    }

    AppendText(m_Prompt);
  }

  DocumentEnd();

  m_CommandStartPosition = GetCurrentPos();

  EmptyUndoBuffer();

  return true;
}

bool wxExSTCShell::SetCommandFromHistory(const wxString& short_command)
{
  const int no_asked_for = atoi(short_command.c_str());

  if (no_asked_for > 0)
  {
    int no = 1;

    for (
#ifdef wxExUSE_CPP0X
      auto it = m_Commands.begin();
#else
      std::list < wxString >::iterator it = m_Commands.begin();
#endif
      it != m_Commands.end();
      ++it)
    {
      if (no == no_asked_for)
      {
        m_Command = *it;
        return true;
      }

      no++;
    }
  }
  else
  {
    wxString short_command_check;

    if (m_CommandEnd == GetEOL())
    {
      short_command_check = short_command;
    }
    else
    {
      short_command_check =
        short_command.substr(
          0,
          short_command.length() - m_CommandEnd.length());
    }

    for (
#ifdef wxExUSE_CPP0X
      auto it = m_Commands.rbegin();
#else
      std::list < wxString >::reverse_iterator it = m_Commands.rbegin();
#endif
      it != m_Commands.rend();
      ++it)
    {
      const wxString command = *it;

      if (command.StartsWith(short_command_check))
      {
        m_Command = command;
        return true;
      }
    }
  }

  return false;
}

bool wxExSTCShell::SetPrompt(const wxString& prompt, bool do_prompt)
{
  if (!m_Enabled)
  {
    return false;
  }

  m_Prompt = prompt;

  if (do_prompt)
  {
    Prompt();
  }

  return true;
}

void wxExSTCShell::ShowCommand(int key)
{
  SetTargetStart(m_CommandStartPosition);
  SetTargetEnd(GetTextLength());

  if (key == WXK_UP)
  {
    if (m_CommandsIterator != m_Commands.begin())
    {
      m_CommandsIterator--;
    }
  }
  else
  {
    if (m_CommandsIterator != m_Commands.end())
    {
      m_CommandsIterator++;
    }
  }

  if (m_CommandsIterator != m_Commands.end())
  {
    m_Command = *m_CommandsIterator;
    ReplaceTarget(m_Command);
  }

  DocumentEnd();
}

void wxExSTCShell::ShowHistory()
{
  int command_no = 1;

  for (
#ifdef wxExUSE_CPP0X
    auto it = m_Commands.begin();
#else
    std::list < wxString >::iterator it = m_Commands.begin();
#endif
    it != m_Commands.end();
    ++it)
  {
    const wxString command = *it;

    AppendText(wxString::Format("\n%d %s",
      command_no++,
      command.c_str()));
  }
}

#endif // wxUSE_GUI
<|endoftext|>


<commit_before>// Copyright (c) 2015 Felix Rieseberg <feriese@microsoft.com> and Jason Poon <jason.poon@microsoft.com>. All rights reserved.
// Copyright (c) 2015 Ryan McShane <rmcshane@bandwidth.com> and Brandon Smith <bsmith@bandwidth.com>
// Thanks to both of those folks mentioned above who first thought up a bunch of this code
// and released it as MIT to the world.

#include "browser/win/windows_toast_notification.h"

#include <shlobj.h>

#include "base/strings/utf_string_conversions.h"
#include "browser/notification_delegate.h"
#include "browser/win/scoped_hstring.h"
#include "browser/win/notification_presenter_win.h"
#include "common/application_info.h"

using namespace ABI::Windows::Data::Xml::Dom;

namespace brightray {

namespace {

bool GetAppUserModelId(ScopedHString* app_id) {
  PWSTR current_app_id;
  if (SUCCEEDED(GetCurrentProcessExplicitAppUserModelID(&current_app_id))) {
    app_id->Reset(current_app_id);
    CoTaskMemFree(current_app_id);
  } else {
    app_id->Reset(base::UTF8ToUTF16(GetApplicationName()));
  }
  return app_id->success();
}

}  // namespace

// static
Notification* Notification::Create(NotificationDelegate* delegate,
                                   NotificationPresenter* presenter) {
  return new WindowsToastNotification(delegate, presenter);
}

// static
ComPtr<ABI::Windows::UI::Notifications::IToastNotificationManagerStatics>
    WindowsToastNotification::toast_manager_;

// static
ComPtr<ABI::Windows::UI::Notifications::IToastNotifier>
    WindowsToastNotification::toast_notifier_;

// static
bool WindowsToastNotification::Initialize() {
  // Just initialize, don't care if it fails or already initialized.
  Windows::Foundation::Initialize(RO_INIT_MULTITHREADED);

  ScopedHString toast_manager_str(
      RuntimeClass_Windows_UI_Notifications_ToastNotificationManager);
  if (!toast_manager_str.success())
    return false;
  if (FAILED(Windows::Foundation::GetActivationFactory(toast_manager_str,
                                                       &toast_manager_)))
    return false;

  ScopedHString app_id;
  if (!GetAppUserModelId(&app_id))
    return false;

  return SUCCEEDED(
      toast_manager_->CreateToastNotifierWithId(app_id, &toast_notifier_));
}

WindowsToastNotification::WindowsToastNotification(
    NotificationDelegate* delegate,
    NotificationPresenter* presenter)
    : Notification(delegate, presenter) {
}

WindowsToastNotification::~WindowsToastNotification() {
  // Remove the notification on exit.
  if (toast_notification_) {
    RemoveCallbacks(toast_notification_.Get());
    Dismiss();
  }
}

void WindowsToastNotification::Show(
    const base::string16& title,
    const base::string16& msg,
    const std::string& tag,
    const GURL& icon_url,
    const SkBitmap& icon,
    const bool silent) {
  auto presenter_win = static_cast<NotificationPresenterWin*>(presenter());
  std::wstring icon_path = presenter_win->SaveIconToFilesystem(icon, icon_url);

  ComPtr<IXmlDocument> toast_xml;
  if(FAILED(GetToastXml(toast_manager_.Get(), title, msg, icon_path, silent, &toast_xml))) {
    NotificationFailed();
    return;
  }

  ScopedHString toast_str(
      RuntimeClass_Windows_UI_Notifications_ToastNotification);
  if (!toast_str.success()) {
    NotificationFailed();
    return;
  }

  ComPtr<ABI::Windows::UI::Notifications::IToastNotificationFactory> toast_factory;
  if (FAILED(Windows::Foundation::GetActivationFactory(toast_str,
                                                       &toast_factory))) {
    NotificationFailed();
    return;
  }

  if (FAILED(toast_factory->CreateToastNotification(toast_xml.Get(),
                                                    &toast_notification_))) {
    NotificationFailed();
    return;
  }

  if (!SetupCallbacks(toast_notification_.Get())) {
    NotificationFailed();
    return;
  }

  if (FAILED(toast_notifier_->Show(toast_notification_.Get()))) {
    NotificationFailed();
    return;
  }

  delegate()->NotificationDisplayed();
}

void WindowsToastNotification::Dismiss() {
  toast_notifier_->Hide(toast_notification_.Get());
}

bool WindowsToastNotification::GetToastXml(
    ABI::Windows::UI::Notifications::IToastNotificationManagerStatics* toastManager,
    const std::wstring& title,
    const std::wstring& msg,
    const std::wstring& icon_path,
    const bool silent,
    IXmlDocument** toast_xml) {
  ABI::Windows::UI::Notifications::ToastTemplateType template_type;
  if (title.empty() || msg.empty()) {
    // Single line toast.
    template_type = icon_path.empty() ? ABI::Windows::UI::Notifications::ToastTemplateType_ToastText01 :
                                        ABI::Windows::UI::Notifications::ToastTemplateType_ToastImageAndText01;
    if (FAILED(toast_manager_->GetTemplateContent(template_type, toast_xml)))
      return false;
    if (!SetXmlText(*toast_xml, title.empty() ? msg : title))
      return false;
  } else {
    // Title and body toast.
    template_type = icon_path.empty() ? ABI::Windows::UI::Notifications::ToastTemplateType_ToastText02 :
                                        ABI::Windows::UI::Notifications::ToastTemplateType_ToastImageAndText02;
    if (FAILED(toastManager->GetTemplateContent(template_type, toast_xml)))
      return false;
    if (!SetXmlText(*toast_xml, title, msg))
      return false;
  }

  // Configure the toast's notification sound
  if (silent) {
    if (FAILED(SetXmlAudioSilent(*toast_xml)))
      return false;
  }

  // Configure the toast's image
  if (!icon_path.empty())
    return SetXmlImage(*toast_xml, icon_path);

  return true;
}

bool WindowsToastNotification::SetXmlAudioSilent(
  IXmlDocument* doc) {
    ScopedHString tag(L"toast");
  if (!tag.success())
    return false;

  ComPtr<IXmlNodeList> node_list;
  if (FAILED(doc->GetElementsByTagName(tag, &node_list)))
    return false;

  ComPtr<IXmlNode> root;
  if (FAILED(node_list->Item(0, &root)))
    return false;

  ComPtr<IXmlElement> audio_element;
  ScopedHString audio_str(L"audio");
  if (FAILED(doc->CreateElement(audio_str, &audio_element)))
    return false;

  ComPtr<IXmlNode> audio_node_tmp;
  if (FAILED(audio_element.As(&audio_node_tmp)))
    return false;

  // Append audio node to toast xml
  ComPtr<IXmlNode> audio_node;
  if (FAILED(root->AppendChild(audio_node_tmp.Get(), &audio_node)))
    return false;

  // Create silent attribute
  ComPtr<IXmlNamedNodeMap> attributes;
  if (FAILED(audio_node->get_Attributes(&attributes)))
    return false;

  ComPtr<IXmlAttribute> silent_attribute;
  ScopedHString silent_str(L"silent");
  if (FAILED(doc->CreateAttribute(silent_str, &silent_attribute)))
    return false;

  ComPtr<IXmlNode> silent_attribute_node;
  if (FAILED(silent_attribute.As(&silent_attribute_node)))
    return false;

  // Set silent attribute to true
  ScopedHString silent_value(L"true");
  if (!silent_value.success())
    return false;

  ComPtr<IXmlText> silent_text;
  if (FAILED(doc->CreateTextNode(silent_value, &silent_text)))
    return false;

  ComPtr<IXmlNode> silent_node;
  if (FAILED(silent_text.As(&silent_node)))
    return false;

  ComPtr<IXmlNode> child_node;
  if (FAILED(silent_attribute_node->AppendChild(silent_node.Get(), &child_node)))
    return false;

  ComPtr<IXmlNode> silent_attribute_pnode;
  return SUCCEEDED(attributes.Get()->SetNamedItem(silent_attribute_node.Get(), &silent_attribute_pnode));
}

bool WindowsToastNotification::SetXmlText(
    IXmlDocument* doc, const std::wstring& text) {
  ScopedHString tag;
  ComPtr<IXmlNodeList> node_list;
  if (!GetTextNodeList(&tag, doc, &node_list, 1))
    return false;

  ComPtr<IXmlNode> node;
  if (FAILED(node_list->Item(0, &node)))
    return false;

  return AppendTextToXml(doc, node.Get(), text);
}

bool WindowsToastNotification::SetXmlText(
    IXmlDocument* doc, const std::wstring& title, const std::wstring& body) {
  ScopedHString tag;
  ComPtr<IXmlNodeList> node_list;
  if (!GetTextNodeList(&tag, doc, &node_list, 2))
    return false;

  ComPtr<IXmlNode> node;
  if (FAILED(node_list->Item(0, &node)))
    return false;

  if (!AppendTextToXml(doc, node.Get(), title))
    return false;

  if (FAILED(node_list->Item(1, &node)))
    return false;

  return AppendTextToXml(doc, node.Get(), body);
}

bool WindowsToastNotification::SetXmlImage(
    IXmlDocument* doc, const std::wstring& icon_path) {
  ScopedHString tag(L"image");
  if (!tag.success())
    return false;

  ComPtr<IXmlNodeList> node_list;
  if (FAILED(doc->GetElementsByTagName(tag, &node_list)))
    return false;

  ComPtr<IXmlNode> image_node;
  if (FAILED(node_list->Item(0, &image_node)))
    return false;

  ComPtr<IXmlNamedNodeMap> attrs;
  if (FAILED(image_node->get_Attributes(&attrs)))
    return false;

  ScopedHString src(L"src");
  if (!src.success())
    return false;

  ComPtr<IXmlNode> src_attr;
  if (FAILED(attrs->GetNamedItem(src, &src_attr)))
    return false;

  ScopedHString img_path(icon_path.c_str());
  if (!img_path.success())
    return false;

  ComPtr<IXmlText> src_text;
  if (FAILED(doc->CreateTextNode(img_path, &src_text)))
    return false;

  ComPtr<IXmlNode> src_node;
  if (FAILED(src_text.As(&src_node)))
    return false;

  ComPtr<IXmlNode> child_node;
  return SUCCEEDED(src_attr->AppendChild(src_node.Get(), &child_node));
}

bool WindowsToastNotification::GetTextNodeList(
    ScopedHString* tag,
    IXmlDocument* doc,
    IXmlNodeList** node_list,
    uint32_t req_length) {
  tag->Reset(L"text");
  if (!tag->success())
    return false;

  if (FAILED(doc->GetElementsByTagName(*tag, node_list)))
    return false;

  uint32_t node_length;
  if (FAILED((*node_list)->get_Length(&node_length)))
    return false;

  return node_length >= req_length;
}

bool WindowsToastNotification::AppendTextToXml(
    IXmlDocument* doc, IXmlNode* node, const std::wstring& text) {
  ScopedHString str(text);
  if (!str.success())
    return false;

  ComPtr<IXmlText> xml_text;
  if (FAILED(doc->CreateTextNode(str, &xml_text)))
    return false;

  ComPtr<IXmlNode> text_node;
  if (FAILED(xml_text.As(&text_node)))
    return false;

  ComPtr<IXmlNode> append_node;
  return SUCCEEDED(node->AppendChild(text_node.Get(), &append_node));
}

bool WindowsToastNotification::SetupCallbacks(
    ABI::Windows::UI::Notifications::IToastNotification* toast) {
  event_handler_ = Make<ToastEventHandler>(this);
  if (FAILED(toast->add_Activated(event_handler_.Get(), &activated_token_)))
    return false;

  if (FAILED(toast->add_Dismissed(event_handler_.Get(), &dismissed_token_)))
    return false;

  return SUCCEEDED(toast->add_Failed(event_handler_.Get(), &failed_token_));
}

bool WindowsToastNotification::RemoveCallbacks(
    ABI::Windows::UI::Notifications::IToastNotification* toast) {
  if (FAILED(toast->remove_Activated(activated_token_)))
    return false;

  if (FAILED(toast->remove_Dismissed(dismissed_token_)))
    return false;

  return SUCCEEDED(toast->remove_Failed(failed_token_));
}

/*
/ Toast Event Handler
*/
ToastEventHandler::ToastEventHandler(Notification* notification)
    : notification_(notification->GetWeakPtr()) {
}

ToastEventHandler::~ToastEventHandler() {
}

IFACEMETHODIMP ToastEventHandler::Invoke(
    ABI::Windows::UI::Notifications::IToastNotification* sender, IInspectable* args) {
  notification_->NotificationClicked();
  return S_OK;
}

IFACEMETHODIMP ToastEventHandler::Invoke(
    ABI::Windows::UI::Notifications::IToastNotification* sender,
    ABI::Windows::UI::Notifications::IToastDismissedEventArgs* e) {
  notification_->NotificationDismissed();
  return S_OK;
}

IFACEMETHODIMP ToastEventHandler::Invoke(
    ABI::Windows::UI::Notifications::IToastNotification* sender,
    ABI::Windows::UI::Notifications::IToastFailedEventArgs* e) {
  notification_->NotificationFailed();
  return S_OK;
}

}  // namespace brightray
<commit_msg>Delay notification events to next tick<commit_after>// Copyright (c) 2015 Felix Rieseberg <feriese@microsoft.com> and Jason Poon <jason.poon@microsoft.com>. All rights reserved.
// Copyright (c) 2015 Ryan McShane <rmcshane@bandwidth.com> and Brandon Smith <bsmith@bandwidth.com>
// Thanks to both of those folks mentioned above who first thought up a bunch of this code
// and released it as MIT to the world.

#include "browser/win/windows_toast_notification.h"

#include <shlobj.h>

#include "base/strings/utf_string_conversions.h"
#include "browser/notification_delegate.h"
#include "browser/win/scoped_hstring.h"
#include "browser/win/notification_presenter_win.h"
#include "common/application_info.h"
#include "content/public/browser/browser_thread.h"

using namespace ABI::Windows::Data::Xml::Dom;

namespace brightray {

namespace {

bool GetAppUserModelId(ScopedHString* app_id) {
  PWSTR current_app_id;
  if (SUCCEEDED(GetCurrentProcessExplicitAppUserModelID(&current_app_id))) {
    app_id->Reset(current_app_id);
    CoTaskMemFree(current_app_id);
  } else {
    app_id->Reset(base::UTF8ToUTF16(GetApplicationName()));
  }
  return app_id->success();
}

}  // namespace

// static
Notification* Notification::Create(NotificationDelegate* delegate,
                                   NotificationPresenter* presenter) {
  return new WindowsToastNotification(delegate, presenter);
}

// static
ComPtr<ABI::Windows::UI::Notifications::IToastNotificationManagerStatics>
    WindowsToastNotification::toast_manager_;

// static
ComPtr<ABI::Windows::UI::Notifications::IToastNotifier>
    WindowsToastNotification::toast_notifier_;

// static
bool WindowsToastNotification::Initialize() {
  // Just initialize, don't care if it fails or already initialized.
  Windows::Foundation::Initialize(RO_INIT_MULTITHREADED);

  ScopedHString toast_manager_str(
      RuntimeClass_Windows_UI_Notifications_ToastNotificationManager);
  if (!toast_manager_str.success())
    return false;
  if (FAILED(Windows::Foundation::GetActivationFactory(toast_manager_str,
                                                       &toast_manager_)))
    return false;

  ScopedHString app_id;
  if (!GetAppUserModelId(&app_id))
    return false;

  return SUCCEEDED(
      toast_manager_->CreateToastNotifierWithId(app_id, &toast_notifier_));
}

WindowsToastNotification::WindowsToastNotification(
    NotificationDelegate* delegate,
    NotificationPresenter* presenter)
    : Notification(delegate, presenter) {
}

WindowsToastNotification::~WindowsToastNotification() {
  // Remove the notification on exit.
  if (toast_notification_) {
    RemoveCallbacks(toast_notification_.Get());
    Dismiss();
  }
}

void WindowsToastNotification::Show(
    const base::string16& title,
    const base::string16& msg,
    const std::string& tag,
    const GURL& icon_url,
    const SkBitmap& icon,
    const bool silent) {
  auto presenter_win = static_cast<NotificationPresenterWin*>(presenter());
  std::wstring icon_path = presenter_win->SaveIconToFilesystem(icon, icon_url);

  ComPtr<IXmlDocument> toast_xml;
  if(FAILED(GetToastXml(toast_manager_.Get(), title, msg, icon_path, silent, &toast_xml))) {
    NotificationFailed();
    return;
  }

  ScopedHString toast_str(
      RuntimeClass_Windows_UI_Notifications_ToastNotification);
  if (!toast_str.success()) {
    NotificationFailed();
    return;
  }

  ComPtr<ABI::Windows::UI::Notifications::IToastNotificationFactory> toast_factory;
  if (FAILED(Windows::Foundation::GetActivationFactory(toast_str,
                                                       &toast_factory))) {
    NotificationFailed();
    return;
  }

  if (FAILED(toast_factory->CreateToastNotification(toast_xml.Get(),
                                                    &toast_notification_))) {
    NotificationFailed();
    return;
  }

  if (!SetupCallbacks(toast_notification_.Get())) {
    NotificationFailed();
    return;
  }

  if (FAILED(toast_notifier_->Show(toast_notification_.Get()))) {
    NotificationFailed();
    return;
  }

  delegate()->NotificationDisplayed();
}

void WindowsToastNotification::Dismiss() {
  toast_notifier_->Hide(toast_notification_.Get());
}

bool WindowsToastNotification::GetToastXml(
    ABI::Windows::UI::Notifications::IToastNotificationManagerStatics* toastManager,
    const std::wstring& title,
    const std::wstring& msg,
    const std::wstring& icon_path,
    const bool silent,
    IXmlDocument** toast_xml) {
  ABI::Windows::UI::Notifications::ToastTemplateType template_type;
  if (title.empty() || msg.empty()) {
    // Single line toast.
    template_type = icon_path.empty() ? ABI::Windows::UI::Notifications::ToastTemplateType_ToastText01 :
                                        ABI::Windows::UI::Notifications::ToastTemplateType_ToastImageAndText01;
    if (FAILED(toast_manager_->GetTemplateContent(template_type, toast_xml)))
      return false;
    if (!SetXmlText(*toast_xml, title.empty() ? msg : title))
      return false;
  } else {
    // Title and body toast.
    template_type = icon_path.empty() ? ABI::Windows::UI::Notifications::ToastTemplateType_ToastText02 :
                                        ABI::Windows::UI::Notifications::ToastTemplateType_ToastImageAndText02;
    if (FAILED(toastManager->GetTemplateContent(template_type, toast_xml)))
      return false;
    if (!SetXmlText(*toast_xml, title, msg))
      return false;
  }

  // Configure the toast's notification sound
  if (silent) {
    if (FAILED(SetXmlAudioSilent(*toast_xml)))
      return false;
  }

  // Configure the toast's image
  if (!icon_path.empty())
    return SetXmlImage(*toast_xml, icon_path);

  return true;
}

bool WindowsToastNotification::SetXmlAudioSilent(
  IXmlDocument* doc) {
    ScopedHString tag(L"toast");
  if (!tag.success())
    return false;

  ComPtr<IXmlNodeList> node_list;
  if (FAILED(doc->GetElementsByTagName(tag, &node_list)))
    return false;

  ComPtr<IXmlNode> root;
  if (FAILED(node_list->Item(0, &root)))
    return false;

  ComPtr<IXmlElement> audio_element;
  ScopedHString audio_str(L"audio");
  if (FAILED(doc->CreateElement(audio_str, &audio_element)))
    return false;

  ComPtr<IXmlNode> audio_node_tmp;
  if (FAILED(audio_element.As(&audio_node_tmp)))
    return false;

  // Append audio node to toast xml
  ComPtr<IXmlNode> audio_node;
  if (FAILED(root->AppendChild(audio_node_tmp.Get(), &audio_node)))
    return false;

  // Create silent attribute
  ComPtr<IXmlNamedNodeMap> attributes;
  if (FAILED(audio_node->get_Attributes(&attributes)))
    return false;

  ComPtr<IXmlAttribute> silent_attribute;
  ScopedHString silent_str(L"silent");
  if (FAILED(doc->CreateAttribute(silent_str, &silent_attribute)))
    return false;

  ComPtr<IXmlNode> silent_attribute_node;
  if (FAILED(silent_attribute.As(&silent_attribute_node)))
    return false;

  // Set silent attribute to true
  ScopedHString silent_value(L"true");
  if (!silent_value.success())
    return false;

  ComPtr<IXmlText> silent_text;
  if (FAILED(doc->CreateTextNode(silent_value, &silent_text)))
    return false;

  ComPtr<IXmlNode> silent_node;
  if (FAILED(silent_text.As(&silent_node)))
    return false;

  ComPtr<IXmlNode> child_node;
  if (FAILED(silent_attribute_node->AppendChild(silent_node.Get(), &child_node)))
    return false;

  ComPtr<IXmlNode> silent_attribute_pnode;
  return SUCCEEDED(attributes.Get()->SetNamedItem(silent_attribute_node.Get(), &silent_attribute_pnode));
}

bool WindowsToastNotification::SetXmlText(
    IXmlDocument* doc, const std::wstring& text) {
  ScopedHString tag;
  ComPtr<IXmlNodeList> node_list;
  if (!GetTextNodeList(&tag, doc, &node_list, 1))
    return false;

  ComPtr<IXmlNode> node;
  if (FAILED(node_list->Item(0, &node)))
    return false;

  return AppendTextToXml(doc, node.Get(), text);
}

bool WindowsToastNotification::SetXmlText(
    IXmlDocument* doc, const std::wstring& title, const std::wstring& body) {
  ScopedHString tag;
  ComPtr<IXmlNodeList> node_list;
  if (!GetTextNodeList(&tag, doc, &node_list, 2))
    return false;

  ComPtr<IXmlNode> node;
  if (FAILED(node_list->Item(0, &node)))
    return false;

  if (!AppendTextToXml(doc, node.Get(), title))
    return false;

  if (FAILED(node_list->Item(1, &node)))
    return false;

  return AppendTextToXml(doc, node.Get(), body);
}

bool WindowsToastNotification::SetXmlImage(
    IXmlDocument* doc, const std::wstring& icon_path) {
  ScopedHString tag(L"image");
  if (!tag.success())
    return false;

  ComPtr<IXmlNodeList> node_list;
  if (FAILED(doc->GetElementsByTagName(tag, &node_list)))
    return false;

  ComPtr<IXmlNode> image_node;
  if (FAILED(node_list->Item(0, &image_node)))
    return false;

  ComPtr<IXmlNamedNodeMap> attrs;
  if (FAILED(image_node->get_Attributes(&attrs)))
    return false;

  ScopedHString src(L"src");
  if (!src.success())
    return false;

  ComPtr<IXmlNode> src_attr;
  if (FAILED(attrs->GetNamedItem(src, &src_attr)))
    return false;

  ScopedHString img_path(icon_path.c_str());
  if (!img_path.success())
    return false;

  ComPtr<IXmlText> src_text;
  if (FAILED(doc->CreateTextNode(img_path, &src_text)))
    return false;

  ComPtr<IXmlNode> src_node;
  if (FAILED(src_text.As(&src_node)))
    return false;

  ComPtr<IXmlNode> child_node;
  return SUCCEEDED(src_attr->AppendChild(src_node.Get(), &child_node));
}

bool WindowsToastNotification::GetTextNodeList(
    ScopedHString* tag,
    IXmlDocument* doc,
    IXmlNodeList** node_list,
    uint32_t req_length) {
  tag->Reset(L"text");
  if (!tag->success())
    return false;

  if (FAILED(doc->GetElementsByTagName(*tag, node_list)))
    return false;

  uint32_t node_length;
  if (FAILED((*node_list)->get_Length(&node_length)))
    return false;

  return node_length >= req_length;
}

bool WindowsToastNotification::AppendTextToXml(
    IXmlDocument* doc, IXmlNode* node, const std::wstring& text) {
  ScopedHString str(text);
  if (!str.success())
    return false;

  ComPtr<IXmlText> xml_text;
  if (FAILED(doc->CreateTextNode(str, &xml_text)))
    return false;

  ComPtr<IXmlNode> text_node;
  if (FAILED(xml_text.As(&text_node)))
    return false;

  ComPtr<IXmlNode> append_node;
  return SUCCEEDED(node->AppendChild(text_node.Get(), &append_node));
}

bool WindowsToastNotification::SetupCallbacks(
    ABI::Windows::UI::Notifications::IToastNotification* toast) {
  event_handler_ = Make<ToastEventHandler>(this);
  if (FAILED(toast->add_Activated(event_handler_.Get(), &activated_token_)))
    return false;

  if (FAILED(toast->add_Dismissed(event_handler_.Get(), &dismissed_token_)))
    return false;

  return SUCCEEDED(toast->add_Failed(event_handler_.Get(), &failed_token_));
}

bool WindowsToastNotification::RemoveCallbacks(
    ABI::Windows::UI::Notifications::IToastNotification* toast) {
  if (FAILED(toast->remove_Activated(activated_token_)))
    return false;

  if (FAILED(toast->remove_Dismissed(dismissed_token_)))
    return false;

  return SUCCEEDED(toast->remove_Failed(failed_token_));
}

/*
/ Toast Event Handler
*/
ToastEventHandler::ToastEventHandler(Notification* notification)
    : notification_(notification->GetWeakPtr()) {
}

ToastEventHandler::~ToastEventHandler() {
}

IFACEMETHODIMP ToastEventHandler::Invoke(
    ABI::Windows::UI::Notifications::IToastNotification* sender, IInspectable* args) {
  content::BrowserThread::PostTask(
      content::BrowserThread::UI, FROM_HERE,
      base::Bind(&Notification::NotificationClicked, notification_));
  return S_OK;
}

IFACEMETHODIMP ToastEventHandler::Invoke(
    ABI::Windows::UI::Notifications::IToastNotification* sender,
    ABI::Windows::UI::Notifications::IToastDismissedEventArgs* e) {
  content::BrowserThread::PostTask(
      content::BrowserThread::UI, FROM_HERE,
      base::Bind(&Notification::NotificationDismissed, notification_));
  return S_OK;
}

IFACEMETHODIMP ToastEventHandler::Invoke(
    ABI::Windows::UI::Notifications::IToastNotification* sender,
    ABI::Windows::UI::Notifications::IToastFailedEventArgs* e) {
  content::BrowserThread::PostTask(
      content::BrowserThread::UI, FROM_HERE,
      base::Bind(&Notification::NotificationFailed, notification_));
  return S_OK;
}

}  // namespace brightray
<|endoftext|>