1 /* Copyright 2020 Purdea Andrei
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.
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.
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/>.
17 #include "monitorwindow.h"
18 #include "ui_monitorwindow.h"
19 #include <QGraphicsView>
24 #include <QMessageBox>
29 MonitorWindow::MonitorWindow(HidThread
&thread
, QWidget
*parent
) :
31 ui(new Ui::MonitorWindow
),
36 current_layout
= nullptr;
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
);
41 this->setContextMenuPolicy(Qt::CustomContextMenu
);
42 connect(this, SIGNAL(customContextMenuRequested(const QPoint
&)),
43 this, SLOT(ShowContextMenu(const QPoint
&)));
44 HORIZONTAL_MARGIN
= 10;
48 void MonitorWindow::ShowContextMenu(const QPoint
&pos
)
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
));
57 MonitorWindow::~MonitorWindow()
62 void MonitorWindow::setMinimumSizeUnits(unsigned int width_units_times_8
, unsigned int height_units_times_8
)
64 HORIZONTAL_MARGIN
= ui
->label_keyboardname
->pos().x();
65 VERTICAL_MARGIN
= ui
->label_keyboardname
->pos().x();
66 this->setMinimumSize(static_cast<int>(width_units_times_8
* MIN_HORIZONTAL_SCALE
/ 8 + 2 * HORIZONTAL_MARGIN
),
67 ui
->last_label
->geometry().y() + ui
->last_label
->geometry().height() +
68 static_cast<int>(height_units_times_8
* MIN_VERTICAL_SCALE
/ 8 + 2 * VERTICAL_MARGIN
));
71 void MonitorWindow::loadLayout(QString name
)
74 ui
->layoutSel
->clear();
75 for (i
=0;i
<n_keyboards
;i
++)
77 if (name
.compare(QString(keyboards
[i
].kbd_name
))==0)
79 keyboard
= &keyboards
[i
];
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));
82 for (j
=0;j
<keyboard
->n_layouts
;j
++)
84 ui
->layoutSel
->addItem(QString(keyboard
->layouts
[j
].lay_name
));
89 ui
->layoutSel
->addItem(QString("Matrix (without showing skipped columns/rows)"));
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);
98 on_reportMonitorError("Unknown keyboard (you may need to update the util version)!");
102 keyboard_width_uis_times_8
= 0;
103 keyboard_height_uis_times_8
= 0;
104 for (i
=0;i
<keyboard
->n_layouts
;i
++)
106 const struct lay_def
*layout
= &keyboard
->layouts
[i
];
108 for (j
=0;j
<layout
->n_keys
;j
++)
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
;
116 setMinimumSizeUnits(keyboard_width_uis_times_8
, keyboard_height_uis_times_8
);
119 void MonitorWindow::displaySquare(int x
, int y
, int w
, int h
, unsigned int col
, unsigned int row
, QPainter
&painter
)
121 QPen
textColor(Qt::black
);
122 painter
.setPen(QPen(Qt::black
));
123 switch (is_was_key_pressed
.at(row
).at(col
))
127 textColor
= QPen(Qt::white
);
128 painter
.setBrush(QBrush(QColor("#008000")));
131 painter
.setBrush(QBrush(QColor("#00FF00")));
134 painter
.setBrush(QBrush(QColor("#FFFFFF")));
137 QRectF
rect(x
, y
, w
, h
);
138 painter
.drawRect(rect
);
139 if (thresholds
.size())
141 uint16_t threshold
= get_threshold(col
, row
);
142 painter
.setPen(textColor
);
143 painter
.drawText(rect
, Qt::AlignCenter
, QString::number(threshold
));
147 void MonitorWindow::updateCurrentLayout()
150 this->current_layout
= nullptr;
151 for (i
=0;i
<keyboard
->n_layouts
;i
++)
153 if (ui
->layoutSel
->currentText().compare(QString(keyboard
->layouts
[i
].lay_name
))==0)
155 this->current_layout
= &keyboard
->layouts
[i
];
161 void MonitorWindow::paintEvent(QPaintEvent
*event
)
163 HORIZONTAL_MARGIN
= ui
->label_keyboardname
->pos().x();
164 VERTICAL_MARGIN
= ui
->label_keyboardname
->pos().x();
166 if (!keyboard
) return;
167 QPainter
painter(this);
168 int xadd
= HORIZONTAL_MARGIN
;
169 int yadd
= ui
->last_label
->geometry().y() + ui
->last_label
->geometry().height() + VERTICAL_MARGIN
;
170 updateCurrentLayout();
171 const struct lay_def
*layout
= this->current_layout
;
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
;
177 for (j
=0;j
<layout
->n_keys
;j
++)
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
);
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
++)
199 for (col
= 0; col
< keyboard
->cols
; col
++)
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
);
213 void MonitorWindow::on_layoutSel_activated(const QString
&arg1
)
216 updateCurrentLayout();
217 if (this->current_layout
)
219 setMinimumSizeUnits(this->keyboard_width_uis_times_8
, this->keyboard_height_uis_times_8
);
221 setMinimumSizeUnits(this->keyboard
->cols
* 8, this->keyboard
->rows
* 8);
226 void MonitorWindow::on_keyboardName(std::string name
)
228 loadLayout(QString::fromStdString(name
));
232 void MonitorWindow::on_thresholds(std::vector
<std::vector
<uint8_t>> thresholds
)
234 this->thresholds
= thresholds
;
238 void MonitorWindow::on_keystate(std::vector
<uint8_t> data
)
240 unsigned int col
, row
;
242 if (!keyboard
) return;
243 unsigned int bytes_per_row
= 1;
244 if (keyboard
->cols
> 16)
247 } else if (keyboard
->cols
> 8)
251 for (col
=0; col
< keyboard
->cols
; col
++)
253 for (row
=0; row
< keyboard
->rows
; row
++)
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
);
257 if (pressed
) new_is_was_key_pressed
|= 3;
258 else new_is_was_key_pressed
&= ~1;
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
;
263 if (different
) this->repaint();
266 uint16_t MonitorWindow::get_threshold(unsigned int col
, unsigned int row
)
268 if (thresholds
.at(0).at(0) == 0) return static_cast<uint16_t>((thresholds
.at(0).at(1) | (thresholds
.at(0).at(2) << 8)));
270 unsigned int bytes_per_row
= 1;
271 if (keyboard
->cols
> 16)
274 } else if (keyboard
->cols
> 8)
278 for (i
=0;i
<thresholds
.at(0).at(0); i
++)
280 if ((thresholds
.at(i
).at(3 + col
/ 8 + row
* bytes_per_row
) >> (col
% 8)) & 1)
282 return static_cast<uint16_t>(thresholds
.at(i
).at(1) | (thresholds
.at(i
).at(2) << 8));
288 void MonitorWindow::on_reportMonitorError(std::string error_message
)
290 QMessageBox::critical(this, "Error", error_message
.c_str());
293 void MonitorWindow::on_MonitorWindow_finished(int result
)
296 thread
.closeMonitoredDevice();