2727)
2828
2929import attr
30+ from collections import namedtuple
3031from enum import Enum
3132from hidtools .hut import HUT
3233from hidtools .hid import HidUnit
@@ -862,13 +863,66 @@ def offset_rotation(value):
862863
863864
864865class TestDTH2452Tablet (test_multitouch .BaseTest .TestMultitouch , TouchTabletTest ):
866+ ContactIds = namedtuple ("ContactIds" , "contact_id, tracking_id, slot_num" )
867+
865868 def create_device (self ):
866869 return test_multitouch .Digitizer (
867870 "DTH 2452" ,
868871 rdesc = "05 0d 09 04 a1 01 85 0c 95 01 75 08 15 00 26 ff 00 81 03 09 54 81 02 09 22 a1 02 05 0d 95 01 75 01 25 01 09 42 81 02 81 03 09 47 81 02 95 05 81 03 09 51 26 ff 00 75 10 95 01 81 02 35 00 65 11 55 0e 05 01 09 30 26 a0 44 46 96 14 81 42 09 31 26 9a 26 46 95 0b 81 42 05 0d 75 08 95 01 15 00 09 48 26 5f 00 46 7c 14 81 02 09 49 25 35 46 7d 0b 81 02 45 00 65 00 55 00 c0 05 0d 09 22 a1 02 05 0d 95 01 75 01 25 01 09 42 81 02 81 03 09 47 81 02 95 05 81 03 09 51 26 ff 00 75 10 95 01 81 02 35 00 65 11 55 0e 05 01 09 30 26 a0 44 46 96 14 81 42 09 31 26 9a 26 46 95 0b 81 42 05 0d 75 08 95 01 15 00 09 48 26 5f 00 46 7c 14 81 02 09 49 25 35 46 7d 0b 81 02 45 00 65 00 55 00 c0 05 0d 09 22 a1 02 05 0d 95 01 75 01 25 01 09 42 81 02 81 03 09 47 81 02 95 05 81 03 09 51 26 ff 00 75 10 95 01 81 02 35 00 65 11 55 0e 05 01 09 30 26 a0 44 46 96 14 81 42 09 31 26 9a 26 46 95 0b 81 42 05 0d 75 08 95 01 15 00 09 48 26 5f 00 46 7c 14 81 02 09 49 25 35 46 7d 0b 81 02 45 00 65 00 55 00 c0 05 0d 09 22 a1 02 05 0d 95 01 75 01 25 01 09 42 81 02 81 03 09 47 81 02 95 05 81 03 09 51 26 ff 00 75 10 95 01 81 02 35 00 65 11 55 0e 05 01 09 30 26 a0 44 46 96 14 81 42 09 31 26 9a 26 46 95 0b 81 42 05 0d 75 08 95 01 15 00 09 48 26 5f 00 46 7c 14 81 02 09 49 25 35 46 7d 0b 81 02 45 00 65 00 55 00 c0 05 0d 09 22 a1 02 05 0d 95 01 75 01 25 01 09 42 81 02 81 03 09 47 81 02 95 05 81 03 09 51 26 ff 00 75 10 95 01 81 02 35 00 65 11 55 0e 05 01 09 30 26 a0 44 46 96 14 81 42 09 31 26 9a 26 46 95 0b 81 42 05 0d 75 08 95 01 15 00 09 48 26 5f 00 46 7c 14 81 02 09 49 25 35 46 7d 0b 81 02 45 00 65 00 55 00 c0 05 0d 27 ff ff 00 00 75 10 95 01 09 56 81 02 75 08 95 0e 81 03 09 55 26 ff 00 75 08 b1 02 85 0a 06 00 ff 09 c5 96 00 01 b1 02 c0 06 00 ff 09 01 a1 01 09 01 85 13 15 00 26 ff 00 75 08 95 3f 81 02 06 00 ff 09 01 15 00 26 ff 00 75 08 95 3f 91 02 c0" ,
869872 input_info = (0x3 , 0x056A , 0x0383 ),
870873 )
871874
875+ def make_contact (self , contact_id = 0 , t = 0 ):
876+ """
877+ Make a single touch contact that can move over time.
878+
879+ Creates a touch object that has a well-known position in space that
880+ does not overlap with other contacts. The value of `t` may be
881+ incremented over time to move the point along a linear path.
882+ """
883+ x = 50 + 10 * contact_id + t
884+ y = 100 + 100 * contact_id + t
885+ return test_multitouch .Touch (contact_id , x , y )
886+
887+ def make_contacts (self , n , t = 0 ):
888+ """
889+ Make multiple touch contacts that can move over time.
890+
891+ Returns a list of `n` touch objects that are positioned at well-known
892+ locations. The value of `t` may be incremented over time to move the
893+ points along a linear path.
894+ """
895+ return [ self .make_contact (id , t ) for id in range (0 , n ) ]
896+
897+ def assert_contact (self , uhdev , evdev , contact_ids , t = 0 ):
898+ """
899+ Assert properties of a contact generated by make_contact.
900+ """
901+ contact_id = contact_ids .contact_id
902+ tracking_id = contact_ids .tracking_id
903+ slot_num = contact_ids .slot_num
904+
905+ x = 50 + 10 * contact_id + t
906+ y = 100 + 100 * contact_id + t
907+
908+ # If the data isn't supposed to be stored in any slots, there is
909+ # nothing we can check for in the evdev stream.
910+ if slot_num is None :
911+ assert tracking_id == - 1
912+ return
913+
914+ assert evdev .slots [slot_num ][libevdev .EV_ABS .ABS_MT_TRACKING_ID ] == tracking_id
915+ if tracking_id != - 1 :
916+ assert evdev .slots [slot_num ][libevdev .EV_ABS .ABS_MT_POSITION_X ] == x
917+ assert evdev .slots [slot_num ][libevdev .EV_ABS .ABS_MT_POSITION_Y ] == y
918+
919+ def assert_contacts (self , uhdev , evdev , data , t = 0 ):
920+ """
921+ Assert properties of a list of contacts generated by make_contacts.
922+ """
923+ for contact_ids in data :
924+ self .assert_contact (uhdev , evdev , contact_ids , t )
925+
872926 def test_contact_id_0 (self ):
873927 """
874928 Bring a finger in contact with the tablet, then hold it down and remove it.
@@ -919,4 +973,226 @@ def test_confidence_false(self):
919973
920974 slot = self .get_slot (uhdev , t0 , 0 )
921975
922- assert not events
976+ assert not events
977+
978+ def test_confidence_multitouch (self ):
979+ """
980+ Bring multiple fingers in contact with the tablet, some with the
981+ confidence bit set, and some without.
982+
983+ Ensure that all confident touches are reported and that all non-
984+ confident touches are ignored.
985+ """
986+ uhdev = self .uhdev
987+ evdev = uhdev .get_evdev ()
988+
989+ touches = self .make_contacts (5 )
990+ touches [0 ].confidence = False
991+ touches [2 ].confidence = False
992+ touches [4 ].confidence = False
993+
994+ r = uhdev .event (touches )
995+ events = uhdev .next_sync_events ()
996+ self .debug_reports (r , uhdev , events )
997+
998+ assert libevdev .InputEvent (libevdev .EV_KEY .BTN_TOUCH , 1 ) in events
999+
1000+ self .assert_contacts (uhdev , evdev ,
1001+ [ self .ContactIds (contact_id = 0 , tracking_id = - 1 , slot_num = None ),
1002+ self .ContactIds (contact_id = 1 , tracking_id = 0 , slot_num = 0 ),
1003+ self .ContactIds (contact_id = 2 , tracking_id = - 1 , slot_num = None ),
1004+ self .ContactIds (contact_id = 3 , tracking_id = 1 , slot_num = 1 ),
1005+ self .ContactIds (contact_id = 4 , tracking_id = - 1 , slot_num = None ) ])
1006+
1007+ def confidence_change_assert_playback (self , uhdev , evdev , timeline ):
1008+ """
1009+ Assert proper behavior of contacts that move and change tipswitch /
1010+ confidence status over time.
1011+
1012+ Given a `timeline` list of touch states to iterate over, verify
1013+ that the contacts move and are reported as up/down as expected
1014+ by the state of the tipswitch and confidence bits.
1015+ """
1016+ t = 0
1017+
1018+ for state in timeline :
1019+ touches = self .make_contacts (len (state ), t )
1020+
1021+ for item in zip (touches , state ):
1022+ item [0 ].tipswitch = item [1 ][1 ]
1023+ item [0 ].confidence = item [1 ][2 ]
1024+
1025+ r = uhdev .event (touches )
1026+ events = uhdev .next_sync_events ()
1027+ self .debug_reports (r , uhdev , events )
1028+
1029+ ids = [ x [0 ] for x in state ]
1030+ self .assert_contacts (uhdev , evdev , ids , t )
1031+
1032+ t += 1
1033+
1034+ def test_confidence_loss_a (self ):
1035+ """
1036+ Transition a confident contact to a non-confident contact by
1037+ first clearing the tipswitch.
1038+
1039+ Ensure that the driver reports the transitioned contact as
1040+ being removed and that other contacts continue to report
1041+ normally. This mode of confidence loss is used by the
1042+ DTH-2452.
1043+ """
1044+ uhdev = self .uhdev
1045+ evdev = uhdev .get_evdev ()
1046+
1047+ self .confidence_change_assert_playback (uhdev , evdev , [
1048+ # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident
1049+ # Both fingers confidently in contact
1050+ [(self .ContactIds (contact_id = 0 , tracking_id = 0 , slot_num = 0 ), True , True ),
1051+ (self .ContactIds (contact_id = 1 , tracking_id = 1 , slot_num = 1 ), True , True )],
1052+
1053+ # t=1: Contact 0 == !Down + confident; Contact 1 == Down + confident
1054+ # First finger looses confidence and clears only the tipswitch flag
1055+ [(self .ContactIds (contact_id = 0 , tracking_id = - 1 , slot_num = 0 ), False , True ),
1056+ (self .ContactIds (contact_id = 1 , tracking_id = 1 , slot_num = 1 ), True , True )],
1057+
1058+ # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident
1059+ # First finger has lost confidence and has both flags cleared
1060+ [(self .ContactIds (contact_id = 0 , tracking_id = - 1 , slot_num = 0 ), False , False ),
1061+ (self .ContactIds (contact_id = 1 , tracking_id = 1 , slot_num = 1 ), True , True )],
1062+
1063+ # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident
1064+ # First finger has lost confidence and has both flags cleared
1065+ [(self .ContactIds (contact_id = 0 , tracking_id = - 1 , slot_num = 0 ), False , False ),
1066+ (self .ContactIds (contact_id = 1 , tracking_id = 1 , slot_num = 1 ), True , True )]
1067+ ])
1068+
1069+ def test_confidence_loss_b (self ):
1070+ """
1071+ Transition a confident contact to a non-confident contact by
1072+ cleraing both tipswitch and confidence bits simultaneously.
1073+
1074+ Ensure that the driver reports the transitioned contact as
1075+ being removed and that other contacts continue to report
1076+ normally. This mode of confidence loss is used by some
1077+ AES devices.
1078+ """
1079+ uhdev = self .uhdev
1080+ evdev = uhdev .get_evdev ()
1081+
1082+ self .confidence_change_assert_playback (uhdev , evdev , [
1083+ # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident
1084+ # Both fingers confidently in contact
1085+ [(self .ContactIds (contact_id = 0 , tracking_id = 0 , slot_num = 0 ), True , True ),
1086+ (self .ContactIds (contact_id = 1 , tracking_id = 1 , slot_num = 1 ), True , True )],
1087+
1088+ # t=1: Contact 0 == !Down + !confident; Contact 1 == Down + confident
1089+ # First finger looses confidence and has both flags cleared simultaneously
1090+ [(self .ContactIds (contact_id = 0 , tracking_id = - 1 , slot_num = 0 ), False , False ),
1091+ (self .ContactIds (contact_id = 1 , tracking_id = 1 , slot_num = 1 ), True , True )],
1092+
1093+ # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident
1094+ # First finger has lost confidence and has both flags cleared
1095+ [(self .ContactIds (contact_id = 0 , tracking_id = - 1 , slot_num = 0 ), False , False ),
1096+ (self .ContactIds (contact_id = 1 , tracking_id = 1 , slot_num = 1 ), True , True )],
1097+
1098+ # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident
1099+ # First finger has lost confidence and has both flags cleared
1100+ [(self .ContactIds (contact_id = 0 , tracking_id = - 1 , slot_num = 0 ), False , False ),
1101+ (self .ContactIds (contact_id = 1 , tracking_id = 1 , slot_num = 1 ), True , True )]
1102+ ])
1103+
1104+ def test_confidence_loss_c (self ):
1105+ """
1106+ Transition a confident contact to a non-confident contact by
1107+ clearing only the confidence bit.
1108+
1109+ Ensure that the driver reports the transitioned contact as
1110+ being removed and that other contacts continue to report
1111+ normally.
1112+ """
1113+ uhdev = self .uhdev
1114+ evdev = uhdev .get_evdev ()
1115+
1116+ self .confidence_change_assert_playback (uhdev , evdev , [
1117+ # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident
1118+ # Both fingers confidently in contact
1119+ [(self .ContactIds (contact_id = 0 , tracking_id = 0 , slot_num = 0 ), True , True ),
1120+ (self .ContactIds (contact_id = 1 , tracking_id = 1 , slot_num = 1 ), True , True )],
1121+
1122+ # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident
1123+ # First finger looses confidence and clears only the confidence flag
1124+ [(self .ContactIds (contact_id = 0 , tracking_id = - 1 , slot_num = 0 ), True , False ),
1125+ (self .ContactIds (contact_id = 1 , tracking_id = 1 , slot_num = 1 ), True , True )],
1126+
1127+ # t=2: Contact 0 == !Down + !confident; Contact 1 == Down + confident
1128+ # First finger has lost confidence and has both flags cleared
1129+ [(self .ContactIds (contact_id = 0 , tracking_id = - 1 , slot_num = 0 ), False , False ),
1130+ (self .ContactIds (contact_id = 1 , tracking_id = 1 , slot_num = 1 ), True , True )],
1131+
1132+ # t=3: Contact 0 == !Down + !confident; Contact 1 == Down + confident
1133+ # First finger has lost confidence and has both flags cleared
1134+ [(self .ContactIds (contact_id = 0 , tracking_id = - 1 , slot_num = 0 ), False , False ),
1135+ (self .ContactIds (contact_id = 1 , tracking_id = 1 , slot_num = 1 ), True , True )]
1136+ ])
1137+
1138+ def test_confidence_gain_a (self ):
1139+ """
1140+ Transition a contact that was always non-confident to confident.
1141+
1142+ Ensure that the confident contact is reported normally.
1143+ """
1144+ uhdev = self .uhdev
1145+ evdev = uhdev .get_evdev ()
1146+
1147+ self .confidence_change_assert_playback (uhdev , evdev , [
1148+ # t=0: Contact 0 == Down + !confident; Contact 1 == Down + confident
1149+ # Only second finger is confidently in contact
1150+ [(self .ContactIds (contact_id = 0 , tracking_id = - 1 , slot_num = None ), True , False ),
1151+ (self .ContactIds (contact_id = 1 , tracking_id = 0 , slot_num = 0 ), True , True )],
1152+
1153+ # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident
1154+ # First finger gains confidence
1155+ [(self .ContactIds (contact_id = 0 , tracking_id = - 1 , slot_num = None ), True , False ),
1156+ (self .ContactIds (contact_id = 1 , tracking_id = 0 , slot_num = 0 ), True , True )],
1157+
1158+ # t=2: Contact 0 == Down + confident; Contact 1 == Down + confident
1159+ # First finger remains confident
1160+ [(self .ContactIds (contact_id = 0 , tracking_id = 1 , slot_num = 1 ), True , True ),
1161+ (self .ContactIds (contact_id = 1 , tracking_id = 0 , slot_num = 0 ), True , True )],
1162+
1163+ # t=3: Contact 0 == Down + confident; Contact 1 == Down + confident
1164+ # First finger remains confident
1165+ [(self .ContactIds (contact_id = 0 , tracking_id = 1 , slot_num = 1 ), True , True ),
1166+ (self .ContactIds (contact_id = 1 , tracking_id = 0 , slot_num = 0 ), True , True )]
1167+ ])
1168+
1169+ def test_confidence_gain_b (self ):
1170+ """
1171+ Transition a contact from non-confident to confident.
1172+
1173+ Ensure that the confident contact is reported normally.
1174+ """
1175+ uhdev = self .uhdev
1176+ evdev = uhdev .get_evdev ()
1177+
1178+ self .confidence_change_assert_playback (uhdev , evdev , [
1179+ # t=0: Contact 0 == Down + confident; Contact 1 == Down + confident
1180+ # First and second finger confidently in contact
1181+ [(self .ContactIds (contact_id = 0 , tracking_id = 0 , slot_num = 0 ), True , True ),
1182+ (self .ContactIds (contact_id = 1 , tracking_id = 1 , slot_num = 1 ), True , True )],
1183+
1184+ # t=1: Contact 0 == Down + !confident; Contact 1 == Down + confident
1185+ # Firtst finger looses confidence
1186+ [(self .ContactIds (contact_id = 0 , tracking_id = - 1 , slot_num = 0 ), True , False ),
1187+ (self .ContactIds (contact_id = 1 , tracking_id = 1 , slot_num = 1 ), True , True )],
1188+
1189+ # t=2: Contact 0 == Down + confident; Contact 1 == Down + confident
1190+ # First finger gains confidence
1191+ [(self .ContactIds (contact_id = 0 , tracking_id = 2 , slot_num = 0 ), True , True ),
1192+ (self .ContactIds (contact_id = 1 , tracking_id = 1 , slot_num = 1 ), True , True )],
1193+
1194+ # t=3: Contact 0 == !Down + confident; Contact 1 == Down + confident
1195+ # First finger goes up
1196+ [(self .ContactIds (contact_id = 0 , tracking_id = - 1 , slot_num = 0 ), False , True ),
1197+ (self .ContactIds (contact_id = 1 , tracking_id = 1 , slot_num = 1 ), True , True )]
1198+ ])
0 commit comments