@@ -874,6 +874,229 @@ class USIPen(PenDigitizer):
874874 pass
875875
876876
877+ class XPPen_ArtistPro16Gen2_28bd_095b (PenDigitizer ):
878+ """
879+ Pen with two buttons and a rubber end, but which reports
880+ the second button as an eraser
881+ """
882+
883+ def __init__ (
884+ self ,
885+ name ,
886+ rdesc_str = None ,
887+ rdesc = None ,
888+ application = "Pen" ,
889+ physical = "Stylus" ,
890+ input_info = (BusType .USB , 0x28BD , 0x095B ),
891+ evdev_name_suffix = None ,
892+ ):
893+ super ().__init__ (
894+ name , rdesc_str , rdesc , application , physical , input_info , evdev_name_suffix
895+ )
896+ self .fields .append ("Secondary Barrel Switch" )
897+
898+ def move_to (self , pen , state , button ):
899+ # fill in the previous values
900+ if pen .current_state == PenState .PEN_IS_OUT_OF_RANGE :
901+ pen .restore ()
902+
903+ print (f"\n *** pen is moving to { state } ***" )
904+
905+ if state == PenState .PEN_IS_OUT_OF_RANGE :
906+ pen .backup ()
907+ pen .x = 0
908+ pen .y = 0
909+ pen .tipswitch = False
910+ pen .tippressure = 0
911+ pen .azimuth = 0
912+ pen .inrange = False
913+ pen .width = 0
914+ pen .height = 0
915+ pen .invert = False
916+ pen .eraser = False
917+ pen .xtilt = 0
918+ pen .ytilt = 0
919+ pen .twist = 0
920+ pen .barrelswitch = False
921+ elif state == PenState .PEN_IS_IN_RANGE :
922+ pen .tipswitch = False
923+ pen .inrange = True
924+ pen .invert = False
925+ pen .eraser = False
926+ pen .barrelswitch = False
927+ elif state == PenState .PEN_IS_IN_CONTACT :
928+ pen .tipswitch = True
929+ pen .inrange = True
930+ pen .invert = False
931+ pen .eraser = False
932+ pen .barrelswitch = False
933+ elif state == PenState .PEN_IS_IN_RANGE_WITH_BUTTON :
934+ pen .tipswitch = False
935+ pen .inrange = True
936+ pen .invert = False
937+ assert button is not None
938+ pen .barrelswitch = button == BtnPressed .PRIMARY_PRESSED
939+ pen .eraser = button == BtnPressed .SECONDARY_PRESSED
940+ elif state == PenState .PEN_IS_IN_CONTACT_WITH_BUTTON :
941+ pen .tipswitch = True
942+ pen .inrange = True
943+ pen .invert = False
944+ assert button is not None
945+ pen .barrelswitch = button == BtnPressed .PRIMARY_PRESSED
946+ pen .eraser = button == BtnPressed .SECONDARY_PRESSED
947+ elif state == PenState .PEN_IS_IN_RANGE_WITH_ERASING_INTENT :
948+ pen .tipswitch = False
949+ pen .inrange = True
950+ pen .invert = True
951+ pen .eraser = False
952+ pen .barrelswitch = False
953+ elif state == PenState .PEN_IS_ERASING :
954+ pen .tipswitch = True
955+ pen .inrange = True
956+ pen .invert = True
957+ pen .eraser = False
958+ pen .barrelswitch = False
959+
960+ pen .xtilt = 0
961+ pen .ytilt = 0
962+ pen .current_state = state
963+
964+
965+ class XPPen_Artist24_28bd_093a (PenDigitizer ):
966+ """
967+ Pen that reports secondary barrel switch through eraser
968+ """
969+
970+ def __init__ (
971+ self ,
972+ name ,
973+ rdesc_str = None ,
974+ rdesc = None ,
975+ application = "Pen" ,
976+ physical = "Stylus" ,
977+ input_info = (BusType .USB , 0x28BD , 0x093A ),
978+ evdev_name_suffix = None ,
979+ ):
980+ super ().__init__ (
981+ name , rdesc_str , rdesc , application , physical , input_info , evdev_name_suffix
982+ )
983+ self .fields .append ("Secondary Barrel Switch" )
984+ self .previous_state = PenState .PEN_IS_OUT_OF_RANGE
985+
986+ def move_to (self , pen , state , button , debug = True ):
987+ # fill in the previous values
988+ if pen .current_state == PenState .PEN_IS_OUT_OF_RANGE :
989+ pen .restore ()
990+
991+ if debug :
992+ print (f"\n *** pen is moving to { state } ***" )
993+
994+ if state == PenState .PEN_IS_OUT_OF_RANGE :
995+ pen .backup ()
996+ pen .tipswitch = False
997+ pen .tippressure = 0
998+ pen .azimuth = 0
999+ pen .inrange = False
1000+ pen .width = 0
1001+ pen .height = 0
1002+ pen .invert = False
1003+ pen .eraser = False
1004+ pen .xtilt = 0
1005+ pen .ytilt = 0
1006+ pen .twist = 0
1007+ pen .barrelswitch = False
1008+ elif state == PenState .PEN_IS_IN_RANGE :
1009+ pen .tipswitch = False
1010+ pen .inrange = True
1011+ pen .invert = False
1012+ pen .eraser = False
1013+ pen .barrelswitch = False
1014+ elif state == PenState .PEN_IS_IN_CONTACT :
1015+ pen .tipswitch = True
1016+ pen .inrange = True
1017+ pen .invert = False
1018+ pen .eraser = False
1019+ pen .barrelswitch = False
1020+ elif state == PenState .PEN_IS_IN_RANGE_WITH_BUTTON :
1021+ pen .tipswitch = False
1022+ pen .inrange = True
1023+ pen .invert = False
1024+ assert button is not None
1025+ pen .barrelswitch = button == BtnPressed .PRIMARY_PRESSED
1026+ pen .eraser = button == BtnPressed .SECONDARY_PRESSED
1027+ elif state == PenState .PEN_IS_IN_CONTACT_WITH_BUTTON :
1028+ pen .tipswitch = True
1029+ pen .inrange = True
1030+ pen .invert = False
1031+ assert button is not None
1032+ pen .barrelswitch = button == BtnPressed .PRIMARY_PRESSED
1033+ pen .eraser = button == BtnPressed .SECONDARY_PRESSED
1034+
1035+ pen .current_state = state
1036+
1037+ def send_intermediate_state (self , pen , state , button ):
1038+ intermediate_pen = copy .copy (pen )
1039+ self .move_to (intermediate_pen , state , button , debug = False )
1040+ return super ().event (intermediate_pen , button )
1041+
1042+ def event (self , pen , button ):
1043+ rs = []
1044+
1045+ # the pen reliably sends in-range events in a normal case (non emulation of eraser mode)
1046+ if self .previous_state == PenState .PEN_IS_IN_CONTACT :
1047+ if pen .current_state == PenState .PEN_IS_OUT_OF_RANGE :
1048+ rs .extend (
1049+ self .send_intermediate_state (pen , PenState .PEN_IS_IN_RANGE , button )
1050+ )
1051+
1052+ if button == BtnPressed .SECONDARY_PRESSED :
1053+ if self .previous_state == PenState .PEN_IS_IN_RANGE :
1054+ if pen .current_state == PenState .PEN_IS_IN_RANGE_WITH_BUTTON :
1055+ rs .extend (
1056+ self .send_intermediate_state (
1057+ pen , PenState .PEN_IS_OUT_OF_RANGE , button
1058+ )
1059+ )
1060+
1061+ if self .previous_state == PenState .PEN_IS_IN_RANGE_WITH_BUTTON :
1062+ if pen .current_state == PenState .PEN_IS_IN_RANGE :
1063+ rs .extend (
1064+ self .send_intermediate_state (
1065+ pen , PenState .PEN_IS_OUT_OF_RANGE , button
1066+ )
1067+ )
1068+
1069+ if self .previous_state == PenState .PEN_IS_IN_CONTACT :
1070+ if pen .current_state == PenState .PEN_IS_IN_CONTACT_WITH_BUTTON :
1071+ rs .extend (
1072+ self .send_intermediate_state (
1073+ pen , PenState .PEN_IS_OUT_OF_RANGE , button
1074+ )
1075+ )
1076+ rs .extend (
1077+ self .send_intermediate_state (
1078+ pen , PenState .PEN_IS_IN_RANGE_WITH_BUTTON , button
1079+ )
1080+ )
1081+
1082+ if self .previous_state == PenState .PEN_IS_IN_CONTACT_WITH_BUTTON :
1083+ if pen .current_state == PenState .PEN_IS_IN_CONTACT :
1084+ rs .extend (
1085+ self .send_intermediate_state (
1086+ pen , PenState .PEN_IS_OUT_OF_RANGE , button
1087+ )
1088+ )
1089+ rs .extend (
1090+ self .send_intermediate_state (
1091+ pen , PenState .PEN_IS_IN_RANGE , button
1092+ )
1093+ )
1094+
1095+ rs .extend (super ().event (pen , button ))
1096+ self .previous_state = pen .current_state
1097+ return rs
1098+
1099+
8771100################################################################################
8781101#
8791102# Windows 7 compatible devices
@@ -1052,3 +1275,26 @@ def create_device(self):
10521275 rdesc = "05 0d 09 04 a1 01 85 01 09 22 a1 02 55 0e 65 11 35 00 15 00 09 42 25 01 75 01 95 01 81 02 25 7f 09 30 75 07 81 42 95 01 75 08 09 51 81 02 75 10 05 01 26 04 20 46 e6 09 09 30 81 02 26 60 15 46 9a 06 09 31 81 02 05 0d 55 0f 75 08 25 ff 45 ff 09 48 81 42 09 49 81 42 55 0e c0 09 22 a1 02 09 42 25 01 75 01 95 01 81 02 25 7f 09 30 75 07 81 42 95 01 75 08 09 51 81 02 75 10 05 01 26 04 20 46 e6 09 09 30 81 02 26 60 15 46 9a 06 09 31 81 02 05 0d 55 0f 75 08 25 ff 45 ff 09 48 81 42 09 49 81 42 55 0e c0 09 22 a1 02 09 42 25 01 75 01 95 01 81 02 25 7f 09 30 75 07 81 42 95 01 75 08 09 51 81 02 75 10 05 01 26 04 20 46 e6 09 09 30 81 02 26 60 15 46 9a 06 09 31 81 02 05 0d 55 0f 75 08 25 ff 45 ff 09 48 81 42 09 49 81 42 55 0e c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 30 75 07 81 42 75 08 09 51 95 01 81 02 05 01 26 04 20 75 10 55 0e 65 11 09 30 35 00 46 e6 09 81 02 26 60 15 46 9a 06 09 31 81 02 05 0d 55 0f 75 08 25 ff 45 ff 09 48 81 42 09 49 81 42 55 0e c0 09 22 a1 02 09 42 15 00 25 01 75 01 95 01 81 02 25 7f 09 30 75 07 81 42 75 08 09 51 95 01 81 02 05 01 26 04 20 75 10 55 0e 65 11 09 30 35 00 46 e6 09 81 02 26 60 15 46 9a 06 09 31 81 02 05 0d 55 0f 75 08 25 ff 45 ff 09 48 81 42 09 49 81 42 55 0e c0 09 54 15 00 25 7f 75 08 95 01 81 02 85 02 09 55 95 01 25 0a b1 02 85 03 06 00 ff 09 c5 15 00 26 ff 00 75 08 96 00 01 b1 02 c0 05 0d 09 02 a1 01 09 20 a1 00 85 08 05 01 a4 09 30 35 00 46 e6 09 15 00 26 04 20 55 0d 65 13 75 10 95 01 81 02 09 31 46 9a 06 26 60 15 81 02 b4 05 0d 09 38 95 01 75 08 15 00 25 01 81 02 09 30 75 10 26 ff 0f 81 02 09 31 81 02 09 42 09 44 09 5a 09 3c 09 45 09 32 75 01 95 06 25 01 81 02 95 02 81 03 09 3d 55 0e 65 14 36 d8 dc 46 28 23 16 d8 dc 26 28 23 95 01 75 10 81 02 09 3e 81 02 09 41 15 00 27 a0 8c 00 00 35 00 47 a0 8c 00 00 81 02 05 20 0a 53 04 65 00 16 01 f8 26 ff 07 75 10 95 01 81 02 0a 54 04 81 02 0a 55 04 81 02 0a 57 04 81 02 0a 58 04 81 02 0a 59 04 81 02 0a 72 04 81 02 0a 73 04 81 02 0a 74 04 81 02 05 0d 09 3b 15 00 25 64 75 08 81 02 09 5b 25 ff 75 40 81 02 06 00 ff 09 5b 75 20 81 02 05 0d 09 5c 26 ff 00 75 08 81 02 09 5e 81 02 09 70 a1 02 15 01 25 06 09 72 09 73 09 74 09 75 09 76 09 77 81 20 c0 06 00 ff 09 01 15 00 27 ff ff 00 00 75 10 95 01 81 02 85 09 09 81 a1 02 09 81 15 01 25 04 09 82 09 83 09 84 09 85 81 20 c0 85 10 09 5c a1 02 15 00 25 01 75 08 95 01 09 38 b1 02 09 5c 26 ff 00 b1 02 09 5d 75 01 95 01 25 01 b1 02 95 07 b1 03 c0 85 11 09 5e a1 02 09 38 15 00 25 01 75 08 95 01 b1 02 09 5e 26 ff 00 b1 02 09 5f 75 01 25 01 b1 02 75 07 b1 03 c0 85 12 09 70 a1 02 75 08 95 01 15 00 25 01 09 38 b1 02 09 70 a1 02 25 06 09 72 09 73 09 74 09 75 09 76 09 77 b1 20 c0 09 71 75 01 25 01 b1 02 75 07 b1 03 c0 85 13 09 80 15 00 25 ff 75 40 95 01 b1 02 85 14 09 44 a1 02 09 38 75 08 95 01 25 01 b1 02 15 01 25 03 09 44 a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 09 5a a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 09 45 a1 02 09 a4 09 44 09 5a 09 45 09 a3 b1 20 c0 c0 85 15 75 08 95 01 05 0d 09 90 a1 02 09 38 25 01 b1 02 09 91 75 10 26 ff 0f b1 02 09 92 75 40 25 ff b1 02 05 06 09 2a 75 08 26 ff 00 a1 02 09 2d b1 02 09 2e b1 02 c0 c0 85 16 05 06 09 2b a1 02 05 0d 25 01 09 38 b1 02 05 06 09 2b a1 02 09 2d 26 ff 00 b1 02 09 2e b1 02 c0 c0 85 17 06 00 ff 09 01 a1 02 05 0d 09 38 75 08 95 01 25 01 b1 02 06 00 ff 09 01 75 10 27 ff ff 00 00 b1 02 c0 85 18 05 0d 09 38 75 08 95 01 15 00 25 01 b1 02 c0 c0 06 f0 ff 09 01 a1 01 85 0e 09 01 15 00 25 ff 75 08 95 40 91 02 09 01 15 00 25 ff 75 08 95 40 81 02 c0" ,
10531276 input_info = (BusType .I2C , 0x27C6 , 0x0E00 ),
10541277 )
1278+
1279+
1280+ class TestXPPen_ArtistPro16Gen2_28bd_095b (BaseTest .TestTablet ):
1281+ hid_bpfs = [("XPPen__ArtistPro16Gen2.bpf.o" , True )]
1282+
1283+ def create_device (self ):
1284+ dev = XPPen_ArtistPro16Gen2_28bd_095b (
1285+ "uhid test XPPen Artist Pro 16 Gen2 28bd 095b" ,
1286+ rdesc = "05 0d 09 02 a1 01 85 07 09 20 a1 00 09 42 09 44 09 45 09 3c 15 00 25 01 75 01 95 04 81 02 95 01 81 03 09 32 15 00 25 01 95 01 81 02 95 02 81 03 75 10 95 01 35 00 a4 05 01 09 30 65 13 55 0d 46 ff 34 26 ff 7f 81 02 09 31 46 20 21 26 ff 7f 81 02 b4 09 30 45 00 26 ff 3f 81 42 09 3d 15 81 25 7f 75 08 95 01 81 02 09 3e 15 81 25 7f 81 02 c0 c0" ,
1287+ input_info = (BusType .USB , 0x28BD , 0x095B ),
1288+ )
1289+ return dev
1290+
1291+
1292+ class TestXPPen_Artist24_28bd_093a (BaseTest .TestTablet ):
1293+ hid_bpfs = [("XPPen__Artist24.bpf.o" , True )]
1294+
1295+ def create_device (self ):
1296+ return XPPen_Artist24_28bd_093a (
1297+ "uhid test XPPen Artist 24 28bd 093a" ,
1298+ rdesc = "05 0d 09 02 a1 01 85 07 09 20 a1 00 09 42 09 44 09 45 15 00 25 01 75 01 95 03 81 02 95 02 81 03 09 32 95 01 81 02 95 02 81 03 75 10 95 01 35 00 a4 05 01 09 30 65 13 55 0d 46 f0 50 26 ff 7f 81 02 09 31 46 91 2d 26 ff 7f 81 02 b4 09 30 45 00 26 ff 1f 81 42 09 3d 15 81 25 7f 75 08 95 01 81 02 09 3e 15 81 25 7f 81 02 c0 c0" ,
1299+ input_info = (BusType .USB , 0x28BD , 0x093A ),
1300+ )
0 commit comments