1 # Copyright 2014 The Chromium Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 """Implementation of a USB HID mouse. 6 7 Two classes are provided by this module. The MouseFeature class implements 8 the core functionality of a HID mouse and can be included in any HID gadget. 9 The MouseGadget class implements an example mouse gadget. 10 """ 11 12 import struct 13 14 import hid_constants 15 import hid_descriptors 16 import hid_gadget 17 import usb_constants 18 19 20 class MouseFeature(hid_gadget.HidFeature): 21 """HID feature implementation for a mouse. 22 23 REPORT_DESC provides an example HID report descriptor for a device including 24 this functionality. 25 """ 26 27 REPORT_DESC = hid_descriptors.ReportDescriptor( 28 hid_descriptors.UsagePage(0x01), # Generic Desktop 29 hid_descriptors.Usage(0x02), # Mouse 30 hid_descriptors.Collection( 31 hid_constants.CollectionType.APPLICATION, 32 hid_descriptors.Usage(0x01), # Pointer 33 hid_descriptors.Collection( 34 hid_constants.CollectionType.PHYSICAL, 35 hid_descriptors.UsagePage(0x09), # Buttons 36 hid_descriptors.UsageMinimum(1), 37 hid_descriptors.UsageMaximum(3), 38 hid_descriptors.LogicalMinimum(0, force_length=1), 39 hid_descriptors.LogicalMaximum(1), 40 hid_descriptors.ReportCount(3), 41 hid_descriptors.ReportSize(1), 42 hid_descriptors.Input(hid_descriptors.Data, 43 hid_descriptors.Variable, 44 hid_descriptors.Absolute), 45 hid_descriptors.ReportCount(1), 46 hid_descriptors.ReportSize(5), 47 hid_descriptors.Input(hid_descriptors.Constant), 48 hid_descriptors.UsagePage(0x01), # Generic Desktop 49 hid_descriptors.Usage(0x30), # X 50 hid_descriptors.Usage(0x31), # Y 51 hid_descriptors.LogicalMinimum(0x81), # -127 52 hid_descriptors.LogicalMaximum(127), 53 hid_descriptors.ReportSize(8), 54 hid_descriptors.ReportCount(2), 55 hid_descriptors.Input(hid_descriptors.Data, 56 hid_descriptors.Variable, 57 hid_descriptors.Relative) 58 ) 59 ) 60 ) 61 62 def __init__(self): 63 super(MouseFeature, self).__init__() 64 self._buttons = 0 65 66 def ButtonDown(self, button): 67 self._buttons |= button 68 if self.IsConnected(): 69 self.SendReport(self.EncodeInputReport()) 70 71 def ButtonUp(self, button): 72 self._buttons &= ~button 73 if self.IsConnected(): 74 self.SendReport(self.EncodeInputReport()) 75 76 def Move(self, x_displacement, y_displacement): 77 if self.IsConnected(): 78 self.SendReport(self.EncodeInputReport(x_displacement, y_displacement)) 79 80 def EncodeInputReport(self, x_displacement=0, y_displacement=0): 81 return struct.pack('Bbb', self._buttons, x_displacement, y_displacement) 82 83 def GetInputReport(self): 84 """Construct an input report. 85 86 See Device Class Definition for Human Interface Devices (HID) Version 1.11 87 Appendix B.2. 88 89 Returns: 90 A packed input report. 91 """ 92 return self.EncodeInputReport() 93 94 95 class MouseGadget(hid_gadget.HidGadget): 96 """USB gadget implementation of a HID mouse.""" 97 98 def __init__(self): 99 self._feature = MouseFeature() 100 super(MouseGadget, self).__init__( 101 report_desc=MouseFeature.REPORT_DESC, 102 features={0: self._feature}, 103 packet_size=8, 104 interval_ms=1, 105 out_endpoint=False, 106 vendor_id=usb_constants.VendorID.GOOGLE, 107 product_id=usb_constants.ProductID.GOOGLE_MOUSE_GADGET, 108 device_version=0x0100) 109 self.AddStringDescriptor(1, 'Google Inc.') 110 self.AddStringDescriptor(2, 'Mouse Gadget') 111 112 def ButtonDown(self, button): 113 self._feature.ButtonDown(button) 114 115 def ButtonUp(self, button): 116 self._feature.ButtonUp(button) 117 118 def Move(self, x_displacement, y_displacement): 119 self._feature.Move(x_displacement, y_displacement) 120 121 122 def RegisterHandlers(): 123 """Registers web request handlers with the application server.""" 124 125 from tornado import web 126 127 class WebConfigureHandler(web.RequestHandler): 128 129 def post(self): 130 gadget = MouseGadget() 131 server.SwitchGadget(gadget) 132 133 class WebClickHandler(web.RequestHandler): 134 135 def post(self): 136 BUTTONS = { 137 '1': hid_constants.Mouse.BUTTON_1, 138 '2': hid_constants.Mouse.BUTTON_2, 139 '3': hid_constants.Mouse.BUTTON_3, 140 } 141 142 button = BUTTONS[self.get_argument('button')] 143 server.gadget.ButtonDown(button) 144 server.gadget.ButtonUp(button) 145 146 class WebMoveHandler(web.RequestHandler): 147 148 def post(self): 149 x = int(self.get_argument('x')) 150 y = int(self.get_argument('y')) 151 server.gadget.Move(x, y) 152 153 import server 154 server.app.add_handlers('.*$', [ 155 (r'/mouse/configure', WebConfigureHandler), 156 (r'/mouse/move', WebMoveHandler), 157 (r'/mouse/click', WebClickHandler), 158 ]) 159