44
55#include " Recorder.hpp"
66
7- using namespace ydotool ::Tools;
7+ using namespace ydotool ;
8+ using namespace Tools ;
89
910const char ydotool_tool_name[] = " recorder" ;
1011
1112
1213static 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
1826const 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
94279void 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+
0 commit comments