<commit_before>// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
//
// Copyright 2015 Heiko Fink, All Rights Reserved.
//
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
//
// Perception Neuron (TM) is a trademark of Beijing Noitom Technology Ltd.
//
// Description:
// This code implements the Controller functions that are used to connect to the Perception Neuron (TM) Axis Player.
//

#include "PerceptionNeuronPrivatePCH.h"
#include "PerceptionNeuronController.h"

// Sets default values
APerceptionNeuronController::APerceptionNeuronController()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void APerceptionNeuronController::BeginPlay()
{
	Super::BeginPlay();

}

// Called every frame
void APerceptionNeuronController::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	// Read each tick a new motionline. Additionally motionlines are just discarded.
	if ((bConnected == true) || (bPlay == true))
	{
		uint32 BytesPending = 0;

		if (bConnected == true)
			if (ReceiverSocket->HasPendingData(BytesPending) != true)
				BytesPending = 0;

		if ((BytesPending) || (bPlay == true))
		{
#define SIZEOFDATA 10000
			uint8 Data[SIZEOFDATA];
			int32 BytesRead = 0;
			bool bMotionLineBeginFound = false;
			bool bMotionLineEndFound = false;

			if (bConnected == true) // Network connection?
			{
				if (ReceiverSocket->Recv(Data, SIZEOFDATA, BytesRead) != true)
					BytesRead = 0;
			}
			else if (bPlay == true)	// Player connected ?
			{
				DeltaTimeAdded += DeltaTime;
				if (FrameTime < DeltaTimeAdded) // Synchronize to FrameTime
				{
					DeltaTimeAdded -= FrameTime;
					while (DeltaTimeAdded > FrameTime) // We are too slow => Skip frames
					{
						DeltaTimeAdded -= FrameTime;
						MotionLinePointer++;
						if ((MotionLinePointer >= (MotionLineOffset + Frames)) && (bEndless == true))
							MotionLinePointer = MotionLineOffset;
					}

					if (MotionLinePointer < (MotionLineOffset + Frames))
					{
						int32 CurrentMotionLine = MotionLinePointer;
						if (SIZEOFDATA >= PlayerMotionLines[CurrentMotionLine].Len() + 1) // Sanity check
						{
							// Convert string based motion line back to an array of chars to pump the line through the parser (Little bit ugly...)
							memcpy(Data, TCHAR_TO_ANSI(*PlayerMotionLines[CurrentMotionLine]), PlayerMotionLines[CurrentMotionLine].Len());
							Data[PlayerMotionLines[CurrentMotionLine].Len()] = '\n';
							BytesRead = PlayerMotionLines[CurrentMotionLine].Len() + 1;
						}
						MotionLinePointer++; // Increment line for next time;
						if ((MotionLinePointer >= (MotionLineOffset + Frames)) && (bEndless == true))
							MotionLinePointer = MotionLineOffset;
					}
				}
			}

			if (BytesRead)
			{
				int32 i, j, k;

				if (MotionLineFormat == Neuron)
				{
					// Scan for the beginning of a motion line and synchronize to Avatarname
					// Neuron BVH output format:
					// 0 Avatarname 0.00 0.00 ... 0.00 ||
					for (i = j = k = 0; i < BytesRead - 2; i++)
					{
						if ((Data[i] == '0') && (Data[i + 1] == ' ') && ((Data[i + 2] < '0') || (Data[i + 2] > '9')) && (Data[i + 2] != '-'))
						{
							bMotionLineBeginFound = true;
							// Skip Avatarname
							for (j = i + 2; j < BytesRead; j++)
							{
								if (Data[j] == ' ')
									break;
							}
							break;
						}
					}
					if (bMotionLineBeginFound == true)
					{
						// Scan for the end of the motion line
						for (k = j; k < BytesRead; k++)
						{
							if ((Data[k] == '|') || (Data[k] == '\n') || (Data[k] == '\r'))
							{
								bMotionLineEndFound = true;
								break;
							}
						}
					}
				}
				else
				{
					// Just synchronize to first float and check if floatcount matches bonecount else try next line
					// Standard BVH format:
					// 0.00 0.00 ... 0.00
					i = j = k = 0;
					for (; i < BytesRead - 1;)
					{
						for (; i < BytesRead; i++)
						{
							if ((Data[i] >= '0') && (Data[i] <= '9') || (Data[i] == '-') || (Data[i] == '.'))
							{
								j = i;
								bMotionLineBeginFound = true;
								break;
							}
						}
						if (bMotionLineBeginFound == true)
						{
							int32 t = 0;
							bMotionLineEndFound = false;
							// Scan for the end of the motion line and count empty spaces between floats
							for (i = j; i < BytesRead; i++)
							{
								if (i < (BytesRead - 1))
									if (((Data[i] == ' ') || (Data[i] == '\t')) && (((Data[i + 1] >= '0') && (Data[i + 1] <= '9')) || (Data[i + 1] == '-') || (Data[i + 1] == '.')))
										t++;
								if ((Data[i] == '\n') || (Data[i] == '\r'))
								{
									bMotionLineEndFound = true;
									break;
								}
							}
							if (bMotionLineEndFound)
							{
								int32 FloatsPerBone = 6;	 // 3 for x,y,z translation and 3 for x,y,z rotation
								int32 FloatsPerReference = 6; // 3 for x,y,z translation and 3 for x,y,z rotation

								if (bDisplacement == false)
									FloatsPerBone = 3;	// If there is no displacement (translation) info we have only 3 floats for rotation left
								if (bReference == false)
									FloatsPerReference = 0;
								// Check if empty spaces matches bone count
								if (t == ((Skeleton.BoneNr * FloatsPerBone) + FloatsPerReference - 1))
								{
									k = i;
									break;
								}
							}
						}
					}
				}

				if (bMotionLineEndFound == true)
				{
					// We have now a valid motion line captured between j and k

					// Forward to first value
					for (i = j; i < k; i++)
						if ((Data[i] >= '0') && (Data[i] <= '9') || (Data[i] == '-') || (Data[i] == '.'))
							break;

					int32 m = 0;
					// Get all float values from motion line
					for (int32 f = 0; f < MAXFLOATS && i < k; f++)
					{
						// Read ASCII float value into float array
						if (f >= FloatSkip)
						{
							MotionLine[m] = atof((char *)&Data[i]);
							m++;
						}
						// Forward to end of value we read
						for (; i < k; i++)
							if (((Data[i] < '0') || (Data[i] > '9')) && (Data[i] != '-') && (Data[i] != '.'))
								break;

						// Forward to next value
						for (; i < k; i++)
						{
							// Sanitycheck for line ending
							if ((Data[i] == '|') || (Data[i] == '\n') || (Data[i] == '\r'))
							{
								i = k; // Stop scanning
								break;
							}
							if ((Data[i] >= '0') && (Data[i] <= '9') || (Data[i] == '-') || (Data[i] == '.'))
								break;
						}
					}
					FloatCount = m;
				} // End bMotionLineEndFound

				if (bConnected)
				{
					// If we get more data then we can process per tick, read socket till no more data is left and ignore it
					while (ReceiverSocket->HasPendingData(BytesPending))
					{
						ReceiverSocket->Recv(Data, SIZEOFDATA, BytesRead);
						// if (GEngine) GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("Data skipped: %u"), BytesRead));
					}
				}
			} // End Recv
		} // End HasPendingData
	} // End bConnected

}

// Connect to BVH server (for e.g. Axis Neuron SW)
bool APerceptionNeuronController::Connect(FString HostName, int32 Port)
{
	if (bConnected == true)
		return false;

	// Set up reference bone handling
	// Template does not support transition for the reference bone yet
	// Should be no problem, because normally reference bone does not change values.
	if (bReference == true)
	{
		// Skip reference bone x,y,z translation and x,y,z rotation
		// regardless if displacement is active or not (Axis Neuron sends always 6 floats)
		FloatSkip = 6;
	}

	// Create TCP Socket and connect
	ISocketSubsystem* const SocketSubSystem = ISocketSubsystem::Get();
	if (SocketSubSystem)
	{
		ReceiverSocket = SocketSubSystem->CreateSocket(NAME_Stream, TEXT("PerceptionNeuronSocket"), false);
		if (ReceiverSocket == NULL)
			return false;

		auto ResolveInfo = SocketSubSystem->GetHostByName(TCHAR_TO_ANSI(*HostName));
		while (!ResolveInfo->IsComplete());

		if (ResolveInfo->GetErrorCode() == 0)
		{
			const FInternetAddr* Addr = &ResolveInfo->GetResolvedAddress();
			uint32 IP;
			Addr->GetIp(IP);

			TSharedRef<FInternetAddr> InetAddr = SocketSubSystem->CreateInternetAddr();
			InetAddr->SetIp(IP);
			InetAddr->SetPort(Port);

			bConnected = ReceiverSocket->Connect(*InetAddr);
		}
	}

	return bConnected;
}

// Called when the game stops
void APerceptionNeuronController::BeginDestroy()
{
	Super::BeginDestroy();

	Disconnect();
}

// Disconnect from BVH server
void APerceptionNeuronController::Disconnect(void)
{
	if (bConnected == true)
	{
		bConnected = false;
		ReceiverSocket->Close();
	}
}

// BVH Player
bool APerceptionNeuronController::Play(FString BVHFileName, bool bPlayEndless)
{
	// Set to default
	bPlayerInitialized = false;
	bPlay = false;
	PlayerMotionLines.Empty();

	// Load BVH file
	if (FFileHelper::LoadANSITextFileToStrings(*(FPaths::GameDir() + FString("Content/") + BVHFileName), NULL, PlayerMotionLines) != true)
	{
		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("Could not load BVH file:%s"), *(FPaths::GameDir() + FString("Content/") + BVHFileName)));
		}
		return false;
	}

	// Parse BVH file, get frame time and beginning of motion line part
	for (int32 i = 0; i < PlayerMotionLines.Num(); i++)
	{
		PlayerMotionLines[i].Trim();

		if (PlayerMotionLines[i].StartsWith(TEXT("Frames:"), ESearchCase::IgnoreCase) == true)
		{
			TArray<FString> Words;
			if (PlayerMotionLines[i].ParseIntoArray(Words, TEXT(" "), false) >= 2)
			{
				Frames = FCString::Atoi(*Words[1]);
			}
		}
		else if (PlayerMotionLines[i].StartsWith(TEXT("Frame Time:"), ESearchCase::IgnoreCase) == true)
		{
			TArray<FString> Words;
			if (PlayerMotionLines[i].ParseIntoArray(Words, TEXT(" "), false) >= 2)
			{
				FrameTime = FCString::Atof(*Words[2]);
				if (FrameTime == 0)
					FrameTime = 0.010f;  // Default to a common value
			}
			if ((i + 1) <= PlayerMotionLines.Num())
			{
				MotionLineOffset = i + 1;
				MotionLinePointer = MotionLineOffset;
				bPlayerInitialized = true;
				bPlay = true;
				APerceptionNeuronController::bEndless = bPlayEndless;
				if (Frames > PlayerMotionLines.Num() - (i + 1))
					Frames = PlayerMotionLines.Num() - (i + 1);
				DeltaTimeAdded = FrameTime; // Play motion immediately with next frame
			}
		}
	}
	return bPlayerInitialized;
}

bool APerceptionNeuronController::Pause(bool bPause)
{
	if (bPlayerInitialized == true)
		bPlay = bPause;
	return bPlay;
}

bool APerceptionNeuronController::Rewind()
{
	if (bPlayerInitialized == true)
		MotionLinePointer = MotionLineOffset;
	return bPlayerInitialized;
}
<commit_msg>Do not resolve HostName if HostName is already a valid IPv4 address.<commit_after>// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
//
// Copyright 2015 Heiko Fink, All Rights Reserved.
//
// This file is subject to the terms and conditions defined in
// file 'LICENSE.txt', which is part of this source code package.
//
// Perception Neuron (TM) is a trademark of Beijing Noitom Technology Ltd.
//
// Description:
// This code implements the Controller functions that are used to connect to the Perception Neuron (TM) Axis Player.
//

#include "PerceptionNeuronPrivatePCH.h"
#include "PerceptionNeuronController.h"

// Sets default values
APerceptionNeuronController::APerceptionNeuronController()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

}

// Called when the game starts or when spawned
void APerceptionNeuronController::BeginPlay()
{
	Super::BeginPlay();

}

// Called every frame
void APerceptionNeuronController::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	// Read each tick a new motionline. Additionally motionlines are just discarded.
	if ((bConnected == true) || (bPlay == true))
	{
		uint32 BytesPending = 0;

		if (bConnected == true)
			if (ReceiverSocket->HasPendingData(BytesPending) != true)
				BytesPending = 0;

		if ((BytesPending) || (bPlay == true))
		{
#define SIZEOFDATA 10000
			uint8 Data[SIZEOFDATA];
			int32 BytesRead = 0;
			bool bMotionLineBeginFound = false;
			bool bMotionLineEndFound = false;

			if (bConnected == true) // Network connection?
			{
				if (ReceiverSocket->Recv(Data, SIZEOFDATA, BytesRead) != true)
					BytesRead = 0;
			}
			else if (bPlay == true)	// Player connected ?
			{
				DeltaTimeAdded += DeltaTime;
				if (FrameTime < DeltaTimeAdded) // Synchronize to FrameTime
				{
					DeltaTimeAdded -= FrameTime;
					while (DeltaTimeAdded > FrameTime) // We are too slow => Skip frames
					{
						DeltaTimeAdded -= FrameTime;
						MotionLinePointer++;
						if ((MotionLinePointer >= (MotionLineOffset + Frames)) && (bEndless == true))
							MotionLinePointer = MotionLineOffset;
					}

					if (MotionLinePointer < (MotionLineOffset + Frames))
					{
						int32 CurrentMotionLine = MotionLinePointer;
						if (SIZEOFDATA >= PlayerMotionLines[CurrentMotionLine].Len() + 1) // Sanity check
						{
							// Convert string based motion line back to an array of chars to pump the line through the parser (Little bit ugly...)
							memcpy(Data, TCHAR_TO_ANSI(*PlayerMotionLines[CurrentMotionLine]), PlayerMotionLines[CurrentMotionLine].Len());
							Data[PlayerMotionLines[CurrentMotionLine].Len()] = '\n';
							BytesRead = PlayerMotionLines[CurrentMotionLine].Len() + 1;
						}
						MotionLinePointer++; // Increment line for next time;
						if ((MotionLinePointer >= (MotionLineOffset + Frames)) && (bEndless == true))
							MotionLinePointer = MotionLineOffset;
					}
				}
			}

			if (BytesRead)
			{
				int32 i, j, k;

				if (MotionLineFormat == Neuron)
				{
					// Scan for the beginning of a motion line and synchronize to Avatarname
					// Neuron BVH output format:
					// 0 Avatarname 0.00 0.00 ... 0.00 ||
					for (i = j = k = 0; i < BytesRead - 2; i++)
					{
						if ((Data[i] == '0') && (Data[i + 1] == ' ') && ((Data[i + 2] < '0') || (Data[i + 2] > '9')) && (Data[i + 2] != '-'))
						{
							bMotionLineBeginFound = true;
							// Skip Avatarname
							for (j = i + 2; j < BytesRead; j++)
							{
								if (Data[j] == ' ')
									break;
							}
							break;
						}
					}
					if (bMotionLineBeginFound == true)
					{
						// Scan for the end of the motion line
						for (k = j; k < BytesRead; k++)
						{
							if ((Data[k] == '|') || (Data[k] == '\n') || (Data[k] == '\r'))
							{
								bMotionLineEndFound = true;
								break;
							}
						}
					}
				}
				else
				{
					// Just synchronize to first float and check if floatcount matches bonecount else try next line
					// Standard BVH format:
					// 0.00 0.00 ... 0.00
					i = j = k = 0;
					for (; i < BytesRead - 1;)
					{
						for (; i < BytesRead; i++)
						{
							if ((Data[i] >= '0') && (Data[i] <= '9') || (Data[i] == '-') || (Data[i] == '.'))
							{
								j = i;
								bMotionLineBeginFound = true;
								break;
							}
						}
						if (bMotionLineBeginFound == true)
						{
							int32 t = 0;
							bMotionLineEndFound = false;
							// Scan for the end of the motion line and count empty spaces between floats
							for (i = j; i < BytesRead; i++)
							{
								if (i < (BytesRead - 1))
									if (((Data[i] == ' ') || (Data[i] == '\t')) && (((Data[i + 1] >= '0') && (Data[i + 1] <= '9')) || (Data[i + 1] == '-') || (Data[i + 1] == '.')))
										t++;
								if ((Data[i] == '\n') || (Data[i] == '\r'))
								{
									bMotionLineEndFound = true;
									break;
								}
							}
							if (bMotionLineEndFound)
							{
								int32 FloatsPerBone = 6;	 // 3 for x,y,z translation and 3 for x,y,z rotation
								int32 FloatsPerReference = 6; // 3 for x,y,z translation and 3 for x,y,z rotation

								if (bDisplacement == false)
									FloatsPerBone = 3;	// If there is no displacement (translation) info we have only 3 floats for rotation left
								if (bReference == false)
									FloatsPerReference = 0;
								// Check if empty spaces matches bone count
								if (t == ((Skeleton.BoneNr * FloatsPerBone) + FloatsPerReference - 1))
								{
									k = i;
									break;
								}
							}
						}
					}
				}

				if (bMotionLineEndFound == true)
				{
					// We have now a valid motion line captured between j and k

					// Forward to first value
					for (i = j; i < k; i++)
						if ((Data[i] >= '0') && (Data[i] <= '9') || (Data[i] == '-') || (Data[i] == '.'))
							break;

					int32 m = 0;
					// Get all float values from motion line
					for (int32 f = 0; f < MAXFLOATS && i < k; f++)
					{
						// Read ASCII float value into float array
						if (f >= FloatSkip)
						{
							MotionLine[m] = atof((char *)&Data[i]);
							m++;
						}
						// Forward to end of value we read
						for (; i < k; i++)
							if (((Data[i] < '0') || (Data[i] > '9')) && (Data[i] != '-') && (Data[i] != '.'))
								break;

						// Forward to next value
						for (; i < k; i++)
						{
							// Sanitycheck for line ending
							if ((Data[i] == '|') || (Data[i] == '\n') || (Data[i] == '\r'))
							{
								i = k; // Stop scanning
								break;
							}
							if ((Data[i] >= '0') && (Data[i] <= '9') || (Data[i] == '-') || (Data[i] == '.'))
								break;
						}
					}
					FloatCount = m;
				} // End bMotionLineEndFound

				if (bConnected)
				{
					// If we get more data then we can process per tick, read socket till no more data is left and ignore it
					while (ReceiverSocket->HasPendingData(BytesPending))
					{
						ReceiverSocket->Recv(Data, SIZEOFDATA, BytesRead);
						// if (GEngine) GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("Data skipped: %u"), BytesRead));
					}
				}
			} // End Recv
		} // End HasPendingData
	} // End bConnected

}

// Connect to BVH server (for e.g. Axis Neuron SW)
bool APerceptionNeuronController::Connect(FString HostName, int32 Port)
{
	if (bConnected == true)
		return false;

	// Set up reference bone handling
	// Template does not support transition for the reference bone yet
	// Should be no problem, because normally reference bone does not change values.
	if (bReference == true)
	{
		// Skip reference bone x,y,z translation and x,y,z rotation
		// regardless if displacement is active or not (Axis Neuron sends always 6 floats)
		FloatSkip = 6;
	}

	// Create TCP Socket and connect
	ISocketSubsystem* const SocketSubSystem = ISocketSubsystem::Get();
	if (SocketSubSystem)
	{
		ReceiverSocket = SocketSubSystem->CreateSocket(NAME_Stream, TEXT("PerceptionNeuronSocket"), false);
		if (ReceiverSocket == NULL)
			return false;

		// Is HostName already a valid IPv4 address?
		bool bValidAddress = false;
		uint32 IP = 0;
		FIPv4Address IPv4Address;
		if (true == FIPv4Address::Parse(HostName, IPv4Address))
		{
			IP = IPv4Address.Value;
			bValidAddress = true;
		}
		else
		{	// Resolve HostName
			auto ResolveInfo = SocketSubSystem->GetHostByName(TCHAR_TO_ANSI(*HostName));
			while (!ResolveInfo->IsComplete());

			if (ResolveInfo->GetErrorCode() == 0)
			{
				const FInternetAddr* Addr = &ResolveInfo->GetResolvedAddress();
				Addr->GetIp(IP);
				bValidAddress = true;
			}
		}

		// Connect
		if (bValidAddress)
		{
			TSharedRef<FInternetAddr> InetAddr = SocketSubSystem->CreateInternetAddr();
			InetAddr->SetIp(IP);
			InetAddr->SetPort(Port);
			bConnected = ReceiverSocket->Connect(*InetAddr);
		}
	}

	return bConnected;
}

// Called when the game stops
void APerceptionNeuronController::BeginDestroy()
{
	Super::BeginDestroy();

	Disconnect();
}

// Disconnect from BVH server
void APerceptionNeuronController::Disconnect(void)
{
	if (bConnected == true)
	{
		bConnected = false;
		ReceiverSocket->Close();
	}
}

// BVH Player
bool APerceptionNeuronController::Play(FString BVHFileName, bool bPlayEndless)
{
	// Set to default
	bPlayerInitialized = false;
	bPlay = false;
	PlayerMotionLines.Empty();

	// Load BVH file
	if (FFileHelper::LoadANSITextFileToStrings(*(FPaths::GameDir() + FString("Content/") + BVHFileName), NULL, PlayerMotionLines) != true)
	{
		if (GEngine)
		{
			GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("Could not load BVH file:%s"), *(FPaths::GameDir() + FString("Content/") + BVHFileName)));
		}
		return false;
	}

	// Parse BVH file, get frame time and beginning of motion line part
	for (int32 i = 0; i < PlayerMotionLines.Num(); i++)
	{
		PlayerMotionLines[i].Trim();

		if (PlayerMotionLines[i].StartsWith(TEXT("Frames:"), ESearchCase::IgnoreCase) == true)
		{
			TArray<FString> Words;
			if (PlayerMotionLines[i].ParseIntoArray(Words, TEXT(" "), false) >= 2)
			{
				Frames = FCString::Atoi(*Words[1]);
			}
		}
		else if (PlayerMotionLines[i].StartsWith(TEXT("Frame Time:"), ESearchCase::IgnoreCase) == true)
		{
			TArray<FString> Words;
			if (PlayerMotionLines[i].ParseIntoArray(Words, TEXT(" "), false) >= 2)
			{
				FrameTime = FCString::Atof(*Words[2]);
				if (FrameTime == 0)
					FrameTime = 0.010f;  // Default to a common value
			}
			if ((i + 1) <= PlayerMotionLines.Num())
			{
				MotionLineOffset = i + 1;
				MotionLinePointer = MotionLineOffset;
				bPlayerInitialized = true;
				bPlay = true;
				APerceptionNeuronController::bEndless = bPlayEndless;
				if (Frames > PlayerMotionLines.Num() - (i + 1))
					Frames = PlayerMotionLines.Num() - (i + 1);
				DeltaTimeAdded = FrameTime; // Play motion immediately with next frame
			}
		}
	}
	return bPlayerInitialized;
}

bool APerceptionNeuronController::Pause(bool bPause)
{
	if (bPlayerInitialized == true)
		bPlay = bPause;
	return bPlay;
}

bool APerceptionNeuronController::Rewind()
{
	if (bPlayerInitialized == true)
		MotionLinePointer = MotionLineOffset;
	return bPlayerInitialized;
}
<|endoftext|>


<commit_before>
#include "forms/list.h"
#include "forms/scrollbar.h"
#include "framework/event.h"
#include "framework/framework.h"

namespace OpenApoc
{

ListBox::ListBox(Control *Owner) : ListBox(Owner, nullptr) {}

ListBox::ListBox(Control *Owner, ScrollBar *ExternalScrollBar)
    : Control(Owner), scroller_is_internal(ExternalScrollBar == nullptr), hovered(nullptr),
      selected(nullptr), scroller(ExternalScrollBar), ItemSize(64), ItemSpacing(1),
      ListOrientation(Orientation::Vertical), HoverColour(0, 0, 0, 0), SelectedColour(0, 0, 0, 0)
{
}

ListBox::~ListBox() { Clear(); }

void ListBox::ConfigureInternalScrollBar()
{
	scroller = new ScrollBar(this);
	scroller->Size.x = 16;
	scroller->Size.y = 16;
	switch (ListOrientation)
	{
		case Orientation::Vertical:
			scroller->Location.x = this->Size.x - scroller->Size.x;
			scroller->Location.y = 0;
			scroller->Size.y = this->Size.y;
			break;
		case Orientation::Horizontal:
			scroller->Location.x = 0;
			scroller->Location.y = this->Size.y - scroller->Size.y;
			scroller->Size.x = this->Size.x;
			break;
	}
	scroller->canCopy = false;
	scroller->Visible = false;
	scroller_is_internal = true;
}

void ListBox::OnRender()
{
	int offset = 0;

	for (auto c = Controls.begin(); c != Controls.end(); c++)
	{
		Control *ctrl = *c;
		if (ctrl != scroller)
		{
			switch (ListOrientation)
			{
				case Orientation::Vertical:
					ctrl->Location.x = 0;
					ctrl->Location.y = offset - scroller->GetValue();
					if (ItemSize != 0)
					{
						ctrl->Size.x = (scroller_is_internal ? scroller->Location.x : this->Size.x);
						ctrl->Size.y = ItemSize;
					}
					offset += ctrl->Size.y + ItemSpacing;
					break;
				case Orientation::Horizontal:
					ctrl->Location.x = offset - scroller->GetValue();
					ctrl->Location.y = 0;
					if (ItemSize != 0)
					{
						ctrl->Size.x = ItemSize;
						ctrl->Size.y = (scroller_is_internal ? scroller->Location.y : this->Size.y);
					}
					offset += ctrl->Size.x + ItemSpacing;
					break;
			}
		}
	}
	ResolveLocation();
	switch (ListOrientation)
	{
		case Orientation::Vertical:
			scroller->Maximum = std::max(offset - this->Size.y, scroller->Minimum);
			break;
		case Orientation::Horizontal:
			scroller->Maximum = std::max(offset - this->Size.x, scroller->Minimum);
			break;
	}
	scroller->LargeChange =
	    static_cast<int>(std::max((scroller->Maximum - scroller->Minimum + 2) / 10.0f, 4.0f));
}

void ListBox::PostRender()
{
	Control::PostRender();
	for (auto c = Controls.begin(); c != Controls.end(); c++)
	{
		Control *ctrl = *c;
		if (ctrl != scroller)
		{
			if (ctrl == hovered)
			{
				fw().renderer->drawRect(ctrl->Location, ctrl->Size, HoverColour);
			}
			else if (ctrl == selected)
			{
				fw().renderer->drawRect(ctrl->Location, ctrl->Size, SelectedColour);
			}
		}
	}
}

void ListBox::EventOccured(Event *e)
{
	Control::EventOccured(e);
	if (e->Type == EVENT_FORM_INTERACTION)
	{
		if (e->Data.Forms.RaisedBy == this || e->Data.Forms.RaisedBy == scroller)
		{
			// FIXME: Make scrolling amount match wheel amount
			// Should wheel orientation match scroll orientation?
			if (e->Data.Forms.EventFlag == FormEventType::MouseMove &&
			    (e->Data.Mouse.WheelVertical < 0 || e->Data.Mouse.WheelHorizontal < 0))
			{
				// scroller->ScrollPrev();
			}
			else if (e->Data.Forms.EventFlag == FormEventType::MouseMove &&
			         (e->Data.Mouse.WheelVertical > 0 || e->Data.Mouse.WheelHorizontal > 0))
			{
				// scroller->ScrollNext();
			}
		}
		else
		{
			if (e->Data.Forms.EventFlag == FormEventType::MouseEnter ||
			    e->Data.Forms.EventFlag == FormEventType::MouseLeave ||
			    e->Data.Forms.EventFlag == FormEventType::MouseClick)
			{
				for (auto c = Controls.begin(); c != Controls.end(); c++)
				{
					Control *ctrl = *c;
					if (e->Data.Forms.RaisedBy == ctrl)
					{
						switch (e->Data.Forms.EventFlag)
						{
							case FormEventType::MouseEnter:
								hovered = ctrl;
								break;
							case FormEventType::MouseLeave:
								hovered = nullptr;
								break;
							case FormEventType::MouseClick:
								selected = ctrl;
								break;
							default:
								break;
						}
					}
				}
			}
		}
	}
}

void ListBox::Update()
{
	Control::Update();
	if (scroller == nullptr)
	{
		ConfigureInternalScrollBar();
	}
	if (scroller)
	{
		scroller->Update();
	}
}

void ListBox::UnloadResources() {}

void ListBox::Clear()
{
	while (Controls.size() > 0)
	{
		delete Controls.back();
		Controls.pop_back();
	}
	if (scroller_is_internal)
	{
		ConfigureInternalScrollBar();
	}
	ResolveLocation();
}

void ListBox::AddItem(Control *Item)
{
	Controls.push_back(Item);
	Item->SetParent(this);
	ResolveLocation();
}

Control *ListBox::RemoveItem(Control *Item)
{
	for (auto i = Controls.begin(); i != Controls.end(); i++)
	{
		if (*i == Item)
		{
			Controls.erase(i);
			ResolveLocation();
			return Item;
		}
	}
	return nullptr;
}

Control *ListBox::RemoveItem(int Index)
{
	Control *c = Controls.at(Index);
	Controls.erase(Controls.begin() + Index);
	ResolveLocation();
	return c;
}

Control *ListBox::operator[](int Index) { return Controls.at(Index); }

Control *ListBox::CopyTo(Control *CopyParent)
{
	ListBox *copy;
	if (scroller_is_internal)
	{
		copy = new ListBox(CopyParent);
	}
	else
	{
		copy = new ListBox(CopyParent, static_cast<ScrollBar *>(scroller->lastCopiedTo));
	}
	copy->ItemSize = this->ItemSize;
	copy->ItemSpacing = this->ItemSpacing;
	copy->ListOrientation = this->ListOrientation;
	copy->HoverColour = this->HoverColour;
	copy->SelectedColour = this->SelectedColour;
	CopyControlData(copy);
	return copy;
}

void ListBox::ConfigureFromXML(tinyxml2::XMLElement *Element)
{
	Control::ConfigureFromXML(Element);
	tinyxml2::XMLElement *subnode;
	UString attribvalue;

	subnode = Element->FirstChildElement("item");
	if (subnode != nullptr)
	{
		if (subnode->Attribute("size") != nullptr && UString(subnode->Attribute("size")) != "")
		{
			ItemSize = Strings::ToInteger(subnode->Attribute("size"));
		}
		if (subnode->Attribute("spacing") != nullptr &&
		    UString(subnode->Attribute("spacing")) != "")
		{
			ItemSpacing = Strings::ToInteger(subnode->Attribute("spacing"));
		}
	}
	subnode = Element->FirstChildElement("orientation");
	if (subnode != nullptr && UString(subnode->GetText()) != "")
	{
		UString value = subnode->GetText();
		if (value == "horizontal")
		{
			ListOrientation = Orientation::Horizontal;
		}
		else if (value == "vertical")
		{
			ListOrientation = Orientation::Vertical;
		}
	}
	subnode = Element->FirstChildElement("hovercolour");
	if (subnode != nullptr)
	{
		if (subnode->Attribute("a") != nullptr && UString(subnode->Attribute("a")) != "")
		{
			HoverColour = Colour{
			    Strings::ToU8(subnode->Attribute("r")), Strings::ToU8(subnode->Attribute("g")),
			    Strings::ToU8(subnode->Attribute("b")), Strings::ToU8(subnode->Attribute("a"))};
		}
		else
		{
			HoverColour = Colour{Strings::ToU8(subnode->Attribute("r")),
			                     Strings::ToU8(subnode->Attribute("g")),
			                     Strings::ToU8(subnode->Attribute("b"))};
		}
	}
	subnode = Element->FirstChildElement("selcolour");
	if (subnode != nullptr)
	{
		if (subnode->Attribute("a") != nullptr && UString(subnode->Attribute("a")) != "")
		{
			SelectedColour = Colour{
			    Strings::ToU8(subnode->Attribute("r")), Strings::ToU8(subnode->Attribute("g")),
			    Strings::ToU8(subnode->Attribute("b")), Strings::ToU8(subnode->Attribute("a"))};
		}
		else
		{
			SelectedColour = Colour{Strings::ToU8(subnode->Attribute("r")),
			                        Strings::ToU8(subnode->Attribute("g")),
			                        Strings::ToU8(subnode->Attribute("b"))};
		}
	}
}
}; // namespace OpenApoc
<commit_msg>Finishing listbox events and mousewheel scrolling<commit_after>
#include "forms/list.h"
#include "forms/scrollbar.h"
#include "framework/event.h"
#include "framework/framework.h"

namespace OpenApoc
{

ListBox::ListBox(Control *Owner) : ListBox(Owner, nullptr) {}

ListBox::ListBox(Control *Owner, ScrollBar *ExternalScrollBar)
    : Control(Owner), scroller_is_internal(ExternalScrollBar == nullptr), hovered(nullptr),
      selected(nullptr), scroller(ExternalScrollBar), ItemSize(64), ItemSpacing(1),
      ListOrientation(Orientation::Vertical), HoverColour(0, 0, 0, 0), SelectedColour(0, 0, 0, 0)
{
}

ListBox::~ListBox() { Clear(); }

void ListBox::ConfigureInternalScrollBar()
{
	scroller = new ScrollBar(this);
	scroller->Size.x = 16;
	scroller->Size.y = 16;
	switch (ListOrientation)
	{
		case Orientation::Vertical:
			scroller->Location.x = this->Size.x - scroller->Size.x;
			scroller->Location.y = 0;
			scroller->Size.y = this->Size.y;
			break;
		case Orientation::Horizontal:
			scroller->Location.x = 0;
			scroller->Location.y = this->Size.y - scroller->Size.y;
			scroller->Size.x = this->Size.x;
			break;
	}
	scroller->canCopy = false;
	scroller_is_internal = true;
}

void ListBox::OnRender()
{
	int offset = 0;

	for (auto c = Controls.begin(); c != Controls.end(); c++)
	{
		Control *ctrl = *c;
		if (ctrl != scroller)
		{
			switch (ListOrientation)
			{
				case Orientation::Vertical:
					ctrl->Location.x = 0;
					ctrl->Location.y = offset - scroller->GetValue();
					if (ItemSize != 0)
					{
						ctrl->Size.x = (scroller_is_internal ? scroller->Location.x : this->Size.x);
						ctrl->Size.y = ItemSize;
					}
					offset += ctrl->Size.y + ItemSpacing;
					break;
				case Orientation::Horizontal:
					ctrl->Location.x = offset - scroller->GetValue();
					ctrl->Location.y = 0;
					if (ItemSize != 0)
					{
						ctrl->Size.x = ItemSize;
						ctrl->Size.y = (scroller_is_internal ? scroller->Location.y : this->Size.y);
					}
					offset += ctrl->Size.x + ItemSpacing;
					break;
			}
		}
	}
	ResolveLocation();
	switch (ListOrientation)
	{
		case Orientation::Vertical:
			scroller->Maximum = std::max(offset - this->Size.y, scroller->Minimum);
			break;
		case Orientation::Horizontal:
			scroller->Maximum = std::max(offset - this->Size.x, scroller->Minimum);
			break;
	}
	scroller->LargeChange =
	    static_cast<int>(std::max((scroller->Maximum - scroller->Minimum + 2) / 10.0f, 4.0f));
}

void ListBox::PostRender()
{
	Control::PostRender();
	for (auto c = Controls.begin(); c != Controls.end(); c++)
	{
		Control *ctrl = *c;
		if (ctrl != scroller)
		{
			if (ctrl == selected)
			{
				fw().renderer->drawRect(ctrl->Location, ctrl->Size, SelectedColour);
			}
			else if (ctrl == hovered)
			{
				fw().renderer->drawRect(ctrl->Location, ctrl->Size, HoverColour);
			}
		}
	}
}

void ListBox::EventOccured(Event *e)
{
	Control::EventOccured(e);
	if (e->Type == EVENT_FORM_INTERACTION)
	{
		Control *ctrl = e->Data.Forms.RaisedBy;
		if (e->Data.Forms.EventFlag == FormEventType::MouseMove)
		{
			// FIXME: Scrolling amount should match wheel amount
			// Should wheel orientation match as well? Who has horizontal scrolls??
			if (ctrl == this || ctrl->GetParent() == this)
			{
				int wheelDelta =
				    e->Data.Forms.MouseInfo.WheelVertical + e->Data.Forms.MouseInfo.WheelHorizontal;
				if (wheelDelta > 0)
				{
					scroller->ScrollPrev();
				}
				else if (wheelDelta < 0)
				{
					scroller->ScrollNext();
				}
			}

			if (ctrl == this || ctrl == scroller)
			{
				ctrl = nullptr;
			}
			if (hovered != ctrl)
			{
				hovered = ctrl;
				auto le = new Event();
				le->Type = e->Type;
				le->Data.Forms = e->Data.Forms;
				le->Data.Forms.RaisedBy = this;
				le->Data.Forms.EventFlag = FormEventType::ListBoxChangeHover;
				fw().PushEvent(le);
			}
		}
		else if (e->Data.Forms.EventFlag == FormEventType::MouseDown)
		{
			if (selected != ctrl && ctrl->GetParent() == this && ctrl != scroller)
			{
				selected = ctrl;
				auto le = new Event();
				le->Type = e->Type;
				le->Data.Forms = e->Data.Forms;
				le->Data.Forms.RaisedBy = this;
				le->Data.Forms.EventFlag = FormEventType::ListBoxChangeSelected;
				fw().PushEvent(le);
			}
		}
	}
}

void ListBox::Update()
{
	Control::Update();
	if (scroller == nullptr)
	{
		ConfigureInternalScrollBar();
	}
	if (scroller)
	{
		scroller->Update();
	}
}

void ListBox::UnloadResources() {}

void ListBox::Clear()
{
	while (Controls.size() > 0)
	{
		delete Controls.back();
		Controls.pop_back();
	}
	if (scroller_is_internal)
	{
		ConfigureInternalScrollBar();
	}
	ResolveLocation();
}

void ListBox::AddItem(Control *Item)
{
	Controls.push_back(Item);
	Item->SetParent(this);
	ResolveLocation();
}

Control *ListBox::RemoveItem(Control *Item)
{
	for (auto i = Controls.begin(); i != Controls.end(); i++)
	{
		if (*i == Item)
		{
			Controls.erase(i);
			ResolveLocation();
			return Item;
		}
	}
	return nullptr;
}

Control *ListBox::RemoveItem(int Index)
{
	Control *c = Controls.at(Index);
	Controls.erase(Controls.begin() + Index);
	ResolveLocation();
	return c;
}

Control *ListBox::operator[](int Index) { return Controls.at(Index); }

Control *ListBox::CopyTo(Control *CopyParent)
{
	ListBox *copy;
	if (scroller_is_internal)
	{
		copy = new ListBox(CopyParent);
	}
	else
	{
		copy = new ListBox(CopyParent, static_cast<ScrollBar *>(scroller->lastCopiedTo));
	}
	copy->ItemSize = this->ItemSize;
	copy->ItemSpacing = this->ItemSpacing;
	copy->ListOrientation = this->ListOrientation;
	copy->HoverColour = this->HoverColour;
	copy->SelectedColour = this->SelectedColour;
	CopyControlData(copy);
	return copy;
}

void ListBox::ConfigureFromXML(tinyxml2::XMLElement *Element)
{
	Control::ConfigureFromXML(Element);
	tinyxml2::XMLElement *subnode;
	UString attribvalue;

	subnode = Element->FirstChildElement("item");
	if (subnode != nullptr)
	{
		if (subnode->Attribute("size") != nullptr && UString(subnode->Attribute("size")) != "")
		{
			ItemSize = Strings::ToInteger(subnode->Attribute("size"));
		}
		if (subnode->Attribute("spacing") != nullptr &&
		    UString(subnode->Attribute("spacing")) != "")
		{
			ItemSpacing = Strings::ToInteger(subnode->Attribute("spacing"));
		}
	}
	subnode = Element->FirstChildElement("orientation");
	if (subnode != nullptr && UString(subnode->GetText()) != "")
	{
		UString value = subnode->GetText();
		if (value == "horizontal")
		{
			ListOrientation = Orientation::Horizontal;
		}
		else if (value == "vertical")
		{
			ListOrientation = Orientation::Vertical;
		}
	}
	subnode = Element->FirstChildElement("hovercolour");
	if (subnode != nullptr)
	{
		if (subnode->Attribute("a") != nullptr && UString(subnode->Attribute("a")) != "")
		{
			HoverColour = Colour{
			    Strings::ToU8(subnode->Attribute("r")), Strings::ToU8(subnode->Attribute("g")),
			    Strings::ToU8(subnode->Attribute("b")), Strings::ToU8(subnode->Attribute("a"))};
		}
		else
		{
			HoverColour = Colour{Strings::ToU8(subnode->Attribute("r")),
			                     Strings::ToU8(subnode->Attribute("g")),
			                     Strings::ToU8(subnode->Attribute("b"))};
		}
	}
	subnode = Element->FirstChildElement("selcolour");
	if (subnode != nullptr)
	{
		if (subnode->Attribute("a") != nullptr && UString(subnode->Attribute("a")) != "")
		{
			SelectedColour = Colour{
			    Strings::ToU8(subnode->Attribute("r")), Strings::ToU8(subnode->Attribute("g")),
			    Strings::ToU8(subnode->Attribute("b")), Strings::ToU8(subnode->Attribute("a"))};
		}
		else
		{
			SelectedColour = Colour{Strings::ToU8(subnode->Attribute("r")),
			                        Strings::ToU8(subnode->Attribute("g")),
			                        Strings::ToU8(subnode->Attribute("b"))};
		}
	}
}
}; // namespace OpenApoc
<|endoftext|>


<commit_before>/*    Copyright (c) 2010-2012, Delft University of Technology
 *    All rights reserved.
 *
 *    Redistribution and use in source and binary forms, with or without modification, are
 *    permitted provided that the following conditions are met:
 *      - Redistributions of source code must retain the above copyright notice, this list of
 *        conditions and the following disclaimer.
 *      - Redistributions in binary form must reproduce the above copyright notice, this list of
 *        conditions and the following disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *      - Neither the name of the Delft University of Technology nor the names of its contributors
 *        may be used to endorse or promote products derived from this software without specific
 *        prior written permission.
 *
 *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
 *    OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *    COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 *    GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 *    AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 *    OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *    Changelog
 *      YYMMDD    Author            Comment
 *      110519    F.M. Engelen      Creation of code.
 *      110628    K. Kumar          Minor comment and layout modifications.
 *      110701    K. Kumar          Updated unit tests to check relative error;
 *                                  Updated file path.
 *      110718    F.M. Engelen      Took out falacy in test (only checking the norm) and
 *                                  added ItoE and EtoI transformation.
 *      110726    K. Kumar          Minor modifications; updated relative error
 *                                  wrt to norm.
 *      110808    F.M. Engelen      Updated with better tests, changed test for vertical frame.
 *      120529    E.A.G. Heeren     Boostified unit tests.
 *      120614    P. Musegaas       Removed unneccessary using statements and normalizations.
 *
 *    References
 *
 *    The reference frame definitions/abbreviations can be found in the file
 *    referenceFrameTransformations.h.
 *
 */

#define BOOST_TEST_MAIN

#include <boost/test/unit_test.hpp>

#include <Eigen/Core>

#include <TudatCore/Basics/testMacros.h>
#include <TudatCore/Astrodynamics/BasicAstrodynamics/unitConversions.h>

#include "Tudat/Astrodynamics/ReferenceFrames/referenceFrameTransformations.h"

namespace tudat
{
namespace unit_tests
{

BOOST_AUTO_TEST_SUITE( test_reference_frame_transformations )

// Summary of tests.
// Test 1: Test inertial to rotating planetocentric frame transformation.
// Test 2: Check whether the transformed matrix of Test 1 is also correct.
// Test 3: Same test as Test 1 for the transformation quaternion.
// Test 4: Same test as Test 2 for the transformation quaternion.
// Test 5: Test airspeed-based aerodynamic to body frame transformation.
// Test 6: Test planetocentric to local vertical frame transformation quaternion.
// Test 7: Check whether the transformed matrix of Test 6 is also correct.

// Test inertial to rotating planetocentric frame transformations.
BOOST_AUTO_TEST_CASE( testRotatingPlanetocentricFrameTransformations )
{
    // Using declarations.
    using std::atan2;
    using std::cos;
    using std::sin;
    using std::pow;
    using std::sqrt;
    using tudat::unit_conversions::convertDegreesToRadians;

    // Test 1: Test Inertial to rotating planetocentric frame transformation.
    {
        // Initialize initial location vector in inertial frame.
        Eigen::Vector3d startLocation;
        startLocation( 0 ) = 4.0;
        startLocation( 1 ) = 1.0;
        startLocation( 2 ) = 5.0;

        double horizontalStartLocationSize = sqrt( pow( startLocation( 0 ), 2.0 )
                                                   + pow( startLocation( 1 ), 2.0 ) );

        // Declare and initialize angle between vector and XR-axis.
        double startAngle = atan2( startLocation( 1 ), startLocation( 0 ) );

        // Rotate by 10 degrees around the positive Z-axis
        double angleInTime = convertDegreesToRadians( 10.0 );

        // Declare and initialize the angle between the XI- and XR-axis.
        double endAngle = startAngle - angleInTime;

        // Declare the expected location of the point in the planetocentric reference frame.
        Eigen::Vector3d expectedLocation;
        expectedLocation( 0 ) = horizontalStartLocationSize * cos( endAngle );
        expectedLocation( 1 ) = horizontalStartLocationSize * sin( endAngle );
        expectedLocation( 2 ) = startLocation( 2 );

        // Compute location of the point in the rotating frame subject to the transformation matrix.
        Eigen::Vector3d transformedLocation;
        transformedLocation = reference_frames::
                getInertialToPlanetocentricFrameTransformationMatrix( angleInTime ) * startLocation;

        // Check whether both vectors are equal within tolerances.
        TUDAT_CHECK_MATRIX_CLOSE_FRACTION( transformedLocation, expectedLocation,
                                           std::numeric_limits< double >::epsilon( ) );
    }

    // Test 2: Check whether the transformed matrix of Test 1 is also correct.
    // Compute the error in the calculation.
    {
        // Initialize initial location vector in inertial frame.
        Eigen::Vector3d startLocation;
        startLocation( 0 ) = 4.0;
        startLocation( 1 ) = 1.0;
        startLocation( 2 ) = 5.0;

        // Rotate by 10 degrees around the positive Z-axis
        double angleInTime = convertDegreesToRadians( 10.0 );

        // Compute location of the point in the rotating frame subject to the transformation matrix.
        Eigen::Vector3d transformedLocation;
        transformedLocation = tudat::reference_frames::
                getRotatingPlanetocentricToInertialFrameTransformationMatrix( angleInTime ) *
                tudat::reference_frames::
                getInertialToPlanetocentricFrameTransformationMatrix( angleInTime )
                * startLocation;

        // Check whether both vectors are equal within tolerances.
        TUDAT_CHECK_MATRIX_CLOSE_FRACTION( transformedLocation, startLocation,
                                           std::numeric_limits< double >::epsilon( ) );
    }
}

BOOST_AUTO_TEST_CASE( testRotatingPlanetocentricFrameTransformationQuaternion )
{
    // Using declarations.
    using std::atan2;
    using std::cos;
    using std::sin;
    using std::pow;
    using std::sqrt;
    using tudat::unit_conversions::convertDegreesToRadians;

    // Test 3: Same test as Test 1 for the transformation quaternion.
    // Compute location of the point in the Rotating frame subject to the transformation matrix.
    {
        // Initialize initial location vector in inertial frame.
        Eigen::Vector3d startLocation;
        startLocation( 0 ) = 4.0;
        startLocation( 1 ) = 1.0;
        startLocation( 2 ) = 5.0;

        double horizontalStartLocationSize = sqrt( pow( startLocation( 0 ), 2.0 )
                                                   + pow( startLocation( 1 ), 2.0 ) );

        // Declare and initialize angle between vector and XR-axis.
        double startAngle = atan2( startLocation( 1 ), startLocation( 0 ) );

        // Rotate by 10 degrees around the positive Z-axis
        double angleInTime = convertDegreesToRadians( 10.0 );

        // Declare and initialize the angle between the XI- and XR-axis.
        double endAngle = startAngle - angleInTime;

        // Declare the expected location of the point in the planetocentric reference frame.
        Eigen::Vector3d expectedLocation;
        expectedLocation( 0 ) = horizontalStartLocationSize * cos( endAngle );
        expectedLocation( 1 ) = horizontalStartLocationSize * sin( endAngle );
        expectedLocation( 2 ) = startLocation( 2 );

        // Compute location of the point in the rotating frame subject to the transformation matrix.
        Eigen::Vector3d transformedLocation;
        transformedLocation = reference_frames::
                getInertialToPlanetocentricFrameTransformationQuaternion( angleInTime )
                * startLocation;

        // Check whether both vectors are equal within tolerances.
        TUDAT_CHECK_MATRIX_CLOSE_FRACTION( transformedLocation, expectedLocation,
                                           std::numeric_limits< double >::epsilon( ) );
    }

// Test 4: Same test as Test 2 for the transformation quaternion.
// Compute the error in the calculation.
    {
        // Initialize initial location vector in inertial frame.
        Eigen::Vector3d startLocation;
        startLocation( 0 ) = 4.0;
        startLocation( 1 ) = 1.0;
        startLocation( 2 ) = 5.0;

        // Rotate by 10 degrees around the positive Z-axis
        double angleInTime = convertDegreesToRadians( 10.0 );

        // Compute location of the point in the rotating frame subject to the transformation matrix.
        Eigen::Vector3d transformedLocation;
        transformedLocation = reference_frames::
                getRotatingPlanetocentricToInertialFrameTransformationQuaternion( angleInTime ) *
                reference_frames::
                getInertialToPlanetocentricFrameTransformationQuaternion( angleInTime )
                * startLocation;

        // Check whether both vectors are equal within tolerances.
        TUDAT_CHECK_MATRIX_CLOSE_FRACTION( transformedLocation, startLocation,
                                           std::numeric_limits< double >::epsilon( ) );
    }
}

BOOST_AUTO_TEST_CASE( testAirspeedBasedAerodynamicToBodyFrameTransformation )
{
    // Using declarations.
    using std::atan2;
    using std::cos;
    using tudat::unit_conversions::convertDegreesToRadians;

    // Test 5: Test airspeed-Based Aerodynamic to body frame transformation.
    // Declare and initialize the start location and angles.
    {
        // Initialize initial location vector in inertial frame.
        Eigen::Vector3d startLocation;
        startLocation( 0 ) = -10.0;
        startLocation( 1 ) = 0.0;
        startLocation( 2 ) = 0.0;

        double angleOfAttack = convertDegreesToRadians( 45.0 );
        double angleOfSideslip = convertDegreesToRadians( 60.0 );;

        // Compute expected location.
        // As there is only an angle of attack, the following simplified equations can be used.
        Eigen::Vector3d expectedLocation;
        expectedLocation( 0 ) = startLocation( 0 ) * cos( angleOfSideslip ) * cos( angleOfAttack );
        expectedLocation( 1 ) = startLocation( 0 ) * sin( angleOfSideslip );
        expectedLocation( 2 ) = startLocation( 0 ) * cos( angleOfSideslip ) * sin( angleOfAttack );

        // Compute location of the point in the rotating frame subject to the transformation matrix.
        Eigen::Vector3d transformedLocation;
        transformedLocation = reference_frames::
                getAirspeedBasedAerodynamicToBodyFrameTransformationMatrix( angleOfAttack,
                                                                            angleOfSideslip )
                * startLocation;

        // Check whether both vectors are equal within tolerances.
        TUDAT_CHECK_MATRIX_CLOSE_FRACTION( transformedLocation, expectedLocation, 1.0e-14 );
    }
}

BOOST_AUTO_TEST_CASE( testRotatingPlanetocentricToLocalVerticalFrameTransformationQuaternion )
{
    // Using declarations.
    using std::atan2;
    using std::cos;
    using std::sin;
    using tudat::unit_conversions::convertDegreesToRadians;

    // Test 6: Test Rotating planetocentric to local vertical frame transformation quaternion.
    {
        // Initialize initial location vector.
        Eigen::Vector3d startLocation;
        startLocation( 0 ) = 10.0;
        startLocation( 1 ) = 5.0;
        startLocation( 2 ) = 2.0;

        // Declare rotation angle for planet and set to 90 degrees.
        double longitude = convertDegreesToRadians( 60.0 );

        // Declare latitude and set to 45 degrees.
        double latitude = convertDegreesToRadians( 20.0 );

        // Declare the expected location of the point in the planet reference frame.
        Eigen::Vector3d expectedLocation;
        expectedLocation( 0 ) = -cos( longitude ) * sin( latitude ) * startLocation( 0 ) -
                sin( longitude ) * sin( latitude ) * startLocation( 1 )  +
                cos( latitude ) * startLocation( 2 );
        expectedLocation( 1 ) = -sin( longitude ) * startLocation( 0 ) +
                cos( longitude ) * startLocation( 1 ) +
                0.0;
        expectedLocation( 2 ) = -cos( longitude ) * cos( latitude ) * startLocation( 0 ) -
                sin( longitude ) * cos( latitude ) * startLocation( 1 )  -
                sin( latitude ) * startLocation( 2 );

        // Compute location of the point in the rotating frame subject to the transformation matrix.
        Eigen::Vector3d transformedLocation;
        transformedLocation = reference_frames::
                getRotatingPlanetocentricToLocalVerticalFrameTransformationQuaternion( longitude,
                                                                                latitude )
                * startLocation;

        // Check whether both vectors are equal within tolerances.
        TUDAT_CHECK_MATRIX_CLOSE_FRACTION( transformedLocation, expectedLocation, 1.0e-14 );
    }

    // Test 7: Check whether the transformed matrix of Test 6 is also correct.
    // Compute the error in the calculation.
    {
        // Initialize initial location vector.
        Eigen::Vector3d startLocation;
        startLocation( 0 ) = 10.0;
        startLocation( 1 ) = 5.0;
        startLocation( 2 ) = 2.0;

        // Declare rotation angle for planet and set to 90 degrees.
        double longitude = convertDegreesToRadians( 60.0 );

        // Declare latitude and set to 45 degrees.
        double latitude = convertDegreesToRadians( 20.0 );

        // Compute location of the point in the rotating frame subject to the transformation matrix.
        Eigen::Vector3d transformedLocation;
        transformedLocation = reference_frames::
                getLocalVerticalToRotatingPlanetocentricFrameTransformationQuaternion( longitude,
                                                                                latitude )
                * reference_frames::
                getRotatingPlanetocentricToLocalVerticalFrameTransformationQuaternion( longitude,
                                                                                latitude )
                * startLocation;

        // Check whether both vectors are equal within tolerances.
        TUDAT_CHECK_MATRIX_CLOSE_FRACTION( transformedLocation, startLocation, 1.0e-14 );
    }
}

BOOST_AUTO_TEST_SUITE_END( )

} //namespace unit_tests
} //namespace tudat

<commit_msg>Repository management wiki test: added two tab stops in the file header of the reference frame transformation unit test.<commit_after>/*    Copyright (c) 2010-2012, Delft University of Technology
 *    All rights reserved.
 *
 *    Redistribution and use in source and binary forms, with or without modification, are
 *    permitted provided that the following conditions are met:
 *      - Redistributions of source code must retain the above copyright notice, this list of
 *        conditions and the following disclaimer.
 *      - Redistributions in binary form must reproduce the above copyright notice, this list of
 *        conditions and the following disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *      - Neither the name of the Delft University of Technology nor the names of its contributors
 *        may be used to endorse or promote products derived from this software without specific
 *        prior written permission.
 *
 *    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
 *    OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 *    MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *    COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 *    GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 *    AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 *    OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *    Changelog
 *      YYMMDD    Author            Comment
 *      110519    F.M. Engelen      Creation of code.
 *      110628    K. Kumar          Minor comment and layout modifications.
 *      110701    K. Kumar          Updated unit tests to check relative error;
 *                                  Updated file path.
 *      110718    F.M. Engelen      Took out falacy in test (only checking the norm) and
 *                                  added ItoE and EtoI transformation.
 *      110726    K. Kumar          Minor modifications; updated relative error
 *                                  wrt to norm.
 *      110808    F.M. Engelen      Updated with better tests, changed test for vertical frame.
 *      120529    E.A.G. Heeren     Boostified unit tests.
 *      120614    P. Musegaas       Removed unneccessary using statements and normalizations.
 *
 *    References
 *
 *      The reference frame definitions/abbreviations can be found in the file
 *      referenceFrameTransformations.h.
 *
 */

#define BOOST_TEST_MAIN

#include <boost/test/unit_test.hpp>

#include <Eigen/Core>

#include <TudatCore/Basics/testMacros.h>
#include <TudatCore/Astrodynamics/BasicAstrodynamics/unitConversions.h>

#include "Tudat/Astrodynamics/ReferenceFrames/referenceFrameTransformations.h"

namespace tudat
{
namespace unit_tests
{

BOOST_AUTO_TEST_SUITE( test_reference_frame_transformations )

// Summary of tests.
// Test 1: Test inertial to rotating planetocentric frame transformation.
// Test 2: Check whether the transformed matrix of Test 1 is also correct.
// Test 3: Same test as Test 1 for the transformation quaternion.
// Test 4: Same test as Test 2 for the transformation quaternion.
// Test 5: Test airspeed-based aerodynamic to body frame transformation.
// Test 6: Test planetocentric to local vertical frame transformation quaternion.
// Test 7: Check whether the transformed matrix of Test 6 is also correct.

// Test inertial to rotating planetocentric frame transformations.
BOOST_AUTO_TEST_CASE( testRotatingPlanetocentricFrameTransformations )
{
    // Using declarations.
    using std::atan2;
    using std::cos;
    using std::sin;
    using std::pow;
    using std::sqrt;
    using tudat::unit_conversions::convertDegreesToRadians;

    // Test 1: Test Inertial to rotating planetocentric frame transformation.
    {
        // Initialize initial location vector in inertial frame.
        Eigen::Vector3d startLocation;
        startLocation( 0 ) = 4.0;
        startLocation( 1 ) = 1.0;
        startLocation( 2 ) = 5.0;

        double horizontalStartLocationSize = sqrt( pow( startLocation( 0 ), 2.0 )
                                                   + pow( startLocation( 1 ), 2.0 ) );

        // Declare and initialize angle between vector and XR-axis.
        double startAngle = atan2( startLocation( 1 ), startLocation( 0 ) );

        // Rotate by 10 degrees around the positive Z-axis
        double angleInTime = convertDegreesToRadians( 10.0 );

        // Declare and initialize the angle between the XI- and XR-axis.
        double endAngle = startAngle - angleInTime;

        // Declare the expected location of the point in the planetocentric reference frame.
        Eigen::Vector3d expectedLocation;
        expectedLocation( 0 ) = horizontalStartLocationSize * cos( endAngle );
        expectedLocation( 1 ) = horizontalStartLocationSize * sin( endAngle );
        expectedLocation( 2 ) = startLocation( 2 );

        // Compute location of the point in the rotating frame subject to the transformation matrix.
        Eigen::Vector3d transformedLocation;
        transformedLocation = reference_frames::
                getInertialToPlanetocentricFrameTransformationMatrix( angleInTime ) * startLocation;

        // Check whether both vectors are equal within tolerances.
        TUDAT_CHECK_MATRIX_CLOSE_FRACTION( transformedLocation, expectedLocation,
                                           std::numeric_limits< double >::epsilon( ) );
    }

    // Test 2: Check whether the transformed matrix of Test 1 is also correct.
    // Compute the error in the calculation.
    {
        // Initialize initial location vector in inertial frame.
        Eigen::Vector3d startLocation;
        startLocation( 0 ) = 4.0;
        startLocation( 1 ) = 1.0;
        startLocation( 2 ) = 5.0;

        // Rotate by 10 degrees around the positive Z-axis
        double angleInTime = convertDegreesToRadians( 10.0 );

        // Compute location of the point in the rotating frame subject to the transformation matrix.
        Eigen::Vector3d transformedLocation;
        transformedLocation = tudat::reference_frames::
                getRotatingPlanetocentricToInertialFrameTransformationMatrix( angleInTime ) *
                tudat::reference_frames::
                getInertialToPlanetocentricFrameTransformationMatrix( angleInTime )
                * startLocation;

        // Check whether both vectors are equal within tolerances.
        TUDAT_CHECK_MATRIX_CLOSE_FRACTION( transformedLocation, startLocation,
                                           std::numeric_limits< double >::epsilon( ) );
    }
}

BOOST_AUTO_TEST_CASE( testRotatingPlanetocentricFrameTransformationQuaternion )
{
    // Using declarations.
    using std::atan2;
    using std::cos;
    using std::sin;
    using std::pow;
    using std::sqrt;
    using tudat::unit_conversions::convertDegreesToRadians;

    // Test 3: Same test as Test 1 for the transformation quaternion.
    // Compute location of the point in the Rotating frame subject to the transformation matrix.
    {
        // Initialize initial location vector in inertial frame.
        Eigen::Vector3d startLocation;
        startLocation( 0 ) = 4.0;
        startLocation( 1 ) = 1.0;
        startLocation( 2 ) = 5.0;

        double horizontalStartLocationSize = sqrt( pow( startLocation( 0 ), 2.0 )
                                                   + pow( startLocation( 1 ), 2.0 ) );

        // Declare and initialize angle between vector and XR-axis.
        double startAngle = atan2( startLocation( 1 ), startLocation( 0 ) );

        // Rotate by 10 degrees around the positive Z-axis
        double angleInTime = convertDegreesToRadians( 10.0 );

        // Declare and initialize the angle between the XI- and XR-axis.
        double endAngle = startAngle - angleInTime;

        // Declare the expected location of the point in the planetocentric reference frame.
        Eigen::Vector3d expectedLocation;
        expectedLocation( 0 ) = horizontalStartLocationSize * cos( endAngle );
        expectedLocation( 1 ) = horizontalStartLocationSize * sin( endAngle );
        expectedLocation( 2 ) = startLocation( 2 );

        // Compute location of the point in the rotating frame subject to the transformation matrix.
        Eigen::Vector3d transformedLocation;
        transformedLocation = reference_frames::
                getInertialToPlanetocentricFrameTransformationQuaternion( angleInTime )
                * startLocation;

        // Check whether both vectors are equal within tolerances.
        TUDAT_CHECK_MATRIX_CLOSE_FRACTION( transformedLocation, expectedLocation,
                                           std::numeric_limits< double >::epsilon( ) );
    }

// Test 4: Same test as Test 2 for the transformation quaternion.
// Compute the error in the calculation.
    {
        // Initialize initial location vector in inertial frame.
        Eigen::Vector3d startLocation;
        startLocation( 0 ) = 4.0;
        startLocation( 1 ) = 1.0;
        startLocation( 2 ) = 5.0;

        // Rotate by 10 degrees around the positive Z-axis
        double angleInTime = convertDegreesToRadians( 10.0 );

        // Compute location of the point in the rotating frame subject to the transformation matrix.
        Eigen::Vector3d transformedLocation;
        transformedLocation = reference_frames::
                getRotatingPlanetocentricToInertialFrameTransformationQuaternion( angleInTime ) *
                reference_frames::
                getInertialToPlanetocentricFrameTransformationQuaternion( angleInTime )
                * startLocation;

        // Check whether both vectors are equal within tolerances.
        TUDAT_CHECK_MATRIX_CLOSE_FRACTION( transformedLocation, startLocation,
                                           std::numeric_limits< double >::epsilon( ) );
    }
}

BOOST_AUTO_TEST_CASE( testAirspeedBasedAerodynamicToBodyFrameTransformation )
{
    // Using declarations.
    using std::atan2;
    using std::cos;
    using tudat::unit_conversions::convertDegreesToRadians;

    // Test 5: Test airspeed-Based Aerodynamic to body frame transformation.
    // Declare and initialize the start location and angles.
    {
        // Initialize initial location vector in inertial frame.
        Eigen::Vector3d startLocation;
        startLocation( 0 ) = -10.0;
        startLocation( 1 ) = 0.0;
        startLocation( 2 ) = 0.0;

        double angleOfAttack = convertDegreesToRadians( 45.0 );
        double angleOfSideslip = convertDegreesToRadians( 60.0 );;

        // Compute expected location.
        // As there is only an angle of attack, the following simplified equations can be used.
        Eigen::Vector3d expectedLocation;
        expectedLocation( 0 ) = startLocation( 0 ) * cos( angleOfSideslip ) * cos( angleOfAttack );
        expectedLocation( 1 ) = startLocation( 0 ) * sin( angleOfSideslip );
        expectedLocation( 2 ) = startLocation( 0 ) * cos( angleOfSideslip ) * sin( angleOfAttack );

        // Compute location of the point in the rotating frame subject to the transformation matrix.
        Eigen::Vector3d transformedLocation;
        transformedLocation = reference_frames::
                getAirspeedBasedAerodynamicToBodyFrameTransformationMatrix( angleOfAttack,
                                                                            angleOfSideslip )
                * startLocation;

        // Check whether both vectors are equal within tolerances.
        TUDAT_CHECK_MATRIX_CLOSE_FRACTION( transformedLocation, expectedLocation, 1.0e-14 );
    }
}

BOOST_AUTO_TEST_CASE( testRotatingPlanetocentricToLocalVerticalFrameTransformationQuaternion )
{
    // Using declarations.
    using std::atan2;
    using std::cos;
    using std::sin;
    using tudat::unit_conversions::convertDegreesToRadians;

    // Test 6: Test Rotating planetocentric to local vertical frame transformation quaternion.
    {
        // Initialize initial location vector.
        Eigen::Vector3d startLocation;
        startLocation( 0 ) = 10.0;
        startLocation( 1 ) = 5.0;
        startLocation( 2 ) = 2.0;

        // Declare rotation angle for planet and set to 90 degrees.
        double longitude = convertDegreesToRadians( 60.0 );

        // Declare latitude and set to 45 degrees.
        double latitude = convertDegreesToRadians( 20.0 );

        // Declare the expected location of the point in the planet reference frame.
        Eigen::Vector3d expectedLocation;
        expectedLocation( 0 ) = -cos( longitude ) * sin( latitude ) * startLocation( 0 ) -
                sin( longitude ) * sin( latitude ) * startLocation( 1 )  +
                cos( latitude ) * startLocation( 2 );
        expectedLocation( 1 ) = -sin( longitude ) * startLocation( 0 ) +
                cos( longitude ) * startLocation( 1 ) +
                0.0;
        expectedLocation( 2 ) = -cos( longitude ) * cos( latitude ) * startLocation( 0 ) -
                sin( longitude ) * cos( latitude ) * startLocation( 1 )  -
                sin( latitude ) * startLocation( 2 );

        // Compute location of the point in the rotating frame subject to the transformation matrix.
        Eigen::Vector3d transformedLocation;
        transformedLocation = reference_frames::
                getRotatingPlanetocentricToLocalVerticalFrameTransformationQuaternion( longitude,
                                                                                latitude )
                * startLocation;

        // Check whether both vectors are equal within tolerances.
        TUDAT_CHECK_MATRIX_CLOSE_FRACTION( transformedLocation, expectedLocation, 1.0e-14 );
    }

    // Test 7: Check whether the transformed matrix of Test 6 is also correct.
    // Compute the error in the calculation.
    {
        // Initialize initial location vector.
        Eigen::Vector3d startLocation;
        startLocation( 0 ) = 10.0;
        startLocation( 1 ) = 5.0;
        startLocation( 2 ) = 2.0;

        // Declare rotation angle for planet and set to 90 degrees.
        double longitude = convertDegreesToRadians( 60.0 );

        // Declare latitude and set to 45 degrees.
        double latitude = convertDegreesToRadians( 20.0 );

        // Compute location of the point in the rotating frame subject to the transformation matrix.
        Eigen::Vector3d transformedLocation;
        transformedLocation = reference_frames::
                getLocalVerticalToRotatingPlanetocentricFrameTransformationQuaternion( longitude,
                                                                                latitude )
                * reference_frames::
                getRotatingPlanetocentricToLocalVerticalFrameTransformationQuaternion( longitude,
                                                                                latitude )
                * startLocation;

        // Check whether both vectors are equal within tolerances.
        TUDAT_CHECK_MATRIX_CLOSE_FRACTION( transformedLocation, startLocation, 1.0e-14 );
    }
}

BOOST_AUTO_TEST_SUITE_END( )

} //namespace unit_tests
} //namespace tudat

<|endoftext|>


<commit_before>// Initial version copied from Initial version copied from https://github.com/JetBrains/intellij-community/blob/master/native/WinFsNotifier/fileWatcher3.c
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.

#ifdef _WIN32

#include "native.h"
#include "generic.h"
#include "win.h"

// TODO Find the right size for this
#define EVENT_BUFFER_SIZE (16*1024)

#define CREATE_SHARE (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
#define CREATE_FLAGS (FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED)
#define EVENT_MASK (FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | \
                    FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE)

class Server {
public:
    Server(
        JavaVM *jvm,
        JNIEnv *env,
        jobject watcherCallback,
        wchar_t drivePath[4],
        int watchedPathCount,
        wchar_t **watchedPaths
    ) {
        this->jvm = jvm;
        this->watcherCallback = env->NewGlobalRef(watcherCallback);
        this->stopEventHandle = CreateEvent(NULL, FALSE, FALSE, NULL);
        this->watchedPathCount = watchedPathCount;
        this->watchedPaths = watchedPaths;
        wcscpy_s(this->drivePath, 4, drivePath);

        this->threadHandle = CreateThread(
            NULL,                   // default security attributes
            0,                      // use default stack size
            EventProcessingThread,  // thread function name
            this,                   // argument to thread function
            0,                      // use default creation flags
            NULL                    // the thread identifier
        );
        SetThreadPriority(this->threadHandle, THREAD_PRIORITY_ABOVE_NORMAL);
    }

    void close(JNIEnv *env) {
        SetEvent(this->stopEventHandle);
        WaitForSingleObject(this->threadHandle, INFINITE);
        CloseHandle(this->threadHandle);
        CloseHandle(this->stopEventHandle);
        for (int i = 0; i < this->watchedPathCount; i++) {
            free(this->watchedPaths[i]);
        }
        free(this->watchedPaths);
        env->DeleteGlobalRef(this->watcherCallback);
    }

    static DWORD WINAPI EventProcessingThread(LPVOID data) {
        Server *server = (Server*) data;
        server->run();
        return 0;
    }

private:
    JavaVM *jvm;
    JNIEnv *env;
    wchar_t drivePath[4];
    int watchedPathCount;
    wchar_t **watchedPaths;
    jobject watcherCallback;

    HANDLE stopEventHandle;
    HANDLE threadHandle;

    void run() {
        printf("~~~~ Starting thread\n");

        // TODO Extract this logic to some shared function
        jint statAttach = jvm->AttachCurrentThreadAsDaemon((void **) &(env), NULL);
        if (statAttach != JNI_OK) {
            printf("Failed to attach JNI to current thread: %d\n", statAttach);
            return;
        }

        OVERLAPPED overlapped;
        memset(&overlapped, 0, sizeof(overlapped));
        overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

        HANDLE hDrive = CreateFileW(drivePath, GENERIC_READ, CREATE_SHARE, NULL, OPEN_EXISTING, CREATE_FLAGS, NULL);

        char buffer[EVENT_BUFFER_SIZE];
        HANDLE handles[2] = {stopEventHandle, overlapped.hEvent};
        while (true) {
            int rcDrive = ReadDirectoryChangesW(
                hDrive,
                buffer,
                sizeof(buffer),
                TRUE,
                EVENT_MASK,
                NULL,
                &overlapped,
                NULL
            );
            if (rcDrive == 0) {
                // TODO Error handling
                printf("~~~~ Couldn't read directory: %d - %d\n", rcDrive, GetLastError());
                break;
            }

            DWORD rc = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
            if (rc == WAIT_OBJECT_0) {
                break;
            }
            if (rc == WAIT_OBJECT_0 + 1) {
                DWORD dwBytesReturned;
                if (!GetOverlappedResult(hDrive, &overlapped, &dwBytesReturned, FALSE)) {
                    // TODO Error handling
                    printf("~~~~ Failed to wait\n");
                    break;
                }

                if (dwBytesReturned == 0) {
                    // don't send dirty too much, everything is changed anyway
                    // TODO Understand what this does
                    if (WaitForSingleObject(stopEventHandle, 500) == WAIT_OBJECT_0)
                        break;

                    // Got a buffer overflow => current changes lost => send INVALIDATE on root
                    reportEvent(FILE_EVENT_INVALIDATE, drivePath, 3);
                } else {
                    FILE_NOTIFY_INFORMATION *info = (FILE_NOTIFY_INFORMATION *)buffer;
                    do {
                        handlePathChanged(info);
                        info = (FILE_NOTIFY_INFORMATION *)((char *)info + info->NextEntryOffset);
                    } while (info->NextEntryOffset != 0);
                }
            }
        }

        printf("~~~~ Stopping thread\n");

        CloseHandle(overlapped.hEvent);
        CloseHandle(hDrive);

        // TODO Extract this logic to some shared function
        jint statDetach = jvm->DetachCurrentThread();
        if (statDetach != JNI_OK) {
            printf("Failed to detach JNI from current thread: %d\n", statAttach);
            return;
        }

        return;
    }

    void reportEvent(jint type, wchar_t *changedPath, int changedPathLen) {
        jstring changedPathJava = wchar_to_java(env, changedPath, changedPathLen, NULL);
        jclass callback_class = env->GetObjectClass(watcherCallback);
        jmethodID methodCallback = env->GetMethodID(callback_class, "pathChanged", "(ILjava/lang/String;)V");
        // TODO Do we need to add a global reference to the string here?
        env->CallVoidMethod(watcherCallback, methodCallback, type, changedPathJava);
    }

    void handlePathChanged(FILE_NOTIFY_INFORMATION *info) {
        int pathLen = info->FileNameLength / sizeof(wchar_t);
        wchar_t *changedPath = add_prefix(info->FileName, pathLen, drivePath);
        int changedPathLen = pathLen + 3;

        wprintf(L"~~~~ Changed: 0x%x %ls\n", info->Action, changedPath);

        jint type;
        if (info->Action == FILE_ACTION_ADDED || info->Action == FILE_ACTION_RENAMED_NEW_NAME) {
            type = FILE_EVENT_CREATED;
        } else if (info->Action == FILE_ACTION_REMOVED || info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
            type = FILE_EVENT_REMOVED;
        } else if (info->Action == FILE_ACTION_MODIFIED) {
            type = FILE_EVENT_MODIFIED;
        } else {
            wprintf(L"~~~~ Unknown event 0x%x for %ls\n", info->Action, changedPath);
            type = FILE_EVENT_UNKNOWN;
        }

        bool watching = false;
        for (int i = 0; i < watchedPathCount; i++) {
            wchar_t* watchedPath = watchedPaths[i];
            wprintf(L"~~~~ Checking if '%ls' starts with '%ls'\n", changedPath, watchedPath);
            if (wcsncmp(watchedPath, changedPath, wcslen(watchedPath)) == 0) {
                watching = true;
                break;
            }
        }
        if (!watching) {
            wprintf(L"~~~~ Ignoring %ls (root is not watched)\n", changedPath);
            return;
        }

        reportEvent(type, changedPath, changedPathLen);
        free(changedPath);
    }
};

JNIEXPORT jobject JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsFileEventFunctions_startWatching(JNIEnv *env, jclass target, jobjectArray paths, jobject javaCallback, jobject result) {

    printf("\n~~~~ Configuring...\n");

    JavaVM* jvm;
    int jvmStatus = env->GetJavaVM(&jvm);
    if (jvmStatus != JNI_OK) {
        mark_failed_with_errno(env, "Could not store jvm instance.", result);
        return NULL;
    }

    int watchedPathCount = env->GetArrayLength(paths);
    if (watchedPathCount == 0) {
        mark_failed_with_errno(env, "No paths given to watch.", result);
        return NULL;
    }

    wchar_t **watchedPaths = (wchar_t**)malloc(watchedPathCount * sizeof(wchar_t*));
    wchar_t driveLetter = L'\0';
    for (int i = 0; i < watchedPathCount; i++) {
        jstring path = (jstring) env->GetObjectArrayElement(paths, i);
        wchar_t* watchedPath = java_to_wchar_path(env, path, result);
        int watchedPathLen = wcslen(watchedPath);
        if (watchedPathLen > 240 || watchedPath[0] == L'\\') {
            mark_failed_with_errno(env, "Cannot watch long paths for now.", result);
            return NULL;
        }
        if (driveLetter == L'\0') {
            driveLetter = watchedPath[0];
        } else if (driveLetter != watchedPath[0]) {
            mark_failed_with_errno(env, "Cannot watch multiple drives for now.", result);
            return NULL;
        }
        if (watchedPath[watchedPathLen - 1] != L'\\') {
            wchar_t* oldWatchedPath = watchedPath;
            watchedPath = add_suffix(watchedPath, watchedPathLen, L"\\");
            free(oldWatchedPath);
        }
        wprintf(L"~~~~ Watching %ls\n", watchedPath);
        watchedPaths[i] = watchedPath;
    }
    wchar_t drivePath[4] = {towupper(watchedPaths[0][0]), L':', L'\\', L'\0'};

    Server* server = new Server(
        jvm,
        env,
        javaCallback,
        drivePath,
        watchedPathCount,
        watchedPaths
    );

    jclass clsWatch = env->FindClass("net/rubygrapefruit/platform/internal/jni/WindowsFileEventFunctions$WatcherImpl");
    jmethodID constructor = env->GetMethodID(clsWatch, "<init>", "(Ljava/lang/Object;)V");
    return env->NewObject(clsWatch, constructor, env->NewDirectByteBuffer(server, sizeof(server)));
}

JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsFileEventFunctions_stopWatching(JNIEnv *env, jclass target, jobject detailsObj, jobject result) {
    Server* server = (Server*)env->GetDirectBufferAddress(detailsObj);
    server->close(env);
    delete server;
}

#endif
<commit_msg>Remove JetBrains reference<commit_after>#ifdef _WIN32

#include "native.h"
#include "generic.h"
#include "win.h"

// TODO Find the right size for this
#define EVENT_BUFFER_SIZE (16*1024)

#define CREATE_SHARE (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
#define CREATE_FLAGS (FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED)
#define EVENT_MASK (FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | \
                    FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE)

class Server {
public:
    Server(
        JavaVM *jvm,
        JNIEnv *env,
        jobject watcherCallback,
        wchar_t drivePath[4],
        int watchedPathCount,
        wchar_t **watchedPaths
    ) {
        this->jvm = jvm;
        this->watcherCallback = env->NewGlobalRef(watcherCallback);
        this->stopEventHandle = CreateEvent(NULL, FALSE, FALSE, NULL);
        this->watchedPathCount = watchedPathCount;
        this->watchedPaths = watchedPaths;
        wcscpy_s(this->drivePath, 4, drivePath);

        this->threadHandle = CreateThread(
            NULL,                   // default security attributes
            0,                      // use default stack size
            EventProcessingThread,  // thread function name
            this,                   // argument to thread function
            0,                      // use default creation flags
            NULL                    // the thread identifier
        );
        SetThreadPriority(this->threadHandle, THREAD_PRIORITY_ABOVE_NORMAL);
    }

    void close(JNIEnv *env) {
        SetEvent(this->stopEventHandle);
        WaitForSingleObject(this->threadHandle, INFINITE);
        CloseHandle(this->threadHandle);
        CloseHandle(this->stopEventHandle);
        for (int i = 0; i < this->watchedPathCount; i++) {
            free(this->watchedPaths[i]);
        }
        free(this->watchedPaths);
        env->DeleteGlobalRef(this->watcherCallback);
    }

    static DWORD WINAPI EventProcessingThread(LPVOID data) {
        Server *server = (Server*) data;
        server->run();
        return 0;
    }

private:
    JavaVM *jvm;
    JNIEnv *env;
    wchar_t drivePath[4];
    int watchedPathCount;
    wchar_t **watchedPaths;
    jobject watcherCallback;

    HANDLE stopEventHandle;
    HANDLE threadHandle;

    void run() {
        printf("~~~~ Starting thread\n");

        // TODO Extract this logic to some shared function
        jint statAttach = jvm->AttachCurrentThreadAsDaemon((void **) &(env), NULL);
        if (statAttach != JNI_OK) {
            printf("Failed to attach JNI to current thread: %d\n", statAttach);
            return;
        }

        OVERLAPPED overlapped;
        memset(&overlapped, 0, sizeof(overlapped));
        overlapped.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

        HANDLE hDrive = CreateFileW(drivePath, GENERIC_READ, CREATE_SHARE, NULL, OPEN_EXISTING, CREATE_FLAGS, NULL);

        char buffer[EVENT_BUFFER_SIZE];
        HANDLE handles[2] = {stopEventHandle, overlapped.hEvent};
        while (true) {
            int rcDrive = ReadDirectoryChangesW(
                hDrive,
                buffer,
                sizeof(buffer),
                TRUE,
                EVENT_MASK,
                NULL,
                &overlapped,
                NULL
            );
            if (rcDrive == 0) {
                // TODO Error handling
                printf("~~~~ Couldn't read directory: %d - %d\n", rcDrive, GetLastError());
                break;
            }

            DWORD rc = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
            if (rc == WAIT_OBJECT_0) {
                break;
            }
            if (rc == WAIT_OBJECT_0 + 1) {
                DWORD dwBytesReturned;
                if (!GetOverlappedResult(hDrive, &overlapped, &dwBytesReturned, FALSE)) {
                    // TODO Error handling
                    printf("~~~~ Failed to wait\n");
                    break;
                }

                if (dwBytesReturned == 0) {
                    // don't send dirty too much, everything is changed anyway
                    // TODO Understand what this does
                    if (WaitForSingleObject(stopEventHandle, 500) == WAIT_OBJECT_0)
                        break;

                    // Got a buffer overflow => current changes lost => send INVALIDATE on root
                    reportEvent(FILE_EVENT_INVALIDATE, drivePath, 3);
                } else {
                    FILE_NOTIFY_INFORMATION *info = (FILE_NOTIFY_INFORMATION *)buffer;
                    do {
                        handlePathChanged(info);
                        info = (FILE_NOTIFY_INFORMATION *)((char *)info + info->NextEntryOffset);
                    } while (info->NextEntryOffset != 0);
                }
            }
        }

        printf("~~~~ Stopping thread\n");

        CloseHandle(overlapped.hEvent);
        CloseHandle(hDrive);

        // TODO Extract this logic to some shared function
        jint statDetach = jvm->DetachCurrentThread();
        if (statDetach != JNI_OK) {
            printf("Failed to detach JNI from current thread: %d\n", statAttach);
            return;
        }

        return;
    }

    void reportEvent(jint type, wchar_t *changedPath, int changedPathLen) {
        jstring changedPathJava = wchar_to_java(env, changedPath, changedPathLen, NULL);
        jclass callback_class = env->GetObjectClass(watcherCallback);
        jmethodID methodCallback = env->GetMethodID(callback_class, "pathChanged", "(ILjava/lang/String;)V");
        // TODO Do we need to add a global reference to the string here?
        env->CallVoidMethod(watcherCallback, methodCallback, type, changedPathJava);
    }

    void handlePathChanged(FILE_NOTIFY_INFORMATION *info) {
        int pathLen = info->FileNameLength / sizeof(wchar_t);
        wchar_t *changedPath = add_prefix(info->FileName, pathLen, drivePath);
        int changedPathLen = pathLen + 3;

        wprintf(L"~~~~ Changed: 0x%x %ls\n", info->Action, changedPath);

        jint type;
        if (info->Action == FILE_ACTION_ADDED || info->Action == FILE_ACTION_RENAMED_NEW_NAME) {
            type = FILE_EVENT_CREATED;
        } else if (info->Action == FILE_ACTION_REMOVED || info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
            type = FILE_EVENT_REMOVED;
        } else if (info->Action == FILE_ACTION_MODIFIED) {
            type = FILE_EVENT_MODIFIED;
        } else {
            wprintf(L"~~~~ Unknown event 0x%x for %ls\n", info->Action, changedPath);
            type = FILE_EVENT_UNKNOWN;
        }

        bool watching = false;
        for (int i = 0; i < watchedPathCount; i++) {
            wchar_t* watchedPath = watchedPaths[i];
            wprintf(L"~~~~ Checking if '%ls' starts with '%ls'\n", changedPath, watchedPath);
            if (wcsncmp(watchedPath, changedPath, wcslen(watchedPath)) == 0) {
                watching = true;
                break;
            }
        }
        if (!watching) {
            wprintf(L"~~~~ Ignoring %ls (root is not watched)\n", changedPath);
            return;
        }

        reportEvent(type, changedPath, changedPathLen);
        free(changedPath);
    }
};

JNIEXPORT jobject JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsFileEventFunctions_startWatching(JNIEnv *env, jclass target, jobjectArray paths, jobject javaCallback, jobject result) {

    printf("\n~~~~ Configuring...\n");

    JavaVM* jvm;
    int jvmStatus = env->GetJavaVM(&jvm);
    if (jvmStatus != JNI_OK) {
        mark_failed_with_errno(env, "Could not store jvm instance.", result);
        return NULL;
    }

    int watchedPathCount = env->GetArrayLength(paths);
    if (watchedPathCount == 0) {
        mark_failed_with_errno(env, "No paths given to watch.", result);
        return NULL;
    }

    wchar_t **watchedPaths = (wchar_t**)malloc(watchedPathCount * sizeof(wchar_t*));
    wchar_t driveLetter = L'\0';
    for (int i = 0; i < watchedPathCount; i++) {
        jstring path = (jstring) env->GetObjectArrayElement(paths, i);
        wchar_t* watchedPath = java_to_wchar_path(env, path, result);
        int watchedPathLen = wcslen(watchedPath);
        if (watchedPathLen > 240 || watchedPath[0] == L'\\') {
            mark_failed_with_errno(env, "Cannot watch long paths for now.", result);
            return NULL;
        }
        if (driveLetter == L'\0') {
            driveLetter = watchedPath[0];
        } else if (driveLetter != watchedPath[0]) {
            mark_failed_with_errno(env, "Cannot watch multiple drives for now.", result);
            return NULL;
        }
        if (watchedPath[watchedPathLen - 1] != L'\\') {
            wchar_t* oldWatchedPath = watchedPath;
            watchedPath = add_suffix(watchedPath, watchedPathLen, L"\\");
            free(oldWatchedPath);
        }
        wprintf(L"~~~~ Watching %ls\n", watchedPath);
        watchedPaths[i] = watchedPath;
    }
    wchar_t drivePath[4] = {towupper(watchedPaths[0][0]), L':', L'\\', L'\0'};

    Server* server = new Server(
        jvm,
        env,
        javaCallback,
        drivePath,
        watchedPathCount,
        watchedPaths
    );

    jclass clsWatch = env->FindClass("net/rubygrapefruit/platform/internal/jni/WindowsFileEventFunctions$WatcherImpl");
    jmethodID constructor = env->GetMethodID(clsWatch, "<init>", "(Ljava/lang/Object;)V");
    return env->NewObject(clsWatch, constructor, env->NewDirectByteBuffer(server, sizeof(server)));
}

JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsFileEventFunctions_stopWatching(JNIEnv *env, jclass target, jobject detailsObj, jobject result) {
    Server* server = (Server*)env->GetDirectBufferAddress(detailsObj);
    server->close(env);
    delete server;
}

#endif
<|endoftext|>