MayaFlux 0.5.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 249 of file TabletBackend.cpp.

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