summaryrefslogtreecommitdiff
path: root/app/controller/serial-controller.cc
blob: 8485ec4100e4b7867e3b2c75ffbb2ed1d10cba26 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include "serial-controller.hh"
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <sys/ioctl.h>

namespace nes::app::test
{
	serial_controller::serial_controller(char const* path)
		: path_{ path }
	{
		fd_ = open(path, O_RDONLY | O_NOCTTY | O_NDELAY | O_NONBLOCK);
		if (fd_ == -1)
		{
			perror("serial_controller: open");
			return;
		}

		if (!set_up()) { close(); }
	}

	serial_controller::~serial_controller()
	{
		close();
	}

	auto serial_controller::is_reliable() const -> bool
	{
		return fd_ != -1;
	}

	auto serial_controller::read_buttons() -> sys::button_mask
	{
		if (fd_ == -1) { return sys::button_mask{}; }

		// Poll for any updates until we can't read anymore.
		while (true)
		{
			auto buf = u8{};
			auto const res = read(fd_, &buf, 1);
			if (res == -1)
			{
				if (errno == EAGAIN || errno == EWOULDBLOCK) { break; }

				perror("read from controller");
				close();
				break;
			}

			current_ = sys::button_mask::from_raw_value(buf);
		}

		return current_;
	}


	auto serial_controller::set_up() -> bool
	{
		if (ioctl(fd_, TIOCEXCL) == -1)
		{
			perror("serial_controller: ioctl");
			return false;
		}

		auto attr = termios{};
		if (tcgetattr(fd_, &attr) == -1)
		{
			perror("serial_controller: tcgetattr");
			return false;
		}

		cfsetispeed(&attr, B57600);
		cfsetospeed(&attr, B57600);
		attr.c_cflag = attr.c_cflag | CLOCAL | CREAD;
		attr.c_cflag = (attr.c_cflag & static_cast<tcflag_t>(~CSIZE)) | CS8;

		if (tcsetattr(fd_, TCSANOW, &attr) == -1)
		{
			perror("serial_controller: tcsetattr");
			return false;
		}

		return true;
	}

	auto serial_controller::close() -> void
	{
		if (fd_ != -1)
		{
			::close(fd_);
			fd_ = -1;
		}
	}
} // namespace nes::app::mac