| 222 | } |
| 223 | |
| 224 | void EvdevEncoderManager::onReadable() |
| 225 | { |
| 226 | if (m_fd < 0) return; |
| 227 | struct input_event ev[32]; |
| 228 | while (true) { |
| 229 | ssize_t n = ::read(m_fd, ev, sizeof(ev)); |
| 230 | if (n < 0) { |
| 231 | if (errno == EAGAIN || errno == EWOULDBLOCK) break; |
| 232 | // ENODEV typically means the BT dial disconnected. |
| 233 | if (errno == ENODEV) { |
| 234 | qCInfo(lcDevices) << "EvdevEncoderManager: device removed"; |
| 235 | closeFd(); |
| 236 | m_rescanTimer->start(); // wait for hot-plug re-add |
| 237 | return; |
| 238 | } |
| 239 | qCWarning(lcDevices) << "EvdevEncoderManager: read err" |
| 240 | << std::strerror(errno); |
| 241 | closeFd(); |
| 242 | return; |
| 243 | } |
| 244 | if (n == 0) break; |
| 245 | const int count = static_cast<int>(n / sizeof(input_event)); |
| 246 | for (int i = 0; i < count; ++i) { |
| 247 | const struct input_event& e = ev[i]; |
| 248 | if (e.type != EV_KEY) continue; |
| 249 | |
| 250 | const int keycode = e.code; |
| 251 | const int value = e.value; // 0 = release, 1 = press, 2 = autorepeat |
| 252 | |
| 253 | // Rotary: emit a tick per press or autorepeat tick. The |
| 254 | // ~30 Hz autorepeat rate from the dial firmware gives a |
| 255 | // natural acceleration feel under continuous rotation. |
| 256 | if (keycode == kRotaryCwKey) { |
| 257 | if (value == 1 || value == 2) emit tuneSteps(+1); |
| 258 | continue; |
| 259 | } |
| 260 | if (keycode == kRotaryCcwKey) { |
| 261 | if (value == 1 || value == 2) emit tuneSteps(-1); |
| 262 | continue; |
| 263 | } |
| 264 | |
| 265 | // Modifier state for chord assembly. |
| 266 | if (keycode == KEY_LEFTCTRL || keycode == KEY_RIGHTCTRL) { |
| 267 | m_ctrlDown = (value != 0); |
| 268 | if (!m_ctrlDown) { |
| 269 | // Ctrl released — chord window closing. Reset |
| 270 | // compound flags so the next chord starts clean. |
| 271 | m_lastNonModKey = -1; |
| 272 | m_prevsongAlongsideCtrl = false; |
| 273 | } |
| 274 | continue; |
| 275 | } |
| 276 | |
| 277 | // PREVIOUSSONG can arrive alongside a Ctrl chord (Mode Cycle |
| 278 | // emits Ctrl+Y+KEY_PREVIOUSSONG as a compound). Track that |
| 279 | // it was present during a chord window so we can emit the |
| 280 | // compound signature. |
| 281 | if (keycode == KEY_PREVIOUSSONG && m_ctrlDown && value == 1) { |
nothing calls this directly
no test coverage detected