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