-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathmain.cpp
190 lines (168 loc) · 5.85 KB
/
main.cpp
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#include <QApplication>
#include <QDateTime>
#include <QDesktopWidget>
#include <QImage>
#include <QPainter>
#include <QPixmap>
#include <QPoint>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <fcntl.h>
#include <stdint.h>
#include <time.h>
#include <unistd.h>
#include "CapsLockState.h"
#include "CaptureConfig.h"
#include "Cursor.h"
#include "MurmurHash3.h"
volatile bool g_finish = false;
static void sleepMS(int ms)
{
timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
nanosleep(&ts, NULL);
}
static void usage(const char *argv0)
{
printf("Usage: %s --rect X Y W H --output OUTFILE\n", argv0);
}
static bool stringEndsWith(const std::string &str, const std::string &suffix)
{
if (suffix.size() > str.size())
return false;
return !str.compare(str.size() - suffix.size(), suffix.size(), suffix);
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
screencast::CaptureConfig config;
std::string output;
std::string baseName;
for (int i = 1; i < argc; ) {
const char *arg = argv[i];
if (!strcmp(arg, "--rect") && (i + 4 < argc)) {
config.captureX = atoi(argv[i + 1]);
config.captureY = atoi(argv[i + 2]);
config.captureWidth = atoi(argv[i + 3]);
config.captureHeight = atoi(argv[i + 4]);
i += 5;
} else if (!strcmp(arg, "--help")) {
usage(argv[0]);
exit(0);
} else if (!strcmp(arg, "--output") && (i + 1 < argc)) {
output = argv[i + 1];
if (!stringEndsWith(output, ".js")) {
fprintf(stderr, "Output file must end with .js extension.\n");
exit(1);
}
baseName = output.substr(0, output.size() - 3);
i += 2;
} else {
fprintf(stderr, "error: Unrecognized argument: %s\n", argv[i]);
usage(argv[0]);
exit(1);
}
}
if (config.captureWidth == 0 || config.captureHeight == 0) {
fprintf(stderr,
"error: A capture rectangle must be specified with --rect.\n");
usage(argv[0]);
exit(1);
}
if (output.empty()) {
fprintf(stderr,
"error: An output file must be specified with --output.\n");
usage(argv[0]);
exit(1);
}
FILE *fp = fopen(output.c_str(), "wb");
if (!fp) {
fprintf(stderr, "error: Could not open %s\n", output.c_str());
exit(1);
}
setvbuf(fp, NULL, _IOLBF, 0);
fprintf(fp, "%s = {\n", baseName.c_str());
fprintf(fp, "\"width\": %d,\n", config.captureWidth);
fprintf(fp, "\"height\": %d,\n", config.captureHeight);
fprintf(fp, "\"steps\": [\n");
printf("Starting in 2 seconds.\n");
sleep(2);
printf("Now recording -- press ENTER to stop.\n");
QImage previousImage;
screencast::Cursor previousCursor;
bool previousFrozen = false;
QPoint frozenMousePosition;
{
int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(STDIN_FILENO, F_SETFL, flags);
}
const int FPS = 60;
const int maxDelayMS = 250;
bool firstFrame = true;
int delay = 0;
char buf;
while (read(STDIN_FILENO, &buf, 1) == -1 && errno == EAGAIN) {
// Sleep so we poll the screen regularly.
sleepMS(1000 / FPS);
if (!firstFrame)
delay = std::min(maxDelayMS, delay + 1000 / FPS);
// Check for CAPS LOCK status. If the key is pressed, "freeze" the
// recording.
const bool frozen = screencast::capsLockEnabled();
if (previousFrozen && frozen)
continue;
if (!previousFrozen && frozen) {
// Newly frozen. Save mouse position;
printf("FROZEN\n");
fprintf(fp, "//FROZEN\n");
frozenMousePosition = QCursor::pos();
previousFrozen = frozen;
continue;
}
if (previousFrozen && !frozen) {
// Newly unfrozen. Warp to the frozen mouse position. Pause for
// another frame to give programs a chance to update the mouse
// cursor image (and hover, etc).
printf("UNFROZEN\n");
fprintf(fp, "//UNFROZEN\n");
QCursor::setPos(frozenMousePosition);
previousFrozen = frozen;
// XXX: I don't know why, but constructing this Cursor is necessary
// to force the setPos call above to take effect *before* pausing.
screencast::Cursor flushYetAnotherQtCache(config);
continue;
}
screencast::Cursor cursor(config);
QDesktopWidget *desktop = QApplication::desktop();
QPixmap screenshotPixmap = QPixmap::grabWindow(
desktop->winId(),
config.captureX, config.captureY,
config.captureWidth, config.captureHeight);
QImage screenshot = screenshotPixmap.toImage();
if (screenshot != previousImage) {
QString sampleName = QString("sample_%0.png").arg(QDateTime::currentMSecsSinceEpoch());
previousImage = screenshot;
screenshot.save(sampleName);
fprintf(fp, "[%d,\"screen\",\"%s\"],\n", delay, sampleName.toStdString().c_str());
delay = 0;
firstFrame = false;
}
if (cursor.imageID() != previousCursor.imageID()) {
fprintf(fp, "[%d,\"cursor\",\"cursor_%u.png\",%d,%d],\n",
delay, cursor.imageID(),
cursor.position().x(), cursor.position().y());
delay = 0;
} else if (cursor.position() != previousCursor.position()) {
fprintf(fp, "[%d,\"cpos\",%d,%d],\n",
delay,
cursor.position().x(), cursor.position().y());
delay = 0;
}
previousCursor = cursor;
}
fprintf(fp, "]};\n");
fclose(fp);
return 0;
}