xwhatsit util: fix code that handles minimum window size
[jackhill/qmk/firmware.git] / keyboards / xwhatsit / util / util / monitorwindow.cpp
CommitLineData
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 29MonitorWindow::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
48void 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
57MonitorWindow::~MonitorWindow()
58{
59 delete ui;
60}
61
0c5817c9 62void 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 71void 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
119void 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
147void 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
161void 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
213void 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
226void MonitorWindow::on_keyboardName(std::string name)
227{
228 loadLayout(QString::fromStdString(name));
229 this->repaint();
230}
231
232void MonitorWindow::on_thresholds(std::vector<std::vector<uint8_t>> thresholds)
233{
234 this->thresholds = thresholds;
235 this->repaint();
236}
237
238void 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
266uint16_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 288void MonitorWindow::on_reportMonitorError(std::string error_message)
1c506c79 289{
372aff5f
PA
290 QMessageBox::critical(this, "Error", error_message.c_str());
291}
292
293void MonitorWindow::on_MonitorWindow_finished(int result)
294{
295 Q_UNUSED(result);
296 thread.closeMonitoredDevice();
1c506c79 297}