MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches

◆ parse_descriptor()

TabletLayout MayaFlux::Core::TabletBackend::parse_descriptor ( std::span< const uint8_t >  descriptor)
static

Parse a HID report descriptor into a tablet layout.

Free of device state and of HIDAPI, so captured descriptor bytes can be parsed and asserted against with no hardware present.

Parameters
descriptorRaw report descriptor bytes.
Returns
Parsed layout. No fields means no digitizer usages were found.

Definition at line 253 of file TabletBackend.cpp.

254{
255
256 TabletLayout layout;
257
258 GlobalState global;
259 std::vector<GlobalState> global_stack;
260
261 std::vector<LocalUsage> local_usages;
262 uint32_t usage_min = 0;
263 uint32_t usage_max = 0;
264 bool has_usage_range = false;
265
266 std::unordered_map<uint8_t, uint32_t> bit_cursor;
267 bool digitizer_seen = false;
268
269 const auto clear_locals = [&]() {
270 local_usages.clear();
271 usage_min = 0;
272 usage_max = 0;
273 has_usage_range = false;
274 };
275
276 size_t i = 0;
277 while (i < descriptor.size()) {
278 const uint8_t prefix = descriptor[i];
279
280 if (prefix == 0xFE) {
281 if (i + 1 >= descriptor.size())
282 break;
283 i += 2U + descriptor[i + 1];
284 continue;
285 }
286
287 size_t size = prefix & 0x03U;
288 if (size == 3)
289 size = 4;
290 const uint8_t type = (prefix >> 2U) & 0x03U;
291 const uint8_t tag = (prefix >> 4U) & 0x0FU;
292
293 ++i;
294 if (i + size > descriptor.size())
295 break;
296
297 const uint8_t* payload = descriptor.data() + i;
298 i += size;
299
300 if (type == 1) {
301 switch (tag) {
302 case 0x0:
303 global.usage_page = static_cast<uint16_t>(item_unsigned(payload, size));
304 if (global.usage_page == k_page_digitizer)
305 digitizer_seen = true;
306 break;
307
308 case 0x1:
309 global.logical_min = item_signed(payload, size);
310 break;
311
312 case 0x2: {
313 const int32_t as_signed = item_signed(payload, size);
314 global.logical_max = (global.logical_min >= 0 && as_signed < 0)
315 ? static_cast<int32_t>(item_unsigned(payload, size))
316 : as_signed;
317 break;
318 }
319
320 case 0x7:
321 global.report_size = item_unsigned(payload, size);
322 break;
323
324 case 0x8:
325 global.report_id = static_cast<uint8_t>(item_unsigned(payload, size));
326 layout.uses_report_ids = true;
327 break;
328
329 case 0x9:
330 global.report_count = item_unsigned(payload, size);
331 break;
332
333 case 0xA:
334 global_stack.push_back(global);
335 break;
336
337 case 0xB:
338 if (!global_stack.empty()) {
339 global = global_stack.back();
340 global_stack.pop_back();
341 }
342 break;
343
344 default:
345 break;
346 }
347 continue;
348 }
349
350 if (type == 2) {
351 switch (tag) {
352 case 0x0:
353 if (size == 4) {
354 const uint32_t full = item_unsigned(payload, size);
355 const auto page = static_cast<uint16_t>(full >> 16U);
356 local_usages.push_back({ .page = page, .usage = static_cast<uint16_t>(full & 0xFFFFU) });
357 if (page == k_page_digitizer)
358 digitizer_seen = true;
359 } else {
360 local_usages.push_back({ .page = global.usage_page,
361 .usage = static_cast<uint16_t>(item_unsigned(payload, size)) });
362 }
363 break;
364
365 case 0x1:
366 usage_min = item_unsigned(payload, size);
367 has_usage_range = true;
368 break;
369
370 case 0x2:
371 usage_max = item_unsigned(payload, size);
372 has_usage_range = true;
373 break;
374
375 default:
376 break;
377 }
378 continue;
379 }
380
381 if (type != 0) {
382 continue;
383 }
384
385 if (tag != 0x8) {
386 clear_locals();
387 continue;
388 }
389
390 const uint32_t flags = item_unsigned(payload, size);
391 const bool is_constant = (flags & 0x01U) != 0U;
392
393 if (global.report_size == 0 || global.report_count == 0) {
394 clear_locals();
395 continue;
396 }
397
398 uint32_t& cursor = bit_cursor[global.report_id];
399
400 for (uint32_t index = 0; index < global.report_count; ++index) {
401 LocalUsage current { .page = global.usage_page, .usage = 0 };
402
403 if (!is_constant) {
404 if (index < local_usages.size()) {
405 current = local_usages[index];
406 } else if (!local_usages.empty()) {
407 current = local_usages.back();
408 } else if (has_usage_range) {
409 current.usage = static_cast<uint16_t>(
410 std::min(usage_min + index, usage_max));
411 }
412 }
413
414 const size_t slot = is_constant
415 ? k_slot_none
416 : slot_for_usage(current.page, current.usage);
417
418 if (slot != k_slot_none) {
419 TabletField field;
420 field.report_id = global.report_id;
421 field.usage_page = current.page;
422 field.usage = current.usage;
423 field.bit_offset = cursor;
424 field.bit_size = global.report_size;
425 field.logical_min = global.logical_min;
426 field.logical_max = global.logical_max;
427 field.slot = slot;
428 layout.fields.push_back(field);
429
430 if (slot < TabletFrame::SLOT_COUNT)
431 layout.axes |= axis_for_slot(slot);
432 if (slot == k_pseudo_invert)
433 layout.has_invert = true;
434 if (slot == k_pseudo_serial)
435 layout.has_serial = true;
436 }
437
438 cursor += global.report_size;
439 }
440
441 clear_locals();
442 }
443
444 if (!digitizer_seen)
445 layout.fields.clear();
446
447 return layout;
448}
uint64_t payload
Definition Composite.cpp:18
bool size
glm::vec2 current
uint16_t page
vk::PhysicalDeviceType type
Definition VKDevice.cpp:126
uint32_t index
Definition VKDevice.cpp:122
static constexpr size_t SLOT_COUNT

References MayaFlux::Core::TabletLayout::axes, MayaFlux::Core::TabletField::bit_offset, MayaFlux::Core::TabletField::bit_size, current, MayaFlux::Core::TabletLayout::fields, MayaFlux::Core::TabletLayout::has_invert, MayaFlux::Core::TabletLayout::has_serial, index, MayaFlux::Core::TabletField::logical_max, MayaFlux::Core::TabletField::logical_min, page, payload, MayaFlux::Core::TabletField::report_id, size, MayaFlux::Core::TabletField::slot, MayaFlux::Core::TabletFrame::SLOT_COUNT, type, MayaFlux::Core::TabletField::usage, MayaFlux::Core::TabletField::usage_page, and MayaFlux::Core::TabletLayout::uses_report_ids.

Referenced by adopt_device().

+ Here is the caller graph for this function: