mirror of
				https://github.com/dolphin-emu/dolphin.git
				synced 2025-10-25 09:29:43 +00:00 
			
		
		
		
	some useless IR pointer math... It still doesn't make rotation work a sign fix when tilting along y axis (twisting the wiimote) git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6121 8ced0084-cf51-0410-be5f-012b33b47a6e
		
			
				
	
	
		
			420 lines
		
	
	
	
		
			8.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			420 lines
		
	
	
	
		
			8.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "UDPWiimote.h"
 | |
| 
 | |
| #ifdef _WIN32
 | |
| 
 | |
| #include <winsock2.h>
 | |
| #include <ws2tcpip.h>
 | |
| #define sock_t SOCKET
 | |
| #define ERRNO WSAGetLastError()
 | |
| #define EWOULDBLOCK WSAEWOULDBLOCK
 | |
| #define BAD_SOCK INVALID_SOCKET
 | |
| #define close(x) closesocket(x)
 | |
| #define cleanup do {noinst--; if (noinst==0) WSACleanup();} while (0)
 | |
| #define blockingoff(sock) ioctlsocket(sock, FIONBIO, &iMode)
 | |
| #define dataz char*
 | |
| #ifdef _MSC_VER
 | |
| #pragma comment (lib, "Ws2_32.lib")
 | |
| #endif
 | |
| 
 | |
| #else
 | |
| 
 | |
| #include <unistd.h>
 | |
| #include <errno.h>
 | |
| #include <sys/types.h>
 | |
| #include <sys/socket.h>
 | |
| #include <netinet/in.h>
 | |
| #include <arpa/inet.h>
 | |
| #include <netdb.h>
 | |
| #include <unistd.h>
 | |
| #include <fcntl.h>
 | |
| #define BAD_SOCK -1
 | |
| #define ERRNO errno
 | |
| #define cleanup noinst--
 | |
| #define blockingoff(sock) fcntl(sock, F_SETFL, O_NONBLOCK)
 | |
| #define dataz void*
 | |
| #define sock_t int
 | |
| 
 | |
| #endif
 | |
| 
 | |
| #include "Thread.h"
 | |
| #include "Timer.h"
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <time.h>
 | |
| #include <list>
 | |
| 
 | |
| struct UDPWiimote::_d
 | |
| {
 | |
| 	Common::Thread * thread;
 | |
| 	std::list<sock_t> sockfds;
 | |
| 	Common::CriticalSection termLock,mutex,nameMutex;
 | |
| 	volatile bool exit;
 | |
| 	sock_t bipv4_fd,bipv6_fd;
 | |
| };
 | |
| 
 | |
| int UDPWiimote::noinst=0;
 | |
| 
 | |
| void _UDPWiiThread(void* arg)
 | |
| {
 | |
| 	((UDPWiimote*)arg)->mainThread();
 | |
| }
 | |
| 
 | |
| THREAD_RETURN UDPWiiThread(void* arg)
 | |
| {
 | |
| 	_UDPWiiThread(arg);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| UDPWiimote::UDPWiimote(const char *_port, const char * name, int _index) : 
 | |
| 	port(_port), displayName(name),
 | |
| 	d(new _d) ,x(0),y(0),z(1.0f),naX(0),naY(0),naZ(-1.0f),nunX(0),nunY(0),
 | |
| 	pointerX(1001.0f/2),pointerY(0),nunMask(0),mask(0),index(_index), int_port(atoi(_port))
 | |
| {
 | |
| 		
 | |
| 	static bool sranded=false;
 | |
| 	if (!sranded)
 | |
| 	{
 | |
| 		srand(time(0));
 | |
| 		sranded=true;
 | |
| 	}
 | |
| 	bcastMagic=rand() & 0xFFFF;
 | |
| 	
 | |
| 	#ifdef _WIN32
 | |
| 	u_long iMode = 1;
 | |
| 	#endif
 | |
| 	struct addrinfo hints, *servinfo, *p;
 | |
| 	int rv;
 | |
| 	d->thread=NULL;
 | |
| 
 | |
| 	#ifdef _WIN32
 | |
| 	if (noinst==0)
 | |
| 	{
 | |
| 		WORD sockVersion;
 | |
| 		WSADATA wsaData;
 | |
| 		sockVersion = MAKEWORD(2, 2);
 | |
| 		WSAStartup(sockVersion, &wsaData);
 | |
| 	}
 | |
| 	#endif
 | |
| 	
 | |
| 	noinst++;
 | |
| 
 | |
| 	memset(&hints, 0, sizeof hints);
 | |
| 	hints.ai_family = AF_INET;
 | |
| 	hints.ai_socktype = SOCK_DGRAM;
 | |
| 	hints.ai_flags = AI_PASSIVE; // use my IP
 | |
| 	
 | |
| 	if (!int_port){
 | |
| 		cleanup;
 | |
| 		err=-1;
 | |
| 		return;
 | |
| 	}
 | |
| 	
 | |
| 	if ((rv = getaddrinfo(NULL, _port, &hints, &servinfo)) != 0) {
 | |
| 		cleanup;
 | |
| 		err=-1;
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	// loop through all the results and bind to everything we can
 | |
| 	for(p = servinfo; p != NULL; p = p->ai_next) {
 | |
| 		sock_t sock;
 | |
| 		if ((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == BAD_SOCK) {
 | |
| 			continue;
 | |
| 		}
 | |
| 		if (bind(sock, p->ai_addr, p->ai_addrlen) == -1) {
 | |
| 			close(sock);
 | |
| 			continue;
 | |
| 		}
 | |
| 		d->sockfds.push_back(sock);
 | |
| 	}
 | |
| 	
 | |
| 	if (d->sockfds.empty()) {
 | |
| 		cleanup;
 | |
| 		err=-2;
 | |
| 		return;
 | |
| 	}
 | |
| 	
 | |
| 	freeaddrinfo(servinfo);
 | |
| 	err=0;
 | |
| 	d->exit=false;
 | |
| 	initBroadcastIPv4();
 | |
| 	initBroadcastIPv6();
 | |
| 	d->termLock.Enter();
 | |
| 	d->thread = new Common::Thread(UDPWiiThread,this);
 | |
| 	d->termLock.Leave();
 | |
| 	return;
 | |
| }
 | |
| 
 | |
| void UDPWiimote::mainThread()
 | |
| {
 | |
| 	d->termLock.Enter();
 | |
| 	Common::Timer time;
 | |
| 	fd_set fds;
 | |
| 	struct timeval timeout;
 | |
| 	timeout.tv_sec=0;
 | |
| 	timeout.tv_usec=0;
 | |
| 	time.Update();
 | |
| 	do
 | |
| 	{
 | |
| 		int maxfd=0;
 | |
| 		FD_ZERO(&fds);
 | |
| 		for (std::list<sock_t>::iterator i=d->sockfds.begin(); i!=d->sockfds.end(); i++)
 | |
| 		{
 | |
| 			FD_SET(*i,&fds);
 | |
| #ifndef _WIN32
 | |
| 			if (*i>=maxfd)
 | |
| 				maxfd=(*i)+1;
 | |
| #endif
 | |
| 		}
 | |
| 		
 | |
| 		u64 tleft=timeout.tv_sec*1000+timeout.tv_usec/1000;
 | |
| 		u64 telapsed=time.GetTimeDifference();
 | |
| 		time.Update();
 | |
| 		if (tleft<=telapsed)
 | |
| 		{
 | |
| 			timeout.tv_sec=1;
 | |
| 			timeout.tv_usec=500000;
 | |
| 			broadcastPresence();
 | |
| 		} else {
 | |
| 			tleft-=telapsed;
 | |
| 			timeout.tv_sec=tleft/1000;
 | |
| 			timeout.tv_usec=(tleft%1000)*1000;
 | |
| 		}
 | |
| 		
 | |
| 		d->termLock.Leave(); //VERY hacky. don't like it
 | |
| 		if (d->exit) return; 
 | |
| 		int rt=select(maxfd,&fds,NULL,NULL,&timeout);
 | |
| 		if (d->exit) return;
 | |
| 		d->termLock.Enter();
 | |
| 		if (d->exit) return;
 | |
| 		
 | |
| 		if (rt)
 | |
| 		{
 | |
| 			for (std::list<sock_t>::iterator i=d->sockfds.begin(); i!=d->sockfds.end(); i++)
 | |
| 				if (FD_ISSET(*i,&fds))
 | |
| 				{
 | |
| 					sock_t fd=*i;
 | |
| 					u8 bf[64];
 | |
| 					int size=60;
 | |
| 					size_t addr_len;
 | |
| 					struct sockaddr_storage their_addr;
 | |
| 					addr_len = sizeof their_addr;
 | |
| 					if ((size = recvfrom(fd, 
 | |
| 										 (dataz)bf, 
 | |
| 										 size , 0,(struct sockaddr *)&their_addr, (socklen_t*)&addr_len)) == -1) 
 | |
| 					{
 | |
| 						ERROR_LOG(WIIMOTE,"UDPWii Packet error");
 | |
| 					}
 | |
| 					else
 | |
| 					{
 | |
| 						d->mutex.Enter();
 | |
| 						if (pharsePacket(bf,size)==0)
 | |
| 						{
 | |
| 							//NOTICE_LOG(WIIMOTE,"UDPWII New pack");
 | |
| 						} else {
 | |
| 							//NOTICE_LOG(WIIMOTE,"UDPWII Wrong pack format... ignoring");
 | |
| 						}
 | |
| 						d->mutex.Leave();
 | |
| 					}
 | |
| 				}
 | |
| 		}
 | |
| 	} while (!(d->exit));
 | |
| 	d->termLock.Leave();
 | |
| }
 | |
| 
 | |
| UDPWiimote::~UDPWiimote()
 | |
| {
 | |
| 	d->exit=true;
 | |
| 	d->thread->WaitForDeath();
 | |
| 	d->termLock.Enter();
 | |
| 	d->termLock.Leave();
 | |
| 	for (std::list<sock_t>::iterator i=d->sockfds.begin(); i!=d->sockfds.end(); i++)
 | |
| 		close(*i);
 | |
| 	close(d->bipv4_fd);
 | |
| 	close(d->bipv6_fd);
 | |
| 	cleanup;
 | |
| 	delete d;
 | |
| }
 | |
| 
 | |
| #define ACCEL_FLAG (1<<0)
 | |
| #define BUTT_FLAG (1<<1)
 | |
| #define IR_FLAG (1<<2)
 | |
| #define NUN_FLAG (1<<3)
 | |
| #define NUNACCEL_FLAG (1<<4)
 | |
| 
 | |
| int UDPWiimote::pharsePacket(u8 * bf, size_t size)
 | |
| {
 | |
| 	if (size<3) return -1;
 | |
| 	if (bf[0]!=0xde)
 | |
| 		return -1;
 | |
| 	//if (bf[1]==0)
 | |
| 	//	time=0;
 | |
| 	//if (bf[1]<time) //NOT LONGER NEEDED TO ALLOW MULTIPLE IPHONES ON A SINGLE PORT
 | |
| 	//	return -1;
 | |
| 	//time=bf[1];
 | |
| 	u32 *p=(u32*)(&bf[3]);
 | |
| 	if (bf[2]&ACCEL_FLAG)
 | |
| 	{
 | |
| 		if ((size-(((u8*)p)-bf))<12) return -1;
 | |
| 		double ux,uy,uz;
 | |
| 		ux=(double)((s32)ntohl(*p)); p++;
 | |
| 		uy=(double)((s32)ntohl(*p)); p++;
 | |
| 		uz=(double)((s32)ntohl(*p)); p++;
 | |
| 		x=ux/1048576; //packet accel data
 | |
| 		y=uy/1048576;
 | |
| 		z=uz/1048576;
 | |
| 	}
 | |
| 	if (bf[2]&BUTT_FLAG)
 | |
| 	{
 | |
| 		if ((size-(((u8*)p)-bf))<4) return -1;
 | |
| 		mask=ntohl(*p); p++;
 | |
| 	}
 | |
| 	if (bf[2]&IR_FLAG)
 | |
| 	{
 | |
| 		if ((size-(((u8*)p)-bf))<8) return -1;
 | |
| 		pointerX=((double)((s32)ntohl(*p)))/1048576; p++;
 | |
| 		pointerY=((double)((s32)ntohl(*p)))/1048576; p++;
 | |
| 	}
 | |
| 	if (bf[2]&NUN_FLAG)
 | |
| 	{
 | |
| 		if ((size-(((u8*)p)-bf))<9) return -1;
 | |
| 		nunMask=*((u8*)p); p=(u32*)(((u8*)p)+1);
 | |
| 		nunX=((double)((s32)ntohl(*p)))/1048576; p++;
 | |
| 		nunY=((double)((s32)ntohl(*p)))/1048576; p++;
 | |
| 	}
 | |
| 	if (bf[2]&NUNACCEL_FLAG)
 | |
| 	{
 | |
| 		if ((size-(((u8*)p)-bf))<12) return -1;
 | |
| 		double ux,uy,uz;
 | |
| 		ux=(double)((s32)ntohl(*p)); p++;
 | |
| 		uy=(double)((s32)ntohl(*p)); p++;
 | |
| 		uz=(double)((s32)ntohl(*p)); p++;
 | |
| 		naX=ux/1048576; //packet accel data
 | |
| 		naY=uy/1048576;
 | |
| 		naZ=uz/1048576;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| void UDPWiimote::initBroadcastIPv4()
 | |
| {
 | |
| 	d->bipv4_fd=socket(AF_INET, SOCK_DGRAM, 0);
 | |
| 	if (d->bipv4_fd == BAD_SOCK)
 | |
| 	{
 | |
| 		WARN_LOG(WIIMOTE,"socket() failed");
 | |
| 		return;
 | |
| 	}
 | |
| 	
 | |
| 	int broad=1;
 | |
| 	if (setsockopt(d->bipv4_fd,SOL_SOCKET,SO_BROADCAST, (const dataz)(&broad), sizeof broad) == -1)
 | |
| 	{
 | |
| 		WARN_LOG(WIIMOTE,"setsockopt(SO_BROADCAST) failed");
 | |
| 		return;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void UDPWiimote::broadcastIPv4(const void * data, size_t size)
 | |
| {
 | |
| 
 | |
| 	struct sockaddr_in their_addr;
 | |
| 	their_addr.sin_family = AF_INET;
 | |
| 	their_addr.sin_port = htons(4431);
 | |
| 	their_addr.sin_addr.s_addr = INADDR_BROADCAST;
 | |
|     memset(their_addr.sin_zero, '\0', sizeof their_addr.sin_zero);
 | |
| 	
 | |
| 	int num;
 | |
| 	if ((num=sendto(d->bipv4_fd,(const dataz)data,size,0,(struct sockaddr *) &their_addr, sizeof their_addr)) == -1)
 | |
| 	{
 | |
| 		WARN_LOG(WIIMOTE,"sendto() failed");
 | |
| 		return;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void UDPWiimote::initBroadcastIPv6()
 | |
| {
 | |
| 	//TODO: IPv6 support
 | |
| }
 | |
| 
 | |
| void UDPWiimote::broadcastIPv6(const void * data, size_t size)
 | |
| {
 | |
| 	//TODO: IPv6 support
 | |
| }
 | |
| 
 | |
| void UDPWiimote::broadcastPresence()
 | |
| {
 | |
| 	size_t slen;
 | |
| 	u8 bf[512];
 | |
| 	bf[0]=0xdf; //magic number
 | |
| 	*((u16*)(&(bf[1])))=htons(bcastMagic); //unique per-wiimote 16-bit ID
 | |
| 	bf[3]=(u8)index; //wiimote index
 | |
| 	*((u16*)(&(bf[4])))=htons(int_port); //port
 | |
| 	d->nameMutex.Enter();
 | |
| 	slen=displayName.size();
 | |
| 	if (slen>=256)
 | |
| 		slen=255;
 | |
| 	bf[6]=(u8)slen; //display name size (max 255)
 | |
| 	memcpy(&(bf[7]),displayName.c_str(),slen); //display name
 | |
| 	d->nameMutex.Leave();
 | |
| 	broadcastIPv4(bf,7+slen);
 | |
| 	broadcastIPv6(bf,7+slen);
 | |
| }
 | |
| 
 | |
| void UDPWiimote::getAccel(float &_x, float &_y, float &_z)
 | |
| {
 | |
| 	d->mutex.Enter();
 | |
| 	_x=(float)x;
 | |
| 	_y=(float)y;
 | |
| 	_z=(float)z;
 | |
| 	d->mutex.Leave();
 | |
| }
 | |
| 
 | |
| u32 UDPWiimote::getButtons()
 | |
| {
 | |
| 	u32 msk;
 | |
| 	d->mutex.Enter();
 | |
| 	msk=mask;
 | |
| 	d->mutex.Leave();
 | |
| 	return msk;
 | |
| }
 | |
| 
 | |
| void UDPWiimote::getIR(float &_x, float &_y)
 | |
| {
 | |
| 	d->mutex.Enter();
 | |
| 	_x=(float)pointerX;
 | |
| 	_y=(float)pointerY;
 | |
| 	d->mutex.Leave();
 | |
| }
 | |
| 
 | |
| void UDPWiimote::getNunchuck(float &_x, float &_y, u8 &_mask)
 | |
| {
 | |
| 	d->mutex.Enter();
 | |
| 	_x=(float)nunX;
 | |
| 	_y=(float)nunY;
 | |
| 	_mask=nunMask;
 | |
| 	d->mutex.Leave();
 | |
| }
 | |
| 
 | |
| void UDPWiimote::getNunchuckAccel(float &_x, float &_y, float &_z)
 | |
| {
 | |
| 	d->mutex.Enter();
 | |
| 	_x=(float)naX;
 | |
| 	_y=(float)naY;
 | |
| 	_z=(float)naZ;
 | |
| 	d->mutex.Leave();
 | |
| }
 | |
| 
 | |
| 
 | |
| const char * UDPWiimote::getPort()
 | |
| {
 | |
| 	return port.c_str();
 | |
| }
 | |
| 
 | |
| 
 | |
| void UDPWiimote::changeName(const char * name)
 | |
| {
 | |
| 	d->nameMutex.Enter();
 | |
| 	displayName=name;
 | |
| 	d->nameMutex.Leave();
 | |
| }
 |