Skip to content

Commit 3572cbe

Browse files
committed
Finally the recorder tool is here
1 parent 34c4ef4 commit 3572cbe

File tree

3 files changed

+233
-29
lines changed

3 files changed

+233
-29
lines changed

CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ add_executable(ydotoold ${SOURCE_FILES_DAEMON})
5757
target_link_libraries(ydotoold ydotool_library dl pthread boost_program_options uInputPlus evdevPlus)
5858

5959
add_executable(ydotool_client ${SOURCE_FILES_CLIENT})
60-
target_link_libraries(ydotool_client ydotool_library boost_program_options uInputPlus evdevPlus)
60+
target_link_libraries(ydotool_client ydotool_library boost_program_options pthread uInputPlus evdevPlus)
6161
set_target_properties(ydotool_client PROPERTIES OUTPUT_NAME ydotool)
6262

6363
add_executable(ydotool_client_static ${SOURCE_FILES_CLIENT})
64-
target_link_libraries(ydotool_client_static ydotool_library_static boost_program_options uInputPlus evdevPlus -static)
64+
target_link_libraries(ydotool_client_static ydotool_library_static boost_program_options pthread uInputPlus evdevPlus -static)
6565
set_target_properties(ydotool_client_static PROPERTIES OUTPUT_NAME ydotool_static)
6666

6767
#add_library(mousemove SHARED Tools/MouseMove/MouseMove.hpp Tools/MouseMove/MouseMove.cpp)

Tools/Recorder/Recorder.cpp

Lines changed: 218 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,78 @@
44

55
#include "Recorder.hpp"
66

7-
using namespace ydotool::Tools;
7+
using namespace ydotool;
8+
using namespace Tools;
89

910
const char ydotool_tool_name[] = "recorder";
1011

1112

1213
static void ShowHelp(const char *argv_0){
13-
std::cerr << "Usage: " << argv_0 << " <output file> [devices]\n"
14+
std::cerr << "Usage: " << argv_0 << " [--delay <ms] [--duration <ms>] [--record <output file> [devices]] [--replay <input file>]\n"
1415
<< " --help Show this help.\n"
15-
<< " devices Devices to record from. Default is all." << std::endl;
16+
<< " --record \n"
17+
<< " devices Devices to record from. Default is all, including non-keyboard devices.\n"
18+
<< " --replay \n"
19+
<< " --display \n"
20+
<< " --delay ms Delay time before start recording/replaying. Default 5000ms.\n"
21+
<< " --duration ms Record duration. Otherwise use SIGINT to stop recording.\n"
22+
"\n"
23+
"The record file can't be replayed on an architecture with different endianness." << std::endl;
1624
}
1725

1826
const char *Recorder::Name() {
1927
return ydotool_tool_name;
2028
}
2129

30+
static int fd_file = -1;
2231

23-
int Recorder::Exec(int argc, const char **argv) {
24-
std::cout << "argc = " << argc << "\n";
32+
static std::vector<uint8_t> record_buffer;
33+
static Recorder::file_header header;
2534

26-
for (int i=1; i<argc; i++) {
27-
std::cout << "argv["<<i<<"] = " << argv[i] << "\n";
28-
}
35+
static void generate_header() {
36+
auto &m = header.magic;
37+
m[0] = 'Y';
38+
m[1] = 'D';
39+
m[2] = 'T';
40+
m[3] = 'L';
41+
42+
header.feature_mask = 0;
43+
header.size = record_buffer.size();
44+
header.crc32 = Utils::crc32(record_buffer.data(), record_buffer.size());
45+
}
46+
47+
static void stop_handler(int whatever) {
48+
std::cout << "Saving file...\n";
49+
generate_header();
50+
51+
write(fd_file, &header, sizeof(header));
52+
write(fd_file, record_buffer.data(), record_buffer.size());
53+
54+
close(fd_file);
55+
56+
std::cout << "Done.\n";
57+
58+
exit(0);
59+
}
2960

61+
62+
int Recorder::Exec(int argc, const char **argv) {
3063
std::vector<std::string> extra_args;
3164

65+
int delay = 5000;
66+
int duration = 0;
67+
int mode = 0;
68+
3269
try {
3370

3471
po::options_description desc("");
3572
desc.add_options()
3673
("help", "Show this help")
74+
("record", "")
75+
("replay", "")
76+
("display", "")
77+
("delay", po::value<int>())
78+
("duration", po::value<int>())
3779
("extra-args", po::value(&extra_args));
3880

3981

@@ -48,47 +90,190 @@ int Recorder::Exec(int argc, const char **argv) {
4890
run(), vm);
4991
po::notify(vm);
5092

93+
if (vm.count("delay")) {
94+
delay = vm["delay"].as<int>();
95+
}
96+
97+
if (vm.count("duration")) {
98+
duration = vm["duration"].as<int>();
99+
}
51100

52101
if (vm.count("help")) {
53102
ShowHelp(argv[0]);
54103
return -1;
55104
}
56105

106+
if (vm.count("record")) {
107+
mode = 1;
108+
}
109+
110+
if (vm.count("replay")) {
111+
mode = 2;
112+
}
113+
114+
if (vm.count("display")) {
115+
mode = 3;
116+
}
117+
118+
if (!mode)
119+
throw std::invalid_argument("mode not specified");
120+
57121
if (extra_args.empty())
58-
throw std::invalid_argument("output file not specified");
122+
throw std::invalid_argument("file not specified");
59123

60124

61125
} catch (std::exception &e) {
62126
std::cerr << "ydotool: " << argv[0] << ": error: " << e.what() << std::endl;
127+
std::cerr << "Use --help for help.\n";
128+
63129
return 2;
64130
}
65131

66132
auto& filepath = extra_args.front();
67133

68-
fd_file = open(filepath.c_str(), O_WRONLY|O_CREAT, 0644);
134+
if (mode == 1)
135+
fd_file = open(filepath.c_str(), O_WRONLY|O_CREAT, 0644);
136+
else
137+
fd_file = open(filepath.c_str(), O_RDONLY);
69138

70139
if (fd_file == -1) {
71140
std::cerr << "ydotool: " << argv[0] << ": error: failed to open " << filepath << ": "
72-
<< strerror(errno) << std::endl;
141+
<< strerror(errno) << std::endl;
73142
return 2;
74143
}
75144

76-
extra_args.erase(extra_args.begin());
145+
std::cerr << "Delay was set to "
146+
<< delay << " milliseconds.\n";
77147

78-
auto& device_list = extra_args;
79148

80-
if (device_list.empty()) {
81-
device_list = find_all_devices();
149+
150+
if (mode == 1) {
151+
extra_args.erase(extra_args.begin());
152+
153+
auto &device_list = extra_args;
82154

83155
if (device_list.empty()) {
84-
std::cerr << "ydotool: " << argv[0] << ": error: no event device found in /dev/input/"
85-
<< std::endl;
86-
return 2;
156+
device_list = find_all_devices();
157+
158+
if (device_list.empty()) {
159+
std::cerr << "ydotool: " << argv[0] << ": error: no event device found in /dev/input/"
160+
<< std::endl;
161+
return 2;
162+
}
87163
}
164+
165+
signal(SIGINT, stop_handler);
166+
167+
if (duration)
168+
std::thread([duration]() {
169+
std::cerr << "Duration was set to "
170+
<< duration << " milliseconds.\n";
171+
usleep(duration * 1000);
172+
kill(getpid(), SIGINT);
173+
}).detach();
174+
175+
if (delay)
176+
usleep(delay * 1000);
177+
178+
do_record(device_list);
179+
} else if (mode == 2) {
180+
if (delay)
181+
usleep(delay * 1000);
182+
183+
do_replay();
184+
} else if (mode == 3) {
185+
do_display();
186+
}
187+
}
188+
189+
190+
void Recorder::do_replay() {
191+
struct stat statat;
192+
193+
fstat(fd_file, &statat);
194+
195+
if (statat.st_size < sizeof(file_header)+sizeof(data_chunk)) {
196+
fprintf(stderr, "File too small\n");
197+
abort();
198+
}
199+
200+
auto filedata = (uint8_t *)mmap(nullptr, statat.st_size, PROT_READ, MAP_SHARED, fd_file, 0);
201+
202+
assert(filedata);
203+
204+
auto file_hdr = (file_header *)filedata;
205+
auto file_end = filedata + statat.st_size;
206+
auto data_start = (filedata + sizeof(file_header));
207+
208+
auto cur_pos = data_start;
209+
210+
auto size_cur = file_end - data_start;
211+
212+
if (size_cur == file_hdr->size) {
213+
fprintf(stderr, "Size match\n");
214+
} else {
215+
fprintf(stderr, "Size mismatch: %zu != %zu\n", size_cur, file_hdr->size);
216+
abort();
88217
}
89218

90-
do_record(device_list);
219+
auto crc32_cur = Utils::crc32(data_start, size_cur);
220+
221+
if (crc32_cur == file_hdr->crc32) {
222+
fprintf(stderr, "CRC32 match\n");
223+
} else {
224+
fprintf(stderr, "CRC32 mismatch: %08x != %08x\n", crc32_cur, file_hdr->crc32);
225+
abort();
226+
}
91227

228+
std::cerr << "Started replaying\n";
229+
230+
while (cur_pos < file_end) {
231+
auto dat = (data_chunk *)cur_pos;
232+
usleep(dat->delay[0] * 1000000 + dat->delay[1] / 1000);
233+
234+
uInputContext->Emit(dat->ev_type, dat->ev_code, dat->ev_value);
235+
236+
cur_pos += sizeof(data_chunk);
237+
}
238+
}
239+
240+
void Recorder::do_display() {
241+
struct stat statat;
242+
243+
fstat(fd_file, &statat);
244+
245+
if (statat.st_size < sizeof(file_header)+sizeof(data_chunk)) {
246+
fprintf(stderr, "File too small\n");
247+
abort();
248+
}
249+
250+
auto filedata = (uint8_t *)mmap(nullptr, statat.st_size, PROT_READ, MAP_SHARED, fd_file, 0);
251+
252+
assert(filedata);
253+
254+
auto file_hdr = (file_header *)filedata;
255+
auto file_end = filedata + statat.st_size;
256+
auto data_start = (filedata + sizeof(file_header));
257+
258+
auto cur_pos = data_start;
259+
260+
auto size_cur = file_end - data_start;
261+
auto crc32_cur = Utils::crc32(data_start, size_cur);
262+
263+
264+
printf("CRC32: 0x%08x / 0x%08x\n", crc32_cur, file_hdr->crc32);
265+
printf("Data length: %zu / %zu (%zu events)\n", file_hdr->size, size_cur, file_hdr->size / sizeof(data_chunk));
266+
puts("============================================");
267+
268+
269+
while (cur_pos < file_end) {
270+
auto dat = (data_chunk *)cur_pos;
271+
printf("Offset: 0x%lx\n", (uint8_t *)(dat)-filedata);
272+
printf("Delay: %" PRIu64 ".%09" PRIu64 " second\n", dat->delay[0], dat->delay[1]);
273+
printf("Event: %u, %u, %u\n", dat->ev_type, dat->ev_code, dat->ev_value);
274+
puts("-");
275+
cur_pos += sizeof(data_chunk);
276+
}
92277
}
93278

94279
void Recorder::do_record(const std::vector<std::string> &__devices) {
@@ -123,25 +308,28 @@ void Recorder::do_record(const std::vector<std::string> &__devices) {
123308

124309
for (int i=0; i<rc; i++) {
125310
auto &it = events[i];
126-
127311
auto eev = (evdevPlus::EventDevice *)it.data.ptr;
128-
129312
auto buf = eev->Read();
130313

314+
data_chunk dat;
315+
131316
clock_gettime(CLOCK_MONOTONIC, &tm_now2);
132317

133318
Utils::timespec_diff(&tm_now, &tm_now2, &tm_diff);
134319

135320
tm_now = tm_now2;
136321

137322
uint64_t time_hdr[2];
138-
time_hdr[0] = tm_diff.tv_sec;
139-
time_hdr[1] = tm_diff.tv_nsec;
323+
dat.delay[0] = tm_diff.tv_sec;
324+
dat.delay[1] = tm_diff.tv_nsec;
325+
326+
dat.ev_type = buf.Type;
327+
dat.ev_code = buf.Code;
328+
dat.ev_value = buf.Value;
140329

141-
write(fd_file, time_hdr, 16);
142-
write(fd_file, &buf.event, sizeof(input_event));
330+
record_buffer.insert(record_buffer.end(), (uint8_t *)&dat, (uint8_t *)&dat+sizeof(data_chunk));
143331

144-
write(STDERR_FILENO, ".", 1);
332+
// write(STDERR_FILENO, ".", 1);
145333
}
146334
}
147335

@@ -167,3 +355,7 @@ std::vector<std::string> Recorder::find_all_devices() {
167355
return ret;
168356
}
169357

358+
359+
360+
361+

Tools/Recorder/Recorder.hpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,30 @@ namespace ydotool {
1919
struct file_header {
2020
char magic[4];
2121
uint32_t crc32;
22+
uint64_t size;
2223
uint64_t feature_mask;
2324
} __attribute__((__packed__));
2425

26+
struct data_chunk {
27+
uint64_t delay[2];
28+
uint16_t ev_type;
29+
uint16_t ev_code;
30+
int32_t ev_value;
31+
} __attribute__((__packed__));
32+
2533
private:
2634
int fd_epoll = -1;
27-
int fd_file = -1;
35+
// int fd_file = -1;
2836

2937
public:
3038
const char *Name() override;
3139

3240
void do_record(const std::vector<std::string> &__devices);
3341

42+
void do_replay();
43+
44+
void do_display();
45+
3446
std::vector<std::string> find_all_devices();
3547

3648
int Exec(int argc, const char **argv) override;

0 commit comments

Comments
 (0)