Commit | Line | Data |
---|---|---|
95520199 PA |
1 | /* Copyright 2020 Purdea Andrei |
2 | * | |
3 | * This program is free software: you can redistribute it and/or modify | |
4 | * it under the terms of the GNU General Public License as published by | |
5 | * the Free Software Foundation, either version 2 of the License, or | |
6 | * (at your option) any later version. | |
7 | * | |
8 | * This program is distributed in the hope that it will be useful, | |
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 | * GNU General Public License for more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License | |
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
15 | */ | |
16 | ||
0dfaef40 PA |
17 | #include "signal_level.h" |
18 | #include "ui_signal_level.h" | |
19 | #include <QGraphicsView> | |
20 | #include <QPainter> | |
21 | #include <QFile> | |
b57f1e31 PA |
22 | #include <QMenu> |
23 | #include <QAction> | |
0dfaef40 PA |
24 | #include <QMessageBox> |
25 | #include <iostream> | |
805a7dce | 26 | #include <algorithm> |
0dfaef40 PA |
27 | #include "kbd_defs.h" |
28 | ||
29 | SignalLevelMonitorWindow::SignalLevelMonitorWindow(HidThread &thread, QWidget *parent) : | |
30 | QDialog(parent), | |
31 | ui(new Ui::SignalLevelMonitorWindow), | |
32 | thread(thread) | |
33 | { | |
34 | ui->setupUi(this); | |
35 | keyboard = nullptr; | |
805a7dce | 36 | current_layout = nullptr; |
0dfaef40 PA |
37 | connect(&thread, &HidThread::keyboardName, this, &SignalLevelMonitorWindow::on_keyboardName); |
38 | connect(&thread, &HidThread::reportMonitorError, this, &SignalLevelMonitorWindow::on_reportMonitorError); | |
39 | connect(&thread, &HidThread::reportSignalLevel, this, &SignalLevelMonitorWindow::on_signallevel); | |
b57f1e31 PA |
40 | this->setContextMenuPolicy(Qt::CustomContextMenu); |
41 | connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), | |
42 | this, SLOT(ShowContextMenu(const QPoint &))); | |
9b0419fb PA |
43 | HORIZONTAL_MARGIN = 10; |
44 | VERTICAL_MARGIN = 10; | |
b57f1e31 PA |
45 | } |
46 | ||
47 | void SignalLevelMonitorWindow::ShowContextMenu(const QPoint &pos) | |
48 | { | |
49 | QMenu contextMenu(tr("Context menu"), this); | |
50 | QAction action1("Close", this); | |
51 | connect(&action1, SIGNAL(triggered()), this, SLOT(close())); | |
52 | contextMenu.addAction(&action1); | |
53 | contextMenu.exec(mapToGlobal(pos)); | |
0dfaef40 PA |
54 | } |
55 | ||
56 | SignalLevelMonitorWindow::~SignalLevelMonitorWindow() | |
57 | { | |
58 | delete ui; | |
59 | } | |
60 | ||
0c5817c9 | 61 | void SignalLevelMonitorWindow::setMinimumSizeUnits(unsigned int width_units_times_8, unsigned int height_units_times_8) |
805a7dce | 62 | { |
9b0419fb PA |
63 | HORIZONTAL_MARGIN = ui->label_keyboardname->pos().x(); |
64 | VERTICAL_MARGIN = ui->label_keyboardname->pos().x(); | |
24e57c23 | 65 | this->setMinimumSize(static_cast<int>(width_units_times_8 * MIN_HORIZONTAL_SCALE / 8 + 2 * HORIZONTAL_MARGIN), |
805a7dce | 66 | ui->last_label->geometry().y() + ui->last_label->geometry().height() + |
0c5817c9 | 67 | static_cast<int>(height_units_times_8 * MIN_VERTICAL_SCALE / 8 + 2 * VERTICAL_MARGIN)); |
805a7dce PA |
68 | } |
69 | ||
0dfaef40 PA |
70 | void SignalLevelMonitorWindow::loadLayout(QString name) |
71 | { | |
72 | int i; | |
0dfaef40 PA |
73 | ui->layoutSel->clear(); |
74 | for (i=0;i<n_keyboards;i++) | |
75 | { | |
76 | if (name.compare(QString(keyboards[i].kbd_name))==0) | |
77 | { | |
78 | keyboard = &keyboards[i]; | |
6fd6e42a PA |
79 | this->signal_level = std::vector<std::vector<uint16_t>>(static_cast<unsigned long>(keyboard->rows), std::vector<uint16_t>(static_cast<unsigned long>(keyboard->cols), 0xFFFFU)); |
80 | this->max_signal_level = std::vector<std::vector<uint16_t>>(static_cast<unsigned long>(keyboard->rows), std::vector<uint16_t>(static_cast<unsigned long>(keyboard->cols), 0xFFFFU)); | |
81 | this->min_signal_level = std::vector<std::vector<uint16_t>>(static_cast<unsigned long>(keyboard->rows), std::vector<uint16_t>(static_cast<unsigned long>(keyboard->cols), 0xFFFFU)); | |
0dfaef40 PA |
82 | int j; |
83 | for (j=0;j<keyboard->n_layouts;j++) | |
84 | { | |
85 | ui->layoutSel->addItem(QString(keyboard->layouts[j].lay_name)); | |
86 | } | |
87 | break; | |
88 | } | |
89 | } | |
805a7dce | 90 | ui->layoutSel->addItem(QString("Matrix (without showing skipped columns/rows)")); |
0dfaef40 PA |
91 | QString name_simp = name; |
92 | if (name.startsWith("keyboards/")) name_simp = name.mid(strlen("keyboards/")); | |
93 | int lastslash = name_simp.lastIndexOf("/"); | |
94 | if (lastslash > 0) name_simp = name_simp.left(lastslash); | |
95 | ui->label_keyboardname->setText(QString("Keyboard: ") + name_simp); | |
96 | ui->layoutSel->setEnabled(true); | |
ff765ccb PA |
97 | if (!keyboard) |
98 | { | |
99 | on_reportMonitorError("Unknown keyboard (you may need to update the util version)!"); | |
100 | this->close(); | |
101 | return; | |
102 | } | |
0c5817c9 PA |
103 | keyboard_width_uis_times_8 = 0; |
104 | keyboard_height_uis_times_8 = 0; | |
0dfaef40 PA |
105 | for (i=0;i<keyboard->n_layouts;i++) |
106 | { | |
107 | const struct lay_def *layout = &keyboard->layouts[i]; | |
108 | int j; | |
109 | for (j=0;j<layout->n_keys;j++) | |
110 | { | |
0c5817c9 PA |
111 | unsigned int w8 = static_cast<unsigned int>((layout->keys[j].x + layout->keys[j].w)*8 + 0.5); |
112 | unsigned int h8 = static_cast<unsigned int>((layout->keys[j].y + layout->keys[j].h)*8 + 0.5); | |
113 | if (w8 > keyboard_width_uis_times_8) keyboard_width_uis_times_8 = w8; | |
114 | if (h8 > keyboard_height_uis_times_8) keyboard_height_uis_times_8 = h8; | |
0dfaef40 PA |
115 | } |
116 | } | |
0c5817c9 | 117 | setMinimumSizeUnits(keyboard_width_uis_times_8, keyboard_height_uis_times_8); |
d82f09a9 PA |
118 | } |
119 | ||
120 | QColor SignalLevelMonitorWindow::getColor(uint16_t value, uint16_t mins, uint16_t maxs) | |
121 | { | |
122 | double scale = (value - mins + 0.0) / (maxs - mins); | |
123 | uint8_t red = static_cast<uint8_t>(0xff * (1 - scale) + 0xc0 * scale); | |
124 | uint8_t green = static_cast<uint8_t>(0xff * scale + 0xc0 * (1 - scale)); | |
125 | uint8_t blue = 0xc0; | |
126 | return QColor(red, green, blue); | |
0dfaef40 PA |
127 | } |
128 | ||
805a7dce PA |
129 | void SignalLevelMonitorWindow::displaySquare(int x, int y, int w, int h, unsigned int col, unsigned int row, uint16_t mins, uint16_t maxs, QPainter &painter) |
130 | { | |
131 | painter.setPen(QPen(Qt::black)); | |
132 | painter.setBrush(Qt::NoBrush); | |
133 | QRectF rect(x, y, w, h); | |
134 | painter.drawRect(rect); | |
135 | if ((signal_level.at(row).at(col)!=0xffffu) && (mins!=maxs)) | |
136 | { | |
137 | painter.setPen(Qt::NoPen); | |
138 | QString maxstring = QString::number(max_signal_level.at(row).at(col)); | |
139 | QString currstring = QString::number(signal_level.at(row).at(col)); | |
140 | QString minstring = QString::number(min_signal_level.at(row).at(col)); | |
141 | painter.setBrush(QBrush(getColor(max_signal_level.at(row).at(col), mins, maxs))); | |
142 | QRectF recti_max(x+1, y+1, w-1, (h-1) / 3); | |
143 | painter.drawRect(recti_max); | |
144 | painter.setBrush(QBrush(getColor(signal_level.at(row).at(col), mins, maxs))); | |
145 | QRectF recti_curr(x+1, y+1 + (h-1) / 3, w-1, (h-1) / 3); | |
146 | painter.drawRect(recti_curr); | |
147 | painter.setBrush(QBrush(getColor(min_signal_level.at(row).at(col), mins, maxs))); | |
148 | QRectF recti_min(x+1, y+1 + (h-1) / 3 *2, w-1, h-1 - ((h-1) / 3) * 2); | |
149 | painter.drawRect(recti_min); | |
150 | painter.setPen(Qt::black); | |
151 | painter.drawText(recti_max, Qt::AlignCenter, maxstring); | |
152 | painter.drawText(recti_curr, Qt::AlignCenter, currstring); | |
153 | painter.drawText(recti_min, Qt::AlignCenter, minstring); | |
154 | ||
155 | } else { | |
156 | painter.setPen(Qt::NoPen); | |
157 | painter.setBrush(QBrush(QColor("#FFFFFF"))); | |
158 | QRectF recti(x+1, y+1, w-1, h-1); | |
159 | painter.drawRect(recti); | |
160 | if (signal_level.at(row).at(col)!=0xffffu) | |
161 | { | |
162 | painter.setPen(Qt::black); | |
163 | QString currstring = QString::number(signal_level.at(row).at(col)); | |
164 | painter.drawText(recti, Qt::AlignCenter, currstring); | |
165 | } | |
166 | } | |
167 | } | |
168 | ||
169 | void SignalLevelMonitorWindow::updateCurrentLayout() | |
170 | { | |
171 | int i; | |
172 | this->current_layout = nullptr; | |
173 | for (i=0;i<keyboard->n_layouts;i++) | |
174 | { | |
175 | if (ui->layoutSel->currentText().compare(QString(keyboard->layouts[i].lay_name))==0) | |
176 | { | |
177 | this->current_layout = &keyboard->layouts[i]; | |
178 | return; | |
179 | } | |
180 | } | |
181 | } | |
182 | ||
0dfaef40 PA |
183 | void SignalLevelMonitorWindow::paintEvent(QPaintEvent *event) |
184 | { | |
185 | Q_UNUSED(event); | |
9b0419fb PA |
186 | HORIZONTAL_MARGIN = ui->label_keyboardname->pos().x(); |
187 | VERTICAL_MARGIN = ui->label_keyboardname->pos().x(); | |
0dfaef40 PA |
188 | if (!keyboard) return; |
189 | QPainter painter(this); | |
190 | int xadd = HORIZONTAL_MARGIN; | |
191 | int yadd = ui->last_label->geometry().y() + ui->last_label->geometry().height() + VERTICAL_MARGIN; | |
805a7dce PA |
192 | updateCurrentLayout(); |
193 | const struct lay_def *layout = this->current_layout; | |
194 | if (layout) | |
0dfaef40 | 195 | { |
0c5817c9 PA |
196 | double scale_x = (1. * this->width() - 1 - 2 * HORIZONTAL_MARGIN) * 8 / keyboard_width_uis_times_8; |
197 | double scale_y = (1. * this->height() - 1 - VERTICAL_MARGIN - yadd) * 8 / keyboard_height_uis_times_8; | |
805a7dce PA |
198 | int j; |
199 | uint16_t mins = 0xffffu, maxs = 0xffffu; | |
200 | for (j=0;j<layout->n_keys;j++) | |
0dfaef40 | 201 | { |
805a7dce PA |
202 | const unsigned int col = layout->keys[j].col; |
203 | const unsigned int row = layout->keys[j].row; | |
204 | if (signal_level.at(row).at(col)!=0xffffu) | |
205 | { | |
206 | if ((mins == 0xffffu) || (mins > min_signal_level.at(row).at(col))) | |
207 | { | |
208 | mins = min_signal_level.at(row).at(col); | |
209 | } | |
210 | if ((maxs == 0xffffu) || (maxs < max_signal_level.at(row).at(col))) | |
211 | { | |
212 | maxs = max_signal_level.at(row).at(col); | |
213 | } | |
214 | } | |
215 | } | |
216 | for (j=0;j<layout->n_keys;j++) | |
217 | { | |
218 | const unsigned int col = layout->keys[j].col; | |
219 | const unsigned int row = layout->keys[j].row; | |
220 | const int x_ = static_cast<int>(layout->keys[j].x * 8 + 0.5); | |
221 | const int y_ = static_cast<int>(layout->keys[j].y * 8 + 0.5); | |
222 | const int w_ = static_cast<int>(layout->keys[j].w * 8 + 0.5); | |
223 | const int h_ = static_cast<int>(layout->keys[j].h * 8 + 0.5); | |
224 | const int x__ = static_cast<int>(scale_x * x_ / 8 + 0.5); | |
225 | const int y__ = static_cast<int>(scale_y * y_ / 8 + 0.5); | |
226 | const int w = static_cast<int>(scale_x * (x_ + w_) / 8 + 0.5) - x__; | |
227 | const int h = static_cast<int>(scale_y * (y_ + h_) / 8 + 0.5) - y__; | |
228 | const int x = x__ + xadd; | |
229 | const int y = y__ + yadd; | |
230 | displaySquare(x, y, w, h, col, row, mins, maxs, painter); | |
231 | } | |
232 | } else { | |
233 | unsigned int col, row; | |
234 | double scale_x = (1. * this->width() - 2 * HORIZONTAL_MARGIN) / keyboard->cols; | |
235 | double scale_y = (1. * this->height() - VERTICAL_MARGIN - yadd) / keyboard->rows; | |
236 | uint16_t mins = 0xffffu, maxs = 0xffffu; | |
237 | for (row = 0; row < keyboard->rows; row++) | |
238 | { | |
239 | for (col = 0; col < keyboard->cols; col++) | |
0dfaef40 | 240 | { |
980b3e50 | 241 | if (signal_level.at(row).at(col)!=0xffffu) |
0dfaef40 | 242 | { |
980b3e50 | 243 | if ((mins == 0xffffu) || (mins > min_signal_level.at(row).at(col))) |
0dfaef40 | 244 | { |
980b3e50 | 245 | mins = min_signal_level.at(row).at(col); |
0dfaef40 | 246 | } |
980b3e50 | 247 | if ((maxs == 0xffffu) || (maxs < max_signal_level.at(row).at(col))) |
0dfaef40 | 248 | { |
980b3e50 | 249 | maxs = max_signal_level.at(row).at(col); |
0dfaef40 | 250 | } |
0dfaef40 PA |
251 | } |
252 | } | |
805a7dce PA |
253 | } |
254 | for (row = 0; row < keyboard->rows; row++) | |
255 | { | |
256 | for (col = 0; col < keyboard->cols; col++) | |
0dfaef40 | 257 | { |
805a7dce PA |
258 | const int x__ = static_cast<int>(scale_x * col + 0.5); |
259 | const int y__ = static_cast<int>(scale_y * row + 0.5); | |
260 | const int w = static_cast<int>(scale_x * (col + 1) + 0.5) - x__; | |
261 | const int h = static_cast<int>(scale_y * (row + 1) + 0.5) - y__; | |
262 | const int x = x__ + xadd; | |
263 | const int y = y__ + yadd; | |
264 | displaySquare(x, y, w, h, col, row, mins, maxs, painter); | |
0dfaef40 | 265 | } |
0dfaef40 PA |
266 | } |
267 | } | |
268 | } | |
269 | ||
270 | void SignalLevelMonitorWindow::on_layoutSel_activated(const QString &arg1) | |
271 | { | |
272 | Q_UNUSED(arg1); | |
805a7dce PA |
273 | updateCurrentLayout(); |
274 | if (this->current_layout) | |
275 | { | |
0c5817c9 | 276 | setMinimumSizeUnits(this->keyboard_width_uis_times_8, this->keyboard_height_uis_times_8); |
805a7dce | 277 | } else { |
0c5817c9 | 278 | setMinimumSizeUnits(this->keyboard->cols * 8, this->keyboard->rows * 8); |
805a7dce | 279 | } |
0dfaef40 PA |
280 | this->repaint(); |
281 | } | |
282 | ||
283 | void SignalLevelMonitorWindow::on_keyboardName(std::string name) | |
284 | { | |
285 | loadLayout(QString::fromStdString(name)); | |
286 | this->repaint(); | |
287 | } | |
288 | ||
289 | void SignalLevelMonitorWindow::on_signallevel(std::vector<uint16_t> data) | |
290 | { | |
ff765ccb PA |
291 | if (!keyboard) |
292 | { | |
293 | return; | |
294 | } | |
980b3e50 PA |
295 | uint16_t col = data.at(0); |
296 | uint16_t row = data.at(1); | |
0dfaef40 PA |
297 | unsigned int i; |
298 | bool different = false; | |
299 | for (i=2;i<data.size();i++) | |
300 | { | |
980b3e50 | 301 | if (signal_level.at(row).at(col) != data.at(i)) |
0dfaef40 PA |
302 | { |
303 | different = true; | |
304 | } | |
980b3e50 PA |
305 | signal_level.at(row).at(col) = data.at(i); |
306 | if ((min_signal_level.at(row).at(col)==0xffffu) || (data.at(i) < min_signal_level.at(row).at(col))) | |
d82f09a9 | 307 | { |
980b3e50 | 308 | min_signal_level.at(row).at(col) = data.at(i); |
d82f09a9 | 309 | } |
980b3e50 | 310 | if ((max_signal_level.at(row).at(col)==0xffffu) || (data.at(i) > max_signal_level.at(row).at(col))) |
d82f09a9 | 311 | { |
980b3e50 | 312 | max_signal_level.at(row).at(col) = data.at(i); |
d82f09a9 | 313 | } |
0dfaef40 PA |
314 | col += 1; |
315 | if (col >= keyboard->cols) | |
316 | { | |
317 | col -= keyboard->cols; | |
318 | row += 1; | |
319 | } | |
320 | if (row >= keyboard->rows) | |
321 | { | |
322 | break; | |
323 | } | |
324 | } | |
325 | if (different) | |
326 | this->repaint(); | |
327 | } | |
328 | ||
329 | void SignalLevelMonitorWindow::on_reportMonitorError(std::string error_message) | |
330 | { | |
331 | QMessageBox::critical(this, "Error", error_message.c_str()); | |
332 | } | |
333 | ||
334 | void SignalLevelMonitorWindow::on_SignalLevelMonitorWindow_finished(int result) | |
335 | { | |
336 | Q_UNUSED(result); | |
337 | thread.closeMonitoredDevice(); | |
338 | } |