27
27
)
28
28
29
29
import attr
30
+ from collections import namedtuple
30
31
from enum import Enum
31
32
from hidtools .hut import HUT
32
33
from hidtools .hid import HidUnit
@@ -862,13 +863,66 @@ def offset_rotation(value):
862
863
863
864
864
865
class TestDTH2452Tablet (test_multitouch .BaseTest .TestMultitouch , TouchTabletTest ):
866
+ ContactIds = namedtuple ("ContactIds" , "contact_id, tracking_id, slot_num" )
867
+
865
868
def create_device (self ):
866
869
return test_multitouch .Digitizer (
867
870
"DTH 2452" ,
868
871
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" ,
869
872
input_info = (0x3 , 0x056A , 0x0383 ),
870
873
)
871
874
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
+
872
926
def test_contact_id_0 (self ):
873
927
"""
874
928
Bring a finger in contact with the tablet, then hold it down and remove it.
@@ -919,4 +973,226 @@ def test_confidence_false(self):
919
973
920
974
slot = self .get_slot (uhdev , t0 , 0 )
921
975
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