<commit_before>#pragma once

#include <condition_variable>
#include <exception>
#include <mutex>

#include "circular_queue.hh"

namespace chan {

struct channel_closed_exception: public std::exception {
	virtual const char* what() const throw() {
		return "cannot close an already closed channel";
	}
} _channel_closed_exception;

struct closed_channel_write_exception: public std::exception {
	virtual const char* what() const throw() {
		return "cannot write values to a closed channel";
	}
} _closed_channel_write_exception;

struct buffered_chan_zero_size_exception: public std::exception {
	virtual const char* what() const throw() {
		return "cannot create a buffered channel of size 0, use an unbuffered channel instead";
	}
} _buffered_chan_zero_size_exception;

template <typename T>
class chan;

/**
 * read_chan defines an interface for a channel that only supports reads
 *
 * example usage:
 *
 * ```
 * void read(chan::read_chan<int>& c) {
 * 	int x = 0;
 * 	c >> x;
 * 	printf("read: %d\n", x);
 * }
 *
 * int main() {
 * 	chan::unbuffered_chan<int> c;
 *
 * 	std::thread t(read, std::ref(c));
 *
 * 	printf("writing: 2\n");
 * 	c << 2;
 *
 * 	t.join();
 * }
 * ```
 * */
template <typename T>
class read_chan {
public:
	virtual bool close() = 0;
	virtual bool isClosed() const = 0;
	virtual bool read(T&) = 0;
	virtual bool recv(T&) = 0;
	virtual chan<T>& operator>>(T&) = 0;
};

/**
 * write_chan defines an interface for a channel that only supports writes
 *
 * example usage:
 *
 * ```
 * void write(chan::write_chan<int>& c) {
 * 	printf("writing: 2\n");
 * 	c << 2;
 * }
 *
 * int main() {
 * 	chan::unbuffered_chan<int> c;
 *
 * 	std::thread t(write, std::ref(c));
 *
 * 	int x = 0;
 * 	c >> x;
 * 	printf("read: %d\n", x);
 *
 * 	t.join();
 * }
 * ```
 * */
template <typename T>
class write_chan {
public:
	virtual bool close() = 0;
	virtual bool isClosed() const = 0;
	virtual void write(const T&) = 0;
	virtual void send(const T&) = 0;
	virtual chan<T>& operator<<(const T&) = 0;
};

/**
 * chan defines the common interface for a channel supporting reads and writes.
 * It defines all required functions except for read and write, which are
 * implemented individually by buffered_chan and unbuffered_chan.
 *
 * The abscence of these 2 functions makes this an abstract type, so it cannot
 * be instantiated.
 * */
template <typename T>
class chan : public read_chan<T>, public write_chan<T> {
protected:
	bool is_closed;

	mutable std::mutex data_mutex;

	int read_wait_count;
	int write_wait_count;

	mutable std::condition_variable read_available;
	mutable std::condition_variable write_available;

public:
	chan() : is_closed(false), read_wait_count(0), write_wait_count(0) {}

	/**
	 * close will close a channel if it wasn't already closed, otherwise it
	 * will simply return false.
	 *
	 * @return  bool   the result of the close operation
	 * */
	bool close() {
		std::unique_lock<std::mutex> data_lock(data_mutex);

		if (is_closed) {
			throw _channel_closed_exception;
		}

		is_closed = true;

		// notify any waiting read and write condition variables
		read_available.notify_all();
		write_available.notify_all();

		return true;
	}

	/**
	 * isClosed will return a boolean value indicating whether the channel
	 * has been closed or not.
	 *
	 * @return  bool   the current status
	 * */
	bool isClosed() const {
		std::unique_lock<std::mutex> data_lock(data_mutex);
		return is_closed;
	}

	/**
	 * send is an alias for write.
	 * */
	inline void send(const T& val) {
		this->write(val);
	}

	/**
	 * operator<< overloads the left shift operator to write
	 * to the channel, similar to std::ostream
	 * */
	virtual chan<T>& operator<<(const T& val) {
		this->write(val);
		return *this;
	}

	/**
	 * recv is an alias for read.
	 * */
	inline bool recv(T& val) {
		return this->read(val);
	}

	/**
	 * operator>> overloads the right shift operator to read from the
	 * channel, similar to std::istream
	 * */
	chan<T>& operator>>(T& val) {
		this->read(val);
		return *this;
	}
};

/**
 * unbuffered_chan implements chan with unbuffered bidirectional reads and
 * writes.
 *
 * In addition to synchronizing data, it also synchronizes reads and
 * writes and at a time only one thread is allowed to read or write data.
 *
 * "Unbuffered channels combine communication—the exchange of a value—
 * with synchronization—guaranteeing that two calculations (goroutines)
 * are in a known state."
 *
 * - https://golang.org/doc/effective_go.html#channels
 * */
template <typename T>
class unbuffered_chan : public chan<T> {
private:
	T data;
	bool set;

	mutable std::mutex read_mutex;
	mutable std::mutex write_mutex;

public:
	unbuffered_chan() : data(T()), set(false) {}

	unbuffered_chan(const unbuffered_chan& other) = delete;
	unbuffered_chan& operator=(const unbuffered_chan& other) = delete;
	unbuffered_chan(unbuffered_chan&& other) = delete;
	unbuffered_chan& operator=(unbuffered_chan&& other) = delete;

	/**
	 * write here implements writing to an unbuffered channel in a manner
	 * that is semantically similar to the mechanism in golang.
	 *
	 * It first synchronizes write, then data.
	 *
	 * It proceeds with first writing the value, and then proceeding to
	 * notify any waiting readers, following which, if the data still hasn't
	 * been consumed, it blocks the current thread waiting to be notified on
	 * the data being consumed.
	 *
	 * "A send on a channel happens before the corresponding receive from
	 * that channel completes."
	 *
	 * - https://golang.org/ref/mem#tmp_7
	 *
	 *
	 * @param   const T&   the value to add
	 *
	 * @return  bool       true if the mission was a success
	 * */
	void write(const T& val) {
		std::unique_lock<std::mutex> write_lock(write_mutex);
		std::unique_lock<std::mutex> data_lock(this->data_mutex);

		if (this->is_closed) {
			throw _closed_channel_write_exception;
		}

		data = val;
		set = true;
		this->write_wait_count++;

		if (this->read_wait_count > 0) {
			this->read_available.notify_one();
		}

		// wait until data is consumed
		while (!this->is_closed && set) {
			this->write_available.wait(data_lock);
		}
	}

	/**
	 * read first synchronizes the read, then data. Then it first waits for
	 * a write to happen, following which if the channel is still open, it
	 * will consume the data and notify any blocked writers to unblock
	 * themselves.
	 *
	 * "A receive from an unbuffered channel happens before the send on that
	 * channel completes."
	 *
	 * - https://golang.org/ref/mem#tmp_7
	 *
	 *
	 * @param   T&    the reference that is assigned the value in the front
	 *
	 * @return  bool  the result of this mission
	 * */
	bool read(T& valref) {
		std::unique_lock<std::mutex> read_lock(read_mutex);
		std::unique_lock<std::mutex> data_lock(this->data_mutex);

		while (!this->is_closed && this->write_wait_count == 0) {
			this->read_wait_count++;
			this->read_available.wait(data_lock);
			this->read_wait_count--;
		}

		if (this->is_closed) {
			// TODO: figure out error here
			valref = T();
			return false;
		}

		valref = data;
		set = false;

		this->write_wait_count--;
		this->write_available.notify_one();

		return true;
	}
};

/**
 * buffered_chan implements a buffered channel that supports multiple read/write
 * operations, upto a fixed "capacity".
 *
 * It can be used as a semaphore, to perform operations in batches.
 * */
template <typename T>
class buffered_chan : public chan<T> {
private:
	int capacity;
	circular_queue<T> data;

public:
	buffered_chan(int capacity)
	    : capacity(capacity), data(circular_queue<T>(capacity)) {
		if (capacity == 0) {
			throw _buffered_chan_zero_size_exception;
		}
	}

	buffered_chan(const buffered_chan& other) = delete;
	buffered_chan& operator=(const buffered_chan& other) = delete;
	buffered_chan(buffered_chan&& other) = delete;
	buffered_chan& operator=(buffered_chan&& other) = delete;

	/**
	 * write implements writing to a buffered channel. There is no call
	 * synchronization here, we only synchronize access to data. First we
	 * wait until the buffer is full. Then, if the channel isn't closed, we
	 * add the value to the channel's buffer and notify any waiting readers.
	 *
	 *
	 * @param   const T&   the value to add
	 *
	 * @return  bool       true if the mission was a success
	 * */
	void write(const T& val) {
		std::unique_lock<std::mutex> data_lock(this->data_mutex);

		while (!this->is_closed && data.size() == capacity) {
			this->write_wait_count++;
			this->write_available.wait(data_lock);
			this->write_wait_count--;
		}

		if (this->is_closed) {
			throw _closed_channel_write_exception;
		}

		data.push(val);

		// signal waiting reader
		if (this->read_wait_count > 0) {
			this->read_available.notify_one();
		}

		// NOTE: this doesn't immediately block for read
	}

	/**
	 * read implements data removal for a buffered channel. There is no call
	 * synchronization. It first blocks while the queue is empty. Once the
	 * queue is non-empty, it removes a value from the front of the queue
	 * and saves it in the passed reference, following which it notifies any
	 * waiting writers.
	 *
	 *
	 * @param   T&    the reference that is assigned the value in the front
	 *
	 * @return  bool  the result of this mission
	 * */
	bool read(T& valref) {
		std::unique_lock<std::mutex> data_lock(this->data_mutex);

		while (data.empty()) {
			if (this->is_closed) {
				// TODO: figure out error handling here
				valref = T();
				return false;
			}

			this->read_wait_count++;
			this->read_available.wait(data_lock);
			this->read_wait_count--;
		}

		valref = data.front();
		data.pop();

		if (this->write_wait_count > 0) {
			this->write_available.notify_one();
		}

		return true;
	}
};

}  // namespace chan
<commit_msg>chan: add closing destructor<commit_after>#pragma once

#include <condition_variable>
#include <exception>
#include <mutex>

#include "circular_queue.hh"

namespace chan {

struct channel_closed_exception: public std::exception {
	virtual const char* what() const throw() {
		return "cannot close an already closed channel";
	}
} _channel_closed_exception;

struct closed_channel_write_exception: public std::exception {
	virtual const char* what() const throw() {
		return "cannot write values to a closed channel";
	}
} _closed_channel_write_exception;

struct buffered_chan_zero_size_exception: public std::exception {
	virtual const char* what() const throw() {
		return "cannot create a buffered channel of size 0, use an unbuffered channel instead";
	}
} _buffered_chan_zero_size_exception;

template <typename T>
class chan;

/**
 * read_chan defines an interface for a channel that only supports reads
 *
 * example usage:
 *
 * ```
 * void read(chan::read_chan<int>& c) {
 * 	int x = 0;
 * 	c >> x;
 * 	printf("read: %d\n", x);
 * }
 *
 * int main() {
 * 	chan::unbuffered_chan<int> c;
 *
 * 	std::thread t(read, std::ref(c));
 *
 * 	printf("writing: 2\n");
 * 	c << 2;
 *
 * 	t.join();
 * }
 * ```
 * */
template <typename T>
class read_chan {
public:
	virtual bool close() = 0;
	virtual bool isClosed() const = 0;
	virtual bool read(T&) = 0;
	virtual bool recv(T&) = 0;
	virtual chan<T>& operator>>(T&) = 0;
};

/**
 * write_chan defines an interface for a channel that only supports writes
 *
 * example usage:
 *
 * ```
 * void write(chan::write_chan<int>& c) {
 * 	printf("writing: 2\n");
 * 	c << 2;
 * }
 *
 * int main() {
 * 	chan::unbuffered_chan<int> c;
 *
 * 	std::thread t(write, std::ref(c));
 *
 * 	int x = 0;
 * 	c >> x;
 * 	printf("read: %d\n", x);
 *
 * 	t.join();
 * }
 * ```
 * */
template <typename T>
class write_chan {
public:
	virtual bool close() = 0;
	virtual bool isClosed() const = 0;
	virtual void write(const T&) = 0;
	virtual void send(const T&) = 0;
	virtual chan<T>& operator<<(const T&) = 0;
};

/**
 * chan defines the common interface for a channel supporting reads and writes.
 * It defines all required functions except for read and write, which are
 * implemented individually by buffered_chan and unbuffered_chan.
 *
 * The abscence of these 2 functions makes this an abstract type, so it cannot
 * be instantiated.
 * */
template <typename T>
class chan : public read_chan<T>, public write_chan<T> {
protected:
	bool is_closed;

	mutable std::mutex data_mutex;

	int read_wait_count;
	int write_wait_count;

	mutable std::condition_variable read_available;
	mutable std::condition_variable write_available;

public:
	chan() : is_closed(false), read_wait_count(0), write_wait_count(0) {}

	~chan() {
		if (!is_closed) {
			close();
		}
	}

	/**
	 * close will close a channel if it wasn't already closed, otherwise it
	 * will simply return false.
	 *
	 * @return  bool   the result of the close operation
	 * */
	bool close() {
		std::unique_lock<std::mutex> data_lock(data_mutex);

		if (is_closed) {
			throw _channel_closed_exception;
		}

		is_closed = true;

		// notify any waiting read and write condition variables
		read_available.notify_all();
		write_available.notify_all();

		return true;
	}

	/**
	 * isClosed will return a boolean value indicating whether the channel
	 * has been closed or not.
	 *
	 * @return  bool   the current status
	 * */
	bool isClosed() const {
		std::unique_lock<std::mutex> data_lock(data_mutex);
		return is_closed;
	}

	/**
	 * send is an alias for write.
	 * */
	inline void send(const T& val) {
		this->write(val);
	}

	/**
	 * operator<< overloads the left shift operator to write
	 * to the channel, similar to std::ostream
	 * */
	virtual chan<T>& operator<<(const T& val) {
		this->write(val);
		return *this;
	}

	/**
	 * recv is an alias for read.
	 * */
	inline bool recv(T& val) {
		return this->read(val);
	}

	/**
	 * operator>> overloads the right shift operator to read from the
	 * channel, similar to std::istream
	 * */
	chan<T>& operator>>(T& val) {
		this->read(val);
		return *this;
	}
};

/**
 * unbuffered_chan implements chan with unbuffered bidirectional reads and
 * writes.
 *
 * In addition to synchronizing data, it also synchronizes reads and
 * writes and at a time only one thread is allowed to read or write data.
 *
 * "Unbuffered channels combine communication—the exchange of a value—
 * with synchronization—guaranteeing that two calculations (goroutines)
 * are in a known state."
 *
 * - https://golang.org/doc/effective_go.html#channels
 * */
template <typename T>
class unbuffered_chan : public chan<T> {
private:
	T data;
	bool set;

	mutable std::mutex read_mutex;
	mutable std::mutex write_mutex;

public:
	unbuffered_chan() : data(T()), set(false) {}

	unbuffered_chan(const unbuffered_chan& other) = delete;
	unbuffered_chan& operator=(const unbuffered_chan& other) = delete;
	unbuffered_chan(unbuffered_chan&& other) = delete;
	unbuffered_chan& operator=(unbuffered_chan&& other) = delete;

	/**
	 * write here implements writing to an unbuffered channel in a manner
	 * that is semantically similar to the mechanism in golang.
	 *
	 * It first synchronizes write, then data.
	 *
	 * It proceeds with first writing the value, and then proceeding to
	 * notify any waiting readers, following which, if the data still hasn't
	 * been consumed, it blocks the current thread waiting to be notified on
	 * the data being consumed.
	 *
	 * "A send on a channel happens before the corresponding receive from
	 * that channel completes."
	 *
	 * - https://golang.org/ref/mem#tmp_7
	 *
	 *
	 * @param   const T&   the value to add
	 *
	 * @return  bool       true if the mission was a success
	 * */
	void write(const T& val) {
		std::unique_lock<std::mutex> write_lock(write_mutex);
		std::unique_lock<std::mutex> data_lock(this->data_mutex);

		if (this->is_closed) {
			throw _closed_channel_write_exception;
		}

		data = val;
		set = true;
		this->write_wait_count++;

		if (this->read_wait_count > 0) {
			this->read_available.notify_one();
		}

		// wait until data is consumed
		while (!this->is_closed && set) {
			this->write_available.wait(data_lock);
		}
	}

	/**
	 * read first synchronizes the read, then data. Then it first waits for
	 * a write to happen, following which if the channel is still open, it
	 * will consume the data and notify any blocked writers to unblock
	 * themselves.
	 *
	 * "A receive from an unbuffered channel happens before the send on that
	 * channel completes."
	 *
	 * - https://golang.org/ref/mem#tmp_7
	 *
	 *
	 * @param   T&    the reference that is assigned the value in the front
	 *
	 * @return  bool  the result of this mission
	 * */
	bool read(T& valref) {
		std::unique_lock<std::mutex> read_lock(read_mutex);
		std::unique_lock<std::mutex> data_lock(this->data_mutex);

		while (!this->is_closed && this->write_wait_count == 0) {
			this->read_wait_count++;
			this->read_available.wait(data_lock);
			this->read_wait_count--;
		}

		if (this->is_closed) {
			// TODO: figure out error here
			valref = T();
			return false;
		}

		valref = data;
		set = false;

		this->write_wait_count--;
		this->write_available.notify_one();

		return true;
	}
};

/**
 * buffered_chan implements a buffered channel that supports multiple read/write
 * operations, upto a fixed "capacity".
 *
 * It can be used as a semaphore, to perform operations in batches.
 * */
template <typename T>
class buffered_chan : public chan<T> {
private:
	int capacity;
	circular_queue<T> data;

public:
	buffered_chan(int capacity)
	    : capacity(capacity), data(circular_queue<T>(capacity)) {
		if (capacity == 0) {
			throw _buffered_chan_zero_size_exception;
		}
	}

	buffered_chan(const buffered_chan& other) = delete;
	buffered_chan& operator=(const buffered_chan& other) = delete;
	buffered_chan(buffered_chan&& other) = delete;
	buffered_chan& operator=(buffered_chan&& other) = delete;

	/**
	 * write implements writing to a buffered channel. There is no call
	 * synchronization here, we only synchronize access to data. First we
	 * wait until the buffer is full. Then, if the channel isn't closed, we
	 * add the value to the channel's buffer and notify any waiting readers.
	 *
	 *
	 * @param   const T&   the value to add
	 *
	 * @return  bool       true if the mission was a success
	 * */
	void write(const T& val) {
		std::unique_lock<std::mutex> data_lock(this->data_mutex);

		while (!this->is_closed && data.size() == capacity) {
			this->write_wait_count++;
			this->write_available.wait(data_lock);
			this->write_wait_count--;
		}

		if (this->is_closed) {
			throw _closed_channel_write_exception;
		}

		data.push(val);

		// signal waiting reader
		if (this->read_wait_count > 0) {
			this->read_available.notify_one();
		}

		// NOTE: this doesn't immediately block for read
	}

	/**
	 * read implements data removal for a buffered channel. There is no call
	 * synchronization. It first blocks while the queue is empty. Once the
	 * queue is non-empty, it removes a value from the front of the queue
	 * and saves it in the passed reference, following which it notifies any
	 * waiting writers.
	 *
	 *
	 * @param   T&    the reference that is assigned the value in the front
	 *
	 * @return  bool  the result of this mission
	 * */
	bool read(T& valref) {
		std::unique_lock<std::mutex> data_lock(this->data_mutex);

		while (data.empty()) {
			if (this->is_closed) {
				// TODO: figure out error handling here
				valref = T();
				return false;
			}

			this->read_wait_count++;
			this->read_available.wait(data_lock);
			this->read_wait_count--;
		}

		valref = data.front();
		data.pop();

		if (this->write_wait_count > 0) {
			this->write_available.notify_one();
		}

		return true;
	}
};

}  // namespace chan
<|endoftext|>


<commit_before>#include "gamestates/skirmishstate.hpp"

#include "gamestates/deploystate.hpp"
#include "engine/pathfinding/astar.hpp"
#include "engine/pathfinding/path.hpp"
#include "gui/texturemanager.hpp"
#include "gui/ng/label.hpp"
#include "gui/ng/spritewidget.hpp"
#include "gui/ng/button.hpp"
#include "gui/squaredetailwindow.hpp"

#include <SFML/Graphics/Sprite.hpp>

#include <iostream>
#include <memory>

namespace qrw
{

SkirmishState::SkirmishState(sf::RenderWindow* renderWindow)
	: SceneState(renderWindow, EGameStateId::EGSID_SKIRMISH_STATE),
	  _squareMarker(new SquareMarker())
{
    _squareDetailWindow = new SquareDetailWindow();
    _guiUptr->addWidget(_squareDetailWindow);

	_squareMarker->setVisible(false);

	_playerNameText = new namelessgui::Text();
	_playerNameText->setText("Player Name");
	_playerNameText->setParentAnchor({0.5, 0});
	_playerNameText->setAnchor({0.5, 0});
	_toolBar->addWidget(_playerNameText);

	namelessgui::Button* endTurnButton = new namelessgui::Button();
	endTurnButton->setText("End Turn");
	endTurnButton->setSize({140, 30});
	endTurnButton->setParentAnchor({0.5, 1});
	endTurnButton->setAnchor({0.5, 1.0});
	endTurnButton->setRelativePosition({0.0f, -5.0f});
	endTurnButton->signalclicked.connect(std::bind(&SkirmishState::endTurn, this));
	_toolBar->addWidget(endTurnButton);
}

void SkirmishState::init(GameState *previousState)
{
    SceneState::init(previousState);

    if(previousState->getId() != EGameStateId::EGSID_DEPLOY_STATE)
        return;

    DeployState* deployState = static_cast<DeployState*>(previousState);

    _board = deployState->getBoard();
	g_scene.setBoard(_board);

	_players = deployState->getPlayers();
	_currentPlayer = 0;
	_playerNameText->setText(_players[_currentPlayer]->getName());

    // Initialize square detail window.
	Coordinates cursorPosition = g_scene.getCursorPosition();
	Unit* unit = _board->getUnit(cursorPosition);
	Terrain* terrain = _board->getTerrain(cursorPosition);
	_squareDetailWindow->setUnitAndTerrain(unit, terrain);
}

void SkirmishState::draw()
{
	SceneState::draw();
	_squareMarker->draw(*_renderWindow, sf::RenderStates::Default);
	drawPath();
}

EGameStateId SkirmishState::update()
{
    if(_backToMainMenu)
        return EGameStateId::EGSID_MAIN_MENU_STATE;

    return EGameStateId::EGSID_NO_CHANGE;
}

void SkirmishState::slotCursorMoved(const Coordinates &boardPosition)
{
	if(_board->isOnBoard(boardPosition))
	{
		Unit* unitUnderCursor = _board->getUnit(boardPosition);
		Terrain* terrainUnderCursor = _board->getTerrain(boardPosition);
		_squareDetailWindow->setUnitAndTerrain(unitUnderCursor, terrainUnderCursor);

		if(_selectedUnit)
		{
			_path.reset(_board->findPath(_selectedUnit->getPosition(), boardPosition));

			Cursor::Color cursorColor = Cursor::Color::ESC_DEFAULT;
			if(!_path || _path->getMovementCosts() > _selectedUnit->getCurrentMovement())
				cursorColor = Cursor::Color::ESC_WARNING;
			if(boardPosition == _squareMarker->getBoardPosition())
				cursorColor = Cursor::Color::ESC_DEFAULT;

			g_scene.getCursor().setFillColor(cursorColor);
		}
	}
    else
		_squareDetailWindow->setUnitAndTerrain(nullptr, nullptr);
}

void SkirmishState::moveUnit()
{
	if(!_selectedUnit) return;
	if(!_path) return;

	int pathCosts = _path->getMovementCosts();
	if(pathCosts == 0) return;

	int maxDistance = _selectedUnit->getCurrentMovement();
	if(pathCosts > maxDistance) return;

	int remainingMovement = maxDistance - pathCosts;
	_selectedUnit->setCurrentMovement(remainingMovement);

	_board->moveUnit(_squareMarker->getBoardPosition(), _path->getTarget());
	_selectedUnit->setPosition(_path->getTarget());

	std::cout << "Move unit." << std::endl;
}

void SkirmishState::performAttack(Unit* attackedUnit)
{
	const Coordinates& positionOfAttackedUnit = attackedUnit->getPosition();

	if(!_selectedUnit->isTargetWithinAttackRange(positionOfAttackedUnit))
		return;

	if(_selectedUnit->getCurrentMovement() == 0)
		return;
	_selectedUnit->setCurrentMovement(0);

	int inflictedDamage = _selectedUnit->getModifiedAttack() - attackedUnit->getModifiedDefense();
	inflictedDamage = inflictedDamage < 0 ? 0 : inflictedDamage;
	attackedUnit->damage(inflictedDamage);

	if(attackedUnit->getHP() == 0)
	{
		_board->removeUnit(positionOfAttackedUnit);
		_board->moveUnit(_selectedUnit->getPosition(), positionOfAttackedUnit);
		_selectedUnit->setPosition(positionOfAttackedUnit);
	}
	else
	{
		inflictedDamage = attackedUnit->getModifiedAttack() - _selectedUnit->getModifiedDefense();
		inflictedDamage = inflictedDamage < 0 ? 0 : inflictedDamage;
		_selectedUnit->damage(inflictedDamage);

		if(_selectedUnit->getHP() == 0)
		{
			_board->removeUnit(_selectedUnit->getPosition());
		}
	}

	updateSquareDetailWindow(positionOfAttackedUnit);

	std::cout << "Attack unit." << std::endl;
}

void SkirmishState::replenishTroops()
{
	Unit* unit;
	for(auto unitIter : _board->getUnits())
	{
		unit = unitIter.second;
		unit->setCurrentMovement(unit->getMovement());
	}
}

void SkirmishState::updateSquareDetailWindow(const Coordinates& position)
{
	_squareDetailWindow->setUnitAndTerrain(
				_board->getUnit(position),
				_board->getTerrain(position));
}

void SkirmishState::slotCursorLeftClicked(const Coordinates &boardPosition)
{
	Unit* unitUnderCursor = _board->getUnit(boardPosition);
	Terrain* terrainUnderCursor = _board->getTerrain(boardPosition);

	_squareDetailWindow->setUnitAndTerrain(unitUnderCursor, terrainUnderCursor);

	// Case 1: Unit is selected and instructed to move.
	if(_selectedUnit && !unitUnderCursor)
	{
		moveUnit();
		deselectUnit();
		return;
	}

	// Case 2: Unit is selected and instructed to attack enemy.
	if(_selectedUnit && unitUnderCursor && unitUnderCursor->getPlayer() != _selectedUnit->getPlayer())
	{
		performAttack(unitUnderCursor);
		deselectUnit();
		return;
	}

	// Do not allow to select units of other player
	if(unitUnderCursor && unitUnderCursor->getPlayer() != _players[_currentPlayer])
	{
		// Select unit
		_selectedUnit = unitUnderCursor;
		_squareMarker->setBoardPosition(boardPosition);
		_squareMarker->setVisible(true);

		return;
	}

	deselectUnit();
}

bool SkirmishState::handleEvent(sf::Event& event)
{
	if(SceneState::handleEvent(event))
		return true;

	if(event.type == sf::Event::MouseButtonReleased)
	{
		if(event.mouseButton.button == sf::Mouse::Right)
		{
			deselectUnit();
			return true;
		}
	}

	return false;
}

void SkirmishState::endTurn()
{
	_currentPlayer = (_currentPlayer + 1) % _players.size();
	_playerNameText->setText(_players[_currentPlayer]->getName());

	deselectUnit();
	replenishTroops();
}

void SkirmishState::drawPath()
{
	if(!_path)
		return;

	const int pathLength = _path->getLength();

	const Coordinates* previous = nullptr;
	const Coordinates* current  = &_path->getStep(0);
	const Coordinates* next     = &_path->getStep(1);

	sf::Sprite footstep = sf::Sprite(*TextureManager::getInstance()->getTexture("footstep"));

	// Do not render first step.
	for(int i = 1; i < pathLength; ++i)
	{
		previous = current;
		current  = next;

		// Reset the previously applied transformations.
		footstep.setOrigin(16, 16);
		footstep.setScale(1, 1);
		footstep.setRotation(0);

		// Transformations relative to the previous step
		Coordinates prevDelta(*previous - *current);
		if(prevDelta.getX() != 0)
			footstep.rotate(-90 * prevDelta.getX());
		if(prevDelta.getY() != 0)
			footstep.scale(1, prevDelta.getY());

		// Transformations relative to the next step (if possible)
		if(i < pathLength - 1)
		{
			next = &_path->getStep(i+1);

			Coordinates prevNextDelta(*previous - *next);

			// If the path has a corner at this position
			if(prevNextDelta.getX() != 0 && prevNextDelta.getY() != 0)
			{
				int rotationdirection = 0;
				// horizontal
				if(prevDelta.getX() == 0)
				{
					rotationdirection = -1;
				}
				// vertical
				else if(prevDelta.getY() == 0)
				{
					rotationdirection = +1;
				}
				footstep.rotate(rotationdirection * 45 * (prevNextDelta.getX() * prevNextDelta.getY()));
			}
		}

		footstep.setPosition(
			32 * (0.5f + current->getX()),
			32 * (0.5f + current->getY())
		);

		_renderWindow->draw(footstep);
	}
}

void SkirmishState::deselectUnit()
{
	_selectedUnit = nullptr;
	_squareMarker->setVisible(false);
	g_scene.getCursor().setFillColor(Cursor::Color::ESC_DEFAULT);
	_path.reset();
}

} // namespace qrw
<commit_msg>Fixed crashes when cursor is initially moved in skirmish state.<commit_after>#include "gamestates/skirmishstate.hpp"

#include "gamestates/deploystate.hpp"
#include "engine/pathfinding/astar.hpp"
#include "engine/pathfinding/path.hpp"
#include "gui/texturemanager.hpp"
#include "gui/ng/label.hpp"
#include "gui/ng/spritewidget.hpp"
#include "gui/ng/button.hpp"
#include "gui/squaredetailwindow.hpp"

#include <SFML/Graphics/Sprite.hpp>

#include <iostream>
#include <memory>

namespace qrw
{

SkirmishState::SkirmishState(sf::RenderWindow* renderWindow)
	: SceneState(renderWindow, EGameStateId::EGSID_SKIRMISH_STATE),
	  _squareMarker(new SquareMarker()),
	  _selectedUnit(nullptr)
{
    _squareDetailWindow = new SquareDetailWindow();
    _guiUptr->addWidget(_squareDetailWindow);

	_squareMarker->setVisible(false);

	_playerNameText = new namelessgui::Text();
	_playerNameText->setText("Player Name");
	_playerNameText->setParentAnchor({0.5, 0});
	_playerNameText->setAnchor({0.5, 0});
	_toolBar->addWidget(_playerNameText);

	namelessgui::Button* endTurnButton = new namelessgui::Button();
	endTurnButton->setText("End Turn");
	endTurnButton->setSize({140, 30});
	endTurnButton->setParentAnchor({0.5, 1});
	endTurnButton->setAnchor({0.5, 1.0});
	endTurnButton->setRelativePosition({0.0f, -5.0f});
	endTurnButton->signalclicked.connect(std::bind(&SkirmishState::endTurn, this));
	_toolBar->addWidget(endTurnButton);
}

void SkirmishState::init(GameState *previousState)
{
    SceneState::init(previousState);

    if(previousState->getId() != EGameStateId::EGSID_DEPLOY_STATE)
        return;

    DeployState* deployState = static_cast<DeployState*>(previousState);

    _board = deployState->getBoard();
	g_scene.setBoard(_board);

	_players = deployState->getPlayers();
	_currentPlayer = 0;
	_playerNameText->setText(_players[_currentPlayer]->getName());

    // Initialize square detail window.
	Coordinates cursorPosition = g_scene.getCursorPosition();
	Unit* unit = _board->getUnit(cursorPosition);
	Terrain* terrain = _board->getTerrain(cursorPosition);
	_squareDetailWindow->setUnitAndTerrain(unit, terrain);
}

void SkirmishState::draw()
{
	SceneState::draw();
	_squareMarker->draw(*_renderWindow, sf::RenderStates::Default);
	drawPath();
}

EGameStateId SkirmishState::update()
{
    if(_backToMainMenu)
        return EGameStateId::EGSID_MAIN_MENU_STATE;

    return EGameStateId::EGSID_NO_CHANGE;
}

void SkirmishState::slotCursorMoved(const Coordinates &boardPosition)
{
	if(_board->isOnBoard(boardPosition))
	{
		Unit* unitUnderCursor = _board->getUnit(boardPosition);
		Terrain* terrainUnderCursor = _board->getTerrain(boardPosition);
		_squareDetailWindow->setUnitAndTerrain(unitUnderCursor, terrainUnderCursor);

		if(_selectedUnit)
		{
			_path.reset(_board->findPath(_selectedUnit->getPosition(), boardPosition));

			Cursor::Color cursorColor = Cursor::Color::ESC_DEFAULT;
			if(!_path || _path->getMovementCosts() > _selectedUnit->getCurrentMovement())
				cursorColor = Cursor::Color::ESC_WARNING;
			if(boardPosition == _squareMarker->getBoardPosition())
				cursorColor = Cursor::Color::ESC_DEFAULT;

			g_scene.getCursor().setFillColor(cursorColor);
		}
	}
    else
		_squareDetailWindow->setUnitAndTerrain(nullptr, nullptr);
}

void SkirmishState::moveUnit()
{
	if(!_selectedUnit) return;
	if(!_path) return;

	int pathCosts = _path->getMovementCosts();
	if(pathCosts == 0) return;

	int maxDistance = _selectedUnit->getCurrentMovement();
	if(pathCosts > maxDistance) return;

	int remainingMovement = maxDistance - pathCosts;
	_selectedUnit->setCurrentMovement(remainingMovement);

	_board->moveUnit(_squareMarker->getBoardPosition(), _path->getTarget());
	_selectedUnit->setPosition(_path->getTarget());

	std::cout << "Move unit." << std::endl;
}

void SkirmishState::performAttack(Unit* attackedUnit)
{
	const Coordinates& positionOfAttackedUnit = attackedUnit->getPosition();

	if(!_selectedUnit->isTargetWithinAttackRange(positionOfAttackedUnit))
		return;

	if(_selectedUnit->getCurrentMovement() == 0)
		return;
	_selectedUnit->setCurrentMovement(0);

	int inflictedDamage = _selectedUnit->getModifiedAttack() - attackedUnit->getModifiedDefense();
	inflictedDamage = inflictedDamage < 0 ? 0 : inflictedDamage;
	attackedUnit->damage(inflictedDamage);

	if(attackedUnit->getHP() == 0)
	{
		_board->removeUnit(positionOfAttackedUnit);
		_board->moveUnit(_selectedUnit->getPosition(), positionOfAttackedUnit);
		_selectedUnit->setPosition(positionOfAttackedUnit);
	}
	else
	{
		inflictedDamage = attackedUnit->getModifiedAttack() - _selectedUnit->getModifiedDefense();
		inflictedDamage = inflictedDamage < 0 ? 0 : inflictedDamage;
		_selectedUnit->damage(inflictedDamage);

		if(_selectedUnit->getHP() == 0)
		{
			_board->removeUnit(_selectedUnit->getPosition());
		}
	}

	updateSquareDetailWindow(positionOfAttackedUnit);

	std::cout << "Attack unit." << std::endl;
}

void SkirmishState::replenishTroops()
{
	Unit* unit;
	for(auto unitIter : _board->getUnits())
	{
		unit = unitIter.second;
		unit->setCurrentMovement(unit->getMovement());
	}
}

void SkirmishState::updateSquareDetailWindow(const Coordinates& position)
{
	_squareDetailWindow->setUnitAndTerrain(
				_board->getUnit(position),
				_board->getTerrain(position));
}

void SkirmishState::slotCursorLeftClicked(const Coordinates &boardPosition)
{
	Unit* unitUnderCursor = _board->getUnit(boardPosition);
	Terrain* terrainUnderCursor = _board->getTerrain(boardPosition);

	_squareDetailWindow->setUnitAndTerrain(unitUnderCursor, terrainUnderCursor);

	// Case 1: Unit is selected and instructed to move.
	if(_selectedUnit && !unitUnderCursor)
	{
		moveUnit();
		deselectUnit();
		return;
	}

	// Case 2: Unit is selected and instructed to attack enemy.
	if(_selectedUnit && unitUnderCursor && unitUnderCursor->getPlayer() != _selectedUnit->getPlayer())
	{
		performAttack(unitUnderCursor);
		deselectUnit();
		return;
	}

	// Do not allow to select units of other player
	if(unitUnderCursor && unitUnderCursor->getPlayer() != _players[_currentPlayer])
	{
		// Select unit
		_selectedUnit = unitUnderCursor;
		_squareMarker->setBoardPosition(boardPosition);
		_squareMarker->setVisible(true);

		return;
	}

	deselectUnit();
}

bool SkirmishState::handleEvent(sf::Event& event)
{
	if(SceneState::handleEvent(event))
		return true;

	if(event.type == sf::Event::MouseButtonReleased)
	{
		if(event.mouseButton.button == sf::Mouse::Right)
		{
			deselectUnit();
			return true;
		}
	}

	return false;
}

void SkirmishState::endTurn()
{
	_currentPlayer = (_currentPlayer + 1) % _players.size();
	_playerNameText->setText(_players[_currentPlayer]->getName());

	deselectUnit();
	replenishTroops();
}

void SkirmishState::drawPath()
{
	if(!_path)
		return;

	const int pathLength = _path->getLength();

	const Coordinates* previous = nullptr;
	const Coordinates* current  = &_path->getStep(0);
	const Coordinates* next     = &_path->getStep(1);

	sf::Sprite footstep = sf::Sprite(*TextureManager::getInstance()->getTexture("footstep"));

	// Do not render first step.
	for(int i = 1; i < pathLength; ++i)
	{
		previous = current;
		current  = next;

		// Reset the previously applied transformations.
		footstep.setOrigin(16, 16);
		footstep.setScale(1, 1);
		footstep.setRotation(0);

		// Transformations relative to the previous step
		Coordinates prevDelta(*previous - *current);
		if(prevDelta.getX() != 0)
			footstep.rotate(-90 * prevDelta.getX());
		if(prevDelta.getY() != 0)
			footstep.scale(1, prevDelta.getY());

		// Transformations relative to the next step (if possible)
		if(i < pathLength - 1)
		{
			next = &_path->getStep(i+1);

			Coordinates prevNextDelta(*previous - *next);

			// If the path has a corner at this position
			if(prevNextDelta.getX() != 0 && prevNextDelta.getY() != 0)
			{
				int rotationdirection = 0;
				// horizontal
				if(prevDelta.getX() == 0)
				{
					rotationdirection = -1;
				}
				// vertical
				else if(prevDelta.getY() == 0)
				{
					rotationdirection = +1;
				}
				footstep.rotate(rotationdirection * 45 * (prevNextDelta.getX() * prevNextDelta.getY()));
			}
		}

		footstep.setPosition(
			32 * (0.5f + current->getX()),
			32 * (0.5f + current->getY())
		);

		_renderWindow->draw(footstep);
	}
}

void SkirmishState::deselectUnit()
{
	_selectedUnit = nullptr;
	_squareMarker->setVisible(false);
	g_scene.getCursor().setFillColor(Cursor::Color::ESC_DEFAULT);
	_path.reset();
}

} // namespace qrw
<|endoftext|>


<commit_before>#include "kwm.h"
#include "helpers.h"
#include "daemon.h"
#include "display.h"
#include "space.h"
#include "window.h"
#include "keys.h"
#include "interpreter.h"
#include "scratchpad.h"
#include "border.h"
#include "config.h"
#include "command.h"
#include "cursor.h"

#include "axlib/axlib.h"

#define internal static

const std::string KwmCurrentVersion = "Kwm Version 2.2.0";
ax_state AXState = {};
ax_application *FocusedApplication;
ax_display *FocusedDisplay;
std::map<CFStringRef, space_info> WindowTree;

kwm_mach KWMMach = {};
kwm_path KWMPath = {};
kwm_screen KWMScreen = {};
kwm_toggles KWMToggles = {};
kwm_focus KWMFocus = {};
kwm_mode KWMMode = {};
kwm_tiling KWMTiling = {};
kwm_thread KWMThread = {};
kwm_hotkeys KWMHotkeys = {};
kwm_border FocusedBorder = {};
kwm_border MarkedBorder = {};
scratchpad Scratchpad = {};

/* TODO(koekeishiya): Should probably be moved to a 'cursor.cpp' or similar in the future,
                      along with other cursor-related functionality we might want */
EVENT_CALLBACK(Callback_AXEvent_MouseMoved)
{
    FocusWindowBelowCursor();
}

CGEventRef CGEventCallback(CGEventTapProxy Proxy, CGEventType Type, CGEventRef Event, void *Refcon)
{
    switch(Type)
    {
        case kCGEventTapDisabledByTimeout:
        case kCGEventTapDisabledByUserInput:
        {
            if(!KWMMach.DisableEventTapInternal)
            {
                DEBUG("Restarting Event Tap");
                CGEventTapEnable(KWMMach.EventTap, true);
            }
        } break;
        case kCGEventKeyDown:
        {
            /* TODO(koekeishiya): Is there a better way to decide whether
                                  we should eat the CGEventRef or not (?) */
            if(KWMToggles.UseBuiltinHotkeys)
            {
                hotkey Eventkey = {}, *Hotkey = NULL;
                Hotkey = (hotkey *) calloc(1, sizeof(hotkey));
                if(Hotkey)
                {
                    CreateHotkeyFromCGEvent(Event, &Eventkey);
                    if(HotkeyExists(Eventkey.Mod, Eventkey.Key, Hotkey, KWMHotkeys.ActiveMode->Name))
                    {
                        AXLibConstructEvent(AXEvent_HotkeyPressed, Hotkey);
                        if(!Hotkey->Passthrough)
                            return NULL;
                    }
                }
            }
        } break;
        case kCGEventMouseMoved:
        {
            if(KWMMode.Focus == FocusModeAutoraise)
                AXLibConstructEvent(AXEvent_MouseMoved, NULL);
        } break;
        default: {} break;
    }

    return Event;
}

internal bool
CheckPrivileges()
{
    bool Result = false;
    const void * Keys[] = { kAXTrustedCheckOptionPrompt };
    const void * Values[] = { kCFBooleanTrue };

    CFDictionaryRef Options;
    Options = CFDictionaryCreate(kCFAllocatorDefault,
                                 Keys, Values, sizeof(Keys) / sizeof(*Keys),
                                 &kCFCopyStringDictionaryKeyCallBacks,
                                 &kCFTypeDictionaryValueCallBacks);

    Result = AXIsProcessTrustedWithOptions(Options);
    CFRelease(Options);

    return Result;
}

internal bool
CheckArguments(int argc, char **argv)
{
    bool Result = false;

    if(argc == 2)
    {
        std::string Arg = argv[1];
        if(Arg == "--version")
        {
            std::cout << KwmCurrentVersion << std::endl;
            Result = true;
        }
    }

    return Result;
}

internal bool
GetKwmFilePath()
{
    bool Result = false;
    char PathBuf[PROC_PIDPATHINFO_MAXSIZE];
    pid_t Pid = getpid();
    int Ret = proc_pidpath(Pid, PathBuf, sizeof(PathBuf));
    if (Ret > 0)
    {
        KWMPath.FilePath = PathBuf;

        std::size_t Split = KWMPath.FilePath.find_last_of("/\\");
        KWMPath.FilePath = KWMPath.FilePath.substr(0, Split);
        Result = true;
    }

    return Result;
}

internal void
KwmClearSettings()
{
    std::map<int, CFTypeRef>::iterator It;
    for(It = KWMTiling.AllowedWindowRoles.begin(); It != KWMTiling.AllowedWindowRoles.end(); ++It)
        CFRelease(It->second);

    KWMTiling.AllowedWindowRoles.clear();
    KWMHotkeys.Modes.clear();
    KWMTiling.WindowRules.clear();
    KWMTiling.SpaceSettings.clear();
    KWMTiling.DisplaySettings.clear();
    KWMTiling.EnforcedWindows.clear();
    KWMHotkeys.ActiveMode = GetBindingMode("default");
}

internal void
KwmExecuteConfig()
{
    char *HomeP = std::getenv("HOME");
    if(!HomeP)
    {
        DEBUG("Failed to get environment variable 'HOME'");
        return;
    }

    KWMPath.EnvHome = HomeP;
    KwmParseConfig(KWMPath.ConfigFile);
}

internal void
KwmExecuteInitScript()
{
    std::string InitFile = KWMPath.EnvHome + "/" + KWMPath.ConfigFolder + "/init";

    struct stat Buffer;
    if(stat(InitFile.c_str(), &Buffer) == 0)
        KwmExecuteThreadedSystemCommand(InitFile);
}

internal void
SignalHandler(int Signum)
{
    ShowAllScratchpadWindows();
    DEBUG("SignalHandler() " << Signum);

    CloseBorder(&FocusedBorder);
    CloseBorder(&MarkedBorder);
    exit(Signum);
}

internal void
Fatal(const std::string &Err)
{
    std::cout << Err << std::endl;
    exit(1);
}

internal void
KwmInit()
{
    if(!CheckPrivileges())
        Fatal("Could not access OSX Accessibility!");

    if(KwmStartDaemon())
        pthread_create(&KWMThread.Daemon, NULL, &KwmDaemonHandleConnectionBG, NULL);
    else
        Fatal("Kwm: Could not start daemon..");

    /*
    signal(SIGSEGV, SignalHandler);
    signal(SIGABRT, SignalHandler);
    signal(SIGTRAP, SignalHandler);
    signal(SIGTERM, SignalHandler);
    signal(SIGKILL, SignalHandler);
    signal(SIGINT, SignalHandler);
    */

    KWMScreen.SplitRatio = 0.5;
    KWMScreen.SplitMode = SPLIT_OPTIMAL;
    KWMScreen.PrevSpace = -1;
    KWMScreen.DefaultOffset = CreateDefaultScreenOffset();
    KWMScreen.MaxCount = 5;
    KWMScreen.ActiveCount = 0;

    KWMToggles.EnableTilingMode = true;
    KWMToggles.UseBuiltinHotkeys = true;
    KWMToggles.UseMouseFollowsFocus = true;
    KWMTiling.OptimalRatio = 1.618;
    KWMTiling.LockToContainer = true;
    KWMTiling.MonitorWindows = true;

    KWMMode.Space = SpaceModeBSP;
    KWMMode.Focus = FocusModeAutoraise;
    KWMMode.Cycle = CycleModeScreen;

    FocusedBorder.Radius = -1;
    MarkedBorder.Radius = -1;

    KWMPath.ConfigFile = "kwmrc";
    KWMPath.ConfigFolder = ".kwm";
    KWMPath.BSPLayouts = "layouts";
    KWMHotkeys.ActiveMode = GetBindingMode("default");

    GetKwmFilePath();
    KwmExecuteConfig();
    KwmExecuteInitScript();
}

void KwmQuit()
{
    ShowAllScratchpadWindows();
    CloseBorder(&FocusedBorder);
    CloseBorder(&MarkedBorder);

    exit(0);
}

void KwmReloadConfig()
{
    KwmClearSettings();
    KwmExecuteConfig();
}

int main(int argc, char **argv)
{
    if(CheckArguments(argc, argv))
        return 0;

    KwmInit();
    KWMMach.EventMask = ((1 << kCGEventKeyDown) |
                         (1 << kCGEventMouseMoved));

    KWMMach.EventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, KWMMach.EventMask, CGEventCallback, NULL);
    if(!KWMMach.EventTap || !CGEventTapIsEnabled(KWMMach.EventTap))
        Fatal("ERROR: Could not create event-tap!");

    CFRunLoopAddSource(CFRunLoopGetMain(),
                       CFMachPortCreateRunLoopSource(kCFAllocatorDefault, KWMMach.EventTap, 0),
                       kCFRunLoopCommonModes);
    CGEventTapEnable(KWMMach.EventTap, true);

    /* NOTE(koekeishiya): Initialize AXLIB */
    AXLibInit(&AXState);
    AXLibStartEventLoop();

    ax_display *MainDisplay = AXLibMainDisplay();
    ax_display *Display = MainDisplay;
    do
    {
        ax_space *PrevSpace = Display->Space;
        Display->Space = AXLibGetActiveSpace(Display);
        Display->PrevSpace = PrevSpace;
        Display = AXLibNextDisplay(Display);
    } while(Display != MainDisplay);

    FocusedDisplay = MainDisplay;
    FocusedApplication = AXLibGetFocusedApplication();
    CreateWindowNodeTree(FocusedDisplay);
    /* ----------------------------------- */

    NSApplicationLoad();
    CFRunLoopRun();
    return 0;
}
<commit_msg>prevent focus-follows-mouse during space transitions<commit_after>#include "kwm.h"
#include "helpers.h"
#include "daemon.h"
#include "display.h"
#include "space.h"
#include "window.h"
#include "keys.h"
#include "interpreter.h"
#include "scratchpad.h"
#include "border.h"
#include "config.h"
#include "command.h"
#include "cursor.h"

#include "axlib/axlib.h"

#define internal static

const std::string KwmCurrentVersion = "Kwm Version 2.2.0";
ax_state AXState = {};
ax_application *FocusedApplication;
ax_display *FocusedDisplay;
std::map<CFStringRef, space_info> WindowTree;

kwm_mach KWMMach = {};
kwm_path KWMPath = {};
kwm_screen KWMScreen = {};
kwm_toggles KWMToggles = {};
kwm_focus KWMFocus = {};
kwm_mode KWMMode = {};
kwm_tiling KWMTiling = {};
kwm_thread KWMThread = {};
kwm_hotkeys KWMHotkeys = {};
kwm_border FocusedBorder = {};
kwm_border MarkedBorder = {};
scratchpad Scratchpad = {};

/* TODO(koekeishiya): Should probably be moved to a 'cursor.cpp' or similar in the future,
                      along with other cursor-related functionality we might want */
EVENT_CALLBACK(Callback_AXEvent_MouseMoved)
{
    if(!AXLibIsSpaceTransitionInProgress())
        FocusWindowBelowCursor();
}

CGEventRef CGEventCallback(CGEventTapProxy Proxy, CGEventType Type, CGEventRef Event, void *Refcon)
{
    switch(Type)
    {
        case kCGEventTapDisabledByTimeout:
        case kCGEventTapDisabledByUserInput:
        {
            if(!KWMMach.DisableEventTapInternal)
            {
                DEBUG("Restarting Event Tap");
                CGEventTapEnable(KWMMach.EventTap, true);
            }
        } break;
        case kCGEventKeyDown:
        {
            /* TODO(koekeishiya): Is there a better way to decide whether
                                  we should eat the CGEventRef or not (?) */
            if(KWMToggles.UseBuiltinHotkeys)
            {
                hotkey Eventkey = {}, *Hotkey = NULL;
                Hotkey = (hotkey *) calloc(1, sizeof(hotkey));
                if(Hotkey)
                {
                    CreateHotkeyFromCGEvent(Event, &Eventkey);
                    if(HotkeyExists(Eventkey.Mod, Eventkey.Key, Hotkey, KWMHotkeys.ActiveMode->Name))
                    {
                        AXLibConstructEvent(AXEvent_HotkeyPressed, Hotkey);
                        if(!Hotkey->Passthrough)
                            return NULL;
                    }
                }
            }
        } break;
        case kCGEventMouseMoved:
        {
            if(KWMMode.Focus == FocusModeAutoraise)
                AXLibConstructEvent(AXEvent_MouseMoved, NULL);
        } break;
        default: {} break;
    }

    return Event;
}

internal bool
CheckPrivileges()
{
    bool Result = false;
    const void * Keys[] = { kAXTrustedCheckOptionPrompt };
    const void * Values[] = { kCFBooleanTrue };

    CFDictionaryRef Options;
    Options = CFDictionaryCreate(kCFAllocatorDefault,
                                 Keys, Values, sizeof(Keys) / sizeof(*Keys),
                                 &kCFCopyStringDictionaryKeyCallBacks,
                                 &kCFTypeDictionaryValueCallBacks);

    Result = AXIsProcessTrustedWithOptions(Options);
    CFRelease(Options);

    return Result;
}

internal bool
CheckArguments(int argc, char **argv)
{
    bool Result = false;

    if(argc == 2)
    {
        std::string Arg = argv[1];
        if(Arg == "--version")
        {
            std::cout << KwmCurrentVersion << std::endl;
            Result = true;
        }
    }

    return Result;
}

internal bool
GetKwmFilePath()
{
    bool Result = false;
    char PathBuf[PROC_PIDPATHINFO_MAXSIZE];
    pid_t Pid = getpid();
    int Ret = proc_pidpath(Pid, PathBuf, sizeof(PathBuf));
    if (Ret > 0)
    {
        KWMPath.FilePath = PathBuf;

        std::size_t Split = KWMPath.FilePath.find_last_of("/\\");
        KWMPath.FilePath = KWMPath.FilePath.substr(0, Split);
        Result = true;
    }

    return Result;
}

internal void
KwmClearSettings()
{
    std::map<int, CFTypeRef>::iterator It;
    for(It = KWMTiling.AllowedWindowRoles.begin(); It != KWMTiling.AllowedWindowRoles.end(); ++It)
        CFRelease(It->second);

    KWMTiling.AllowedWindowRoles.clear();
    KWMHotkeys.Modes.clear();
    KWMTiling.WindowRules.clear();
    KWMTiling.SpaceSettings.clear();
    KWMTiling.DisplaySettings.clear();
    KWMTiling.EnforcedWindows.clear();
    KWMHotkeys.ActiveMode = GetBindingMode("default");
}

internal void
KwmExecuteConfig()
{
    char *HomeP = std::getenv("HOME");
    if(!HomeP)
    {
        DEBUG("Failed to get environment variable 'HOME'");
        return;
    }

    KWMPath.EnvHome = HomeP;
    KwmParseConfig(KWMPath.ConfigFile);
}

internal void
KwmExecuteInitScript()
{
    std::string InitFile = KWMPath.EnvHome + "/" + KWMPath.ConfigFolder + "/init";

    struct stat Buffer;
    if(stat(InitFile.c_str(), &Buffer) == 0)
        KwmExecuteThreadedSystemCommand(InitFile);
}

internal void
SignalHandler(int Signum)
{
    ShowAllScratchpadWindows();
    DEBUG("SignalHandler() " << Signum);

    CloseBorder(&FocusedBorder);
    CloseBorder(&MarkedBorder);
    exit(Signum);
}

internal void
Fatal(const std::string &Err)
{
    std::cout << Err << std::endl;
    exit(1);
}

internal void
KwmInit()
{
    if(!CheckPrivileges())
        Fatal("Could not access OSX Accessibility!");

    if(KwmStartDaemon())
        pthread_create(&KWMThread.Daemon, NULL, &KwmDaemonHandleConnectionBG, NULL);
    else
        Fatal("Kwm: Could not start daemon..");

    /*
    signal(SIGSEGV, SignalHandler);
    signal(SIGABRT, SignalHandler);
    signal(SIGTRAP, SignalHandler);
    signal(SIGTERM, SignalHandler);
    signal(SIGKILL, SignalHandler);
    signal(SIGINT, SignalHandler);
    */

    KWMScreen.SplitRatio = 0.5;
    KWMScreen.SplitMode = SPLIT_OPTIMAL;
    KWMScreen.PrevSpace = -1;
    KWMScreen.DefaultOffset = CreateDefaultScreenOffset();
    KWMScreen.MaxCount = 5;
    KWMScreen.ActiveCount = 0;

    KWMToggles.EnableTilingMode = true;
    KWMToggles.UseBuiltinHotkeys = true;
    KWMToggles.UseMouseFollowsFocus = true;
    KWMTiling.OptimalRatio = 1.618;
    KWMTiling.LockToContainer = true;
    KWMTiling.MonitorWindows = true;

    KWMMode.Space = SpaceModeBSP;
    KWMMode.Focus = FocusModeAutoraise;
    KWMMode.Cycle = CycleModeScreen;

    FocusedBorder.Radius = -1;
    MarkedBorder.Radius = -1;

    KWMPath.ConfigFile = "kwmrc";
    KWMPath.ConfigFolder = ".kwm";
    KWMPath.BSPLayouts = "layouts";
    KWMHotkeys.ActiveMode = GetBindingMode("default");

    GetKwmFilePath();
    KwmExecuteConfig();
    KwmExecuteInitScript();
}

void KwmQuit()
{
    ShowAllScratchpadWindows();
    CloseBorder(&FocusedBorder);
    CloseBorder(&MarkedBorder);

    exit(0);
}

void KwmReloadConfig()
{
    KwmClearSettings();
    KwmExecuteConfig();
}

int main(int argc, char **argv)
{
    if(CheckArguments(argc, argv))
        return 0;

    KwmInit();
    KWMMach.EventMask = ((1 << kCGEventKeyDown) |
                         (1 << kCGEventMouseMoved));

    KWMMach.EventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, KWMMach.EventMask, CGEventCallback, NULL);
    if(!KWMMach.EventTap || !CGEventTapIsEnabled(KWMMach.EventTap))
        Fatal("ERROR: Could not create event-tap!");

    CFRunLoopAddSource(CFRunLoopGetMain(),
                       CFMachPortCreateRunLoopSource(kCFAllocatorDefault, KWMMach.EventTap, 0),
                       kCFRunLoopCommonModes);
    CGEventTapEnable(KWMMach.EventTap, true);

    /* NOTE(koekeishiya): Initialize AXLIB */
    AXLibInit(&AXState);
    AXLibStartEventLoop();

    ax_display *MainDisplay = AXLibMainDisplay();
    ax_display *Display = MainDisplay;
    do
    {
        ax_space *PrevSpace = Display->Space;
        Display->Space = AXLibGetActiveSpace(Display);
        Display->PrevSpace = PrevSpace;
        Display = AXLibNextDisplay(Display);
    } while(Display != MainDisplay);

    FocusedDisplay = MainDisplay;
    FocusedApplication = AXLibGetFocusedApplication();
    CreateWindowNodeTree(FocusedDisplay);
    /* ----------------------------------- */

    NSApplicationLoad();
    CFRunLoopRun();
    return 0;
}
<|endoftext|>


<commit_before>#include "core.h"

Core::Core() : timer(ints), gpu(ints), cpu(mmu)
{
	mmu.register_chunk(0, 0x7FFF, cart.get_memory_controller());
	mmu.register_chunk(0x8000, 0x9FFF, &gpu); //vram
	mmu.register_chunk(0xA000, 0xBFFF, cart.get_memory_controller());
	mmu.register_chunk(0xC000, 0xFDFF, &ram);
	mmu.register_chunk(0xFE00, 0xFE9F, &gpu); //oam tables
	mmu.register_chunk(0xFF00, 0xFF00, &joypad);//input keys register
	mmu.register_chunk(0xFF01, 0xFF02, &tr); //TEST READER!!!!
	mmu.register_chunk(0xFF04, 0xFF07, &timer);//timer controls
	mmu.register_chunk(0xFF0F, 0xFF0F, &ints);//interrupts flags
	mmu.register_chunk(0xFF10, 0xFF3F, &apu); //APU registers + wave RAM
	mmu.register_chunk(0xFF40, 0xFF4B, &gpu); //gpu control regs
	mmu.register_chunk(0xFF4D, 0xFF4D, &speed); //CPU speed switch (CGB)
	mmu.register_chunk(0xFF4F, 0xFF4F, &gpu); //gpu vram bank reg (CGB)
	mmu.register_chunk(0xFF51, 0xFF55, &gpu); //gpu HDMA/GDMA regs (CGB)
	mmu.register_chunk(0xFF68, 0xFF6B, &gpu); //gpu color BGP/OBP regs (CGB)
	mmu.register_chunk(0xFF70, 0xFF70, &ram); //ram bank register (CGB)
	mmu.register_chunk(0xFF80, 0xFFFE, &ram); //high ram
	mmu.register_chunk(0xFFFF, 0xFFFF, &ints); //interrupts

	debugger.attach_mmu(make_function(&MMU::read_byte, &mmu), make_function(&MMU::write_byte, &mmu));
	debugger.attach_gpu(gpu.get_debug_func());

	cpu.attach_debugger(debugger.get_cpu());
	mmu.attach_debug_callback(make_function(&Debugger::check_memory_access, &debugger));
}

bool Core::load_cartrige(std::string file_name)
{
	//load ROM from file
	if (!cart.load_cartrige(file_name))
		return false;

	gpu.attach_dma_ptrs(cart.get_dma_controller(), &ram);

	bool enable_cgb = cart.is_cgb_ready();
	cpu.enable_cgb_mode(enable_cgb);
	gpu.enable_cgb_mode(enable_cgb);
	ram.enable_cgb_mode(enable_cgb);

	mmu.swap_chunk(0, 0x7FFF, cart.get_memory_controller());
	mmu.swap_chunk(0xA000, 0xBFFF, cart.get_memory_controller());

	return true;
}

void Core::load_state(std::istream& load_stream)
{
	ints.deserialize(load_stream);
	//mmu

	cpu.deserialize(load_stream);
	gpu.deserialize(load_stream);
	apu.deserialize(load_stream);

	timer.deserialize(load_stream);
	cart.deserialize(load_stream);
	ram.deserialize(load_stream);
	//serial port
	joypad.deserialize(load_stream);
	speed.deserialize(load_stream);
}

void Core::save_state(std::ostream& save_stream)
{
	ints.serialize(save_stream);
	//mmu

	cpu.serialize(save_stream);
	gpu.serialize(save_stream);
	apu.serialize(save_stream);

	timer.serialize(save_stream);
	cart.serialize(save_stream);
	ram.serialize(save_stream);
	//serial port
	joypad.serialize(save_stream);
	speed.serialize(save_stream);
}

void Core::run()
{
	bool spin = true;

	while (spin)
	{
		spin = pump_input_callback(joypad);

		while (!gpu.is_entering_vblank()) //TODO: if someone turn off lcd, this loop may spin forever
		{
			u32 sync_cycles = 0;

			if (ints.is_any_raised())
			{
				cpu.unhalt();

				if (cpu.is_interrupt_enabled())
					sync_cycles = cpu.handle_interrupt(ints.get_first_raised());
			}

			debugger.step();

			sync_cycles += cpu.step(sync_cycles);
			sync_cycles += gpu.step(sync_cycles);
			apu.step(sync_cycles);
			timer.step(sync_cycles);

			gpu.set_speed(speed.double_speed);
			apu.set_speed(speed.double_speed);
		}

		draw_frame_callback(gpu.get_frame_buffer());
		gpu.clear_frame_buffer();
		debugger.after_vblank();
	}
}

void Core::attach_callbacks(const external_callbacks& endpoints)
{
	apu.attach_endpoints(endpoints.swap_sample_buffer, endpoints.audio_control);

	draw_frame_callback = endpoints.draw_frame;
	pump_input_callback = endpoints.update_input;
}
<commit_msg>temporary fix for mmu chunk swapping<commit_after>#include "core.h"

Core::Core() : timer(ints), gpu(ints), cpu(mmu)
{
	mmu.register_chunk(0, 0x7FFF, cart.get_memory_controller());
	mmu.register_chunk(0x8000, 0x9FFF, &gpu); //vram
	mmu.register_chunk(0xA000, 0xBFFF, cart.get_memory_controller());
	mmu.register_chunk(0xC000, 0xFDFF, &ram);
	mmu.register_chunk(0xFE00, 0xFE9F, &gpu); //oam tables
	mmu.register_chunk(0xFF00, 0xFF00, &joypad);//input keys register
	mmu.register_chunk(0xFF01, 0xFF02, &tr); //TEST READER!!!!
	mmu.register_chunk(0xFF04, 0xFF07, &timer);//timer controls
	mmu.register_chunk(0xFF0F, 0xFF0F, &ints);//interrupts flags
	mmu.register_chunk(0xFF10, 0xFF3F, &apu); //APU registers + wave RAM
	mmu.register_chunk(0xFF40, 0xFF4B, &gpu); //gpu control regs
	mmu.register_chunk(0xFF4D, 0xFF4D, &speed); //CPU speed switch (CGB)
	mmu.register_chunk(0xFF4F, 0xFF4F, &gpu); //gpu vram bank reg (CGB)
	mmu.register_chunk(0xFF51, 0xFF55, &gpu); //gpu HDMA/GDMA regs (CGB)
	mmu.register_chunk(0xFF68, 0xFF6B, &gpu); //gpu color BGP/OBP regs (CGB)
	mmu.register_chunk(0xFF70, 0xFF70, &ram); //ram bank register (CGB)
	mmu.register_chunk(0xFF80, 0xFFFE, &ram); //high ram
	mmu.register_chunk(0xFFFF, 0xFFFF, &ints); //interrupts

	debugger.attach_mmu(make_function(&MMU::read_byte, &mmu), make_function(&MMU::write_byte, &mmu));
	debugger.attach_gpu(gpu.get_debug_func());

	cpu.attach_debugger(debugger.get_cpu());
	mmu.attach_debug_callback(make_function(&Debugger::check_memory_access, &debugger));
}

bool Core::load_cartrige(std::string file_name)
{
	//load ROM from file
	if (!cart.load_cartrige(file_name))
		return false;

	gpu.attach_dma_ptrs(cart.get_dma_controller(), &ram);

	bool enable_cgb = cart.is_cgb_ready();
	cpu.enable_cgb_mode(enable_cgb);
	gpu.enable_cgb_mode(enable_cgb);
	ram.enable_cgb_mode(enable_cgb);

	mmu.swap_chunk(0, 0x8000, cart.get_memory_controller()); //TODO: it`s inconsistent with register_chunk scheme of adressing
	mmu.swap_chunk(0xA000, 0xC000, cart.get_memory_controller());

	return true;
}

void Core::load_state(std::istream& load_stream)
{
	ints.deserialize(load_stream);
	//mmu

	cpu.deserialize(load_stream);
	gpu.deserialize(load_stream);
	apu.deserialize(load_stream);

	timer.deserialize(load_stream);
	cart.deserialize(load_stream);
	ram.deserialize(load_stream);
	//serial port
	joypad.deserialize(load_stream);
	speed.deserialize(load_stream);
}

void Core::save_state(std::ostream& save_stream)
{
	ints.serialize(save_stream);
	//mmu

	cpu.serialize(save_stream);
	gpu.serialize(save_stream);
	apu.serialize(save_stream);

	timer.serialize(save_stream);
	cart.serialize(save_stream);
	ram.serialize(save_stream);
	//serial port
	joypad.serialize(save_stream);
	speed.serialize(save_stream);
}

void Core::run()
{
	bool spin = true;

	while (spin)
	{
		spin = pump_input_callback(joypad);

		while (!gpu.is_entering_vblank()) //TODO: if someone turn off lcd, this loop may spin forever
		{
			u32 sync_cycles = 0;

			if (ints.is_any_raised())
			{
				cpu.unhalt();

				if (cpu.is_interrupt_enabled())
					sync_cycles = cpu.handle_interrupt(ints.get_first_raised());
			}

			debugger.step();

			sync_cycles += cpu.step(sync_cycles);
			sync_cycles += gpu.step(sync_cycles);
			apu.step(sync_cycles);
			timer.step(sync_cycles);

			gpu.set_speed(speed.double_speed);
			apu.set_speed(speed.double_speed);
		}

		draw_frame_callback(gpu.get_frame_buffer());
		gpu.clear_frame_buffer();
		debugger.after_vblank();
	}
}

void Core::attach_callbacks(const external_callbacks& endpoints)
{
	apu.attach_endpoints(endpoints.swap_sample_buffer, endpoints.audio_control);

	draw_frame_callback = endpoints.draw_frame;
	pump_input_callback = endpoints.update_input;
}
<|endoftext|>


<commit_before>#include "kwm.h"
#include "helpers.h"
#include "daemon.h"
#include "display.h"
#include "space.h"
#include "window.h"
#include "keys.h"
#include "interpreter.h"
#include "border.h"

const std::string KwmCurrentVersion = "Kwm Version 1.1.1";
const std::string PlistFile = "com.koekeishiya.kwm.plist";

kwm_mach KWMMach = {};
kwm_path KWMPath = {};
kwm_screen KWMScreen = {};
kwm_toggles KWMToggles = {};
kwm_focus KWMFocus = {};
kwm_mode KWMMode = {};
kwm_tiling KWMTiling = {};
kwm_cache KWMCache = {};
kwm_thread KWMThread = {};
kwm_hotkeys KWMHotkeys = {};
kwm_border FocusedBorder = {};
kwm_border MarkedBorder = {};
kwm_border PrefixBorder = {};

CGEventRef CGEventCallback(CGEventTapProxy Proxy, CGEventType Type, CGEventRef Event, void *Refcon)
{
    pthread_mutex_lock(&KWMThread.Lock);

    switch(Type)
    {
        case kCGEventTapDisabledByTimeout:
        case kCGEventTapDisabledByUserInput:
        {
            DEBUG("Restarting Event Tap")
            CGEventTapEnable(KWMMach.EventTap, true);
        } break;
        case kCGEventKeyDown:
        {
            if(KWMToggles.UseBuiltinHotkeys && KwmMainHotkeyTrigger(&Event))
            {
                    pthread_mutex_unlock(&KWMThread.Lock);
                    return NULL;
            }

            if(KWMMode.Focus == FocusModeAutofocus &&
               !IsActiveSpaceFloating())
            {
                CGEventSetIntegerValueField(Event, kCGKeyboardEventAutorepeat, 0);
                CGEventPostToPSN(&KWMFocus.PSN, Event);
                pthread_mutex_unlock(&KWMThread.Lock);
                return NULL;
            }
        } break;
        case kCGEventKeyUp:
        {
            if(KWMMode.Focus == FocusModeAutofocus &&
               !IsActiveSpaceFloating())
            {
                CGEventSetIntegerValueField(Event, kCGKeyboardEventAutorepeat, 0);
                CGEventPostToPSN(&KWMFocus.PSN, Event);
                pthread_mutex_unlock(&KWMThread.Lock);
                return NULL;
            }
        } break;
        case kCGEventMouseMoved:
        {
            UpdateActiveScreen();

            if(KWMMode.Focus != FocusModeDisabled &&
               KWMMode.Focus != FocusModeStandby &&
               !IsActiveSpaceFloating())
                FocusWindowBelowCursor();
        } break;
        case kCGEventLeftMouseDown:
        {
            DEBUG("Left mouse button was pressed")
            if(!IsActiveSpaceFloating())
            {
                FocusWindowBelowCursor();
                if(KWMToggles.EnableDragAndDrop)
                    KWMToggles.DragInProgress = true;
            }
        } break;
        case kCGEventLeftMouseUp:
        {
            DEBUG("Left mouse button was released")
            if(!IsActiveSpaceFloating())
            {
                if(KWMToggles.EnableDragAndDrop && KWMToggles.DragInProgress)
                    KWMToggles.DragInProgress = false;

                if(KWMFocus.Window && FocusedBorder.Enabled)
                {
                    if(IsWindowFloating(KWMFocus.Window->WID, NULL))
                        UpdateBorder("focused");
                }
            }
        } break;
    }

    pthread_mutex_unlock(&KWMThread.Lock);
    return Event;
}

void KwmQuit()
{
    CloseBorder(&FocusedBorder);
    CloseBorder(&MarkedBorder);
    CloseBorder(&PrefixBorder);

    exit(0);
}

void * KwmWindowMonitor(void*)
{
    while(1)
    {
        pthread_mutex_lock(&KWMThread.Lock);
        CheckPrefixTimeout();

        if(!IsSpaceTransitionInProgress())
            UpdateWindowTree();

        pthread_mutex_unlock(&KWMThread.Lock);
        usleep(100000);
    }
}

void KwmReloadConfig()
{
    KwmClearSettings();
    KwmExecuteConfig();
}

void KwmClearSettings()
{
    std::map<std::string, std::vector<CFTypeRef> >::iterator It;
    for(It = KWMTiling.AllowedWindowRoles.begin(); It != KWMTiling.AllowedWindowRoles.end(); ++It)
    {
        std::vector<CFTypeRef> &WindowRoles = It->second;
        for(std::size_t RoleIndex = 0; RoleIndex < WindowRoles.size(); ++RoleIndex)
            CFRelease(WindowRoles[RoleIndex]);

        WindowRoles.clear();
    }

    KWMTiling.FloatingAppLst.clear();
    KWMHotkeys.List.clear();
    KWMHotkeys.Prefix.Enabled = false;
}

void KwmExecuteConfig()
{
    char *HomeP = std::getenv("HOME");
    if(!HomeP)
    {
        DEBUG("Failed to get environment variable 'HOME'")
        return;
    }

    KWMPath.EnvHome = HomeP;
    KWMPath.ConfigFolder = ".kwm";
    KwmExecuteFile(KWMPath.ConfigFile);
}

void KwmExecuteFile(std::string File)
{
    std::ifstream FileHandle(KWMPath.EnvHome + "/" + KWMPath.ConfigFolder + "/" + File);
    if(FileHandle.fail())
    {
        DEBUG("Could not open " << KWMPath.EnvHome << "/" << KWMPath.ConfigFolder << "/" << File
              << ", make sure the file exists." << std::endl)
        return;
    }

    std::string Line;
    while(std::getline(FileHandle, Line))
    {
        if(!Line.empty() && Line[0] != '#')
        {
            if(IsPrefixOfString(Line, "kwmc"))
                KwmInterpretCommand(Line, 0);
            else if(IsPrefixOfString(Line, "sys"))
                KwmExecuteThreadedSystemCommand(Line);
            else if(IsPrefixOfString(Line, "include"))
                KwmExecuteFile(Line);
        }
    }

    FileHandle.close();
}

void KwmExecuteSystemCommand(std::string Command)
{
    system(Command.c_str());
}

void KwmExecuteThreadedSystemCommand(std::string Command)
{
    std::string *HCommand = new std::string(Command);
    pthread_create(&KWMThread.SystemCommand, NULL, &KwmStartThreadedSystemCommand, HCommand);
}

void * KwmStartThreadedSystemCommand(void *Args)
{
    std::string Command = *((std::string*)Args);
    std::cout << Command << std::endl;
    KwmExecuteSystemCommand(Command);
    delete (std::string*)Args;
    return NULL;
}

bool IsKwmAlreadyAddedToLaunchd()
{
    std::string SymlinkFullPath = KWMPath.EnvHome + "/Library/LaunchAgents/" + PlistFile;

    struct stat attr;
    int Result = stat(SymlinkFullPath.c_str(), &attr);
    if(Result == -1)
        return false;

    return true;
}

void AddKwmToLaunchd()
{
    if(IsKwmAlreadyAddedToLaunchd())
        return;

    std::string SymlinkFullPath = KWMPath.EnvHome + "/Library/LaunchAgents/" + PlistFile;

    std::ifstream TemplateFD(KWMPath.FilePath + "/kwm_template.plist");
    if(TemplateFD.fail())
        return;

    std::ofstream OutFD(SymlinkFullPath);
    if(OutFD.fail())
        return;

    std::string Line;
    std::vector<std::string> PlistContents;
    while(std::getline(TemplateFD, Line))
        PlistContents.push_back(Line);

    DEBUG("AddKwmToLaunchd() Creating file: " << SymlinkFullPath)
    for(std::size_t LineNumber = 0; LineNumber < PlistContents.size(); ++LineNumber)
    {
        if(LineNumber == 8)
            OutFD << "    <string>" + KWMPath.FilePath + "/kwm</string>" << std::endl;
        else
            OutFD << PlistContents[LineNumber] << std::endl;
    }

    TemplateFD.close();
    OutFD.close();
}

void RemoveKwmFromLaunchd()
{
    if(!IsKwmAlreadyAddedToLaunchd())
        return;

    std::string SymlinkFullPath = KWMPath.EnvHome + "/Library/LaunchAgents/" + PlistFile;
    std::string RemoveSymlink = "rm " + SymlinkFullPath;

    system(RemoveSymlink.c_str());
    DEBUG("RemoveKwmFromLaunchd() Removing file: " << SymlinkFullPath)
}

bool GetKwmFilePath()
{
    bool Result = false;
    char PathBuf[PROC_PIDPATHINFO_MAXSIZE];
    pid_t Pid = getpid();
    int Ret = proc_pidpath(Pid, PathBuf, sizeof(PathBuf));
    if (Ret > 0)
    {
        KWMPath.FilePath = PathBuf;

        std::size_t Split = KWMPath.FilePath.find_last_of("/\\");
        KWMPath.FilePath = KWMPath.FilePath.substr(0, Split);
        KWMPath.HotkeySOFullPath = KWMPath.FilePath + "/hotkeys.so";
        Result = true;
    }

    return Result;
}

void KwmInit()
{
    if(!CheckPrivileges())
        Fatal("Could not access OSX Accessibility!");

    if (pthread_mutex_init(&KWMThread.Lock, NULL) != 0)
        Fatal("Could not create mutex!");

    if(KwmStartDaemon())
        pthread_create(&KWMThread.Daemon, NULL, &KwmDaemonHandleConnectionBG, NULL);
    else
        Fatal("Kwm: Could not start daemon..");

    signal(SIGSEGV, SignalHandler);

    KWMScreen.SplitRatio = 0.5;
    KWMScreen.SplitMode = -1;
    KWMScreen.MarkedWindow = -1;
    KWMScreen.PrevSpace = -1;
    KWMScreen.DefaultOffset = CreateDefaultScreenOffset();
    KWMScreen.MaxCount = 5;
    KWMScreen.ActiveCount = 0;

    KWMToggles.EnableTilingMode = true;
    KWMToggles.UseBuiltinHotkeys = true;
    KWMToggles.EnableDragAndDrop = true;
    KWMToggles.UseMouseFollowsFocus = true;

    KWMMode.Space = SpaceModeBSP;
    KWMMode.Focus = FocusModeAutoraise;
    KWMMode.Cycle = CycleModeScreen;

    KWMHotkeys.Prefix.Enabled = false;
    KWMHotkeys.Prefix.Global = false;
    KWMHotkeys.Prefix.Active = false;
    KWMHotkeys.Prefix.Timeout = 0.75;

    KWMPath.ConfigFile = "kwmrc";
    GetKwmFilePath();

    KwmExecuteConfig();
    GetActiveDisplays();

    pthread_create(&KWMThread.WindowMonitor, NULL, &KwmWindowMonitor, NULL);
}

bool CheckPrivileges()
{
    bool Result = false;
    const void * Keys[] = { kAXTrustedCheckOptionPrompt };
    const void * Values[] = { kCFBooleanTrue };

    CFDictionaryRef Options;
    Options = CFDictionaryCreate(kCFAllocatorDefault,
            Keys, Values, sizeof(Keys) / sizeof(*Keys),
            &kCFCopyStringDictionaryKeyCallBacks,
            &kCFTypeDictionaryValueCallBacks);

    Result = AXIsProcessTrustedWithOptions(Options);
    CFRelease(Options);

    return Result;
}

bool CheckArguments(int argc, char **argv)
{
    bool Result = false;

    if(argc == 2)
    {
        std::string Arg = argv[1];
        if(Arg == "--version")
        {
            std::cout << KwmCurrentVersion << std::endl;
            Result = true;
        }
    }

    return Result;
}

void SignalHandler(int Signum)
{
    CloseBorder(&FocusedBorder);
    CloseBorder(&MarkedBorder);
    CloseBorder(&PrefixBorder);

    signal(Signum, SIG_DFL);
    kill(getpid(), Signum);
}

void Fatal(const std::string &Err)
{
    std::cout << Err << std::endl;
    exit(1);
}

int main(int argc, char **argv)
{
    if(CheckArguments(argc, argv))
        return 0;

    KwmInit();
    KWMMach.EventMask = ((1 << kCGEventKeyDown) |
                         (1 << kCGEventKeyUp) |
                         (1 << kCGEventMouseMoved) |
                         (1 << kCGEventLeftMouseDown) |
                         (1 << kCGEventLeftMouseUp));

    KWMMach.EventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, 0, KWMMach.EventMask, CGEventCallback, NULL);
    if(!KWMMach.EventTap || !CGEventTapIsEnabled(KWMMach.EventTap))
        Fatal("ERROR: Could not create event-tap!");

    CFRunLoopAddSource(CFRunLoopGetCurrent(),
                       CFMachPortCreateRunLoopSource(kCFAllocatorDefault, KWMMach.EventTap, 0),
                       kCFRunLoopCommonModes);

    CGEventTapEnable(KWMMach.EventTap, true);
    CreateWorkspaceWatcher(KWMMach.WorkspaceWatcher);

    NSApplicationLoad();
    CFRunLoopRun();
    return 0;
}
<commit_msg>reduce timeout<commit_after>#include "kwm.h"
#include "helpers.h"
#include "daemon.h"
#include "display.h"
#include "space.h"
#include "window.h"
#include "keys.h"
#include "interpreter.h"
#include "border.h"

const std::string KwmCurrentVersion = "Kwm Version 1.1.1";
const std::string PlistFile = "com.koekeishiya.kwm.plist";

kwm_mach KWMMach = {};
kwm_path KWMPath = {};
kwm_screen KWMScreen = {};
kwm_toggles KWMToggles = {};
kwm_focus KWMFocus = {};
kwm_mode KWMMode = {};
kwm_tiling KWMTiling = {};
kwm_cache KWMCache = {};
kwm_thread KWMThread = {};
kwm_hotkeys KWMHotkeys = {};
kwm_border FocusedBorder = {};
kwm_border MarkedBorder = {};
kwm_border PrefixBorder = {};

CGEventRef CGEventCallback(CGEventTapProxy Proxy, CGEventType Type, CGEventRef Event, void *Refcon)
{
    pthread_mutex_lock(&KWMThread.Lock);

    switch(Type)
    {
        case kCGEventTapDisabledByTimeout:
        case kCGEventTapDisabledByUserInput:
        {
            DEBUG("Restarting Event Tap")
            CGEventTapEnable(KWMMach.EventTap, true);
        } break;
        case kCGEventKeyDown:
        {
            if(KWMToggles.UseBuiltinHotkeys && KwmMainHotkeyTrigger(&Event))
            {
                    pthread_mutex_unlock(&KWMThread.Lock);
                    return NULL;
            }

            if(KWMMode.Focus == FocusModeAutofocus &&
               !IsActiveSpaceFloating())
            {
                CGEventSetIntegerValueField(Event, kCGKeyboardEventAutorepeat, 0);
                CGEventPostToPSN(&KWMFocus.PSN, Event);
                pthread_mutex_unlock(&KWMThread.Lock);
                return NULL;
            }
        } break;
        case kCGEventKeyUp:
        {
            if(KWMMode.Focus == FocusModeAutofocus &&
               !IsActiveSpaceFloating())
            {
                CGEventSetIntegerValueField(Event, kCGKeyboardEventAutorepeat, 0);
                CGEventPostToPSN(&KWMFocus.PSN, Event);
                pthread_mutex_unlock(&KWMThread.Lock);
                return NULL;
            }
        } break;
        case kCGEventMouseMoved:
        {
            UpdateActiveScreen();

            if(KWMMode.Focus != FocusModeDisabled &&
               KWMMode.Focus != FocusModeStandby &&
               !IsActiveSpaceFloating())
                FocusWindowBelowCursor();
        } break;
        case kCGEventLeftMouseDown:
        {
            DEBUG("Left mouse button was pressed")
            if(!IsActiveSpaceFloating())
            {
                FocusWindowBelowCursor();
                if(KWMToggles.EnableDragAndDrop)
                    KWMToggles.DragInProgress = true;
            }
        } break;
        case kCGEventLeftMouseUp:
        {
            DEBUG("Left mouse button was released")
            if(!IsActiveSpaceFloating())
            {
                if(KWMToggles.EnableDragAndDrop && KWMToggles.DragInProgress)
                    KWMToggles.DragInProgress = false;

                if(KWMFocus.Window && FocusedBorder.Enabled)
                {
                    if(IsWindowFloating(KWMFocus.Window->WID, NULL))
                        UpdateBorder("focused");
                }
            }
        } break;
    }

    pthread_mutex_unlock(&KWMThread.Lock);
    return Event;
}

void KwmQuit()
{
    CloseBorder(&FocusedBorder);
    CloseBorder(&MarkedBorder);
    CloseBorder(&PrefixBorder);

    exit(0);
}

void * KwmWindowMonitor(void*)
{
    while(1)
    {
        pthread_mutex_lock(&KWMThread.Lock);
        CheckPrefixTimeout();

        if(!IsSpaceTransitionInProgress())
            UpdateWindowTree();

        pthread_mutex_unlock(&KWMThread.Lock);
        usleep(50000);
    }
}

void KwmReloadConfig()
{
    KwmClearSettings();
    KwmExecuteConfig();
}

void KwmClearSettings()
{
    std::map<std::string, std::vector<CFTypeRef> >::iterator It;
    for(It = KWMTiling.AllowedWindowRoles.begin(); It != KWMTiling.AllowedWindowRoles.end(); ++It)
    {
        std::vector<CFTypeRef> &WindowRoles = It->second;
        for(std::size_t RoleIndex = 0; RoleIndex < WindowRoles.size(); ++RoleIndex)
            CFRelease(WindowRoles[RoleIndex]);

        WindowRoles.clear();
    }

    KWMTiling.FloatingAppLst.clear();
    KWMHotkeys.List.clear();
    KWMHotkeys.Prefix.Enabled = false;
}

void KwmExecuteConfig()
{
    char *HomeP = std::getenv("HOME");
    if(!HomeP)
    {
        DEBUG("Failed to get environment variable 'HOME'")
        return;
    }

    KWMPath.EnvHome = HomeP;
    KWMPath.ConfigFolder = ".kwm";
    KwmExecuteFile(KWMPath.ConfigFile);
}

void KwmExecuteFile(std::string File)
{
    std::ifstream FileHandle(KWMPath.EnvHome + "/" + KWMPath.ConfigFolder + "/" + File);
    if(FileHandle.fail())
    {
        DEBUG("Could not open " << KWMPath.EnvHome << "/" << KWMPath.ConfigFolder << "/" << File
              << ", make sure the file exists." << std::endl)
        return;
    }

    std::string Line;
    while(std::getline(FileHandle, Line))
    {
        if(!Line.empty() && Line[0] != '#')
        {
            if(IsPrefixOfString(Line, "kwmc"))
                KwmInterpretCommand(Line, 0);
            else if(IsPrefixOfString(Line, "sys"))
                KwmExecuteThreadedSystemCommand(Line);
            else if(IsPrefixOfString(Line, "include"))
                KwmExecuteFile(Line);
        }
    }

    FileHandle.close();
}

void KwmExecuteSystemCommand(std::string Command)
{
    system(Command.c_str());
}

void KwmExecuteThreadedSystemCommand(std::string Command)
{
    std::string *HCommand = new std::string(Command);
    pthread_create(&KWMThread.SystemCommand, NULL, &KwmStartThreadedSystemCommand, HCommand);
}

void * KwmStartThreadedSystemCommand(void *Args)
{
    std::string Command = *((std::string*)Args);
    std::cout << Command << std::endl;
    KwmExecuteSystemCommand(Command);
    delete (std::string*)Args;
    return NULL;
}

bool IsKwmAlreadyAddedToLaunchd()
{
    std::string SymlinkFullPath = KWMPath.EnvHome + "/Library/LaunchAgents/" + PlistFile;

    struct stat attr;
    int Result = stat(SymlinkFullPath.c_str(), &attr);
    if(Result == -1)
        return false;

    return true;
}

void AddKwmToLaunchd()
{
    if(IsKwmAlreadyAddedToLaunchd())
        return;

    std::string SymlinkFullPath = KWMPath.EnvHome + "/Library/LaunchAgents/" + PlistFile;

    std::ifstream TemplateFD(KWMPath.FilePath + "/kwm_template.plist");
    if(TemplateFD.fail())
        return;

    std::ofstream OutFD(SymlinkFullPath);
    if(OutFD.fail())
        return;

    std::string Line;
    std::vector<std::string> PlistContents;
    while(std::getline(TemplateFD, Line))
        PlistContents.push_back(Line);

    DEBUG("AddKwmToLaunchd() Creating file: " << SymlinkFullPath)
    for(std::size_t LineNumber = 0; LineNumber < PlistContents.size(); ++LineNumber)
    {
        if(LineNumber == 8)
            OutFD << "    <string>" + KWMPath.FilePath + "/kwm</string>" << std::endl;
        else
            OutFD << PlistContents[LineNumber] << std::endl;
    }

    TemplateFD.close();
    OutFD.close();
}

void RemoveKwmFromLaunchd()
{
    if(!IsKwmAlreadyAddedToLaunchd())
        return;

    std::string SymlinkFullPath = KWMPath.EnvHome + "/Library/LaunchAgents/" + PlistFile;
    std::string RemoveSymlink = "rm " + SymlinkFullPath;

    system(RemoveSymlink.c_str());
    DEBUG("RemoveKwmFromLaunchd() Removing file: " << SymlinkFullPath)
}

bool GetKwmFilePath()
{
    bool Result = false;
    char PathBuf[PROC_PIDPATHINFO_MAXSIZE];
    pid_t Pid = getpid();
    int Ret = proc_pidpath(Pid, PathBuf, sizeof(PathBuf));
    if (Ret > 0)
    {
        KWMPath.FilePath = PathBuf;

        std::size_t Split = KWMPath.FilePath.find_last_of("/\\");
        KWMPath.FilePath = KWMPath.FilePath.substr(0, Split);
        KWMPath.HotkeySOFullPath = KWMPath.FilePath + "/hotkeys.so";
        Result = true;
    }

    return Result;
}

void KwmInit()
{
    if(!CheckPrivileges())
        Fatal("Could not access OSX Accessibility!");

    if (pthread_mutex_init(&KWMThread.Lock, NULL) != 0)
        Fatal("Could not create mutex!");

    if(KwmStartDaemon())
        pthread_create(&KWMThread.Daemon, NULL, &KwmDaemonHandleConnectionBG, NULL);
    else
        Fatal("Kwm: Could not start daemon..");

    signal(SIGSEGV, SignalHandler);

    KWMScreen.SplitRatio = 0.5;
    KWMScreen.SplitMode = -1;
    KWMScreen.MarkedWindow = -1;
    KWMScreen.PrevSpace = -1;
    KWMScreen.DefaultOffset = CreateDefaultScreenOffset();
    KWMScreen.MaxCount = 5;
    KWMScreen.ActiveCount = 0;

    KWMToggles.EnableTilingMode = true;
    KWMToggles.UseBuiltinHotkeys = true;
    KWMToggles.EnableDragAndDrop = true;
    KWMToggles.UseMouseFollowsFocus = true;

    KWMMode.Space = SpaceModeBSP;
    KWMMode.Focus = FocusModeAutoraise;
    KWMMode.Cycle = CycleModeScreen;

    KWMHotkeys.Prefix.Enabled = false;
    KWMHotkeys.Prefix.Global = false;
    KWMHotkeys.Prefix.Active = false;
    KWMHotkeys.Prefix.Timeout = 0.75;

    KWMPath.ConfigFile = "kwmrc";
    GetKwmFilePath();

    KwmExecuteConfig();
    GetActiveDisplays();

    pthread_create(&KWMThread.WindowMonitor, NULL, &KwmWindowMonitor, NULL);
}

bool CheckPrivileges()
{
    bool Result = false;
    const void * Keys[] = { kAXTrustedCheckOptionPrompt };
    const void * Values[] = { kCFBooleanTrue };

    CFDictionaryRef Options;
    Options = CFDictionaryCreate(kCFAllocatorDefault,
            Keys, Values, sizeof(Keys) / sizeof(*Keys),
            &kCFCopyStringDictionaryKeyCallBacks,
            &kCFTypeDictionaryValueCallBacks);

    Result = AXIsProcessTrustedWithOptions(Options);
    CFRelease(Options);

    return Result;
}

bool CheckArguments(int argc, char **argv)
{
    bool Result = false;

    if(argc == 2)
    {
        std::string Arg = argv[1];
        if(Arg == "--version")
        {
            std::cout << KwmCurrentVersion << std::endl;
            Result = true;
        }
    }

    return Result;
}

void SignalHandler(int Signum)
{
    CloseBorder(&FocusedBorder);
    CloseBorder(&MarkedBorder);
    CloseBorder(&PrefixBorder);

    signal(Signum, SIG_DFL);
    kill(getpid(), Signum);
}

void Fatal(const std::string &Err)
{
    std::cout << Err << std::endl;
    exit(1);
}

int main(int argc, char **argv)
{
    if(CheckArguments(argc, argv))
        return 0;

    KwmInit();
    KWMMach.EventMask = ((1 << kCGEventKeyDown) |
                         (1 << kCGEventKeyUp) |
                         (1 << kCGEventMouseMoved) |
                         (1 << kCGEventLeftMouseDown) |
                         (1 << kCGEventLeftMouseUp));

    KWMMach.EventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, 0, KWMMach.EventMask, CGEventCallback, NULL);
    if(!KWMMach.EventTap || !CGEventTapIsEnabled(KWMMach.EventTap))
        Fatal("ERROR: Could not create event-tap!");

    CFRunLoopAddSource(CFRunLoopGetCurrent(),
                       CFMachPortCreateRunLoopSource(kCFAllocatorDefault, KWMMach.EventTap, 0),
                       kCFRunLoopCommonModes);

    CGEventTapEnable(KWMMach.EventTap, true);
    CreateWorkspaceWatcher(KWMMach.WorkspaceWatcher);

    NSApplicationLoad();
    CFRunLoopRun();
    return 0;
}
<|endoftext|>


<commit_before>#include <fstream>
#include <iostream>
#include <vector>

#include <sys/mman.h>
#include <cstring>

#include "brainfuck.h"

void* alloc_writable_memory(size_t size) {
  void* ptr = mmap(0, size,
                   PROT_READ | PROT_WRITE,
                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
  if (ptr == (void*)-1) {
    perror("mmap");
    return NULL;
  }
  return ptr;
}

// Sets a RX permission on the given memory, which must be page-aligned. Returns
// 0 on success. On failure, prints out the error and returns -1.
int make_memory_executable(void* m, size_t size) {
  if (mprotect(m, size, PROT_READ | PROT_EXEC) == -1) {
    perror("mprotect");
    return -1;
  }
  return 0;
}

class JITProgram
{
private:
    unsigned char *program_, *ptr_;
    unsigned int mem[30000];

public:
    JITProgram() {
        program_ = (unsigned char*)alloc_writable_memory(1000000);
        ptr_ = program_;
        memset(mem, 0x00, sizeof(mem));
    }

    unsigned char* get_ptr () const { return ptr_; }
    void set_ptr (unsigned char* ptr) { ptr_ = ptr; }

    void writeb(unsigned char byte) {
        (*ptr_++) = byte;
    }

    void writel(ssize_t value) {
        writes((unsigned char*)&value, 4);
    }

    void writes(const unsigned char* bytes, ssize_t size) {
        memcpy(ptr_, bytes, size);
        ptr_ += size;
    }

    void movq_rax(ssize_t value) {
        // 0: 48 c7 c0 01 00 00 00          movq    $1, %rax
        writes((unsigned char*)"\x48\xc7\xc0", 3);
        writel(value);
    }

    void start() {
        // 0000000000000070 break:
        //       70: cc                            int3
        // writeb(0xcc);
    }

    void finish() {
        // 0000000000000071 finish:
        //       71: c3                            retq
        writeb(0xc3);
    }

    void run() {
        make_memory_executable(program_, 1000000);

        // asm("int $3");

        asm("movq %0, %%rdi"
            :
            : "r"(&mem)
            : "%rdi");

        ((void (*)())program_)();
    }
};

class JITVisitor : public ExpressionVisitor
{
private:
    JITProgram &program_;
    std::vector<unsigned char*> stack;

public:
    JITVisitor(JITProgram& program) : program_(program), stack() {}

    virtual void visit(const Increment& inc) {
        // 0000000000000000 increment:
        //        0: 48 c7 c0 01 00 00 00          movq    $1, %rax
        //        7: 01 07                         addl    %eax, (%rdi)
        program_.movq_rax(inc.offset());
        program_.writes((unsigned char*)"\x01\x07", 2);
    }

    virtual void visit(const Decrement& dec) {
        // 0000000000000009 decrement:
        //        9: 48 c7 c0 01 00 00 00          movq    $1, %rax
        //       10: 29 07                         subl    %eax, (%rdi)
        program_.movq_rax(dec.offset());
        program_.writes((unsigned char*)"\x29\x07", 2);
    }

    virtual void visit(const Forward& fwd) {
        // 0000000000000012 forward:
        //       12: 48 c7 c0 01 00 00 00          movq    $1, %rax
        //       19: 48 01 c7                      addq    %rax, %rdi
        program_.movq_rax(fwd.offset()*4);
        program_.writes((unsigned char*)"\x48\x01\xc7", 3);
    }

    virtual void visit(const Backward& bwd) {
        // 000000000000001c backward:
        //       1c: 48 c7 c0 01 00 00 00          movq    $1, %rax
        //       23: 48 29 c7                      subq    %rax, %rdi
        program_.movq_rax(bwd.offset()*4);
        program_.writes((unsigned char*)"\x48\x29\xc7", 3);
    }

    virtual void visit(const Input&) {
        // 0000000000000026 read:
        //       26: 57                            pushq   %rdi
        //       27: 48 c7 c0 03 00 00 02          movq    $33554435, %rax
        //       2e: 48 89 fe                      movq    %rdi, %rsi
        //       31: 48 c7 c7 01 00 00 00          movq    $1, %rdi
        //       38: 48 c7 c2 01 00 00 00          movq    $1, %rdx
        //       3f: 0f 05                         syscall
        //       41: 5f                            popq    %rdi

        program_.writeb(0x57);
        program_.movq_rax(0x02000003);
        program_.writes((unsigned char*)"\x48\x89\xfe", 3);
        program_.writes((unsigned char*)"\x48\xc7\xc7\x00\x00\x00\x00", 7);
        program_.writes((unsigned char*)"\x48\xc7\xc2\x01\x00\x00\x00", 7);
        program_.writes((unsigned char*)"\x0f\x05", 2);
        program_.writeb(0x5f);
    }

    virtual void visit(const Output&) {
        // 0000000000000042 write:
        //       42: 57                            pushq   %rdi
        //       43: 48 c7 c0 04 00 00 02          movq    $33554436, %rax
        //       4a: 48 89 fe                      movq    %rdi, %rsi
        //       4d: 48 c7 c7 01 00 00 00          movq    $1, %rdi
        //       54: 48 c7 c2 01 00 00 00          movq    $1, %rdx
        //       5b: 0f 05                         syscall
        //       5d: 5f                            popq    %rdi

        program_.writeb(0x57);
        program_.movq_rax(0x02000004);
        program_.writes((unsigned char*)"\x48\x89\xfe", 3);
        program_.writes((unsigned char*)"\x48\xc7\xc7\x01\x00\x00\x00", 7);
        program_.writes((unsigned char*)"\x48\xc7\xc2\x01\x00\x00\x00", 7);
        program_.writes((unsigned char*)"\x0f\x05", 2);
        program_.writeb(0x5f);
    }

    virtual void visit(const Loop& loop) {
        // 000000000000005e loop_start:
        //       5e: 83 3f 00                      cmpl    $0, (%rdi)
        //       61: 0f 84 00 00 00 00             je  0
        program_.writes((unsigned char*)"\x83\x3f\x00", 3);
        program_.writes((unsigned char*)"\x0f\x84", 2);
        program_.writel(0); // reserve 4 bytes

        // push current position to stack
        stack.push_back(program_.get_ptr());

        // recurse into subexpressions:
        this->visit(loop.children());

        // recover last position
        unsigned char* after_loop_start = stack.back();
        stack.pop_back();

        // 0000000000000067 loop_end:
        //       67: 83 3f 00                      cmpl    $0, (%rdi)
        //       6a: 0f 85 00 00 00 00             jne 0
        program_.writes((unsigned char*)"\x83\x3f\x00", 3);
        program_.writes((unsigned char*)"\x0f\x85", 2);
        // calculate how much to jump back (consider the 4 bytes
        // of the operand itself):
        ssize_t jump_back = after_loop_start - program_.get_ptr() - 4;
        // append the distance to the jump:
        program_.writel(jump_back);

        // now calculate how much to jump forward in case we want
        // to skip the loop, to fill the pending jump:
        unsigned char *after_loop_end = program_.get_ptr();
        ssize_t jump_fwd = after_loop_end - after_loop_start;

        // go back to the original position (-4), fill the pending
        // jump distance and get back to the the current pos:
        program_.set_ptr(after_loop_start - 4);
        program_.writel(jump_fwd);
        program_.set_ptr(after_loop_end);
    }

    void visit(const ExpressionVector& expressions) {
        for(const auto &expression: expressions) {
            expression->accept(*this);
        }
    }
};

int main(int argc, char *argv[]) {
    std::ifstream ifs(argv[1]);

    if (!ifs) {
        std::cerr << "Invalid filename!" << std::endl;
        return 1;
    }

    std::vector<char> program;

    std::copy(
        (std::istreambuf_iterator<char>(ifs)),
        (std::istreambuf_iterator<char>()),
        std::back_inserter(program)
    );

    try {
        auto expressions = Parser().parse(program);
        JITProgram jit_program;
        jit_program.start();
        JITVisitor visitor(jit_program);
        visitor.visit(expressions);
        jit_program.finish();
        jit_program.run();
    } catch (std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }

    return 0;
}
<commit_msg>JIT: improvements<commit_after>#include <fstream>
#include <iostream>
#include <vector>

#include <sys/mman.h>
#include <cstring>

#include "brainfuck.h"

// Sets a RX permission on the given memory, which must be page-aligned. Returns
// 0 on success. On failure, prints out the error and returns -1.
int make_memory_executable(void* m, size_t size) {
  if (mprotect(m, size, PROT_READ | PROT_EXEC) == -1) {
    perror("mprotect");
    return -1;
  }
  return 0;
}

class JITProgram
{
private:
    uint8_t *program_, *ptr_;
    size_t size_;

public:
    JITProgram(size_t size) :
      program_((uint8_t*) mmap(0, size,
                   PROT_READ | PROT_WRITE,
                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)),
      ptr_(program_),
      size_(size) {} // MAP_FAILED

    ~JITProgram() {
        munmap(program_, size_); // -1
    }

    uint8_t* get_ptr () const { return ptr_; }
    void set_ptr (uint8_t* ptr) { ptr_ = ptr; }

    void writeb(uint8_t byte) {
        (*ptr_++) = byte;
    }

    void writel(uint32_t value) {
        writes((uint8_t*)&value, 4);
    }

    void writes(const uint8_t* bytes, uint32_t size) {
        memcpy(ptr_, bytes, size);
        ptr_ += size;
    }

    void start() {
        // 0000000000000070 break:
        //       70: cc                            int3
        // writeb(0xcc);
    }

    void finish() {
        // 0000000000000071 finish:
        //       71: c3                            retq
        writeb(0xc3);
    }

    void run() {
        uint32_t memory_[30000];
        memset(memory_, 0x00, sizeof(memory_));

        make_memory_executable(program_, size_);

        // asm("int $3");

        // http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html
        asm("movq %0, %%rdi"
            :
            : "r"(&memory_)
            : "%rdi");

        ((void (*)())program_)();
    }
};

class JITCompiler : public ExpressionVisitor
{
private:
    JITProgram &program_;
    std::vector<uint8_t*> stack;

public:
    JITCompiler(JITProgram& program) : program_(program), stack() {}

    virtual void visit(const Increment& inc) {
        // 0000000000000000 increment:
        //        0: 48 c7 c0 01 00 00 00          movq    $1, %rax
        //        7: 01 07                         addl    %eax, (%rdi)
        program_.writes((uint8_t*)"\x48\xc7\xc0", 3);
        program_.writel(inc.offset());
        program_.writes((uint8_t*)"\x01\x07", 2);
    }

    virtual void visit(const Decrement& dec) {
        // 0000000000000009 decrement:
        //        9: 48 c7 c0 01 00 00 00          movq    $1, %rax
        //       10: 29 07                         subl    %eax, (%rdi)
        program_.writes((uint8_t*)"\x48\xc7\xc0", 3);
        program_.writel(dec.offset());
        program_.writes((uint8_t*)"\x29\x07", 2);
    }

    virtual void visit(const Forward& fwd) {
        // 0000000000000012 forward:
        //       12: 48 c7 c0 01 00 00 00          movq    $1, %rax
        //       19: 48 01 c7                      addq    %rax, %rdi
        program_.writes((uint8_t*)"\x48\xc7\xc0", 3);
        program_.writel(fwd.offset()*4);
        program_.writes((uint8_t*)"\x48\x01\xc7", 3);
    }

    virtual void visit(const Backward& bwd) {
        // 000000000000001c backward:
        //       1c: 48 c7 c0 01 00 00 00          movq    $1, %rax
        //       23: 48 29 c7                      subq    %rax, %rdi
        program_.writes((uint8_t*)"\x48\xc7\xc0", 3);
        program_.writel(bwd.offset()*4);
        program_.writes((uint8_t*)"\x48\x29\xc7", 3);
    }

    virtual void visit(const Input&) {
        // 0000000000000026 read:
        //       26: 57                            pushq   %rdi
        //       27: 48 c7 c0 03 00 00 02          movq    $33554435, %rax
        //       2e: 48 89 fe                      movq    %rdi, %rsi
        //       31: 48 c7 c7 01 00 00 00          movq    $1, %rdi
        //       38: 48 c7 c2 01 00 00 00          movq    $1, %rdx
        //       3f: 0f 05                         syscall
        //       41: 5f                            popq    %rdi

        program_.writeb(0x57);
        program_.writes((uint8_t*)"\x48\xc7\xc0", 3);
        program_.writel(0x02000003);
        program_.writes((uint8_t*)"\x48\x89\xfe", 3);
        program_.writes((uint8_t*)"\x48\xc7\xc7\x00\x00\x00\x00", 7);
        program_.writes((uint8_t*)"\x48\xc7\xc2\x01\x00\x00\x00", 7);
        program_.writes((uint8_t*)"\x0f\x05", 2);
        program_.writeb(0x5f);
    }

    virtual void visit(const Output&) {
        // 0000000000000042 write:
        //       42: 57                            pushq   %rdi
        //       43: 48 c7 c0 04 00 00 02          movq    $33554436, %rax
        //       4a: 48 89 fe                      movq    %rdi, %rsi
        //       4d: 48 c7 c7 01 00 00 00          movq    $1, %rdi
        //       54: 48 c7 c2 01 00 00 00          movq    $1, %rdx
        //       5b: 0f 05                         syscall
        //       5d: 5f                            popq    %rdi

        program_.writeb(0x57);
        program_.writes((uint8_t*)"\x48\xc7\xc0", 3);
        program_.writel(0x02000004);
        program_.writes((uint8_t*)"\x48\x89\xfe", 3);
        program_.writes((uint8_t*)"\x48\xc7\xc7\x01\x00\x00\x00", 7);
        program_.writes((uint8_t*)"\x48\xc7\xc2\x01\x00\x00\x00", 7);
        program_.writes((uint8_t*)"\x0f\x05", 2);
        program_.writeb(0x5f);
    }

    virtual void visit(const Loop& loop) {
        // 000000000000005e loop_start:
        //       5e: 83 3f 00                      cmpl    $0, (%rdi)
        //       61: 0f 84 00 00 00 00             je  0
        program_.writes((uint8_t*)"\x83\x3f\x00", 3);
        program_.writes((uint8_t*)"\x0f\x84", 2);
        program_.writel(0); // reserve 4 bytes

        // push current position to stack
        stack.push_back(program_.get_ptr());

        // recurse into subexpressions:
        for(const auto &child: loop.children()) {
            child->accept(*this);
        }

        // recover last position
        uint8_t* after_loop_start = stack.back();
        stack.pop_back();

        // 0000000000000067 loop_end:
        //       67: 83 3f 00                      cmpl    $0, (%rdi)
        //       6a: 0f 85 00 00 00 00             jne 0
        program_.writes((uint8_t*)"\x83\x3f\x00", 3);
        program_.writes((uint8_t*)"\x0f\x85", 2);
        // calculate how much to jump back (consider the 4 bytes
        // of the operand itself):
        uint32_t jump_back = after_loop_start - program_.get_ptr() - 4;
        // append the distance to the jump:
        program_.writel(jump_back);

        // now calculate how much to jump forward in case we want
        // to skip the loop, to fill the pending jump:
        uint8_t *after_loop_end = program_.get_ptr();
        uint32_t jump_fwd = after_loop_end - after_loop_start;

        // go back to the original position (-4), fill the pending
        // jump distance and get back to the the current pos:
        program_.set_ptr(after_loop_start - 4);
        program_.writel(jump_fwd);
        program_.set_ptr(after_loop_end);
    }

    void compile(const ExpressionVector& expressions) {
        program_.start();

        for(const auto &expression: expressions) {
            expression->accept(*this);
        }

        program_.finish();
    }
};

int main(int argc, char *argv[]) {
    std::ifstream ifs(argv[1]);

    if (!ifs) {
        std::cerr << "Invalid filename!" << std::endl;
        return 1;
    }

    std::vector<char> program;

    std::copy(
        (std::istreambuf_iterator<char>(ifs)),
        (std::istreambuf_iterator<char>()),
        std::back_inserter(program)
    );

    try {
        auto expressions = Parser().parse(program);

        JITProgram jit_program(1000000);
        JITCompiler compiler(jit_program);
        compiler.compile(expressions);

        jit_program.run();

    } catch (std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }

    return 0;
}
<|endoftext|>