-
-
Notifications
You must be signed in to change notification settings - Fork 418
Multi-touch touchscreen support (#123) #297
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
abadb2b
1f00971
ba4f2d9
dfe04b8
e41e732
87558be
7b4e634
827bf37
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| /* | ||
| Copyright (c) 2021 ilufang | ||
| See the readme for credit to other people. | ||
|
|
||
| Multi-touch touchscreen example | ||
| Draw 7 parallel lines across the screen. | ||
| Open Microsoft Whiteboard, select a brush and press the button. | ||
|
|
||
| See HID Project documentation for more infos | ||
| https://github.com/NicoHood/HID/wiki | ||
| */ | ||
|
|
||
| #include <HID-Project.h> | ||
|
|
||
| const int pinButton = 2; | ||
|
|
||
| void setup() { | ||
| pinMode(pinButton, INPUT_PULLUP); | ||
| Touchscreen.begin(); | ||
| } | ||
|
|
||
| void loop() { | ||
| while(digitalRead(pinButton)); | ||
|
|
||
| int16_t x = 2000, y = 2000; | ||
|
|
||
| for (; x <= 8000; x+=10) { | ||
| for (int i = 0; i < 7; i++) { | ||
| Touchscreen.setFinger(i, x, y+i*1000, (i+1)*15); | ||
| } | ||
| Touchscreen.send(); | ||
| delay(10); | ||
| } | ||
|
|
||
| for (int i = 0; i < 7; i++) { | ||
| Touchscreen.releaseFinger(i); | ||
| } | ||
| Touchscreen.send(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| /* | ||
| Copyright (c) 2021 ilufang | ||
| See the readme for credit to other people. | ||
|
|
||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
|
|
||
| The above copyright notice and this permission notice shall be included in | ||
| all copies or substantial portions of the Software. | ||
|
|
||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| THE SOFTWARE. | ||
| */ | ||
|
|
||
| // Include guard | ||
| #pragma once | ||
|
|
||
| /// Maximum amount of fingers supported | ||
| #define HID_TOUCHSCREEN_MAXFINGERS 10 | ||
| /// Number of fingers in a single report | ||
| #define HID_TOUCHSCREEN_REPORTFINGERS 2 | ||
|
|
||
| // A report will always be the same size, even if you report fewer fingers than | ||
| // REPORTFINGERS. The unused finger entries will simply be zero. If more fingers | ||
| // are present than REPORTFINGERS, multiple reports will be sent to report all | ||
| // fingers. This is know as "Hybrid Mode" on MSDN. The number of supported | ||
| // fingers identified by Windows will still be MAXFINGERS. More than MAXFINGERS | ||
| // contacts may be ignored by Windows even with hybrid mode. | ||
|
|
||
|
|
||
| // Bit-mask flags for 'status' in the HID report | ||
| #define HID_TOUCHSCREEN_TOUCH_CONTACT 0x01 | ||
| #define HID_TOUCHSCREEN_TOUCH_IN_RANGE 0x02 | ||
|
|
||
| typedef struct ATTRIBUTE_PACKED { | ||
| uint8_t status; | ||
| uint8_t pressure; | ||
| uint16_t x; | ||
| uint16_t y; | ||
| } HID_Touchscreen_Finger_t; | ||
|
|
||
| typedef union ATTRIBUTE_PACKED { | ||
| uint8_t whole8[0]; | ||
| uint16_t whole16[0]; | ||
| uint32_t whole32[0]; | ||
| struct ATTRIBUTE_PACKED { | ||
| uint8_t count; | ||
| struct ATTRIBUTE_PACKED { | ||
| uint8_t identifier; | ||
| HID_Touchscreen_Finger_t touch; | ||
| } contacts[HID_TOUCHSCREEN_REPORTFINGERS]; | ||
| }; | ||
| } HID_TouchscreenReport_Data_t; | ||
|
|
||
| class TouchscreenAPI | ||
| { | ||
| public: | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've not written c++ for a while, but why dont you have a constructor? I can see, that I am clearing the data in the other constructors, why dont we do that here as well?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Implicit default constructor is specified to zero everything in C++. But for consistency I added one anyway.
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I remember that this enlarges the sketches flash, is that still correct? I am wondering why I've added it in the first place then...
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I shouldn't as it's the exact same behavior as the default constructor. I believe the compiler can figure out that static linking is all required for the global instances and no extra assign-to-zero code would be generated.
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you test that real quick? It it adds additional overhead we should remove it from all other classes as well, I'd say.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The explicit initializer list in the constructor (not the constructor itself) made the example program text 32 bytes larger. In disassembly gcc inserted runtime code to zero memory. If the commonsense in x86 that global data are zeroed during exe image loading also applies to AVR (in my test instance it does), then it is preferable to not include an explicit initializer list. I'm afraid I do not have the free time to perform project-wide polishing for you. I've removed the initializer list from this case. If there are no other touchscreen-specific issues, could you merge and publish this and then perform any other optimizations you'd like? Thanks. |
||
|
|
||
| inline TouchscreenAPI(); | ||
|
|
||
| inline void begin(); | ||
| inline void end(); | ||
|
|
||
NicoHood marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /** | ||
| * Set contact status for a finger in the internal data structure. You must | ||
| * call send manually after setting all fingers to flush them through USB. | ||
| * | ||
| * @param id Finger id. Must be in the range of 0-MAXFINGERS. Same finger | ||
| * must have same id throughout contact. Allocations does not need | ||
| * to be continuous. | ||
| * @param x, y Coordinates. Range 0-10000. (0,0) is top-left on Windows. | ||
| * @param pressure Contact pressure. Range 0-127. When set to 0, the touch | ||
| * is reported as hovering (in-range) | ||
| * @return 1 if success. 0 if id is out-of-bounds | ||
| */ | ||
| inline int setFinger(uint8_t id, uint16_t x, uint16_t y, uint8_t pressure=100); | ||
|
|
||
| /** | ||
| * Release finger in the internal data structure. You must call send | ||
| * manually after setting all fingers to flush them through USB. | ||
| * | ||
| * @param id Finger id. Must be in the range of 0-MAXFINGERS. Same finger | ||
| * must have same id throughout contact. Allocations does not need | ||
| * to be continuous. | ||
| * @return 1 if success. 0 if id is out-of-bounds | ||
| */ | ||
| inline int releaseFinger(uint8_t id); | ||
|
|
||
| /** | ||
| * Generates an HID report reflecting the currently recorded touch status | ||
| * and send through USB. | ||
| */ | ||
| inline int send(); | ||
|
|
||
| /// Send generated report. Needs to be implemented in a lower level | ||
| virtual int sendReport(void *report, int length) = 0; | ||
| inline int sendReport(HID_TouchscreenReport_Data_t &report); | ||
|
|
||
| protected: | ||
|
|
||
| /// Internal records of the current touch statuses. Status in this struct | ||
| /// is used only internally and differs from the one in the report | ||
| HID_Touchscreen_Finger_t _fingers[HID_TOUCHSCREEN_MAXFINGERS]; | ||
| /// Number of active contacts, including just release contacts | ||
| uint8_t _fingers_count; | ||
| }; | ||
|
|
||
| // Implementation is inline | ||
| #include "TouchscreenAPI.hpp" | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| /* | ||
| Copyright (c) 2021 ilufang | ||
| See the readme for credit to other people. | ||
|
|
||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
|
|
||
| The above copyright notice and this permission notice shall be included in | ||
| all copies or substantial portions of the Software. | ||
|
|
||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
| THE SOFTWARE. | ||
| */ | ||
|
|
||
| // Include guard | ||
| #pragma once | ||
|
|
||
| enum _finger_status_t { | ||
| _MT_STATE_INACTIVE = 0, | ||
| _MT_STATE_CONTACT, | ||
| _MT_STATE_RELEASED | ||
| }; | ||
|
|
||
| TouchscreenAPI::TouchscreenAPI() { | ||
| // Empty. Default zero initialization at loading. Do not manually instantiate | ||
| } | ||
|
|
||
| void TouchscreenAPI::begin() { | ||
| end(); | ||
| } | ||
|
|
||
| void TouchscreenAPI::end() { | ||
| for (int i = 0; i < HID_TOUCHSCREEN_MAXFINGERS; i++) { | ||
| releaseFinger(i); | ||
| } | ||
| send(); | ||
| } | ||
|
|
||
| int TouchscreenAPI::setFinger(uint8_t id, uint16_t x, uint16_t y, uint8_t pressure) { | ||
| if (id >= HID_TOUCHSCREEN_MAXFINGERS) { | ||
| return 0; | ||
| } | ||
| if (_fingers[id].status == _MT_STATE_INACTIVE) { | ||
| _fingers_count++; | ||
| } | ||
| _fingers[id].status = _MT_STATE_CONTACT; | ||
| _fingers[id].pressure = pressure; | ||
| _fingers[id].x = x; | ||
| _fingers[id].y = y; | ||
| return 1; | ||
| } | ||
|
|
||
| int TouchscreenAPI::releaseFinger(uint8_t id) { | ||
| if (id >= HID_TOUCHSCREEN_MAXFINGERS) { | ||
| return 0; | ||
| } | ||
| if (_fingers[id].status == _MT_STATE_CONTACT) { | ||
| _fingers[id].status = _MT_STATE_RELEASED; | ||
| } | ||
| return 1; | ||
| } | ||
|
|
||
| int TouchscreenAPI::send() { | ||
| int ret = 0; | ||
| HID_TouchscreenReport_Data_t report; | ||
|
|
||
| // Craft report(s) | ||
| report.count = _fingers_count; | ||
|
|
||
| int rptentry=0; | ||
| for (int i = 0; i < HID_TOUCHSCREEN_MAXFINGERS; i++) { | ||
| if (_fingers[i].status == _MT_STATE_INACTIVE) { | ||
| continue; | ||
NicoHood marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| report.contacts[rptentry].identifier = i; // valid for first report only | ||
|
|
||
| if (_fingers[i].status == _MT_STATE_RELEASED) { | ||
| // Released contacts need to be reported once with TipSW=0 | ||
| report.contacts[rptentry].touch = {}; | ||
| _fingers_count--; | ||
| _fingers[i].status = _MT_STATE_INACTIVE; | ||
| } else { | ||
| // Active contacts must be reported even when not moved | ||
| report.contacts[rptentry].touch.status = HID_TOUCHSCREEN_TOUCH_IN_RANGE; | ||
| if (_fingers[i].pressure > 0) { | ||
| report.contacts[rptentry].touch.status |= HID_TOUCHSCREEN_TOUCH_CONTACT; | ||
| } | ||
| report.contacts[rptentry].touch.x = _fingers[i].x; | ||
| report.contacts[rptentry].touch.y = _fingers[i].y; | ||
| report.contacts[rptentry].touch.pressure = _fingers[i].pressure; | ||
| } | ||
|
|
||
| rptentry++; | ||
| if (rptentry == HID_TOUCHSCREEN_REPORTFINGERS) { | ||
| // Report full. Send now. | ||
| // If there are more contacts, they will be sent in subsequent | ||
| // reports with contact count set to 0 | ||
| // See "Hybrid Mode" on MSDN docs | ||
| ret += sendReport(report); | ||
| rptentry = 0; | ||
| report.count = 0; | ||
| } | ||
| } | ||
|
|
||
| if (rptentry != 0) { | ||
| // Send remaining touches | ||
| for (; rptentry != HID_TOUCHSCREEN_REPORTFINGERS; rptentry++) { | ||
| report.contacts[rptentry] = {}; | ||
| } | ||
| ret += sendReport(report); | ||
| } | ||
|
|
||
| return ret; | ||
| } | ||
|
|
||
| int TouchscreenAPI::sendReport(HID_TouchscreenReport_Data_t &report) { | ||
| return sendReport(&report, sizeof(HID_TouchscreenReport_Data_t)); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.