<commit_before>
/*
 * Copyright 2015 Cloudius Systems
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <stdint.h>
#include <memory>
#include "bytes.hh"
#include "utils/allocation_strategy.hh"

struct blob_storage {
    using size_type = uint32_t;
    using char_type = bytes_view::value_type;

    blob_storage** backref;
    size_type size;
    char_type data[];

    blob_storage(blob_storage** backref, size_type size) noexcept
        : backref(backref)
        , size(size)
    {
        *backref = this;
    }

    blob_storage(blob_storage&& o) noexcept
        : backref(o.backref)
        , size(o.size)
    {
        *backref = this;
        memcpy(data, o.data, size);
    }
} __attribute__((packed));

// A managed version of "bytes" (can be used with LSA).
class managed_bytes {
    static constexpr size_t max_inline_size = 15;
    struct small_blob {
        bytes_view::value_type data[max_inline_size];
        int8_t size; // -1 -> use blob_storage
    };
    union {
        blob_storage* ptr;
        small_blob small;
    } _u;
    static_assert(sizeof(small_blob) > sizeof(blob_storage*), "inline size too small");
private:
    bool external() const {
        return _u.small.size < 0;
    }
public:
    using size_type = blob_storage::size_type;
    struct initialized_later {};

    managed_bytes() {
        _u.small.size = 0;
    }

    managed_bytes(const blob_storage::char_type* ptr, size_type size)
        : managed_bytes(bytes_view(ptr, size)) {}

    managed_bytes(const bytes& b) : managed_bytes(static_cast<bytes_view>(b)) {}

    managed_bytes(initialized_later, size_type size) {
        if (size <= max_inline_size) {
            _u.small.size = size;
        } else {
            _u.small.size = -1;
            void* p = current_allocator().alloc(&standard_migrator<blob_storage>::object,
                sizeof(blob_storage) + size, alignof(blob_storage));
            new (p) blob_storage(&_u.ptr, size);
        }
    }

    managed_bytes(bytes_view v) : managed_bytes(initialized_later(), v.size()) {
        memcpy(data(), v.data(), v.size());
    }

    managed_bytes(std::initializer_list<bytes::value_type> b) : managed_bytes(b.begin(), b.size()) {}

    ~managed_bytes() {
        if (external()) {
            current_allocator().destroy(_u.ptr);
        }
    }

    managed_bytes(const managed_bytes& o) : managed_bytes(static_cast<bytes_view>(o)) {}

    managed_bytes(managed_bytes&& o) noexcept
        : _u(o._u)
    {
        if (external()) {
            if (_u.ptr) {
                _u.ptr->backref = &_u.ptr;
            }
        }
        o._u.small.size = 0;
    }

    managed_bytes& operator=(managed_bytes&& o) {
        if (this != &o) {
            this->~managed_bytes();
            new (this) managed_bytes(std::move(o));
        }
        return *this;
    }

    managed_bytes& operator=(const managed_bytes& o) {
        if (this != &o) {
            // FIXME: not exception safe
            this->~managed_bytes();
            new (this) managed_bytes(o);
        }
        return *this;
    }

    bool operator==(const managed_bytes& o) const {
        return static_cast<bytes_view>(*this) == static_cast<bytes_view>(o);
    }

    bool operator!=(const managed_bytes& o) const {
        return !(*this == o);
    }

    operator bytes_view() const {
        return { data(), size() };
    }

    bytes_view::value_type& operator[](size_type index) {
        return data()[index];
    }

    const bytes_view::value_type& operator[](size_type index) const {
        return data()[index];
    }

    size_type size() const {
        if (external()) {
            return _u.ptr->size;
        } else {
            return _u.small.size;
        }
    }

    const blob_storage::char_type* begin() const {
        return data();
    }

    const blob_storage::char_type* end() const {
        return data() + size();
    }

    blob_storage::char_type* begin() {
        return data();
    }

    blob_storage::char_type* end() {
        return data() + size();
    }

    bool empty() const {
        return _u.small.size == 0;
    }

    blob_storage::char_type* data() {
        if (external()) {
            return _u.ptr->data;
        } else {
            return _u.small.data;
        }
    }

    const blob_storage::char_type* data() const {
        return const_cast<managed_bytes*>(this)->data();
    }
};

namespace std {

template <>
struct hash<managed_bytes> {
    size_t operator()(managed_bytes v) const {
        return hash<bytes_view>()(v);
    }
};

}
<commit_msg>managed_bytes: conform to allocation_strategy's max_preferred_allocation_size<commit_after>
/*
 * Copyright 2015 Cloudius Systems
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <stdint.h>
#include <memory>
#include "bytes.hh"
#include "utils/allocation_strategy.hh"

struct blob_storage {
    using size_type = uint32_t;
    using char_type = bytes_view::value_type;

    blob_storage** backref;
    size_type size;
    size_type frag_size;
    blob_storage* next;
    char_type data[];

    blob_storage(blob_storage** backref, size_type size, size_type frag_size) noexcept
        : backref(backref)
        , size(size)
        , frag_size(frag_size)
        , next(nullptr)
    {
        *backref = this;
    }

    blob_storage(blob_storage&& o) noexcept
        : backref(o.backref)
        , size(o.size)
        , frag_size(o.frag_size)
        , next(o.next)
    {
        *backref = this;
        o.next = nullptr;
        if (next) {
            next->backref = &next;
        }
        memcpy(data, o.data, size);
    }
} __attribute__((packed));

// A managed version of "bytes" (can be used with LSA).
class managed_bytes {
    static constexpr size_t max_inline_size = 15;
    struct small_blob {
        bytes_view::value_type data[max_inline_size];
        int8_t size; // -1 -> use blob_storage
    };
    union {
        blob_storage* ptr;
        small_blob small;
    } _u;
    static_assert(sizeof(small_blob) > sizeof(blob_storage*), "inline size too small");
private:
    bool external() const {
        return _u.small.size < 0;
    }
    size_t max_seg(allocation_strategy& alctr) {
        return alctr.preferred_max_contiguous_allocation() - sizeof(blob_storage);
    }
    void free_chain(blob_storage* p) {
        auto& alctr = current_allocator();
        while (p) {
            auto n = p->next;
            alctr.destroy(p);
            p = n;
        }
    }
public:
    using size_type = blob_storage::size_type;
    struct initialized_later {};

    managed_bytes() {
        _u.small.size = 0;
    }

    managed_bytes(const blob_storage::char_type* ptr, size_type size)
        : managed_bytes(bytes_view(ptr, size)) {}

    managed_bytes(const bytes& b) : managed_bytes(static_cast<bytes_view>(b)) {}

    managed_bytes(initialized_later, size_type size) {
        if (size <= max_inline_size) {
            _u.small.size = size;
        } else {
            _u.small.size = -1;
            auto& alctr = current_allocator();
            auto maxseg = max_seg(alctr);
            auto now = std::min(size_t(size), maxseg);
            void* p = alctr.alloc(&standard_migrator<blob_storage>::object,
                sizeof(blob_storage) + now, alignof(blob_storage));
            auto first = new (p) blob_storage(&_u.ptr, size, now);
            auto last = first;
            size -= now;
            try {
                while (size) {
                    auto now = std::min(size_t(size), maxseg);
                    void* p = alctr.alloc(&standard_migrator<blob_storage>::object,
                        sizeof(blob_storage) + now, alignof(blob_storage));
                    last = new (p) blob_storage(&last->next, 0, now);
                    size -= now;
                }
            } catch (...) {
                free_chain(first);
                throw;
            }
        }
    }

    managed_bytes(bytes_view v) : managed_bytes(initialized_later(), v.size()) {
        auto p = v.data();
        auto s = v.size();
        if (!external()) {
            memcpy(_u.small.data, p, s);
            return;
        }
        auto b = _u.ptr;
        while (s) {
            memcpy(b->data, p, b->frag_size);
            p += b->frag_size;
            s -= b->frag_size;
            b = b->next;
        }
        assert(!b);
    }

    managed_bytes(std::initializer_list<bytes::value_type> b) : managed_bytes(b.begin(), b.size()) {}

    ~managed_bytes() {
        if (external()) {
            free_chain(_u.ptr);
        }
    }

    managed_bytes(const managed_bytes& o) : managed_bytes(initialized_later(), o.size()) {
        if (!external()) {
            memcpy(data(), o.data(), size());
            return;
        }
        auto s = size();
        blob_storage* const* next_src = &o._u.ptr;
        blob_storage* blob_src = nullptr;
        size_type size_src = 0;
        size_type offs_src = 0;
        blob_storage** next_dst = &_u.ptr;
        blob_storage* blob_dst = nullptr;
        size_type size_dst = 0;
        size_type offs_dst = 0;
        while (s) {
            if (!size_src) {
                blob_src = *next_src;
                next_src = &blob_src->next;
                size_src = blob_src->frag_size;
                offs_src = 0;
            }
            if (!size_dst) {
                blob_dst = *next_dst;
                next_dst = &blob_dst->next;
                size_dst = blob_dst->frag_size;
                offs_dst = 0;
            }
            auto now = std::min(size_src, size_dst);
            memcpy(blob_dst->data + offs_dst, blob_src->data + offs_src, now);
            s -= now;
            offs_src += now; size_src -= now;
            offs_dst += now; size_dst -= now;
        }
        assert(size_src == 0 && size_dst == 0);
    }

    managed_bytes(managed_bytes&& o) noexcept
        : _u(o._u)
    {
        if (external()) {
            if (_u.ptr) {
                _u.ptr->backref = &_u.ptr;
            }
        }
        o._u.small.size = 0;
    }

    managed_bytes& operator=(managed_bytes&& o) {
        if (this != &o) {
            this->~managed_bytes();
            new (this) managed_bytes(std::move(o));
        }
        return *this;
    }

    managed_bytes& operator=(const managed_bytes& o) {
        if (this != &o) {
            // FIXME: not exception safe
            this->~managed_bytes();
            new (this) managed_bytes(o);
        }
        return *this;
    }

    bool operator==(const managed_bytes& o) const {
        return static_cast<bytes_view>(*this) == static_cast<bytes_view>(o);
    }

    bool operator!=(const managed_bytes& o) const {
        return !(*this == o);
    }

    operator bytes_view() const {
        return { data(), size() };
    }

    bytes_view::value_type& operator[](size_type index) {
        return data()[index];
    }

    const bytes_view::value_type& operator[](size_type index) const {
        return data()[index];
    }

    size_type size() const {
        if (external()) {
            return _u.ptr->size;
        } else {
            return _u.small.size;
        }
    }

    const blob_storage::char_type* begin() const {
        return data();
    }

    const blob_storage::char_type* end() const {
        return data() + size();
    }

    blob_storage::char_type* begin() {
        return data();
    }

    blob_storage::char_type* end() {
        return data() + size();
    }

    bool empty() const {
        return _u.small.size == 0;
    }

    blob_storage::char_type* data() {
        if (external()) {
            assert(!_u.ptr->next);  // must be linearized
            return _u.ptr->data;
        } else {
            return _u.small.data;
        }
    }

    const blob_storage::char_type* data() const {
        return const_cast<managed_bytes*>(this)->data();
    }

    void linearize() {
        if (!external() || !_u.ptr->next) {
            return;
        }
        auto& alctr = current_allocator();
        auto size = _u.ptr->size;
        void* p = alctr.alloc(&standard_migrator<blob_storage>::object,
            sizeof(blob_storage) + size, alignof(blob_storage));
        auto old = _u.ptr;
        auto blob = new (p) blob_storage(&_u.ptr, size, size);
        auto pos = size_type(0);
        while (old) {
            memcpy(blob->data + pos, old->data, old->frag_size);
            pos += old->frag_size;
            auto next = old->next;
            alctr.destroy(old);
            old = next;
        }
        assert(pos == size);
    }

    void scatter() {
        if (!external()) {
            return;
        }
        if (_u.ptr->size <= max_seg(current_allocator())) {
            return;
        }
        *this = managed_bytes(*this);
    }
};

namespace std {

template <>
struct hash<managed_bytes> {
    size_t operator()(managed_bytes v) const {
        return hash<bytes_view>()(v);
    }
};

}
<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Lewis Baker
// Licenced under MIT license. See LICENSE.txt for details.
///////////////////////////////////////////////////////////////////////////////

#include <cppcoro/io_service.hpp>
#include <cppcoro/net/socket.hpp>
#include <cppcoro/task.hpp>
#include <cppcoro/when_all.hpp>
#include <cppcoro/sync_wait.hpp>
#include <cppcoro/on_scope_exit.hpp>
#include <cppcoro/cancellation_source.hpp>
#include <cppcoro/cancellation_token.hpp>
#include <cppcoro/async_scope.hpp>

#include "doctest/doctest.h"

using namespace cppcoro;
using namespace cppcoro::net;

TEST_SUITE_BEGIN("socket");

TEST_CASE("create TCP/IPv4")
{
	io_service ioSvc;
	auto socket = socket::create_tcpv4(ioSvc);
}

TEST_CASE("create TCP/IPv6")
{
	io_service ioSvc;
	auto socket = socket::create_tcpv6(ioSvc);
}

TEST_CASE("create UDP/IPv4")
{
	io_service ioSvc;
	auto socket = socket::create_udpv4(ioSvc);
}

TEST_CASE("create UDP/IPv6")
{
	io_service ioSvc;
	auto socket = socket::create_udpv6(ioSvc);
}

TEST_CASE("TCP/IPv4 connect/disconnect")
{
	io_service ioSvc;

	ip_endpoint serverAddress;

	task<int> serverTask;

	auto server = [&](socket listeningSocket) -> task<int>
	{
		auto s = socket::create_tcpv4(ioSvc);
		co_await listeningSocket.accept(s);
		co_await s.disconnect();
		co_return 0;
	};

	{
		auto serverSocket = socket::create_tcpv4(ioSvc);
		serverSocket.bind(ipv4_endpoint{ ipv4_address::loopback(), 0 });
		serverSocket.listen(3);
		serverAddress = serverSocket.local_endpoint();
		serverTask = server(std::move(serverSocket));
	}

	auto client = [&]() -> task<int>
	{
		auto s = socket::create_tcpv4(ioSvc);
		s.bind(ipv4_endpoint{ ipv4_address::loopback(), 0 });
		co_await s.connect(serverAddress);
		co_await s.disconnect();
		co_return 0;
	};

	task<int> clientTask = client();

	(void)sync_wait(when_all(
		[&]() -> task<int>
		{
			auto stopOnExit = on_scope_exit([&] { ioSvc.stop(); });
			(void)co_await when_all(std::move(serverTask), std::move(clientTask));
			co_return 0;
		}(),
		[&]() -> task<int>
		{
			ioSvc.process_events();
			co_return 0;
		}()));
}

TEST_CASE("send/recv TCP/IPv4")
{
	io_service ioSvc;

	auto listeningSocket = socket::create_tcpv4(ioSvc);

	listeningSocket.bind(ipv4_endpoint{ ipv4_address::loopback(), 0 });
	listeningSocket.listen(3);

	auto echoServer = [&]() -> task<int>
	{
		auto acceptingSocket = socket::create_tcpv4(ioSvc);

		co_await listeningSocket.accept(acceptingSocket);

		std::uint8_t buffer[64];
		std::size_t bytesReceived;
		do
		{
			bytesReceived = co_await acceptingSocket.recv(buffer, sizeof(buffer));
			if (bytesReceived > 0)
			{
				std::size_t bytesSent = 0;
				do
				{
					bytesSent += co_await acceptingSocket.send(
						buffer + bytesSent,
						bytesReceived - bytesSent);
				} while (bytesSent < bytesReceived);
			}
		} while (bytesReceived > 0);

		acceptingSocket.close_send();

		co_await acceptingSocket.disconnect();

		co_return 0;
	};

	auto echoClient = [&]() -> task<int>
	{
		auto connectingSocket = socket::create_tcpv4(ioSvc);

		connectingSocket.bind(ipv4_endpoint{});

		co_await connectingSocket.connect(listeningSocket.local_endpoint());

		auto receive = [&]() -> task<int>
		{
			std::uint8_t buffer[100];
			std::uint64_t totalBytesReceived = 0;
			std::size_t bytesReceived;
			do
			{
				bytesReceived = co_await connectingSocket.recv(buffer, sizeof(buffer));
				for (std::size_t i = 0; i < bytesReceived; ++i)
				{
					std::uint64_t byteIndex = totalBytesReceived + i;
					std::uint8_t expectedByte = 'a' + (byteIndex % 26);
					CHECK(buffer[i] == expectedByte);
				}

				totalBytesReceived += bytesReceived;
			} while (bytesReceived > 0);

			CHECK(totalBytesReceived == 1000);

			co_return 0;
		};

		auto send = [&]() -> task<int>
		{
			std::uint8_t buffer[100];
			for (std::uint64_t i = 0; i < 1000; i += sizeof(buffer))
			{
				for (std::size_t j = 0; j < sizeof(buffer); ++j)
				{
					buffer[j] = 'a' + ((i + j) % 26);
				}

				std::size_t bytesSent = 0;
				do
				{
					bytesSent += co_await connectingSocket.send(buffer + bytesSent, sizeof(buffer) - bytesSent);
				} while (bytesSent < sizeof(buffer));
			}

			connectingSocket.close_send();

			co_return 0;
		};

		co_await when_all(send(), receive());

		co_await connectingSocket.disconnect();

		co_return 0;
	};

	(void)sync_wait(when_all(
		[&]() -> task<int>
		{
			auto stopOnExit = on_scope_exit([&] { ioSvc.stop(); });
			(void)co_await when_all(echoClient(), echoServer());
			co_return 0;
		}(),
		[&]() -> task<int>
		{
			ioSvc.process_events();
			co_return 0;
		}()));
}

#if !CPPCORO_COMPILER_MSVC || CPPCORO_COMPILER_MSVC >= 191626926 || !CPPCORO_CPU_X86
// HACK: Don't compile this function under MSVC x86.
// It results in an ICE under VS 2017.8 / 2017.9 Preview 1.
// Seems to be fixed in 2017.9 Preview 3

TEST_CASE("send/recv TCP/IPv4 many connections")
{
	io_service ioSvc;

	auto listeningSocket = socket::create_tcpv4(ioSvc);

	listeningSocket.bind(ipv4_endpoint{ ipv4_address::loopback(), 0 });
	listeningSocket.listen(20);

	cancellation_source canceller;

	auto handleConnection = [](socket s) -> task<void>
	{
		std::uint8_t buffer[64];
		std::size_t bytesReceived;
		do
		{
			bytesReceived = co_await s.recv(buffer, sizeof(buffer));
			if (bytesReceived > 0)
			{
				std::size_t bytesSent = 0;
				do
				{
					bytesSent += co_await s.send(
						buffer + bytesSent,
						bytesReceived - bytesSent);
				} while (bytesSent < bytesReceived);
			}
		} while (bytesReceived > 0);

		s.close_send();

		co_await s.disconnect();
	};

	auto echoServer = [&](cancellation_token ct) -> task<>
	{
		async_scope connectionScope;

		std::exception_ptr ex;
		try
		{
			while (true) {
				auto acceptingSocket = socket::create_tcpv4(ioSvc);
				co_await listeningSocket.accept(acceptingSocket, ct);
				connectionScope.spawn(
					handleConnection(std::move(acceptingSocket)));
			}
		}
		catch (const cppcoro::operation_cancelled&)
		{
		}
		catch (...)
		{
			ex = std::current_exception();
		}

		co_await connectionScope.join();

		if (ex)
		{
			std::rethrow_exception(ex);
		}
	};

	auto echoClient = [&]() -> task<>
	{
		auto connectingSocket = socket::create_tcpv4(ioSvc);

		connectingSocket.bind(ipv4_endpoint{});

		co_await connectingSocket.connect(listeningSocket.local_endpoint());

		auto receive = [&]() -> task<>
		{
			std::uint8_t buffer[100];
			std::uint64_t totalBytesReceived = 0;
			std::size_t bytesReceived;
			do
			{
				bytesReceived = co_await connectingSocket.recv(buffer, sizeof(buffer));
				for (std::size_t i = 0; i < bytesReceived; ++i)
				{
					std::uint64_t byteIndex = totalBytesReceived + i;
					std::uint8_t expectedByte = 'a' + (byteIndex % 26);
					CHECK(buffer[i] == expectedByte);
				}

				totalBytesReceived += bytesReceived;
			} while (bytesReceived > 0);

			CHECK(totalBytesReceived == 1000);
		};

		auto send = [&]() -> task<>
		{
			std::uint8_t buffer[100];
			for (std::uint64_t i = 0; i < 1000; i += sizeof(buffer))
			{
				for (std::size_t j = 0; j < sizeof(buffer); ++j)
				{
					buffer[j] = 'a' + ((i + j) % 26);
				}

				std::size_t bytesSent = 0;
				do
				{
					bytesSent += co_await connectingSocket.send(buffer + bytesSent, sizeof(buffer) - bytesSent);
				} while (bytesSent < sizeof(buffer));
			}

			connectingSocket.close_send();
		};

		co_await when_all(send(), receive());

		co_await connectingSocket.disconnect();
	};

	auto manyEchoClients = [&](int count) -> task<void>
	{
		auto shutdownServerOnExit = on_scope_exit([&]
		{
			canceller.request_cancellation();
		});

		std::vector<task<>> clientTasks;
		clientTasks.reserve(count);

		for (int i = 0; i < count; ++i)
		{
			clientTasks.emplace_back(echoClient());
		}

		co_await when_all(std::move(clientTasks));
	};

	(void)sync_wait(when_all(
		[&]() -> task<>
		{
			auto stopOnExit = on_scope_exit([&] { ioSvc.stop(); });
			(void)co_await when_all(
				manyEchoClients(20),
				echoServer(canceller.token()));
		}(),
		[&]() -> task<>
		{
			ioSvc.process_events();
			co_return;
		}()));
}

#endif

TEST_CASE("udp send_to/recv_from")
{
	io_service ioSvc;

	auto server = [&](socket serverSocket) -> task<int>
	{
		std::uint8_t buffer[100];

		auto[bytesReceived, remoteEndPoint] = co_await serverSocket.recv_from(buffer, 100);
		CHECK(bytesReceived == 50);

		// Send an ACK response.
		{
			const std::uint8_t response[1] = { 0 };
			co_await serverSocket.send_to(remoteEndPoint, &response, 1);
		}

		// Second message received won't fit within buffer.
		try
		{
			std::tie(bytesReceived, remoteEndPoint) = co_await serverSocket.recv_from(buffer, 100);
			FAIL("Should have thrown");
		}
		catch (const std::system_error&)
		{
			// TODO: Map this situation to some kind of error_condition value.
			// The win32 ERROR_MORE_DATA error code doesn't seem to map to any of the standard std::errc values.
			//
			// CHECK(ex.code() == ???);
			//
			// Possibly also need to switch to returning a std::error_code directly rather than
			// throwing a std::system_error for this case.
		}

		// Send an NACK response.
		{
			const std::uint8_t response[1] = { 1 };
			co_await serverSocket.send_to(remoteEndPoint, response, 1);
		}

		co_return 0;
	};

	ip_endpoint serverAddress;

	task<int> serverTask;

	{
		auto serverSocket = socket::create_udpv4(ioSvc);
		serverSocket.bind(ipv4_endpoint{ ipv4_address::loopback(), 0 });
		serverAddress = serverSocket.local_endpoint();
		serverTask = server(std::move(serverSocket));
	}

	auto client = [&]() -> task<int>
	{
		auto socket = socket::create_udpv4(ioSvc);

		// don't need to bind(), should be implicitly bound on first send_to().

		// Send first message of 50 bytes
		{
			std::uint8_t buffer[50] = { 0 };
			co_await socket.send_to(serverAddress, buffer, 50);
		}

		// Receive ACK message
		{
			std::uint8_t buffer[1];
			auto[bytesReceived, ackAddress] = co_await socket.recv_from(buffer, 1);
			CHECK(bytesReceived == 1);
			CHECK(buffer[0] == 0);
			CHECK(ackAddress == serverAddress);
		}

		// Send second message of 128 bytes
		{
			std::uint8_t buffer[128] = { 0 };
			co_await socket.send_to(serverAddress, buffer, 128);
		}

		// Receive NACK message
		{
			std::uint8_t buffer[1];
			auto[bytesReceived, ackAddress] = co_await socket.recv_from(buffer, 1);
			CHECK(bytesReceived == 1);
			CHECK(buffer[0] == 1);
			CHECK(ackAddress == serverAddress);
		}

		co_return 0;
	};

	(void)sync_wait(when_all(
		[&]() -> task<int>
		{
			auto stopOnExit = on_scope_exit([&] { ioSvc.stop(); });
			(void)co_await when_all(std::move(serverTask), client());
			co_return 0;
		}(),
		[&]() -> task<int>
		{
			ioSvc.process_events();
			co_return 0;
		}()));
}

TEST_SUITE_END();
<commit_msg>Disable socket test that causes an ICE under msvc x86.<commit_after>///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Lewis Baker
// Licenced under MIT license. See LICENSE.txt for details.
///////////////////////////////////////////////////////////////////////////////

#include <cppcoro/io_service.hpp>
#include <cppcoro/net/socket.hpp>
#include <cppcoro/task.hpp>
#include <cppcoro/when_all.hpp>
#include <cppcoro/sync_wait.hpp>
#include <cppcoro/on_scope_exit.hpp>
#include <cppcoro/cancellation_source.hpp>
#include <cppcoro/cancellation_token.hpp>
#include <cppcoro/async_scope.hpp>

#include "doctest/doctest.h"

using namespace cppcoro;
using namespace cppcoro::net;

TEST_SUITE_BEGIN("socket");

TEST_CASE("create TCP/IPv4")
{
	io_service ioSvc;
	auto socket = socket::create_tcpv4(ioSvc);
}

TEST_CASE("create TCP/IPv6")
{
	io_service ioSvc;
	auto socket = socket::create_tcpv6(ioSvc);
}

TEST_CASE("create UDP/IPv4")
{
	io_service ioSvc;
	auto socket = socket::create_udpv4(ioSvc);
}

TEST_CASE("create UDP/IPv6")
{
	io_service ioSvc;
	auto socket = socket::create_udpv6(ioSvc);
}

TEST_CASE("TCP/IPv4 connect/disconnect")
{
	io_service ioSvc;

	ip_endpoint serverAddress;

	task<int> serverTask;

	auto server = [&](socket listeningSocket) -> task<int>
	{
		auto s = socket::create_tcpv4(ioSvc);
		co_await listeningSocket.accept(s);
		co_await s.disconnect();
		co_return 0;
	};

	{
		auto serverSocket = socket::create_tcpv4(ioSvc);
		serverSocket.bind(ipv4_endpoint{ ipv4_address::loopback(), 0 });
		serverSocket.listen(3);
		serverAddress = serverSocket.local_endpoint();
		serverTask = server(std::move(serverSocket));
	}

	auto client = [&]() -> task<int>
	{
		auto s = socket::create_tcpv4(ioSvc);
		s.bind(ipv4_endpoint{ ipv4_address::loopback(), 0 });
		co_await s.connect(serverAddress);
		co_await s.disconnect();
		co_return 0;
	};

	task<int> clientTask = client();

	(void)sync_wait(when_all(
		[&]() -> task<int>
		{
			auto stopOnExit = on_scope_exit([&] { ioSvc.stop(); });
			(void)co_await when_all(std::move(serverTask), std::move(clientTask));
			co_return 0;
		}(),
		[&]() -> task<int>
		{
			ioSvc.process_events();
			co_return 0;
		}()));
}

TEST_CASE("send/recv TCP/IPv4")
{
	io_service ioSvc;

	auto listeningSocket = socket::create_tcpv4(ioSvc);

	listeningSocket.bind(ipv4_endpoint{ ipv4_address::loopback(), 0 });
	listeningSocket.listen(3);

	auto echoServer = [&]() -> task<int>
	{
		auto acceptingSocket = socket::create_tcpv4(ioSvc);

		co_await listeningSocket.accept(acceptingSocket);

		std::uint8_t buffer[64];
		std::size_t bytesReceived;
		do
		{
			bytesReceived = co_await acceptingSocket.recv(buffer, sizeof(buffer));
			if (bytesReceived > 0)
			{
				std::size_t bytesSent = 0;
				do
				{
					bytesSent += co_await acceptingSocket.send(
						buffer + bytesSent,
						bytesReceived - bytesSent);
				} while (bytesSent < bytesReceived);
			}
		} while (bytesReceived > 0);

		acceptingSocket.close_send();

		co_await acceptingSocket.disconnect();

		co_return 0;
	};

	auto echoClient = [&]() -> task<int>
	{
		auto connectingSocket = socket::create_tcpv4(ioSvc);

		connectingSocket.bind(ipv4_endpoint{});

		co_await connectingSocket.connect(listeningSocket.local_endpoint());

		auto receive = [&]() -> task<int>
		{
			std::uint8_t buffer[100];
			std::uint64_t totalBytesReceived = 0;
			std::size_t bytesReceived;
			do
			{
				bytesReceived = co_await connectingSocket.recv(buffer, sizeof(buffer));
				for (std::size_t i = 0; i < bytesReceived; ++i)
				{
					std::uint64_t byteIndex = totalBytesReceived + i;
					std::uint8_t expectedByte = 'a' + (byteIndex % 26);
					CHECK(buffer[i] == expectedByte);
				}

				totalBytesReceived += bytesReceived;
			} while (bytesReceived > 0);

			CHECK(totalBytesReceived == 1000);

			co_return 0;
		};

		auto send = [&]() -> task<int>
		{
			std::uint8_t buffer[100];
			for (std::uint64_t i = 0; i < 1000; i += sizeof(buffer))
			{
				for (std::size_t j = 0; j < sizeof(buffer); ++j)
				{
					buffer[j] = 'a' + ((i + j) % 26);
				}

				std::size_t bytesSent = 0;
				do
				{
					bytesSent += co_await connectingSocket.send(buffer + bytesSent, sizeof(buffer) - bytesSent);
				} while (bytesSent < sizeof(buffer));
			}

			connectingSocket.close_send();

			co_return 0;
		};

		co_await when_all(send(), receive());

		co_await connectingSocket.disconnect();

		co_return 0;
	};

	(void)sync_wait(when_all(
		[&]() -> task<int>
		{
			auto stopOnExit = on_scope_exit([&] { ioSvc.stop(); });
			(void)co_await when_all(echoClient(), echoServer());
			co_return 0;
		}(),
		[&]() -> task<int>
		{
			ioSvc.process_events();
			co_return 0;
		}()));
}

#if !CPPCORO_COMPILER_MSVC || CPPCORO_COMPILER_MSVC > 191627032 || !CPPCORO_CPU_X86
// HACK: Don't compile this function under MSVC x86.
// It results in an ICE under VS 2017.15 and earlier.

TEST_CASE("send/recv TCP/IPv4 many connections")
{
	io_service ioSvc;

	auto listeningSocket = socket::create_tcpv4(ioSvc);

	listeningSocket.bind(ipv4_endpoint{ ipv4_address::loopback(), 0 });
	listeningSocket.listen(20);

	cancellation_source canceller;

	auto handleConnection = [](socket s) -> task<void>
	{
		std::uint8_t buffer[64];
		std::size_t bytesReceived;
		do
		{
			bytesReceived = co_await s.recv(buffer, sizeof(buffer));
			if (bytesReceived > 0)
			{
				std::size_t bytesSent = 0;
				do
				{
					bytesSent += co_await s.send(
						buffer + bytesSent,
						bytesReceived - bytesSent);
				} while (bytesSent < bytesReceived);
			}
		} while (bytesReceived > 0);

		s.close_send();

		co_await s.disconnect();
	};

	auto echoServer = [&](cancellation_token ct) -> task<>
	{
		async_scope connectionScope;

		std::exception_ptr ex;
		try
		{
			while (true) {
				auto acceptingSocket = socket::create_tcpv4(ioSvc);
				co_await listeningSocket.accept(acceptingSocket, ct);
				connectionScope.spawn(
					handleConnection(std::move(acceptingSocket)));
			}
		}
		catch (const cppcoro::operation_cancelled&)
		{
		}
		catch (...)
		{
			ex = std::current_exception();
		}

		co_await connectionScope.join();

		if (ex)
		{
			std::rethrow_exception(ex);
		}
	};

	auto echoClient = [&]() -> task<>
	{
		auto connectingSocket = socket::create_tcpv4(ioSvc);

		connectingSocket.bind(ipv4_endpoint{});

		co_await connectingSocket.connect(listeningSocket.local_endpoint());

		auto receive = [&]() -> task<>
		{
			std::uint8_t buffer[100];
			std::uint64_t totalBytesReceived = 0;
			std::size_t bytesReceived;
			do
			{
				bytesReceived = co_await connectingSocket.recv(buffer, sizeof(buffer));
				for (std::size_t i = 0; i < bytesReceived; ++i)
				{
					std::uint64_t byteIndex = totalBytesReceived + i;
					std::uint8_t expectedByte = 'a' + (byteIndex % 26);
					CHECK(buffer[i] == expectedByte);
				}

				totalBytesReceived += bytesReceived;
			} while (bytesReceived > 0);

			CHECK(totalBytesReceived == 1000);
		};

		auto send = [&]() -> task<>
		{
			std::uint8_t buffer[100];
			for (std::uint64_t i = 0; i < 1000; i += sizeof(buffer))
			{
				for (std::size_t j = 0; j < sizeof(buffer); ++j)
				{
					buffer[j] = 'a' + ((i + j) % 26);
				}

				std::size_t bytesSent = 0;
				do
				{
					bytesSent += co_await connectingSocket.send(buffer + bytesSent, sizeof(buffer) - bytesSent);
				} while (bytesSent < sizeof(buffer));
			}

			connectingSocket.close_send();
		};

		co_await when_all(send(), receive());

		co_await connectingSocket.disconnect();
	};

	auto manyEchoClients = [&](int count) -> task<void>
	{
		auto shutdownServerOnExit = on_scope_exit([&]
		{
			canceller.request_cancellation();
		});

		std::vector<task<>> clientTasks;
		clientTasks.reserve(count);

		for (int i = 0; i < count; ++i)
		{
			clientTasks.emplace_back(echoClient());
		}

		co_await when_all(std::move(clientTasks));
	};

	(void)sync_wait(when_all(
		[&]() -> task<>
		{
			auto stopOnExit = on_scope_exit([&] { ioSvc.stop(); });
			(void)co_await when_all(
				manyEchoClients(20),
				echoServer(canceller.token()));
		}(),
		[&]() -> task<>
		{
			ioSvc.process_events();
			co_return;
		}()));
}

#endif

TEST_CASE("udp send_to/recv_from")
{
	io_service ioSvc;

	auto server = [&](socket serverSocket) -> task<int>
	{
		std::uint8_t buffer[100];

		auto[bytesReceived, remoteEndPoint] = co_await serverSocket.recv_from(buffer, 100);
		CHECK(bytesReceived == 50);

		// Send an ACK response.
		{
			const std::uint8_t response[1] = { 0 };
			co_await serverSocket.send_to(remoteEndPoint, &response, 1);
		}

		// Second message received won't fit within buffer.
		try
		{
			std::tie(bytesReceived, remoteEndPoint) = co_await serverSocket.recv_from(buffer, 100);
			FAIL("Should have thrown");
		}
		catch (const std::system_error&)
		{
			// TODO: Map this situation to some kind of error_condition value.
			// The win32 ERROR_MORE_DATA error code doesn't seem to map to any of the standard std::errc values.
			//
			// CHECK(ex.code() == ???);
			//
			// Possibly also need to switch to returning a std::error_code directly rather than
			// throwing a std::system_error for this case.
		}

		// Send an NACK response.
		{
			const std::uint8_t response[1] = { 1 };
			co_await serverSocket.send_to(remoteEndPoint, response, 1);
		}

		co_return 0;
	};

	ip_endpoint serverAddress;

	task<int> serverTask;

	{
		auto serverSocket = socket::create_udpv4(ioSvc);
		serverSocket.bind(ipv4_endpoint{ ipv4_address::loopback(), 0 });
		serverAddress = serverSocket.local_endpoint();
		serverTask = server(std::move(serverSocket));
	}

	auto client = [&]() -> task<int>
	{
		auto socket = socket::create_udpv4(ioSvc);

		// don't need to bind(), should be implicitly bound on first send_to().

		// Send first message of 50 bytes
		{
			std::uint8_t buffer[50] = { 0 };
			co_await socket.send_to(serverAddress, buffer, 50);
		}

		// Receive ACK message
		{
			std::uint8_t buffer[1];
			auto[bytesReceived, ackAddress] = co_await socket.recv_from(buffer, 1);
			CHECK(bytesReceived == 1);
			CHECK(buffer[0] == 0);
			CHECK(ackAddress == serverAddress);
		}

		// Send second message of 128 bytes
		{
			std::uint8_t buffer[128] = { 0 };
			co_await socket.send_to(serverAddress, buffer, 128);
		}

		// Receive NACK message
		{
			std::uint8_t buffer[1];
			auto[bytesReceived, ackAddress] = co_await socket.recv_from(buffer, 1);
			CHECK(bytesReceived == 1);
			CHECK(buffer[0] == 1);
			CHECK(ackAddress == serverAddress);
		}

		co_return 0;
	};

	(void)sync_wait(when_all(
		[&]() -> task<int>
		{
			auto stopOnExit = on_scope_exit([&] { ioSvc.stop(); });
			(void)co_await when_all(std::move(serverTask), client());
			co_return 0;
		}(),
		[&]() -> task<int>
		{
			ioSvc.process_events();
			co_return 0;
		}()));
}

TEST_SUITE_END();
<|endoftext|>


<commit_before>#include <iostream>

#include "sre/Texture.hpp"
#include "sre/Renderer.hpp"
#include "sre/Material.hpp"

#include <glm/gtx/euler_angles.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <sre/SDLRenderer.hpp>
#include <sre/impl/GL.hpp>
#include <sre/Inspector.hpp>
#include <sre/ModelImporter.hpp>

using namespace sre;

class StencilExample{
public:
    StencilExample(){
        r.init();

        std::vector<std::shared_ptr<Material>> materials_unused;

        mesh = sre::ModelImporter::importObj("examples_data/", "suzanne.obj", materials_unused);
        plane = Mesh::create().withQuad(1).build();

        camera.setPerspectiveProjection(45,0.1,10);
        camera.lookAt({0,0,3.5},{0,0,0},{0,1,0});

        worldLights.setAmbientLight(glm::vec3{0.05});
        worldLights.addLight(Light::create().withPointLight({0.5,2,0.5}).build());

        matStencilWrite = Shader::create()
                .withSourceFile("unlit_vert.glsl", ShaderType::Vertex)
                .withSourceFile("unlit_frag.glsl", ShaderType::Fragment)
                .withDepthWrite(false)
                .withStencil(Stencil{
                    .func = StencilFunc ::Always,
                    .ref = 1,
                    .mask = 1,
                    .fail = StencilOp::Replace,
                    .zfail = StencilOp::Replace,
                    .zpass = StencilOp::Replace,
                })
                .withName("StencilWrite").build()->createMaterial();

        matStencilTest = Shader::create()
                .withSourceFile("unlit_vert.glsl", ShaderType::Vertex)
                .withSourceFile("unlit_frag.glsl", ShaderType::Fragment)
                .withStencil(Stencil{
                        .func = StencilFunc ::Equal,
                        .ref = 1,
                        .mask = 1,
                        .fail = StencilOp::Keep,
                        .zfail = StencilOp::Keep,
                        .zpass = StencilOp::Keep,
                })
                .withName("StencilWrite").build()->createMaterial();
        matStencilTest->setColor({0.3,0.3,0.3});

        mat1 = Shader::getStandardPhong()->createMaterial();
        shadow = Shader::getUnlit()->createMaterial();
        shadow->setColor({0.3,0.3,0.3});

        std::string info;
        if (!mat1->getShader()->validateMesh(mesh.get(), info)){
            std::cout << info <<std::endl;
        } else {
            std::cout << "Mesh ok" << std::endl;
        }

        r.frameRender = [&](){
            render();
        };
        r.mouseEvent = [&](SDL_Event& event){
            if (event.type == SDL_MOUSEMOTION){
                float mouseSpeed = 1/50.0f;
                rotateY = event.motion.x*mouseSpeed;
                rotateX = event.motion.y*mouseSpeed;
            }
            if (event.button.button==SDL_BUTTON_RIGHT){
                showInspector = true;
            }
        };
        r.startEventLoop();
    }

    void render(){
        auto rp = RenderPass::create()
                .withCamera(camera)
                .withClearColor(true,{0,0,0,1})
                .withClearStencil(true)
                .withWorldLights(&worldLights)
                .build();

        auto pos = worldLights.getLight(0)->position;

        float y = pos.y-0.01f-shadowPlane;
        glm::mat4 shadow = glm::transpose(glm::mat4(
                1,0,0,0,
                0,1,0,0,
                0,0,1,0,
                0,1/-y,0,0
        ));
        glm::mat4 projectedShadow = glm::translate(pos) * shadow * glm::translate(-pos);

        auto modelRotation = (glm::rotate(rotateX, glm::vec3(1,0,0))*glm::rotate(rotateY, glm::vec3(0,1,0)));

        if (drawShadow) {
            if (useStencil){
                rp.draw(plane, glm::translate(glm::vec3{0,shadowPlane,0})*glm::rotate(-glm::half_pi<float>(),glm::vec3{1,0,0}), matStencilWrite);
                rp.draw(mesh, projectedShadow * modelRotation, matStencilTest);
            } else {
               rp.draw(mesh, projectedShadow * modelRotation, this->shadow);
            }
        }
        rp.draw(mesh, modelRotation, mat1);
        if (drawPlane){
            rp.draw(plane, glm::translate(glm::vec3{0,shadowPlane,0})*glm::rotate(-glm::half_pi<float>(),glm::vec3{1,0,0}), mat1);
        }

        ImGui::SetNextWindowPos(ImVec2(0,0));
        ImGui::SetNextWindowContentSize(ImVec2(300,100));
        ImGui::Begin("Shadow");
        ImGui::DragFloat("Ground height ",&shadowPlane,0.1f);
        ImGui::Checkbox("Draw plane",&drawPlane);
        ImGui::Checkbox("Draw shadow",&drawShadow);
        ImGui::Checkbox("Use stencil",&useStencil);
        ImGui::End();

        static Inspector inspector;
        inspector.update();
        if (showInspector){
            inspector.gui();
        }
    }
private:
    float time;
    SDLRenderer r;
    Camera camera;
    float shadowPlane = -1;
    std::shared_ptr<Mesh> mesh;
    std::shared_ptr<Mesh> plane;
    std::shared_ptr<Material> mat1;
    std::shared_ptr<Material> matStencilWrite;
    std::shared_ptr<Material> matStencilTest;
    std::shared_ptr<Material> shadow;
    WorldLights worldLights;
    float rotateX = 0;
    float rotateY = 0;
    bool drawPlane = true;
    bool drawShadow = true;
    bool useStencil = false;
    bool showInspector = false;
};

int main() {
    new StencilExample();

    return 0;
}

<commit_msg>Add stencil support in shader<commit_after>#include <iostream>

#include "sre/Texture.hpp"
#include "sre/Renderer.hpp"
#include "sre/Material.hpp"

#include <glm/gtx/euler_angles.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <sre/SDLRenderer.hpp>
#include <sre/impl/GL.hpp>
#include <sre/Inspector.hpp>
#include <sre/ModelImporter.hpp>

using namespace sre;

class StencilExample{
public:
    StencilExample(){
        r.init();

        std::vector<std::shared_ptr<Material>> materials_unused;

        mesh = sre::ModelImporter::importObj("examples_data/", "suzanne.obj", materials_unused);
        plane = Mesh::create().withQuad(1).build();

        camera.setPerspectiveProjection(45,0.1,10);
        camera.lookAt({0,0,3.5},{0,0,0},{0,1,0});

        worldLights.setAmbientLight(glm::vec3{0.05});
        worldLights.addLight(Light::create().withPointLight({0.5,2,0.5}).build());

        matStencilWrite = Shader::create()
                .withSourceFile("unlit_vert.glsl", ShaderType::Vertex)
                .withSourceFile("unlit_frag.glsl", ShaderType::Fragment)
                .withDepthWrite(false)
                .withStencil(Stencil{
                    .func = StencilFunc ::Always,
                    .ref = 1,
                    .mask = 1,
                    .fail = StencilOp::Replace,
                    .zfail = StencilOp::Replace,
                    .zpass = StencilOp::Replace,
                })
                .withName("StencilWrite").build()->createMaterial();

        matStencilTest = Shader::create()
                .withSourceFile("unlit_vert.glsl", ShaderType::Vertex)
                .withSourceFile("unlit_frag.glsl", ShaderType::Fragment)
                .withStencil(Stencil{
                        .func = StencilFunc ::Equal,
                        .ref = 1,
                        .mask = 1
                })
                .withName("StencilWrite").build()->createMaterial();
        matStencilTest->setColor({0.3,0.3,0.3});

        mat1 = Shader::getStandardPhong()->createMaterial();
        shadow = Shader::getUnlit()->createMaterial();
        shadow->setColor({0.3,0.3,0.3});

        std::string info;
        if (!mat1->getShader()->validateMesh(mesh.get(), info)){
            std::cout << info <<std::endl;
        } else {
            std::cout << "Mesh ok" << std::endl;
        }

        r.frameRender = [&](){
            render();
        };
        r.mouseEvent = [&](SDL_Event& event){
            if (event.type == SDL_MOUSEMOTION){
                float mouseSpeed = 1/50.0f;
                rotateY = event.motion.x*mouseSpeed;
                rotateX = event.motion.y*mouseSpeed;
            }
            if (event.button.button==SDL_BUTTON_RIGHT){
                showInspector = true;
            }
        };
        r.startEventLoop();
    }

    void render(){
        auto rp = RenderPass::create()
                .withCamera(camera)
                .withClearColor(true,{0,0,0,1})
                .withClearStencil(true)
                .withWorldLights(&worldLights)
                .build();

        auto pos = worldLights.getLight(0)->position;

        float y = pos.y-0.01f-shadowPlane;
        glm::mat4 shadow = glm::transpose(glm::mat4(
                1,0,0,0,
                0,1,0,0,
                0,0,1,0,
                0,1/-y,0,0
        ));
        glm::mat4 projectedShadow = glm::translate(pos) * shadow * glm::translate(-pos);

        auto modelRotation = (glm::rotate(rotateX, glm::vec3(1,0,0))*glm::rotate(rotateY, glm::vec3(0,1,0)));

        if (drawShadow) {
            if (useStencil){
                rp.draw(plane, glm::translate(glm::vec3{0,shadowPlane,0})*glm::rotate(-glm::half_pi<float>(),glm::vec3{1,0,0}), matStencilWrite);
                rp.draw(mesh, projectedShadow * modelRotation, matStencilTest);
            } else {
               rp.draw(mesh, projectedShadow * modelRotation, this->shadow);
            }
        }
        rp.draw(mesh, modelRotation, mat1);
        if (drawPlane){
            rp.draw(plane, glm::translate(glm::vec3{0,shadowPlane,0})*glm::rotate(-glm::half_pi<float>(),glm::vec3{1,0,0}), mat1);
        }

        ImGui::SetNextWindowPos(ImVec2(0,0));
        ImGui::SetNextWindowContentSize(ImVec2(300,100));
        ImGui::Begin("Shadow");
        ImGui::DragFloat("Ground height ",&shadowPlane,0.1f);
        ImGui::Checkbox("Draw plane",&drawPlane);
        ImGui::Checkbox("Draw shadow",&drawShadow);
        ImGui::Checkbox("Use stencil",&useStencil);
        ImGui::DragFloat3("Light pos",&worldLights.getLight(0)->position.x,.1f);
        ImGui::End();

        static Inspector inspector;
        inspector.update();
        if (showInspector){
            inspector.gui();
        }
    }
private:
    float time;
    SDLRenderer r;
    Camera camera;
    float shadowPlane = -1;
    std::shared_ptr<Mesh> mesh;
    std::shared_ptr<Mesh> plane;
    std::shared_ptr<Material> mat1;
    std::shared_ptr<Material> matStencilWrite;
    std::shared_ptr<Material> matStencilTest;
    std::shared_ptr<Material> shadow;
    WorldLights worldLights;
    float rotateX = 0;
    float rotateY = 0;
    bool drawPlane = true;
    bool drawShadow = true;
    bool useStencil = false;
    bool showInspector = false;
};

int main() {
    new StencilExample();

    return 0;
}

<|endoftext|>


<commit_before>#include <stdio.h>
#include "Target.h"

Target::Target(int targetNumber) : number(targetNumber){}

Target::~Target(void){}

void Target::show()
{
	printf("Showing target #%d\n", number);
}

void Target::hide()
{
	printf("Hiding target #%d\n", number);
}<commit_msg>Added code banner<commit_after>/*
 * Author: Laurent Goderre
 */

#include <stdio.h>
#include "Target.h"

Target::Target(int targetNumber) : number(targetNumber){}

Target::~Target(void){}

void Target::show()
{
	printf("Showing target #%d\n", number);
}

void Target::hide()
{
	printf("Hiding target #%d\n", number);
}<|endoftext|>


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

/*
** Functions that are shared between condor_starter and condor_starter.smp.
** This is a temporary hack, until we more elegantly merge the PVM
** starter with the real starter.
** Written by Derek Wright <wright@cs.wisc.edu> on 10/8/98.
*/

#include "condor_common.h"
#include "starter_common.h"
#include "condor_version.h"
#include "basename.h"
#include "internet.h"
#include "subsystem_info.h"
#include "std_univ_io.h"

extern int		EventSigs[];
extern char*	InitiatingHost;
extern StdUnivSock	*SyscallStream;	// stream to shadow for remote system calls
extern char*	UidDomain;				// Machines we share UID space with
extern bool		TrustUidDomain;	// Should we trust what the submit side claims?

MyString SlotName;

/*
  Unblock all signals except those which will encode asynchronous
  events.  Those will be unblocked at the proper moment by the finite
  state machine driver.
*/
void
init_sig_mask()
{
	sigset_t	sigmask;
	int			i;

	sigemptyset( &sigmask );
	for( i=0; EventSigs[i]; i++ ) {
		sigaddset( &sigmask, EventSigs[i] );
	}
	(void)sigprocmask( SIG_SETMASK, &sigmask, 0 );
}

void
initial_bookeeping( int /* argc */, char *argv[] )
{
	char*	logAppend = NULL;
	char*	submitHost = NULL;
	int		usageError = FALSE;
	char**	ptr;

	(void) SetSyscalls( SYS_LOCAL | SYS_MAPPED );

           // Process command line args.
	submitHost = argv[1];
	for(ptr = argv + 2; *ptr && !usageError; ptr++) {
		if(ptr[0][0] == '<') {
				// We were passed the startd sinful string, which for
				// now, we're just ignoring.
			continue;
		}
		if(ptr[0][0] == '-') {
			switch( ptr[0][1] ) {
			case 'a':
				ptr++;
				if( ptr && *ptr ) {
					logAppend = *ptr;
				} else {
					usageError = TRUE;
				}
				break;
			default:
				fprintf( stderr, "Error:  Unknown option %s\n", *ptr );
				usageError = TRUE;
			}
		}
	}

		// we need to call this *before* we try to set_condor_priv(),
		// so that if CONDOR_IDS is defined in the config file, we'll
		// get the right value.
	config();

	set_condor_priv();

	init_shadow_connections();

		// If we're told on the command-line to append something to
		// the name of our log file (b/c of the SMP startd), we do
		// that here, so that when we setup logging, we get the right
		// filename.  -Derek Wright 9/2/98
	if( logAppend ) {
		char *tmp1, *tmp2;
		if( !(tmp1 = param("STARTER_LOG")) ) {
			EXCEPT( "STARTER_LOG not defined!" );
		}
		tmp2 = (char*)malloc( (strlen(tmp1) + strlen(logAppend) + 2) *
							  sizeof(char) );
		if( !tmp2 ) {
			EXCEPT( "Out of memory!" );
		}
		sprintf( tmp2, "%s.%s", tmp1, logAppend );
		config_insert( "STARTER_LOG", tmp2 );
		free( tmp1 );
		free( tmp2 );

		// Stash logAppend as the SlotName
		SlotName = logAppend;
	} else {
		// We were not told to append anything to the name of the
		// log file.  This means we are not on an SMP machine,
		// so set SlotName to be slot1 (i.e. cpu 1).
		SlotName = "slot1";
	}

	init_logging();
	dprintf( D_ALWAYS, "********** STARTER starting up ***********\n" );
	dprintf( D_ALWAYS, "** %s\n", CondorVersion() );
	dprintf( D_ALWAYS, "** %s\n", CondorPlatform() );
	dprintf( D_ALWAYS, "******************************************\n" );

	InitiatingHost = submitHost;
	dprintf( D_ALWAYS, "Submitting machine is \"%s\"\n", InitiatingHost );

	init_params();

		/* Now if we have an error, we can print a message */
	if( usageError ) {
		usage( argv[0] );	/* usage() exits */
	}

	init_sig_mask();

	_EXCEPT_Cleanup = exception_cleanup;
}

/*
  Set up file descriptor for log file.  If LOCAL_LOGGING is TRUE, we
  use regular dprintf() logging to the file specified in STARTER_LOG.

  If LOCAL_LOGGING is FALSE, we will send all our logging information
  to the shadow, where it will show up in the shadow's log file.
*/
void
init_logging()
{
	int		is_local = TRUE;
	char	*pval;

	pval = param( "STARTER_LOCAL_LOGGING" );
	if( pval ) {
		if( pval[0] == 'f' || pval[0] == 'F' ) {
			is_local=FALSE;
		}
		free( pval );
	}

	if( is_local ) {
			// Use regular, local logging.
		dprintf_config( get_mySubSystem()->getName() );// Log file on local machine
	} else {
			// Set up to do logging through the shadow
		close( fileno(stderr) );
		dup2( CLIENT_LOG, fileno(stderr) );
		setvbuf( stderr, NULL, _IOLBF, 0 ); // line buffering

		pval = param( "STARTER_DEBUG" );
		if( pval ) {
			set_debug_flags( pval );
			free( pval );
		}
	}
	return;
}

/*
  Create a new connection to the shadow using the existing remote
  system call stream.
*/
ReliSock*
NewConnection( int id )
{
	int 	portno;
	int		syscall_number = CONDOR_new_connection;
	ReliSock* reli = new ReliSock();
	char 	addr[128];
	memset( addr, '\0', 128 );

	SyscallStream->encode();

		// Send the request
	if( !SyscallStream->code(syscall_number) ) {
		EXCEPT( "Can't send CONDOR_new_connection request" );
	}
	if( !SyscallStream->code(id) ) {
		EXCEPT( "Can't send process id for CONDOR_new_connection request" );
	}

		// Turn the stream around
	SyscallStream->end_of_message();
	SyscallStream->decode();

		// Read the port number
	if( !SyscallStream->code(portno) ) {
		EXCEPT( "Can't read port number for new connection" );
	}
	if( portno < 0 ) {
		SyscallStream->code( errno );
		EXCEPT( "Can't get port for new connection" );
	}
	SyscallStream->end_of_message();

		// Create the sinful string we want to connect back to.
	sprintf( addr, "<%s:%d>", SyscallStream->peer_ip_str(), portno );
	dprintf( D_FULLDEBUG, "New Socket address: %s\n", addr );
	if( ! reli->connect(addr, 0) ) {
		EXCEPT( "Can't connect to shadow at %s", addr );
	}
	return reli;
}

/*
	Support USER_JOB_WRAPPER parameter; this function will twiddle
	the a_out_name and args as appropiate.
*/
void
support_job_wrapper(MyString &a_out_name, ArgList *args)
{
	char *wrapper = NULL;

	ASSERT(args);
	ASSERT(args->Count() > 0);

	if ( (wrapper=param("USER_JOB_WRAPPER")) == NULL ) {
			// no wrapper desired, we have nothing to do.
		return;
	}

		// make certain this wrapper program exists and is executable
	if ( access(wrapper,X_OK) < 0 ) {
		EXCEPT("Cannot find/execute USER_JOB_WRAPPER file %s",wrapper);
	}

	// reset old argv[0] to the full path to the job executable
	args->RemoveArg(0);
	args->InsertArg(a_out_name.Value(),0);

		// now reset a_out_name and insert new argv[0]
	a_out_name = wrapper;
	args->InsertArg(condor_basename(wrapper),0);

		// and finally free wrapper, which was malloced by param()
	free(wrapper);

	return;
}

/*
  We've been requested to start the user job with the given UID, but
  we apply our own rules to determine whether we'll honor that request.
*/
void
determine_user_ids( uid_t &requested_uid, gid_t &requested_gid )
{

	struct passwd	*pwd_entry;

		// don't allow any root processes
	if( requested_uid == 0 || requested_gid == 0 ) {
		EXCEPT( "Attempt to start user process with root privileges" );
	}

		// if the submitting machine is in our shared UID domain, honor
		// the request
		// alternatively, if the site has "TRUST_UID_DOMAIN = True" in
		// their config file, we don't do the DNS check
	if( TrustUidDomain || host_in_domain(InitiatingHost, UidDomain) ) {

		/* check to see if there is an entry in the passwd file for this uid */
		if( (pwd_entry=getpwuid(requested_uid)) == NULL ) {
			char *want_soft = NULL;

			if ( (want_soft=param("SOFT_UID_DOMAIN")) == NULL ||
				 (*want_soft != 'T' && *want_soft != 't') ) {
			  EXCEPT("Uid not found in passwd file & SOFT_UID_DOMAIN is False");
			}
			if ( want_soft ) {
				free(want_soft);
			}
		}
		(void)endpwent();

		return;
	}

		// otherwise, we run the process an "nobody"
	if( (pwd_entry = getpwnam("nobody")) == NULL ) {
#ifdef HPUX
		// the HPUX9 release does not have a default entry for nobody,
		// so we'll help condor admins out a bit here...
		requested_uid = 59999;
		requested_gid = 59999;
		return;
#else
		EXCEPT( "Can't find UID for \"nobody\" in passwd file" );
#endif
	}

	requested_uid = pwd_entry->pw_uid;
	requested_gid = pwd_entry->pw_gid;

#ifdef HPUX
	// HPUX9 has a bug in that getpwnam("nobody") always returns
	// a gid of 60001, no matter what the group file (or NIS) says!
	// on top of that, legal UID/GIDs must be -1<x<60000, so unless we
	// patch here, we will generate an EXCEPT later when we try a
	// setgid().  -Todd Tannenbaum, 3/95
	if ( (requested_uid > 59999) || (requested_uid < 0) )
		requested_uid = 59999;
	if ( (requested_gid > 59999) || (requested_gid < 0) )
		requested_gid = 59999;
#endif

}

void
setSlotEnv( Env* env_obj ) {
	// add name of SMP slot (from startd) into environment
	MyString slot_env;
	slot_env.sprintf("_CONDOR_SLOT=%s", SlotName.Value());
	env_obj->SetEnv(slot_env.Value());
	if (param_boolean("ALLOW_VM_CRUFT", false)) {
		slot_env.sprintf("CONDOR_VM=%s", SlotName.Value());
		env_obj->SetEnv(slot_env.Value());
	}
}
<commit_msg>param+char check -> param_boolean_crufty for STARTER_LOCAL_LOGGING in starter_common<commit_after>/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/

/*
** Functions that are shared between condor_starter and condor_starter.smp.
** This is a temporary hack, until we more elegantly merge the PVM
** starter with the real starter.
** Written by Derek Wright <wright@cs.wisc.edu> on 10/8/98.
*/

#include "condor_common.h"
#include "starter_common.h"
#include "condor_version.h"
#include "basename.h"
#include "internet.h"
#include "subsystem_info.h"
#include "std_univ_io.h"

extern int		EventSigs[];
extern char*	InitiatingHost;
extern StdUnivSock	*SyscallStream;	// stream to shadow for remote system calls
extern char*	UidDomain;				// Machines we share UID space with
extern bool		TrustUidDomain;	// Should we trust what the submit side claims?

MyString SlotName;

/*
  Unblock all signals except those which will encode asynchronous
  events.  Those will be unblocked at the proper moment by the finite
  state machine driver.
*/
void
init_sig_mask()
{
	sigset_t	sigmask;
	int			i;

	sigemptyset( &sigmask );
	for( i=0; EventSigs[i]; i++ ) {
		sigaddset( &sigmask, EventSigs[i] );
	}
	(void)sigprocmask( SIG_SETMASK, &sigmask, 0 );
}

void
initial_bookeeping( int /* argc */, char *argv[] )
{
	char*	logAppend = NULL;
	char*	submitHost = NULL;
	int		usageError = FALSE;
	char**	ptr;

	(void) SetSyscalls( SYS_LOCAL | SYS_MAPPED );

           // Process command line args.
	submitHost = argv[1];
	for(ptr = argv + 2; *ptr && !usageError; ptr++) {
		if(ptr[0][0] == '<') {
				// We were passed the startd sinful string, which for
				// now, we're just ignoring.
			continue;
		}
		if(ptr[0][0] == '-') {
			switch( ptr[0][1] ) {
			case 'a':
				ptr++;
				if( ptr && *ptr ) {
					logAppend = *ptr;
				} else {
					usageError = TRUE;
				}
				break;
			default:
				fprintf( stderr, "Error:  Unknown option %s\n", *ptr );
				usageError = TRUE;
			}
		}
	}

		// we need to call this *before* we try to set_condor_priv(),
		// so that if CONDOR_IDS is defined in the config file, we'll
		// get the right value.
	config();

	set_condor_priv();

	init_shadow_connections();

		// If we're told on the command-line to append something to
		// the name of our log file (b/c of the SMP startd), we do
		// that here, so that when we setup logging, we get the right
		// filename.  -Derek Wright 9/2/98
	if( logAppend ) {
		char *tmp1, *tmp2;
		if( !(tmp1 = param("STARTER_LOG")) ) {
			EXCEPT( "STARTER_LOG not defined!" );
		}
		tmp2 = (char*)malloc( (strlen(tmp1) + strlen(logAppend) + 2) *
							  sizeof(char) );
		if( !tmp2 ) {
			EXCEPT( "Out of memory!" );
		}
		sprintf( tmp2, "%s.%s", tmp1, logAppend );
		config_insert( "STARTER_LOG", tmp2 );
		free( tmp1 );
		free( tmp2 );

		// Stash logAppend as the SlotName
		SlotName = logAppend;
	} else {
		// We were not told to append anything to the name of the
		// log file.  This means we are not on an SMP machine,
		// so set SlotName to be slot1 (i.e. cpu 1).
		SlotName = "slot1";
	}

	init_logging();
	dprintf( D_ALWAYS, "********** STARTER starting up ***********\n" );
	dprintf( D_ALWAYS, "** %s\n", CondorVersion() );
	dprintf( D_ALWAYS, "** %s\n", CondorPlatform() );
	dprintf( D_ALWAYS, "******************************************\n" );

	InitiatingHost = submitHost;
	dprintf( D_ALWAYS, "Submitting machine is \"%s\"\n", InitiatingHost );

	init_params();

		/* Now if we have an error, we can print a message */
	if( usageError ) {
		usage( argv[0] );	/* usage() exits */
	}

	init_sig_mask();

	_EXCEPT_Cleanup = exception_cleanup;
}

/*
  Set up file descriptor for log file.  If LOCAL_LOGGING is TRUE, we
  use regular dprintf() logging to the file specified in STARTER_LOG.

  If LOCAL_LOGGING is FALSE, we will send all our logging information
  to the shadow, where it will show up in the shadow's log file.
*/
void
init_logging()
{
	char	*pval;

	if( param_boolean_crufty( "STARTER_LOCAL_LOGGING", true ) ) {
			// Use regular, local logging.
		dprintf_config( get_mySubSystem()->getName() );// Log file on local machine
	} else {
			// Set up to do logging through the shadow
		close( fileno(stderr) );
		dup2( CLIENT_LOG, fileno(stderr) );
		setvbuf( stderr, NULL, _IOLBF, 0 ); // line buffering

		pval = param( "STARTER_DEBUG" );
		if( pval ) {
			set_debug_flags( pval );
			free( pval );
		}
	}
	return;
}

/*
  Create a new connection to the shadow using the existing remote
  system call stream.
*/
ReliSock*
NewConnection( int id )
{
	int 	portno;
	int		syscall_number = CONDOR_new_connection;
	ReliSock* reli = new ReliSock();
	char 	addr[128];
	memset( addr, '\0', 128 );

	SyscallStream->encode();

		// Send the request
	if( !SyscallStream->code(syscall_number) ) {
		EXCEPT( "Can't send CONDOR_new_connection request" );
	}
	if( !SyscallStream->code(id) ) {
		EXCEPT( "Can't send process id for CONDOR_new_connection request" );
	}

		// Turn the stream around
	SyscallStream->end_of_message();
	SyscallStream->decode();

		// Read the port number
	if( !SyscallStream->code(portno) ) {
		EXCEPT( "Can't read port number for new connection" );
	}
	if( portno < 0 ) {
		SyscallStream->code( errno );
		EXCEPT( "Can't get port for new connection" );
	}
	SyscallStream->end_of_message();

		// Create the sinful string we want to connect back to.
	sprintf( addr, "<%s:%d>", SyscallStream->peer_ip_str(), portno );
	dprintf( D_FULLDEBUG, "New Socket address: %s\n", addr );
	if( ! reli->connect(addr, 0) ) {
		EXCEPT( "Can't connect to shadow at %s", addr );
	}
	return reli;
}

/*
	Support USER_JOB_WRAPPER parameter; this function will twiddle
	the a_out_name and args as appropiate.
*/
void
support_job_wrapper(MyString &a_out_name, ArgList *args)
{
	char *wrapper = NULL;

	ASSERT(args);
	ASSERT(args->Count() > 0);

	if ( (wrapper=param("USER_JOB_WRAPPER")) == NULL ) {
			// no wrapper desired, we have nothing to do.
		return;
	}

		// make certain this wrapper program exists and is executable
	if ( access(wrapper,X_OK) < 0 ) {
		EXCEPT("Cannot find/execute USER_JOB_WRAPPER file %s",wrapper);
	}

	// reset old argv[0] to the full path to the job executable
	args->RemoveArg(0);
	args->InsertArg(a_out_name.Value(),0);

		// now reset a_out_name and insert new argv[0]
	a_out_name = wrapper;
	args->InsertArg(condor_basename(wrapper),0);

		// and finally free wrapper, which was malloced by param()
	free(wrapper);

	return;
}

/*
  We've been requested to start the user job with the given UID, but
  we apply our own rules to determine whether we'll honor that request.
*/
void
determine_user_ids( uid_t &requested_uid, gid_t &requested_gid )
{

	struct passwd	*pwd_entry;

		// don't allow any root processes
	if( requested_uid == 0 || requested_gid == 0 ) {
		EXCEPT( "Attempt to start user process with root privileges" );
	}

		// if the submitting machine is in our shared UID domain, honor
		// the request
		// alternatively, if the site has "TRUST_UID_DOMAIN = True" in
		// their config file, we don't do the DNS check
	if( TrustUidDomain || host_in_domain(InitiatingHost, UidDomain) ) {

		/* check to see if there is an entry in the passwd file for this uid */
		if( (pwd_entry=getpwuid(requested_uid)) == NULL ) {
			char *want_soft = NULL;

			if ( (want_soft=param("SOFT_UID_DOMAIN")) == NULL ||
				 (*want_soft != 'T' && *want_soft != 't') ) {
			  EXCEPT("Uid not found in passwd file & SOFT_UID_DOMAIN is False");
			}
			if ( want_soft ) {
				free(want_soft);
			}
		}
		(void)endpwent();

		return;
	}

		// otherwise, we run the process an "nobody"
	if( (pwd_entry = getpwnam("nobody")) == NULL ) {
#ifdef HPUX
		// the HPUX9 release does not have a default entry for nobody,
		// so we'll help condor admins out a bit here...
		requested_uid = 59999;
		requested_gid = 59999;
		return;
#else
		EXCEPT( "Can't find UID for \"nobody\" in passwd file" );
#endif
	}

	requested_uid = pwd_entry->pw_uid;
	requested_gid = pwd_entry->pw_gid;

#ifdef HPUX
	// HPUX9 has a bug in that getpwnam("nobody") always returns
	// a gid of 60001, no matter what the group file (or NIS) says!
	// on top of that, legal UID/GIDs must be -1<x<60000, so unless we
	// patch here, we will generate an EXCEPT later when we try a
	// setgid().  -Todd Tannenbaum, 3/95
	if ( (requested_uid > 59999) || (requested_uid < 0) )
		requested_uid = 59999;
	if ( (requested_gid > 59999) || (requested_gid < 0) )
		requested_gid = 59999;
#endif

}

void
setSlotEnv( Env* env_obj ) {
	// add name of SMP slot (from startd) into environment
	MyString slot_env;
	slot_env.sprintf("_CONDOR_SLOT=%s", SlotName.Value());
	env_obj->SetEnv(slot_env.Value());
	if (param_boolean("ALLOW_VM_CRUFT", false)) {
		slot_env.sprintf("CONDOR_VM=%s", SlotName.Value());
		env_obj->SetEnv(slot_env.Value());
	}
}
<|endoftext|>


<commit_before>/******************************************************************************

  This source file is part of the MoleQueue project.

  Copyright 2012 Kitware, Inc.

  This source code is released under the New BSD License, (the "License").

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

******************************************************************************/

#include <QtTest>

#include "filesystemtools.h"

#include "molequeuetestconfig.h"
#include "testserver.h" // for getRandomSocketName

#include <QtCore/QProcess>
#include <QtCore/QProcessEnvironment>

// Define ENABLE_ZMQ_TESTS if both zeromq and python are available
#ifndef MoleQueue_PYTHON_EXECUTABLE
#ifndef MoleQueue_HAS_ZMQ
#define ENABLE_ZMQ_TESTS
#endif // MoleQueue_HAS_ZMQ
#endif // MoleQueue_PYTHON_EXECUTABLE

class ClientServerTest : public QObject
{
  Q_OBJECT
public:
  ClientServerTest()
    : QObject(NULL),
      m_numClients(10),
      m_workDir(MoleQueue_BINARY_DIR "/testworkdir"),
      m_moleQueueExecutable(MoleQueue_BINARY_DIR "/bin/molequeue"),
      m_serverProcess(NULL)
  {
    randomizeSocketName();
  }

private:
  int m_numClients;
  QString m_workDir;
  QString m_socketName;
  QString m_moleQueueExecutable;
  QStringList m_moleQueueDefaultArgs;
  QProcess *m_serverProcess;
  QList<QProcess*> m_clientProcesses;

  /// Delete the testing workdir and initialize it with the directory at
  /// @a sourcePath.
  bool resetWorkDir(const QString &sourcePath);

  /// Create a new randomized socket name, stored in m_socketName;
  void randomizeSocketName();

  /// Create the server process (m_serverProcess) and reset
  /// m_moleQueueDefaultArgs to set the workdir, socketname, and enable rpcKill.
  bool setupServerProcess();

  /// Create a Cxx client process
  QProcess *addClientProcess();

#ifdef ENABLE_ZMQ_TESTS
  /// Create a client process initialized for python. The process is returned
  /// and added to m_clientProcesses.
  QProcess *addPythonClientProcess();
#endif // ENABLE_ZMQ_TESTS

private slots:
  /// Called before the first test function is executed.
  void initTestCase();
  /// Called after the last test function is executed.
  void cleanupTestCase();
  /// Called before each test function is executed.
  void init();
  /// Called after every test function.
  void cleanup();

  // Python client tests:
#ifdef ENABLE_ZMQ_TESTS
  void submitOnePy();
  void submit200Py();
  void submit200FromManyClientsPy();
#endif // ENABLE_ZMQ_TESTS
};

bool ClientServerTest::resetWorkDir(const QString &sourcePath)
{
  // Initialize working directory
  if (QFileInfo(m_workDir).exists()) {
    if (!MoleQueue::FileSystemTools::recursiveRemoveDirectory(m_workDir)) {
      qWarning() << "Could not remove old working directory" << m_workDir;
      return false;
    }
  }
  if (!MoleQueue::FileSystemTools::recursiveCopyDirectory(sourcePath,
                                                          m_workDir)) {
    qWarning() << "Could not initialize working directory" << m_workDir
               << "from" << sourcePath;
    return false;
  }
  return true;
}

void ClientServerTest::randomizeSocketName()
{
  m_socketName = TestServer::getRandomSocketName();
}

bool ClientServerTest::setupServerProcess()
{
  m_moleQueueDefaultArgs.clear();
  m_moleQueueDefaultArgs
      << "--workdir" << m_workDir
      << "--socketname" << m_socketName
      << "--rpc-kill";

  if (m_serverProcess) {
    delete m_serverProcess;
    m_serverProcess = NULL;
  }
  m_serverProcess = new QProcess(this);
  m_serverProcess->setProcessChannelMode(QProcess::ForwardedChannels);
  return true;
}

QProcess *ClientServerTest::addClientProcess()
{
  QProcess *clientProcess = new QProcess(this);
  clientProcess->setProcessChannelMode(::QProcess::ForwardedChannels);
  m_clientProcesses.append(clientProcess);
  return clientProcess;
}

#ifdef ENABLE_ZMQ_TESTS
QProcess *ClientServerTest::addPythonClientProcess()
{
  QProcess *clientProcess = addClientProcess();
  QProcessEnvironment env = clientProcess->processEnvironment();
  env.insert("PYTHONPATH", (env.value("PYTHONPATH").isEmpty()
                            ? QString()
                            : env.value("PYTHONPATH") + ':') +
             MoleQueue_SOURCE_DIR "/python");
  clientProcess->setProcessEnvironment(env);
  return clientProcess;
}
#endif // ENABLE_ZMQ_TESTS

void ClientServerTest::initTestCase()
{
  QVERIFY2(resetWorkDir(MoleQueue_TESTDATA_DIR "/testworkdir_unix"),
           "Failed to reset working directory for test.");

  // Setup server process
  QVERIFY(setupServerProcess());

  // Start server
  qDebug() << "Starting server:" << m_moleQueueExecutable
           << m_moleQueueDefaultArgs.join(" ");
  m_serverProcess->start(m_moleQueueExecutable, m_moleQueueDefaultArgs);
  QVERIFY(m_serverProcess->waitForStarted(10*1000));
  QTest::qSleep(1 * 1000); // Wait one second for server to start
}

void ClientServerTest::cleanupTestCase()
{
  // send killRpc message
  QProcess *clientProcess = addClientProcess();
  QString clientCommand = MoleQueue_TESTEXEC_DIR "sendRpcKill";
  QStringList clientArguments;
  clientArguments << "-s" << m_socketName;

  qDebug() << "Starting client:" << clientCommand
           << clientArguments.join(" ");
  clientProcess->start(clientCommand, clientArguments);

  // Wait for client to finish
  QVERIFY2(clientProcess->waitForFinished(300*1000), "Client timed out.");
  QCOMPARE(clientProcess->exitCode(), 0);

  // Wait for server to finish
  QVERIFY2(m_serverProcess->waitForFinished(5*1000), "Server timed out.");
  QCOMPARE(m_serverProcess->exitCode(), 0);

  // In case the rpcKill call fails, kill the process
  if (m_serverProcess->state() != QProcess::NotRunning)
    m_serverProcess->kill();
  m_serverProcess->deleteLater();
  m_serverProcess = NULL;

  // Clean up the sendRpcKill client.
  cleanup();
}

void ClientServerTest::init()
{
}

void ClientServerTest::cleanup()
{
  foreach (QProcess *proc, m_clientProcesses) {
    if (proc->state() != QProcess::NotRunning)
      proc->kill();
  }
  qDeleteAll(m_clientProcesses);
  m_clientProcesses.clear();
}

#ifdef ENABLE_ZMQ_TESTS
void ClientServerTest::submitOnePy()
{
  // Setup client process
  QProcess *clientProcess = addPythonClientProcess();
  QString clientCommand = MoleQueue_PYTHON_EXECUTABLE;
  QStringList clientArguments;
  clientArguments
      << MoleQueue_TESTSCRIPT_DIR "/submitJob.py"
      << "-s" << m_socketName
      << "-n" << QString::number(1);

  qDebug() << "Starting client:" << clientCommand
           << clientArguments.join(" ");

  clientProcess->start(clientCommand, QStringList(clientArguments));

  // Wait 5 seconds for client to start
  QVERIFY2(clientProcess->waitForStarted(5*1000), "Client did not start.");

  // Wait 10 seconds for client to finish
  QVERIFY2(clientProcess->waitForFinished(10*1000), "Client timed out.");
  QCOMPARE(clientProcess->exitCode(), 0);
}

void ClientServerTest::submit200Py()
{
  // Setup client process
  QProcess *clientProcess = addPythonClientProcess();
  QString clientCommand = MoleQueue_PYTHON_EXECUTABLE;
  QStringList clientArguments;
  clientArguments
      << MoleQueue_TESTSCRIPT_DIR "/submitJob.py"
      << "-s" << m_socketName
      << "-n" << QString::number(200);

  qDebug() << "Starting client:" << clientCommand
           << clientArguments.join(" ");

  clientProcess->start(clientCommand, QStringList(clientArguments));

  // Wait 5 seconds for client to start
  QVERIFY2(clientProcess->waitForStarted(5*1000), "Client did not start.");

  // Wait one minute for client to finish
  QVERIFY2(clientProcess->waitForFinished(60*1000), "Client timed out.");
  QCOMPARE(clientProcess->exitCode(), 0);
}

void ClientServerTest::submit200FromManyClientsPy()
{
  // Setup client processes
  while (m_clientProcesses.size() < m_numClients)
    addPythonClientProcess();
  QString clientCommand = MoleQueue_PYTHON_EXECUTABLE;
  QStringList clientArguments;
  clientArguments
      << MoleQueue_TESTSCRIPT_DIR "/submitJob.py"
      << "-s" << m_socketName
      << "-n" << QString::number(200);

  qDebug() << "Starting" << m_numClients << "clients:" << clientCommand
           << clientArguments.join(" ");

  int clientId = 0;
  foreach (QProcess *cliProc, m_clientProcesses) {
    cliProc->start(clientCommand,
                   QStringList(clientArguments)
                   << "-c" << QString::number(++clientId));
  }

  // Wait 5 seconds for each client to start
  clientId = 0;
  foreach (QProcess *cliProc, m_clientProcesses) {
    ++clientId;
    QVERIFY2(cliProc->waitForStarted(5*1000),
             (QByteArray("Client ") + QByteArray::number(clientId) +
              QByteArray(" failed to start.")).constData());
  }

  // Wait two minutes for all clients to finish
  clientId = 0;
  foreach (QProcess *cliProc, m_clientProcesses) {
    ++clientId;
    QVERIFY2(cliProc->waitForFinished(2*60*1000),
             (QByteArray("Client ") + QByteArray::number(clientId) +
              QByteArray(" timed out.")).constData());
    QCOMPARE(cliProc->exitCode(), 0);
  }
}
#endif // ENABLE_ZMQ_TESTS
QTEST_MAIN(ClientServerTest)

#include "clientservertest.moc"
<commit_msg>Fix path to molequeue server on MacOS.<commit_after>/******************************************************************************

  This source file is part of the MoleQueue project.

  Copyright 2012 Kitware, Inc.

  This source code is released under the New BSD License, (the "License").

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

******************************************************************************/

#include <QtTest>

#include "filesystemtools.h"

#include "molequeuetestconfig.h"
#include "testserver.h" // for getRandomSocketName

#include <QtCore/QProcess>
#include <QtCore/QProcessEnvironment>

// Define ENABLE_ZMQ_TESTS if both zeromq and python are available
#ifndef MoleQueue_PYTHON_EXECUTABLE
#ifndef MoleQueue_HAS_ZMQ
#define ENABLE_ZMQ_TESTS
#endif // MoleQueue_HAS_ZMQ
#endif // MoleQueue_PYTHON_EXECUTABLE

class ClientServerTest : public QObject
{
  Q_OBJECT
public:
  ClientServerTest()
    : QObject(NULL),
      m_numClients(10),
      m_workDir(MoleQueue_BINARY_DIR "/testworkdir"),
      m_moleQueueExecutable(MoleQueue_BINARY_DIR "/bin/molequeue"),
      m_serverProcess(NULL)
  {
#ifdef __APPLE__
    m_moleQueueExecutable = MoleQueue_BINARY_DIR "/bin/molequeue.app/Contents/"
        "MacOS/molequeue";
#endif // __APPLE__
    randomizeSocketName();
  }

private:
  int m_numClients;
  QString m_workDir;
  QString m_socketName;
  QString m_moleQueueExecutable;
  QStringList m_moleQueueDefaultArgs;
  QProcess *m_serverProcess;
  QList<QProcess*> m_clientProcesses;

  /// Delete the testing workdir and initialize it with the directory at
  /// @a sourcePath.
  bool resetWorkDir(const QString &sourcePath);

  /// Create a new randomized socket name, stored in m_socketName;
  void randomizeSocketName();

  /// Create the server process (m_serverProcess) and reset
  /// m_moleQueueDefaultArgs to set the workdir, socketname, and enable rpcKill.
  bool setupServerProcess();

  /// Create a Cxx client process
  QProcess *addClientProcess();

#ifdef ENABLE_ZMQ_TESTS
  /// Create a client process initialized for python. The process is returned
  /// and added to m_clientProcesses.
  QProcess *addPythonClientProcess();
#endif // ENABLE_ZMQ_TESTS

private slots:
  /// Called before the first test function is executed.
  void initTestCase();
  /// Called after the last test function is executed.
  void cleanupTestCase();
  /// Called before each test function is executed.
  void init();
  /// Called after every test function.
  void cleanup();

  // Python client tests:
#ifdef ENABLE_ZMQ_TESTS
  void submitOnePy();
  void submit200Py();
  void submit200FromManyClientsPy();
#endif // ENABLE_ZMQ_TESTS
};

bool ClientServerTest::resetWorkDir(const QString &sourcePath)
{
  // Initialize working directory
  if (QFileInfo(m_workDir).exists()) {
    if (!MoleQueue::FileSystemTools::recursiveRemoveDirectory(m_workDir)) {
      qWarning() << "Could not remove old working directory" << m_workDir;
      return false;
    }
  }
  if (!MoleQueue::FileSystemTools::recursiveCopyDirectory(sourcePath,
                                                          m_workDir)) {
    qWarning() << "Could not initialize working directory" << m_workDir
               << "from" << sourcePath;
    return false;
  }
  return true;
}

void ClientServerTest::randomizeSocketName()
{
  m_socketName = TestServer::getRandomSocketName();
}

bool ClientServerTest::setupServerProcess()
{
  m_moleQueueDefaultArgs.clear();
  m_moleQueueDefaultArgs
      << "--workdir" << m_workDir
      << "--socketname" << m_socketName
      << "--rpc-kill";

  if (m_serverProcess) {
    delete m_serverProcess;
    m_serverProcess = NULL;
  }
  m_serverProcess = new QProcess(this);
  m_serverProcess->setProcessChannelMode(QProcess::ForwardedChannels);
  return true;
}

QProcess *ClientServerTest::addClientProcess()
{
  QProcess *clientProcess = new QProcess(this);
  clientProcess->setProcessChannelMode(::QProcess::ForwardedChannels);
  m_clientProcesses.append(clientProcess);
  return clientProcess;
}

#ifdef ENABLE_ZMQ_TESTS
QProcess *ClientServerTest::addPythonClientProcess()
{
  QProcess *clientProcess = addClientProcess();
  QProcessEnvironment env = clientProcess->processEnvironment();
  env.insert("PYTHONPATH", (env.value("PYTHONPATH").isEmpty()
                            ? QString()
                            : env.value("PYTHONPATH") + ':') +
             MoleQueue_SOURCE_DIR "/python");
  clientProcess->setProcessEnvironment(env);
  return clientProcess;
}
#endif // ENABLE_ZMQ_TESTS

void ClientServerTest::initTestCase()
{
  QVERIFY2(resetWorkDir(MoleQueue_TESTDATA_DIR "/testworkdir_unix"),
           "Failed to reset working directory for test.");

  // Setup server process
  QVERIFY(setupServerProcess());

  // Start server
  qDebug() << "Starting server:" << m_moleQueueExecutable
           << m_moleQueueDefaultArgs.join(" ");
  m_serverProcess->start(m_moleQueueExecutable, m_moleQueueDefaultArgs);
  QVERIFY(m_serverProcess->waitForStarted(10*1000));
  QTest::qSleep(1 * 1000); // Wait one second for server to start
}

void ClientServerTest::cleanupTestCase()
{
  // send killRpc message
  QProcess *clientProcess = addClientProcess();
  QString clientCommand = MoleQueue_TESTEXEC_DIR "sendRpcKill";
  QStringList clientArguments;
  clientArguments << "-s" << m_socketName;

  qDebug() << "Starting client:" << clientCommand
           << clientArguments.join(" ");
  clientProcess->start(clientCommand, clientArguments);

  // Wait for client to finish
  QVERIFY2(clientProcess->waitForFinished(300*1000), "Client timed out.");
  QCOMPARE(clientProcess->exitCode(), 0);

  // Wait for server to finish
  QVERIFY2(m_serverProcess->waitForFinished(5*1000), "Server timed out.");
  QCOMPARE(m_serverProcess->exitCode(), 0);

  // In case the rpcKill call fails, kill the process
  if (m_serverProcess->state() != QProcess::NotRunning)
    m_serverProcess->kill();
  m_serverProcess->deleteLater();
  m_serverProcess = NULL;

  // Clean up the sendRpcKill client.
  cleanup();
}

void ClientServerTest::init()
{
}

void ClientServerTest::cleanup()
{
  foreach (QProcess *proc, m_clientProcesses) {
    if (proc->state() != QProcess::NotRunning)
      proc->kill();
  }
  qDeleteAll(m_clientProcesses);
  m_clientProcesses.clear();
}

#ifdef ENABLE_ZMQ_TESTS
void ClientServerTest::submitOnePy()
{
  // Setup client process
  QProcess *clientProcess = addPythonClientProcess();
  QString clientCommand = MoleQueue_PYTHON_EXECUTABLE;
  QStringList clientArguments;
  clientArguments
      << MoleQueue_TESTSCRIPT_DIR "/submitJob.py"
      << "-s" << m_socketName
      << "-n" << QString::number(1);

  qDebug() << "Starting client:" << clientCommand
           << clientArguments.join(" ");

  clientProcess->start(clientCommand, QStringList(clientArguments));

  // Wait 5 seconds for client to start
  QVERIFY2(clientProcess->waitForStarted(5*1000), "Client did not start.");

  // Wait 10 seconds for client to finish
  QVERIFY2(clientProcess->waitForFinished(10*1000), "Client timed out.");
  QCOMPARE(clientProcess->exitCode(), 0);
}

void ClientServerTest::submit200Py()
{
  // Setup client process
  QProcess *clientProcess = addPythonClientProcess();
  QString clientCommand = MoleQueue_PYTHON_EXECUTABLE;
  QStringList clientArguments;
  clientArguments
      << MoleQueue_TESTSCRIPT_DIR "/submitJob.py"
      << "-s" << m_socketName
      << "-n" << QString::number(200);

  qDebug() << "Starting client:" << clientCommand
           << clientArguments.join(" ");

  clientProcess->start(clientCommand, QStringList(clientArguments));

  // Wait 5 seconds for client to start
  QVERIFY2(clientProcess->waitForStarted(5*1000), "Client did not start.");

  // Wait one minute for client to finish
  QVERIFY2(clientProcess->waitForFinished(60*1000), "Client timed out.");
  QCOMPARE(clientProcess->exitCode(), 0);
}

void ClientServerTest::submit200FromManyClientsPy()
{
  // Setup client processes
  while (m_clientProcesses.size() < m_numClients)
    addPythonClientProcess();
  QString clientCommand = MoleQueue_PYTHON_EXECUTABLE;
  QStringList clientArguments;
  clientArguments
      << MoleQueue_TESTSCRIPT_DIR "/submitJob.py"
      << "-s" << m_socketName
      << "-n" << QString::number(200);

  qDebug() << "Starting" << m_numClients << "clients:" << clientCommand
           << clientArguments.join(" ");

  int clientId = 0;
  foreach (QProcess *cliProc, m_clientProcesses) {
    cliProc->start(clientCommand,
                   QStringList(clientArguments)
                   << "-c" << QString::number(++clientId));
  }

  // Wait 5 seconds for each client to start
  clientId = 0;
  foreach (QProcess *cliProc, m_clientProcesses) {
    ++clientId;
    QVERIFY2(cliProc->waitForStarted(5*1000),
             (QByteArray("Client ") + QByteArray::number(clientId) +
              QByteArray(" failed to start.")).constData());
  }

  // Wait two minutes for all clients to finish
  clientId = 0;
  foreach (QProcess *cliProc, m_clientProcesses) {
    ++clientId;
    QVERIFY2(cliProc->waitForFinished(2*60*1000),
             (QByteArray("Client ") + QByteArray::number(clientId) +
              QByteArray(" timed out.")).constData());
    QCOMPARE(cliProc->exitCode(), 0);
  }
}
#endif // ENABLE_ZMQ_TESTS
QTEST_MAIN(ClientServerTest)

#include "clientservertest.moc"
<|endoftext|>


<commit_before>#include "CommonData.hpp"
#include "Config.hpp"
#include "EventInputQueue.hpp"
#include "FlagStatus.hpp"
#include "IOLockWrapper.hpp"
#include "ListHookedPointing.hpp"

namespace org_pqrs_KeyRemap4MacBook {
  namespace {
    ListHookedPointing listHookedPointing;
  }

  ListHookedPointing&
  ListHookedPointing::instance(void)
  {
    return listHookedPointing;
  }

  ListHookedPointing::Item::Item(IOHIDevice* p) : ListHookedDevice::Item(p),
    orig_relativePointerEventAction_(NULL),
    orig_relativePointerEventTarget_(NULL),
    orig_scrollWheelEventAction_(NULL),
    orig_scrollWheelEventTarget_(NULL),
    previousbuttons_(0)
  {
  }

  ListHookedPointing::Item::~Item(void)
  {
    IOLOG_DEBUG("ListHookedPointing::Item::~Item()\n");
    restoreEventAction();
  }

  bool
  ListHookedPointing::Item::refresh(void)
  {
    if (! device_) goto restore;

    // ------------------------------------------------------------
    if (! Config::get_initialized()) {
      goto restore;
    }
    if (Config::get_essential_config(BRIDGE_ESSENTIAL_CONFIG_INDEX_general_dont_remap_thirdvendor_pointing) &&
        deviceType_ != DeviceType::APPLE_INTERNAL &&
        deviceType_ != DeviceType::APPLE_EXTERNAL) {
      goto restore;
    }
    if (Config::get_essential_config(BRIDGE_ESSENTIAL_CONFIG_INDEX_general_dont_remap_apple_pointing) &&
        isEqualVendor(DeviceVendor::APPLE_COMPUTER)) {
      goto restore;
    }

    // Logitech Cordless Presenter
    if (Config::get_essential_config(BRIDGE_ESSENTIAL_CONFIG_INDEX_general_dont_remap_logitech_cordless_presenter) &&
        isEqualVendorProduct(DeviceVendor::LOGITECH, DeviceProduct::LOGITECH_CORDLESS_PRESENTER)) {
      goto restore;
    }
#if 0
    // Apple Magic Mouse
    if (isEqualVendorProduct(DeviceVendor::APPLE_COMPUTER, DeviceProduct::MAGIC_MOUSE)) {
      goto restore;
    }
#endif

    return replaceEventAction();

  restore:
    return restoreEventAction();
  }

  bool
  ListHookedPointing::Item::replaceEventAction(void)
  {
    if (! device_) return false;

    IOHIPointing* pointing = OSDynamicCast(IOHIPointing, device_);
    if (! pointing) return false;

    bool result = false;

    // ----------------------------------------
    {
      RelativePointerEventCallback callback = reinterpret_cast<RelativePointerEventCallback>(pointing->_relativePointerEventAction);

      if (callback != EventInputQueue::push_RelativePointerEventCallback) {
        IOLOG_DEBUG("ListHookedPointing::Item::replaceEventAction (RelativePointerEventCallback) device_:%p\n", device_);

        orig_relativePointerEventAction_ = callback;
        orig_relativePointerEventTarget_ = pointing->_relativePointerEventTarget;

        pointing->_relativePointerEventAction = reinterpret_cast<RelativePointerEventAction>(EventInputQueue::push_RelativePointerEventCallback);

        result = true;
      }
    }
    {
      ScrollWheelEventCallback callback = reinterpret_cast<ScrollWheelEventCallback>(pointing->_scrollWheelEventAction);

      if (callback != EventInputQueue::push_ScrollWheelEventCallback) {
        IOLOG_DEBUG("ListHookedPointing::Item::replaceEventAction (ScrollWheelEventCallback) device_:%p\n", device_);

        orig_scrollWheelEventAction_ = callback;
        orig_scrollWheelEventTarget_ = pointing->_scrollWheelEventTarget;

        pointing->_scrollWheelEventAction = reinterpret_cast<ScrollWheelEventAction>(EventInputQueue::push_ScrollWheelEventCallback);

        result = true;
      }
    }

    return result;
  }

  bool
  ListHookedPointing::Item::restoreEventAction(void)
  {
    if (! device_) return false;

    IOHIPointing* pointing = OSDynamicCast(IOHIPointing, device_);
    if (! pointing) return false;

    bool result = false;

    // ----------------------------------------
    {
      RelativePointerEventCallback callback = reinterpret_cast<RelativePointerEventCallback>(pointing->_relativePointerEventAction);

      if (callback == EventInputQueue::push_RelativePointerEventCallback) {
        IOLOG_DEBUG("HookedPointing::restoreEventAction (RelativePointerEventCallback) device_:%p\n", device_);

        pointing->_relativePointerEventAction = reinterpret_cast<RelativePointerEventAction>(orig_relativePointerEventAction_);

        result = true;
      }
    }
    {
      ScrollWheelEventCallback callback = reinterpret_cast<ScrollWheelEventCallback>(pointing->_scrollWheelEventAction);

      if (callback == EventInputQueue::push_ScrollWheelEventCallback) {
        IOLOG_DEBUG("HookedPointing::restoreEventAction (ScrollWheelEventCallback) device_:%p\n", device_);

        pointing->_scrollWheelEventAction = reinterpret_cast<ScrollWheelEventAction>(orig_scrollWheelEventAction_);

        result = true;
      }
    }

    orig_relativePointerEventAction_ = NULL;
    orig_scrollWheelEventAction_ = NULL;
    orig_relativePointerEventTarget_ = NULL;
    orig_scrollWheelEventTarget_ = NULL;

    return result;
  }

  // ======================================================================
  void
  ListHookedPointing::Item::apply(const Params_RelativePointerEventCallback& params)
  {
    RelativePointerEventCallback callback = orig_relativePointerEventAction_;
    if (! callback) return;

    OSObject* target = orig_relativePointerEventTarget_;
    if (! target) return;

    OSObject* sender = OSDynamicCast(OSObject, device_);
    if (! sender) return;

    const AbsoluteTime& ts = CommonData::getcurrent_ts();
    OSObject* refcon = NULL;

    params.log("sending");
    callback(target, params.buttons.get(), params.dx, params.dy, ts, sender, refcon);
  }

  void
  ListHookedPointing::Item::apply(const Params_ScrollWheelEventCallback& params)
  {
    ScrollWheelEventCallback callback = orig_scrollWheelEventAction_;
    if (! callback) return;

    OSObject* target = orig_scrollWheelEventTarget_;
    if (! target) return;

    OSObject* sender = OSDynamicCast(OSObject, device_);
    if (! sender) return;

    const AbsoluteTime& ts = CommonData::getcurrent_ts();
    OSObject* refcon = NULL;

    params.log("sending");
    callback(target,
             params.deltaAxis1,  params.deltaAxis2,  params.deltaAxis3,
             params.fixedDelta1, params.fixedDelta2, params.fixedDelta3,
             params.pointDelta1, params.pointDelta2, params.pointDelta3,
             params.options, ts, sender, refcon);
  }

  void
  ListHookedPointing::apply(const Params_RelativePointerEventCallback& params)
  {
    // if all button are released, send event for all devices.
    if (params.buttons == Buttons(0) &&
        params.dx == 0 &&
        params.dy == 0) {
      for (Item* p = static_cast<Item*>(list_->front()); p; p = static_cast<Item*>(p->getnext())) {
        if (! p->isReplaced()) continue;

        p->apply(params);
      }

    } else {
      ListHookedPointing::Item* p = static_cast<ListHookedPointing::Item*>(get_replaced_nolock());
      if (p) {
        p->apply(params);
      }
    }
  }

  void
  ListHookedPointing::apply(const Params_ScrollWheelEventCallback& params)
  {
    ListHookedPointing::Item* p = static_cast<ListHookedPointing::Item*>(get_replaced_nolock());
    if (p) {
      p->apply(params);
    }
  }
}
<commit_msg>apply uncrustify<commit_after>#include "CommonData.hpp"
#include "Config.hpp"
#include "EventInputQueue.hpp"
#include "FlagStatus.hpp"
#include "IOLockWrapper.hpp"
#include "ListHookedPointing.hpp"

namespace org_pqrs_KeyRemap4MacBook {
  namespace {
    ListHookedPointing listHookedPointing;
  }

  ListHookedPointing&
  ListHookedPointing::instance(void)
  {
    return listHookedPointing;
  }

  ListHookedPointing::Item::Item(IOHIDevice* p) : ListHookedDevice::Item(p),
    orig_relativePointerEventAction_(NULL),
    orig_relativePointerEventTarget_(NULL),
    orig_scrollWheelEventAction_(NULL),
    orig_scrollWheelEventTarget_(NULL),
    previousbuttons_(0)
  {}

  ListHookedPointing::Item::~Item(void)
  {
    IOLOG_DEBUG("ListHookedPointing::Item::~Item()\n");
    restoreEventAction();
  }

  bool
  ListHookedPointing::Item::refresh(void)
  {
    if (! device_) goto restore;

    // ------------------------------------------------------------
    if (! Config::get_initialized()) {
      goto restore;
    }
    if (Config::get_essential_config(BRIDGE_ESSENTIAL_CONFIG_INDEX_general_dont_remap_thirdvendor_pointing) &&
        deviceType_ != DeviceType::APPLE_INTERNAL &&
        deviceType_ != DeviceType::APPLE_EXTERNAL) {
      goto restore;
    }
    if (Config::get_essential_config(BRIDGE_ESSENTIAL_CONFIG_INDEX_general_dont_remap_apple_pointing) &&
        isEqualVendor(DeviceVendor::APPLE_COMPUTER)) {
      goto restore;
    }

    // Logitech Cordless Presenter
    if (Config::get_essential_config(BRIDGE_ESSENTIAL_CONFIG_INDEX_general_dont_remap_logitech_cordless_presenter) &&
        isEqualVendorProduct(DeviceVendor::LOGITECH, DeviceProduct::LOGITECH_CORDLESS_PRESENTER)) {
      goto restore;
    }
#if 0
    // Apple Magic Mouse
    if (isEqualVendorProduct(DeviceVendor::APPLE_COMPUTER, DeviceProduct::MAGIC_MOUSE)) {
      goto restore;
    }
#endif

    return replaceEventAction();

  restore:
    return restoreEventAction();
  }

  bool
  ListHookedPointing::Item::replaceEventAction(void)
  {
    if (! device_) return false;

    IOHIPointing* pointing = OSDynamicCast(IOHIPointing, device_);
    if (! pointing) return false;

    bool result = false;

    // ----------------------------------------
    {
      RelativePointerEventCallback callback = reinterpret_cast<RelativePointerEventCallback>(pointing->_relativePointerEventAction);

      if (callback != EventInputQueue::push_RelativePointerEventCallback) {
        IOLOG_DEBUG("ListHookedPointing::Item::replaceEventAction (RelativePointerEventCallback) device_:%p\n", device_);

        orig_relativePointerEventAction_ = callback;
        orig_relativePointerEventTarget_ = pointing->_relativePointerEventTarget;

        pointing->_relativePointerEventAction = reinterpret_cast<RelativePointerEventAction>(EventInputQueue::push_RelativePointerEventCallback);

        result = true;
      }
    }
    {
      ScrollWheelEventCallback callback = reinterpret_cast<ScrollWheelEventCallback>(pointing->_scrollWheelEventAction);

      if (callback != EventInputQueue::push_ScrollWheelEventCallback) {
        IOLOG_DEBUG("ListHookedPointing::Item::replaceEventAction (ScrollWheelEventCallback) device_:%p\n", device_);

        orig_scrollWheelEventAction_ = callback;
        orig_scrollWheelEventTarget_ = pointing->_scrollWheelEventTarget;

        pointing->_scrollWheelEventAction = reinterpret_cast<ScrollWheelEventAction>(EventInputQueue::push_ScrollWheelEventCallback);

        result = true;
      }
    }

    return result;
  }

  bool
  ListHookedPointing::Item::restoreEventAction(void)
  {
    if (! device_) return false;

    IOHIPointing* pointing = OSDynamicCast(IOHIPointing, device_);
    if (! pointing) return false;

    bool result = false;

    // ----------------------------------------
    {
      RelativePointerEventCallback callback = reinterpret_cast<RelativePointerEventCallback>(pointing->_relativePointerEventAction);

      if (callback == EventInputQueue::push_RelativePointerEventCallback) {
        IOLOG_DEBUG("HookedPointing::restoreEventAction (RelativePointerEventCallback) device_:%p\n", device_);

        pointing->_relativePointerEventAction = reinterpret_cast<RelativePointerEventAction>(orig_relativePointerEventAction_);

        result = true;
      }
    }
    {
      ScrollWheelEventCallback callback = reinterpret_cast<ScrollWheelEventCallback>(pointing->_scrollWheelEventAction);

      if (callback == EventInputQueue::push_ScrollWheelEventCallback) {
        IOLOG_DEBUG("HookedPointing::restoreEventAction (ScrollWheelEventCallback) device_:%p\n", device_);

        pointing->_scrollWheelEventAction = reinterpret_cast<ScrollWheelEventAction>(orig_scrollWheelEventAction_);

        result = true;
      }
    }

    orig_relativePointerEventAction_ = NULL;
    orig_scrollWheelEventAction_ = NULL;
    orig_relativePointerEventTarget_ = NULL;
    orig_scrollWheelEventTarget_ = NULL;

    return result;
  }

  // ======================================================================
  void
  ListHookedPointing::Item::apply(const Params_RelativePointerEventCallback& params)
  {
    RelativePointerEventCallback callback = orig_relativePointerEventAction_;
    if (! callback) return;

    OSObject* target = orig_relativePointerEventTarget_;
    if (! target) return;

    OSObject* sender = OSDynamicCast(OSObject, device_);
    if (! sender) return;

    const AbsoluteTime& ts = CommonData::getcurrent_ts();
    OSObject* refcon = NULL;

    params.log("sending");
    callback(target, params.buttons.get(), params.dx, params.dy, ts, sender, refcon);
  }

  void
  ListHookedPointing::Item::apply(const Params_ScrollWheelEventCallback& params)
  {
    ScrollWheelEventCallback callback = orig_scrollWheelEventAction_;
    if (! callback) return;

    OSObject* target = orig_scrollWheelEventTarget_;
    if (! target) return;

    OSObject* sender = OSDynamicCast(OSObject, device_);
    if (! sender) return;

    const AbsoluteTime& ts = CommonData::getcurrent_ts();
    OSObject* refcon = NULL;

    params.log("sending");
    callback(target,
             params.deltaAxis1,  params.deltaAxis2,  params.deltaAxis3,
             params.fixedDelta1, params.fixedDelta2, params.fixedDelta3,
             params.pointDelta1, params.pointDelta2, params.pointDelta3,
             params.options, ts, sender, refcon);
  }

  void
  ListHookedPointing::apply(const Params_RelativePointerEventCallback& params)
  {
    // if all button are released, send event for all devices.
    if (params.buttons == Buttons(0) &&
        params.dx == 0 &&
        params.dy == 0) {
      for (Item* p = static_cast<Item*>(list_->front()); p; p = static_cast<Item*>(p->getnext())) {
        if (! p->isReplaced()) continue;

        p->apply(params);
      }

    } else {
      ListHookedPointing::Item* p = static_cast<ListHookedPointing::Item*>(get_replaced_nolock());
      if (p) {
        p->apply(params);
      }
    }
  }

  void
  ListHookedPointing::apply(const Params_ScrollWheelEventCallback& params)
  {
    ListHookedPointing::Item* p = static_cast<ListHookedPointing::Item*>(get_replaced_nolock());
    if (p) {
      p->apply(params);
    }
  }
}
<|endoftext|>