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 | ||
1c506c79 PA |
17 | #include "monitorwindow.h" |
18 | #include "ui_monitorwindow.h" | |
19 | #include <QGraphicsView> | |
20 | #include <QPainter> | |
21 | #include <QFile> | |
b57f1e31 PA |
22 | #include <QMenu> |
23 | #include <QAction> | |
372aff5f | 24 | #include <QMessageBox> |
1c506c79 | 25 | #include <iostream> |
805a7dce | 26 | #include <algorithm> |
372aff5f | 27 | #include "kbd_defs.h" |
1c506c79 | 28 | |
372aff5f | 29 | MonitorWindow::MonitorWindow(HidThread &thread, QWidget *parent) : |
1c506c79 | 30 | QDialog(parent), |
372aff5f PA |
31 | ui(new Ui::MonitorWindow), |
32 | thread(thread) | |
1c506c79 PA |
33 | { |
34 | ui->setupUi(this); | |
372aff5f | 35 | keyboard = nullptr; |
805a7dce | 36 | current_layout = nullptr; |
372aff5f PA |
37 | connect(&thread, &HidThread::keyboardName, this, &MonitorWindow::on_keyboardName); |
38 | connect(&thread, &HidThread::reportMonitorError, this, &MonitorWindow::on_reportMonitorError); | |
39 | connect(&thread, &HidThread::thresholds, this, &MonitorWindow::on_thresholds); | |
40 | connect(&thread, &HidThread::keystate, this, &MonitorWindow::on_keystate); | |
b57f1e31 PA |
41 | this->setContextMenuPolicy(Qt::CustomContextMenu); |
42 | connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), | |
43 | this, SLOT(ShowContextMenu(const QPoint &))); | |
9b0419fb PA |
44 | HORIZONTAL_MARGIN = 10; |
45 | VERTICAL_MARGIN = 10; | |
b57f1e31 PA |
46 | } |
47 | ||
48 | void MonitorWindow::ShowContextMenu(const QPoint &pos) | |
49 | { | |
50 | QMenu contextMenu(tr("Context menu"), this); | |
51 | QAction action1("Close", this); | |
52 | connect(&action1, SIGNAL(triggered()), this, SLOT(close())); | |
53 | contextMenu.addAction(&action1); | |
54 | contextMenu.exec(mapToGlobal(pos)); | |
1c506c79 PA |
55 | } |
56 | ||
57 | MonitorWindow::~MonitorWindow() | |
58 | { | |
59 | delete ui; | |
60 | } | |
61 | ||
0c5817c9 | 62 | void MonitorWindow::setMinimumSizeUnits(unsigned int width_units_times_8, unsigned int height_units_times_8) |
805a7dce | 63 | { |
9b0419fb PA |
64 | HORIZONTAL_MARGIN = ui->label_keyboardname->pos().x(); |
65 | VERTICAL_MARGIN = ui->label_keyboardname->pos().x(); | |
24e57c23 | 66 | this->setMinimumSize(static_cast<int>(width_units_times_8 * MIN_HORIZONTAL_SCALE / 8 + 2 * HORIZONTAL_MARGIN), |
805a7dce | 67 | ui->last_label->geometry().y() + ui->last_label->geometry().height() + |
0c5817c9 | 68 | static_cast<int>(height_units_times_8 * MIN_VERTICAL_SCALE / 8 + 2 * VERTICAL_MARGIN)); |
805a7dce PA |
69 | } |
70 | ||
372aff5f | 71 | void MonitorWindow::loadLayout(QString name) |
1c506c79 | 72 | { |
372aff5f | 73 | int i; |
1c506c79 | 74 | ui->layoutSel->clear(); |
372aff5f | 75 | for (i=0;i<n_keyboards;i++) |
1c506c79 | 76 | { |
372aff5f PA |
77 | if (name.compare(QString(keyboards[i].kbd_name))==0) |
78 | { | |
79 | keyboard = &keyboards[i]; | |
6bdc5464 | 80 | is_was_key_pressed = std::vector<std::vector<int>>(static_cast<unsigned long>(keyboard->rows), std::vector<int>(static_cast<unsigned long>(keyboard->cols), 0)); |
372aff5f PA |
81 | int j; |
82 | for (j=0;j<keyboard->n_layouts;j++) | |
83 | { | |
84 | ui->layoutSel->addItem(QString(keyboard->layouts[j].lay_name)); | |
85 | } | |
86 | break; | |
87 | } | |
1c506c79 | 88 | } |
805a7dce | 89 | ui->layoutSel->addItem(QString("Matrix (without showing skipped columns/rows)")); |
372aff5f PA |
90 | QString name_simp = name; |
91 | if (name.startsWith("keyboards/")) name_simp = name.mid(strlen("keyboards/")); | |
92 | int lastslash = name_simp.lastIndexOf("/"); | |
93 | if (lastslash > 0) name_simp = name_simp.left(lastslash); | |
94 | ui->label_keyboardname->setText(QString("Keyboard: ") + name_simp); | |
95 | ui->layoutSel->setEnabled(true); | |
805a7dce PA |
96 | if (!keyboard) |
97 | { | |
ff765ccb PA |
98 | on_reportMonitorError("Unknown keyboard (you may need to update the util version)!"); |
99 | this->close(); | |
100 | return; | |
101 | } | |
0c5817c9 PA |
102 | keyboard_width_uis_times_8 = 0; |
103 | keyboard_height_uis_times_8 = 0; | |
372aff5f PA |
104 | for (i=0;i<keyboard->n_layouts;i++) |
105 | { | |
106 | const struct lay_def *layout = &keyboard->layouts[i]; | |
107 | int j; | |
108 | for (j=0;j<layout->n_keys;j++) | |
109 | { | |
0c5817c9 PA |
110 | const unsigned int w8 = static_cast<unsigned int>((layout->keys[j].x + layout->keys[j].w)*8 + 0.5); |
111 | const unsigned int h8 = static_cast<unsigned int>((layout->keys[j].y + layout->keys[j].h)*8 + 0.5); | |
112 | if (w8 > keyboard_width_uis_times_8) keyboard_width_uis_times_8 = w8; | |
113 | if (h8 > keyboard_height_uis_times_8) keyboard_height_uis_times_8 = h8; | |
372aff5f PA |
114 | } |
115 | } | |
0c5817c9 | 116 | setMinimumSizeUnits(keyboard_width_uis_times_8, keyboard_height_uis_times_8); |
805a7dce PA |
117 | } |
118 | ||
119 | void MonitorWindow::displaySquare(int x, int y, int w, int h, unsigned int col, unsigned int row, QPainter &painter) | |
120 | { | |
121 | QPen textColor(Qt::black); | |
122 | painter.setPen(QPen(Qt::black)); | |
123 | switch (is_was_key_pressed.at(row).at(col)) | |
124 | { | |
125 | case 1: | |
126 | case 3: | |
127 | textColor = QPen(Qt::white); | |
128 | painter.setBrush(QBrush(QColor("#008000"))); | |
129 | break; | |
130 | case 2: | |
131 | painter.setBrush(QBrush(QColor("#00FF00"))); | |
132 | break; | |
133 | default: | |
134 | painter.setBrush(QBrush(QColor("#FFFFFF"))); | |
135 | break; | |
136 | } | |
137 | QRectF rect(x, y, w, h); | |
138 | painter.drawRect(rect); | |
139 | if (thresholds.size()) | |
140 | { | |
141 | uint16_t threshold = get_threshold(col, row); | |
142 | painter.setPen(textColor); | |
143 | painter.drawText(rect, Qt::AlignCenter, QString::number(threshold)); | |
144 | } | |
145 | } | |
146 | ||
147 | void MonitorWindow::updateCurrentLayout() | |
148 | { | |
149 | int i; | |
150 | this->current_layout = nullptr; | |
151 | for (i=0;i<keyboard->n_layouts;i++) | |
152 | { | |
153 | if (ui->layoutSel->currentText().compare(QString(keyboard->layouts[i].lay_name))==0) | |
154 | { | |
155 | this->current_layout = &keyboard->layouts[i]; | |
156 | return; | |
157 | } | |
158 | } | |
1c506c79 PA |
159 | } |
160 | ||
161 | void MonitorWindow::paintEvent(QPaintEvent *event) | |
162 | { | |
9b0419fb PA |
163 | HORIZONTAL_MARGIN = ui->label_keyboardname->pos().x(); |
164 | VERTICAL_MARGIN = ui->label_keyboardname->pos().x(); | |
1c506c79 | 165 | Q_UNUSED(event); |
372aff5f | 166 | if (!keyboard) return; |
1c506c79 | 167 | QPainter painter(this); |
372aff5f PA |
168 | int xadd = HORIZONTAL_MARGIN; |
169 | int yadd = ui->last_label->geometry().y() + ui->last_label->geometry().height() + VERTICAL_MARGIN; | |
805a7dce PA |
170 | updateCurrentLayout(); |
171 | const struct lay_def *layout = this->current_layout; | |
172 | if (layout) | |
1c506c79 | 173 | { |
0c5817c9 PA |
174 | double scale_x = (1. * this->width() - 1 - 2 * HORIZONTAL_MARGIN) * 8 / keyboard_width_uis_times_8; |
175 | double scale_y = (1. * this->height() - 1 - VERTICAL_MARGIN - yadd) * 8 / keyboard_height_uis_times_8; | |
805a7dce PA |
176 | int j; |
177 | for (j=0;j<layout->n_keys;j++) | |
1c506c79 | 178 | { |
805a7dce PA |
179 | const unsigned int col = layout->keys[j].col; |
180 | const unsigned int row = layout->keys[j].row; | |
181 | const int x_ = static_cast<int>(layout->keys[j].x * 8 + 0.5); | |
182 | const int y_ = static_cast<int>(layout->keys[j].y * 8 + 0.5); | |
183 | const int w_ = static_cast<int>(layout->keys[j].w * 8 + 0.5); | |
184 | const int h_ = static_cast<int>(layout->keys[j].h * 8 + 0.5); | |
185 | const int x__ = static_cast<int>(scale_x * x_ / 8 + 0.5); | |
186 | const int y__ = static_cast<int>(scale_y * y_ / 8 + 0.5); | |
187 | const int w = static_cast<int>(scale_x * (x_ + w_) / 8 + 0.5) - x__; | |
188 | const int h = static_cast<int>(scale_y * (y_ + h_) / 8 + 0.5) - y__; | |
189 | const int x = x__ + xadd; | |
190 | const int y = y__ + yadd; | |
191 | displaySquare(x, y, w, h, col, row, painter); | |
192 | } | |
193 | } else { | |
194 | unsigned int col, row; | |
195 | double scale_x = (1. * this->width() - 2 * HORIZONTAL_MARGIN) / keyboard->cols; | |
196 | double scale_y = (1. * this->height() - VERTICAL_MARGIN - yadd) / keyboard->rows; | |
197 | for (row = 0; row < keyboard->rows; row++) | |
198 | { | |
199 | for (col = 0; col < keyboard->cols; col++) | |
372aff5f | 200 | { |
805a7dce PA |
201 | const int x__ = static_cast<int>(scale_x * col + 0.5); |
202 | const int y__ = static_cast<int>(scale_y * row + 0.5); | |
203 | const int w = static_cast<int>(scale_x * (col + 1) + 0.5) - x__; | |
204 | const int h = static_cast<int>(scale_y * (row + 1) + 0.5) - y__; | |
205 | const int x = x__ + xadd; | |
206 | const int y = y__ + yadd; | |
207 | displaySquare(x, y, w, h, col, row, painter); | |
372aff5f | 208 | } |
1c506c79 | 209 | } |
372aff5f PA |
210 | } |
211 | } | |
212 | ||
213 | void MonitorWindow::on_layoutSel_activated(const QString &arg1) | |
214 | { | |
215 | Q_UNUSED(arg1); | |
805a7dce PA |
216 | updateCurrentLayout(); |
217 | if (this->current_layout) | |
218 | { | |
0c5817c9 | 219 | setMinimumSizeUnits(this->keyboard_width_uis_times_8, this->keyboard_height_uis_times_8); |
805a7dce | 220 | } else { |
0c5817c9 | 221 | setMinimumSizeUnits(this->keyboard->cols * 8, this->keyboard->rows * 8); |
805a7dce | 222 | } |
372aff5f PA |
223 | this->repaint(); |
224 | } | |
225 | ||
226 | void MonitorWindow::on_keyboardName(std::string name) | |
227 | { | |
228 | loadLayout(QString::fromStdString(name)); | |
229 | this->repaint(); | |
230 | } | |
231 | ||
232 | void MonitorWindow::on_thresholds(std::vector<std::vector<uint8_t>> thresholds) | |
233 | { | |
234 | this->thresholds = thresholds; | |
235 | this->repaint(); | |
236 | } | |
237 | ||
238 | void MonitorWindow::on_keystate(std::vector<uint8_t> data) | |
239 | { | |
240 | unsigned int col, row; | |
241 | int different = 0; | |
242 | if (!keyboard) return; | |
c5ddd873 PA |
243 | unsigned int bytes_per_row = 1; |
244 | if (keyboard->cols > 16) | |
245 | { | |
246 | bytes_per_row = 4; | |
247 | } else if (keyboard->cols > 8) | |
248 | { | |
249 | bytes_per_row = 2; | |
250 | } | |
372aff5f PA |
251 | for (col=0; col < keyboard->cols; col++) |
252 | { | |
253 | for (row=0; row < keyboard->rows; row++) | |
1c506c79 | 254 | { |
be4ef164 PA |
255 | uint8_t pressed = (data.at(col / 8 + row * bytes_per_row) >> (col % 8)) & 1; |
256 | int new_is_was_key_pressed = is_was_key_pressed.at(row).at(col); | |
372aff5f PA |
257 | if (pressed) new_is_was_key_pressed |= 3; |
258 | else new_is_was_key_pressed &= ~1; | |
be4ef164 PA |
259 | if (is_was_key_pressed.at(row).at(col) != new_is_was_key_pressed) different = 1; |
260 | is_was_key_pressed.at(row).at(col) = new_is_was_key_pressed; | |
1c506c79 | 261 | } |
372aff5f | 262 | } |
596f4fc2 | 263 | if (different) this->repaint(); |
372aff5f PA |
264 | } |
265 | ||
266 | uint16_t MonitorWindow::get_threshold(unsigned int col, unsigned int row) | |
267 | { | |
be4ef164 | 268 | if (thresholds.at(0).at(0) == 0) return static_cast<uint16_t>((thresholds.at(0).at(1) | (thresholds.at(0).at(2) << 8))); |
372aff5f | 269 | unsigned int i; |
c5ddd873 PA |
270 | unsigned int bytes_per_row = 1; |
271 | if (keyboard->cols > 16) | |
272 | { | |
273 | bytes_per_row = 4; | |
274 | } else if (keyboard->cols > 8) | |
275 | { | |
276 | bytes_per_row = 2; | |
277 | } | |
be4ef164 | 278 | for (i=0;i<thresholds.at(0).at(0); i++) |
372aff5f | 279 | { |
be4ef164 | 280 | if ((thresholds.at(i).at(3 + col / 8 + row * bytes_per_row) >> (col % 8)) & 1) |
1c506c79 | 281 | { |
be4ef164 | 282 | return static_cast<uint16_t>(thresholds.at(i).at(1) | (thresholds.at(i).at(2) << 8)); |
1c506c79 PA |
283 | } |
284 | } | |
372aff5f | 285 | return 0; |
1c506c79 PA |
286 | } |
287 | ||
372aff5f | 288 | void MonitorWindow::on_reportMonitorError(std::string error_message) |
1c506c79 | 289 | { |
372aff5f PA |
290 | QMessageBox::critical(this, "Error", error_message.c_str()); |
291 | } | |
292 | ||
293 | void MonitorWindow::on_MonitorWindow_finished(int result) | |
294 | { | |
295 | Q_UNUSED(result); | |
296 | thread.closeMonitoredDevice(); | |
1c506c79 | 297 | } |