-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtty_output.cpp
142 lines (112 loc) · 4.17 KB
/
tty_output.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
#include "output.h"
#include "frame.h"
#include "logger.h"
#include "image.h"
#include "time.h"
#include <cassert>
#include <cstdio>
#include <cstring>
#include <chrono>
#include <vector>
#define BLOCK_CHARACTER {0xE2,0x96,0x88}
#define HALFBLOCK_TOP_CHARACTER {0xE2, 0x96, 0x80}
#define HALFBLOCK_BOTTOM_CHARACTER {0xE2, 0x96, 0x84}
void frame_data_to_string(uint8_t* top, uint8_t* bottom, unsigned width, std::vector<char>& outString) {
const uint8_t* end = top + width;
while(top < end) {
// Cut out any alpha
// Get rid of dark greys
if(GRAY_TO_MONOCHROME(*top++)) {
if(GRAY_TO_MONOCHROME(*bottom++)) {
const unsigned char block[3] = BLOCK_CHARACTER;
outString.resize(outString.size() + 3);
memcpy(outString.data() + outString.size() - 3, block, 3);
} else {
const unsigned char block[3] = HALFBLOCK_TOP_CHARACTER;
outString.resize(outString.size() + 3);
memcpy(outString.data() + outString.size() - 3, block, 3);
}
} else if(GRAY_TO_MONOCHROME(*bottom++)) {
const unsigned char block[3] = HALFBLOCK_BOTTOM_CHARACTER;
outString.resize(outString.size() + 3);
memcpy(outString.data() + outString.size() - 3, block, 3);
} else {
outString.push_back(' ');
}
// Should probably add some sort of command line switch for this,
// supports grey in the terminal.
// Very slow and flickery tho
#if 0
uint8_t t = (*top++) / 11;
uint8_t b = (*bottom++) / 11;
std::string c = fmt::format("\033[38;5;{}m\033[48;5;{}m", 232 + t, 232 + b);
outString.insert(outString.end(), c.begin(), c.end());
const unsigned char block[3] = HALFBLOCK_TOP_CHARACTER;
outString.resize(outString.size() + 3);
memcpy(outString.data() + outString.size() - 3, block, 3);
#endif
}
outString.push_back(0);
}
void print_frame_data(std::vector<std::vector<char>>& rows) {
puts("\033c");
for(std::vector<char> r : rows) {
puts(r.data());
}
}
TTYOutput::TTYOutput(int width, int height)
: Output(width, height) {
m_out = stdout;
}
TTYOutput::~TTYOutput() {
if(m_currentFrame)
free_frame(m_currentFrame);
if(m_lastFrame)
free_frame(m_lastFrame);
if(m_nextFrame)
free_frame(m_nextFrame);
}
void TTYOutput::run() {
std::unique_lock lock{m_frameLock};
// TODO: Should probably figure out a clean way to use condition_variable
// so this function doesn't hog the lock.
// Shouldn't be an issue tho since the kernel should use FIFO to ensure
// each thread gets time on the lock
if(!m_nextFrame) {
return;
}
m_currentFrame = m_nextFrame;
m_nextFrame = nullptr;
lock.unlock();
m_frameCondition.notify_all();
std::vector<std::vector<char>> strings;
for(unsigned i = 0; i < m_height; i += 2) {
std::vector<char> string = {};
frame_data_to_string(m_currentFrame->data + i * m_width, m_currentFrame->data + (i + 1) * m_width, m_width, string);
assert(string.back() == 0);
strings.push_back(std::move(string));
}
lock.lock();
// TODO: In theory it is possible for the decoder to not ask for a new
// frame in time and hence m_lastFrame will still be valid.
// For now just crash in this case.
assert(!m_lastFrame);
int currentTs = m_currentFrame->usTimestamp;
m_lastFrame = m_currentFrame;
m_currentFrame = nullptr;
// We are done with the frame data
lock.unlock();
m_frameCondition.notify_all();
// Sleep until it is time to draw the next frame
if(m_lastFrameTimestamp >= 0) {
long sleepTime = (currentTs - m_lastFrameTimestamp)
- std::chrono::duration_cast<std::chrono::microseconds>(m_lastFrameDrawn - std::chrono::steady_clock::now()).count();
// Don't sleep for any more than a second
if(sleepTime > 0 && sleepTime < 1000000) {
usleep(sleepTime);
}
}
print_frame_data(strings);
m_lastFrameTimestamp = currentTs;
m_lastFrameDrawn = std::chrono::steady_clock::now();
}