Guide d’implémentation de l’haptique pour dispositifs d'entrée

Ce document détaille l’implémentation du protocole pour les périphériques d’entrée haptic qui se connectent à un hôte compatible Windows 11. Cela n’inclut pas de conseils sur les contraintes mécaniques, les contraintes électriques ou la sélection de composants pour générer la réponse haptique dans le matériel de l’appareil d’entrée.

Classes d’appareils pris en charge

Windows 11 prend en charge les classes suivantes d’appareils d’entrée compatibles haptique :

  • Haptic Touchpad est une extension de la classe d’appareil tactile sur Windows. Ce guide d’implémentation s'ajoute au Guide d’implémentation du pavé tactile et se concentre sur l’implémentation des haptiques au sein du numériseur pour pavé tactile. Les pavés tactiles haptiques doivent donc répondre aux exigences du Guide d’implémentation du pavé tactile en plus de celles contenues ici.

  • Haptic Mouse est une extension de la classe d’appareil souris sur Windows. Les souris haptiques doivent répondre aux exigences contenues dans cette documentation.

Note

Les périphériques d’entrée de stylet compatibles haptic sont une classe d’appareil spéciale qui ne sera pas couverte dans ce document. Pour plus d’informations sur l’implémentation d’un appareil de stylet avec des commentaires haptiques, consultez le Guide d’implémentation du stylet Haptic.

Implémentation du protocole de retours haptiques des dispositifs d'entrée

Une bonne compréhension du protocole HID est nécessaire pour pouvoir comprendre les informations présentées ici. Pour plus d’informations sur le protocole HID, consultez les ressources suivantes :

Le firmware du dispositif d'entrée à retour haptique doit uniquement signaler les usages décrits dans cette rubrique. Windows utilisera le microprogramme et ses propres pilotes HID pour activer l’appareil et donner Windows accès aux applications à l’appareil.

Les exemples de descripteurs pour chaque classe d’appareil prise en charge sont fournis dans la section Exemples de descripteurs de rapport ci-dessous.

Rétroaction haptique initiée par l'hôte

Un appareil d’entrée compatible haptique peut prendre en charge les commentaires haptiques initiés par l’hôte, qui peuvent être déclenchés à tout moment après l’énumération. Les fonctionnalités associées aux haptics doivent être incluses dans une collection HID SimpleHapticsController (Page 0x0E, Utilisation 0x01).

  • Pour les pavés tactiles, cette collection doit être un sous-ensemble de la collection de niveau supérieur du pavé tactile Windows Precision.
  • Pour les souris, cette collection doit être une collection de niveau supérieur et un homologue de la collection de niveau supérieur "souris".

La prise en charge des commentaires haptiques initiés par l’hôte nécessite des rapports HID :

  • Rapport GET_FEATURE utilisé par l’hôte pour interroger les formes d’onde prises en charge et leurs durées. Consultez la section « Rapport sur les fonctionnalités des informations de forme d'onde » ci-dessous.
  • Rapport OUTPUT utilisé par l’hôte pour déclencher manuellement des haptiques. Consultez la section « Rapport de sortie du déclencheur manuel » ci-dessous.

Pour les pavés tactiles, ces rapports doivent être définis dans deux collections enfants logiques de SimpleHapticsController, qui sont sous la collection enfant principale de SimpleHapticsController. Pour les souris, ces rapports peuvent être définis directement dans la collection de niveau supérieur.

Formes d'onde

Le tableau suivant définit les formes d’onde prises en charge par l’hôte pour les périphériques d’entrée compatibles haptique. Les formes d’onde prises en charge par un appareil sont associées à un nombre ordinal. L’utilisation et la durée de la forme d’onde sont fournies à l’hôte via le rapport de fonctionnalité des informations de forme d’onde (voir ci-dessous). Lors du déclenchement d’un signal de rétroaction, l’hôte fournit l’ordinal de la forme d’onde souhaitée comme valeur pour un déclenchement manuel.

Obligatoire et facultatif
Forme d'onde Descriptif Page ID Obligatoire/facultatif
Aucun Pas d'opération. Ne doit pas avoir d’impact sur l’état de jeu des formes d’ondes en cours 0x0E 0x1001 Obligatoire
Arrêtez Arrête la lecture des formes d’ondes en cours 0x0E 0x1002 Obligatoire
Survoler Impulsion lumineuse qui indique les états de survol, signalant la possibilité d'une action à venir. 0x0E 0x1008 Obligatoire
Entrer en collision Pulsation douce pour indiquer l’atteinte d’une borne ou d’une limite 0x0E 0x1012 Obligatoire
Align Impulsion nette lorsqu’un objet s’aligne sur un repère d’alignement 0x0E 0x1013 Obligatoire
Étape Impulsion ferme pour les changements discrets, comme le passage à des étapes ou des valeurs 0x0E 0x1014 Obligatoire
Croître Signal dynamique qui exprime le mouvement, les transitions ou l'activité de système intelligent. 0x0E 0x1015 Obligatoire
Presse Impulsion représentant une pression sur un bouton 0x0E 0x1006 Voir ci-dessous
Libération Impulsion représentant une libération de bouton 0x0E 0x1007 Voir ci-dessous
Success Modèle croissant qui confirme une action terminée 0x0E 0x1009 Voir ci-dessous
Erreur Modèle décroissant qui indique une action ayant échoué 0x0E 0x100A Voir ci-dessous

Les formes d’onde de pression et relâchement sont facultatives, mais si l’une est prise en charge, l’autre doit l’être également. Pour les pavés tactiles, les formes d’ondes doivent correspondre au retour de pression et de relâchement initié par l'appareil.

Les formes d'onde de réussite et d'erreur sont obligatoires pour les souris et facultatives pour les pavés tactiles. Si l’un est soutenu, l’autre doit l’être également.

Interdit

Les formes d’ondes suivantes NE DOIVENT PAS être prises en charge.

Forme d'onde ID Remarques
Click 0x1003 Provoquerait une confusion avec les commentaires haptiques existants pour les pressions sur les boutons.
Buzz Continu 0x1004 Les formes d'ondes continues ne doivent pas être supportées.
Rumble continu 0x1005 Les formes d'ondes continues ne doivent pas être supportées.
Encre continue 0x100B Applicable uniquement aux stylos.
Crayon en mode continu 0x100C Applicable uniquement aux stylos.
Marqueur continu 0x100D Applicable uniquement aux stylos.
Marqueur chisel continu 0x100E Applicable uniquement aux stylos.
Pinceau continu 0x100F Applicable uniquement aux stylos.
Gomme continue 0x1010 Applicable uniquement aux stylos.
Sparkle Continu 0x1011 Applicable uniquement aux stylos.

Rapport sur les fonctionnalités d’information de forme d'onde

L'hôte émettra ce rapport GET_FEATURE lors de l'interrogation de l'appareil concernant les formes d'onde qu'il prend en charge. Ce rapport de fonctionnalités doit avoir un ID de rapport dédié.

Le rapport doit avoir deux collections logiques secondaires, une pour la liste de formes d’ondes et une pour la liste de durées. Ces collections doivent définir une plage d’utilisation sur la page Ordinal (0x0A), ce qui permet à l’hôte d’interroger la forme d’onde et la durée associées à chaque ordinal.

Utilisations obligatoires et facultatives
Membre Descriptif Page ID Obligatoire/facultatif
Liste de formes d’ondes Collection logique contenant une liste ordonnée de formes d’ondes haptiques prises en charge par l’appareil 0x0E 0x10 Obligatoire
Liste de durées Collection logique contenant une liste ordonnée de durées pour les formes d’ondes dans la liste des formes d’ondes 0x0E 0x11 Obligatoire
Liste des formes d'onde (obligatoire)

Cette collection fournit le mappage entre les ordinals et les formes d’onde correspondantes. Les ordinals 1 et 2 correspondent implicitement à None et Stop et n’ont pas besoin d’être déclarés dans le descripteur. Par conséquent, l’utilisation minimale de la plage d’utilisation de la collection peut être de 3, et la valeur maximale d’utilisation doit être suffisamment grande pour affecter des ordinales à toutes les formes d’onde prises en charge. Il n’existe aucun ordre requis pour affecter des formes d’ondes aux ordinals 3 et versions ultérieures . seuls les ordinals 1 et 2 ont des définitions fixes.

Si la valeur maximale d’utilisation est supérieure au nombre de formes d’onde prises en charge par l’appareil, l’appareil doit signaler Aucun pour les ordinals non pris en charge.

La plage logique de la plage d'utilisation doit inclure tous les usages des formes d'onde pris en charge. La plage physique et les unités doivent être 0.

Liste de durées (obligatoire)

Cette collection fournit les durées des formes d’ondes définies dans la liste des formes d’ondes. La plage d’utilisation minimale et maximale de la collection doit être identique à celle de la liste de formes d’ondes.

Les formes d’onde discrètes doivent avoir une durée non nulle. Aucun et Stop, s’il est spécifié, doivent avoir une durée de zéro.

Le minimum logique de la plage d’utilisation doit être égal à zéro, et la valeur maximale logique doit être au moins aussi importante que la durée de la forme d’onde discrète la plus longue. L’hôte traite les valeurs logiques en millisecondes. La plage physique doit être égale à zéro ou identique à la plage logique. Si la plage physique et la plage logique correspondent, les unités doivent être en millisecondes.

Rapport de sortie de déclencheur manuel

L’hôte émettra ce rapport lors du déclenchement de retours haptiques discrets. Ce rapport de sortie doit avoir un ID de rapport dédié.

Utilisations obligatoires et facultatives
Membre Descriptif Page ID Obligatoire/facultatif
Déclencheur manuel Forme d’onde à déclencher en tant que commande explicite à partir de l’hôte 0x0E 0x21 Obligatoire
Intensité Intensité des commentaires 0x0E 0x23 Obligatoire
Nombre de répétitions Nombre de fois où répéter les commentaires après la lecture initiale 0x0E 0x24 Voir ci-dessous
Période de redéclenchement Durée d’attente avant de déclencher à nouveau les commentaires lors de la répétition 0x0E 0x25 Voir ci-dessous
Temps de coupure de forme d’onde Durée maximale pendant laquelle les commentaires peuvent être lus avant d’être coupés 0x0E 0x28 Voir ci-dessous

Le compte des répétitions, la période de réactivation et le temps de coupure de l'onde sont facultatifs, mais si l’un d’eux est pris en charge, les deux autres doivent l’être également.

Utilisations interdites
Usage ID Remarques
Déclencheur automatique 0x20 Non pris en charge par l’hôte.
Déclencheur automatique associé au contrôle 0x22 Non pris en charge par l’hôte.
Déclencheur manuel (obligatoire)

Cette utilisation contient l’ordinal de la forme d’onde, telle que définie à partir du rapport des fonctionnalités d’information de forme d’onde, que l’hôte a demandé de lire. Lorsqu’un rapport de sortie contenant un ordinal autre qu’Aucun n’est envoyé à l’appareil, il doit immédiatement commencer à lire la forme d’onde spécifiée avec les propriétés supplémentaires incluses dans le rapport de sortie (Intensité, Nombre de répétitions, Période de retrigger, Temps de coupure, si pris en charge). L’appareil ne doit respecter que les ordinales pour les formes d’ondes discrètes, None et Stop. Si l’ordinal correspond à Stop, toute lecture de forme d’onde discrète en cours doit être arrêtée. Si l’ordinal correspond à None, aucune action ne doit être effectuée et les retours haptiques en cours doivent continuer à fonctionner.

La plage logique doit inclure tous les ordinaux possibles, y compris les ordinals implicites 1 (Aucun) et 2 (Stop). La plage physique et les unités doivent être 0.

Intensité (obligatoire)

Cette utilisation représente le pourcentage d’intensité maximale à appliquer à la forme d’onde demandée, avec le maximum logique représentant l’intensité maximale et le minimum logique représentant aucun retour du tout.

Le minimum logique doit être égal à zéro et le maximum logique doit être sélectionné en fonction des fonctionnalités de l’appareil, par exemple, si l’appareil prend en charge quatre niveaux d’intensité, le maximum logique doit être de quatre. Si l’appareil prend en charge une intensité plus granulaire, le maximum logique peut être plus grand, mais il ne doit pas dépasser 100. L’appareil doit prendre en charge au moins quatre niveaux d’intensité, de sorte que le maximum logique minimal est de quatre. Une intensité de zéro indique qu’aucun retour ne doit être lu : l’hôte utilise uniquement cette valeur pour Stop.

La plage physique et les unités doivent être 0.

Nombre de répétitions (facultatif)

Cette utilisation représente le nombre de répétitions de la forme d’onde après la lecture initiale. La valeur zéro indique que la forme d’onde ne doit être jouée qu’une seule fois.

Si cette utilisation est prise en charge, la période de réactivation et les usages liés à l'heure de coupure doivent également être pris en charge.

Le minimum logique doit être égal à zéro, et la valeur maximale logique doit être supérieure à zéro. La valeur maximale logique doit être limitée à une valeur raisonnable (par exemple, 10). La plage physique et les unités doivent être 0.

Période de réactivation (facultatif)

Cette utilisation représente la durée entre les réenclenchements de la forme d'onde, mesurée à partir du moment de déclenchement du précédent. Une valeur de zéro doit être interprétée comme identique à la durée par défaut de la forme d’onde, de sorte que le retrigger se produit immédiatement après la fin de la précédente. Les valeurs inférieures à la durée par défaut de la forme d’onde doivent interrompre la forme d’onde et la redémarrer.

Si cette utilisation est prise en charge, le nombre de répétitions et les utilisations de temps de coupure doivent également être prises en charge.

L’hôte traite les valeurs logiques en millisecondes. Le minimum logique doit être égal à zéro et le maximum logique doit être d’au moins 1 000 (représentant une seconde). La plage physique doit être égale à zéro ou identique à la plage logique. Si la plage physique n’est pas égale à zéro, les unités doivent être en millisecondes.

Temps de coupure de forme d’onde (facultatif)

Cette utilisation représente la durée maximale pendant laquelle un déclencheur unique peut entraîner la lecture, qui prend en compte le nombre de répétitions et la période de réenclenchement.

Si cette utilisation est prise en charge, le nombre de répétitions et les périodes de réactivation doivent également être pris en charge.

L’hôte traite les valeurs logiques en millisecondes. Le minimum logique doit être au moins aussi grand que la durée de la forme d’onde discrète la plus longue, multipliée par le maximum logique de l’utilisation du nombre de répétitions plus un. Ce minimum logique représente la durée de déclenchement de la forme d’onde la plus longue avec le nombre maximal de déclencheurs répétés pris en charge et aucun délai entre les retriggers. Le maximum logique peut être limité pour empêcher une durée excessive de commentaires haptiques pour une seule demande, à la discrétion de l’appareil. La plage physique doit être égale à zéro ou identique à la plage logique. Si la plage physique n’est pas égale à zéro, les unités doivent être en millisecondes.

Guide du pavé tactile haptique

Les éléments de cette section s’appliquent uniquement aux pavés tactiles haptiques.

Rétroaction haptique initiée par l'appareil

Un pavé tactile haptique est chargé de déclencher une rétroaction haptique lorsqu’il détermine que la surface du pavé tactile a été enfoncée ou relâchée. Il peut choisir de prendre en charge les rapports SET_FEATURE pour autoriser la personnalisation de son comportement par l’utilisateur lors de cette opération :

  • Intensité de la rétroaction haptique
  • Force requise pour déclencher une pression sur un bouton

Ces deux rapports de fonctionnalités sont obligatoires si le pavé tactile prend également en charge les commentaires haptiques initiés par l’hôte. Chaque rapport doit utiliser un ID de rapport distinct, non utilisé avec une autre utilisation.

Pendant l’énumération, l’hôte évalue la plage logique et physique prise en charge à partir du descripteur et calcule les options exposées pour l’interface utilisateur des paramètres, notamment les valeurs par défaut. L’hôte émet le SET_FEATURE pour communiquer la valeur spécifiée par l’utilisateur à l’appareil ; cette émission peut se produire à tout moment, mais doit se produire chaque fois que le paramètre est modifié, qu’un commutateur utilisateur se produit et que l’appareil est énuméré ou réinitialisé. Avant que le rapport SET_FEATURE ait été émis, l’appareil doit utiliser un défaut raisonnable de son choix (par exemple, le milieu de sa plage logique).

Rapport de caractéristiques d’intensité haptique

Ce rapport SET_FEATURE spécifie la préférence de l’utilisateur pour l’intensité du retour haptique lors de l'appui et du relâchement du bouton. Elle ne s'applique pas à l'intensité des rétroactions initiées par l'hôte, si elles sont prises en charge par le dispositif. Pour prendre en charge cette configuration, l’appareil doit définir une collection enfant logique SimpleHapticsController (Page 0x0E, Utilisation 0x01) dans la collection de niveau supérieur Windows Precision Touchpad, contenant l'utilisation de l'intensité haptique (Page 0x0E, Utilisation 0x23) en tant que rapport de caractéristiques avec un ID de rapport dédié. Cette collection enfant ne doit pas contenir les utilisations de Déclencheur Automatique (Page 0x0E, Utilisation 0x20) ou de Déclencheur Manuel (Page 0x0E, Utilisation 0x21). Il doit être distinct de la collection enfant SimpleHapticsController utilisée pour les commentaires haptiques initiés par l’hôte (si pris en charge).

Le minimum logique doit être égal à zéro, et le maximum logique doit être supérieur ou égal à quatre. La préférence de l’utilisateur sera mise à l’échelle linéairement dans l’intervalle logique, avec zéro indiquant qu’aucun retour d'information ne doit être déclenché pour la pression et la libération du bouton.

Bouton Appuyer sur le rapport de fonctionnalité seuil

Ce rapport SET_FEATURE spécifie la préférence de l’utilisateur pour la force requise pour déclencher une pression sur un bouton. Pour prendre en charge cette configuration, l’appareil doit définir l’utilisation du seuil d’appui sur le bouton (page 0x0D, utilisation 0xB0) en tant que rapport de caractéristique avec un ID de rapport dédié dans la collection de niveau supérieur Windows Precision Touchpad. Il ne doit pas se trouver dans une collection logique SimpleHapticsController.

La plage logique doit être mappée de manière linéaire à la plage physique de valeurs, et être uniformément espacée et centrée autour de la valeur par défaut. Lors de l’acquisition de la plage logique, la valeur par défaut est calculée en utilisant formule suivante :

Diagramme montrant la formule de calcul du seuil par défaut de pression du bouton en unités logiques

Le minimum logique, la valeur par défaut et la valeur maximale logique correspondent à 3 niveaux distincts de pression sur les boutons exposés à un utilisateur par le biais de l’interface utilisateur des paramètres Windows (prenant en charge respectivement « Faible », « Moyen » et « Élevé »).

La plage physique recommandée pour le seuil de pression sur le bouton consiste à couvrir au moins la plage comprise entre 110 g et 190 g, correspondant respectivement aux valeurs minimales et maximales. Pour un exemple de descripteur utilisant un maximum physique de 190 g et un minimum physique de 110 g (par conséquent, selon la formule ci-dessus, la valeur par défaut sera 150 g) consultez Exemples de descripteurs de rapport.

Exemples de descripteurs de rapport HID

Exemple de descripteur de pavé tactile haptique

Le descripteur suivant prend en charge toutes les utilisations obligatoires et facultatives. Il déclare la prise en charge de cinq formes d’ondes, avec la durée la plus longue de 50 ms.

Toutes les plages logiques doivent être mises à jour en fonction de la prise en charge des appareils. Pour prendre en charge un nombre différent de formes d’ondes :

  • La plage logique de l’utilisation du déclencheur manuel doit être mise à jour
  • Les plages d’utilisation et le nombre de rapports pour la liste de formes d’ondes et la liste de durée doivent être mis à jour

Pour prendre en charge une longueur d’onde maximale différente, les plages logiques suivantes doivent être mises à jour :

  • Période de retrigger (sortie)
  • Délai de coupure de forme d’onde (sortie)
  • Liste de durées (fonctionnalité)
0x05, 0x0D,       // UsagePage(Digitizers[0x000D])
0x09, 0x05,       // UsageId(Touch Pad[0x0005])
0xA1, 0x01,       // Collection(Application)
0x85, 0x40,       //  ReportId(64)
0x05, 0x0D,       //  UsagePage(Digitizers[0x000D])
0x09, 0xB0,       //  UsageId(Button Press Threshold[0x00B0])
0x35, 0x6E,       //  PhysicalMinimum(110)
0x46, 0xBE, 0x00, //  PhysicalMaximum(190)
0x66, 0x01, 0x01, //  Unit('gram', SiLinear, Gram:1)
0x55, 0x00,       //  UnitExponent(1)
0x15, 0x01,       //  LogicalMinimum(1)
0x25, 0x03,       //  LogicalMaximum(3)
0x95, 0x01,       //  ReportCount(1)
0x75, 0x08,       //  ReportSize(8)
0xB1, 0x02,       //  Feature(Data, Variable, Absolute)
0x85, 0x41,       //  ReportId(65)
0x05, 0x0E,       //  UsagePage(Haptics[0x000E])
0x09, 0x01,       //  UsageId(Simple Haptic Controller[0x0001])
0xA1, 0x02,       //  Collection(Logical)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x23,       //   UsageId(Intensity[0x0023])
0x35, 0x00,       //   PhysicalMinimum(0)
0x45, 0x00,       //   PhysicalMaximum(0)
0x65, 0x00,       //   Unit(None)
0x55, 0x00,       //   UnitExponent(1)
0x15, 0x00,       //   LogicalMinimum(0)
0x25, 0x04,       //   LogicalMaximum(4)
0x95, 0x01,       //   ReportCount(1)
0x75, 0x08,       //   ReportSize(8)
0xB1, 0x02,       //   Feature(Data, Variable, Absolute)
0xC0,             //  EndCollection()
0x85, 0x42,       //  ReportId(66)
0x05, 0x0E,       //  UsagePage(Haptics[0x000E])
0x09, 0x01,       //  UsageId(Simple Haptic Controller[0x0001])
0xA1, 0x02,       //  Collection(Logical)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x10,       //   UsageId(Waveform List[0x0010])
0xA1, 0x02,       //   Collection(Logical)
0x05, 0x0A,       //    UsagePage(Ordinal[0x000A])
0x19, 0x03,       //    UsageIdMin(Instance 3[0x0003])
0x29, 0x07,       //    UsageIdMax(Instance 7[0x0007])
0x35, 0x00,       //    PhysicalMinimum(0)
0x45, 0x00,       //    PhysicalMaximum(0)
0x65, 0x00,       //    Unit(None)
0x55, 0x00,       //    UnitExponent(1)
0x16, 0x01, 0x10, //    LogicalMinimum(4,097)
0x26, 0xFF, 0x2F, //    LogicalMaximum(12,287)
0x95, 0x05,       //    ReportCount(5)
0x75, 0x10,       //    ReportSize(16)
0xB1, 0x02,       //    Feature(Data, Variable, Absolute)
0xC0,             //   EndCollection()
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x11,       //   UsageId(Duration List[0x0011])
0xA1, 0x02,       //   Collection(Logical)
0x05, 0x0A,       //    UsagePage(Ordinal[0x000A])
0x19, 0x03,       //    UsageIdMin(Instance 3[0x0003])
0x29, 0x07,       //    UsageIdMax(Instance 7[0x0007])
0x35, 0x00,       //    PhysicalMinimum(0)
0x45, 0x32,       //    PhysicalMaximum(50)
0x66, 0x01, 0x10, //    Unit('millisecond', SiLinear, Seconds:1)
0x55, 0x0D,       //    UnitExponent(0.001)
0x15, 0x00,       //    LogicalMinimum(0)
0x25, 0x32,       //    LogicalMaximum(50)
0x95, 0x05,       //    ReportCount(5)
0x75, 0x08,       //    ReportSize(8)
0xB1, 0x02,       //    Feature(Data, Variable, Absolute)
0xC0,             //   EndCollection()
0xC0,             //  EndCollection()
0x85, 0x43,       //  ReportId(67)
0x05, 0x0E,       //  UsagePage(Haptics[0x000E])
0x09, 0x01,       //  UsageId(Simple Haptic Controller[0x0001])
0xA1, 0x02,       //  Collection(Logical)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x21,       //   UsageId(Manual Trigger[0x0021])
0x35, 0x00,       //   PhysicalMinimum(0)
0x45, 0x00,       //   PhysicalMaximum(0)
0x65, 0x00,       //   Unit(None)
0x55, 0x00,       //   UnitExponent(1)
0x15, 0x01,       //   LogicalMinimum(1)
0x25, 0x07,       //   LogicalMaximum(7)
0x95, 0x01,       //   ReportCount(1)
0x75, 0x08,       //   ReportSize(8)
0x91, 0x02,       //   Output(Data, Variable, Absolute)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x23,       //   UsageId(Intensity[0x0023])
0x35, 0x00,       //   PhysicalMinimum(0)
0x45, 0x00,       //   PhysicalMaximum(0)
0x65, 0x00,       //   Unit(None)
0x55, 0x00,       //   UnitExponent(1)
0x15, 0x00,       //   LogicalMinimum(0)
0x25, 0x04,       //   LogicalMaximum(4)
0x95, 0x01,       //   ReportCount(1)
0x75, 0x08,       //   ReportSize(8)
0x91, 0x02,       //   Output(Data, Variable, Absolute)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x24,       //   UsageId(Repeat Count[0x0024])
0x35, 0x00,       //   PhysicalMinimum(0)
0x45, 0x00,       //   PhysicalMaximum(0)
0x65, 0x00,       //   Unit(None)
0x55, 0x00,       //   UnitExponent(1)
0x15, 0x00,       //   LogicalMinimum(0)
0x25, 0x05,       //   LogicalMaximum(5)
0x95, 0x01,       //   ReportCount(1)
0x75, 0x08,       //   ReportSize(8)
0x91, 0x02,       //   Output(Data, Variable, Absolute)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x25,       //   UsageId(Retrigger Period[0x0025])
0x35, 0x00,       //   PhysicalMinimum(0)
0x46, 0xE8, 0x03, //   PhysicalMaximum(1,000)
0x66, 0x01, 0x10, //   Unit('millisecond', SiLinear, Seconds:1)
0x55, 0x0D,       //   UnitExponent(0.001)
0x15, 0x00,       //   LogicalMinimum(0)
0x26, 0xE8, 0x03, //   LogicalMaximum(1,000)
0x95, 0x01,       //   ReportCount(1)
0x75, 0x10,       //   ReportSize(16)
0x91, 0x02,       //   Output(Data, Variable, Absolute)
0x05, 0x0E,       //   UsagePage(Haptics[0x000E])
0x09, 0x28,       //   UsageId(Waveform Cutoff Time[0x0028])
0x36, 0xE8, 0x03, //   PhysicalMinimum(1,000)
0x46, 0x88, 0x13, //   PhysicalMaximum(5,000)
0x66, 0x01, 0x10, //   Unit('millisecond', SiLinear, Seconds:1)
0x55, 0x0D,       //   UnitExponent(0.001)
0x16, 0xE8, 0x03, //   LogicalMinimum(1,000)
0x26, 0x88, 0x13, //   LogicalMaximum(5,000)
0x95, 0x01,       //   ReportCount(1)
0x75, 0x10,       //   ReportSize(16)
0x91, 0x02,       //   Output(Data, Variable, Absolute)
0xC0,             //  EndCollection()
0xC0,             // EndCollection()

Le descripteur ci-dessus a été généré via le fichier Waratah suivant :

[[settings]]
packingInBytes = 1
optimize = false

[[unit]]
name = 'millisecond'
second = [0.001, 1.0]

[[applicationCollection]]
usage = ['Digitizers', 'Touch Pad']

 # Button press threshold feature report
 [[applicationCollection.featureReport]]
 id = 0x40

  [[applicationCollection.featureReport.variableItem]]
  usage = ['Digitizers', 'Button Press Threshold']
  logicalValueRange = [1, 3]
  physicalValueRange = [110, 190]
  unit = 'gram'

 # Feedback intensity feature report
 [[applicationCollection.featureReport]]
 id = 0x41

  [[applicationCollection.featureReport.logicalCollection]]
  usage = ['Haptics', 'Simple Haptic Controller']

   [[applicationCollection.featureReport.logicalCollection.variableItem]]
   usage = ['Haptics', 'Intensity']
   logicalValueRange = [0, 4]

 # Host-initiated waveform information feature report
 [[applicationCollection.featureReport]]
 id = 0x42

  [[applicationCollection.featureReport.logicalCollection]]
  usage = ['Haptics', 'Simple Haptic Controller']

   [[applicationCollection.featureReport.logicalCollection.logicalCollection]]
   usage = ['Haptics', 'Waveform List']

    [[applicationCollection.featureReport.logicalCollection.logicalCollection.variableItem]]
    usageRange = ['Ordinal', 'Instance 3', 'Instance 7']
    logicalValueRange = [0x1001, 0x2FFF]

   [[applicationCollection.featureReport.logicalCollection.logicalCollection]]
   usage = ['Haptics', 'Duration List']

    [[applicationCollection.featureReport.logicalCollection.logicalCollection.variableItem]]
    usageRange = ['Ordinal', 'Instance 3', 'Instance 7']
    logicalValueRange = [0, 50]
    physicalValueRange = [0, 50]
    unit = 'millisecond'

 # Host-initiated waveform manual trigger output report
 [[applicationCollection.outputReport]]
 id = 0x43

  [[applicationCollection.outputReport.logicalCollection]]
  usage = ['Haptics', 'Simple Haptic Controller']

   [[applicationCollection.outputReport.logicalCollection.variableItem]]
   usage = ['Haptics', 'Manual Trigger']
   logicalValueRange = [1, 7]

   [[applicationCollection.outputReport.logicalCollection.variableItem]]
   usage = ['Haptics', 'Intensity']
   logicalValueRange = [0, 4]

   [[applicationCollection.outputReport.logicalCollection.variableItem]]
   usage = ['Haptics', 'Repeat Count']
   logicalValueRange = [0, 5]

   [[applicationCollection.outputReport.logicalCollection.variableItem]]
   usage = ['Haptics', 'Retrigger Period']
   logicalValueRange = [0, 1000]
   physicalValueRange = [0, 1000]
   unit = 'millisecond'

   [[applicationCollection.outputReport.logicalCollection.variableItem]]
   usage = ['Haptics', 'Waveform Cutoff Time']
   logicalValueRange = [1000, 5000]
   physicalValueRange = [1000, 5000]
   unit = 'millisecond'

Exemple de descripteur de souris Haptic

Le descripteur suivant prend en charge toutes les utilisations obligatoires et facultatives. Il déclare la prise en charge de huit formes d’ondes, avec la durée la plus longue de 200 ms.

Toutes les plages logiques doivent être mises à jour en fonction de la prise en charge des appareils. Pour prendre en charge un nombre différent de formes d’ondes :

  • La plage logique de l’utilisation du déclencheur manuel doit être mise à jour
  • Les plages d’utilisation et le nombre de rapports pour la liste de formes d’ondes et la liste de durée doivent être mis à jour

Pour prendre en charge une longueur d’onde maximale différente, les plages logiques suivantes doivent être mises à jour :

  • Période de retrigger (sortie)
  • Délai de coupure de forme d’onde (sortie)
  • Liste de durées (fonctionnalité)
0x05, 0x01,       // UsagePage(Generic Desktop[0x0001])
0x09, 0x02,       // UsageId(Mouse[0x0002])
0xA1, 0x01,       // Collection(Application)
0x85, 0x01,       //  ReportId(1)
0x09, 0x01,       //  UsageId(Pointer[0x0001])
0xA1, 0x00,       //  Collection(Physical)
0x09, 0x30,       //   UsageId(X[0x0030])
0x09, 0x31,       //   UsageId(Y[0x0031])
0x15, 0x80,       //   LogicalMinimum(-128)
0x25, 0x7F,       //   LogicalMaximum(127)
0x95, 0x02,       //   ReportCount(2)
0x75, 0x08,       //   ReportSize(8)
0x81, 0x06,       //   Input(Data, Variable, Relative)
0x05, 0x09,       //   UsagePage(Button[0x0009])
0x19, 0x01,       //   UsageIdMin(Button 1[0x0001])
0x29, 0x03,       //   UsageIdMax(Button 3[0x0003])
0x15, 0x00,       //   LogicalMinimum(0)
0x25, 0x01,       //   LogicalMaximum(1)
0x95, 0x03,       //   ReportCount(3)
0x75, 0x01,       //   ReportSize(1)
0x81, 0x02,       //   Input(Data, Variable, Absolute)
0xC0,             //  EndCollection()
0x95, 0x01,       //  ReportCount(1)
0x75, 0x05,       //  ReportSize(5)
0x81, 0x03,       //  Input(Constant, Variable, Absolute)
0xC0,             // EndCollection()
0x05, 0x0E,       // UsagePage(Haptics[0x000E])
0x09, 0x01,       // UsageId(Simple Haptic Controller[0x0001])
0xA1, 0x01,       // Collection(Application)
0x85, 0x10,       //  ReportId(16)
0x09, 0x10,       //  UsageId(Waveform List[0x0010])
0xA1, 0x02,       //  Collection(Logical)
0x05, 0x0A,       //   UsagePage(Ordinal[0x000A])
0x19, 0x03,       //   UsageIdMin(Instance 3[0x0003])
0x29, 0x0A,       //   UsageIdMax(Instance 10[0x000A])
0x16, 0x01, 0x10, //   LogicalMinimum(4,097)
0x26, 0xFF, 0x2F, //   LogicalMaximum(12,287)
0x95, 0x08,       //   ReportCount(8)
0x75, 0x0E,       //   ReportSize(14)
0xB1, 0x02,       //   Feature(Data, Variable, Absolute)
0xC0,             //  EndCollection()
0x05, 0x0E,       //  UsagePage(Haptics[0x000E])
0x09, 0x11,       //  UsageId(Duration List[0x0011])
0xA1, 0x02,       //  Collection(Logical)
0x05, 0x0A,       //   UsagePage(Ordinal[0x000A])
0x19, 0x03,       //   UsageIdMin(Instance 3[0x0003])
0x29, 0x0A,       //   UsageIdMax(Instance 10[0x000A])
0x46, 0xC8, 0x00, //   PhysicalMaximum(200)
0x66, 0x01, 0x10, //   Unit('millisecond', SiLinear, Seconds:1)
0x55, 0x0D,       //   UnitExponent(0.001)
0x15, 0x00,       //   LogicalMinimum(0)
0x26, 0xC8, 0x00, //   LogicalMaximum(200)
0x75, 0x08,       //   ReportSize(8)
0xB1, 0x02,       //   Feature(Data, Variable, Absolute)
0xC0,             //  EndCollection()
0x85, 0x11,       //  ReportId(17)
0x05, 0x0E,       //  UsagePage(Haptics[0x000E])
0x09, 0x21,       //  UsageId(Manual Trigger[0x0021])
0x45, 0x00,       //  PhysicalMaximum(0)
0x65, 0x00,       //  Unit(None)
0x55, 0x00,       //  UnitExponent(1)
0x15, 0x01,       //  LogicalMinimum(1)
0x25, 0x0A,       //  LogicalMaximum(10)
0x95, 0x01,       //  ReportCount(1)
0x75, 0x04,       //  ReportSize(4)
0x91, 0x02,       //  Output(Data, Variable, Absolute)
0x09, 0x23,       //  UsageId(Intensity[0x0023])
0x15, 0x00,       //  LogicalMinimum(0)
0x25, 0x04,       //  LogicalMaximum(4)
0x75, 0x03,       //  ReportSize(3)
0x91, 0x02,       //  Output(Data, Variable, Absolute)
0x09, 0x24,       //  UsageId(Repeat Count[0x0024])
0x25, 0x05,       //  LogicalMaximum(5)
0x91, 0x02,       //  Output(Data, Variable, Absolute)
0x09, 0x25,       //  UsageId(Retrigger Period[0x0025])
0x46, 0xE8, 0x03, //  PhysicalMaximum(1,000)
0x66, 0x01, 0x10, //  Unit('millisecond', SiLinear, Seconds:1)
0x55, 0x0D,       //  UnitExponent(0.001)
0x26, 0xE8, 0x03, //  LogicalMaximum(1,000)
0x75, 0x0A,       //  ReportSize(10)
0x91, 0x02,       //  Output(Data, Variable, Absolute)
0x09, 0x28,       //  UsageId(Waveform Cutoff Time[0x0028])
0x36, 0xE8, 0x03, //  PhysicalMinimum(1,000)
0x46, 0x88, 0x13, //  PhysicalMaximum(5,000)
0x16, 0xE8, 0x03, //  LogicalMinimum(1,000)
0x26, 0x88, 0x13, //  LogicalMaximum(5,000)
0x75, 0x0D,       //  ReportSize(13)
0x91, 0x02,       //  Output(Data, Variable, Absolute)
0x75, 0x07,       //  ReportSize(7)
0x91, 0x03,       //  Output(Constant, Variable, Absolute)
0xC0,             // EndCollection()

Le descripteur ci-dessus a été généré via le fichier Waratah suivant :

[[unit]]
name = 'millisecond'
second = [0.001, 1.0]

[[applicationCollection]]
usage = ['Generic Desktop', 'Mouse']

 # Mouse
 [[applicationCollection.inputReport]]

  [[applicationCollection.inputReport.physicalCollection]]
  usage = ['Generic Desktop', 'Pointer']

   [[applicationCollection.inputReport.physicalCollection.variableItem]]
   usage = ['Generic Desktop', 'X']
   sizeInBits = 8
   logicalValueRange = 'maxSignedSizeRange'
   reportFlags = ['relative']

   [[applicationCollection.inputReport.physicalCollection.variableItem]]
   usage = ['Generic Desktop', 'Y']
   sizeInBits = 8
   logicalValueRange = 'maxSignedSizeRange'
   reportFlags = ['relative']

   [[applicationCollection.inputReport.physicalCollection.variableItem]]
   usageRange = ['Button', 'Button 1', 'Button 3']
   logicalValueRange = [0, 1]

[[applicationCollection]]
usage = ['Haptics', 'Simple Haptic Controller']

 # Host-initiated waveform information feature report
 [[applicationCollection.featureReport]]
 id = 0x10

  [[applicationCollection.featureReport.logicalCollection]]
  usage = ['Haptics', 'Waveform List']

   [[applicationCollection.featureReport.logicalCollection.variableItem]]
   usageRange = ['Ordinal', 'Instance 3', 'Instance 10']
   logicalValueRange = [0x1001, 0x2FFF]

  [[applicationCollection.featureReport.logicalCollection]]
  usage = ['Haptics', 'Duration List']

   [[applicationCollection.featureReport.logicalCollection.variableItem]]
   usageRange = ['Ordinal', 'Instance 3', 'Instance 10']
   logicalValueRange = [0, 200]
   physicalValueRange = [0, 200]
   unit = 'millisecond'

 # Host-initiated waveform manual trigger output report
 [[applicationCollection.outputReport]]
 id = 0x11

  [[applicationCollection.outputReport.variableItem]]
  usage = ['Haptics', 'Manual Trigger']
  logicalValueRange = [1, 10]

  [[applicationCollection.outputReport.variableItem]]
  usage = ['Haptics', 'Intensity']
  logicalValueRange = [0, 4]

  [[applicationCollection.outputReport.variableItem]]
  usage = ['Haptics', 'Repeat Count']
  logicalValueRange = [0, 5]

  [[applicationCollection.outputReport.variableItem]]
  usage = ['Haptics', 'Retrigger Period']
  logicalValueRange = [0, 1000]
  physicalValueRange = [0, 1000]
  unit = 'millisecond'

  [[applicationCollection.outputReport.variableItem]]
  usage = ['Haptics', 'Waveform Cutoff Time']
  logicalValueRange = [1000, 5000]
  physicalValueRange = [1000, 5000]
  unit = 'millisecond'