mirror of
https://github.com/go-vgo/robotgo.git
synced 2025-06-17 21:43:55 +00:00
Compare commits
1198 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
dd208c6dd1 | ||
![]() |
ae4390f607 | ||
![]() |
48706562fc | ||
![]() |
023426ab13 | ||
![]() |
d34a1491cb | ||
![]() |
c431c8f974 | ||
![]() |
a183783e9c | ||
![]() |
c1115cebc7 | ||
![]() |
a8c387a070 | ||
![]() |
3eef3b5879 | ||
![]() |
b731094f61 | ||
![]() |
8d4679db07 | ||
![]() |
02c668a946 | ||
![]() |
5c2864485d | ||
![]() |
f101b10497 | ||
![]() |
73c07dc991 | ||
![]() |
fc47bc8216 | ||
![]() |
912ddcbfe4 | ||
![]() |
57b5e5bca2 | ||
![]() |
8031f7f526 | ||
![]() |
36bc85ad19 | ||
![]() |
5fc2bd4c73 | ||
![]() |
47abfac5ee | ||
![]() |
df0731c4df | ||
![]() |
03432155c1 | ||
![]() |
6a1f060a8c | ||
![]() |
e924f25fe1 | ||
![]() |
d300eedf54 | ||
![]() |
c07f3f0171 | ||
![]() |
0b18fa5058 | ||
![]() |
471f7ba05f | ||
![]() |
a84a195c4c | ||
![]() |
c8df366f89 | ||
![]() |
70f811ce53 | ||
![]() |
ecc260ea31 | ||
![]() |
78d01703b8 | ||
![]() |
47cde13dab | ||
![]() |
8db59aac2d | ||
![]() |
217d6cf1f1 | ||
![]() |
75fd24ea0a | ||
![]() |
bed6776ca2 | ||
![]() |
1923d7bb48 | ||
![]() |
ead43d062e | ||
![]() |
3258566802 | ||
![]() |
0de26ecee4 | ||
![]() |
c48e3d072c | ||
![]() |
b1ad7db6be | ||
![]() |
0110b85d40 | ||
![]() |
3e5b10f720 | ||
![]() |
cb51e2aa10 | ||
![]() |
85f2702683 | ||
![]() |
5b9871b063 | ||
![]() |
b201ab9f80 | ||
![]() |
d3e364fa26 | ||
![]() |
1f437d12e4 | ||
![]() |
c5110d0ac8 | ||
![]() |
f98b4c52bd | ||
![]() |
f1db2ede4a | ||
![]() |
d90493e63e | ||
![]() |
63eb594ea5 | ||
![]() |
8a990ac042 | ||
![]() |
bf1758b424 | ||
![]() |
9424b71a73 | ||
![]() |
6c137427db | ||
![]() |
ffbf4f875b | ||
![]() |
05c54c6f25 | ||
![]() |
3171c5bdff | ||
![]() |
b718cf4a8c | ||
![]() |
bfc28d12fa | ||
![]() |
e9421b58b6 | ||
![]() |
4d780b9908 | ||
![]() |
39818d41fa | ||
![]() |
eacf74f488 | ||
![]() |
61b77ce3e1 | ||
![]() |
01e4fffcb8 | ||
![]() |
e2f5f54804 | ||
![]() |
f22f063926 | ||
![]() |
722feb7ba3 | ||
![]() |
dbf0d84f12 | ||
![]() |
b8a6b1778e | ||
![]() |
a780825c09 | ||
![]() |
de675b4dd2 | ||
![]() |
ef191f2dfd | ||
![]() |
44b2715de0 | ||
![]() |
90f41269da | ||
![]() |
8a5ca54915 | ||
![]() |
5b0ac98ef3 | ||
![]() |
c2b5c864d2 | ||
![]() |
0c4055bfee | ||
![]() |
af32110521 | ||
![]() |
5c3cbd6e17 | ||
![]() |
3182fb9d73 | ||
![]() |
6fa2f418fa | ||
![]() |
8e7a413ec6 | ||
![]() |
8ce3a5dff0 | ||
![]() |
8ea126d35b | ||
![]() |
c62f7fd81e | ||
![]() |
92fd032685 | ||
![]() |
ffcf6b26cd | ||
![]() |
451bb91209 | ||
![]() |
cbca6d08c7 | ||
![]() |
d249cdcd9e | ||
![]() |
4140463ed1 | ||
![]() |
186bceb9d0 | ||
![]() |
01212c5684 | ||
![]() |
07dd53a897 | ||
![]() |
9afa311121 | ||
![]() |
94be6c56c1 | ||
![]() |
e0583e83ec | ||
![]() |
6a2ffab625 | ||
![]() |
679aef0f70 | ||
![]() |
cc6f1934f9 | ||
![]() |
4bdacf4187 | ||
![]() |
c8d35acd24 | ||
![]() |
e4848b4608 | ||
![]() |
f47571eaae | ||
![]() |
dd149367b3 | ||
![]() |
da736a93ee | ||
![]() |
73a40e7861 | ||
![]() |
5e5c4ddbfa | ||
![]() |
23d3df9027 | ||
![]() |
bb76af18f0 | ||
![]() |
b13320884a | ||
![]() |
df1617d894 | ||
![]() |
b20007917d | ||
![]() |
f830c8ed13 | ||
![]() |
736fa1cc26 | ||
![]() |
9f8d308eaa | ||
![]() |
95be5f752f | ||
![]() |
4bc64ebf67 | ||
![]() |
bdae1a7759 | ||
![]() |
c9e1c3db67 | ||
![]() |
e8b3975205 | ||
![]() |
f4f4a3d319 | ||
![]() |
3052272818 | ||
![]() |
6313e52156 | ||
![]() |
f911550742 | ||
![]() |
2f936e1f49 | ||
![]() |
7aa5beb8f7 | ||
![]() |
4eea01b88d | ||
![]() |
7103f6b891 | ||
![]() |
bb90d71b5f | ||
![]() |
0ef48f1c4d | ||
![]() |
be6984fd34 | ||
![]() |
2f2ed595f6 | ||
![]() |
5079db2df7 | ||
![]() |
469cd4da5e | ||
![]() |
a8faa0e906 | ||
![]() |
0293ab5d0c | ||
![]() |
fc5dac6f8a | ||
![]() |
4bf054f47a | ||
![]() |
c3cda41c2d | ||
![]() |
e9b5a9251e | ||
![]() |
47ca6812fe | ||
![]() |
4ad733fe83 | ||
![]() |
f428ffb1fb | ||
![]() |
a1cb2ebef2 | ||
![]() |
31c248aedd | ||
![]() |
db2eda3e1f | ||
![]() |
ee25e577e5 | ||
![]() |
62c61cf52a | ||
![]() |
1934220f50 | ||
![]() |
673c9a68a3 | ||
![]() |
e08e05307e | ||
![]() |
e53c75ff95 | ||
![]() |
c5cdd0a33f | ||
![]() |
8ba7338861 | ||
![]() |
7df3bc3800 | ||
![]() |
8744c88f5f | ||
![]() |
e533734f0b | ||
![]() |
a2e387b448 | ||
![]() |
bfa6b232be | ||
![]() |
51ca08e484 | ||
![]() |
debc2719ee | ||
![]() |
985c590284 | ||
![]() |
94c4b98edd | ||
![]() |
895aed8f22 | ||
![]() |
6bf430b1cd | ||
![]() |
1d2ec44308 | ||
![]() |
60bf136aa1 | ||
![]() |
e7c518eb2a | ||
![]() |
e755317599 | ||
![]() |
efc7c90b9f | ||
![]() |
c493c4981d | ||
![]() |
f110d5bf9e | ||
![]() |
ade0381d15 | ||
![]() |
0dcc87c355 | ||
![]() |
3d7cce3a31 | ||
![]() |
60ddc20e02 | ||
![]() |
16e0808dbd | ||
![]() |
3278758575 | ||
![]() |
389f7c6802 | ||
![]() |
ffa27a50cc | ||
![]() |
e1c5590f30 | ||
![]() |
93a9f8194f | ||
![]() |
a917683d97 | ||
![]() |
34d0fbf4bc | ||
![]() |
3e97de3745 | ||
![]() |
2f79ebe879 | ||
![]() |
ab66b269b8 | ||
![]() |
a00655dbe4 | ||
![]() |
45b85e1ec3 | ||
![]() |
c395a0dcd6 | ||
![]() |
5d651eb627 | ||
![]() |
c0ef98e712 | ||
![]() |
d14fae83be | ||
![]() |
8aa3348a2a | ||
![]() |
ebdc6878e5 | ||
![]() |
a9d995fdf6 | ||
![]() |
be9b02c588 | ||
![]() |
264f658a46 | ||
![]() |
6c1e845183 | ||
![]() |
84d85b1995 | ||
![]() |
4a043157ad | ||
![]() |
1fac32d382 | ||
![]() |
74c3f6e08e | ||
![]() |
76b123142f | ||
![]() |
b543a49322 | ||
![]() |
fafd2bfcd4 | ||
![]() |
b439c59d85 | ||
![]() |
5d460e5100 | ||
![]() |
d82ca8cee4 | ||
![]() |
3655e795d7 | ||
![]() |
f0b614d78e | ||
![]() |
c61fe44788 | ||
![]() |
2d52e2c008 | ||
![]() |
75cfe774b3 | ||
![]() |
00fa7ff5fc | ||
![]() |
4599d66f5b | ||
![]() |
2502329583 | ||
![]() |
82cde2281e | ||
![]() |
99963bc6fc | ||
![]() |
c5446b655d | ||
![]() |
ed45411f5c | ||
![]() |
b6ef01a929 | ||
![]() |
149d0f8307 | ||
![]() |
e81c9ec843 | ||
![]() |
cd7ed72bf2 | ||
![]() |
72167ef467 | ||
![]() |
5c0f1d340b | ||
![]() |
70f6c276ac | ||
![]() |
8ad853dca2 | ||
![]() |
9d145c042c | ||
![]() |
427d2b4d61 | ||
![]() |
ce43fa6621 | ||
![]() |
76abeff3ff | ||
![]() |
80ee9fcc43 | ||
![]() |
18243a2192 | ||
![]() |
168a26ff5d | ||
![]() |
969478625a | ||
![]() |
540bf594ff | ||
![]() |
b8b8450f67 | ||
![]() |
bafa051f92 | ||
![]() |
921059926e | ||
![]() |
8defbda0cd | ||
![]() |
63f914ae62 | ||
![]() |
477a7b473c | ||
![]() |
897af2a0db | ||
![]() |
acfb61e8ce | ||
![]() |
caf3fbb982 | ||
![]() |
481fbb72a5 | ||
![]() |
4021a8bb92 | ||
![]() |
30f4520541 | ||
![]() |
a860bbcff9 | ||
![]() |
b81aad6085 | ||
![]() |
ce0972d699 | ||
![]() |
83ebdf3efd | ||
![]() |
0b2e9034b2 | ||
![]() |
4705e668bc | ||
![]() |
bf3f823fd7 | ||
![]() |
112fc4c6bb | ||
![]() |
ec4d4dd0d8 | ||
![]() |
adc7bd42d1 | ||
![]() |
7685ce8a3b | ||
![]() |
cf237175a7 | ||
![]() |
a93a984215 | ||
![]() |
d2f905e308 | ||
![]() |
0b6cc4867e | ||
![]() |
f6abdbe21b | ||
![]() |
337cd3f73a | ||
![]() |
627f5c9d3b | ||
![]() |
348b39cd04 | ||
![]() |
ca337b8505 | ||
![]() |
cc2d0198ea | ||
![]() |
28fd0b9af6 | ||
![]() |
48f1adce7e | ||
![]() |
108c6395f8 | ||
![]() |
6c249c12a3 | ||
![]() |
fe42b2a74c | ||
![]() |
b2702100cb | ||
![]() |
25c764cd1d | ||
![]() |
21eeb19818 | ||
![]() |
8b6f29d3c6 | ||
![]() |
903fad7d02 | ||
![]() |
61f10e4f53 | ||
![]() |
14aa4712b4 | ||
![]() |
6bbf8390df | ||
![]() |
cf0d8988a0 | ||
![]() |
0a95d7f957 | ||
![]() |
6cd40fcf04 | ||
![]() |
8d6e60ce67 | ||
![]() |
ab77e8d99a | ||
![]() |
c466829ded | ||
![]() |
5327672335 | ||
![]() |
34fc3ffb38 | ||
![]() |
3e6ecc5925 | ||
![]() |
8a7d6a1306 | ||
![]() |
d725cdc6f2 | ||
![]() |
506109e241 | ||
![]() |
2c0b4772a5 | ||
![]() |
f8539139b7 | ||
![]() |
b9cb452594 | ||
![]() |
14b7e05f78 | ||
![]() |
fb06a29292 | ||
![]() |
bb3d3a536b | ||
![]() |
b25cecf984 | ||
![]() |
a6f18bc2b3 | ||
![]() |
4302f69b95 | ||
![]() |
15d5d147bc | ||
![]() |
2489a6f693 | ||
![]() |
c99807378f | ||
![]() |
0c235cb43a | ||
![]() |
faeb2b1e20 | ||
![]() |
818cdf3af6 | ||
![]() |
e433e36e8a | ||
![]() |
1f517156ad | ||
![]() |
0a69ed90af | ||
![]() |
c1e28220b5 | ||
![]() |
b3d250190b | ||
![]() |
6fd41b9a7c | ||
![]() |
8839fc2157 | ||
![]() |
6daec24027 | ||
![]() |
dee7a282a0 | ||
![]() |
267670f258 | ||
![]() |
39e066c5f7 | ||
![]() |
956dcaf18d | ||
![]() |
19e5617166 | ||
![]() |
85c5ef6345 | ||
![]() |
19936348de | ||
![]() |
1b6fef95a2 | ||
![]() |
3c8efddb74 | ||
![]() |
1de0de6dca | ||
![]() |
94832cef2b | ||
![]() |
617b7b0dc8 | ||
![]() |
84e06ad3f5 | ||
![]() |
335b3f50a2 | ||
![]() |
cae09b8577 | ||
![]() |
4782da7e77 | ||
![]() |
27bc0e2d97 | ||
![]() |
816115aab2 | ||
![]() |
e38beacc8b | ||
![]() |
19502c79ce | ||
![]() |
21a42d0670 | ||
![]() |
1054870213 | ||
![]() |
341f2281d6 | ||
![]() |
40f1bc089d | ||
![]() |
10ae65cc6c | ||
![]() |
86da52b96d | ||
![]() |
fe1a46368f | ||
![]() |
9393d57c20 | ||
![]() |
6542298646 | ||
![]() |
3b92bb6207 | ||
![]() |
db6723eb00 | ||
![]() |
a640442a1c | ||
![]() |
7643a714b8 | ||
![]() |
ad5a1aa7ca | ||
![]() |
0c262d4d42 | ||
![]() |
7a92ff967a | ||
![]() |
dc5f64ef65 | ||
![]() |
088d674453 | ||
![]() |
f05cc5e6f2 | ||
![]() |
336b7cea9f | ||
![]() |
ddf775a017 | ||
![]() |
1498a7d32e | ||
![]() |
a250653ee4 | ||
![]() |
185c253a82 | ||
![]() |
4331bf368a | ||
![]() |
0feac878f9 | ||
![]() |
41564cbe2d | ||
![]() |
4030e5a9ca | ||
![]() |
b92ccdeff1 | ||
![]() |
aabde2eca6 | ||
![]() |
b060544693 | ||
![]() |
2768d0877a | ||
![]() |
a7dc7e59b1 | ||
![]() |
4dcdfe7f42 | ||
![]() |
c070578b42 | ||
![]() |
a23859625e | ||
![]() |
cb4f0d1a38 | ||
![]() |
458d389e09 | ||
![]() |
cc45178922 | ||
![]() |
657cbe4701 | ||
![]() |
51661803b1 | ||
![]() |
cd0b69b3a1 | ||
![]() |
446c52862e | ||
![]() |
9468b8fdde | ||
![]() |
aae0a14e7f | ||
![]() |
28f81d59cc | ||
![]() |
871ec1a2c4 | ||
![]() |
d4998c871c | ||
![]() |
05e35e1d1e | ||
![]() |
621e6b4585 | ||
![]() |
c55761e55f | ||
![]() |
3e402696be | ||
![]() |
debdcc31d8 | ||
![]() |
ed8d5e5c4f | ||
![]() |
c562426b5e | ||
![]() |
6257bc4be9 | ||
![]() |
f04e5bf6a3 | ||
![]() |
e09f3fef5f | ||
![]() |
9969a331a6 | ||
![]() |
6724c74c7a | ||
![]() |
a76997757b | ||
![]() |
0e61a637be | ||
![]() |
422f8fdc88 | ||
![]() |
5f77def0e4 | ||
![]() |
a38bb946c1 | ||
![]() |
44939e5933 | ||
![]() |
45251144e5 | ||
![]() |
58057ea219 | ||
![]() |
450feedc11 | ||
![]() |
9483a7f010 | ||
![]() |
b99994998d | ||
![]() |
9b1346d066 | ||
![]() |
2b0f8633ac | ||
![]() |
4fcd79c208 | ||
![]() |
2daa5b775e | ||
![]() |
294346face | ||
![]() |
7b5dd14927 | ||
![]() |
b57c40186d | ||
![]() |
336f647d75 | ||
![]() |
3dba431fcd | ||
![]() |
8f377aaa53 | ||
![]() |
d008e6aa25 | ||
![]() |
6f34d75bbc | ||
![]() |
7bbc39e975 | ||
![]() |
2a8a0e9d92 | ||
![]() |
5bd4a2c50d | ||
![]() |
8d3cb6a6e0 | ||
![]() |
b1f632fd0c | ||
![]() |
1a59ed4cc1 | ||
![]() |
0ad9a5bbb9 | ||
![]() |
3c085288dd | ||
![]() |
c22af9f286 | ||
![]() |
9d31f612ec | ||
![]() |
bdec2bf058 | ||
![]() |
fe5f2231fc | ||
![]() |
cad782738c | ||
![]() |
0d7798c6c3 | ||
![]() |
fec092bfd7 | ||
![]() |
e7f198aaf8 | ||
![]() |
26cd76492a | ||
![]() |
fa8bd3612f | ||
![]() |
6eeacf0a7e | ||
![]() |
031b46cb88 | ||
![]() |
bde093a1c7 | ||
![]() |
988cd3273d | ||
![]() |
d8605a2191 | ||
![]() |
52a89bd12a | ||
![]() |
32f91a5150 | ||
![]() |
feb6b7ef0a | ||
![]() |
708278a48d | ||
![]() |
4d037227a4 | ||
![]() |
3c1663dd9d | ||
![]() |
7b583478d4 | ||
![]() |
bfaba0b1d2 | ||
![]() |
25a4002924 | ||
![]() |
488a81253f | ||
![]() |
5c45871af7 | ||
![]() |
5bf71e637e | ||
![]() |
b1dd8732ff | ||
![]() |
d0efccf91a | ||
![]() |
3baa63dc7f | ||
![]() |
8a93b17118 | ||
![]() |
7bdc236c3f | ||
![]() |
83852edd8b | ||
![]() |
06ad6eb8a1 | ||
![]() |
4fe4fe8b22 | ||
![]() |
f50de43642 | ||
![]() |
5528eee031 | ||
![]() |
9bbfcc1a55 | ||
![]() |
fe3b4b3331 | ||
![]() |
fb2d6f2720 | ||
![]() |
b537cf4a3e | ||
![]() |
08deeb3c73 | ||
![]() |
fdaa440f26 | ||
![]() |
39d2014c54 | ||
![]() |
b49f16ed0a | ||
![]() |
9a856a039b | ||
![]() |
ec1ff45b53 | ||
![]() |
38b39b0d1a | ||
![]() |
272bb3bb2a | ||
![]() |
05e614891d | ||
![]() |
12fb240b56 | ||
![]() |
484e86d55f | ||
![]() |
11ccaad187 | ||
![]() |
10f2393123 | ||
![]() |
ca32ffeb86 | ||
![]() |
46870282e5 | ||
![]() |
1f047c8524 | ||
![]() |
8ea6d8e59d | ||
![]() |
d70fbf3fa9 | ||
![]() |
f77b561d4f | ||
![]() |
2ce203af8f | ||
![]() |
b152b7ed01 | ||
![]() |
769fd0e296 | ||
![]() |
27a8e625cd | ||
![]() |
fbfe90a5d6 | ||
![]() |
698ab9de39 | ||
![]() |
a05e255abe | ||
![]() |
69bfaf9806 | ||
![]() |
5800de8871 | ||
![]() |
6fa51928bf | ||
![]() |
fd840a1f7c | ||
![]() |
37921d6678 | ||
![]() |
eedf0d1855 | ||
![]() |
1502226aa0 | ||
![]() |
8b8545f558 | ||
![]() |
5cab1ed749 | ||
![]() |
fe0977bbcb | ||
![]() |
7839ea895e | ||
![]() |
11268cb7c2 | ||
![]() |
5dd3b6cd66 | ||
![]() |
d76ab89c9f | ||
![]() |
d8d060970d | ||
![]() |
18933afae0 | ||
![]() |
baf3c322fc | ||
![]() |
436a8f8f43 | ||
![]() |
961f9eedc6 | ||
![]() |
67bbac75ce | ||
![]() |
db296452f1 | ||
![]() |
55a4babb8c | ||
![]() |
456920ff10 | ||
![]() |
e6a1406694 | ||
![]() |
8d4f46988b | ||
![]() |
e26e811e6d | ||
![]() |
af1d0d2d00 | ||
![]() |
8a4154aa60 | ||
![]() |
b7a76b9be5 | ||
![]() |
c7ce55023a | ||
![]() |
5485466381 | ||
![]() |
8060518620 | ||
![]() |
63b9b72025 | ||
![]() |
f11d01eb22 | ||
![]() |
aa077ecb78 | ||
![]() |
8bfef356cc | ||
![]() |
ee19a94ec5 | ||
![]() |
d3c96e4b81 | ||
![]() |
d984c6147d | ||
![]() |
f765226230 | ||
![]() |
bec66ba51f | ||
![]() |
b5d72b8d18 | ||
![]() |
614483dabc | ||
![]() |
519c7bf718 | ||
![]() |
49b63cb1d5 | ||
![]() |
730e7537bf | ||
![]() |
0e570e2eb9 | ||
![]() |
2eba2fee5d | ||
![]() |
1b45c63c16 | ||
![]() |
abb0448c63 | ||
![]() |
818706be18 | ||
![]() |
5f5f208a43 | ||
![]() |
4d5dc9d6e3 | ||
![]() |
fe67159aac | ||
![]() |
2e6aa4b0bd | ||
![]() |
f05b2858f4 | ||
![]() |
7eeb418c3a | ||
![]() |
dbe6d0b35c | ||
![]() |
550fc6a80b | ||
![]() |
079dcfc995 | ||
![]() |
fca53605c0 | ||
![]() |
df682b04f3 | ||
![]() |
bd34490e34 | ||
![]() |
38e5dc7bf7 | ||
![]() |
c56a448b6e | ||
![]() |
df1a872e09 | ||
![]() |
f30b2c7797 | ||
![]() |
c79095de4c | ||
![]() |
593e3ed9f9 | ||
![]() |
e7ab665c51 | ||
![]() |
82dcb4ec53 | ||
![]() |
f632105618 | ||
![]() |
d087d797f0 | ||
![]() |
cbf92c2601 | ||
![]() |
6e6028a14d | ||
![]() |
9c21c09ef9 | ||
![]() |
00440d167e | ||
![]() |
fbaa3998c6 | ||
![]() |
d9a01c0511 | ||
![]() |
28f256a4c5 | ||
![]() |
be112ee312 | ||
![]() |
ffb5410362 | ||
![]() |
36804105bc | ||
![]() |
c86926da97 | ||
![]() |
2faf30a559 | ||
![]() |
4f343de1c5 | ||
![]() |
c9767fb59b | ||
![]() |
f449b485a0 | ||
![]() |
1fa740cacc | ||
![]() |
1d1ed41db2 | ||
![]() |
9255eff980 | ||
![]() |
bfa0ad7821 | ||
![]() |
235139bced | ||
![]() |
8297ed4a8f | ||
![]() |
bd801f81ce | ||
![]() |
a451b33679 | ||
![]() |
f0d84c0310 | ||
![]() |
ca5f3ad701 | ||
![]() |
833ec8956f | ||
![]() |
41f7af8129 | ||
![]() |
ff82bb5c96 | ||
![]() |
0d48fe5fed | ||
![]() |
f0f4731193 | ||
![]() |
edede45aab | ||
![]() |
1e087ab487 | ||
![]() |
64a0d310e5 | ||
![]() |
10a550d9da | ||
![]() |
c3334d86b3 | ||
![]() |
a5f566e505 | ||
![]() |
6417b546fe | ||
![]() |
a9655c1230 | ||
![]() |
7affc72180 | ||
![]() |
29ae3d4fdf | ||
![]() |
0a4ae24aec | ||
![]() |
772a43de44 | ||
![]() |
4df22c5baa | ||
![]() |
a3da8915b9 | ||
![]() |
6b94d024dc | ||
![]() |
41579a975b | ||
![]() |
ab149287dd | ||
![]() |
53a882d7fd | ||
![]() |
b899d2b4c2 | ||
![]() |
f7eec20978 | ||
![]() |
56ef33d171 | ||
![]() |
1eb7c942a9 | ||
![]() |
b94947d765 | ||
![]() |
2d58c588c4 | ||
![]() |
d412c7f357 | ||
![]() |
c4a29c3476 | ||
![]() |
b5f6d20a74 | ||
![]() |
bbb5cd6041 | ||
![]() |
e0ecc78a58 | ||
![]() |
7109ef85db | ||
![]() |
f981187a7a | ||
![]() |
399bd641ca | ||
![]() |
a8c2edaa50 | ||
![]() |
c7dee954e5 | ||
![]() |
01c130e1f6 | ||
![]() |
277c3d97fe | ||
![]() |
6aa6b45d1c | ||
![]() |
fcb42bcca9 | ||
![]() |
1db4c61115 | ||
![]() |
98137a8faf | ||
![]() |
735c121307 | ||
![]() |
7579a78d3c | ||
![]() |
661c80150c | ||
![]() |
50af1aa75f | ||
![]() |
ea74787b10 | ||
![]() |
c66bdc1120 | ||
![]() |
674f807cf1 | ||
![]() |
ba0ba03d7c | ||
![]() |
82cdb49c02 | ||
![]() |
314fb336f3 | ||
![]() |
0551b5ea70 | ||
![]() |
0ad1cab036 | ||
![]() |
aed59fefb7 | ||
![]() |
e35fbfe7c8 | ||
![]() |
ffdcf30ac2 | ||
![]() |
a099d228ba | ||
![]() |
7308e0c82e | ||
![]() |
ada3ddc5e1 | ||
![]() |
d49a45186e | ||
![]() |
a649a05df8 | ||
![]() |
6cd9daa91c | ||
![]() |
25aa801286 | ||
![]() |
cb549ee939 | ||
![]() |
332a0deb6f | ||
![]() |
01d6e006a7 | ||
![]() |
60a9ad9648 | ||
![]() |
5b858cbc70 | ||
![]() |
f46a93a5e4 | ||
![]() |
3578a97a28 | ||
![]() |
14bf2aeab4 | ||
![]() |
fc76ba9a5c | ||
![]() |
69aa3a798e | ||
![]() |
80aea16f32 | ||
![]() |
537e89c6fd | ||
![]() |
31a5c61264 | ||
![]() |
1aff63a651 | ||
![]() |
116e0c1843 | ||
![]() |
0d402e0432 | ||
![]() |
d768b5e315 | ||
![]() |
f7c0fa2441 | ||
![]() |
2251b4a063 | ||
![]() |
d643b9ffe5 | ||
![]() |
e11eccdd3f | ||
![]() |
57db5a7202 | ||
![]() |
0545aff0f2 | ||
![]() |
997e60a165 | ||
![]() |
f45dc8d4d7 | ||
![]() |
098dcc9582 | ||
![]() |
3028bf0bb1 | ||
![]() |
f2a0505446 | ||
![]() |
677fa2f18a | ||
![]() |
0d6d65418e | ||
![]() |
43958d71e2 | ||
![]() |
a4a4d26f62 | ||
![]() |
c26d9022db | ||
![]() |
36f02502a8 | ||
![]() |
4a98546f6f | ||
![]() |
69a0185f86 | ||
![]() |
fff1d95fcf | ||
![]() |
43e1594f59 | ||
![]() |
9d92e0f852 | ||
![]() |
3d4f723800 | ||
![]() |
022e79d6d3 | ||
![]() |
7aedce7c70 | ||
![]() |
73866c8273 | ||
![]() |
f8d7ecde45 | ||
![]() |
ef847f3613 | ||
![]() |
18116eb94b | ||
![]() |
8969268af6 | ||
![]() |
8d588443b3 | ||
![]() |
d280b98394 | ||
![]() |
19c54fff4f | ||
![]() |
e71e8e9c3e | ||
![]() |
9c57dfa17f | ||
![]() |
67aa69f4c3 | ||
![]() |
fb86491ad9 | ||
![]() |
193b4935d4 | ||
![]() |
99bcbcd591 | ||
![]() |
e0dd579aaa | ||
![]() |
5c7cf62d54 | ||
![]() |
b37868f1ee | ||
![]() |
e6fa68d0ff | ||
![]() |
ece4ec792f | ||
![]() |
e0ac006ff1 | ||
![]() |
ef8856688b | ||
![]() |
7d02eed27e | ||
![]() |
1e7006d7de | ||
![]() |
7b3cc5a7a2 | ||
![]() |
76fa65b35a | ||
![]() |
b120479bcb | ||
![]() |
06196a8934 | ||
![]() |
c577ec56ee | ||
![]() |
f180f3ac03 | ||
![]() |
1a0abfac82 | ||
![]() |
2e0ca1387c | ||
![]() |
f569b05cde | ||
![]() |
7ec766458d | ||
![]() |
ca2a57ea6e | ||
![]() |
6621146431 | ||
![]() |
753e2801d9 | ||
![]() |
fe8b496e72 | ||
![]() |
54e8f5f3fc | ||
![]() |
5ef1a4ee70 | ||
![]() |
b69982534b | ||
![]() |
96820b2f41 | ||
![]() |
906e2a0461 | ||
![]() |
ca56f9d5e6 | ||
![]() |
24eeb2765f | ||
![]() |
364f192178 | ||
![]() |
5cb95037c5 | ||
![]() |
3da51f7f02 | ||
![]() |
832a8634ab | ||
![]() |
cf48f5ee8a | ||
![]() |
95a6f1be1c | ||
![]() |
8d458b66a5 | ||
![]() |
5e6c44832f | ||
![]() |
638117ac64 | ||
![]() |
c70f1f1ebf | ||
![]() |
7e017b4cb4 | ||
![]() |
c9ba5444c3 | ||
![]() |
9d585cc6c4 | ||
![]() |
b1974fbf5b | ||
![]() |
4c627771c2 | ||
![]() |
54947c5065 | ||
![]() |
6550f4af67 | ||
![]() |
17c0de747f | ||
![]() |
dc0cd6ef67 | ||
![]() |
6f5fe3f8f5 | ||
![]() |
ad85cfd792 | ||
![]() |
0a380045a3 | ||
![]() |
e5d5f106b5 | ||
![]() |
6b083e7666 | ||
![]() |
fe6b021fbe | ||
![]() |
1bf1246a1b | ||
![]() |
6adc37e015 | ||
![]() |
05bf630708 | ||
![]() |
fb1b729e2c | ||
![]() |
6e3ed1ce8a | ||
![]() |
35053eefaa | ||
![]() |
71839d6e37 | ||
![]() |
3599bb4277 | ||
![]() |
00dc0fb0ac | ||
![]() |
4d11f631c1 | ||
![]() |
118016e6ff | ||
![]() |
d696f27f88 | ||
![]() |
77a7382e3b | ||
![]() |
f269b7fd27 | ||
![]() |
0c14363eeb | ||
![]() |
8d9f64034f | ||
![]() |
a3c8490578 | ||
![]() |
c3b025bfca | ||
![]() |
e1214bf111 | ||
![]() |
c82ac7045a | ||
![]() |
f71b00e6b9 | ||
![]() |
cd784a6758 | ||
![]() |
7683bf28fa | ||
![]() |
2fc27c517b | ||
![]() |
f469484821 | ||
![]() |
8582221eb3 | ||
![]() |
75e09cd935 | ||
![]() |
35d82c92f1 | ||
![]() |
ad709d6151 | ||
![]() |
ee173ebb47 | ||
![]() |
98987b84fe | ||
![]() |
54f78e2aa8 | ||
![]() |
87e23b9adf | ||
![]() |
6f9b1354dc | ||
![]() |
14a50deec6 | ||
![]() |
0270af042b | ||
![]() |
928aa6aaee | ||
![]() |
bb1e8a0514 | ||
![]() |
477fc947e1 | ||
![]() |
9ae6598e84 | ||
![]() |
7a0ed03500 | ||
![]() |
2cdf8d5a7f | ||
![]() |
adf02582ab | ||
![]() |
f0984bdabc | ||
![]() |
5b0631d48c | ||
![]() |
b6ddb6cb11 | ||
![]() |
f4cff13c0a | ||
![]() |
e01416e3c2 | ||
![]() |
ca01816885 | ||
![]() |
402e93514c | ||
![]() |
1d8be2ad3e | ||
![]() |
99549e7b57 | ||
![]() |
a3aa5c000f | ||
![]() |
83845a8488 | ||
![]() |
e2fd078da8 | ||
![]() |
48a33f8a6d | ||
![]() |
323eed683d | ||
![]() |
3291faad53 | ||
![]() |
2451b767ae | ||
![]() |
6f05573058 | ||
![]() |
a1c3f77e0c | ||
![]() |
5633fcf698 | ||
![]() |
21426f901e | ||
![]() |
cdabf53fd5 | ||
![]() |
124fc3703f | ||
![]() |
68ba8e8aaa | ||
![]() |
36e220b7a1 | ||
![]() |
0b009ab521 | ||
![]() |
992b30aac4 | ||
![]() |
044ed50e53 | ||
![]() |
dd52f408d9 | ||
![]() |
1e7434c6e8 | ||
![]() |
e6a2085c2a | ||
![]() |
2928787f51 | ||
![]() |
51a2394a94 | ||
![]() |
ea1166564a | ||
![]() |
85524fcf16 | ||
![]() |
f3a5747ac2 | ||
![]() |
5c29507018 | ||
![]() |
c8f5ed84ba | ||
![]() |
178d876f8d | ||
![]() |
9a8eade1f6 | ||
![]() |
241b6505dd | ||
![]() |
7278a22ca6 | ||
![]() |
65901862ec | ||
![]() |
f7ea916b21 | ||
![]() |
3447822f65 | ||
![]() |
3d65e37f98 | ||
![]() |
75b468839f | ||
![]() |
f8efcea0b0 | ||
![]() |
7d4573cdd4 | ||
![]() |
9175329c2d | ||
![]() |
6bc9fbefab | ||
![]() |
0c375882f0 | ||
![]() |
ca463c32cd | ||
![]() |
71bb84a0cb | ||
![]() |
18ccd442cb | ||
![]() |
fa7fc547ce | ||
![]() |
3d2ef162e6 | ||
![]() |
0b29899150 | ||
![]() |
76898b335b | ||
![]() |
1e8f73884e | ||
![]() |
6d55c4103d | ||
![]() |
c4e7fbb392 | ||
![]() |
5a710f6198 | ||
![]() |
a6799263b5 | ||
![]() |
1d7f43f3c8 | ||
![]() |
727ab062fb | ||
![]() |
338a8b9292 | ||
![]() |
60c7f1fbb1 | ||
![]() |
c8e0da71ff | ||
![]() |
de0ebaaf3c | ||
![]() |
1bb08731d1 | ||
![]() |
873e9d30dc | ||
![]() |
e565e4feb7 | ||
![]() |
6bf53298d1 | ||
![]() |
f0c511fedf | ||
![]() |
213521927f | ||
![]() |
3e024a3fe1 | ||
![]() |
245f48f4f2 | ||
![]() |
4e24e40d7e | ||
![]() |
cea1b340d7 | ||
![]() |
2a813b1e31 | ||
![]() |
1ec411fd39 | ||
![]() |
77d07f6907 | ||
![]() |
b4fffb3d06 | ||
![]() |
5984aad7bc | ||
![]() |
8f8937daad | ||
![]() |
5c7cc1fb1d | ||
![]() |
e1769c70dc | ||
![]() |
fa1051211b | ||
![]() |
fd7080b0ea | ||
![]() |
751af33b71 | ||
![]() |
56bc179c39 | ||
![]() |
c91af647e4 | ||
![]() |
75ace53123 | ||
![]() |
50f8df44b3 | ||
![]() |
4ac4864a61 | ||
![]() |
8a91b0e572 | ||
![]() |
acdc104173 | ||
![]() |
87cc2ac235 | ||
![]() |
01bee868bb | ||
![]() |
2fbd770917 | ||
![]() |
530b8d6960 | ||
![]() |
6494fa8377 | ||
![]() |
81e5d32577 | ||
![]() |
4d8b9903c4 | ||
![]() |
72b9657ffa | ||
![]() |
8319573011 | ||
![]() |
0870447e4d | ||
![]() |
814c5e6980 | ||
![]() |
c6d60bd3f2 | ||
![]() |
91b6f74131 | ||
![]() |
030b3ada1e | ||
![]() |
585c303c30 | ||
![]() |
0992bd4115 | ||
![]() |
085a80ad39 | ||
![]() |
f01563b960 | ||
![]() |
2e9399a05f | ||
![]() |
50b4b5c734 | ||
![]() |
e4a8b1f854 | ||
![]() |
2a461274b6 | ||
![]() |
b13ad7986e | ||
![]() |
b6c5583f19 | ||
![]() |
409c2361f3 | ||
![]() |
cc6ebbbf33 | ||
![]() |
5c13636739 | ||
![]() |
b5f3ccb41f | ||
![]() |
69b4f4210f | ||
![]() |
44a2fc533b | ||
![]() |
496045e5d0 | ||
![]() |
111d1badb1 | ||
![]() |
5ab8d85856 | ||
![]() |
b0c148f9c1 | ||
![]() |
6a062c7f90 | ||
![]() |
ff8cd7b142 | ||
![]() |
126392f38b | ||
![]() |
d1ea0fd81b | ||
![]() |
794cbf87ad | ||
![]() |
5c5348e88e | ||
![]() |
e3833dda5e | ||
![]() |
2348a44ad6 | ||
![]() |
de187609d7 | ||
![]() |
4705619e99 | ||
![]() |
dd65a1d6cb | ||
![]() |
6ba20dfd3d | ||
![]() |
71c641564d | ||
![]() |
5a52cdd37a | ||
![]() |
cd0c3ed6d8 | ||
![]() |
fdf9448e1d | ||
![]() |
89c3d91e92 | ||
![]() |
f0c84a23d3 | ||
![]() |
2a6299df59 | ||
![]() |
186df36c2c | ||
![]() |
86d35b65b1 | ||
![]() |
9c2797ade0 | ||
![]() |
62b865dbef | ||
![]() |
ec82b0fd15 | ||
![]() |
22fbae18dd | ||
![]() |
e7f841be68 | ||
![]() |
482d465472 | ||
![]() |
6bd6565ce2 | ||
![]() |
962a413203 | ||
![]() |
a9626058ef | ||
![]() |
3280070b18 | ||
![]() |
01841ab92d | ||
![]() |
fe836c3aae | ||
![]() |
3dce490b76 | ||
![]() |
fdddd73837 | ||
![]() |
d5b24d634b | ||
![]() |
7a55c28cc1 | ||
![]() |
c8758ab240 | ||
![]() |
28c7ccf563 | ||
![]() |
567604a5e1 | ||
![]() |
96c089b3dc | ||
![]() |
12b92368c0 | ||
![]() |
e99cfb6e93 | ||
![]() |
52a7bb04d0 | ||
![]() |
dd31f29ad5 | ||
![]() |
974dbcfc15 | ||
![]() |
5681857a2c | ||
![]() |
e2729372c7 | ||
![]() |
e31e3139d1 | ||
![]() |
5e6a6187de | ||
![]() |
728b1d520a | ||
![]() |
1f95f5f2ae | ||
![]() |
08341cc1aa | ||
![]() |
a2645bafeb | ||
![]() |
94423c71ec | ||
![]() |
35215c9e70 | ||
![]() |
f5216aa9dc | ||
![]() |
bfb57c6be9 | ||
![]() |
b618176823 | ||
![]() |
dabec28f14 | ||
![]() |
1b6fc08b8a | ||
![]() |
fda0ba08a3 | ||
![]() |
363c538275 | ||
![]() |
722b2b07e0 | ||
![]() |
5957e315c9 | ||
![]() |
bec5c1e9c3 | ||
![]() |
d54cbecb37 | ||
![]() |
f6cd1d99dd | ||
![]() |
cc49c369c7 | ||
![]() |
b99914bc5e | ||
![]() |
26e0b82abc | ||
![]() |
62a2472ed1 | ||
![]() |
120777342c | ||
![]() |
f1e31356d2 | ||
![]() |
8865d93a65 | ||
![]() |
e48c1ea1d1 | ||
![]() |
51999a0fa9 | ||
![]() |
98daa9a2af | ||
![]() |
6e237b8ca9 | ||
![]() |
d7bb4311d8 | ||
![]() |
15afbb6132 | ||
![]() |
2423ad940d | ||
![]() |
e0a5aabf28 | ||
![]() |
19204ae7f9 | ||
![]() |
3cb987ceb2 | ||
![]() |
600734c884 | ||
![]() |
4c114b0232 | ||
![]() |
828c3173c7 | ||
![]() |
f1229893f0 | ||
![]() |
39e40cddf9 | ||
![]() |
8b1854cac7 | ||
![]() |
a62a48767d | ||
![]() |
bdcad6d55d | ||
![]() |
964875f9de | ||
![]() |
0b6a81e6f0 | ||
![]() |
0a93336c11 | ||
![]() |
4544fabb91 | ||
![]() |
0e67e31cf4 | ||
![]() |
96c8b5b796 | ||
![]() |
7ae127373e | ||
![]() |
0eda71b5e5 | ||
![]() |
15233120e0 | ||
![]() |
35f5016d33 | ||
![]() |
3c5e8caf89 | ||
![]() |
9412f8be63 | ||
![]() |
1b840c8102 | ||
![]() |
3c4a887903 | ||
![]() |
007a4d2ba6 | ||
![]() |
8a94d16c46 | ||
![]() |
e0f9739fa2 | ||
![]() |
5b62cac9bc | ||
![]() |
83dcf38788 | ||
![]() |
43ae76caff | ||
![]() |
b50a1410a1 | ||
![]() |
4930951f77 | ||
![]() |
591622de40 | ||
![]() |
9503875953 | ||
![]() |
b9836d769a | ||
![]() |
401c700580 | ||
![]() |
b8dcaaf0ad | ||
![]() |
4cef045d73 | ||
![]() |
ae548fca8f | ||
![]() |
50508899ac | ||
![]() |
a8e57bc169 | ||
![]() |
065d1acc2b | ||
![]() |
0425190a57 | ||
![]() |
9dcffd344a | ||
![]() |
a2ac46c3be | ||
![]() |
4ff2a48182 | ||
![]() |
e1444437c2 | ||
![]() |
30660e6833 | ||
![]() |
37dfd14681 | ||
![]() |
5005c38b4f | ||
![]() |
8733401ce5 | ||
![]() |
50ce138395 | ||
![]() |
4011915749 | ||
![]() |
852a213e1c | ||
![]() |
9f0400167b | ||
![]() |
aa247025d2 | ||
![]() |
0feb5ee1b1 | ||
![]() |
77ce3b91d7 | ||
![]() |
cded847b46 | ||
![]() |
97f54f7f3d | ||
![]() |
6434928f2d | ||
![]() |
df79f9793b | ||
![]() |
4af3883371 | ||
![]() |
fc9bb0f605 | ||
![]() |
182d9a9803 | ||
![]() |
d238461afa | ||
![]() |
b8413f7bd4 | ||
![]() |
3efeb614a4 | ||
![]() |
379e12d968 | ||
![]() |
e0fb010ae1 | ||
![]() |
4f85eb5056 | ||
![]() |
bb7518a04a | ||
![]() |
eb425d7652 | ||
![]() |
08e95ff4d4 | ||
![]() |
46965224dd | ||
![]() |
43f7b348b5 | ||
![]() |
633665224b | ||
![]() |
8cbae51c3e | ||
![]() |
f5d29c0739 | ||
![]() |
b5676fa258 | ||
![]() |
c98cdf26dc | ||
![]() |
b35fdf1711 | ||
![]() |
883e9c24a9 | ||
![]() |
214ac59298 | ||
![]() |
b55f438c1b | ||
![]() |
fe2dc55988 | ||
![]() |
f6cfce3f4b | ||
![]() |
7a1ffa66a9 | ||
![]() |
5971e02e26 | ||
![]() |
15478423d7 | ||
![]() |
a7f2f933e1 | ||
![]() |
6c237d4994 | ||
![]() |
d64394dfd6 | ||
![]() |
ebee256da1 | ||
![]() |
a263c09d03 | ||
![]() |
ee84b57234 | ||
![]() |
3eee78dbe7 | ||
![]() |
efe8a698d3 | ||
![]() |
95b90c19c9 | ||
![]() |
3b959e4676 | ||
![]() |
ad631a1f96 | ||
![]() |
fb382d9e8d | ||
![]() |
bec112817b | ||
![]() |
18029106ac | ||
![]() |
b1d4b3a101 | ||
![]() |
82195e5af5 | ||
![]() |
ff1925b24d | ||
![]() |
ac648bde74 | ||
![]() |
816bfeaba9 | ||
![]() |
f5574b6253 | ||
![]() |
778c85c03c | ||
![]() |
453375cb59 | ||
![]() |
3454d35f35 | ||
![]() |
97a368ff0d | ||
![]() |
c6fbd155f0 | ||
![]() |
f17838a043 | ||
![]() |
3bf311f348 | ||
![]() |
d375050592 | ||
![]() |
10cf69cd93 | ||
![]() |
871fe1e9cf | ||
![]() |
a24a3702d7 | ||
![]() |
1cd32232a8 | ||
![]() |
df66cf5f67 | ||
![]() |
2bba9858d7 | ||
![]() |
8e831804f4 | ||
![]() |
37a19570e2 | ||
![]() |
9956b6be37 | ||
![]() |
7dfebac8b9 | ||
![]() |
f112ab1abc | ||
![]() |
02ca8ea6fe | ||
![]() |
b8e7e584a7 | ||
![]() |
5afcb0af9c | ||
![]() |
29c6d82cd7 | ||
![]() |
6f13aded25 | ||
![]() |
e1b1d554dd | ||
![]() |
5a80dd3332 | ||
![]() |
255e47b376 | ||
![]() |
269d259e1f | ||
![]() |
cfd8eb3597 | ||
![]() |
1db544e145 | ||
![]() |
26df865f70 | ||
![]() |
6625d2f571 | ||
![]() |
6c790bdaf4 | ||
![]() |
03cff88a6a | ||
![]() |
b217e47c48 | ||
![]() |
100783ccf4 | ||
![]() |
ea0b910646 | ||
![]() |
6d2317ee69 | ||
![]() |
979a569eda | ||
![]() |
d520880502 | ||
![]() |
b83330dc0d | ||
![]() |
8cc86c8d25 | ||
![]() |
7ecdc2b938 | ||
![]() |
e1cc58e554 | ||
![]() |
47245e8862 |
28
.circleci/config.yml
Normal file
28
.circleci/config.yml
Normal file
@ -0,0 +1,28 @@
|
||||
version: 2
|
||||
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
# using custom image, see .circleci/images/primary/Dockerfile
|
||||
# - image: govgo/robotgoci:1.10.3
|
||||
- image: golang:1.23.0
|
||||
working_directory: /gopath/src/github.com/go-vgo/robotgo
|
||||
steps:
|
||||
- checkout
|
||||
# specify any bash command here prefixed with `run: `
|
||||
- run: apt update
|
||||
- run:
|
||||
apt -y install gcc libc6-dev
|
||||
libx11-dev xorg-dev libxtst-dev
|
||||
xsel xclip
|
||||
# libpng++-dev
|
||||
# xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev libxkbcommon-dev
|
||||
- run: apt -y install xvfb
|
||||
#
|
||||
# override:
|
||||
- run: go get -v -t -d ./...
|
||||
- run: xvfb-run go test -v ./...
|
||||
#
|
||||
# codecov.io
|
||||
# - run: xvfb-run go test -v -covermode=count -coverprofile=coverage.out
|
||||
# - run: bash <(curl -s https://codecov.io/bash)
|
22
.circleci/images/primary/Dockerfile
Normal file
22
.circleci/images/primary/Dockerfile
Normal file
@ -0,0 +1,22 @@
|
||||
# FROM golang:1.10.1
|
||||
FROM golang:1.24.2-stretch AS build
|
||||
# FROM govgo/go:1.11.1
|
||||
|
||||
RUN apt update && apt install -y --no-install-recommends \
|
||||
# customize dependencies
|
||||
libx11-dev xorg-dev \
|
||||
libxtst-dev \
|
||||
# Bitmap
|
||||
libpng++-dev \
|
||||
# Event:
|
||||
xcb libxcb-xkb-dev \
|
||||
x11-xkb-utils libx11-xcb-dev \
|
||||
libxkbcommon-x11-dev libxkbcommon-dev \
|
||||
# Clipboard:
|
||||
xsel xclip && \
|
||||
#
|
||||
apt remove --purge --auto-remove && \
|
||||
apt clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN go get -u github.com/go-vgo/robotgo
|
2
.github/CODEOWNERS
vendored
Normal file
2
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# general
|
||||
* @vcaesar
|
18
.github/issue_template.md
vendored
18
.github/issue_template.md
vendored
@ -1,22 +1,26 @@
|
||||
1. Please speak English, this is the language everybody of us can speak and write.
|
||||
2. Please ask questions or config/deploy problems on our Gitter channel: https://gitter.im/go-vgo/robotgo
|
||||
3. Please take a moment to search that an issue doesn't already exist.
|
||||
4. Please give all relevant information below for bug reports, incomplete details will be handled as an invalid report.
|
||||
1. Please **speak English (English only)**, this is the language everybody of us can speak and write.
|
||||
2. Please take a moment to **search** that an issue **doesn't already exist**.
|
||||
3. Please make sure `Golang, GCC` is installed correctly before installing RobotGo.
|
||||
<!-- 4. Please ask questions or config/deploy problems on our Gitter channel: https://gitter.im/go-vgo/robotgo -->
|
||||
5. Please give all relevant information below for bug reports, incomplete details will be handled as an invalid report.
|
||||
|
||||
**You MUST delete the content above including this line before posting, otherwise your issue will be invalid.**
|
||||
|
||||
|
||||
- Robotgo version (or commit ref):
|
||||
- Go version:
|
||||
- Gcc version:
|
||||
- Operating system and bit:
|
||||
- Resolution:
|
||||
- Can you reproduce the bug at [Examples](https://github.com/go-vgo/robotgo/blob/master/examples/main.go):
|
||||
- [ ] Yes (provide example code)
|
||||
- [ ] No
|
||||
- [ ] Not relevant
|
||||
- Provide example code:
|
||||
```Go
|
||||
- Provide example code:
|
||||
|
||||
```
|
||||
```Go
|
||||
|
||||
```
|
||||
- Log gist:
|
||||
|
||||
## Description
|
||||
|
16
.github/pull_request_template.md
vendored
16
.github/pull_request_template.md
vendored
@ -1,22 +1,24 @@
|
||||
The pull request will be closed without any reasons if it does not satisfy any of following requirements:
|
||||
|
||||
1. Make sure you are targeting the `master` branch, pull requests on release branches are only allowed for bug fixes.
|
||||
2. Please read contributing guidelines: [CONTRIBUTING](https://github.com/go-vgo/robotgo/blob/master/CONTRIBUTING.md)
|
||||
3. Describe what your pull request does and which issue you're targeting (if any and Please use English)
|
||||
4. ... if it is not related to any particular issues, explain why we should not reject your pull request.
|
||||
5. The Commits must use English, must be test and No useless submissions.
|
||||
2. Add new features, please provide the reasons and test code.
|
||||
3. Please read contributing guidelines: [CONTRIBUTING](https://github.com/go-vgo/robotgo/blob/master/CONTRIBUTING.md)
|
||||
4. Describe what your pull request does and which issue you're targeting (if any and **Please use English**)
|
||||
5. ... if it is not related to any particular issues, explain why we should not reject your pull request.
|
||||
6. The Commits must **use English**, must be test and No useless submissions.
|
||||
|
||||
**You MUST delete the content above including this line before posting, otherwise your pull request will be invalid.**
|
||||
|
||||
|
||||
**Please provide Issues links to:**
|
||||
|
||||
- Issues: #1
|
||||
|
||||
**Provide test code:**
|
||||
|
||||
```Go
|
||||
|
||||
```
|
||||
```Go
|
||||
|
||||
```
|
||||
|
||||
## Description
|
||||
|
||||
|
30
.github/workflows/go.yml
vendored
Normal file
30
.github/workflows/go.yml
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
name: Go
|
||||
on: [push]
|
||||
jobs:
|
||||
test:
|
||||
# name: build
|
||||
strategy:
|
||||
matrix:
|
||||
# go: [1.12.x, 1.13.x]
|
||||
os: [macOS-latest, windows-latest] # ubuntu-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Set up Go 1.24.0
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.24.0
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Get dependencies
|
||||
run: |
|
||||
go get -v -t -d ./...
|
||||
|
||||
- name: Build
|
||||
run: go build -v .
|
||||
- name: Test
|
||||
run: go test -v robot_info_test.go
|
||||
# run: go test -v .
|
44
.gitignore
vendored
44
.gitignore
vendored
@ -3,6 +3,19 @@
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
robot_test.png
|
||||
# Examples
|
||||
examples/screen/screen
|
||||
examples/screen/saveCapture.png
|
||||
#
|
||||
examples/bitmap/test.png
|
||||
examples/bitmap/test2.png
|
||||
examples/bitmap/test.tif
|
||||
examples/bitmap/test31.tif
|
||||
examples/bitmap/tocbitmap.png
|
||||
examples/bitmap/teststr.png
|
||||
test/test.png
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
@ -29,9 +42,29 @@ _testmain.go
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
#
|
||||
cdeps/hook
|
||||
event/hook
|
||||
vendor
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
# !cdeps/win32/libpng.lib
|
||||
# !cdeps/win32/zlib.lib
|
||||
# !cdeps/win64/libpng.lib
|
||||
# !cdeps/win64/zlib.lib
|
||||
# ##
|
||||
*.a
|
||||
!cdeps/mac/libpng.a
|
||||
!cdeps/mac/amd/libpng.a
|
||||
!cdeps/mac/m1/libpng.a
|
||||
#
|
||||
!cdeps/win32/libpng.a
|
||||
!cdeps/win64/libpng.a
|
||||
!cdeps/win/amd/win32/libpng.a
|
||||
!cdeps/win/amd/win64/libpng.a
|
||||
!cdeps/win/arm/libpng.a
|
||||
#
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
@ -52,3 +85,14 @@ _testmain.go
|
||||
# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
|
||||
.glide/
|
||||
|
||||
#
|
||||
examples/bitmap/test_IMG.png
|
||||
examples/bitmap/imgToBitmap/test_01.png
|
||||
examples/bitmap/imgToBitmap/test_002.jpeg
|
||||
examples/bitmap/imgToBitmap/test_003.jpeg
|
||||
examples/bitmap/imgToBitmap/test_1.png
|
||||
examples/bitmap/imgToBitmap/test_2.jpeg
|
||||
examples/bitmap/imgToBitmap/test.png
|
||||
examples/bitmap/imgToBitmap/test_7.jpeg
|
||||
robot_img.png
|
||||
examples/bitmap/bitmapTobytes/out.jpg
|
||||
|
54
.travis.yml
Normal file
54
.travis.yml
Normal file
@ -0,0 +1,54 @@
|
||||
language: go
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
go:
|
||||
# - 1.7.x
|
||||
# - 1.8.x
|
||||
# - 1.9.x
|
||||
# - 1.10.x
|
||||
# - 1.11.x
|
||||
# - 1.12.x
|
||||
# - 1.13.x
|
||||
# - 1.14.x
|
||||
# - 1.15.x
|
||||
# - 1.16.x
|
||||
# - 1.17.x
|
||||
# - 1.18.x
|
||||
- 1.19.x
|
||||
# - tip
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libx11-dev xorg-dev
|
||||
- libxtst-dev libpng++-dev
|
||||
- xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev
|
||||
# - libusb-dev
|
||||
- libxkbcommon-dev
|
||||
- xsel
|
||||
- xclip
|
||||
#
|
||||
- xvfb
|
||||
# script:
|
||||
# - sudo apt update
|
||||
# - sudo apt install libx11-dev
|
||||
# - sudo apt install xorg-dev
|
||||
# - sudo apt install libxtst-dev libpng++-dev
|
||||
# # Event:
|
||||
# - sudo apt install xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev
|
||||
# - sudo apt install libxkbcommon-dev
|
||||
# # Clipboard:
|
||||
# - sudo apt install xsel
|
||||
# - sudo apt install xclip
|
||||
# - go test -v .
|
||||
|
||||
before_install:
|
||||
- export PATH=$PATH:$HOME/gopath/bin
|
||||
- go get -v -t -d ./...
|
||||
|
||||
script:
|
||||
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then xvfb-run go test -v ./...; fi
|
||||
- if [ "${TRAVIS_OS_NAME}" = "osx" ]; then go test -v ./...; fi
|
@ -24,25 +24,23 @@ This process gives everyone a chance to validate the design, helps prevent dupli
|
||||
|
||||
## Testing redux
|
||||
|
||||
Before sending code out for review, run all the tests for the whole tree to make sure the changes don't break other usage and keep the compatibility on upgrade. You must be test on Mac, Windows, Linux and other. You should install the CLI for Circle CI, as we are using the server for continous testing.
|
||||
Before sending code out for review, run all the tests for the whole tree to make sure the changes don't break other usage and keep the compatibility on upgrade. You must be test on Mac, Windows, Linux and other. You should install the CLI for Circle CI, as we are using the server for continuous testing.
|
||||
|
||||
## Code review
|
||||
|
||||
In addition to the owner, Changes to Robotgo must be reviewed before they are accepted, no matter who makes the change even if it is a maintainer. We use GitHub's pull request workflow to do that and we also use [LGTM](http://lgtm.co) to ensure every PR is reviewed by vz or least 2 maintainers.
|
||||
|
||||
|
||||
## Sign your work
|
||||
|
||||
The sign-off is a simple line at the end of the explanation for the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch.
|
||||
The sign-off is a simple line at the end of the explanation for the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch.
|
||||
|
||||
## Maintainers
|
||||
|
||||
To make sure every PR is checked, we got team maintainers. A maintainer should be a contributor of Robotgo and contributed at least 4 accepted PRs.
|
||||
To make sure every PR is checked, we got team maintainers. A maintainer should be a contributor of Robotgo and contributed at least 4 accepted PRs.
|
||||
|
||||
## Owners
|
||||
|
||||
Since Robotgo is a pure community organization without any company support, Copyright 2016-2017 The go-vgo Project Developers.
|
||||
|
||||
Since Robotgo is a pure community organization without any company support, Copyright 2016 The go-vgo Project Developers.
|
||||
|
||||
## Versions
|
||||
|
||||
@ -55,14 +53,14 @@ Since the `master` branch is a tip version, if you wish to use Robotgo in produc
|
||||
Code that you contribute should use the standard copyright header:
|
||||
|
||||
```
|
||||
// Copyright 2016-2017 The go-vgo Project Developers. See the COPYRIGHT
|
||||
// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// https://github.com/go-vgo/robotgo/blob/master/LICENSE
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// http://www.apache.org/licenses/LICENSE-2.0>
|
||||
//
|
||||
// This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
```
|
||||
|
||||
|
214
LICENSE
214
LICENSE
@ -1,23 +1,201 @@
|
||||
MIT License
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
Copyright (c) 2016 go-vgo Project Developers
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
View with Golang|Es6
|
||||
1. Definitions.
|
||||
|
||||
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:
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
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.
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
478
README.md
478
README.md
@ -1,117 +1,177 @@
|
||||
# Robotgo
|
||||
|
||||
<!--<img align="right" src="https://raw.githubusercontent.com/go-vgo/robotgo/master/logo.jpg">-->
|
||||
<!--[](https://travis-ci.org/go-vgo/robotgo)
|
||||
[](https://codecov.io/gh/go-vgo/robotgo)-->
|
||||
<!--<a href="https://circleci.com/gh/go-vgo/robotgo/tree/dev"><img src="https://img.shields.io/circleci/project/go-vgo/robotgo/dev.svg" alt="Build Status"></a>-->
|
||||
|
||||
[](https://github.com/go-vgo/robotgo/commits/master)
|
||||
[](https://circleci.com/gh/go-vgo/robotgo)
|
||||
[](https://travis-ci.org/go-vgo/robotgo)
|
||||

|
||||
[](https://goreportcard.com/report/github.com/go-vgo/robotgo)
|
||||
[](https://godoc.org/github.com/go-vgo/robotgo)
|
||||
[](https://github.com/go-vgo/robotgo/releases/latest)
|
||||
[](https://pkg.go.dev/github.com/go-vgo/robotgo?tab=doc)
|
||||
[](https://github.com/go-vgo/robotgo/releases/latest)
|
||||
[](https://gitter.im/go-vgo/robotgo?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
<!--<a href="https://github.com/go-vgo/robotgo/releases"><img src="https://img.shields.io/badge/%20version%20-%206.0.0%20-blue.svg?style=flat-square" alt="Releases"></a>-->
|
||||
|
||||
>Golang Desktop Automation. Control the mouse, keyboard, bitmap, read the screen, Window Handle and global event listener.
|
||||
|
||||
RobotGo supports Mac, Windows, and Linux(X11).
|
||||
|
||||
This is a work in progress.
|
||||
<!-- [](https://github.com/go-vgo/robotgo/releases/latest) -->
|
||||
<!-- <a href="https://github.com/go-vgo/robotgo/releases"><img src="https://img.shields.io/badge/%20version%20-%206.0.0%20-blue.svg?style=flat-square" alt="Releases"></a> -->
|
||||
|
||||
[简体中文](https://github.com/go-vgo/robotgo/blob/master/README_zh.md)
|
||||
> Golang Desktop Automation. Control the mouse, keyboard, read the screen, process, Window Handle, image and bitmap and global event listener.
|
||||
|
||||
RobotGo supports Mac, Windows, and Linux(X11); and robotgo supports arm64 and x86-amd64.
|
||||
|
||||
## Contents
|
||||
|
||||
- [Docs](#docs)
|
||||
- [Binding](#binding)
|
||||
- [Requirements](#requirements)
|
||||
- [Installation](#installation)
|
||||
- [Update](#update)
|
||||
- [Examples](#examples)
|
||||
- [Type Conversion and keys](https://github.com/go-vgo/robotgo/blob/master/docs/keys.md)
|
||||
- [Cross-Compiling](https://github.com/go-vgo/robotgo/blob/master/docs/install.md#crosscompiling)
|
||||
- [Authors](#authors)
|
||||
- [Plans](#plans)
|
||||
- [Donate](#donate)
|
||||
- [Contributors](#contributors)
|
||||
- [License](#license)
|
||||
|
||||
## Docs
|
||||
- [API Docs](https://github.com/go-vgo/robotgo/blob/master/docs/doc.md)
|
||||
- [中文文档](https://github.com/go-vgo/robotgo/blob/master/docs/doc_zh.md)
|
||||
- [GoDoc](https://godoc.org/github.com/go-vgo/robotgo)
|
||||
|
||||
- [GoDoc](https://godoc.org/github.com/go-vgo/robotgo) <br>
|
||||
- [API Docs](https://github.com/go-vgo/robotgo/blob/master/docs/doc.md) (Deprecated, no updated)
|
||||
|
||||
## Binding:
|
||||
|
||||
[ADB](https://github.com/vcaesar/adb), packaging android adb API.
|
||||
|
||||
[Robotn](https://github.com/vcaesar/robotn), binding JavaScript and other, support more language.
|
||||
|
||||
## Requirements:
|
||||
|
||||
Now, Please make sure Golang, GCC, zlib and libpng is installed correctly before installing RobotGo.
|
||||
Now, Please make sure `Golang, GCC` is installed correctly before installing RobotGo.
|
||||
|
||||
If you do not need bitmap, you can switch to the robot branch, where there is no zlib and libpng dependency.
|
||||
### ALL:
|
||||
|
||||
### ALL:
|
||||
```
|
||||
```
|
||||
Golang
|
||||
|
||||
GCC
|
||||
zlib & libpng (bitmap)
|
||||
```
|
||||
#### For Mac OS X:
|
||||
Xcode Command Line Tools
|
||||
|
||||
#### For MacOS:
|
||||
|
||||
```
|
||||
brew install libpng
|
||||
brew install homebrew/dupes/zlib
|
||||
```
|
||||
brew install go
|
||||
```
|
||||
|
||||
Xcode Command Line Tools (And Privacy setting: [#277](https://github.com/go-vgo/robotgo/issues/277))
|
||||
|
||||
```
|
||||
xcode-select --install
|
||||
```
|
||||
|
||||
#### For Windows:
|
||||
```
|
||||
MinGW or other GCC
|
||||
|
||||
zlib & libpng (bitmap need it.)
|
||||
```
|
||||
##### [Zlib & libpng Windows32 GCC's Course](https://github.com/go-vgo/Mingw32)
|
||||
winget install Golang.go
|
||||
```
|
||||
|
||||
##### [Download include zlib & libpng Windows64 GCC](https://github.com/go-vgo/Mingw)
|
||||
```
|
||||
winget install MartinStorsjo.LLVM-MinGW.UCRT
|
||||
```
|
||||
|
||||
Or [MinGW-w64](https://sourceforge.net/projects/mingw-w64/files) (Use recommended) or others Mingw [llvm-mingw](https://github.com/mstorsjo/llvm-mingw);
|
||||
|
||||
Download the Mingw, then set system environment variables `C:\mingw64\bin` to the Path.
|
||||
[Set environment variables to run GCC from command line](https://www.youtube.com/results?search_query=Set+environment+variables+to+run+GCC+from+command+line).
|
||||
|
||||
`Or the other GCC` (But you should compile the "libpng" with yourself when use the [bitmap](https://github.com/vcaesar/bitmap).)
|
||||
|
||||
#### For everything else:
|
||||
|
||||
```
|
||||
GCC
|
||||
|
||||
X11 with the XTest extension (also known as the Xtst library)
|
||||
|
||||
Event:
|
||||
|
||||
xcb, xkb, libxkbcommon
|
||||
```
|
||||
X11 with the XTest extension (the Xtst library)
|
||||
|
||||
##### Ubuntu:
|
||||
```yml
|
||||
sudo apt-get install gcc libc6-dev
|
||||
"Clipboard": xsel xclip
|
||||
|
||||
sudo apt-get install libx11-dev
|
||||
sudo apt-get install xorg-dev
|
||||
sudo apt-get install libxtst-dev libpng++-dev
|
||||
|
||||
sudo apt-get install xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev
|
||||
sudo apt-get install libxkbcommon-dev
|
||||
"Bitmap": libpng (Just used by the "bitmap".)
|
||||
|
||||
"Event-Gohook": xcb, xkb, libxkbcommon (Just used by the "hook".)
|
||||
|
||||
sudo apt-get install xsel
|
||||
sudo apt-get install xclip
|
||||
```
|
||||
|
||||
#### Fedora:
|
||||
##### Ubuntu:
|
||||
|
||||
```yml
|
||||
sudo dnf install libxkbcommon-devel libXtst-devel libxkbcommon-x11-devel xorg-x11-xkb-utils-devel
|
||||
# sudo apt install golang
|
||||
sudo snap install go --classic
|
||||
|
||||
# gcc
|
||||
sudo apt install gcc libc6-dev
|
||||
|
||||
# x11
|
||||
sudo apt install libx11-dev xorg-dev libxtst-dev
|
||||
|
||||
# Clipboard
|
||||
sudo apt install xsel xclip
|
||||
|
||||
#
|
||||
# Bitmap
|
||||
sudo apt install libpng++-dev
|
||||
|
||||
# GoHook
|
||||
sudo apt install xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev libxkbcommon-dev
|
||||
|
||||
```
|
||||
|
||||
##### Fedora:
|
||||
|
||||
```yml
|
||||
# x11
|
||||
sudo dnf install libXtst-devel
|
||||
|
||||
# Clipboard
|
||||
sudo dnf install xsel xclip
|
||||
|
||||
#
|
||||
# Bitmap
|
||||
sudo dnf install libpng-devel
|
||||
|
||||
sudo dnf install xsel
|
||||
sudo dnf install xclip
|
||||
# GoHook
|
||||
sudo dnf install libxkbcommon-devel libxkbcommon-x11-devel xorg-x11-xkb-utils-devel
|
||||
|
||||
```
|
||||
|
||||
## Installation:
|
||||
|
||||
With Go module support (Go 1.11+), just import:
|
||||
|
||||
```go
|
||||
import "github.com/go-vgo/robotgo"
|
||||
```
|
||||
|
||||
Otherwise, to install the robotgo package, run the command:
|
||||
|
||||
```
|
||||
go get github.com/go-vgo/robotgo
|
||||
```
|
||||
It's that easy!
|
||||
|
||||
png.h: No such file or directory? Please see [issues/47](https://github.com/go-vgo/robotgo/issues/47).
|
||||
|
||||
## Update:
|
||||
|
||||
```
|
||||
go get -u github.com/go-vgo/robotgo
|
||||
go get -u github.com/go-vgo/robotgo
|
||||
```
|
||||
|
||||
Note go1.10.x C file compilation cache problem, [golang #24355](https://github.com/golang/go/issues/24355).
|
||||
`go mod vendor` problem, [golang #26366](https://github.com/golang/go/issues/26366).
|
||||
|
||||
## [Examples:](https://github.com/go-vgo/robotgo/blob/master/examples)
|
||||
|
||||
#### [Mouse](https://github.com/go-vgo/robotgo/blob/master/examples/mouse/main.go)
|
||||
@ -120,15 +180,41 @@ go get -u github.com/go-vgo/robotgo
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/go-vgo/robotgo"
|
||||
"fmt"
|
||||
"github.com/go-vgo/robotgo"
|
||||
)
|
||||
|
||||
func main() {
|
||||
robotgo.ScrollMouse(10, "up")
|
||||
robotgo.MouseClick("left", true)
|
||||
robotgo.MoveMouseSmooth(100, 200, 1.0, 100.0)
|
||||
}
|
||||
```
|
||||
robotgo.MouseSleep = 300
|
||||
|
||||
robotgo.Move(100, 100)
|
||||
fmt.Println(robotgo.Location())
|
||||
robotgo.Move(100, -200) // multi screen supported
|
||||
robotgo.MoveSmooth(120, -150)
|
||||
fmt.Println(robotgo.Location())
|
||||
|
||||
robotgo.ScrollDir(10, "up")
|
||||
robotgo.ScrollDir(20, "right")
|
||||
|
||||
robotgo.Scroll(0, -10)
|
||||
robotgo.Scroll(100, 0)
|
||||
|
||||
robotgo.MilliSleep(100)
|
||||
robotgo.ScrollSmooth(-10, 6)
|
||||
// robotgo.ScrollRelative(10, -100)
|
||||
|
||||
robotgo.Move(10, 20)
|
||||
robotgo.MoveRelative(0, -10)
|
||||
robotgo.DragSmooth(10, 10)
|
||||
|
||||
robotgo.Click("wheelRight")
|
||||
robotgo.Click("left", true)
|
||||
robotgo.MoveSmooth(100, 200, 1.0, 10.0)
|
||||
|
||||
robotgo.Toggle("left")
|
||||
robotgo.Toggle("left", "up")
|
||||
}
|
||||
```
|
||||
|
||||
#### [Keyboard](https://github.com/go-vgo/robotgo/blob/master/examples/key/main.go)
|
||||
|
||||
@ -142,19 +228,35 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
robotgo.TypeString("Hello World")
|
||||
robotgo.TypeStr("Hello World")
|
||||
robotgo.TypeStr("だんしゃり", 0, 1)
|
||||
// robotgo.TypeStr("テストする")
|
||||
|
||||
robotgo.TypeStr("Hi, Seattle space needle, Golden gate bridge, One world trade center.")
|
||||
robotgo.TypeStr("Hi galaxy, hi stars, hi MT.Rainier, hi sea. こんにちは世界.")
|
||||
robotgo.Sleep(1)
|
||||
|
||||
// ustr := uint32(robotgo.CharCodeAt("Test", 0))
|
||||
// robotgo.UnicodeType(ustr)
|
||||
|
||||
robotgo.KeySleep = 100
|
||||
robotgo.KeyTap("enter")
|
||||
robotgo.TypeString("en")
|
||||
robotgo.KeyTap("i", "alt", "command")
|
||||
arr := []string{"alt", "command"}
|
||||
// robotgo.TypeStr("en")
|
||||
robotgo.KeyTap("i", "alt", "cmd")
|
||||
|
||||
arr := []string{"alt", "cmd"}
|
||||
robotgo.KeyTap("i", arr)
|
||||
|
||||
robotgo.MilliSleep(100)
|
||||
robotgo.KeyToggle("a")
|
||||
robotgo.KeyToggle("a", "up")
|
||||
|
||||
robotgo.WriteAll("Test")
|
||||
text, err := robotgo.ReadAll()
|
||||
if err == nil {
|
||||
fmt.Println(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### [Screen](https://github.com/go-vgo/robotgo/blob/master/examples/screen/main.go)
|
||||
@ -163,63 +265,207 @@ func main() {
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-vgo/robotgo"
|
||||
"github.com/go-vgo/robotgo"
|
||||
"github.com/vcaesar/imgo"
|
||||
)
|
||||
|
||||
func main() {
|
||||
x, y := robotgo.GetMousePos()
|
||||
fmt.Println("pos:", x, y)
|
||||
x, y := robotgo.Location()
|
||||
fmt.Println("pos: ", x, y)
|
||||
|
||||
color := robotgo.GetPixelColor(100, 200)
|
||||
fmt.Println("color----", color)
|
||||
}
|
||||
fmt.Println("color---- ", color)
|
||||
|
||||
sx, sy := robotgo.GetScreenSize()
|
||||
fmt.Println("get screen size: ", sx, sy)
|
||||
|
||||
bit := robotgo.CaptureScreen(10, 10, 30, 30)
|
||||
defer robotgo.FreeBitmap(bit)
|
||||
|
||||
img := robotgo.ToImage(bit)
|
||||
imgo.Save("test.png", img)
|
||||
|
||||
num := robotgo.DisplaysNum()
|
||||
for i := 0; i < num; i++ {
|
||||
robotgo.DisplayID = i
|
||||
img1, _ := robotgo.CaptureImg()
|
||||
path1 := "save_" + strconv.Itoa(i)
|
||||
robotgo.Save(img1, path1+".png")
|
||||
robotgo.SaveJpeg(img1, path1+".jpeg", 50)
|
||||
|
||||
img2, _ := robotgo.CaptureImg(10, 10, 20, 20)
|
||||
robotgo.Save(img2, "test_"+strconv.Itoa(i)+".png")
|
||||
|
||||
x, y, w, h := robotgo.GetDisplayBounds(i)
|
||||
img3, err := robotgo.CaptureImg(x, y, w, h)
|
||||
fmt.Println("Capture error: ", err)
|
||||
robotgo.Save(img3, path1+"_1.png")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### [Bitmap](https://github.com/go-vgo/robotgo/blob/master/examples/bitmap/mian.go)
|
||||
#### [Bitmap](https://github.com/vcaesar/bitmap/blob/main/examples/main.go)
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-vgo/robotgo"
|
||||
"github.com/go-vgo/robotgo"
|
||||
"github.com/vcaesar/bitmap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
bitmap := robotgo.CaptureScreen(10, 20, 30, 40)
|
||||
fmt.Println("...", bitmap)
|
||||
bit := robotgo.CaptureScreen(10, 20, 30, 40)
|
||||
// use `defer robotgo.FreeBitmap(bit)` to free the bitmap
|
||||
defer robotgo.FreeBitmap(bit)
|
||||
|
||||
fx, fy := robotgo.FindBitmap(bitmap)
|
||||
fmt.Println("FindBitmap------", fx, fy)
|
||||
fmt.Println("bitmap...", bit)
|
||||
img := robotgo.ToImage(bit)
|
||||
// robotgo.SavePng(img, "test_1.png")
|
||||
robotgo.Save(img, "test_1.png")
|
||||
|
||||
robotgo.SaveBitmap(bitmap, "test.png")
|
||||
}
|
||||
bit2 := robotgo.ToCBitmap(robotgo.ImgToBitmap(img))
|
||||
fx, fy := bitmap.Find(bit2)
|
||||
fmt.Println("FindBitmap------ ", fx, fy)
|
||||
robotgo.Move(fx, fy)
|
||||
|
||||
arr := bitmap.FindAll(bit2)
|
||||
fmt.Println("Find all bitmap: ", arr)
|
||||
|
||||
fx, fy = bitmap.Find(bit)
|
||||
fmt.Println("FindBitmap------ ", fx, fy)
|
||||
|
||||
bitmap.Save(bit, "test.png")
|
||||
}
|
||||
```
|
||||
|
||||
#### [Event](https://github.com/go-vgo/robotgo/blob/master/examples/event/main.go)
|
||||
#### [OpenCV](https://github.com/vcaesar/gcv)
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/go-vgo/robotgo"
|
||||
"github.com/go-vgo/robotgo"
|
||||
"github.com/vcaesar/gcv"
|
||||
"github.com/vcaesar/bitmap"
|
||||
)
|
||||
|
||||
func main() {
|
||||
keve := robotgo.AddEvent("k")
|
||||
if keve == 0 {
|
||||
fmt.Println("you press...", "k")
|
||||
opencv()
|
||||
}
|
||||
|
||||
func opencv() {
|
||||
name := "test.png"
|
||||
name1 := "test_001.png"
|
||||
robotgo.SaveCapture(name1, 10, 10, 30, 30)
|
||||
robotgo.SaveCapture(name)
|
||||
|
||||
fmt.Print("gcv find image: ")
|
||||
fmt.Println(gcv.FindImgFile(name1, name))
|
||||
fmt.Println(gcv.FindAllImgFile(name1, name))
|
||||
|
||||
bit := bitmap.Open(name1)
|
||||
defer robotgo.FreeBitmap(bit)
|
||||
fmt.Print("find bitmap: ")
|
||||
fmt.Println(bitmap.Find(bit))
|
||||
|
||||
// bit0 := robotgo.CaptureScreen()
|
||||
// img := robotgo.ToImage(bit0)
|
||||
// bit1 := robotgo.CaptureScreen(10, 10, 30, 30)
|
||||
// img1 := robotgo.ToImage(bit1)
|
||||
// defer robotgo.FreeBitmapArr(bit0, bit1)
|
||||
img, _ := robotgo.CaptureImg()
|
||||
img1, _ := robotgo.CaptureImg(10, 10, 30, 30)
|
||||
|
||||
fmt.Print("gcv find image: ")
|
||||
fmt.Println(gcv.FindImg(img1, img))
|
||||
fmt.Println()
|
||||
|
||||
res := gcv.FindAllImg(img1, img)
|
||||
fmt.Println(res[0].TopLeft.Y, res[0].Rects.TopLeft.X, res)
|
||||
x, y := res[0].TopLeft.X, res[0].TopLeft.Y
|
||||
robotgo.Move(x, y-rand.Intn(5))
|
||||
robotgo.MilliSleep(100)
|
||||
robotgo.Click()
|
||||
|
||||
res = gcv.FindAll(img1, img) // use find template and sift
|
||||
fmt.Println("find all: ", res)
|
||||
res1 := gcv.Find(img1, img)
|
||||
fmt.Println("find: ", res1)
|
||||
|
||||
img2, _, _ := robotgo.DecodeImg("test_001.png")
|
||||
x, y = gcv.FindX(img2, img)
|
||||
fmt.Println(x, y)
|
||||
}
|
||||
```
|
||||
|
||||
#### [Event](https://github.com/robotn/gohook/blob/master/examples/main.go)
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
// "github.com/go-vgo/robotgo"
|
||||
hook "github.com/robotn/gohook"
|
||||
)
|
||||
|
||||
func main() {
|
||||
add()
|
||||
low()
|
||||
event()
|
||||
}
|
||||
|
||||
func add() {
|
||||
fmt.Println("--- Please press ctrl + shift + q to stop hook ---")
|
||||
hook.Register(hook.KeyDown, []string{"q", "ctrl", "shift"}, func(e hook.Event) {
|
||||
fmt.Println("ctrl-shift-q")
|
||||
hook.End()
|
||||
})
|
||||
|
||||
fmt.Println("--- Please press w---")
|
||||
hook.Register(hook.KeyDown, []string{"w"}, func(e hook.Event) {
|
||||
fmt.Println("w")
|
||||
})
|
||||
|
||||
s := hook.Start()
|
||||
<-hook.Process(s)
|
||||
}
|
||||
|
||||
func low() {
|
||||
evChan := hook.Start()
|
||||
defer hook.End()
|
||||
|
||||
for ev := range evChan {
|
||||
fmt.Println("hook: ", ev)
|
||||
}
|
||||
}
|
||||
|
||||
func event() {
|
||||
ok := hook.AddEvents("q", "ctrl", "shift")
|
||||
if ok {
|
||||
fmt.Println("add events...")
|
||||
}
|
||||
|
||||
mleft := robotgo.AddEvent("mleft")
|
||||
if mleft == 0 {
|
||||
fmt.Println("you press...", "mouse left button")
|
||||
keve := hook.AddEvent("k")
|
||||
if keve {
|
||||
fmt.Println("you press... ", "k")
|
||||
}
|
||||
}
|
||||
|
||||
mleft := hook.AddEvent("mleft")
|
||||
if mleft {
|
||||
fmt.Println("you press... ", "mouse left button")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### [Window](https://github.com/go-vgo/robotgo/blob/master/examples/window/main.go)
|
||||
@ -228,47 +474,61 @@ func main() {
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-vgo/robotgo"
|
||||
"github.com/go-vgo/robotgo"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fpid, err := robotgo.FindIds("Google")
|
||||
if err == nil {
|
||||
fmt.Println("pids...", fpid)
|
||||
fmt.Println("pids... ", fpid)
|
||||
|
||||
if len(fpid) > 0 {
|
||||
robotgo.TypeStr("Hi galaxy!", fpid[0])
|
||||
robotgo.KeyTap("a", fpid[0], "cmd")
|
||||
|
||||
robotgo.KeyToggle("a", fpid[0])
|
||||
robotgo.KeyToggle("a", fpid[0], "up")
|
||||
|
||||
robotgo.ActivePid(fpid[0])
|
||||
|
||||
robotgo.Kill(fpid[0])
|
||||
}
|
||||
}
|
||||
|
||||
robotgo.ActiveName("chrome")
|
||||
|
||||
isExist, err := robotgo.PidExists(100)
|
||||
if err == nil {
|
||||
if err == nil && isExist {
|
||||
fmt.Println("pid exists is", isExist)
|
||||
|
||||
robotgo.Kill(100)
|
||||
}
|
||||
|
||||
abool := robotgo.ShowAlert("test", "robotgo")
|
||||
if abool == 0 {
|
||||
fmt.Println("ok@@@", "ok")
|
||||
abool := robotgo.Alert("test", "robotgo")
|
||||
if abool {
|
||||
fmt.Println("ok@@@ ", "ok")
|
||||
}
|
||||
|
||||
title := robotgo.GetTitle()
|
||||
fmt.Println("title@@@", title)
|
||||
}
|
||||
fmt.Println("title@@@ ", title)
|
||||
}
|
||||
```
|
||||
|
||||
## Authors
|
||||
|
||||
- [The author is vz](https://github.com/vcaesar)
|
||||
- [Maintainers](https://github.com/orgs/go-vgo/people)
|
||||
- [Contributors](https://github.com/go-vgo/robotgo/graphs/contributors)
|
||||
|
||||
## Plans
|
||||
- Update Find an image on screen, read pixels from an image
|
||||
|
||||
- Refactor some C code to Go (such as x11, windows)
|
||||
- Better multiscreen support
|
||||
- Wayland support
|
||||
- Update Window Handle
|
||||
- Support UTF-8, transitional plan: TypeStr
|
||||
- Try support Android, maybe support IOS
|
||||
- Remove zlib/libpng dependencies
|
||||
|
||||
## Donate
|
||||
|
||||
Supporting robotgo, [buy me a coffee](https://github.com/go-vgo/buy-me-a-coffee).
|
||||
|
||||
#### Paypal
|
||||
|
||||
Donate money by [paypal](https://www.paypal.me/veni0/25) to my account [vzvway@gmail.com](vzvway@gmail.com)
|
||||
|
||||
- Try to support Android and IOS
|
||||
|
||||
## Contributors
|
||||
|
||||
@ -277,6 +537,6 @@ Donate money by [paypal](https://www.paypal.me/veni0/25) to my account [vzvway@g
|
||||
|
||||
## License
|
||||
|
||||
Robotgo is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0), with portions covered by various BSD-like licenses.
|
||||
Robotgo is primarily distributed under the terms of "the Apache License (Version 2.0)", with portions covered by various BSD-like licenses.
|
||||
|
||||
See [LICENSE-APACHE](http://www.apache.org/licenses/LICENSE-2.0), [LICENSE-MIT](https://github.com/go-vgo/robotgo/blob/master/LICENSE).
|
||||
See [LICENSE-APACHE](http://www.apache.org/licenses/LICENSE-2.0), [LICENSE](https://github.com/go-vgo/robotgo/blob/master/LICENSE).
|
||||
|
395
README_zh.md
395
README_zh.md
@ -1,115 +1,141 @@
|
||||
# Robotgo
|
||||
|
||||
## !!! Warning: this page not updated !!!
|
||||
|
||||
[](https://github.com/go-vgo/robotgo/commits/master)
|
||||
[](https://circleci.com/gh/go-vgo/robotgo)
|
||||
[](https://travis-ci.org/go-vgo/robotgo)
|
||||

|
||||
[](https://goreportcard.com/report/github.com/go-vgo/robotgo)
|
||||
[](https://godoc.org/github.com/go-vgo/robotgo)
|
||||
[](https://github.com/go-vgo/robotgo/releases/latest)
|
||||
[](https://github.com/go-vgo/robotgo/releases/latest)
|
||||
[](https://gitter.im/go-vgo/robotgo?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
>Golang 跨平台自动化系统,控制键盘鼠标位图和读取屏幕,窗口句柄以及全局事件监听
|
||||
|
||||
|
||||
> Golang 跨平台自动化系统,控制键盘、鼠标、位图、图像、读取屏幕,进程、窗口句柄以及全局事件监听
|
||||
|
||||
RobotGo 支持 Mac, Windows, and Linux(X11).
|
||||
|
||||
这是一项正在完善中的工作.
|
||||
<br>
|
||||
|
||||
提 Issues 请到 [Github](https://github.com/go-vgo/robotgo), 便于统一管理和即时更新
|
||||
|
||||
QQ 群: 595877611
|
||||
提 Issues 请到 [Github](https://github.com/go-vgo/robotgo), 便于统一管理和即时更新; `REDAME_zh.md 已废弃, 不再更新`
|
||||
|
||||
## Contents
|
||||
- [Docs](#docs)
|
||||
- [Binding](#binding)
|
||||
- [Requirements](#requirements)
|
||||
- [Installation](#installation)
|
||||
- [Update](#update)
|
||||
- [Examples](#examples)
|
||||
- [Cross-Compiling](#crosscompiling)
|
||||
- [Authors](#authors)
|
||||
- [Plans](#plans)
|
||||
- [Donate](#donate)
|
||||
- [Contributors](#contributors)
|
||||
- [License](#license)
|
||||
|
||||
## Docs
|
||||
- [中文文档](https://github.com/go-vgo/robotgo/blob/master/docs/doc_zh.md)
|
||||
- [English Docs](https://github.com/go-vgo/robotgo/blob/master/docs/doc.md)
|
||||
- [GoDoc](https://godoc.org/github.com/go-vgo/robotgo)
|
||||
- [GoDoc](https://godoc.org/github.com/go-vgo/robotgo) <br>
|
||||
|
||||
<!-- - [中文文档](https://github.com/go-vgo/robotgo/blob/master/docs/doc_zh.md) (弃用)
|
||||
- [English Docs](https://github.com/go-vgo/robotgo/blob/master/docs/doc.md) (弃用) -->
|
||||
|
||||
## Binding:
|
||||
|
||||
[Robotn](https://github.com/vcaesar/robotn), binding JavaScript and other, support more language.
|
||||
|
||||
## Requirements:
|
||||
环境要求:
|
||||
|
||||
在安装 RobotGo 之前, 请确保 Golang、GCC、zlib 和 libpng 被正确安装
|
||||
在安装 RobotGo 之前, 请确保 `Golang、GCC` 被正确安装
|
||||
|
||||
如果你不需要bitmap, 你可以切换到robot分支, 那里没有 zlib 和 libpng 依赖
|
||||
|
||||
### ALL:
|
||||
### ALL:
|
||||
```
|
||||
Golang
|
||||
GCC
|
||||
zlib & libpng (bitmap)
|
||||
```
|
||||
#### For Mac OS X:
|
||||
Xcode Command Line Tools
|
||||
```
|
||||
brew install libpng
|
||||
brew install homebrew/dupes/zlib
|
||||
```
|
||||
#### For Windows:
|
||||
```
|
||||
MinGW or other GCC
|
||||
|
||||
zlib & libpng (bitmap 依赖)
|
||||
GCC
|
||||
```
|
||||
|
||||
#### For Mac OS X:
|
||||
|
||||
Xcode Command Line Tools (And Privacy setting: [#277](https://github.com/go-vgo/robotgo/issues/277) )
|
||||
|
||||
```
|
||||
xcode-select --install
|
||||
```
|
||||
|
||||
#### For Windows:
|
||||
|
||||
[MinGW-w64](https://sourceforge.net/projects/mingw-w64/files) (推荐使用)
|
||||
|
||||
```
|
||||
Or the other GCC (But you should compile the "libpng" with yourself.
|
||||
Or you can removed the bitmap.go.)
|
||||
```
|
||||
##### [Zlib & libpng Windows32 GCC 教程](https://github.com/go-vgo/Mingw32)
|
||||
##### [下载包含 zlib 和 libpng 的 64位 MinGW](https://github.com/go-vgo/Mingw)
|
||||
|
||||
#### For everything else (Linux 等其他系统):
|
||||
|
||||
```
|
||||
GCC
|
||||
|
||||
GCC,
|
||||
libpng(bitmap)
|
||||
|
||||
X11 with the XTest extension (also known as the Xtst library)
|
||||
|
||||
事件:
|
||||
|
||||
|
||||
xcb, xkb, libxkbcommon
|
||||
|
||||
Clipboard:
|
||||
|
||||
xsel xclip
|
||||
```
|
||||
|
||||
##### Ubuntu:
|
||||
|
||||
```yml
|
||||
sudo apt-get install gcc libc6-dev
|
||||
sudo apt install gcc libc6-dev
|
||||
|
||||
sudo apt-get install libx11-dev
|
||||
sudo apt-get install xorg-dev
|
||||
sudo apt-get install libxtst-dev libpng++-dev
|
||||
sudo apt install libx11-dev xorg-dev libxtst-dev libpng++-dev
|
||||
|
||||
sudo apt install xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev libxkbcommon-dev
|
||||
|
||||
sudo apt-get install xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev
|
||||
sudo apt-get install libxkbcommon-dev
|
||||
|
||||
sudo apt-get install xsel
|
||||
sudo apt-get install xclip
|
||||
sudo apt install xsel xclip
|
||||
|
||||
```
|
||||
|
||||
#### Fedora:
|
||||
##### Fedora:
|
||||
|
||||
```yml
|
||||
sudo dnf install libxkbcommon-devel libXtst-devel libxkbcommon-x11-devel xorg-x11-xkb-utils-devel
|
||||
sudo dnf install libXtst-devel libxkbcommon-devel libxkbcommon-x11-devel xorg-x11-xkb-utils-devel
|
||||
|
||||
sudo dnf install libpng-devel
|
||||
|
||||
sudo dnf install xsel
|
||||
sudo dnf install xclip
|
||||
sudo dnf install xsel xclip
|
||||
```
|
||||
|
||||
## Installation:
|
||||
With Go module support (Go 1.11+), just import:
|
||||
|
||||
```go
|
||||
import "github.com/go-vgo/robotgo"
|
||||
```
|
||||
|
||||
Otherwise, to install the robotgo package, run the command:
|
||||
|
||||
```
|
||||
go get github.com/go-vgo/robotgo
|
||||
```
|
||||
It's that easy!
|
||||
|
||||
png.h: No such file or directory? Please see [issues/47](https://github.com/go-vgo/robotgo/issues/47).
|
||||
png.h: No such file or directory? Please see [issues/47](https://github.com/go-vgo/robotgo/issues/47).
|
||||
|
||||
## Update:
|
||||
```
|
||||
go get -u github.com/go-vgo/robotgo
|
||||
go get -u github.com/go-vgo/robotgo
|
||||
```
|
||||
|
||||
注意 go1.10.x C 文件编译缓存问题, [golang #24355](https://github.com/golang/go/issues/24355).
|
||||
`go mod vendor` problem, [golang #26366](https://github.com/golang/go/issues/26366).
|
||||
|
||||
|
||||
## [Examples:](https://github.com/go-vgo/robotgo/blob/master/examples)
|
||||
|
||||
#### [鼠标](https://github.com/go-vgo/robotgo/blob/master/examples/mouse/main.go)
|
||||
@ -122,37 +148,66 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
robotgo.ScrollMouse(10, "up")
|
||||
robotgo.MouseClick("left", true)
|
||||
robotgo.MoveMouseSmooth(100, 200, 1.0, 100.0)
|
||||
}
|
||||
```
|
||||
// robotgo.ScrollMouse(10, "up")
|
||||
robotgo.Scroll(0, -10)
|
||||
robotgo.Scroll(100, 0)
|
||||
robotgo.MilliSleep(100)
|
||||
// robotgo.ScrollRelative(10, -100)
|
||||
robotgo.ScrollSmooth(-10, 6)
|
||||
|
||||
robotgo.MouseSleep = 100
|
||||
robotgo.Move(10, 20)
|
||||
robotgo.MoveRelative(0, -10)
|
||||
robotgo.Drag(10, 10)
|
||||
|
||||
robotgo.Click("left", true)
|
||||
robotgo.MoveSmooth(100, 200, 1.0, 10.0)
|
||||
|
||||
robotgo.Toggle("left")
|
||||
robotgo.Toggle("left", "up")
|
||||
}
|
||||
```
|
||||
|
||||
#### [键盘](https://github.com/go-vgo/robotgo/blob/master/examples/key/main.go)
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-vgo/robotgo"
|
||||
)
|
||||
|
||||
func main() {
|
||||
robotgo.TypeString("Hello World")
|
||||
robotgo.TypeStr("Hello World. Winter is coming!")
|
||||
robotgo.TypeStr("だんしゃり", 1.0)
|
||||
// robotgo.TypeStr("テストする")
|
||||
|
||||
robotgo.TypeStr("Hi galaxy. こんにちは世界.")
|
||||
robotgo.Sleep(1)
|
||||
|
||||
// ustr := uint32(robotgo.CharCodeAt("テストする", 0))
|
||||
// robotgo.UnicodeType(ustr)
|
||||
|
||||
robotgo.KeySleep = 100
|
||||
robotgo.KeyTap("enter")
|
||||
robotgo.TypeString("en")
|
||||
// robotgo.TypeStr("en")
|
||||
robotgo.KeyTap("i", "alt", "command")
|
||||
|
||||
arr := []string{"alt", "command"}
|
||||
robotgo.KeyTap("i", arr)
|
||||
|
||||
robotgo.WriteAll("测试")
|
||||
robotgo.MilliSleep(100)
|
||||
robotgo.KeyToggle("a")
|
||||
robotgo.KeyToggle("a", "up")
|
||||
|
||||
robotgo.WriteAll("テストする")
|
||||
text, err := robotgo.ReadAll()
|
||||
if err == nil {
|
||||
fmt.Println(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### [屏幕](https://github.com/go-vgo/robotgo/blob/master/examples/screen/main.go)
|
||||
@ -164,17 +219,29 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-vgo/robotgo"
|
||||
"github.com/vcaesar/imgo"
|
||||
)
|
||||
|
||||
func main() {
|
||||
x, y := robotgo.GetMousePos()
|
||||
fmt.Println("pos:", x, y)
|
||||
fmt.Println("pos: ", x, y)
|
||||
|
||||
color := robotgo.GetPixelColor(100, 200)
|
||||
fmt.Println("color----", color)
|
||||
}
|
||||
|
||||
sx, sy := robotgo.GetScreenSize()
|
||||
fmt.Println("get screen size: ", sx, sy)
|
||||
|
||||
bit := robotgo.CaptureScreen(10, 10, 30, 30)
|
||||
defer robotgo.FreeBitmap(bit)
|
||||
robotgo.SaveBitmap(bit, "test_1.png")
|
||||
|
||||
img := robotgo.ToImage(bit)
|
||||
imgo.Save("test.png", img)
|
||||
}
|
||||
```
|
||||
|
||||
#### [位图](https://github.com/go-vgo/robotgo/blob/master/examples/bitmap/mian.go)
|
||||
#### [位图](https://github.com/go-vgo/robotgo/blob/master/examples/bitmap/main.go)
|
||||
|
||||
```Go
|
||||
package main
|
||||
@ -187,38 +254,152 @@ import (
|
||||
|
||||
func main() {
|
||||
bitmap := robotgo.CaptureScreen(10, 20, 30, 40)
|
||||
fmt.Println("...", bitmap)
|
||||
// use `defer robotgo.FreeBitmap(bit)` to free the bitmap
|
||||
defer robotgo.FreeBitmap(bitmap)
|
||||
|
||||
fmt.Println("bitmap...", bitmap)
|
||||
img := robotgo.ToImage(bitmap)
|
||||
robotgo.SavePng(img, "test_1.png")
|
||||
|
||||
fx, fy := robotgo.FindBitmap(bitmap)
|
||||
bit2 := robotgo.ToCBitmap(robotgo.ImgToBitmap(img))
|
||||
fx, fy := robotgo.FindBitmap(bit2)
|
||||
fmt.Println("FindBitmap------ ", fx, fy)
|
||||
robotgo.Move(fx, fy)
|
||||
|
||||
arr := robotgo.FindAllBitmap(bit2)
|
||||
fmt.Println("Find all bitmap: ", arr)
|
||||
robotgo.SaveBitmap(bitmap, "test.png")
|
||||
|
||||
fx, fy = robotgo.FindBitmap(bitmap)
|
||||
fmt.Println("FindBitmap------", fx, fy)
|
||||
|
||||
robotgo.SaveBitmap(bitmap, "test.png")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### [事件](https://github.com/go-vgo/robotgo/blob/master/examples/event/main.go)
|
||||
#### [OpenCV](https://github.com/vcaesar/gcv)
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
|
||||
"github.com/go-vgo/robotgo"
|
||||
"github.com/go-vgo/robotgo"
|
||||
"github.com/vcaesar/gcv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
opencv()
|
||||
}
|
||||
|
||||
func opencv() {
|
||||
name := "test.png"
|
||||
name1 := "test_001.png"
|
||||
robotgo.SaveCapture(name1, 10, 10, 30, 30)
|
||||
robotgo.SaveCapture(name)
|
||||
|
||||
fmt.Print("gcv find image: ")
|
||||
fmt.Println(gcv.FindImgFile(name1, name))
|
||||
fmt.Println(gcv.FindAllImgFile(name1, name))
|
||||
|
||||
bit := robotgo.OpenBitmap(name1)
|
||||
defer robotgo.FindBitmap(bit)
|
||||
fmt.Print("find bitmap: ")
|
||||
fmt.Println(robotgo.FindBitmap(bit))
|
||||
|
||||
// bit0 := robotgo.CaptureScreen()
|
||||
// img := robotgo.ToImage(bit0)
|
||||
// bit1 := robotgo.CaptureScreen(10, 10, 30, 30)
|
||||
// img1 := robotgo.ToImage(bit1)
|
||||
// defer robotgo.FreeBitmapArr(bit0, bit1)
|
||||
img := robotgo.CaptureImg()
|
||||
img1 := robotgo.CaptureImg(10, 10, 30, 30)
|
||||
|
||||
fmt.Print("gcv find image: ")
|
||||
fmt.Println(gcv.FindImg(img1, img))
|
||||
fmt.Println()
|
||||
|
||||
res := gcv.FindAllImg(img1, img)
|
||||
fmt.Println(res[0].TopLeft.Y, res[0].Rects.TopLeft.X, res)
|
||||
x, y := res[0].TopLeft.X, res[0].TopLeft.Y
|
||||
robotgo.Move(x, y-rand.Intn(5))
|
||||
robotgo.MilliSleep(100)
|
||||
robotgo.Click()
|
||||
|
||||
res = gcv.FindAll(img1, img) // use find template and sift
|
||||
fmt.Println("find all: ", res)
|
||||
res1 := gcv.Find(img1, img)
|
||||
fmt.Println("find: ", res1)
|
||||
|
||||
img2, _, _ := robotgo.DecodeImg("test_001.png")
|
||||
x, y = gcv.FindX(img2, img)
|
||||
fmt.Println(x, y)
|
||||
}
|
||||
```
|
||||
|
||||
#### [事件](https://github.com/go-vgo/robotgo/blob/master/examples/gohook/main.go)
|
||||
|
||||
```Go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-vgo/robotgo"
|
||||
hook "github.com/robotn/gohook"
|
||||
)
|
||||
|
||||
func main() {
|
||||
add()
|
||||
low()
|
||||
event()
|
||||
}
|
||||
|
||||
func add() {
|
||||
fmt.Println("--- Please press ctrl + shift + q to stop hook ---")
|
||||
robotgo.EventHook(hook.KeyDown, []string{"q", "ctrl", "shift"}, func(e hook.Event) {
|
||||
fmt.Println("ctrl-shift-q")
|
||||
robotgo.EventEnd()
|
||||
})
|
||||
|
||||
fmt.Println("--- Please press w---")
|
||||
robotgo.EventHook(hook.KeyDown, []string{"w"}, func(e hook.Event) {
|
||||
fmt.Println("w")
|
||||
})
|
||||
|
||||
s := robotgo.EventStart()
|
||||
<-robotgo.EventProcess(s)
|
||||
}
|
||||
|
||||
func low() {
|
||||
evChan := hook.Start()
|
||||
defer hook.End()
|
||||
|
||||
for ev := range evChan {
|
||||
fmt.Println("hook: ", ev)
|
||||
}
|
||||
}
|
||||
|
||||
func event() {
|
||||
ok := robotgo.AddEvents("q", "ctrl", "shift")
|
||||
if ok {
|
||||
fmt.Println("add events...")
|
||||
}
|
||||
|
||||
keve := robotgo.AddEvent("k")
|
||||
if keve == 0 {
|
||||
fmt.Println("you press...", "k")
|
||||
if keve {
|
||||
fmt.Println("you press... ", "k")
|
||||
}
|
||||
|
||||
mleft := robotgo.AddEvent("mleft")
|
||||
if mleft == 0 {
|
||||
fmt.Println("you press...", "mouse left button")
|
||||
if mleft {
|
||||
fmt.Println("you press... ", "mouse left button")
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### [窗口句柄](https://github.com/go-vgo/robotgo/blob/master/examples/window/main.go)
|
||||
|
||||
```Go
|
||||
@ -234,37 +415,71 @@ func main() {
|
||||
fpid, err := robotgo.FindIds("Google")
|
||||
if err == nil {
|
||||
fmt.Println("pids...", fpid)
|
||||
|
||||
if len(fpid) > 0 {
|
||||
robotgo.ActivePID(fpid[0])
|
||||
|
||||
robotgo.Kill(fpid[0])
|
||||
}
|
||||
}
|
||||
|
||||
robotgo.ActiveName("chrome")
|
||||
|
||||
isExist, err := robotgo.PidExists(100)
|
||||
if err == nil {
|
||||
if err == nil && isExist {
|
||||
fmt.Println("pid exists is", isExist)
|
||||
|
||||
robotgo.Kill(100)
|
||||
}
|
||||
|
||||
abool := robotgo.ShowAlert("test", "robotgo")
|
||||
if abool == 0 {
|
||||
fmt.Println("ok@@@", "ok")
|
||||
abool := robotgo.Alert("test", "robotgo")
|
||||
if abool {
|
||||
fmt.Println("ok@@@ ", "ok")
|
||||
}
|
||||
|
||||
title := robotgo.GetTitle()
|
||||
fmt.Println("title@@@", title)
|
||||
}
|
||||
fmt.Println("title@@@ ", title)
|
||||
}
|
||||
```
|
||||
|
||||
## CrossCompiling
|
||||
|
||||
##### Windows64 to windows32
|
||||
```Go
|
||||
SET CGO_ENABLED=1
|
||||
SET GOARCH=386
|
||||
go build main.go
|
||||
```
|
||||
|
||||
#### Other to windows
|
||||
|
||||
Install Requirements (Ubuntu):
|
||||
```bash
|
||||
sudo apt install gcc-multilib
|
||||
sudo apt install gcc-mingw-w64
|
||||
sudo apt install libz-mingw-w64-dev
|
||||
```
|
||||
|
||||
Build the binary:
|
||||
|
||||
```Go
|
||||
GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ go build -x ./
|
||||
```
|
||||
|
||||
```
|
||||
// CC=mingw-w64\x86_64-7.2.0-win32-seh-rt_v5-rev1\mingw64\bin\gcc.exe
|
||||
// CXX=mingw-w64\x86_64-7.2.0-win32-seh-rt_v5-rev1\mingw64\bin\g++.exe
|
||||
```
|
||||
|
||||
## Authors
|
||||
* [The author is vz](https://github.com/vcaesar)
|
||||
* [Maintainers](https://github.com/orgs/go-vgo/people)
|
||||
* [Contributors](https://github.com/go-vgo/robotgo/graphs/contributors)
|
||||
|
||||
## Plans
|
||||
- 更新 Find an image on screen, read pixels from an image
|
||||
- 更新 Window Handle
|
||||
- 支持 UTF-8, 过渡方案: TypeStr
|
||||
- 尝试支持 Android, 也许支持 IOS
|
||||
- 移除 zlib/libpng 依赖
|
||||
|
||||
## Donate
|
||||
|
||||
支持 robotgo, [buy me a coffee](https://github.com/go-vgo/buy-me-a-coffee).
|
||||
|
||||
#### Paypal
|
||||
|
||||
Donate money by [paypal](https://www.paypal.me/veni0/25) to my account [vzvway@gmail.com](vzvway@gmail.com)
|
||||
|
||||
## Contributors
|
||||
|
||||
|
80
appveyor.yml
Normal file
80
appveyor.yml
Normal file
@ -0,0 +1,80 @@
|
||||
# version format
|
||||
version: "{build}"
|
||||
|
||||
# Operating system (build VM template)
|
||||
# os: Windows Server 2012 R2
|
||||
os: Visual Studio 2017
|
||||
|
||||
# Platform.
|
||||
# platform:
|
||||
# - x64
|
||||
# - x86
|
||||
|
||||
clone_folder: c:\gopath\src\github.com\go-vgo\robotgo
|
||||
|
||||
# Environment variables
|
||||
environment:
|
||||
global:
|
||||
GOPATH: C:\gopath
|
||||
CC: gcc.exe
|
||||
matrix:
|
||||
- GOARCH: amd64
|
||||
# GOVERSION: 1.9.3
|
||||
GETH_ARCH: amd64
|
||||
MSYS2_ARCH: x86_64
|
||||
MSYS2_BITS: 64
|
||||
MSYSTEM: MINGW64
|
||||
PATH: C:\msys64\mingw64\bin\;C:\Program Files (x86)\NSIS\;%PATH%
|
||||
- GOARCH: 386
|
||||
# GOVERSION: 1.9.3
|
||||
GETH_ARCH: 386
|
||||
MSYS2_ARCH: i686
|
||||
MSYS2_BITS: 32
|
||||
MSYSTEM: MINGW32
|
||||
PATH: C:\msys64\mingw32\bin\;C:\Program Files (x86)\NSIS\;%PATH%
|
||||
# - COMPILER: MINGW_W64
|
||||
# ARCHITECTURE: x64
|
||||
GOVERSION: 1.23.0
|
||||
# GOPATH: c:\gopath
|
||||
|
||||
# scripts that run after cloning repository
|
||||
# install:
|
||||
# - set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
|
||||
# - go version
|
||||
# - go env
|
||||
# - gcc --version
|
||||
# - python --version
|
||||
|
||||
install:
|
||||
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
|
||||
- git submodule update --init
|
||||
- rmdir C:\go /s /q
|
||||
- appveyor DownloadFile https://storage.googleapis.com/golang/go%GOVERSION%.windows-%GETH_ARCH%.zip
|
||||
- 7z x go%GOVERSION%.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
|
||||
- go version
|
||||
- go env
|
||||
- gcc --version
|
||||
|
||||
# To run your custom scripts instead of automatic MSBuild
|
||||
build_script:
|
||||
# We need to disable firewall - https://github.com/appveyor/ci/issues/1579#issuecomment-309830648
|
||||
- ps: Disable-NetFirewallRule -DisplayName 'File and Printer Sharing (SMB-Out)'
|
||||
- cd c:\gopath\src\github.com\go-vgo\robotgo
|
||||
- git branch
|
||||
- go get -t ./...
|
||||
|
||||
# To run your custom scripts instead of automatic tests
|
||||
test_script:
|
||||
# Unit tests
|
||||
- ps: Add-AppveyorTest "Unit Tests" -Outcome Running
|
||||
- go test -v github.com/go-vgo/robotgo/...
|
||||
- ps: Update-AppveyorTest "Unit Tests" -Outcome Passed
|
||||
|
||||
# notifications:
|
||||
# - provider: Email
|
||||
# to:
|
||||
# - .io
|
||||
# on_build_failure: true
|
||||
# on_build_status_changed: true
|
||||
# to disable deployment
|
||||
deploy: off
|
21
base/LICENSE
Normal file
21
base/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
The software is licensed under the terms of the MIT license.
|
||||
|
||||
Copyright 2010 Michael Sanders, AE and the go-vgo Project Developers.
|
||||
|
||||
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.
|
@ -5,89 +5,32 @@
|
||||
#include "types.h"
|
||||
#include "rgb.h"
|
||||
#include <assert.h>
|
||||
// #include <stdint.h>
|
||||
#if defined(_MSC_VER)
|
||||
#include "ms_stdint.h"
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
#include <stdint.h>
|
||||
|
||||
struct _MMBitmap {
|
||||
uint8_t *imageBuffer; /* Pixels stored in Quad I format; i.e., origin is in
|
||||
* top left. Length should be height * bytewidth. */
|
||||
size_t width; /* Never 0, unless image is NULL. */
|
||||
size_t height; /* Never 0, unless image is NULL. */
|
||||
size_t bytewidth; /* The aligned width (width + padding). */
|
||||
uint8_t *imageBuffer; /* Pixels stored in Quad I format; */
|
||||
int32_t width; /* Never 0, unless image is NULL. */
|
||||
int32_t height; /* Never 0, unless image is NULL. */
|
||||
|
||||
int32_t bytewidth; /* The aligned width (width + padding). */
|
||||
uint8_t bitsPerPixel; /* Should be either 24 or 32. */
|
||||
uint8_t bytesPerPixel; /* For convenience; should be bitsPerPixel / 8. */
|
||||
};
|
||||
|
||||
typedef struct _MMBitmap MMBitmap;
|
||||
typedef MMBitmap *MMBitmapRef;
|
||||
// MMBitmapRef bitmap;
|
||||
|
||||
/* Creates new MMBitmap with the given values.
|
||||
* Follows the Create Rule (caller is responsible for destroy()'ing object). */
|
||||
MMBitmapRef createMMBitmap(uint8_t *buffer, size_t width, size_t height,
|
||||
size_t bytewidth, uint8_t bitsPerPixel,
|
||||
uint8_t bytesPerPixel);
|
||||
#define MMBitmapPointInBounds(image, p) ((p).x < (image)->width && (p).y < (image)->height)
|
||||
|
||||
/* Releases memory occupied by MMBitmap. */
|
||||
void destroyMMBitmap(MMBitmapRef bitmap);
|
||||
|
||||
/* Releases memory occupied by MMBitmap. Acts via CallBack method*/
|
||||
void destroyMMBitmapBuffer(char * bitmapBuffer, void * hint);
|
||||
|
||||
/* Returns copy of MMBitmap, to be destroy()'d by caller. */
|
||||
MMBitmapRef copyMMBitmap(MMBitmapRef bitmap);
|
||||
|
||||
/* Returns copy of one MMBitmap juxtaposed in another (to be destroy()'d
|
||||
* by the caller.), or NULL on error. */
|
||||
MMBitmapRef copyMMBitmapFromPortion(MMBitmapRef source, MMRect rect);
|
||||
|
||||
#define MMBitmapPointInBounds(image, p) ((p).x < (image)->width && \
|
||||
(p).y < (image)->height)
|
||||
#define MMBitmapRectInBounds(image, r) \
|
||||
(((r).origin.x + (r).size.width <= (image)->width) && \
|
||||
((r).origin.y + (r).size.height <= (image)->height))
|
||||
|
||||
#define MMBitmapGetBounds(image) MMRectMake(0, 0, image->width, image->height)
|
||||
|
||||
/* Get pointer to pixel of MMBitmapRef. No bounds checking is performed (check
|
||||
* yourself before calling this with MMBitmapPointInBounds(). */
|
||||
/* Get pointer to pixel of MMBitmapRef. No bounds checking is performed */
|
||||
#define MMRGBColorRefAtPoint(image, x, y) \
|
||||
(MMRGBColor *)(assert(MMBitmapPointInBounds(image, MMPointMake(x, y))), \
|
||||
((image)->imageBuffer) + (((image)->bytewidth * (y)) \
|
||||
+ ((x) * (image)->bytesPerPixel)))
|
||||
(MMRGBColor *)(assert(MMBitmapPointInBounds(image, MMPointInt32Make(x, y))), \
|
||||
((image)->imageBuffer) + (((image)->bytewidth * (y)) + ((x) * (image)->bytesPerPixel)))
|
||||
|
||||
/* Dereference pixel of MMBitmapRef. Again, no bounds checking is performed. */
|
||||
#define MMRGBColorAtPoint(image, x, y) *MMRGBColorRefAtPoint(image, x, y)
|
||||
|
||||
/* Hex/integer value of color at point. */
|
||||
#define MMRGBHexAtPoint(image, x, y) \
|
||||
hexFromMMRGB(MMRGBColorAtPoint(image, x, y))
|
||||
|
||||
/* Increment either point.x or point.y depending on the position of point.x.
|
||||
* That is, if x + 1 is >= width, increment y and start x at the beginning.
|
||||
* Otherwise, increment x.
|
||||
*
|
||||
* This is used as a convenience macro to scan rows when calling functions such
|
||||
* as findColorInRectAt() and findBitmapInBitmapAt(). */
|
||||
#define ITER_NEXT_POINT(pixel, width, start_x) \
|
||||
do { \
|
||||
if (++(pixel).x >= (width)) { \
|
||||
(pixel).x = start_x; \
|
||||
++(point).y; \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#define MMRGBHexAtPoint(image, x, y) hexFromMMRGB(MMRGBColorAtPoint(image, x, y))
|
||||
|
||||
#endif /* MMBITMAP_H */
|
@ -1,96 +0,0 @@
|
||||
#include "MMBitmap.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
//MMBitmapRef createMMBitmap()
|
||||
MMBitmapRef createMMBitmap(
|
||||
uint8_t *buffer,
|
||||
size_t width,
|
||||
size_t height,
|
||||
size_t bytewidth,
|
||||
uint8_t bitsPerPixel,
|
||||
uint8_t bytesPerPixel
|
||||
){
|
||||
MMBitmapRef bitmap = malloc(sizeof(MMBitmap));
|
||||
if (bitmap == NULL) return NULL;
|
||||
|
||||
bitmap->imageBuffer = buffer;
|
||||
bitmap->width = width;
|
||||
bitmap->height = height;
|
||||
bitmap->bytewidth = bytewidth;
|
||||
bitmap->bitsPerPixel = bitsPerPixel;
|
||||
bitmap->bytesPerPixel = bytesPerPixel;
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
void destroyMMBitmap(MMBitmapRef bitmap)
|
||||
{
|
||||
assert(bitmap != NULL);
|
||||
|
||||
if (bitmap->imageBuffer != NULL) {
|
||||
free(bitmap->imageBuffer);
|
||||
bitmap->imageBuffer = NULL;
|
||||
}
|
||||
|
||||
free(bitmap);
|
||||
}
|
||||
|
||||
void destroyMMBitmapBuffer(char * bitmapBuffer, void * hint)
|
||||
{
|
||||
if (bitmapBuffer != NULL)
|
||||
{
|
||||
free(bitmapBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
MMBitmapRef copyMMBitmap(MMBitmapRef bitmap)
|
||||
{
|
||||
uint8_t *copiedBuf = NULL;
|
||||
|
||||
assert(bitmap != NULL);
|
||||
if (bitmap->imageBuffer != NULL) {
|
||||
const size_t bufsize = bitmap->height * bitmap->bytewidth;
|
||||
copiedBuf = malloc(bufsize);
|
||||
if (copiedBuf == NULL) return NULL;
|
||||
|
||||
memcpy(copiedBuf, bitmap->imageBuffer, bufsize);
|
||||
}
|
||||
|
||||
return createMMBitmap(copiedBuf,
|
||||
bitmap->width,
|
||||
bitmap->height,
|
||||
bitmap->bytewidth,
|
||||
bitmap->bitsPerPixel,
|
||||
bitmap->bytesPerPixel);
|
||||
}
|
||||
|
||||
MMBitmapRef copyMMBitmapFromPortion(MMBitmapRef source, MMRect rect)
|
||||
{
|
||||
assert(source != NULL);
|
||||
|
||||
if (source->imageBuffer == NULL || !MMBitmapRectInBounds(source, rect)) {
|
||||
return NULL;
|
||||
} else {
|
||||
uint8_t *copiedBuf = NULL;
|
||||
const size_t bufsize = rect.size.height * source->bytewidth;
|
||||
const size_t offset = (source->bytewidth * rect.origin.y) +
|
||||
(rect.origin.x * source->bytesPerPixel);
|
||||
|
||||
/* Don't go over the bounds, programmer! */
|
||||
assert((bufsize + offset) <= (source->bytewidth * source->height));
|
||||
|
||||
copiedBuf = malloc(bufsize);
|
||||
if (copiedBuf == NULL) return NULL;
|
||||
|
||||
memcpy(copiedBuf, source->imageBuffer + offset, bufsize);
|
||||
|
||||
return createMMBitmap(copiedBuf,
|
||||
rect.size.width,
|
||||
rect.size.height,
|
||||
source->bytewidth,
|
||||
source->bitsPerPixel,
|
||||
source->bytesPerPixel);
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef MMARRAY_H
|
||||
#define MMARRAY_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
struct _MMPointArray {
|
||||
MMPoint *array; /* Pointer to actual data. */
|
||||
size_t count; /* Number of elements in array. */
|
||||
size_t _allocedCount; /* Private; do not use outside of MMPointArray.c. */
|
||||
};
|
||||
|
||||
typedef struct _MMPointArray MMPointArray;
|
||||
typedef MMPointArray *MMPointArrayRef;
|
||||
|
||||
/* Creates array of an initial size (the maximum size is still limitless).
|
||||
* This follows the "Create" Rule; i.e., responsibility for "destroying" the
|
||||
* array is given to the caller. */
|
||||
MMPointArrayRef createMMPointArray(size_t initialCount);
|
||||
|
||||
/* Frees memory occupied by |pointArray|. Does not accept NULL. */
|
||||
void destroyMMPointArray(MMPointArrayRef pointArray);
|
||||
|
||||
/* Appends a point to an array, increasing the internal size if necessary. */
|
||||
void MMPointArrayAppendPoint(MMPointArrayRef pointArray, MMPoint point);
|
||||
|
||||
/* Retrieve point from array. */
|
||||
#define MMPointArrayGetItem(a, i) ((a)->array)[i]
|
||||
|
||||
/* Set point in array. */
|
||||
#define MMPointArraySetItem(a, i, item) ((a)->array[i] = item)
|
||||
|
||||
#endif /* MMARRAY_H */
|
@ -1,41 +0,0 @@
|
||||
#include "MMPointArray.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
MMPointArrayRef createMMPointArray(size_t initialCount)
|
||||
{
|
||||
MMPointArrayRef pointArray = calloc(1, sizeof(MMPointArray));
|
||||
|
||||
if (initialCount == 0) initialCount = 1;
|
||||
|
||||
pointArray->_allocedCount = initialCount;
|
||||
pointArray->array = malloc(pointArray->_allocedCount * sizeof(MMPoint));
|
||||
if (pointArray->array == NULL) return NULL;
|
||||
|
||||
return pointArray;
|
||||
}
|
||||
|
||||
void destroyMMPointArray(MMPointArrayRef pointArray)
|
||||
{
|
||||
if (pointArray->array != NULL) {
|
||||
free(pointArray->array);
|
||||
pointArray->array = NULL;
|
||||
}
|
||||
|
||||
free(pointArray);
|
||||
}
|
||||
|
||||
void MMPointArrayAppendPoint(MMPointArrayRef pointArray, MMPoint point)
|
||||
{
|
||||
const size_t newCount = ++(pointArray->count);
|
||||
if (pointArray->_allocedCount < newCount) {
|
||||
do {
|
||||
/* Double size each time to avoid calls to realloc(). */
|
||||
pointArray->_allocedCount <<= 1;
|
||||
} while (pointArray->_allocedCount < newCount);
|
||||
pointArray->array = realloc(pointArray->array,
|
||||
sizeof(point) *
|
||||
pointArray->_allocedCount);
|
||||
}
|
||||
|
||||
pointArray->array[pointArray->count - 1] = point;
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef UTHASHTABLE_H
|
||||
#define UTHASHTABLE_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include "uthash.h"
|
||||
|
||||
/* All node structs must begin with this (note that there is NO semicolon). */
|
||||
#define UTHashNode_HEAD UT_hash_handle hh;
|
||||
|
||||
/* This file contains convenience macros and a standard struct for working with
|
||||
* uthash hash tables.
|
||||
*
|
||||
* The main purpose of this is for convenience of creating/freeing nodes. */
|
||||
struct _UTHashTable {
|
||||
void *uttable; /* The uthash table -- must start out as NULL. */
|
||||
void *nodes; /* Contiguous array of nodes. */
|
||||
size_t allocedNodeCount; /* Node count currently allocated for. */
|
||||
size_t nodeCount; /* Current node count. */
|
||||
size_t nodeSize; /* Size of each node. */
|
||||
};
|
||||
|
||||
typedef struct _UTHashTable UTHashTable;
|
||||
|
||||
/* Initiates a hash table to the default values. |table| should point to an
|
||||
* already allocated UTHashTable struct.
|
||||
*
|
||||
* If the |initialCount| argument in initHashTable is given, |nodes| is
|
||||
* allocated immediately to the maximum size and new nodes are simply slices of
|
||||
* that array. This can save calls to malloc if many nodes are to be added, and
|
||||
* the a reasonable maximum number is known ahead of time.
|
||||
*
|
||||
* If the node count goes over this maximum, or if |initialCount| is 0, the
|
||||
* array is dynamically reallocated to fit the size.
|
||||
*/
|
||||
void initHashTable(UTHashTable *table, size_t initialCount, size_t nodeSize);
|
||||
|
||||
/* Frees memory occupied by a UTHashTable's members.
|
||||
*
|
||||
* Note that this does NOT free memory for the UTHashTable pointed to by
|
||||
* |table| itself; if that was allocated on the heap, you must free() it
|
||||
* yourself after calling this. */
|
||||
void destroyHashTable(UTHashTable *table);
|
||||
|
||||
/* Returns memory allocated for a new node. Responsibility for freeing this is
|
||||
* up to the destroyHashTable() macro; this should NOT be freed by the caller.
|
||||
*
|
||||
* This is intended to be used with a HASH_ADD() macro, e.g.:
|
||||
* {%
|
||||
* struct myNode *uttable = utHashTable->uttable;
|
||||
* struct myNode *node = getNewNode(utHashTable);
|
||||
* node->key = 42;
|
||||
* node->value = someValue;
|
||||
* HASH_ADD_INT(uttable, key, node);
|
||||
* utHashTable->uttable = uttable;
|
||||
* %}
|
||||
*
|
||||
* Or, use the UTHASHTABLE_ADD_INT or UTHASHTABLE_ADD_STR macros
|
||||
* for convenience (they are exactly equivalent):
|
||||
* {%
|
||||
* struct myNode *node = getNewNode(utHashTable);
|
||||
* node->key = 42;
|
||||
* node->value = someValue;
|
||||
* UTHASHTABLE_ADD_INT(utHashTable, key, node, struct myNode);
|
||||
* %}
|
||||
*/
|
||||
void *getNewNode(UTHashTable *table);
|
||||
|
||||
#define UTHASHTABLE_ADD_INT(tablePtr, keyName, node, nodeType) \
|
||||
do { \
|
||||
nodeType *uttable = (tablePtr)->uttable; \
|
||||
HASH_ADD_INT(uttable, keyName, node); \
|
||||
(tablePtr)->uttable = uttable; \
|
||||
} while (0)
|
||||
|
||||
#define UTHASHTABLE_ADD_STR(tablePtr, keyName, node, nodeType) \
|
||||
do { \
|
||||
nodeType *uttable = (tablePtr)->uttable; \
|
||||
HASH_ADD_STR(uttable, keyName, node); \
|
||||
(tablePtr)->uttable = uttable; \
|
||||
} while (0)
|
||||
|
||||
#endif /* MMHASHTABLE_H */
|
@ -1,56 +0,0 @@
|
||||
#include "UTHashTable.h"
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* Base struct class (all nodes must contain at least the elements in
|
||||
* this struct). */
|
||||
struct _UTHashNode {
|
||||
UTHashNode_HEAD
|
||||
};
|
||||
|
||||
typedef struct _UTHashNode UTHashNode;
|
||||
|
||||
void initHashTable(UTHashTable *table, size_t initialCount, size_t nodeSize)
|
||||
{
|
||||
assert(table != NULL);
|
||||
assert(nodeSize >= sizeof(UTHashNode));
|
||||
|
||||
table->uttable = NULL; /* Must be set to NULL for uthash. */
|
||||
table->allocedNodeCount = (initialCount == 0) ? 1 : initialCount;
|
||||
table->nodeCount = 0;
|
||||
table->nodeSize = nodeSize;
|
||||
table->nodes = calloc(table->nodeSize, nodeSize * table->allocedNodeCount);
|
||||
}
|
||||
|
||||
void destroyHashTable(UTHashTable *table)
|
||||
{
|
||||
UTHashNode *uttable = table->uttable;
|
||||
UTHashNode *node;
|
||||
|
||||
/* Let uthash do its magic. */
|
||||
while (uttable != NULL) {
|
||||
node = uttable; /* Grab pointer to first item. */
|
||||
HASH_DEL(uttable, node); /* Delete it (table advances to next). */
|
||||
}
|
||||
|
||||
/* Only giant malloc'd block containing each node must be freed. */
|
||||
if (table->nodes != NULL) free(table->nodes);
|
||||
table->uttable = table->nodes = NULL;
|
||||
}
|
||||
|
||||
void *getNewNode(UTHashTable *table)
|
||||
{
|
||||
/* Increment node count, resizing table if necessary. */
|
||||
const size_t newNodeCount = ++(table->nodeCount);
|
||||
if (table->allocedNodeCount < newNodeCount) {
|
||||
do {
|
||||
/* Double size each time to avoid calls to realloc(). */
|
||||
table->allocedNodeCount <<= 1;
|
||||
} while (table->allocedNodeCount < newNodeCount);
|
||||
|
||||
table->nodes = realloc(table->nodes, table->nodeSize *
|
||||
table->allocedNodeCount);
|
||||
}
|
||||
|
||||
return (char *)table->nodes + (table->nodeSize * (table->nodeCount - 1));
|
||||
}
|
2
base/base.go
Normal file
2
base/base.go
Normal file
@ -0,0 +1,2 @@
|
||||
// https://github.com/golang/go/issues/26366
|
||||
package base
|
111
base/base64.c
111
base/base64.c
@ -1,111 +0,0 @@
|
||||
#include "base64.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* Encoding table as described in RFC1113. */
|
||||
const static uint8_t b64_encode_table[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/* Decoding table. */
|
||||
const static int8_t b64_decode_table[256] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00-0F */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10-1F */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 20-2F */
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 30-3F */
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 40-4F */
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 50-5F */
|
||||
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 60-6F */
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 70-7F */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80-8F */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90-9F */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A0-AF */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* B0-BF */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* C0-CF */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* D0-DF */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* E0-EF */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* F0-FF */
|
||||
};
|
||||
|
||||
uint8_t *base64decode(const uint8_t *src, const size_t buflen, size_t *retlen)
|
||||
{
|
||||
int8_t digit, lastdigit;
|
||||
size_t i, j;
|
||||
uint8_t *decoded;
|
||||
const size_t maxlen = ((buflen + 3) / 4) * 3;
|
||||
|
||||
/* Sanity check */
|
||||
assert(src != NULL);
|
||||
|
||||
digit = lastdigit = j = 0;
|
||||
decoded = malloc(maxlen + 1);
|
||||
if (decoded == NULL) return NULL;
|
||||
for (i = 0; i < buflen; ++i) {
|
||||
if ((digit = b64_decode_table[src[i]]) != -1) {
|
||||
/* Decode block */
|
||||
switch (i % 4) {
|
||||
case 1:
|
||||
decoded[j++] = ((lastdigit << 2) | ((digit & 0x30) >> 4));
|
||||
break;
|
||||
case 2:
|
||||
decoded[j++] = (((lastdigit & 0xF) << 4) | ((digit & 0x3C) >> 2));
|
||||
break;
|
||||
case 3:
|
||||
decoded[j++] = (((lastdigit & 0x03) << 6) | digit);
|
||||
break;
|
||||
}
|
||||
lastdigit = digit;
|
||||
}
|
||||
}
|
||||
|
||||
if (retlen != NULL) *retlen = j;
|
||||
decoded[j] = '\0';
|
||||
return decoded; /* Must be free()'d by caller */
|
||||
}
|
||||
|
||||
uint8_t *base64encode(const uint8_t *src, const size_t buflen, size_t *retlen)
|
||||
{
|
||||
size_t i, j;
|
||||
const size_t maxlen = (((buflen + 3) & ~3)) * 4;
|
||||
uint8_t *encoded = malloc(maxlen + 1);
|
||||
if (encoded == NULL) return NULL;
|
||||
|
||||
/* Sanity check */
|
||||
assert(src != NULL);
|
||||
assert(buflen > 0);
|
||||
|
||||
j = 0;
|
||||
for (i = 0; i < buflen + 1; ++i) {
|
||||
/* Encode block */
|
||||
switch (i % 3) {
|
||||
case 0:
|
||||
encoded[j++] = b64_encode_table[src[i] >> 2];
|
||||
encoded[j++] = b64_encode_table[((src[i] & 0x03) << 4) |
|
||||
((src[i + 1] & 0xF0) >> 4)];
|
||||
break;
|
||||
case 1:
|
||||
encoded[j++] = b64_encode_table[((src[i] & 0x0F) << 2) |
|
||||
((src[i + 1] & 0xC0) >> 6)];
|
||||
break;
|
||||
case 2:
|
||||
encoded[j++] = b64_encode_table[(src[i] & 0x3F)];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add padding if necessary */
|
||||
if ((j % 4) != 0) {
|
||||
const size_t with_padding = ((j + 3) & ~3); /* Align to 4 bytes */
|
||||
do {
|
||||
encoded[j++] = '=';
|
||||
} while (j < with_padding);
|
||||
}
|
||||
|
||||
assert(j <= maxlen);
|
||||
|
||||
if (retlen != NULL) *retlen = j;
|
||||
encoded[j] = '\0';
|
||||
return encoded; /* Must be free()'d by caller */
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef BASE64_H
|
||||
#define BASE64_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include "ms_stdint.h"
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/* Decode a base64 encoded string discarding line breaks and noise.
|
||||
*
|
||||
* Returns a new string to be free()'d by caller, or NULL on error.
|
||||
* Returned string is guaranteed to be NUL-terminated.
|
||||
*
|
||||
* If |retlen| is not NULL, it is set to the length of the returned string
|
||||
* (minus the NUL-terminator) on successful return. */
|
||||
uint8_t *base64decode(const uint8_t *buf, const size_t buflen, size_t *retlen);
|
||||
|
||||
/* Encode a base64 encoded string without line breaks or noise.
|
||||
*
|
||||
* Returns a new string to be free()'d by caller, or NULL on error.
|
||||
* Returned string is guaranteed to be NUL-terminated with the correct padding.
|
||||
*
|
||||
* If |retlen| is not NULL, it is set to the length of the returned string
|
||||
* (minus the NUL-terminator) on successful return. */
|
||||
uint8_t *base64encode(const uint8_t *buf, const size_t buflen, size_t *retlen);
|
||||
|
||||
#endif /* BASE64_H */
|
111
base/base64_c.h
111
base/base64_c.h
@ -1,111 +0,0 @@
|
||||
#include "base64.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* Encoding table as described in RFC1113. */
|
||||
const static uint8_t b64_encode_table[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/* Decoding table. */
|
||||
const static int8_t b64_decode_table[256] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00-0F */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10-1F */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 20-2F */
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 30-3F */
|
||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 40-4F */
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 50-5F */
|
||||
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 60-6F */
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 70-7F */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80-8F */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90-9F */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* A0-AF */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* B0-BF */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* C0-CF */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* D0-DF */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* E0-EF */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* F0-FF */
|
||||
};
|
||||
|
||||
uint8_t *base64decode(const uint8_t *src, const size_t buflen, size_t *retlen)
|
||||
{
|
||||
int8_t digit, lastdigit;
|
||||
size_t i, j;
|
||||
uint8_t *decoded;
|
||||
const size_t maxlen = ((buflen + 3) / 4) * 3;
|
||||
|
||||
/* Sanity check */
|
||||
assert(src != NULL);
|
||||
|
||||
digit = lastdigit = j = 0;
|
||||
decoded = malloc(maxlen + 1);
|
||||
if (decoded == NULL) return NULL;
|
||||
for (i = 0; i < buflen; ++i) {
|
||||
if ((digit = b64_decode_table[src[i]]) != -1) {
|
||||
/* Decode block */
|
||||
switch (i % 4) {
|
||||
case 1:
|
||||
decoded[j++] = ((lastdigit << 2) | ((digit & 0x30) >> 4));
|
||||
break;
|
||||
case 2:
|
||||
decoded[j++] = (((lastdigit & 0xF) << 4) | ((digit & 0x3C) >> 2));
|
||||
break;
|
||||
case 3:
|
||||
decoded[j++] = (((lastdigit & 0x03) << 6) | digit);
|
||||
break;
|
||||
}
|
||||
lastdigit = digit;
|
||||
}
|
||||
}
|
||||
|
||||
if (retlen != NULL) *retlen = j;
|
||||
decoded[j] = '\0';
|
||||
return decoded; /* Must be free()'d by caller */
|
||||
}
|
||||
|
||||
uint8_t *base64encode(const uint8_t *src, const size_t buflen, size_t *retlen)
|
||||
{
|
||||
size_t i, j;
|
||||
const size_t maxlen = (((buflen + 3) & ~3)) * 4;
|
||||
uint8_t *encoded = malloc(maxlen + 1);
|
||||
if (encoded == NULL) return NULL;
|
||||
|
||||
/* Sanity check */
|
||||
assert(src != NULL);
|
||||
assert(buflen > 0);
|
||||
|
||||
j = 0;
|
||||
for (i = 0; i < buflen + 1; ++i) {
|
||||
/* Encode block */
|
||||
switch (i % 3) {
|
||||
case 0:
|
||||
encoded[j++] = b64_encode_table[src[i] >> 2];
|
||||
encoded[j++] = b64_encode_table[((src[i] & 0x03) << 4) |
|
||||
((src[i + 1] & 0xF0) >> 4)];
|
||||
break;
|
||||
case 1:
|
||||
encoded[j++] = b64_encode_table[((src[i] & 0x0F) << 2) |
|
||||
((src[i + 1] & 0xC0) >> 6)];
|
||||
break;
|
||||
case 2:
|
||||
encoded[j++] = b64_encode_table[(src[i] & 0x3F)];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Add padding if necessary */
|
||||
if ((j % 4) != 0) {
|
||||
const size_t with_padding = ((j + 3) & ~3); /* Align to 4 bytes */
|
||||
do {
|
||||
encoded[j++] = '=';
|
||||
} while (j < with_padding);
|
||||
}
|
||||
|
||||
assert(j <= maxlen);
|
||||
|
||||
if (retlen != NULL) *retlen = j;
|
||||
encoded[j] = '\0';
|
||||
return encoded; /* Must be free()'d by caller */
|
||||
}
|
36
base/bitmap_free_c.h
Normal file
36
base/bitmap_free_c.h
Normal file
@ -0,0 +1,36 @@
|
||||
#include "MMBitmap.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
MMBitmapRef createMMBitmap_c(uint8_t *buffer, int32_t width, int32_t height,
|
||||
int32_t bytewidth, uint8_t bitsPerPixel, uint8_t bytesPerPixel
|
||||
) {
|
||||
MMBitmapRef bitmap = malloc(sizeof(MMBitmap));
|
||||
if (bitmap == NULL) { return NULL; }
|
||||
|
||||
bitmap->imageBuffer = buffer;
|
||||
bitmap->width = width;
|
||||
bitmap->height = height;
|
||||
bitmap->bytewidth = bytewidth;
|
||||
bitmap->bitsPerPixel = bitsPerPixel;
|
||||
bitmap->bytesPerPixel = bytesPerPixel;
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
void destroyMMBitmap(MMBitmapRef bitmap) {
|
||||
assert(bitmap != NULL);
|
||||
|
||||
if (bitmap->imageBuffer != NULL) {
|
||||
free(bitmap->imageBuffer);
|
||||
bitmap->imageBuffer = NULL;
|
||||
}
|
||||
|
||||
free(bitmap);
|
||||
}
|
||||
|
||||
void destroyMMBitmapBuffer(char * bitmapBuffer, void * hint) {
|
||||
if (bitmapBuffer != NULL) {
|
||||
free(bitmapBuffer);
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef BMP_IO_H
|
||||
#define BMP_IO_H
|
||||
|
||||
#include "MMBitmap.h"
|
||||
#include "io.h"
|
||||
|
||||
enum _BMPReadError {
|
||||
kBMPGenericError = 0,
|
||||
kBMPAccessError,
|
||||
kBMPInvalidKeyError,
|
||||
kBMPUnsupportedHeaderError,
|
||||
kBMPInvalidColorPanesError,
|
||||
kBMPUnsupportedColorDepthError,
|
||||
kBMPUnsupportedCompressionError,
|
||||
kBMPInvalidPixelDataError
|
||||
};
|
||||
|
||||
typedef MMIOError MMBMPReadError;
|
||||
|
||||
/* Returns description of given MMBMPReadError.
|
||||
* Returned string is constant and hence should not be freed. */
|
||||
const char *MMBMPReadErrorString(MMIOError error);
|
||||
|
||||
/* Attempts to read bitmap file at path; returns new MMBitmap on success, or
|
||||
* NULL on error. If |error| is non-NULL, it will be set to the error code
|
||||
* on return.
|
||||
*
|
||||
* Currently supports:
|
||||
* - Uncompressed Windows v3/v4/v5 24-bit or 32-bit BMP.
|
||||
* - OS/2 v1 or v2 24-bit BMP.
|
||||
* - Does NOT yet support: 1-bit, 4-bit, 8-bit, 16-bit, compressed bitmaps,
|
||||
* or PNGs/JPEGs disguised as BMPs (and returns NULL if those are given).
|
||||
*
|
||||
* Responsibility for destroy()'ing returned MMBitmap is left up to caller. */
|
||||
MMBitmapRef newMMBitmapFromBMP(const char *path, MMBMPReadError *error);
|
||||
|
||||
/* Returns a buffer containing the raw BMP file data in Windows v3 BMP format,
|
||||
* ready to be saved to a file. If |len| is not NULL, it will be set to the
|
||||
* number of bytes allocated in the returned buffer.
|
||||
*
|
||||
* Responsibility for free()'ing data is left up to the caller. */
|
||||
uint8_t *createBitmapData(MMBitmapRef bitmap, size_t *len);
|
||||
|
||||
/* Saves bitmap to file in Windows v3 BMP format.
|
||||
* Returns 0 on success, -1 on error. */
|
||||
int saveMMBitmapAsBMP(MMBitmapRef bitmap, const char *path);
|
||||
|
||||
/* Swaps bitmap from Quadrant 1 to Quadran III format, or vice versa
|
||||
* (upside-down Cartesian/PostScript/GL <-> right side up QD/CG raster format).
|
||||
*/
|
||||
void flipBitmapData(void *data, size_t width, size_t height, size_t bytewidth);
|
||||
|
||||
#endif /* BMP_IO_H */
|
431
base/bmp_io_c.h
431
base/bmp_io_c.h
@ -1,431 +0,0 @@
|
||||
#include "bmp_io.h"
|
||||
#include "os.h"
|
||||
#include "endian.h"
|
||||
#include <stdio.h> /* fopen() */
|
||||
#include <string.h> /* memcpy() */
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include "ms_stdbool.h"
|
||||
#include "ms_stdint.h"
|
||||
#else
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#pragma pack(push, 1) /* The following structs should be continguous, so we can
|
||||
* copy them in one read. */
|
||||
/*
|
||||
* Standard, initial BMP Header
|
||||
*/
|
||||
struct BITMAP_FILE_HEADER {
|
||||
uint16_t magic; /* First two byes of the file; should be 0x4D42. */
|
||||
uint32_t fileSize; /* Size of the BMP file in bytes (unreliable). */
|
||||
uint32_t reserved; /* Application-specific. */
|
||||
uint32_t imageOffset; /* Offset to bitmap data. */
|
||||
};
|
||||
|
||||
#define BMP_MAGIC 0x4D42 /* The starting key that marks the file as a BMP. */
|
||||
|
||||
enum _BMP_COMPRESSION {
|
||||
kBMP_RGB = 0, /* No compression. */
|
||||
kBMP_RLE8 = 1, /* Can only be used with 8-bit bitmaps. */
|
||||
kBMP_RLE4 = 2, /* Can only be used with 4-bit bitmaps. */
|
||||
kBMP_BITFIELDS = 3, /* Can only be used with 16/32-bit bitmaps. */
|
||||
kBMP_JPEG = 4, /* Bitmap contains a JPEG image. */
|
||||
kBMP_PNG = 5 /* Bitmap contains a PNG image. */
|
||||
};
|
||||
|
||||
typedef uint32_t BMP_COMPRESSION;
|
||||
|
||||
/*
|
||||
* Windows 3 Header
|
||||
*/
|
||||
struct BITMAP_INFO_HEADER {
|
||||
uint32_t headerSize; /* The size of this header (40 bytes). */
|
||||
int32_t width; /* The bitmap width in pixels. */
|
||||
int32_t height; /* The bitmap height in pixels. */
|
||||
/* (A negative value denotes that the image
|
||||
* is flipped.) */
|
||||
uint16_t colorPlanes; /* The number of color planes; must be 1. */
|
||||
uint16_t bitsPerPixel; /* The color depth of the image (1, 4, 8, 16,
|
||||
* 24, or 32). */
|
||||
BMP_COMPRESSION compression; /* The compression method being used. */
|
||||
uint32_t imageSize; /* Size of the bitmap in bytes (unreliable).*/
|
||||
int32_t xRes; /* The horizontal resolution (unreliable). */
|
||||
int32_t yRes; /* The vertical resolution (unreliable). */
|
||||
uint32_t colorsUsed; /* The number of colors in the color table,
|
||||
* or 0 to default to 2^n. */
|
||||
uint32_t colorsImportant; /* Colors important for displaying bitmap,
|
||||
* or 0 when every color is equally important;
|
||||
* ignored. */
|
||||
};
|
||||
|
||||
/*
|
||||
* OS/2 v1 Header
|
||||
*/
|
||||
struct BITMAP_CORE_HEADER {
|
||||
uint32_t headerSize; /* The size of this header (12 bytes). */
|
||||
uint16_t width; /* The bitmap width in pixels. */
|
||||
uint16_t height; /* The bitmap height in pixels. */
|
||||
uint16_t colorPlanes; /* The number of color planes; must be 1. */
|
||||
uint16_t bitsPerPixel; /* Color depth of the image (1, 4, 8, or 24). */
|
||||
};
|
||||
|
||||
#pragma pack(pop) /* Let the compiler do what it wants now. */
|
||||
|
||||
/* BMP files are always saved in little endian format (x86), so we need to
|
||||
* convert them if we're not on a little endian machine (e.g., ARM & ppc). */
|
||||
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
|
||||
/* Converts bitmap file header from to and from little endian, if and only if
|
||||
* host is big endian. */
|
||||
static void convertBitmapFileHeader(struct BITMAP_FILE_HEADER *header)
|
||||
{
|
||||
header->magic = swapLittleAndHost16(header->magic);
|
||||
swapLittleAndHost32(header->fileSize);
|
||||
swapLittleAndHost32(header->reserved);
|
||||
swapLittleAndHost32(header->imageOffset);
|
||||
}
|
||||
|
||||
/* Converts bitmap info header from to and from little endian, if and only if
|
||||
* host is big endian. */
|
||||
static void convertBitmapInfoHeader(struct BITMAP_INFO_HEADER *header)
|
||||
{
|
||||
header->headerSize = swapLittleAndHost32(header->headerSize);
|
||||
header->width = swapLittleAndHost32(header->width);
|
||||
header->height = swapLittleAndHost32(header->height);
|
||||
header->colorPlanes = swapLittleAndHost16(header->colorPlanes);
|
||||
header->bitsPerPixel = swapLittleAndHost16(header->bitsPerPixel);
|
||||
header->compression = swapLittleAndHost32(header->compression);
|
||||
header->imageSize = swapLittleAndHost32(header->imageSize);
|
||||
header->xRes = swapLittleAndHost32(header->xRes);
|
||||
header->yRes = swapLittleAndHost32(header->yRes);
|
||||
header->colorsUsed = swapLittleAndHost32(header->colorsUsed);
|
||||
header->colorsImportant = swapLittleAndHost32(header->colorsImportant);
|
||||
}
|
||||
|
||||
#elif __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
/* No conversion necessary if we are already little endian. */
|
||||
#define convertBitmapFileHeader(header)
|
||||
#define convertBitmapInfoHeader(header)
|
||||
#endif
|
||||
|
||||
/* Returns newly alloc'd image data from bitmap file. The current position of
|
||||
* the file must be at the start of the image before calling this. */
|
||||
static uint8_t *readImageData(FILE *fp, size_t width, size_t height,
|
||||
uint8_t bytesPerPixel, size_t bytewidth);
|
||||
|
||||
/* Copys image buffer from |bitmap| to |dest| in BGR format. */
|
||||
static void copyBGRDataFromMMBitmap(MMBitmapRef bitmap, uint8_t *dest);
|
||||
|
||||
const char *MMBMPReadErrorString(MMIOError error)
|
||||
{
|
||||
switch (error) {
|
||||
case kBMPAccessError:
|
||||
return "Could not open file";
|
||||
case kBMPInvalidKeyError:
|
||||
return "Not a BMP file";
|
||||
case kBMPUnsupportedHeaderError:
|
||||
return "Unsupported BMP header";
|
||||
case kBMPInvalidColorPanesError:
|
||||
return "Invalid number of color panes in BMP file";
|
||||
case kBMPUnsupportedColorDepthError:
|
||||
return "Unsupported color depth in BMP file";
|
||||
case kBMPUnsupportedCompressionError:
|
||||
return "Unsupported file compression in BMP file";
|
||||
case kBMPInvalidPixelDataError:
|
||||
return "Could not read BMP pixel data";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
MMBitmapRef newMMBitmapFromBMP(const char *path, MMBMPReadError *err)
|
||||
{
|
||||
FILE *fp;
|
||||
struct BITMAP_FILE_HEADER fileHeader = {0}; /* Initialize elements to 0. */
|
||||
struct BITMAP_INFO_HEADER dibHeader = {0};
|
||||
uint32_t headerSize = 0;
|
||||
uint8_t bytesPerPixel;
|
||||
size_t bytewidth;
|
||||
uint8_t *imageBuf;
|
||||
|
||||
if ((fp = fopen(path, "rb")) == NULL) {
|
||||
if (err != NULL) *err = kBMPAccessError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialize error code to generic value. */
|
||||
if (err != NULL) *err = kBMPGenericError;
|
||||
|
||||
if (fread(&fileHeader, sizeof(fileHeader), 1, fp) == 0) goto bail;
|
||||
|
||||
/* Convert from little-endian if it's not already. */
|
||||
convertBitmapFileHeader(&fileHeader);
|
||||
|
||||
/* First two bytes should always be 0x4D42. */
|
||||
if (fileHeader.magic != BMP_MAGIC) {
|
||||
if (err != NULL) *err = kBMPInvalidKeyError;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Get header size. */
|
||||
if (fread(&headerSize, sizeof(headerSize), 1, fp) == 0) goto bail;
|
||||
headerSize = swapLittleAndHost32(headerSize);
|
||||
|
||||
/* Back up before reading header. */
|
||||
if (fseek(fp, -(long)sizeof(headerSize), SEEK_CUR) < 0) goto bail;
|
||||
|
||||
if (headerSize == 12) { /* OS/2 v1 header */
|
||||
struct BITMAP_CORE_HEADER coreHeader = {0};
|
||||
if (fread(&coreHeader, sizeof(coreHeader), 1, fp) == 0) goto bail;
|
||||
|
||||
dibHeader.width = coreHeader.width;
|
||||
dibHeader.height = coreHeader.height;
|
||||
dibHeader.colorPlanes = coreHeader.colorPlanes;
|
||||
dibHeader.bitsPerPixel = coreHeader.bitsPerPixel;
|
||||
} else if (headerSize == 40 || headerSize == 108 || headerSize == 124) {
|
||||
/* Windows v3/v4/v5 header */
|
||||
/* Read only the common part (v3) and skip over the rest. */
|
||||
if (fread(&dibHeader, sizeof(dibHeader), 1, fp) == 0) goto bail;
|
||||
} else {
|
||||
if (err != NULL) *err = kBMPUnsupportedHeaderError;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
convertBitmapInfoHeader(&dibHeader);
|
||||
|
||||
if (dibHeader.colorPlanes != 1) {
|
||||
if (err != NULL) *err = kBMPInvalidColorPanesError;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Currently only 24-bit and 32-bit are supported. */
|
||||
if (dibHeader.bitsPerPixel != 24 && dibHeader.bitsPerPixel != 32) {
|
||||
if (err != NULL) *err = kBMPUnsupportedColorDepthError;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (dibHeader.compression != kBMP_RGB) {
|
||||
if (err != NULL) *err = kBMPUnsupportedCompressionError;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* This can happen because we don't fully parse Windows v4/v5 headers. */
|
||||
if (ftell(fp) != (long)fileHeader.imageOffset) {
|
||||
fseek(fp, fileHeader.imageOffset, SEEK_SET);
|
||||
}
|
||||
|
||||
/* Get bytes per row, including padding. */
|
||||
bytesPerPixel = dibHeader.bitsPerPixel / 8;
|
||||
bytewidth = ADD_PADDING(dibHeader.width * bytesPerPixel);
|
||||
|
||||
imageBuf = readImageData(fp, dibHeader.width, abs(dibHeader.height),
|
||||
bytesPerPixel, bytewidth);
|
||||
fclose(fp);
|
||||
|
||||
if (imageBuf == NULL) {
|
||||
if (err != NULL) *err = kBMPInvalidPixelDataError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* A negative height indicates that the image is flipped.
|
||||
*
|
||||
* We store our bitmaps as "flipped" according to the BMP format; i.e., (0, 0)
|
||||
* is the top left, not bottom left. So we only need to flip the bitmap if
|
||||
* the height is NOT negative. */
|
||||
if (dibHeader.height < 0) {
|
||||
dibHeader.height = -dibHeader.height;
|
||||
} else {
|
||||
flipBitmapData(imageBuf, dibHeader.width, dibHeader.height, bytewidth);
|
||||
}
|
||||
|
||||
return createMMBitmap(imageBuf, dibHeader.width, dibHeader.height,
|
||||
bytewidth, (uint8_t)dibHeader.bitsPerPixel,
|
||||
bytesPerPixel);
|
||||
|
||||
bail:
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t *createBitmapData(MMBitmapRef bitmap, size_t *len)
|
||||
{
|
||||
/* BMP files are always aligned to 4 bytes. */
|
||||
const size_t bytewidth = ((bitmap->width * bitmap->bytesPerPixel) + 3) & ~3;
|
||||
|
||||
const size_t imageSize = bytewidth * bitmap->height;
|
||||
struct BITMAP_FILE_HEADER *fileHeader;
|
||||
struct BITMAP_INFO_HEADER *dibHeader;
|
||||
|
||||
/* Should always be 54. */
|
||||
const size_t imageOffset = sizeof(*fileHeader) + sizeof(*dibHeader);
|
||||
uint8_t *data;
|
||||
const size_t dataLen = imageOffset + imageSize;
|
||||
|
||||
data = calloc(1, dataLen);
|
||||
if (data == NULL) return NULL;
|
||||
|
||||
/* Save top header. */
|
||||
fileHeader = (struct BITMAP_FILE_HEADER *)data;
|
||||
fileHeader->magic = BMP_MAGIC;
|
||||
fileHeader->fileSize = (uint32_t)(sizeof(*dibHeader) + imageSize);
|
||||
fileHeader->imageOffset = (uint32_t)imageOffset;
|
||||
|
||||
/* BMP files are always stored as little-endian, so we need to convert back
|
||||
* if necessary. */
|
||||
convertBitmapFileHeader(fileHeader);
|
||||
|
||||
/* Copy Windows v3 header. */
|
||||
dibHeader = (struct BITMAP_INFO_HEADER *)(data + sizeof(*fileHeader));
|
||||
dibHeader->headerSize = sizeof(*dibHeader); /* Should always be 40. */
|
||||
dibHeader->width = (int32_t)bitmap->width;
|
||||
dibHeader->height = -(int32_t)bitmap->height; /* Our bitmaps are "flipped". */
|
||||
dibHeader->colorPlanes = 1;
|
||||
dibHeader->bitsPerPixel = bitmap->bitsPerPixel;
|
||||
dibHeader->compression = kBMP_RGB; /* Don't save with compression. */
|
||||
dibHeader->imageSize = (uint32_t)imageSize;
|
||||
|
||||
convertBitmapInfoHeader(dibHeader);
|
||||
|
||||
/* Lastly, copy the pixel data. */
|
||||
copyBGRDataFromMMBitmap(bitmap, data + imageOffset);
|
||||
|
||||
if (len != NULL) *len = dataLen;
|
||||
return data;
|
||||
}
|
||||
|
||||
int saveMMBitmapAsBMP(MMBitmapRef bitmap, const char *path)
|
||||
{
|
||||
FILE *fp;
|
||||
size_t dataLen;
|
||||
uint8_t *data;
|
||||
|
||||
if ((fp = fopen(path, "wb")) == NULL) return -1;
|
||||
|
||||
if ((data = createBitmapData(bitmap, &dataLen)) == NULL) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fwrite(data, dataLen, 1, fp) == 0) {
|
||||
free(data);
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(data);
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t *readImageData(FILE *fp, size_t width, size_t height,
|
||||
uint8_t bytesPerPixel, size_t bytewidth)
|
||||
{
|
||||
size_t imageSize = bytewidth * height;
|
||||
uint8_t *imageBuf = calloc(1, imageSize);
|
||||
|
||||
if (MMRGB_IS_BGR && (bytewidth % 4) == 0) { /* No conversion needed. */
|
||||
if (fread(imageBuf, imageSize, 1, fp) == 0) {
|
||||
free(imageBuf);
|
||||
return NULL;
|
||||
}
|
||||
} else { /* Convert from BGR with 4-byte alignment. */
|
||||
uint8_t *row = malloc(bytewidth);
|
||||
size_t y;
|
||||
const size_t bmp_bytewidth = (width * bytesPerPixel + 3) & ~3;
|
||||
|
||||
if (row == NULL) return NULL;
|
||||
assert(bmp_bytewidth <= bytewidth);
|
||||
|
||||
/* Read image data row by row. */
|
||||
for (y = 0; y < height; ++y) {
|
||||
const size_t rowOffset = y * bytewidth;
|
||||
size_t x;
|
||||
uint8_t *rowptr = row;
|
||||
if (fread(row, bmp_bytewidth, 1, fp) == 0) {
|
||||
free(imageBuf);
|
||||
free(row);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (x = 0; x < width; ++x) {
|
||||
const size_t colOffset = x * bytesPerPixel;
|
||||
MMRGBColor *color = (MMRGBColor *)(imageBuf +
|
||||
rowOffset + colOffset);
|
||||
|
||||
/* BMP files are stored in BGR format. */
|
||||
color->blue = rowptr[0];
|
||||
color->green = rowptr[1];
|
||||
color->red = rowptr[2];
|
||||
rowptr += bytesPerPixel;
|
||||
}
|
||||
}
|
||||
|
||||
free(row);
|
||||
}
|
||||
|
||||
return imageBuf;
|
||||
}
|
||||
|
||||
static void copyBGRDataFromMMBitmap(MMBitmapRef bitmap, uint8_t *dest)
|
||||
{
|
||||
if (MMRGB_IS_BGR && (bitmap->bytewidth % 4) == 0) { /* No conversion needed. */
|
||||
memcpy(dest, bitmap->imageBuffer, bitmap->bytewidth * bitmap->height);
|
||||
} else { /* Convert to RGB with other-than-4-byte alignment. */
|
||||
const size_t bytewidth = (bitmap->width * bitmap->bytesPerPixel + 3) & ~3;
|
||||
size_t y;
|
||||
|
||||
/* Copy image data row by row. */
|
||||
for (y = 0; y < bitmap->height; ++y) {
|
||||
uint8_t *rowptr = dest + (y * bytewidth);
|
||||
size_t x;
|
||||
for (x = 0; x < bitmap->width; ++x) {
|
||||
MMRGBColor *color = MMRGBColorRefAtPoint(bitmap, x, y);
|
||||
|
||||
/* BMP files are stored in BGR format. */
|
||||
rowptr[0] = color->blue;
|
||||
rowptr[1] = color->green;
|
||||
rowptr[2] = color->red;
|
||||
|
||||
rowptr += bitmap->bytesPerPixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform an in-place swap from Quadrant 1 to Quadrant III format (upside-down
|
||||
* PostScript/GL to right side up QD/CG raster format) We do this in-place,
|
||||
* which requires more copying, but will touch only half the pages.
|
||||
*
|
||||
* This is blatantly copied from Apple's glGrab example code. */
|
||||
void flipBitmapData(void *data, size_t width, size_t height, size_t bytewidth)
|
||||
{
|
||||
size_t top, bottom;
|
||||
void *topP;
|
||||
void *bottomP;
|
||||
void *tempbuf;
|
||||
|
||||
if (height <= 1) return; /* No flipping necessary if height is <= 1. */
|
||||
|
||||
top = 0;
|
||||
bottom = height - 1;
|
||||
tempbuf = malloc(bytewidth);
|
||||
if (tempbuf == NULL) return;
|
||||
|
||||
while (top < bottom) {
|
||||
topP = (void *)((top * bytewidth) + (intptr_t)data);
|
||||
bottomP = (void *)((bottom * bytewidth) + (intptr_t)data);
|
||||
|
||||
/* Save and swap scanlines.
|
||||
* Does a simple in-place exchange with a temp buffer. */
|
||||
memcpy(tempbuf, topP, bytewidth);
|
||||
memcpy(topP, bottomP, bytewidth);
|
||||
memcpy(bottomP, tempbuf, bytewidth);
|
||||
|
||||
++top;
|
||||
--bottom;
|
||||
}
|
||||
free(tempbuf);
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef COLOR_FIND_H
|
||||
#define COLOR_FIND_H
|
||||
|
||||
#include "MMBitmap.h"
|
||||
#include "MMPointArray.h"
|
||||
|
||||
/* Convenience wrapper around findColorInRect(), where |rect| is the bounds of
|
||||
* the image. */
|
||||
#define findColorInImage(image, color, pointPtr, tolerance) \
|
||||
findColorInRect(image, color, pointPtr, MMBitmapGetBounds(image), tolerance)
|
||||
|
||||
/* Attempt to find a pixel with the given color in |image| inside |rect|.
|
||||
* Returns 0 on success, non-zero on failure. If the color was found and
|
||||
* |point| is not NULL, it will be initialized to the (x, y) coordinates the
|
||||
* RGB color.
|
||||
*
|
||||
* |tolerance| should be in the range 0.0f - 1.0f, denoting how closely the
|
||||
* colors need to match, with 0 being exact and 1 being any. */
|
||||
int findColorInRect(MMBitmapRef image, MMRGBHex color, MMPoint *point,
|
||||
MMRect rect, float tolerance);
|
||||
|
||||
/* Convenience wrapper around findAllRGBInRect(), where |rect| is the bounds of
|
||||
* the image. */
|
||||
#define findAllColorInImage(image, color, tolerance) \
|
||||
findAllColorInRect(image, color, MMBitmapGetBounds(image), tolerance)
|
||||
|
||||
/* Returns MMPointArray of all pixels of given color in |image| inside of
|
||||
* |rect|. Note that an array is returned regardless of whether the color was
|
||||
* found; check array->count to see if it actually was.
|
||||
*
|
||||
* Responsibility for freeing the MMPointArray with destroyMMPointArray() is
|
||||
* given to the caller.
|
||||
*
|
||||
* |tolerance| should be in the range 0.0f - 1.0f, denoting how closely the
|
||||
* colors need to match, with 0 being exact and 1 being any. */
|
||||
MMPointArrayRef findAllColorInRect(MMBitmapRef image, MMRGBHex color,
|
||||
MMRect rect, float tolerance);
|
||||
|
||||
/* Convenience wrapper around countOfColorsInRect, where |rect| is the bounds
|
||||
* of the image. */
|
||||
#define countOfColorsInImage(image, color, tolerance) \
|
||||
countOfColorsInRect(image, color, MMBitmapGetBounds(image), tolerance)
|
||||
|
||||
/* Returns the count of the given color in |rect| inside of |image|. */
|
||||
size_t countOfColorsInRect(MMBitmapRef image, MMRGBHex color, MMRect rect,
|
||||
float tolerance);
|
||||
|
||||
#endif /* COLOR_FIND_H */
|
@ -1,58 +0,0 @@
|
||||
#include "color_find.h"
|
||||
// #include "../screen/screen_init.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
/* Abstracted, general function to avoid repeated code. */
|
||||
static int findColorInRectAt(MMBitmapRef image, MMRGBHex color, MMPoint *point,
|
||||
MMRect rect, float tolerance, MMPoint startPoint)
|
||||
{
|
||||
MMPoint scan = startPoint;
|
||||
if (!MMBitmapRectInBounds(image, rect)) return -1;
|
||||
|
||||
for (; scan.y < rect.size.height; ++scan.y) {
|
||||
for (; scan.x < rect.size.width; ++scan.x) {
|
||||
MMRGBHex found = MMRGBHexAtPoint(image, scan.x, scan.y);
|
||||
if (MMRGBHexSimilarToColor(color, found, tolerance)) {
|
||||
if (point != NULL) *point = scan;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
scan.x = rect.origin.x;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int findColorInRect(MMBitmapRef image, MMRGBHex color,
|
||||
MMPoint *point, MMRect rect, float tolerance)
|
||||
{
|
||||
return findColorInRectAt(image, color, point, rect, tolerance, rect.origin);
|
||||
}
|
||||
|
||||
MMPointArrayRef findAllColorInRect(MMBitmapRef image, MMRGBHex color,
|
||||
MMRect rect, float tolerance)
|
||||
{
|
||||
MMPointArrayRef pointArray = createMMPointArray(0);
|
||||
MMPoint point = MMPointZero;
|
||||
|
||||
while (findColorInRectAt(image, color, &point, rect, tolerance, point) == 0) {
|
||||
MMPointArrayAppendPoint(pointArray, point);
|
||||
ITER_NEXT_POINT(point, rect.size.width, rect.origin.x);
|
||||
}
|
||||
|
||||
return pointArray;
|
||||
}
|
||||
|
||||
size_t countOfColorsInRect(MMBitmapRef image, MMRGBHex color, MMRect rect,
|
||||
float tolerance)
|
||||
{
|
||||
size_t count = 0;
|
||||
MMPoint point = MMPointZero;
|
||||
|
||||
while (findColorInRectAt(image, color, &point, rect, tolerance, point) == 0) {
|
||||
ITER_NEXT_POINT(point, rect.size.width, rect.origin.x);
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
@ -4,11 +4,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#define DEADBEEF_MAX UINT32_MAX
|
||||
|
||||
/* Dead Beef Random Number Generator
|
||||
* From: http://inglorion.net/software/deadbeef_rand
|
||||
* A fast, portable psuedo-random number generator by BJ Amsterdam Zuidoost.
|
||||
* Stated in license terms: "Feel free to use the code in your own software." */
|
||||
/* Dead Beef Random Number Generator From: http://inglorion.net/software/deadbeef_rand */
|
||||
|
||||
/* Generates a random number between 0 and DEADBEEF_MAX. */
|
||||
uint32_t deadbeef_rand(void);
|
||||
@ -22,14 +18,11 @@ uint32_t deadbeef_generate_seed(void);
|
||||
/* Seeds with the above function. */
|
||||
#define deadbeef_srand_time() deadbeef_srand(deadbeef_generate_seed())
|
||||
|
||||
/* Returns random double in the range [a, b).
|
||||
* Taken directly from the rand() man page. */
|
||||
/* Returns random double in the range [a, b).*/
|
||||
#define DEADBEEF_UNIFORM(a, b) \
|
||||
((a) + (deadbeef_rand() / (((double)DEADBEEF_MAX / (b - a) + 1))))
|
||||
|
||||
/* Returns random integer in the range [a, b).
|
||||
* Also taken from the rand() man page. */
|
||||
#define DEADBEEF_RANDRANGE(a, b) \
|
||||
(uint32_t)DEADBEEF_UNIFORM(a, b)
|
||||
/* Returns random integer in the range [a, b).*/
|
||||
#define DEADBEEF_RANDRANGE(a, b) (uint32_t)DEADBEEF_UNIFORM(a, b)
|
||||
|
||||
#endif /* DEADBEEF_RAND_H */
|
||||
|
@ -4,23 +4,19 @@
|
||||
static uint32_t deadbeef_seed;
|
||||
static uint32_t deadbeef_beef = 0xdeadbeef;
|
||||
|
||||
uint32_t deadbeef_rand(void)
|
||||
{
|
||||
uint32_t deadbeef_rand(void) {
|
||||
deadbeef_seed = (deadbeef_seed << 7) ^ ((deadbeef_seed >> 25) + deadbeef_beef);
|
||||
deadbeef_beef = (deadbeef_beef << 7) ^ ((deadbeef_beef >> 25) + 0xdeadbeef);
|
||||
return deadbeef_seed;
|
||||
}
|
||||
|
||||
void deadbeef_srand(uint32_t x)
|
||||
{
|
||||
void deadbeef_srand(uint32_t x) {
|
||||
deadbeef_seed = x;
|
||||
deadbeef_beef = 0xdeadbeef;
|
||||
}
|
||||
|
||||
/* Taken directly from the documentation:
|
||||
* http://inglorion.net/software/cstuff/deadbeef_rand/ */
|
||||
uint32_t deadbeef_generate_seed(void)
|
||||
{
|
||||
/* Taken directly from the documentation: http://inglorion.net/software/cstuff/deadbeef_rand/ */
|
||||
uint32_t deadbeef_generate_seed(void) {
|
||||
uint32_t t = (uint32_t)time(NULL);
|
||||
uint32_t c = (uint32_t)clock();
|
||||
return (t << 24) ^ (c << 11) ^ t ^ (size_t) &c;
|
||||
|
123
base/endian.h
123
base/endian.h
@ -1,123 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef ENDIAN_H
|
||||
#define ENDIAN_H
|
||||
|
||||
#include "os.h"
|
||||
|
||||
/*
|
||||
* (Mostly) cross-platform endian definitions and bit swapping macros.
|
||||
* Unfortunately, there is no standard C header for this, so we just
|
||||
* include the most common ones and fallback to our own custom macros.
|
||||
*/
|
||||
|
||||
#if defined(__linux__) /* Linux */
|
||||
#include <endian.h>
|
||||
#include <byteswap.h>
|
||||
#elif (defined(__FreeBSD__) && __FreeBSD_version >= 470000) || \
|
||||
defined(__OpenBSD__) || defined(__NetBSD__) /* (Free|Open|Net)BSD */
|
||||
#include <sys/endian.h>
|
||||
#define __BIG_ENDIAN BIG_ENDIAN
|
||||
#define __LITTLE_ENDIAN LITTLE_ENDIAN
|
||||
#define __BYTE_ORDER BYTE_ORDER
|
||||
#elif defined(IS_MACOSX) || (defined(BSD) && (BSD >= 199103)) /* Other BSD */
|
||||
#include <machine/endian.h>
|
||||
#define __BIG_ENDIAN BIG_ENDIAN
|
||||
#define __LITTLE_ENDIAN LITTLE_ENDIAN
|
||||
#define __BYTE_ORDER BYTE_ORDER
|
||||
#elif defined(IS_WINDOWS) /* Windows is assumed to be little endian only. */
|
||||
#define __BIG_ENDIAN 4321
|
||||
#define __LITTLE_ENDIAN 1234
|
||||
#define __BYTE_ORDER __LITTLE_ENDIAN
|
||||
#endif
|
||||
|
||||
/* Fallback to custom constants. */
|
||||
#if !defined(__BIG_ENDIAN)
|
||||
#define __BIG_ENDIAN 4321
|
||||
#endif
|
||||
|
||||
#if !defined(__LITTLE_ENDIAN)
|
||||
#define __LITTLE_ENDIAN 1234
|
||||
#endif
|
||||
|
||||
/* Prefer compiler flag settings if given. */
|
||||
#if defined(MM_BIG_ENDIAN)
|
||||
#undef __BYTE_ORDER /* Avoid redefined macro compiler warning. */
|
||||
#define __BYTE_ORDER __BIG_ENDIAN
|
||||
#elif defined(MM_LITTLE_ENDIAN)
|
||||
#undef __BYTE_ORDER /* Avoid redefined macro compiler warning. */
|
||||
#define __BYTE_ORDER __LITTLE_ENDIAN
|
||||
#endif
|
||||
|
||||
/* Define default endian-ness. */
|
||||
#ifndef __LITTLE_ENDIAN
|
||||
#define __LITTLE_ENDIAN 1234
|
||||
#endif /* __LITTLE_ENDIAN */
|
||||
|
||||
#ifndef __BIG_ENDIAN
|
||||
#define __BIG_ENDIAN 4321
|
||||
#endif /* __BIG_ENDIAN */
|
||||
|
||||
#ifndef __BYTE_ORDER
|
||||
#warning "Byte order not defined on your system; assuming little endian"
|
||||
#define __BYTE_ORDER __LITTLE_ENDIAN
|
||||
#endif /* __BYTE_ORDER */
|
||||
|
||||
#if __BYTE_ORDER != __BIG_ENDIAN && __BYTE_ORDER != __LITTLE_ENDIAN
|
||||
#error "__BYTE_ORDER set to unknown byte order"
|
||||
#endif
|
||||
|
||||
#if defined(IS_MACOSX)
|
||||
#include <libkern/OSByteOrder.h>
|
||||
|
||||
/* OS X system functions. */
|
||||
#define bitswap16(i) OSSwapInt16(i)
|
||||
#define bitswap32(i) OSSwapInt32(i)
|
||||
#define swapLittleAndHost32(i) OSSwapLittleToHostInt32(i)
|
||||
#define swapLittleAndHost16(i) OSSwapLittleToHostInt16(i)
|
||||
#else
|
||||
#ifndef bitswap16
|
||||
#if defined(bswap16)
|
||||
#define bitswap16(i) bswap16(i) /* FreeBSD system function */
|
||||
#elif defined(bswap_16)
|
||||
#define bitswap16(i) bswap_16(i) /* Linux system function */
|
||||
#else /* Default macro */
|
||||
#define bitswap16(i) (((uint16_t)(i) & 0xFF00) >> 8) | \
|
||||
(((uint16_t)(i) & 0x00FF) << 8)
|
||||
#endif
|
||||
#endif /* bitswap16 */
|
||||
|
||||
#ifndef bitswap32
|
||||
#if defined(bswap32)
|
||||
#define bitswap32(i) bswap32(i) /* FreeBSD system function. */
|
||||
#elif defined(bswap_32)
|
||||
#define bitswap32(i) bswap_32(i) /* Linux system function. */
|
||||
#else /* Default macro */
|
||||
#define bitswap32(i) (((uint32_t)(i) & 0xFF000000) >> 24) | \
|
||||
((uint32_t)((i) & 0x00FF0000) >> 8) | \
|
||||
((uint32_t)((i) & 0x0000FF00) << 8) | \
|
||||
((uint32_t)((i) & 0x000000FF) << 24)
|
||||
#endif
|
||||
#endif /* bitswap32 */
|
||||
#endif
|
||||
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
/* Little endian to/from host byte order (big endian). */
|
||||
#ifndef swapLittleAndHost16
|
||||
#define swapLittleAndHost16(i) bitswap16(i)
|
||||
#endif /* swapLittleAndHost16 */
|
||||
|
||||
#ifndef swapLittleAndHost32
|
||||
#define swapLittleAndHost32(i) bitswap32(i)
|
||||
#endif /* swapLittleAndHost32 */
|
||||
#elif __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
/* We are already little endian, so no conversion is needed. */
|
||||
#ifndef swapLittleAndHost16
|
||||
#define swapLittleAndHost16(i) i
|
||||
#endif /* swapLittleAndHost16 */
|
||||
|
||||
#ifndef swapLittleAndHost32
|
||||
#define swapLittleAndHost32(i) i
|
||||
#endif /* swapLittleAndHost32 */
|
||||
#endif
|
||||
|
||||
#endif /* ENDIAN_H */
|
@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
/* A complicated, portable model for declaring inline functions in
|
||||
* header files. */
|
||||
/* A complicated, portable model for declaring inline functions in header files. */
|
||||
#if !defined(H_INLINE)
|
||||
#if defined(__GNUC__)
|
||||
#define H_INLINE static __inline__ __attribute__((always_inline))
|
||||
|
79
base/io.c
79
base/io.c
@ -1,79 +0,0 @@
|
||||
#include "io.h"
|
||||
#include "os.h"
|
||||
#include "bmp_io.h"
|
||||
#include "png_io.h"
|
||||
#include <stdio.h> /* For fputs() */
|
||||
#include <string.h> /* For strcmp() */
|
||||
#include <ctype.h> /* For tolower() */
|
||||
|
||||
const char *getExtension(const char *fname, size_t len)
|
||||
{
|
||||
if (fname == NULL || len <= 0) return NULL;
|
||||
|
||||
while (--len > 0 && fname[len] != '.' && fname[len] != '\0')
|
||||
;
|
||||
|
||||
return fname + len + 1;
|
||||
}
|
||||
|
||||
MMImageType imageTypeFromExtension(const char *extension)
|
||||
{
|
||||
char ext[4];
|
||||
const size_t maxlen = sizeof(ext) / sizeof(ext[0]);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; extension[i] != '\0'; ++i) {
|
||||
if (i >= maxlen) return kInvalidImageType;
|
||||
ext[i] = tolower(extension[i]);
|
||||
}
|
||||
ext[i] = '\0';
|
||||
|
||||
if (strcmp(ext, "png") == 0) {
|
||||
return kPNGImageType;
|
||||
} else if (strcmp(ext, "bmp") == 0) {
|
||||
return kBMPImageType;
|
||||
} else {
|
||||
return kInvalidImageType;
|
||||
}
|
||||
}
|
||||
|
||||
MMBitmapRef newMMBitmapFromFile(const char *path,
|
||||
MMImageType type,
|
||||
MMIOError *err)
|
||||
{
|
||||
switch (type) {
|
||||
case kBMPImageType:
|
||||
return newMMBitmapFromBMP(path, err);
|
||||
case kPNGImageType:
|
||||
return newMMBitmapFromPNG(path, err);
|
||||
default:
|
||||
if (err != NULL) *err = kMMIOUnsupportedTypeError;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int saveMMBitmapToFile(MMBitmapRef bitmap,
|
||||
const char *path,
|
||||
MMImageType type)
|
||||
{
|
||||
switch (type) {
|
||||
case kBMPImageType:
|
||||
return saveMMBitmapAsBMP(bitmap, path);
|
||||
case kPNGImageType:
|
||||
return saveMMBitmapAsPNG(bitmap, path);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
const char *MMIOErrorString(MMImageType type, MMIOError error)
|
||||
{
|
||||
switch (type) {
|
||||
case kBMPImageType:
|
||||
return MMBMPReadErrorString(error);
|
||||
case kPNGImageType:
|
||||
return MMPNGReadErrorString(error);
|
||||
default:
|
||||
return "Unsupported image type";
|
||||
}
|
||||
}
|
46
base/io.h
46
base/io.h
@ -1,46 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef IO_H
|
||||
#define IO_H
|
||||
|
||||
#include "MMBitmap.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
enum _MMImageType {
|
||||
kInvalidImageType = 0,
|
||||
kPNGImageType,
|
||||
kBMPImageType /* Currently only PNG and BMP are supported. */
|
||||
};
|
||||
|
||||
typedef uint16_t MMImageType;
|
||||
|
||||
enum _MMIOError {
|
||||
kMMIOUnsupportedTypeError = 0
|
||||
};
|
||||
|
||||
typedef uint16_t MMIOError;
|
||||
|
||||
const char *getExtension(const char *fname, size_t len);
|
||||
|
||||
/* Returns best guess at the MMImageType based on a file extension, or
|
||||
* |kInvalidImageType| if no matching type was found. */
|
||||
MMImageType imageTypeFromExtension(const char *ext);
|
||||
|
||||
/* Attempts to parse the file of the given type at the given path.
|
||||
* |filepath| is an ASCII string describing the absolute POSIX path.
|
||||
* Returns new bitmap (to be destroy()'d by caller) on success, NULL on error.
|
||||
* If |error| is non-NULL, it will be set to the error code on return.
|
||||
*/
|
||||
MMBitmapRef newMMBitmapFromFile(const char *path, MMImageType type, MMIOError *err);
|
||||
|
||||
/* Saves |bitmap| to a file of the given type at the given path.
|
||||
* |filepath| is an ASCII string describing the absolute POSIX path.
|
||||
* Returns 0 on success, -1 on error. */
|
||||
int saveMMBitmapToFile(MMBitmapRef bitmap, const char *path, MMImageType type);
|
||||
|
||||
/* Returns description of given error code.
|
||||
* Returned string is constant and hence should not be freed. */
|
||||
const char *MMIOErrorString(MMImageType type, MMIOError error);
|
||||
|
||||
#endif /* IO_H */
|
79
base/io_c.h
79
base/io_c.h
@ -1,79 +0,0 @@
|
||||
#include "io.h"
|
||||
// #include "os.h"
|
||||
// #include "bmp_io_c.h"
|
||||
#include "png_io_c.h"
|
||||
#include <stdio.h> /* For fputs() */
|
||||
#include <string.h> /* For strcmp() */
|
||||
#include <ctype.h> /* For tolower() */
|
||||
|
||||
const char *getExtension(const char *fname, size_t len)
|
||||
{
|
||||
if (fname == NULL || len <= 0) return NULL;
|
||||
|
||||
while (--len > 0 && fname[len] != '.' && fname[len] != '\0')
|
||||
;
|
||||
|
||||
return fname + len + 1;
|
||||
}
|
||||
|
||||
MMImageType imageTypeFromExtension(const char *extension)
|
||||
{
|
||||
char ext[4];
|
||||
const size_t maxlen = sizeof(ext) / sizeof(ext[0]);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; extension[i] != '\0'; ++i) {
|
||||
if (i >= maxlen) return kInvalidImageType;
|
||||
ext[i] = tolower(extension[i]);
|
||||
}
|
||||
ext[i] = '\0';
|
||||
|
||||
if (strcmp(ext, "png") == 0) {
|
||||
return kPNGImageType;
|
||||
} else if (strcmp(ext, "bmp") == 0) {
|
||||
return kBMPImageType;
|
||||
} else {
|
||||
return kInvalidImageType;
|
||||
}
|
||||
}
|
||||
|
||||
MMBitmapRef newMMBitmapFromFile(const char *path,
|
||||
MMImageType type,
|
||||
MMIOError *err)
|
||||
{
|
||||
switch (type) {
|
||||
case kBMPImageType:
|
||||
return newMMBitmapFromBMP(path, err);
|
||||
case kPNGImageType:
|
||||
return newMMBitmapFromPNG(path, err);
|
||||
default:
|
||||
if (err != NULL) *err = kMMIOUnsupportedTypeError;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int saveMMBitmapToFile(MMBitmapRef bitmap,
|
||||
const char *path,
|
||||
MMImageType type)
|
||||
{
|
||||
switch (type) {
|
||||
case kBMPImageType:
|
||||
return saveMMBitmapAsBMP(bitmap, path);
|
||||
case kPNGImageType:
|
||||
return saveMMBitmapAsPNG(bitmap, path);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
const char *MMIOErrorString(MMImageType type, MMIOError error)
|
||||
{
|
||||
switch (type) {
|
||||
case kBMPImageType:
|
||||
return MMBMPReadErrorString(error);
|
||||
case kPNGImageType:
|
||||
return MMPNGReadErrorString(error);
|
||||
default:
|
||||
return "Unsupported image type";
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
#include "os.h"
|
||||
#include "inline_keywords.h"
|
||||
|
||||
// todo: removed
|
||||
#if !defined(IS_WINDOWS)
|
||||
/* Make sure nanosleep gets defined even when using C89. */
|
||||
#if !defined(__USE_POSIX199309) || !__USE_POSIX199309
|
||||
@ -14,22 +15,12 @@
|
||||
#include <time.h> /* For nanosleep() */
|
||||
#endif
|
||||
|
||||
/*
|
||||
* A more widely supported alternative to usleep(), based on Sleep() in Windows
|
||||
* and nanosleep() everywhere else.
|
||||
*
|
||||
* Pauses execution for the given amount of milliseconds.
|
||||
*/
|
||||
H_INLINE void microsleep(double milliseconds)
|
||||
{
|
||||
/* A more widely supported alternative to usleep(), based on Sleep() in Windows and nanosleep() */
|
||||
H_INLINE void microsleep(double milliseconds) {
|
||||
#if defined(IS_WINDOWS)
|
||||
Sleep((DWORD)milliseconds); /* (Unfortunately truncated to a 32-bit integer.) */
|
||||
#else
|
||||
/* Technically, nanosleep() is not an ANSI function, but it is the most
|
||||
* supported precise sleeping function I can find.
|
||||
*
|
||||
* If it is really necessary, it may be possible to emulate this with some
|
||||
* hack using select() in the future if we really have to. */
|
||||
/* Technically, nanosleep() is not an ANSI function */
|
||||
struct timespec sleepytime;
|
||||
sleepytime.tv_sec = milliseconds / 1000;
|
||||
sleepytime.tv_nsec = (milliseconds - (sleepytime.tv_sec * 1000)) * 1000000;
|
||||
|
@ -1,27 +0,0 @@
|
||||
#pragma once
|
||||
#if !defined(MS_STDBOOL_H) && \
|
||||
(!defined(__bool_true_false_are_defined) || __bool_true_false_are_defined)
|
||||
#define MS_STDBOOL_H
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#error "Use this header only with Microsoft Visual C++ compilers!"
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
#define __bool_true_false_are_defined 1
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#if defined(true) || defined(false) || defined(bool)
|
||||
#error "Boolean type already defined"
|
||||
#endif
|
||||
|
||||
enum {
|
||||
false = 0,
|
||||
true = 1
|
||||
};
|
||||
|
||||
typedef unsigned char bool;
|
||||
|
||||
#endif /* !__cplusplus */
|
||||
|
||||
#endif /* MS_STDBOOL_H */
|
224
base/ms_stdint.h
224
base/ms_stdint.h
@ -1,224 +0,0 @@
|
||||
/* ISO C9x compliant stdint.h for Microsoft Visual Studio
|
||||
* Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
|
||||
*
|
||||
* Copyright (c) 2006-2008 Alexander Chemeris
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of the author may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#error "Use this header only with Microsoft Visual C++ compilers!"
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
#ifndef MSC_STDINT_H
|
||||
#define MSC_STDINT_H
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
/* For Visual Studio 6 in C++ mode and for many Visual Studio versions when
|
||||
* compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
|
||||
* or compiler give many errors like this: */
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
#include <wchar.h>
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* Define _W64 macros to mark types changing their size, like intptr_t. */
|
||||
#ifndef _W64
|
||||
#if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
|
||||
#define _W64 __w64
|
||||
#else
|
||||
#define _W64
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/* 7.18.1 Integer types */
|
||||
|
||||
/* 7.18.1.1 Exact-width integer types */
|
||||
|
||||
/* Visual Studio 6 and Embedded Visual C++ 4 doesn't
|
||||
* realize that, e.g. char has the same size as __int8
|
||||
* so we give up on __intX for them. */
|
||||
#if _MSC_VER < 1300
|
||||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
||||
typedef signed int int32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
#else
|
||||
typedef signed __int8 int8_t;
|
||||
typedef signed __int16 int16_t;
|
||||
typedef signed __int32 int32_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
#endif
|
||||
typedef signed __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
/* 7.18.1.2 Minimum-width integer types */
|
||||
typedef int8_t int_least8_t;
|
||||
typedef int16_t int_least16_t;
|
||||
typedef int32_t int_least32_t;
|
||||
typedef int64_t int_least64_t;
|
||||
typedef uint8_t uint_least8_t;
|
||||
typedef uint16_t uint_least16_t;
|
||||
typedef uint32_t uint_least32_t;
|
||||
typedef uint64_t uint_least64_t;
|
||||
|
||||
/* 7.18.1.3 Fastest minimum-width integer types */
|
||||
typedef int8_t int_fast8_t;
|
||||
typedef int16_t int_fast16_t;
|
||||
typedef int32_t int_fast32_t;
|
||||
typedef int64_t int_fast64_t;
|
||||
typedef uint8_t uint_fast8_t;
|
||||
typedef uint16_t uint_fast16_t;
|
||||
typedef uint32_t uint_fast32_t;
|
||||
typedef uint64_t uint_fast64_t;
|
||||
|
||||
/* 7.18.1.4 Integer types capable of holding object pointers */
|
||||
#if defined(_WIN64)
|
||||
typedef signed __int64 intptr_t;
|
||||
typedef unsigned __int64 uintptr_t;
|
||||
#else
|
||||
typedef _W64 signed int intptr_t;
|
||||
typedef _W64 unsigned int uintptr_t;
|
||||
#endif /* _WIN64 ] */
|
||||
|
||||
/* 7.18.1.5 Greatest-width integer types */
|
||||
typedef int64_t intmax_t;
|
||||
typedef uint64_t uintmax_t;
|
||||
|
||||
/* 7.18.2 Limits of specified-width integer types */
|
||||
|
||||
/* See footnote 220 at page 257 and footnote 221 at page 259 */
|
||||
#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS)
|
||||
|
||||
/* 7.18.2.1 Limits of exact-width integer types */
|
||||
#define INT8_MIN ((int8_t)_I8_MIN)
|
||||
#define INT8_MAX _I8_MAX
|
||||
#define INT16_MIN ((int16_t)_I16_MIN)
|
||||
#define INT16_MAX _I16_MAX
|
||||
#define INT32_MIN ((int32_t)_I32_MIN)
|
||||
#define INT32_MAX _I32_MAX
|
||||
#define INT64_MIN ((int64_t)_I64_MIN)
|
||||
#define INT64_MAX _I64_MAX
|
||||
#define UINT8_MAX _UI8_MAX
|
||||
#define UINT16_MAX _UI16_MAX
|
||||
#define UINT32_MAX _UI32_MAX
|
||||
#define UINT64_MAX _UI64_MAX
|
||||
|
||||
/* 7.18.2.2 Limits of minimum-width integer types */
|
||||
#define INT_LEAST8_MIN INT8_MIN
|
||||
#define INT_LEAST8_MAX INT8_MAX
|
||||
#define INT_LEAST16_MIN INT16_MIN
|
||||
#define INT_LEAST16_MAX INT16_MAX
|
||||
#define INT_LEAST32_MIN INT32_MIN
|
||||
#define INT_LEAST32_MAX INT32_MAX
|
||||
#define INT_LEAST64_MIN INT64_MIN
|
||||
#define INT_LEAST64_MAX INT64_MAX
|
||||
#define UINT_LEAST8_MAX UINT8_MAX
|
||||
#define UINT_LEAST16_MAX UINT16_MAX
|
||||
#define UINT_LEAST32_MAX UINT32_MAX
|
||||
#define UINT_LEAST64_MAX UINT64_MAX
|
||||
|
||||
/* 7.18.2.4 Limits of integer types capable of holding object pointers */
|
||||
#if defined(_WIN64)
|
||||
#define INTPTR_MIN INT64_MIN
|
||||
#define INTPTR_MAX INT64_MAX
|
||||
#define UINTPTR_MAX UINT64_MAX
|
||||
#else
|
||||
#define INTPTR_MIN INT32_MIN
|
||||
#define INTPTR_MAX INT32_MAX
|
||||
#define UINTPTR_MAX UINT32_MAX
|
||||
#endif
|
||||
|
||||
/* 7.18.3 Limits of other integer types */
|
||||
|
||||
#if defined(_WIN64)
|
||||
#define PTRDIFF_MIN _I64_MIN
|
||||
#define PTRDIFF_MAX _I64_MAX
|
||||
#else
|
||||
#define PTRDIFF_MIN _I32_MIN
|
||||
#define PTRDIFF_MAX _I32_MAX
|
||||
#endif /* _WIN64 */
|
||||
|
||||
#define SIG_ATOMIC_MIN INT_MIN
|
||||
#define SIG_ATOMIC_MAX INT_MAX
|
||||
|
||||
#ifndef SIZE_MAX
|
||||
#if defined(_WIN64)
|
||||
#define SIZE_MAX _UI64_MAX
|
||||
#else
|
||||
#define SIZE_MAX _UI32_MAX
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h> */
|
||||
#ifndef WCHAR_MIN
|
||||
#define WCHAR_MIN 0
|
||||
#endif /* WCHAR_MIN */
|
||||
|
||||
#ifndef WCHAR_MAX
|
||||
#define WCHAR_MAX _UI16_MAX
|
||||
#endif /* WCHAR_MAX */
|
||||
|
||||
#define WINT_MIN 0
|
||||
#define WINT_MAX _UI16_MAX
|
||||
#endif /* __STDC_LIMIT_MACROS */
|
||||
|
||||
|
||||
/* 7.18.4 Limits of other integer types */
|
||||
|
||||
#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) /* See footnote 224 at page 260 */
|
||||
|
||||
/* 7.18.4.1 Macros for minimum-width integer constants */
|
||||
|
||||
#define INT8_C(val) val##i8
|
||||
#define INT16_C(val) val##i16
|
||||
#define INT32_C(val) val##i32
|
||||
#define INT64_C(val) val##i64
|
||||
|
||||
#define UINT8_C(val) val##ui8
|
||||
#define UINT16_C(val) val##ui16
|
||||
#define UINT32_C(val) val##ui32
|
||||
#define UINT64_C(val) val##ui64
|
||||
|
||||
/* 7.18.4.2 Macros for greatest-width integer constants */
|
||||
#define INTMAX_C INT64_C
|
||||
#define UINTMAX_C UINT64_C
|
||||
|
||||
#endif /* __STDC_CONSTANT_MACROS */
|
||||
|
||||
#endif /* MSC_STDINT_H */
|
26
base/os.h
26
base/os.h
@ -2,19 +2,12 @@
|
||||
#ifndef OS_H
|
||||
#define OS_H
|
||||
|
||||
/* Python versions under 2.5 don't support this macro, but it's not
|
||||
* terribly difficult to replicate: */
|
||||
#ifndef PyModule_AddIntMacro
|
||||
#define PyModule_AddIntMacro(module, macro) \
|
||||
PyModule_AddIntConstant(module, #macro, macro)
|
||||
#endif /* PyModule_AddIntMacro */
|
||||
|
||||
#if !defined(IS_MACOSX) && defined(__APPLE__) && defined(__MACH__)
|
||||
#define IS_MACOSX
|
||||
#endif /* IS_MACOSX */
|
||||
|
||||
#if !defined(IS_WINDOWS) && (defined(WIN32) || defined(_WIN32) || \
|
||||
defined(__WIN32__) || defined(__WINDOWS__))
|
||||
defined(__WIN32__) || defined(__WINDOWS__) || defined(__CYGWIN__))
|
||||
#define IS_WINDOWS
|
||||
#endif /* IS_WINDOWS */
|
||||
|
||||
@ -30,8 +23,7 @@
|
||||
#error "Sorry, this platform isn't supported yet!"
|
||||
#endif
|
||||
|
||||
/* Interval to align by for large buffers (e.g. bitmaps). */
|
||||
/* Must be a power of 2. */
|
||||
/* Interval to align by for large buffers (e.g. bitmaps). Must be a power of 2. */
|
||||
#ifndef BYTE_ALIGN
|
||||
#define BYTE_ALIGN 4 /* Bytes to align pixel buffers to. */
|
||||
/* #include <stddef.h> */
|
||||
@ -46,4 +38,18 @@
|
||||
#define ADD_PADDING(width) (BYTE_ALIGN + (((width) - 1) & ~(BYTE_ALIGN - 1)))
|
||||
#endif
|
||||
|
||||
#if defined(IS_WINDOWS)
|
||||
#if defined (_WIN64)
|
||||
#define RobotGo_64
|
||||
#else
|
||||
#define RobotGo_32
|
||||
#endif
|
||||
#else
|
||||
#if defined (__x86_64__)
|
||||
#define RobotGo_64
|
||||
#else
|
||||
#define RobotGo_32
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif /* OS_H */
|
||||
|
@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef PASTEBOARD_H
|
||||
#define PASTEBOARD_H
|
||||
|
||||
#include "MMBitmap.h"
|
||||
#include "io.h"
|
||||
|
||||
enum _MMBitmapPasteError {
|
||||
kMMPasteNoError = 0,
|
||||
kMMPasteGenericError,
|
||||
kMMPasteOpenError,
|
||||
kMMPasteClearError,
|
||||
kMMPasteDataError,
|
||||
kMMPastePasteError,
|
||||
kMMPasteUnsupportedError
|
||||
};
|
||||
|
||||
typedef MMIOError MMPasteError;
|
||||
|
||||
/* Copies |bitmap| to the pasteboard as a PNG.
|
||||
* Returns 0 on success, non-zero on error. */
|
||||
MMPasteError copyMMBitmapToPasteboard(MMBitmapRef bitmap);
|
||||
|
||||
/* Returns description of given MMPasteError.
|
||||
* Returned string is constant and hence should not be freed. */
|
||||
const char *MMPasteErrorString(MMPasteError error);
|
||||
|
||||
#endif /* PASTEBOARD_H */
|
@ -1,106 +0,0 @@
|
||||
#include "pasteboard.h"
|
||||
#include "os.h"
|
||||
|
||||
#if defined(IS_MACOSX)
|
||||
#include "png_io.h"
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#elif defined(IS_WINDOWS)
|
||||
#include "bmp_io.h"
|
||||
#endif
|
||||
|
||||
MMPasteError copyMMBitmapToPasteboard(MMBitmapRef bitmap)
|
||||
{
|
||||
#if defined(IS_MACOSX)
|
||||
PasteboardRef clipboard;
|
||||
|
||||
size_t len;
|
||||
uint8_t *pngbuf;
|
||||
CFDataRef data;
|
||||
OSStatus err;
|
||||
|
||||
if (PasteboardCreate(kPasteboardClipboard, &clipboard) != noErr) {
|
||||
return kMMPasteOpenError;
|
||||
}
|
||||
|
||||
if (PasteboardClear(clipboard) != noErr) {
|
||||
CFRelease(clipboard);
|
||||
return kMMPasteClearError;
|
||||
}
|
||||
|
||||
pngbuf = createPNGData(bitmap, &len);
|
||||
if (pngbuf == NULL) {
|
||||
CFRelease(clipboard);
|
||||
return kMMPasteDataError;
|
||||
}
|
||||
|
||||
data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, pngbuf, len,
|
||||
kCFAllocatorNull);
|
||||
if (data == NULL) {
|
||||
CFRelease(clipboard);
|
||||
free(pngbuf);
|
||||
return kMMPasteDataError;
|
||||
}
|
||||
|
||||
err = PasteboardPutItemFlavor(clipboard, bitmap, kUTTypePNG, data, 0);
|
||||
CFRelease(data);
|
||||
CFRelease(clipboard);
|
||||
free(pngbuf);
|
||||
return (err == noErr) ? kMMPasteNoError : kMMPastePasteError;
|
||||
#elif defined(IS_WINDOWS)
|
||||
MMPasteError ret = kMMPasteNoError;
|
||||
uint8_t *bmpData;
|
||||
size_t len;
|
||||
HGLOBAL handle;
|
||||
|
||||
if (!OpenClipboard(NULL)) return kMMPasteOpenError;
|
||||
if (!EmptyClipboard()) return kMMPasteClearError;
|
||||
|
||||
bmpData = createBitmapData(bitmap, &len);
|
||||
if (bmpData == NULL) return kMMPasteDataError;
|
||||
|
||||
/* CF_DIB does not include the BITMAPFILEHEADER struct (and displays a
|
||||
* cryptic error if it is included). */
|
||||
len -= sizeof(BITMAPFILEHEADER);
|
||||
|
||||
/* SetClipboardData() needs a "handle", not just a buffer, so we have to
|
||||
* allocate one with GlobalAlloc(). */
|
||||
if ((handle = GlobalAlloc(GMEM_MOVEABLE, len)) == NULL) {
|
||||
CloseClipboard();
|
||||
free(bmpData);
|
||||
return kMMPasteDataError;
|
||||
}
|
||||
|
||||
memcpy(GlobalLock(handle), bmpData + sizeof(BITMAPFILEHEADER), len);
|
||||
GlobalUnlock(handle);
|
||||
free(bmpData);
|
||||
|
||||
if (SetClipboardData(CF_DIB, handle) == NULL) {
|
||||
ret = kMMPastePasteError;
|
||||
}
|
||||
|
||||
CloseClipboard();
|
||||
GlobalFree(handle);
|
||||
return ret;
|
||||
#elif defined(USE_X11)
|
||||
/* TODO (X11's clipboard is _weird_.) */
|
||||
return kMMPasteUnsupportedError;
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *MMPasteErrorString(MMPasteError err)
|
||||
{
|
||||
switch (err) {
|
||||
case kMMPasteOpenError:
|
||||
return "Could not open pasteboard";
|
||||
case kMMPasteClearError:
|
||||
return "Could not clear pasteboard";
|
||||
case kMMPasteDataError:
|
||||
return "Could not create image data from bitmap";
|
||||
case kMMPastePasteError:
|
||||
return "Could not paste data";
|
||||
case kMMPasteUnsupportedError:
|
||||
return "Unsupported platform";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef PNG_IO_H
|
||||
#define PNG_IO_H
|
||||
|
||||
// #include "MMBitmap_c.h"
|
||||
// #include "io_c.h"
|
||||
|
||||
enum _PNGReadError {
|
||||
kPNGGenericError = 0,
|
||||
kPNGReadError,
|
||||
kPNGAccessError,
|
||||
kPNGInvalidHeaderError
|
||||
};
|
||||
|
||||
typedef MMIOError MMPNGReadError;
|
||||
|
||||
/* Returns description of given MMPNGReadError.
|
||||
* Returned string is constant and hence should not be freed. */
|
||||
const char *MMPNGReadErrorString(MMIOError error);
|
||||
|
||||
/* Attempts to read PNG file at path; returns new MMBitmap on success, or
|
||||
* NULL on error. If |error| is non-NULL, it will be set to the error code
|
||||
* on return.
|
||||
* Responsibility for destroy()'ing returned MMBitmap is left up to caller. */
|
||||
MMBitmapRef newMMBitmapFromPNG(const char *path, MMPNGReadError *error);
|
||||
|
||||
/* Attempts to write PNG at path; returns 0 on success, -1 on error. */
|
||||
int saveMMBitmapAsPNG(MMBitmapRef bitmap, const char *path);
|
||||
|
||||
/* Returns a buffer containing the raw PNG file data, ready to be saved to a
|
||||
* file. |len| will be set to the number of bytes allocated in the returned
|
||||
* buffer (it cannot be NULL).
|
||||
*
|
||||
* Responsibility for free()'ing data is left up to the caller. */
|
||||
uint8_t *createPNGData(MMBitmapRef bitmap, size_t *len);
|
||||
|
||||
#endif /* PNG_IO_H */
|
339
base/png_io_c.h
339
base/png_io_c.h
@ -1,339 +0,0 @@
|
||||
#include "png_io.h"
|
||||
#include "os.h"
|
||||
// #include "libpng/png.c"
|
||||
#include <png.h>
|
||||
#include <stdio.h> /* fopen() */
|
||||
#include <stdlib.h> /* malloc/realloc */
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include "ms_stdint.h"
|
||||
#include "ms_stdbool.h"
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
const char *MMPNGReadErrorString(MMIOError error)
|
||||
{
|
||||
switch (error) {
|
||||
case kPNGAccessError:
|
||||
return "Could not open file";
|
||||
case kPNGReadError:
|
||||
return "Could not read file";
|
||||
case kPNGInvalidHeaderError:
|
||||
return "Not a PNG file";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
MMBitmapRef newMMBitmapFromPNG(const char *path, MMPNGReadError *err)
|
||||
{
|
||||
FILE *fp;
|
||||
uint8_t header[8];
|
||||
png_struct *png_ptr = NULL;
|
||||
png_info *info_ptr = NULL;
|
||||
png_byte bit_depth, color_type;
|
||||
uint8_t *row, *bitmapData;
|
||||
uint8_t bytesPerPixel;
|
||||
png_uint_32 width, height, y;
|
||||
uint32_t bytewidth;
|
||||
|
||||
if ((fp = fopen(path, "rb")) == NULL) {
|
||||
if (err != NULL) *err = kPNGAccessError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialize error code to generic value. */
|
||||
if (err != NULL) *err = kPNGGenericError;
|
||||
|
||||
/* Validate the PNG. */
|
||||
if (fread(header, 1, sizeof header, fp) == 0) {
|
||||
if (err != NULL) *err = kPNGReadError;
|
||||
goto bail;
|
||||
} else if (png_sig_cmp(header, 0, sizeof(header)) != 0) {
|
||||
if (err != NULL) *err = kPNGInvalidHeaderError;
|
||||
goto bail;
|
||||
}
|
||||
|
||||
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
if (png_ptr == NULL) goto bail;
|
||||
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
if (info_ptr == NULL) goto bail;
|
||||
|
||||
/* Set up error handling. */
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
goto bail;
|
||||
}
|
||||
|
||||
png_init_io(png_ptr, fp);
|
||||
|
||||
/* Skip past the header. */
|
||||
png_set_sig_bytes(png_ptr, sizeof header);
|
||||
|
||||
png_read_info(png_ptr, info_ptr);
|
||||
|
||||
/* Convert different image types to common type to be read. */
|
||||
bit_depth = png_get_bit_depth(png_ptr, info_ptr);
|
||||
color_type = png_get_color_type(png_ptr, info_ptr);
|
||||
|
||||
/* Convert color palettes to RGB. */
|
||||
if (color_type == PNG_COLOR_TYPE_PALETTE) {
|
||||
png_set_palette_to_rgb(png_ptr);
|
||||
}
|
||||
|
||||
/* Convert PNG to bit depth of 8. */
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
|
||||
png_set_expand_gray_1_2_4_to_8(png_ptr);
|
||||
} else if (bit_depth == 16) {
|
||||
png_set_strip_16(png_ptr);
|
||||
}
|
||||
|
||||
/* Convert transparency chunk to alpha channel. */
|
||||
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
|
||||
png_set_tRNS_to_alpha(png_ptr);
|
||||
}
|
||||
|
||||
/* Convert gray images to RGB. */
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY ||
|
||||
color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
|
||||
png_set_gray_to_rgb(png_ptr);
|
||||
}
|
||||
|
||||
/* Ignore alpha for now. */
|
||||
if (color_type & PNG_COLOR_MASK_ALPHA) {
|
||||
png_set_strip_alpha(png_ptr);
|
||||
}
|
||||
|
||||
/* Get image attributes. */
|
||||
width = png_get_image_width(png_ptr, info_ptr);
|
||||
height = png_get_image_height(png_ptr, info_ptr);
|
||||
bytesPerPixel = 3; /* All images decompress to this size. */
|
||||
bytewidth = ADD_PADDING(width * bytesPerPixel); /* Align width. */
|
||||
|
||||
/* Decompress the PNG row by row. */
|
||||
bitmapData = calloc(1, bytewidth * height);
|
||||
row = png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));
|
||||
if (bitmapData == NULL || row == NULL) goto bail;
|
||||
for (y = 0; y < height; ++y) {
|
||||
png_uint_32 x;
|
||||
const uint32_t rowOffset = y * bytewidth;
|
||||
uint8_t *rowptr = row;
|
||||
png_read_row(png_ptr, (png_byte *)row, NULL);
|
||||
|
||||
for (x = 0; x < width; ++x) {
|
||||
const uint32_t colOffset = x * bytesPerPixel;
|
||||
MMRGBColor *color = (MMRGBColor *)(bitmapData + rowOffset + colOffset);
|
||||
color->red = *rowptr++;
|
||||
color->green = *rowptr++;
|
||||
color->blue = *rowptr++;
|
||||
}
|
||||
}
|
||||
free(row);
|
||||
|
||||
/* Finish reading. */
|
||||
png_read_end(png_ptr, NULL);
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
fclose(fp);
|
||||
|
||||
return createMMBitmap(bitmapData, width, height,
|
||||
bytewidth, bytesPerPixel * 8, bytesPerPixel);
|
||||
|
||||
bail:
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct _PNGWriteInfo {
|
||||
png_struct *png_ptr;
|
||||
png_info *info_ptr;
|
||||
png_byte **row_pointers;
|
||||
size_t row_count;
|
||||
bool free_row_pointers;
|
||||
};
|
||||
|
||||
typedef struct _PNGWriteInfo PNGWriteInfo;
|
||||
typedef PNGWriteInfo *PNGWriteInfoRef;
|
||||
|
||||
/* Returns pointer to PNGWriteInfo struct containing data ready to be used with
|
||||
* functions such as png_write_png().
|
||||
*
|
||||
* It is the caller's responsibility to destroy() the returned structure with
|
||||
* destroyPNGWriteInfo(). */
|
||||
static PNGWriteInfoRef createPNGWriteInfo(MMBitmapRef bitmap)
|
||||
{
|
||||
PNGWriteInfoRef info = malloc(sizeof(PNGWriteInfo));
|
||||
png_uint_32 y;
|
||||
|
||||
if (info == NULL) return NULL;
|
||||
info->png_ptr = NULL;
|
||||
info->info_ptr = NULL;
|
||||
info->row_pointers = NULL;
|
||||
|
||||
assert(bitmap != NULL);
|
||||
|
||||
/* Initialize the write struct. */
|
||||
info->png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
||||
NULL, NULL, NULL);
|
||||
if (info->png_ptr == NULL) goto bail;
|
||||
|
||||
/* Set up error handling. */
|
||||
if (setjmp(png_jmpbuf(info->png_ptr))) {
|
||||
png_destroy_write_struct(&(info->png_ptr), &(info->info_ptr));
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Initialize the info struct. */
|
||||
info->info_ptr = png_create_info_struct(info->png_ptr);
|
||||
if (info->info_ptr == NULL) {
|
||||
png_destroy_write_struct(&(info->png_ptr), NULL);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Set image attributes. */
|
||||
png_set_IHDR(info->png_ptr,
|
||||
info->info_ptr,
|
||||
(png_uint_32)bitmap->width,
|
||||
(png_uint_32)bitmap->height,
|
||||
8,
|
||||
PNG_COLOR_TYPE_RGB,
|
||||
PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_DEFAULT,
|
||||
PNG_FILTER_TYPE_DEFAULT);
|
||||
|
||||
info->row_count = bitmap->height;
|
||||
info->row_pointers = png_malloc(info->png_ptr,
|
||||
sizeof(png_byte *) * info->row_count);
|
||||
|
||||
if (bitmap->bytesPerPixel == 3) {
|
||||
/* No alpha channel; image data can be copied directly. */
|
||||
for (y = 0; y < info->row_count; ++y) {
|
||||
info->row_pointers[y] = bitmap->imageBuffer + (bitmap->bytewidth * y);
|
||||
}
|
||||
info->free_row_pointers = false;
|
||||
|
||||
/* Convert BGR to RGB if necessary. */
|
||||
if (MMRGB_IS_BGR) {
|
||||
png_set_bgr(info->png_ptr);
|
||||
}
|
||||
} else {
|
||||
/* Ignore alpha channel; copy image data row by row. */
|
||||
const size_t bytesPerPixel = 3;
|
||||
const size_t bytewidth = ADD_PADDING(bitmap->width * bytesPerPixel);
|
||||
|
||||
for (y = 0; y < info->row_count; ++y) {
|
||||
png_uint_32 x;
|
||||
png_byte *row_ptr = png_malloc(info->png_ptr, bytewidth);
|
||||
info->row_pointers[y] = row_ptr;
|
||||
for (x = 0; x < bitmap->width; ++x) {
|
||||
MMRGBColor *color = MMRGBColorRefAtPoint(bitmap, x, y);
|
||||
row_ptr[0] = color->red;
|
||||
row_ptr[1] = color->green;
|
||||
row_ptr[2] = color->blue;
|
||||
|
||||
row_ptr += bytesPerPixel;
|
||||
}
|
||||
}
|
||||
info->free_row_pointers = true;
|
||||
}
|
||||
|
||||
png_set_rows(info->png_ptr, info->info_ptr, info->row_pointers);
|
||||
return info;
|
||||
|
||||
bail:
|
||||
if (info != NULL) free(info);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Free memory in use by |info|. */
|
||||
static void destroyPNGWriteInfo(PNGWriteInfoRef info)
|
||||
{
|
||||
assert(info != NULL);
|
||||
if (info->row_pointers != NULL) {
|
||||
if (info->free_row_pointers) {
|
||||
size_t y;
|
||||
for (y = 0; y < info->row_count; ++y) {
|
||||
free(info->row_pointers[y]);
|
||||
}
|
||||
}
|
||||
png_free(info->png_ptr, info->row_pointers);
|
||||
}
|
||||
|
||||
png_destroy_write_struct(&(info->png_ptr), &(info->info_ptr));
|
||||
free(info);
|
||||
}
|
||||
|
||||
int saveMMBitmapAsPNG(MMBitmapRef bitmap, const char *path)
|
||||
{
|
||||
FILE *fp = fopen(path, "wb");
|
||||
PNGWriteInfoRef info;
|
||||
if (fp == NULL) return -1;
|
||||
|
||||
if ((info = createPNGWriteInfo(bitmap)) == NULL) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
png_init_io(info->png_ptr, fp);
|
||||
png_write_png(info->png_ptr, info->info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
|
||||
fclose(fp);
|
||||
|
||||
destroyPNGWriteInfo(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Structure to store PNG image bytes. */
|
||||
struct io_data
|
||||
{
|
||||
uint8_t *buffer; /* Pointer to raw file data. */
|
||||
size_t size; /* Number of bytes actually written to buffer. */
|
||||
size_t allocedSize; /* Number of bytes allocated for buffer. */
|
||||
};
|
||||
|
||||
/* Called each time libpng attempts to write data in createPNGData(). */
|
||||
void png_append_data(png_struct *png_ptr,
|
||||
png_byte *new_data,
|
||||
png_size_t length)
|
||||
{
|
||||
struct io_data *data = png_get_io_ptr(png_ptr);
|
||||
data->size += length;
|
||||
|
||||
/* Allocate or grow buffer. */
|
||||
if (data->buffer == NULL) {
|
||||
data->allocedSize = data->size;
|
||||
data->buffer = png_malloc(png_ptr, data->allocedSize);
|
||||
assert(data->buffer != NULL);
|
||||
} else if (data->allocedSize < data->size) {
|
||||
do {
|
||||
/* Double size each time to avoid calls to realloc. */
|
||||
data->allocedSize <<= 1;
|
||||
} while (data->allocedSize < data->size);
|
||||
|
||||
data->buffer = realloc(data->buffer, data->allocedSize);
|
||||
}
|
||||
|
||||
/* Copy new bytes to end of buffer. */
|
||||
memcpy(data->buffer + data->size - length, new_data, length);
|
||||
}
|
||||
|
||||
uint8_t *createPNGData(MMBitmapRef bitmap, size_t *len)
|
||||
{
|
||||
PNGWriteInfoRef info = NULL;
|
||||
struct io_data data = {NULL, 0, 0};
|
||||
|
||||
assert(bitmap != NULL);
|
||||
assert(len != NULL);
|
||||
|
||||
if ((info = createPNGWriteInfo(bitmap)) == NULL) return NULL;
|
||||
|
||||
png_set_write_fn(info->png_ptr, &data, &png_append_data, NULL);
|
||||
png_write_png(info->png_ptr, info->info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
|
||||
|
||||
destroyPNGWriteInfo(info);
|
||||
|
||||
*len = data.size;
|
||||
return data.buffer;
|
||||
}
|
33
base/pubs.h
Normal file
33
base/pubs.h
Normal file
@ -0,0 +1,33 @@
|
||||
#if defined(IS_WINDOWS)
|
||||
BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
|
||||
uint32_t *count = (uint32_t*)dwData;
|
||||
(*count)++;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
typedef struct{
|
||||
HWND hWnd;
|
||||
DWORD dwPid;
|
||||
}WNDINFO;
|
||||
|
||||
BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam){
|
||||
WNDINFO* pInfo = (WNDINFO*)lParam;
|
||||
DWORD dwProcessId = 0;
|
||||
GetWindowThreadProcessId(hWnd, &dwProcessId);
|
||||
|
||||
if (dwProcessId == pInfo->dwPid) {
|
||||
pInfo->hWnd = hWnd;
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
HWND GetHwndByPid(DWORD dwProcessId) {
|
||||
WNDINFO info = {0};
|
||||
info.hWnd = NULL;
|
||||
info.dwPid = dwProcessId;
|
||||
EnumWindows(EnumWindowsProc, (LPARAM)&info);
|
||||
|
||||
return info.hWnd;
|
||||
}
|
||||
#endif
|
52
base/rgb.h
52
base/rgb.h
@ -5,21 +5,7 @@
|
||||
#include <stdlib.h> /* For abs() */
|
||||
#include <math.h>
|
||||
#include "inline_keywords.h" /* For H_INLINE */
|
||||
// #include <stdint.h>
|
||||
#if defined(_MSC_VER)
|
||||
#include "ms_stdint.h"
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
|
||||
/* RGB colors in MMBitmaps are stored as BGR for convenience in converting
|
||||
* to/from certain formats (mainly OpenGL).
|
||||
*
|
||||
* It is best not to rely on the order (simply use rgb.{blue,green,red} to
|
||||
* access values), but some situations (e.g., glReadPixels) require one to
|
||||
* do so. In that case, check to make sure to use MMRGB_IS_BGR for future
|
||||
* compatibility. */
|
||||
#include <stdint.h>
|
||||
|
||||
/* #define MMRGB_IS_BGR (offsetof(MMRGBColor, red) > offsetof(MMRGBColor, blue)) */
|
||||
#define MMRGB_IS_BGR 1
|
||||
@ -29,25 +15,19 @@ struct _MMRGBColor {
|
||||
uint8_t green;
|
||||
uint8_t red;
|
||||
};
|
||||
|
||||
typedef struct _MMRGBColor MMRGBColor;
|
||||
|
||||
/* MMRGBHex is a hexadecimal color value, akin to HTML's, in the form 0xRRGGBB
|
||||
* where RR is the red value expressed as hexadecimal, GG is the green value,
|
||||
* and BB is the blue value. */
|
||||
/* MMRGBHex is a hexadecimal color value*/
|
||||
typedef uint32_t MMRGBHex;
|
||||
|
||||
#define MMRGBHEX_MIN 0x000000
|
||||
#define MMRGBHEX_MAX 0xFFFFFF
|
||||
|
||||
/* Converts rgb color to hexadecimal value.
|
||||
* |red|, |green|, and |blue| should each be of the type |uint8_t|, where the
|
||||
* range is 0 - 255. */
|
||||
/* Converts rgb color to hexadecimal value. */
|
||||
#define RGB_TO_HEX(red, green, blue) (((red) << 16) | ((green) << 8) | (blue))
|
||||
|
||||
/* Convenience wrapper for MMRGBColors. */
|
||||
H_INLINE MMRGBHex hexFromMMRGB(MMRGBColor rgb)
|
||||
{
|
||||
H_INLINE MMRGBHex hexFromMMRGB(MMRGBColor rgb) {
|
||||
return RGB_TO_HEX(rgb.red, rgb.green, rgb.blue);
|
||||
}
|
||||
|
||||
@ -56,8 +36,7 @@ H_INLINE MMRGBHex hexFromMMRGB(MMRGBColor rgb)
|
||||
#define BLUE_FROM_HEX(hex) (hex & 0xFF)
|
||||
|
||||
/* Converts hexadecimal color to MMRGBColor. */
|
||||
H_INLINE MMRGBColor MMRGBFromHex(MMRGBHex hex)
|
||||
{
|
||||
H_INLINE MMRGBColor MMRGBFromHex(MMRGBHex hex) {
|
||||
MMRGBColor color;
|
||||
color.red = RED_FROM_HEX(hex);
|
||||
color.green = GREEN_FROM_HEX(hex);
|
||||
@ -70,12 +49,8 @@ H_INLINE MMRGBColor MMRGBFromHex(MMRGBHex hex)
|
||||
(c1).blue == (c2).blue && \
|
||||
(c1).green == (c2).green)
|
||||
|
||||
/* Returns whether two colors are similar within the given range, |tolerance|.
|
||||
* Tolerance can be in the range 0.0f - 1.0f, where 0 denotes the exact
|
||||
* color and 1 denotes any color. */
|
||||
H_INLINE int MMRGBColorSimilarToColor(MMRGBColor c1, MMRGBColor c2,
|
||||
float tolerance)
|
||||
{
|
||||
/* Returns whether two colors are similar within the given range, |tolerance|.*/
|
||||
H_INLINE int MMRGBColorSimilarToColor(MMRGBColor c1, MMRGBColor c2, float tolerance) {
|
||||
/* Speedy case */
|
||||
if (tolerance <= 0.0f) {
|
||||
return MMRGBColorEqualToColor(c1, c2);
|
||||
@ -83,27 +58,20 @@ H_INLINE int MMRGBColorSimilarToColor(MMRGBColor c1, MMRGBColor c2,
|
||||
uint8_t d1 = c1.red - c2.red;
|
||||
uint8_t d2 = c1.green - c2.green;
|
||||
uint8_t d3 = c1.blue - c2.blue;
|
||||
return sqrt((double)(d1 * d1) +
|
||||
(d2 * d2) +
|
||||
return sqrt((double)(d1 * d1) + (d2 * d2) +
|
||||
(d3 * d3)) <= (tolerance * 442.0f);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Identical to MMRGBColorSimilarToColor, only for hex values. */
|
||||
H_INLINE int MMRGBHexSimilarToColor(MMRGBHex h1, MMRGBHex h2, float tolerance)
|
||||
{
|
||||
H_INLINE int MMRGBHexSimilarToColor(MMRGBHex h1, MMRGBHex h2, float tolerance) {
|
||||
if (tolerance <= 0.0f) {
|
||||
return h1 == h2;
|
||||
} else {
|
||||
// uint8_t d1 = RED_FROM_HEX(h1) - RED_FROM_HEX(h2);
|
||||
// uint8_t d2 = GREEN_FROM_HEX(h1) - GREEN_FROM_HEX(h2);
|
||||
// uint8_t d3 = BLUE_FROM_HEX(h1) - BLUE_FROM_HEX(h2);
|
||||
int d1 = RED_FROM_HEX(h1) - RED_FROM_HEX(h2);
|
||||
int d2 = GREEN_FROM_HEX(h1) - GREEN_FROM_HEX(h2);
|
||||
int d3 = BLUE_FROM_HEX(h1) - BLUE_FROM_HEX(h2);
|
||||
return sqrt((double)(d1 * d1) +
|
||||
(d2 * d2) +
|
||||
return sqrt((double)(d1 * d1) + (d2 * d2) +
|
||||
(d3 * d3)) <= (tolerance * 442.0f);
|
||||
}
|
||||
}
|
||||
|
@ -1,46 +0,0 @@
|
||||
#ifndef _PORTABLE_SNPRINTF_H_
|
||||
#define _PORTABLE_SNPRINTF_H_
|
||||
|
||||
#define PORTABLE_SNPRINTF_VERSION_MAJOR 2
|
||||
#define PORTABLE_SNPRINTF_VERSION_MINOR 2
|
||||
|
||||
#include "os.h"
|
||||
#if defined(IS_MACOSX)
|
||||
#define HAVE_SNPRINTF
|
||||
#else
|
||||
#define HAVE_SNPRINTF
|
||||
#define PREFER_PORTABLE_SNPRINTF
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SNPRINTF
|
||||
#include <stdio.h>
|
||||
#else
|
||||
extern int snprintf(char *, size_t, const char *, /*args*/ ...);
|
||||
extern int vsnprintf(char *, size_t, const char *, va_list);
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_SNPRINTF) && defined(PREFER_PORTABLE_SNPRINTF)
|
||||
extern int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);
|
||||
extern int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap);
|
||||
#define snprintf portable_snprintf
|
||||
#define vsnprintf portable_vsnprintf
|
||||
#endif
|
||||
|
||||
extern int asprintf (char **ptr, const char *fmt, /*args*/ ...);
|
||||
extern int vasprintf (char **ptr, const char *fmt, va_list ap);
|
||||
extern int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...);
|
||||
extern int vasnprintf(char **ptr, size_t str_m, const char *fmt, va_list ap);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
1019
base/snprintf_c.h
1019
base/snprintf_c.h
File diff suppressed because it is too large
Load Diff
@ -1,50 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef STR_IO_H
|
||||
#define STR_IO_H
|
||||
|
||||
#include "MMBitmap.h"
|
||||
#include "io.h"
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
enum _MMBMPStringError {
|
||||
kMMBMPStringGenericError = 0,
|
||||
kMMBMPStringInvalidHeaderError,
|
||||
kMMBMPStringDecodeError,
|
||||
kMMBMPStringDecompressError,
|
||||
kMMBMPStringSizeError, /* Size does not match header. */
|
||||
MMMBMPStringEncodeError,
|
||||
kMMBMPStringCompressError
|
||||
};
|
||||
|
||||
typedef MMIOError MMBMPStringError;
|
||||
|
||||
/* Creates a 24-bit bitmap from a compressed, printable string.
|
||||
*
|
||||
* String should be in the format: "b[width],[height],[data]",
|
||||
* where [width] and [height] are the image width & height, and [data]
|
||||
* is the raw image data run through zlib_compress() and base64_encode().
|
||||
*
|
||||
* Returns NULL on error; follows the Create Rule (that is, the caller is
|
||||
* responsible for destroy'()ing object).
|
||||
* If |error| is non-NULL, it will be set to the error code on return.
|
||||
*/
|
||||
MMBitmapRef createMMBitmapFromString(const uint8_t *buffer, size_t buflen,
|
||||
MMBMPStringError *error);
|
||||
|
||||
/* Inverse of createMMBitmapFromString().
|
||||
*
|
||||
* Creates string in the format: "b[width],[height],[data]", where [width] and
|
||||
* [height] are the image width & height, and [data] is the raw image data run
|
||||
* through zlib_compress() and base64_encode().
|
||||
*
|
||||
* Returns NULL on error, or new string on success (to be free'()d by caller).
|
||||
* If |error| is non-NULL, it will be set to the error code on return.
|
||||
*/
|
||||
uint8_t *createStringFromMMBitmap(MMBitmapRef bitmap, MMBMPStringError *error);
|
||||
|
||||
/* Returns description of given error code.
|
||||
* Returned string is constant and hence should not be freed. */
|
||||
const char *MMBitmapStringErrorString(MMBMPStringError err);
|
||||
|
||||
#endif /* STR_IO_H */
|
209
base/str_io_c.h
209
base/str_io_c.h
@ -1,209 +0,0 @@
|
||||
#include "str_io.h"
|
||||
#include "zlib_util_c.h"
|
||||
#include "base64_c.h"
|
||||
#include "snprintf_c.h" /* snprintf() */
|
||||
#include <stdio.h> /* fputs() */
|
||||
#include <ctype.h> /* isdigit() */
|
||||
#include <stdlib.h> /* atoi() */
|
||||
#include <string.h> /* strlen() */
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include "ms_stdbool.h"
|
||||
#else
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#define STR_BITS_PER_PIXEL 24
|
||||
#define STR_BYTES_PER_PIXEL ((STR_BITS_PER_PIXEL) / 8)
|
||||
|
||||
#define MAX_DIMENSION_LEN 5 /* Maximum length for [width] or [height]
|
||||
* in string. */
|
||||
|
||||
const char *MMBitmapStringErrorString(MMBMPStringError err)
|
||||
{
|
||||
switch (err) {
|
||||
case kMMBMPStringInvalidHeaderError:
|
||||
return "Invalid header for string";
|
||||
case kMMBMPStringDecodeError:
|
||||
return "Error decoding string";
|
||||
case kMMBMPStringDecompressError:
|
||||
return "Error decompressing string";
|
||||
case kMMBMPStringSizeError:
|
||||
return "String not of expected size";
|
||||
case MMMBMPStringEncodeError:
|
||||
return "Error encoding string";
|
||||
case kMMBMPStringCompressError:
|
||||
return "Error compressing string";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Parses beginning of string in the form of "[width],[height],*".
|
||||
*
|
||||
* If successful, |width| and |height| are set to the appropropriate values,
|
||||
* |len| is set to the length of [width] + the length of [height] + 2,
|
||||
* and true is returned; otherwise, false is returned.
|
||||
*/
|
||||
static bool getSizeFromString(const uint8_t *buf, size_t buflen,
|
||||
size_t *width, size_t *height,
|
||||
size_t *len);
|
||||
|
||||
MMBitmapRef createMMBitmapFromString(const uint8_t *buffer, size_t buflen,
|
||||
MMBMPStringError *err)
|
||||
{
|
||||
uint8_t *decoded, *decompressed;
|
||||
size_t width, height;
|
||||
size_t len, bytewidth;
|
||||
|
||||
if (*buffer++ != 'b' || !getSizeFromString(buffer, --buflen,
|
||||
&width, &height, &len)) {
|
||||
if (err != NULL) *err = kMMBMPStringInvalidHeaderError;
|
||||
return NULL;
|
||||
}
|
||||
buffer += len;
|
||||
buflen -= len;
|
||||
|
||||
decoded = base64decode(buffer, buflen, NULL);
|
||||
if (decoded == NULL) {
|
||||
if (err != NULL) *err = kMMBMPStringDecodeError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
decompressed = zlib_decompress(decoded, &len);
|
||||
free(decoded);
|
||||
|
||||
if (decompressed == NULL) {
|
||||
if (err != NULL) *err = kMMBMPStringDecompressError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bytewidth = width * STR_BYTES_PER_PIXEL; /* Note that bytewidth is NOT
|
||||
* aligned to a padding. */
|
||||
if (height * bytewidth != len) {
|
||||
if (err != NULL) *err = kMMBMPStringSizeError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return createMMBitmap(decompressed, width, height,
|
||||
bytewidth, STR_BITS_PER_PIXEL, STR_BYTES_PER_PIXEL);
|
||||
}
|
||||
|
||||
/* Returns bitmap data suitable for encoding to a string; that is, 24-bit BGR
|
||||
* bitmap with no padding and 3 bytes per pixel.
|
||||
*
|
||||
* Caller is responsible for free()'ing returned buffer. */
|
||||
static uint8_t *createRawBitmapData(MMBitmapRef bitmap);
|
||||
|
||||
uint8_t *createStringFromMMBitmap(MMBitmapRef bitmap, MMBMPStringError *err)
|
||||
{
|
||||
uint8_t *raw, *compressed;
|
||||
uint8_t *ret, *encoded;
|
||||
size_t len, retlen;
|
||||
|
||||
assert(bitmap != NULL);
|
||||
|
||||
raw = createRawBitmapData(bitmap);
|
||||
if (raw == NULL) {
|
||||
if (err != NULL) *err = kMMBMPStringGenericError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
compressed = zlib_compress(raw,
|
||||
bitmap->width * bitmap->height *
|
||||
STR_BYTES_PER_PIXEL,
|
||||
9, &len);
|
||||
free(raw);
|
||||
if (compressed == NULL) {
|
||||
if (err != NULL) *err = kMMBMPStringCompressError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
encoded = base64encode(compressed, len - 1, &retlen);
|
||||
free(compressed);
|
||||
if (encoded == NULL) {
|
||||
if (err != NULL) *err = MMMBMPStringEncodeError;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
retlen += 3 + (MAX_DIMENSION_LEN * 2);
|
||||
ret = calloc(sizeof(char), (retlen + 1));
|
||||
snprintf((char *)ret, retlen, "b%lu,%lu,%s", (unsigned long)bitmap->width,
|
||||
(unsigned long)bitmap->height,
|
||||
encoded);
|
||||
ret[retlen] = '\0';
|
||||
free(encoded);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t parseDimension(const uint8_t *buf, size_t buflen,
|
||||
size_t *numlen);
|
||||
|
||||
static bool getSizeFromString(const uint8_t *buf, size_t buflen,
|
||||
size_t *width, size_t *height,
|
||||
size_t *len)
|
||||
{
|
||||
size_t numlen;
|
||||
assert(buf != NULL);
|
||||
assert(width != NULL);
|
||||
assert(height != NULL);
|
||||
|
||||
if ((*width = parseDimension(buf, buflen, &numlen)) == 0) {
|
||||
return false;
|
||||
}
|
||||
*len = numlen + 1;
|
||||
|
||||
if ((*height = parseDimension(buf + *len, buflen, &numlen)) == 0) {
|
||||
return false;
|
||||
}
|
||||
*len += numlen + 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Parses one dimension from string as described in getSizeFromString().
|
||||
* Returns dimension on success, or 0 on error. */
|
||||
static uint32_t parseDimension(const uint8_t *buf, size_t buflen,
|
||||
size_t *numlen)
|
||||
{
|
||||
char num[MAX_DIMENSION_LEN + 1];
|
||||
size_t i;
|
||||
// ssize_t len;
|
||||
// size_t len;
|
||||
uint8_t * len;
|
||||
|
||||
assert(buf != NULL);
|
||||
assert(len != NULL);
|
||||
for (i = 0; i < buflen && buf[i] != ',' && buf[i] != '\0'; ++i) {
|
||||
if (!isdigit(buf[i]) || i > MAX_DIMENSION_LEN) return 0;
|
||||
num[i] = buf[i];
|
||||
}
|
||||
num[i] = '\0';
|
||||
*numlen = i;
|
||||
|
||||
return (uint32_t)atoi(num);
|
||||
}
|
||||
|
||||
static uint8_t *createRawBitmapData(MMBitmapRef bitmap)
|
||||
{
|
||||
uint8_t *raw = calloc(STR_BYTES_PER_PIXEL, bitmap->width * bitmap->height);
|
||||
size_t y;
|
||||
|
||||
for (y = 0; y < bitmap->height; ++y) {
|
||||
/* No padding is added to string bitmaps. */
|
||||
const size_t rowOffset = y * bitmap->width * STR_BYTES_PER_PIXEL;
|
||||
size_t x;
|
||||
for (x = 0; x < bitmap->width; ++x) {
|
||||
/* Copy in BGR format. */
|
||||
const size_t colOffset = x * STR_BYTES_PER_PIXEL;
|
||||
uint8_t *dest = raw + rowOffset + colOffset;
|
||||
MMRGBColor *srcColor = MMRGBColorRefAtPoint(bitmap, x, y);
|
||||
dest[0] = srcColor->blue;
|
||||
dest[1] = srcColor->green;
|
||||
dest[2] = srcColor->red;
|
||||
}
|
||||
}
|
||||
|
||||
return raw;
|
||||
}
|
71
base/types.h
71
base/types.h
@ -5,65 +5,64 @@
|
||||
#include "os.h"
|
||||
#include "inline_keywords.h" /* For H_INLINE */
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Some generic, cross-platform types. */
|
||||
#ifdef RobotGo_64
|
||||
typedef int64_t intptr;
|
||||
typedef uint64_t uintptr;
|
||||
#else
|
||||
typedef int32_t intptr;
|
||||
typedef uint32_t uintptr; // Unsigned pointer integer
|
||||
#endif
|
||||
|
||||
struct _MMPoint {
|
||||
size_t x;
|
||||
size_t y;
|
||||
struct _MMPointInt32 {
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
};
|
||||
typedef struct _MMPointInt32 MMPointInt32;
|
||||
|
||||
typedef struct _MMPoint MMPoint;
|
||||
|
||||
struct _MMSize {
|
||||
size_t width;
|
||||
size_t height;
|
||||
struct _MMSizeInt32 {
|
||||
int32_t w;
|
||||
int32_t h;
|
||||
};
|
||||
typedef struct _MMSizeInt32 MMSizeInt32;
|
||||
|
||||
typedef struct _MMSize MMSize;
|
||||
|
||||
struct _MMRect {
|
||||
MMPoint origin;
|
||||
MMSize size;
|
||||
struct _MMRectInt32 {
|
||||
MMPointInt32 origin;
|
||||
MMSizeInt32 size;
|
||||
};
|
||||
typedef struct _MMRectInt32 MMRectInt32;
|
||||
|
||||
typedef struct _MMRect MMRect;
|
||||
|
||||
H_INLINE MMPoint MMPointMake(size_t x, size_t y)
|
||||
{
|
||||
MMPoint point;
|
||||
H_INLINE MMPointInt32 MMPointInt32Make(int32_t x, int32_t y) {
|
||||
MMPointInt32 point;
|
||||
point.x = x;
|
||||
point.y = y;
|
||||
return point;
|
||||
}
|
||||
|
||||
H_INLINE MMSize MMSizeMake(size_t width, size_t height)
|
||||
{
|
||||
MMSize size;
|
||||
size.width = width;
|
||||
size.height = height;
|
||||
H_INLINE MMSizeInt32 MMSizeInt32Make(int32_t w, int32_t h) {
|
||||
MMSizeInt32 size;
|
||||
size.w = w;
|
||||
size.h = h;
|
||||
return size;
|
||||
}
|
||||
|
||||
H_INLINE MMRect MMRectMake(size_t x, size_t y, size_t width, size_t height)
|
||||
{
|
||||
MMRect rect;
|
||||
rect.origin = MMPointMake(x, y);
|
||||
rect.size = MMSizeMake(width, height);
|
||||
H_INLINE MMRectInt32 MMRectInt32Make(int32_t x, int32_t y, int32_t w, int32_t h) {
|
||||
MMRectInt32 rect;
|
||||
rect.origin = MMPointInt32Make(x, y);
|
||||
rect.size = MMSizeInt32Make(w, h);
|
||||
return rect;
|
||||
}
|
||||
|
||||
#define MMPointZero MMPointMake(0, 0)
|
||||
#define MMPointZero MMPointInt32Make(0, 0)
|
||||
|
||||
#if defined(IS_MACOSX)
|
||||
|
||||
#define CGPointFromMMPoint(p) CGPointMake((CGFloat)(p).x, (CGFloat)(p).y)
|
||||
#define MMPointFromCGPoint(p) MMPointMake((size_t)(p).x, (size_t)(p).y)
|
||||
|
||||
#define CGPointFromMMPointInt32(p) CGPointMake((CGFloat)(p).x, (CGFloat)(p).y)
|
||||
#define MMPointInt32FromCGPoint(p) MMPointInt32Make((int32_t)(p).x, (int32_t)(p).y)
|
||||
#elif defined(IS_WINDOWS)
|
||||
|
||||
#define MMPointFromPOINT(p) MMPointMake((size_t)p.x, (size_t)p.y)
|
||||
|
||||
#define MMPointInt32FromPOINT(p) MMPointInt32Make((int32_t)p.x, (int32_t)p.y)
|
||||
#endif
|
||||
|
||||
#endif /* TYPES_H */
|
||||
|
929
base/uthash.h
929
base/uthash.h
@ -1,929 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2009, Troy D. Hanson http://uthash.sourceforge.net
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef UTHASH_H
|
||||
#define UTHASH_H
|
||||
|
||||
#include <string.h> /* memcmp, strlen */
|
||||
#include <stddef.h> /* ptrdiff_t */
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#define UTHASH_VERSION 1.8
|
||||
|
||||
/* C++ requires extra stringent casting */
|
||||
#if defined __cplusplus
|
||||
#define TYPEOF(x) (typeof(x))
|
||||
#else
|
||||
#define TYPEOF(x)
|
||||
#endif
|
||||
|
||||
|
||||
#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */
|
||||
#define uthash_malloc(sz) malloc(sz) /* malloc fcn */
|
||||
#define uthash_free(ptr) free(ptr) /* free fcn */
|
||||
|
||||
#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */
|
||||
#define uthash_expand_fyi(tbl) /* can be defined to log expands */
|
||||
|
||||
/* initial number of buckets */
|
||||
#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */
|
||||
#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */
|
||||
#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */
|
||||
|
||||
/* calculate the element whose hash handle address is hhe */
|
||||
#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)hhp) - (tbl)->hho))
|
||||
|
||||
#define HASH_FIND(hh,head,keyptr,keylen,out) \
|
||||
do { \
|
||||
unsigned _hf_bkt,_hf_hashv; \
|
||||
out=TYPEOF(out)NULL; \
|
||||
if (head) { \
|
||||
HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \
|
||||
if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \
|
||||
HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \
|
||||
keyptr,keylen,out); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#if defined(HASH_BLOOM)
|
||||
#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM)
|
||||
#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0)
|
||||
#define HASH_BLOOM_MAKE(tbl) \
|
||||
do { \
|
||||
(tbl)->bloom_nbits = HASH_BLOOM; \
|
||||
(tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \
|
||||
if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \
|
||||
memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \
|
||||
(tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \
|
||||
} while (0);
|
||||
|
||||
#define HASH_BLOOM_FREE(tbl) \
|
||||
do { \
|
||||
uthash_free((tbl)->bloom_bv); \
|
||||
} while (0);
|
||||
|
||||
#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8)))
|
||||
#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8)))
|
||||
|
||||
#define HASH_BLOOM_ADD(tbl,hashv) \
|
||||
HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
|
||||
|
||||
#define HASH_BLOOM_TEST(tbl,hashv) \
|
||||
HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
|
||||
|
||||
#else
|
||||
#define HASH_BLOOM_MAKE(tbl)
|
||||
#define HASH_BLOOM_FREE(tbl)
|
||||
#define HASH_BLOOM_ADD(tbl,hashv)
|
||||
#define HASH_BLOOM_TEST(tbl,hashv) (1)
|
||||
#endif
|
||||
|
||||
#define HASH_MAKE_TABLE(hh,head) \
|
||||
do { \
|
||||
(head)->hh.tbl = (UT_hash_table*)uthash_malloc( \
|
||||
sizeof(UT_hash_table)); \
|
||||
if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \
|
||||
memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \
|
||||
(head)->hh.tbl->tail = &((head)->hh); \
|
||||
(head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \
|
||||
(head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \
|
||||
(head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \
|
||||
(head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \
|
||||
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
|
||||
if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \
|
||||
memset((head)->hh.tbl->buckets, 0, \
|
||||
HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
|
||||
HASH_BLOOM_MAKE((head)->hh.tbl); \
|
||||
(head)->hh.tbl->signature = HASH_SIGNATURE; \
|
||||
} while(0)
|
||||
|
||||
#define HASH_ADD(hh,head,fieldname,keylen_in,add) \
|
||||
HASH_ADD_KEYPTR(hh,head,&add->fieldname,keylen_in,add)
|
||||
|
||||
#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \
|
||||
do { \
|
||||
unsigned _ha_bkt; \
|
||||
(add)->hh.next = NULL; \
|
||||
(add)->hh.key = (char*)keyptr; \
|
||||
(add)->hh.keylen = keylen_in; \
|
||||
if (!(head)) { \
|
||||
head = (add); \
|
||||
(head)->hh.prev = NULL; \
|
||||
HASH_MAKE_TABLE(hh,head); \
|
||||
} else { \
|
||||
(head)->hh.tbl->tail->next = (add); \
|
||||
(add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \
|
||||
(head)->hh.tbl->tail = &((add)->hh); \
|
||||
} \
|
||||
(head)->hh.tbl->num_items++; \
|
||||
(add)->hh.tbl = (head)->hh.tbl; \
|
||||
HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \
|
||||
(add)->hh.hashv, _ha_bkt); \
|
||||
HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \
|
||||
HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \
|
||||
HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \
|
||||
HASH_FSCK(hh,head); \
|
||||
} while(0)
|
||||
|
||||
#define HASH_TO_BKT( hashv, num_bkts, bkt ) \
|
||||
do { \
|
||||
bkt = ((hashv) & ((num_bkts) - 1)); \
|
||||
} while(0)
|
||||
|
||||
/* delete "delptr" from the hash table.
|
||||
* "the usual" patch-up process for the app-order doubly-linked-list.
|
||||
* The use of _hd_hh_del below deserves special explanation.
|
||||
* These used to be expressed using (delptr) but that led to a bug
|
||||
* if someone used the same symbol for the head and deletee, like
|
||||
* HASH_DELETE(hh,users,users);
|
||||
* We want that to work, but by changing the head (users) below
|
||||
* we were forfeiting our ability to further refer to the deletee (users)
|
||||
* in the patch-up process. Solution: use scratch space in the table to
|
||||
* copy the deletee pointer, then the latter references are via that
|
||||
* scratch pointer rather than through the repointed (users) symbol.
|
||||
*/
|
||||
#define HASH_DELETE(hh,head,delptr) \
|
||||
do { \
|
||||
unsigned _hd_bkt; \
|
||||
struct UT_hash_handle *_hd_hh_del; \
|
||||
if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \
|
||||
uthash_free((head)->hh.tbl->buckets ); \
|
||||
HASH_BLOOM_FREE((head)->hh.tbl); \
|
||||
uthash_free((head)->hh.tbl); \
|
||||
head = NULL; \
|
||||
} else { \
|
||||
_hd_hh_del = &((delptr)->hh); \
|
||||
if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \
|
||||
(head)->hh.tbl->tail = \
|
||||
(UT_hash_handle*)((char*)((delptr)->hh.prev) + \
|
||||
(head)->hh.tbl->hho); \
|
||||
} \
|
||||
if ((delptr)->hh.prev) { \
|
||||
((UT_hash_handle*)((char*)((delptr)->hh.prev) + \
|
||||
(head)->hh.tbl->hho))->next = (delptr)->hh.next; \
|
||||
} else { \
|
||||
head = TYPEOF(head)((delptr)->hh.next); \
|
||||
} \
|
||||
if (_hd_hh_del->next) { \
|
||||
((UT_hash_handle*)((char*)_hd_hh_del->next + \
|
||||
(head)->hh.tbl->hho))->prev = \
|
||||
_hd_hh_del->prev; \
|
||||
} \
|
||||
HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
|
||||
HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \
|
||||
(head)->hh.tbl->num_items--; \
|
||||
} \
|
||||
HASH_FSCK(hh,head); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
|
||||
#define HASH_FIND_STR(head,findstr,out) \
|
||||
HASH_FIND(hh,head,findstr,strlen(findstr),out)
|
||||
#define HASH_ADD_STR(head,strfield,add) \
|
||||
HASH_ADD(hh,head,strfield,strlen(add->strfield),add)
|
||||
#define HASH_FIND_INT(head,findint,out) \
|
||||
HASH_FIND(hh,head,findint,sizeof(int),out)
|
||||
#define HASH_ADD_INT(head,intfield,add) \
|
||||
HASH_ADD(hh,head,intfield,sizeof(int),add)
|
||||
#define HASH_DEL(head,delptr) \
|
||||
HASH_DELETE(hh,head,delptr)
|
||||
|
||||
/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
|
||||
* This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
|
||||
*/
|
||||
#if defined(HASH_DEBUG)
|
||||
#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
|
||||
#define HASH_FSCK(hh,head) \
|
||||
do { \
|
||||
unsigned _bkt_i; \
|
||||
unsigned _count, _bkt_count; \
|
||||
char *_prev; \
|
||||
struct UT_hash_handle *_thh; \
|
||||
if (head) { \
|
||||
_count = 0; \
|
||||
for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \
|
||||
_bkt_count = 0; \
|
||||
_thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \
|
||||
_prev = NULL; \
|
||||
while (_thh) { \
|
||||
if (_prev != (char*)(_thh->hh_prev)) { \
|
||||
HASH_OOPS("invalid hh_prev %p, actual %p\n", \
|
||||
_thh->hh_prev, _prev ); \
|
||||
} \
|
||||
_bkt_count++; \
|
||||
_prev = (char*)(_thh); \
|
||||
_thh = _thh->hh_next; \
|
||||
} \
|
||||
_count += _bkt_count; \
|
||||
if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \
|
||||
HASH_OOPS("invalid bucket count %d, actual %d\n", \
|
||||
(head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \
|
||||
} \
|
||||
} \
|
||||
if (_count != (head)->hh.tbl->num_items) { \
|
||||
HASH_OOPS("invalid hh item count %d, actual %d\n", \
|
||||
(head)->hh.tbl->num_items, _count ); \
|
||||
} \
|
||||
/* traverse hh in app order; check next/prev integrity, count */ \
|
||||
_count = 0; \
|
||||
_prev = NULL; \
|
||||
_thh = &(head)->hh; \
|
||||
while (_thh) { \
|
||||
_count++; \
|
||||
if (_prev !=(char*)(_thh->prev)) { \
|
||||
HASH_OOPS("invalid prev %p, actual %p\n", \
|
||||
_thh->prev, _prev ); \
|
||||
} \
|
||||
_prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \
|
||||
_thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \
|
||||
(head)->hh.tbl->hho) : NULL ); \
|
||||
} \
|
||||
if (_count != (head)->hh.tbl->num_items) { \
|
||||
HASH_OOPS("invalid app item count %d, actual %d\n", \
|
||||
(head)->hh.tbl->num_items, _count ); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
#define HASH_FSCK(hh,head)
|
||||
#endif
|
||||
|
||||
/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
|
||||
* the descriptor to which this macro is defined for tuning the hash function.
|
||||
* The app can #include <unistd.h> to get the prototype for write(2). */
|
||||
#if defined(HASH_EMIT_KEYS)
|
||||
#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \
|
||||
do { \
|
||||
unsigned _klen = fieldlen; \
|
||||
write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \
|
||||
write(HASH_EMIT_KEYS, keyptr, fieldlen); \
|
||||
} while (0)
|
||||
#else
|
||||
#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
|
||||
#endif
|
||||
|
||||
/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
|
||||
#if defined(HASH_FUNCTION)
|
||||
#define HASH_FCN HASH_FUNCTION
|
||||
#else
|
||||
#define HASH_FCN HASH_JEN
|
||||
#endif
|
||||
|
||||
/* The Bernstein hash function, used in Perl prior to v5.6 */
|
||||
#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned _hb_keylen=keylen; \
|
||||
char *_hb_key=(char*)key; \
|
||||
(hashv) = 0; \
|
||||
while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \
|
||||
bkt = (hashv) & (num_bkts-1); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
|
||||
* http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
|
||||
#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned _sx_i; \
|
||||
char *_hs_key=(char*)key; \
|
||||
hashv = 0; \
|
||||
for(_sx_i=0; _sx_i < keylen; _sx_i++) \
|
||||
hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while (0)
|
||||
|
||||
#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned _fn_i; \
|
||||
char *_hf_key=(char*)key; \
|
||||
hashv = 2166136261UL; \
|
||||
for(_fn_i=0; _fn_i < keylen; _fn_i++) \
|
||||
hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0);
|
||||
|
||||
#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned _ho_i; \
|
||||
char *_ho_key=(char*)key; \
|
||||
hashv = 0; \
|
||||
for(_ho_i=0; _ho_i < keylen; _ho_i++) { \
|
||||
hashv += _ho_key[_ho_i]; \
|
||||
hashv += (hashv << 10); \
|
||||
hashv ^= (hashv >> 6); \
|
||||
} \
|
||||
hashv += (hashv << 3); \
|
||||
hashv ^= (hashv >> 11); \
|
||||
hashv += (hashv << 15); \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0)
|
||||
|
||||
#define HASH_JEN_MIX(a,b,c) \
|
||||
do { \
|
||||
a -= b; a -= c; a ^= ( c >> 13 ); \
|
||||
b -= c; b -= a; b ^= ( a << 8 ); \
|
||||
c -= a; c -= b; c ^= ( b >> 13 ); \
|
||||
a -= b; a -= c; a ^= ( c >> 12 ); \
|
||||
b -= c; b -= a; b ^= ( a << 16 ); \
|
||||
c -= a; c -= b; c ^= ( b >> 5 ); \
|
||||
a -= b; a -= c; a ^= ( c >> 3 ); \
|
||||
b -= c; b -= a; b ^= ( a << 10 ); \
|
||||
c -= a; c -= b; c ^= ( b >> 15 ); \
|
||||
} while (0)
|
||||
|
||||
#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
unsigned _hj_i,_hj_j,_hj_k; \
|
||||
char *_hj_key=(char*)key; \
|
||||
hashv = 0xfeedbeef; \
|
||||
_hj_i = _hj_j = 0x9e3779b9; \
|
||||
_hj_k = keylen; \
|
||||
while (_hj_k >= 12) { \
|
||||
_hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \
|
||||
+ ( (unsigned)_hj_key[2] << 16 ) \
|
||||
+ ( (unsigned)_hj_key[3] << 24 ) ); \
|
||||
_hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \
|
||||
+ ( (unsigned)_hj_key[6] << 16 ) \
|
||||
+ ( (unsigned)_hj_key[7] << 24 ) ); \
|
||||
hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \
|
||||
+ ( (unsigned)_hj_key[10] << 16 ) \
|
||||
+ ( (unsigned)_hj_key[11] << 24 ) ); \
|
||||
\
|
||||
HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
|
||||
\
|
||||
_hj_key += 12; \
|
||||
_hj_k -= 12; \
|
||||
} \
|
||||
hashv += keylen; \
|
||||
switch ( _hj_k ) { \
|
||||
case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \
|
||||
case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \
|
||||
case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \
|
||||
case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \
|
||||
case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \
|
||||
case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \
|
||||
case 5: _hj_j += _hj_key[4]; \
|
||||
case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \
|
||||
case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \
|
||||
case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \
|
||||
case 1: _hj_i += _hj_key[0]; \
|
||||
} \
|
||||
HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0)
|
||||
|
||||
/* The Paul Hsieh hash function */
|
||||
#undef get16bits
|
||||
#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
|
||||
|| defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
|
||||
#define get16bits(d) (*((const uint16_t *) (d)))
|
||||
#endif
|
||||
|
||||
#if !defined (get16bits)
|
||||
#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
|
||||
+(uint32_t)(((const uint8_t *)(d))[0]) )
|
||||
#endif
|
||||
#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
char *_sfh_key=(char*)key; \
|
||||
uint32_t _sfh_tmp, _sfh_len = keylen; \
|
||||
\
|
||||
int _sfh_rem = _sfh_len & 3; \
|
||||
_sfh_len >>= 2; \
|
||||
hashv = 0xcafebabe; \
|
||||
\
|
||||
/* Main loop */ \
|
||||
for (;_sfh_len > 0; _sfh_len--) { \
|
||||
hashv += get16bits (_sfh_key); \
|
||||
_sfh_tmp = (get16bits (_sfh_key+2) << 11) ^ hashv; \
|
||||
hashv = (hashv << 16) ^ _sfh_tmp; \
|
||||
_sfh_key += 2*sizeof (uint16_t); \
|
||||
hashv += hashv >> 11; \
|
||||
} \
|
||||
\
|
||||
/* Handle end cases */ \
|
||||
switch (_sfh_rem) { \
|
||||
case 3: hashv += get16bits (_sfh_key); \
|
||||
hashv ^= hashv << 16; \
|
||||
hashv ^= _sfh_key[sizeof (uint16_t)] << 18; \
|
||||
hashv += hashv >> 11; \
|
||||
break; \
|
||||
case 2: hashv += get16bits (_sfh_key); \
|
||||
hashv ^= hashv << 11; \
|
||||
hashv += hashv >> 17; \
|
||||
break; \
|
||||
case 1: hashv += *_sfh_key; \
|
||||
hashv ^= hashv << 10; \
|
||||
hashv += hashv >> 1; \
|
||||
} \
|
||||
\
|
||||
/* Force "avalanching" of final 127 bits */ \
|
||||
hashv ^= hashv << 3; \
|
||||
hashv += hashv >> 5; \
|
||||
hashv ^= hashv << 4; \
|
||||
hashv += hashv >> 17; \
|
||||
hashv ^= hashv << 25; \
|
||||
hashv += hashv >> 6; \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0);
|
||||
|
||||
#if defined(HASH_USING_NO_STRICT_ALIASING)
|
||||
/* The MurmurHash exploits some CPU's (e.g. x86) tolerance for unaligned reads.
|
||||
* For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.
|
||||
* So MurmurHash comes in two versions, the faster unaligned one and the slower
|
||||
* aligned one. We only use the faster one on CPU's where we know it's safe.
|
||||
*
|
||||
* Note the preprocessor built-in defines can be emitted using:
|
||||
*
|
||||
* gcc -m64 -dM -E - < /dev/null (on gcc)
|
||||
* cc -## a.c (where a.c is a simple test file) (Sun Studio)
|
||||
*/
|
||||
#if (defined(__i386__) || defined(__x86_64__))
|
||||
#define HASH_MUR HASH_MUR_UNALIGNED
|
||||
#else
|
||||
#define HASH_MUR HASH_MUR_ALIGNED
|
||||
#endif
|
||||
|
||||
/* Appleby's MurmurHash fast version for unaligned-tolerant archs like i386 */
|
||||
#define HASH_MUR_UNALIGNED(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
const unsigned int _mur_m = 0x5bd1e995; \
|
||||
const int _mur_r = 24; \
|
||||
hashv = 0xcafebabe ^ keylen; \
|
||||
char *_mur_key = (char *)key; \
|
||||
uint32_t _mur_tmp, _mur_len = keylen; \
|
||||
\
|
||||
for (;_mur_len >= 4; _mur_len-=4) { \
|
||||
_mur_tmp = *(uint32_t *)_mur_key; \
|
||||
_mur_tmp *= _mur_m; \
|
||||
_mur_tmp ^= _mur_tmp >> _mur_r; \
|
||||
_mur_tmp *= _mur_m; \
|
||||
hashv *= _mur_m; \
|
||||
hashv ^= _mur_tmp; \
|
||||
_mur_key += 4; \
|
||||
} \
|
||||
\
|
||||
switch(_mur_len) \
|
||||
{ \
|
||||
case 3: hashv ^= _mur_key[2] << 16; \
|
||||
case 2: hashv ^= _mur_key[1] << 8; \
|
||||
case 1: hashv ^= _mur_key[0]; \
|
||||
hashv *= _mur_m; \
|
||||
}; \
|
||||
\
|
||||
hashv ^= hashv >> 13; \
|
||||
hashv *= _mur_m; \
|
||||
hashv ^= hashv >> 15; \
|
||||
\
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0)
|
||||
|
||||
/* Appleby's MurmurHash version for alignment-sensitive archs like Sparc */
|
||||
#define HASH_MUR_ALIGNED(key,keylen,num_bkts,hashv,bkt) \
|
||||
do { \
|
||||
const unsigned int _mur_m = 0x5bd1e995; \
|
||||
const int _mur_r = 24; \
|
||||
hashv = 0xcafebabe ^ keylen; \
|
||||
char *_mur_key = (char *)key; \
|
||||
uint32_t _mur_len = keylen; \
|
||||
int _mur_align = (int)_mur_key & 3; \
|
||||
\
|
||||
if (_mur_align && (_mur_len >= 4)) { \
|
||||
unsigned _mur_t = 0, _mur_d = 0; \
|
||||
switch(_mur_align) { \
|
||||
case 1: _mur_t |= _mur_key[2] << 16; \
|
||||
case 2: _mur_t |= _mur_key[1] << 8; \
|
||||
case 3: _mur_t |= _mur_key[0]; \
|
||||
} \
|
||||
_mur_t <<= (8 * _mur_align); \
|
||||
_mur_key += 4-_mur_align; \
|
||||
_mur_len -= 4-_mur_align; \
|
||||
int _mur_sl = 8 * (4-_mur_align); \
|
||||
int _mur_sr = 8 * _mur_align; \
|
||||
\
|
||||
for (;_mur_len >= 4; _mur_len-=4) { \
|
||||
_mur_d = *(unsigned *)_mur_key; \
|
||||
_mur_t = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \
|
||||
unsigned _mur_k = _mur_t; \
|
||||
_mur_k *= _mur_m; \
|
||||
_mur_k ^= _mur_k >> _mur_r; \
|
||||
_mur_k *= _mur_m; \
|
||||
hashv *= _mur_m; \
|
||||
hashv ^= _mur_k; \
|
||||
_mur_t = _mur_d; \
|
||||
_mur_key += 4; \
|
||||
} \
|
||||
_mur_d = 0; \
|
||||
if(_mur_len >= _mur_align) { \
|
||||
switch(_mur_align) { \
|
||||
case 3: _mur_d |= _mur_key[2] << 16; \
|
||||
case 2: _mur_d |= _mur_key[1] << 8; \
|
||||
case 1: _mur_d |= _mur_key[0]; \
|
||||
} \
|
||||
unsigned _mur_k = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \
|
||||
_mur_k *= _mur_m; \
|
||||
_mur_k ^= _mur_k >> _mur_r; \
|
||||
_mur_k *= _mur_m; \
|
||||
hashv *= _mur_m; \
|
||||
hashv ^= _mur_k; \
|
||||
_mur_k += _mur_align; \
|
||||
_mur_len -= _mur_align; \
|
||||
\
|
||||
switch(_mur_len) \
|
||||
{ \
|
||||
case 3: hashv ^= _mur_key[2] << 16; \
|
||||
case 2: hashv ^= _mur_key[1] << 8; \
|
||||
case 1: hashv ^= _mur_key[0]; \
|
||||
hashv *= _mur_m; \
|
||||
} \
|
||||
} else { \
|
||||
switch(_mur_len) \
|
||||
{ \
|
||||
case 3: _mur_d ^= _mur_key[2] << 16; \
|
||||
case 2: _mur_d ^= _mur_key[1] << 8; \
|
||||
case 1: _mur_d ^= _mur_key[0]; \
|
||||
case 0: hashv ^= (_mur_t >> _mur_sr) | (_mur_d << _mur_sl); \
|
||||
hashv *= _mur_m; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
hashv ^= hashv >> 13; \
|
||||
hashv *= _mur_m; \
|
||||
hashv ^= hashv >> 15; \
|
||||
} else { \
|
||||
for (;_mur_len >= 4; _mur_len-=4) { \
|
||||
unsigned _mur_k = *(unsigned*)_mur_key; \
|
||||
_mur_k *= _mur_m; \
|
||||
_mur_k ^= _mur_k >> _mur_r; \
|
||||
_mur_k *= _mur_m; \
|
||||
hashv *= _mur_m; \
|
||||
hashv ^= _mur_k; \
|
||||
_mur_key += 4; \
|
||||
} \
|
||||
switch(_mur_len) \
|
||||
{ \
|
||||
case 3: hashv ^= _mur_key[2] << 16; \
|
||||
case 2: hashv ^= _mur_key[1] << 8; \
|
||||
case 1: hashv ^= _mur_key[0]; \
|
||||
hashv *= _mur_m; \
|
||||
} \
|
||||
\
|
||||
hashv ^= hashv >> 13; \
|
||||
hashv *= _mur_m; \
|
||||
hashv ^= hashv >> 15; \
|
||||
} \
|
||||
bkt = hashv & (num_bkts-1); \
|
||||
} while(0)
|
||||
#endif /* HASH_USING_NO_STRICT_ALIASING */
|
||||
|
||||
/* key comparison function; return 0 if keys equal */
|
||||
#define HASH_KEYCMP(a,b,len) memcmp(a,b,len)
|
||||
|
||||
/* iterate over items in a known bucket to find desired item */
|
||||
#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \
|
||||
out = TYPEOF(out)((head.hh_head) ? ELMT_FROM_HH(tbl,head.hh_head) : NULL); \
|
||||
while (out) { \
|
||||
if (out->hh.keylen == keylen_in) { \
|
||||
if ((HASH_KEYCMP(out->hh.key,keyptr,keylen_in)) == 0) break; \
|
||||
} \
|
||||
out= TYPEOF(out)((out->hh.hh_next) ? \
|
||||
ELMT_FROM_HH(tbl,out->hh.hh_next) : NULL); \
|
||||
}
|
||||
|
||||
/* add an item to a bucket */
|
||||
#define HASH_ADD_TO_BKT(head,addhh) \
|
||||
do { \
|
||||
head.count++; \
|
||||
(addhh)->hh_next = head.hh_head; \
|
||||
(addhh)->hh_prev = NULL; \
|
||||
if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \
|
||||
(head).hh_head=addhh; \
|
||||
if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \
|
||||
&& (addhh)->tbl->noexpand != 1) { \
|
||||
HASH_EXPAND_BUCKETS((addhh)->tbl); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/* remove an item from a given bucket */
|
||||
#define HASH_DEL_IN_BKT(hh,head,hh_del) \
|
||||
(head).count--; \
|
||||
if ((head).hh_head == hh_del) { \
|
||||
(head).hh_head = hh_del->hh_next; \
|
||||
} \
|
||||
if (hh_del->hh_prev) { \
|
||||
hh_del->hh_prev->hh_next = hh_del->hh_next; \
|
||||
} \
|
||||
if (hh_del->hh_next) { \
|
||||
hh_del->hh_next->hh_prev = hh_del->hh_prev; \
|
||||
}
|
||||
|
||||
/* Bucket expansion has the effect of doubling the number of buckets
|
||||
* and redistributing the items into the new buckets. Ideally the
|
||||
* items will distribute more or less evenly into the new buckets
|
||||
* (the extent to which this is true is a measure of the quality of
|
||||
* the hash function as it applies to the key domain).
|
||||
*
|
||||
* With the items distributed into more buckets, the chain length
|
||||
* (item count) in each bucket is reduced. Thus by expanding buckets
|
||||
* the hash keeps a bound on the chain length. This bounded chain
|
||||
* length is the essence of how a hash provides constant time lookup.
|
||||
*
|
||||
* The calculation of tbl->ideal_chain_maxlen below deserves some
|
||||
* explanation. First, keep in mind that we're calculating the ideal
|
||||
* maximum chain length based on the *new* (doubled) bucket count.
|
||||
* In fractions this is just n/b (n=number of items,b=new num buckets).
|
||||
* Since the ideal chain length is an integer, we want to calculate
|
||||
* ceil(n/b). We don't depend on floating point arithmetic in this
|
||||
* hash, so to calculate ceil(n/b) with integers we could write
|
||||
*
|
||||
* ceil(n/b) = (n/b) + ((n%b)?1:0)
|
||||
*
|
||||
* and in fact a previous version of this hash did just that.
|
||||
* But now we have improved things a bit by recognizing that b is
|
||||
* always a power of two. We keep its base 2 log handy (call it lb),
|
||||
* so now we can write this with a bit shift and logical AND:
|
||||
*
|
||||
* ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
|
||||
*
|
||||
*/
|
||||
#define HASH_EXPAND_BUCKETS(tbl) \
|
||||
do { \
|
||||
unsigned _he_bkt; \
|
||||
unsigned _he_bkt_i; \
|
||||
struct UT_hash_handle *_he_thh, *_he_hh_nxt; \
|
||||
UT_hash_bucket *_he_new_buckets, *_he_newbkt; \
|
||||
_he_new_buckets = (UT_hash_bucket*)uthash_malloc( \
|
||||
2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
|
||||
if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \
|
||||
memset(_he_new_buckets, 0, \
|
||||
2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
|
||||
tbl->ideal_chain_maxlen = \
|
||||
(tbl->num_items >> (tbl->log2_num_buckets+1)) + \
|
||||
((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \
|
||||
tbl->nonideal_items = 0; \
|
||||
for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \
|
||||
{ \
|
||||
_he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \
|
||||
while (_he_thh) { \
|
||||
_he_hh_nxt = _he_thh->hh_next; \
|
||||
HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \
|
||||
_he_newbkt = &(_he_new_buckets[ _he_bkt ]); \
|
||||
if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \
|
||||
tbl->nonideal_items++; \
|
||||
_he_newbkt->expand_mult = _he_newbkt->count / \
|
||||
tbl->ideal_chain_maxlen; \
|
||||
} \
|
||||
_he_thh->hh_prev = NULL; \
|
||||
_he_thh->hh_next = _he_newbkt->hh_head; \
|
||||
if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \
|
||||
_he_thh; \
|
||||
_he_newbkt->hh_head = _he_thh; \
|
||||
_he_thh = _he_hh_nxt; \
|
||||
} \
|
||||
} \
|
||||
tbl->num_buckets *= 2; \
|
||||
tbl->log2_num_buckets++; \
|
||||
uthash_free( tbl->buckets ); \
|
||||
tbl->buckets = _he_new_buckets; \
|
||||
tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \
|
||||
(tbl->ineff_expands+1) : 0; \
|
||||
if (tbl->ineff_expands > 1) { \
|
||||
tbl->noexpand=1; \
|
||||
uthash_noexpand_fyi(tbl); \
|
||||
} \
|
||||
uthash_expand_fyi(tbl); \
|
||||
} while(0)
|
||||
|
||||
|
||||
/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
|
||||
/* Note that HASH_SORT assumes the hash handle name to be hh.
|
||||
* HASH_SRT was added to allow the hash handle name to be passed in. */
|
||||
#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
|
||||
#define HASH_SRT(hh,head,cmpfcn) \
|
||||
do { \
|
||||
unsigned _hs_i; \
|
||||
unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \
|
||||
struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \
|
||||
if (head) { \
|
||||
_hs_insize = 1; \
|
||||
_hs_looping = 1; \
|
||||
_hs_list = &((head)->hh); \
|
||||
while (_hs_looping) { \
|
||||
_hs_p = _hs_list; \
|
||||
_hs_list = NULL; \
|
||||
_hs_tail = NULL; \
|
||||
_hs_nmerges = 0; \
|
||||
while (_hs_p) { \
|
||||
_hs_nmerges++; \
|
||||
_hs_q = _hs_p; \
|
||||
_hs_psize = 0; \
|
||||
for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \
|
||||
_hs_psize++; \
|
||||
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
|
||||
((void*)((char*)(_hs_q->next) + \
|
||||
(head)->hh.tbl->hho)) : NULL); \
|
||||
if (! (_hs_q) ) break; \
|
||||
} \
|
||||
_hs_qsize = _hs_insize; \
|
||||
while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \
|
||||
if (_hs_psize == 0) { \
|
||||
_hs_e = _hs_q; \
|
||||
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
|
||||
((void*)((char*)(_hs_q->next) + \
|
||||
(head)->hh.tbl->hho)) : NULL); \
|
||||
_hs_qsize--; \
|
||||
} else if ( (_hs_qsize == 0) || !(_hs_q) ) { \
|
||||
_hs_e = _hs_p; \
|
||||
_hs_p = (UT_hash_handle*)((_hs_p->next) ? \
|
||||
((void*)((char*)(_hs_p->next) + \
|
||||
(head)->hh.tbl->hho)) : NULL); \
|
||||
_hs_psize--; \
|
||||
} else if (( \
|
||||
cmpfcn(TYPEOF(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
|
||||
TYPEOF(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
|
||||
) <= 0) { \
|
||||
_hs_e = _hs_p; \
|
||||
_hs_p = (UT_hash_handle*)((_hs_p->next) ? \
|
||||
((void*)((char*)(_hs_p->next) + \
|
||||
(head)->hh.tbl->hho)) : NULL); \
|
||||
_hs_psize--; \
|
||||
} else { \
|
||||
_hs_e = _hs_q; \
|
||||
_hs_q = (UT_hash_handle*)((_hs_q->next) ? \
|
||||
((void*)((char*)(_hs_q->next) + \
|
||||
(head)->hh.tbl->hho)) : NULL); \
|
||||
_hs_qsize--; \
|
||||
} \
|
||||
if ( _hs_tail ) { \
|
||||
_hs_tail->next = ((_hs_e) ? \
|
||||
ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \
|
||||
} else { \
|
||||
_hs_list = _hs_e; \
|
||||
} \
|
||||
_hs_e->prev = ((_hs_tail) ? \
|
||||
ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \
|
||||
_hs_tail = _hs_e; \
|
||||
} \
|
||||
_hs_p = _hs_q; \
|
||||
} \
|
||||
_hs_tail->next = NULL; \
|
||||
if ( _hs_nmerges <= 1 ) { \
|
||||
_hs_looping=0; \
|
||||
(head)->hh.tbl->tail = _hs_tail; \
|
||||
(head) = TYPEOF(head)ELMT_FROM_HH((head)->hh.tbl, _hs_list); \
|
||||
} \
|
||||
_hs_insize *= 2; \
|
||||
} \
|
||||
HASH_FSCK(hh,head); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* This function selects items from one hash into another hash.
|
||||
* The end result is that the selected items have dual presence
|
||||
* in both hashes. There is no copy of the items made; rather
|
||||
* they are added into the new hash through a secondary hash
|
||||
* hash handle that must be present in the structure. */
|
||||
#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \
|
||||
do { \
|
||||
unsigned _src_bkt, _dst_bkt; \
|
||||
void *_last_elt=NULL, *_elt; \
|
||||
UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \
|
||||
ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \
|
||||
if (src) { \
|
||||
for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \
|
||||
for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \
|
||||
_src_hh; \
|
||||
_src_hh = _src_hh->hh_next) { \
|
||||
_elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \
|
||||
if (cond(_elt)) { \
|
||||
_dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \
|
||||
_dst_hh->key = _src_hh->key; \
|
||||
_dst_hh->keylen = _src_hh->keylen; \
|
||||
_dst_hh->hashv = _src_hh->hashv; \
|
||||
_dst_hh->prev = _last_elt; \
|
||||
_dst_hh->next = NULL; \
|
||||
if (_last_elt_hh) { _last_elt_hh->next = _elt; } \
|
||||
if (!dst) { \
|
||||
dst = TYPEOF(dst)_elt; \
|
||||
HASH_MAKE_TABLE(hh_dst,dst); \
|
||||
} else { \
|
||||
_dst_hh->tbl = (dst)->hh_dst.tbl; \
|
||||
} \
|
||||
HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \
|
||||
HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \
|
||||
(dst)->hh_dst.tbl->num_items++; \
|
||||
_last_elt = _elt; \
|
||||
_last_elt_hh = _dst_hh; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
HASH_FSCK(hh_dst,dst); \
|
||||
} while (0)
|
||||
|
||||
#define HASH_CLEAR(hh,head) \
|
||||
do { \
|
||||
if (head) { \
|
||||
uthash_free((head)->hh.tbl->buckets ); \
|
||||
uthash_free((head)->hh.tbl); \
|
||||
(head)=NULL; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
/* obtain a count of items in the hash */
|
||||
#define HASH_COUNT(head) HASH_CNT(hh,head)
|
||||
#define HASH_CNT(hh,head) (head?(head->hh.tbl->num_items):0)
|
||||
|
||||
typedef struct UT_hash_bucket {
|
||||
struct UT_hash_handle *hh_head;
|
||||
unsigned count;
|
||||
|
||||
/* expand_mult is normally set to 0. In this situation, the max chain length
|
||||
* threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
|
||||
* the bucket's chain exceeds this length, bucket expansion is triggered).
|
||||
* However, setting expand_mult to a non-zero value delays bucket expansion
|
||||
* (that would be triggered by additions to this particular bucket)
|
||||
* until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
|
||||
* (The multiplier is simply expand_mult+1). The whole idea of this
|
||||
* multiplier is to reduce bucket expansions, since they are expensive, in
|
||||
* situations where we know that a particular bucket tends to be overused.
|
||||
* It is better to let its chain length grow to a longer yet-still-bounded
|
||||
* value, than to do an O(n) bucket expansion too often.
|
||||
*/
|
||||
unsigned expand_mult;
|
||||
|
||||
} UT_hash_bucket;
|
||||
|
||||
/* random signature used only to find hash tables in external analysis */
|
||||
#define HASH_SIGNATURE 0xa0111fe1
|
||||
#define HASH_BLOOM_SIGNATURE 0xb12220f2
|
||||
|
||||
typedef struct UT_hash_table {
|
||||
UT_hash_bucket *buckets;
|
||||
unsigned num_buckets, log2_num_buckets;
|
||||
unsigned num_items;
|
||||
struct UT_hash_handle *tail; /* tail hh in app order, for fast append */
|
||||
ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
|
||||
|
||||
/* in an ideal situation (all buckets used equally), no bucket would have
|
||||
* more than ceil(#items/#buckets) items. that's the ideal chain length. */
|
||||
unsigned ideal_chain_maxlen;
|
||||
|
||||
/* nonideal_items is the number of items in the hash whose chain position
|
||||
* exceeds the ideal chain maxlen. these items pay the penalty for an uneven
|
||||
* hash distribution; reaching them in a chain traversal takes >ideal steps */
|
||||
unsigned nonideal_items;
|
||||
|
||||
/* ineffective expands occur when a bucket doubling was performed, but
|
||||
* afterward, more than half the items in the hash had nonideal chain
|
||||
* positions. If this happens on two consecutive expansions we inhibit any
|
||||
* further expansion, as it's not helping; this happens when the hash
|
||||
* function isn't a good fit for the key domain. When expansion is inhibited
|
||||
* the hash will still work, albeit no longer in constant time. */
|
||||
unsigned ineff_expands, noexpand;
|
||||
|
||||
uint32_t signature; /* used only to find hash tables in external analysis */
|
||||
#if defined(HASH_BLOOM)
|
||||
uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
|
||||
uint8_t *bloom_bv;
|
||||
char bloom_nbits;
|
||||
#endif
|
||||
|
||||
} UT_hash_table;
|
||||
|
||||
typedef struct UT_hash_handle {
|
||||
struct UT_hash_table *tbl;
|
||||
void *prev; /* prev element in app order */
|
||||
void *next; /* next element in app order */
|
||||
struct UT_hash_handle *hh_prev; /* previous hh in bucket order */
|
||||
struct UT_hash_handle *hh_next; /* next hh in bucket order */
|
||||
void *key; /* ptr to enclosing struct's key */
|
||||
unsigned keylen; /* enclosing struct's key len */
|
||||
unsigned hashv; /* result of hash-fcn(key) */
|
||||
} UT_hash_handle;
|
||||
|
||||
#endif /* UTHASH_H */
|
@ -1,29 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef XDISPLAY_H
|
||||
#define XDISPLAY_H
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
/* Returns the main display, closed either on exit or when closeMainDisplay()
|
||||
* is invoked. This removes a bit of the overhead of calling XOpenDisplay() &
|
||||
* XCloseDisplay() everytime the main display needs to be used.
|
||||
*
|
||||
* Note that this is almost certainly not thread safe. */
|
||||
Display *XGetMainDisplay(void);
|
||||
|
||||
/* Closes the main display if it is open, or does nothing if not. */
|
||||
void XCloseMainDisplay(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
char *getXDisplay(void);
|
||||
void setXDisplay(char *name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* XDISPLAY_H */
|
@ -1,14 +1,21 @@
|
||||
#include "xdisplay.h"
|
||||
#include <stdio.h> /* For fputs() */
|
||||
#include <stdlib.h> /* For atexit() */
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
static Display *mainDisplay = NULL;
|
||||
static int registered = 0;
|
||||
static char *displayName = ":0.0";
|
||||
|
||||
static char *displayName = NULL;
|
||||
static int hasDisplayNameChanged = 0;
|
||||
|
||||
Display *XGetMainDisplay(void)
|
||||
{
|
||||
void XCloseMainDisplay(void) {
|
||||
if (mainDisplay != NULL) {
|
||||
XCloseDisplay(mainDisplay);
|
||||
mainDisplay = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Display *XGetMainDisplay(void) {
|
||||
/* Close the display if displayName has changed */
|
||||
if (hasDisplayNameChanged) {
|
||||
XCloseMainDisplay();
|
||||
@ -20,10 +27,15 @@ Display *XGetMainDisplay(void)
|
||||
mainDisplay = XOpenDisplay(displayName);
|
||||
|
||||
/* Then try using environment variable DISPLAY */
|
||||
if (mainDisplay == NULL) {
|
||||
if (mainDisplay == NULL && displayName != NULL) {
|
||||
mainDisplay = XOpenDisplay(NULL);
|
||||
}
|
||||
|
||||
/* Fall back to the most likely :0.0*/
|
||||
if (mainDisplay == NULL) {
|
||||
mainDisplay = XOpenDisplay(":0.0");
|
||||
}
|
||||
|
||||
if (mainDisplay == NULL) {
|
||||
fputs("Could not open main display\n", stderr);
|
||||
} else if (!registered) {
|
||||
@ -35,22 +47,11 @@ Display *XGetMainDisplay(void)
|
||||
return mainDisplay;
|
||||
}
|
||||
|
||||
void XCloseMainDisplay(void)
|
||||
{
|
||||
if (mainDisplay != NULL) {
|
||||
XCloseDisplay(mainDisplay);
|
||||
mainDisplay = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void setXDisplay(char *name)
|
||||
{
|
||||
void setXDisplay(char *name) {
|
||||
displayName = strdup(name);
|
||||
hasDisplayNameChanged = 1;
|
||||
}
|
||||
|
||||
char *getXDisplay(void)
|
||||
{
|
||||
char *getXDisplay(void) {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
|
@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef ZLIB_UTIL_H
|
||||
#define ZLIB_UTIL_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include "ms_stdint.h"
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/* Attempts to decompress given deflated NUL-terminated buffer.
|
||||
*
|
||||
* If successful and |len| is not NULL, |len| will be set to the number of
|
||||
* bytes in the returned buffer.
|
||||
* Returns new string to be free()'d by caller, or NULL on error. */
|
||||
uint8_t *zlib_decompress(const uint8_t *buf, size_t *len);
|
||||
|
||||
/* Attempt to compress given buffer.
|
||||
*
|
||||
* The compression level is passed directly to zlib: it must between 0 and 9,
|
||||
* where 1 gives best speed, 9 gives best compression, and 0 gives no
|
||||
* compression at all.
|
||||
*
|
||||
* If successful and |len| is not NULL, |len| will be set to the number of
|
||||
* bytes in the returned buffer.
|
||||
* Returns new string to be free()'d by caller, or NULL on error. */
|
||||
uint8_t *zlib_compress(const uint8_t *buf, const size_t buflen, int level,
|
||||
size_t *len);
|
||||
|
||||
#endif /* ZLIB_UTIL_H */
|
@ -1,99 +0,0 @@
|
||||
#include "zlib_util.h"
|
||||
#include <zlib.h>
|
||||
#include <stdio.h> /* fprintf() */
|
||||
#include <stdlib.h> /* malloc() */
|
||||
#include <assert.h>
|
||||
|
||||
#define ZLIB_CHUNK (16 * 1024)
|
||||
|
||||
uint8_t *zlib_decompress(const uint8_t *buf, size_t *len)
|
||||
{
|
||||
size_t output_size = ZLIB_CHUNK;
|
||||
uint8_t *output = malloc(output_size);
|
||||
int err;
|
||||
z_stream zst;
|
||||
|
||||
/* Sanity check */
|
||||
if (output == NULL) return NULL;
|
||||
assert(buf != NULL);
|
||||
|
||||
/* Set inflate state */
|
||||
zst.zalloc = Z_NULL;
|
||||
zst.zfree = Z_NULL;
|
||||
zst.opaque = Z_NULL;
|
||||
zst.next_out = (Byte *)output;
|
||||
zst.next_in = (Byte *)buf;
|
||||
zst.avail_out = ZLIB_CHUNK;
|
||||
|
||||
if (inflateInit(&zst) != Z_OK) goto error;
|
||||
|
||||
/* Decompress input buffer */
|
||||
do {
|
||||
if ((err = inflate(&zst, Z_NO_FLUSH)) == Z_OK) { /* Need more memory */
|
||||
zst.avail_out = (uInt)output_size;
|
||||
|
||||
/* Double size each time to avoid calls to realloc() */
|
||||
output_size <<= 1;
|
||||
output = realloc(output, output_size + 1);
|
||||
if (output == NULL) return NULL;
|
||||
|
||||
zst.next_out = (Byte *)(output + zst.avail_out);
|
||||
} else if (err != Z_STREAM_END) { /* Error decompressing */
|
||||
if (zst.msg != NULL) {
|
||||
fprintf(stderr, "Could not decompress data: %s\n", zst.msg);
|
||||
}
|
||||
inflateEnd(&zst);
|
||||
goto error;
|
||||
}
|
||||
} while (err != Z_STREAM_END);
|
||||
|
||||
if (len != NULL) *len = zst.total_out;
|
||||
if (inflateEnd(&zst) != Z_OK) goto error;
|
||||
return output; /* To be free()'d by caller */
|
||||
|
||||
error:
|
||||
if (output != NULL) free(output);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t *zlib_compress(const uint8_t *buf, const size_t buflen, int level,
|
||||
size_t *len)
|
||||
{
|
||||
z_stream zst;
|
||||
uint8_t *output = NULL;
|
||||
|
||||
/* Sanity check */
|
||||
assert(buf != NULL);
|
||||
assert(len != NULL);
|
||||
assert(level <= 9 && level >= 0);
|
||||
|
||||
zst.avail_out = (uInt)((buflen + (buflen / 10)) + 12);
|
||||
output = malloc(zst.avail_out);
|
||||
if (output == NULL) return NULL;
|
||||
|
||||
/* Set deflate state */
|
||||
zst.zalloc = Z_NULL;
|
||||
zst.zfree = Z_NULL;
|
||||
zst.next_out = (Byte *)output;
|
||||
zst.next_in = (Byte *)buf;
|
||||
zst.avail_in = (uInt)buflen;
|
||||
|
||||
if (deflateInit(&zst, level) != Z_OK) goto error;
|
||||
|
||||
/* Compress input buffer */
|
||||
if (deflate(&zst, Z_FINISH) != Z_STREAM_END) {
|
||||
if (zst.msg != NULL) {
|
||||
fprintf(stderr, "Could not compress data: %s\n", zst.msg);
|
||||
}
|
||||
deflateEnd(&zst);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (len != NULL) *len = zst.total_out;
|
||||
if (deflateEnd(&zst) != Z_OK) goto error;
|
||||
return output; /* To be free()'d by caller */
|
||||
|
||||
error:
|
||||
if (output != NULL) free(output);
|
||||
return NULL;
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
// #ifndef BITMAP_CLASS_H
|
||||
// #define BITMAP_CLASS_H
|
||||
|
||||
#include "../base/MMBitmap.h"
|
||||
|
||||
/* This file defines the class "Bitmap" for dealing with raw bitmaps. */
|
||||
struct _BitmapObject {
|
||||
MMBitmapRef bitmap;
|
||||
MMPoint point; /* For iterator */
|
||||
};
|
||||
|
||||
typedef struct _BitmapObject BitmapObject;
|
||||
|
||||
// extern PyTypeObject Bitmap_Type;
|
||||
|
||||
/* Returns a newly-initialized BitmapObject from the given MMBitmap.
|
||||
* The reference to |bitmap| is "stolen"; i.e., only the pointer is copied, and
|
||||
* the reponsibility for free()'ing the buffer is given to the |BitmapObject|.
|
||||
*
|
||||
* Remember to call PyType_Ready() before using this for the first time! */
|
||||
BitmapObject BitmapObject_FromMMBitmap(MMBitmapRef bitmap);
|
||||
|
||||
// #endif /* PY_BITMAP_CLASS_H */
|
||||
//,MMRect rect
|
||||
MMPoint aFindBitmap(MMBitmapRef bit_map,MMRect rect);
|
@ -1,55 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef BITMAP_H
|
||||
#define BITMAP_H
|
||||
|
||||
// #include "../base/types.h"
|
||||
#include "../base/MMBitmap.h"
|
||||
#include "../base/MMPointArray_c.h"
|
||||
|
||||
/* Convenience wrapper around findBitmapInRect(), where |rect| is the bounds
|
||||
* of |haystack|. */
|
||||
#define findBitmapInBitmap(needle, haystack, pointPtr, tol) \
|
||||
findBitmapInRect(needle, haystack, pointPtr, MMBitmapGetBounds(haystack), tol)
|
||||
|
||||
/* Returns 0 and sets |point| to the origin of |needle| in |haystack| if
|
||||
* |needle| was found in |haystack| inside of |rect|, or returns -1 if not.
|
||||
*
|
||||
* |tolerance| should be in the range 0.0f - 1.0f, denoting how closely the
|
||||
* colors in the bitmaps need to match, with 0 being exact and 1 being any.
|
||||
*/
|
||||
int findBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,
|
||||
MMPoint *point, MMRect rect, float tolerance);
|
||||
|
||||
/* Convenience wrapper around findAllBitmapInRect(), where |rect| is the bounds
|
||||
* of |haystack|. */
|
||||
#define findAllBitmapInBitmap(needle, haystack, tolerance) \
|
||||
findAllBitmapInRect(needle, haystack, \
|
||||
MMBitmapGetBounds(haystack), tolerance)
|
||||
|
||||
/* Returns MMPointArray of all occurrences of |needle| in |haystack| inside of
|
||||
* |rect|. Note that an is returned regardless of whether |needle| was found;
|
||||
* check array->count to see if it actually was.
|
||||
*
|
||||
* |tolerance| should be in the range 0.0f - 1.0f, denoting how closely the
|
||||
* colors in the bitmaps need to match, with 0 being exact and 1 being any.
|
||||
*
|
||||
* Responsibility for freeing the MMPointArray with destroyMMPointArray() is
|
||||
* given to the caller.
|
||||
*/
|
||||
MMPointArrayRef findAllBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,
|
||||
MMRect rect, float tolerance);
|
||||
|
||||
// #define MMRGBHexAtPoint(image, x, y) \
|
||||
// hexFromMMRGB(MMRGBColorAtPoint(image, x, y))
|
||||
|
||||
/* Convenience wrapper around countOfBitmapInRect(), where |rect| is the bounds
|
||||
* of |haystack|. */
|
||||
#define countOfBitmapInBitmap(needle, haystack, tolerance) \
|
||||
countOfBitmapInRect(needle, haystack, MMBitmapGetBounds(haystack), tolerance)
|
||||
|
||||
/* Returns the number of occurences of |needle| in |haystack| inside
|
||||
* of |rect|. */
|
||||
size_t countOfBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,
|
||||
MMRect rect, float tolerance);
|
||||
|
||||
#endif /* BITMAP_H */
|
@ -1,270 +0,0 @@
|
||||
#include "bitmap_find.h"
|
||||
#include "../base/UTHashTable_c.h"
|
||||
#include <assert.h>
|
||||
|
||||
/* Node to be used in hash table. */
|
||||
struct shiftNode {
|
||||
UTHashNode_HEAD /* Make structure hashable */
|
||||
MMRGBHex color; /* Key */
|
||||
MMPoint offset; /* Value */
|
||||
};
|
||||
|
||||
/* --- Hash table helper functions --- */
|
||||
|
||||
/* Adds hex-color/offset pair to jump table. */
|
||||
static void addNodeToTable(UTHashTable *table, MMRGBHex color, MMPoint offset);
|
||||
|
||||
/* Returns node associated with color in jump table, or NULL if it
|
||||
* doesn't exist. */
|
||||
static struct shiftNode *nodeForColor(UTHashTable *table, MMRGBHex color);
|
||||
|
||||
/* Returns nonzero (true) if table has key, or zero (false) if not. */
|
||||
#define tableHasKey(table, color) (nodeForColor(table, color) != NULL)
|
||||
|
||||
/* --- Boyer-Moore helper functions --- */
|
||||
|
||||
/* Calculates the first table for use in a Boyer-Moore search algorithm.
|
||||
* Table is in the form [colors: shift_values], where colors are those in
|
||||
* |needle|, and the shift values are each color's distance from the rightmost
|
||||
* offset. All other colors are assumed to have a shift value equal to the
|
||||
* length of needle.
|
||||
*/
|
||||
static void initBadShiftTable(UTHashTable *jumpTable, MMBitmapRef needle);
|
||||
|
||||
/* Frees memory occupied by calling initBadShiftTable().
|
||||
* Currently this is just an alias for destroyHashTable(). */
|
||||
#define destroyBadShiftTable(jumpTable) destroyHashTable(jumpTable)
|
||||
|
||||
/* Returns true if |needle| is found in |haystack| at |offset|. */
|
||||
static int needleAtOffset(MMBitmapRef needle, MMBitmapRef haystack,
|
||||
MMPoint offset, float tolerance);
|
||||
/* --- --- */
|
||||
|
||||
/* An modification of the Boyer-Moore-Horspool Algorithm, only applied to
|
||||
* bitmaps and colors instead of strings and characters.
|
||||
*
|
||||
* TODO: The Boyer-Moore algorithm (with the second jump table) would probably
|
||||
* be more efficient, but this was simpler (for now).
|
||||
*
|
||||
* The jump table (|badShiftTable|) is passed as a parameter to avoid being
|
||||
* recalculated each time. It should be a pointer to a UTHashTable init'd with
|
||||
* initBadShiftTable().
|
||||
*
|
||||
* Returns 0 and sets |point| to the starting point of |needle| in |haystack|
|
||||
* if |needle| was found in |haystack|, or returns -1 if not. */
|
||||
static int findBitmapInRectAt(MMBitmapRef needle,
|
||||
MMBitmapRef haystack,
|
||||
MMPoint *point,
|
||||
MMRect rect,
|
||||
float tolerance,
|
||||
MMPoint startPoint,
|
||||
UTHashTable *badShiftTable)
|
||||
{
|
||||
const size_t scanHeight = rect.size.height - needle->height;
|
||||
const size_t scanWidth = rect.size.width - needle->width;
|
||||
MMPoint pointOffset = startPoint;
|
||||
/* const MMPoint lastPoint = MMPointMake(needle->width - 1, needle->height - 1); */
|
||||
|
||||
/* Sanity check */
|
||||
if (needle->height > haystack->height || needle->width > haystack->width ||
|
||||
!MMBitmapRectInBounds(haystack, rect)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(point != NULL);
|
||||
assert(needle != NULL);
|
||||
assert(needle->height > 0 && needle->width > 0);
|
||||
assert(haystack != NULL);
|
||||
assert(haystack->height > 0 && haystack->width > 0);
|
||||
assert(badShiftTable != NULL);
|
||||
|
||||
/* Search |haystack|, while |needle| can still be within it. */
|
||||
while (pointOffset.y <= scanHeight) {
|
||||
/* struct shiftNode *node = NULL;
|
||||
MMRGBHex lastColor; */
|
||||
|
||||
while (pointOffset.x <= scanWidth) {
|
||||
/* Check offset in |haystack| for |needle|. */
|
||||
if (needleAtOffset(needle, haystack, pointOffset, tolerance)) {
|
||||
// ++pointOffset.x;
|
||||
// ++pointOffset.y;
|
||||
*point = pointOffset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Otherwise, calculate next x offset to check. */
|
||||
/*
|
||||
* Note that here we are getting the skip value based on the last
|
||||
* color of |needle|, no matter where we didn't match. The
|
||||
* alternative of pretending that the mismatched color was the previous
|
||||
* color is slower in the normal case.
|
||||
*/
|
||||
/* lastColor = MMRGBHexAtPoint(haystack, pointOffset.x + lastPoint.x,
|
||||
pointOffset.y + lastPoint.y); */
|
||||
|
||||
/* TODO: This fails on certain edge cases (issue#7). */
|
||||
/* When a color is encountered that does not occur in |needle|, we can
|
||||
* safely skip ahead for the whole length of |needle|.
|
||||
* Otherwise, use the value stored in the jump table. */
|
||||
/* node = nodeForColor(badShiftTable, lastColor);
|
||||
pointOffset.x += (node == NULL) ? needle->width : (node->offset).x; */
|
||||
|
||||
/* For now, be naive. */
|
||||
++pointOffset.x;
|
||||
}
|
||||
|
||||
pointOffset.x = rect.origin.x;
|
||||
|
||||
/* lastColor = MMRGBHexAtPoint(haystack, pointOffset.x + lastPoint.x,
|
||||
pointOffset.y + lastPoint.y);
|
||||
node = nodeForColor(badShiftTable, lastColor);
|
||||
pointOffset.y += node == NULL ? lastPoint.y : (node->offset).y; */
|
||||
|
||||
/* TODO: The above commented out code fails at certain edge cases, e.g.:
|
||||
* Needle: [B, b
|
||||
* b, b,
|
||||
* B, b]
|
||||
* Haystack: [w, w, w, w, w
|
||||
* w, w, w, w, b
|
||||
* w, w, w, b, b
|
||||
* w, w, w, w, b]
|
||||
* The previous algorithm noticed that the first 3 x 3 block had nothing
|
||||
* in common with the image, and thus, after scanning the first row,
|
||||
* skipped three blocks downward to scan the next (which didn't exist,
|
||||
* so the loop ended). However, the needle was hidden IN-BETWEEN this
|
||||
* jump -- skipping was appropriate for scanning the column but not
|
||||
* the row.
|
||||
*
|
||||
* I need to figure out a more optimal solution; temporarily I am just
|
||||
* scanning every single y coordinate, only skipping on x's. This
|
||||
* always works, but is probably not optimal.
|
||||
*/
|
||||
++pointOffset.y;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int findBitmapInRect(MMBitmapRef needle,
|
||||
MMBitmapRef haystack,
|
||||
MMPoint *point,
|
||||
MMRect rect,
|
||||
float tolerance)
|
||||
{
|
||||
UTHashTable badShiftTable;
|
||||
int ret;
|
||||
|
||||
initBadShiftTable(&badShiftTable, needle);
|
||||
ret = findBitmapInRectAt(needle, haystack, point, rect,
|
||||
tolerance, MMPointZero, &badShiftTable);
|
||||
destroyBadShiftTable(&badShiftTable);
|
||||
return ret;
|
||||
}
|
||||
|
||||
MMPointArrayRef findAllBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,
|
||||
MMRect rect, float tolerance)
|
||||
{
|
||||
MMPointArrayRef pointArray = createMMPointArray(0);
|
||||
MMPoint point = MMPointZero;
|
||||
UTHashTable badShiftTable;
|
||||
|
||||
initBadShiftTable(&badShiftTable, needle);
|
||||
while (findBitmapInRectAt(needle, haystack, &point, rect,
|
||||
tolerance, point, &badShiftTable) == 0) {
|
||||
const size_t scanWidth = (haystack->width - needle->width) + 1;
|
||||
MMPointArrayAppendPoint(pointArray, point);
|
||||
ITER_NEXT_POINT(point, scanWidth, 0);
|
||||
}
|
||||
destroyBadShiftTable(&badShiftTable);
|
||||
|
||||
return pointArray;
|
||||
}
|
||||
|
||||
size_t countOfBitmapInRect(MMBitmapRef needle, MMBitmapRef haystack,
|
||||
MMRect rect, float tolerance)
|
||||
{
|
||||
size_t count = 0;
|
||||
MMPoint point = MMPointZero;
|
||||
UTHashTable badShiftTable;
|
||||
|
||||
initBadShiftTable(&badShiftTable, needle);
|
||||
while (findBitmapInRectAt(needle, haystack, &point, rect,
|
||||
tolerance, point, &badShiftTable) == 0) {
|
||||
const size_t scanWidth = (haystack->width - needle->width) + 1;
|
||||
++count;
|
||||
ITER_NEXT_POINT(point, scanWidth, 0);
|
||||
}
|
||||
destroyBadShiftTable(&badShiftTable);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* --- Boyer-Moore helper functions --- */
|
||||
|
||||
static void initBadShiftTable(UTHashTable *jumpTable, MMBitmapRef needle)
|
||||
{
|
||||
const MMPoint lastPoint = MMPointMake(needle->width - 1, needle->height - 1);
|
||||
const size_t maxColors = needle->width * needle->height;
|
||||
MMPoint scan;
|
||||
|
||||
/* Allocate max size initially to avoid a million calls to malloc(). */
|
||||
initHashTable(jumpTable, maxColors, sizeof(struct shiftNode));
|
||||
|
||||
/* Populate jumpTable with analysis of |needle|. */
|
||||
for (scan.y = lastPoint.y; ; --scan.y) {
|
||||
for (scan.x = lastPoint.x; ; --scan.x) {
|
||||
MMRGBHex color = MMRGBHexAtPoint(needle, scan.x, scan.y);
|
||||
if (!tableHasKey(jumpTable, color)) {
|
||||
addNodeToTable(jumpTable, color,
|
||||
MMPointMake(needle->width - scan.x,
|
||||
needle->height - scan.y));
|
||||
}
|
||||
|
||||
if (scan.x == 0) break; /* Avoid infinite loop from unsigned type. */
|
||||
}
|
||||
if (scan.y == 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
static int needleAtOffset(MMBitmapRef needle, MMBitmapRef haystack,
|
||||
MMPoint offset, float tolerance)
|
||||
{
|
||||
const MMPoint lastPoint = MMPointMake(needle->width - 1, needle->height - 1);
|
||||
MMPoint scan;
|
||||
|
||||
/* Note that |needle| is searched backwards, in accordance with the
|
||||
* Boyer-Moore search algorithm. */
|
||||
for (scan.y = lastPoint.y; ; --scan.y) {
|
||||
for (scan.x = lastPoint.x; ; --scan.x) {
|
||||
MMRGBHex ncolor = MMRGBHexAtPoint(needle, scan.x, scan.y);
|
||||
MMRGBHex hcolor = MMRGBHexAtPoint(haystack, offset.x + scan.x,
|
||||
offset.y + scan.y);
|
||||
if (!MMRGBHexSimilarToColor(ncolor, hcolor, tolerance)) return 0;
|
||||
if (scan.x == 0) break; /* Avoid infinite loop from unsigned type. */
|
||||
}
|
||||
if (scan.y == 0) break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* --- Hash table helper functions --- */
|
||||
|
||||
static void addNodeToTable(UTHashTable *table,
|
||||
MMRGBHex hexColor,
|
||||
MMPoint offset)
|
||||
{
|
||||
struct shiftNode *node = getNewNode(table);
|
||||
node->color = hexColor;
|
||||
node->offset = offset;
|
||||
UTHASHTABLE_ADD_INT(table, color, node, struct shiftNode);
|
||||
}
|
||||
|
||||
static struct shiftNode *nodeForColor(UTHashTable *table,
|
||||
MMRGBHex color)
|
||||
{
|
||||
struct shiftNode *uttable = table->uttable;
|
||||
struct shiftNode *node;
|
||||
HASH_FIND_INT(uttable, &color, node);
|
||||
return node;
|
||||
}
|
@ -1,134 +0,0 @@
|
||||
// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// https://github.com/go-vgo/robotgo/blob/master/LICENSE
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// class BMP
|
||||
// {
|
||||
// public:
|
||||
// size_t width;
|
||||
// size_t height;
|
||||
// size_t byteWidth;
|
||||
// uint8_t bitsPerPixel;
|
||||
// uint8_t bytesPerPixel;
|
||||
// uint8_t *image;
|
||||
// };
|
||||
#include "bitmap_class.h"
|
||||
#include "bitmap_find_c.h"
|
||||
#include "../base/color_find_c.h"
|
||||
// #include "../screen/screen_c.h"
|
||||
#include "../base/io_c.h"
|
||||
#include "../base/pasteboard_c.h"
|
||||
#include "../base/str_io_c.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* Returns false and sets error if |bitmap| is NULL. */
|
||||
bool bitmap_ready(MMBitmapRef bitmap){
|
||||
if (bitmap == NULL || bitmap->imageBuffer == NULL) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void bitmap_dealloc(MMBitmapRef bitmap){
|
||||
if (bitmap != NULL) {
|
||||
destroyMMBitmap(bitmap);
|
||||
bitmap = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool bitmap_copy_to_pboard(MMBitmapRef bitmap){
|
||||
MMPasteError err;
|
||||
|
||||
if (!bitmap_ready(bitmap)) return false;
|
||||
if ((err = copyMMBitmapToPasteboard(bitmap)) != kMMPasteNoError) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MMBitmapRef bitmap_deepcopy(MMBitmapRef bitmap){
|
||||
return bitmap == NULL ? NULL : copyMMBitmap(bitmap);
|
||||
}
|
||||
|
||||
MMPoint bitmap_find_bitmap(MMBitmapRef bitmap, MMBitmapRef sbitmap, float tolerance){
|
||||
MMPoint point = {-1, -1};
|
||||
// printf("tolenrance=%f\n", tolerance);
|
||||
if (!bitmap_ready(sbitmap) || !bitmap_ready(bitmap)) {
|
||||
printf("bitmap is not ready yet!\n");
|
||||
return point;
|
||||
}
|
||||
|
||||
MMRect rect = MMBitmapGetBounds(sbitmap);
|
||||
// printf("x=%d,y=%d,width=%d,height=%d\n", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
|
||||
|
||||
if (findBitmapInRect(bitmap, sbitmap, &point,
|
||||
rect, tolerance) == 0) {
|
||||
return point;
|
||||
}
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
MMPoint aFindBitmap(MMBitmapRef bit_map, MMRect rect){
|
||||
// MMRect rect;
|
||||
// rect.size.width = 10;
|
||||
// rect.size.height = 20;
|
||||
// rect.origin.x = 10;
|
||||
// rect.origin.y = 20;
|
||||
|
||||
float tolerance = 0.0f;
|
||||
MMPoint point;
|
||||
|
||||
tolerance = 0.5;
|
||||
|
||||
if (findBitmapInRect(bit_map, bit_map, &point,
|
||||
rect, tolerance) == 0) {
|
||||
return point;
|
||||
}
|
||||
return point;
|
||||
}
|
||||
|
||||
MMBitmapRef bitmap_open(char *path, uint16_t ttype){
|
||||
// MMImageType type;
|
||||
|
||||
MMBitmapRef bitmap;
|
||||
MMIOError err;
|
||||
|
||||
bitmap = newMMBitmapFromFile(path, ttype, &err);
|
||||
// printf("....%zd\n", bitmap->width);
|
||||
return bitmap;
|
||||
|
||||
}
|
||||
|
||||
char *bitmap_save(MMBitmapRef bitmap, char *path, uint16_t type){
|
||||
if (saveMMBitmapToFile(bitmap, path, (MMImageType) type) != 0) {
|
||||
return "Could not save image to file.";
|
||||
}
|
||||
//destroyMMBitmap(bitmap);
|
||||
return "ok";
|
||||
}
|
||||
|
||||
char *aTostringBitmap(MMBitmapRef bitmap){
|
||||
char *buf = NULL;
|
||||
MMBMPStringError err;
|
||||
|
||||
buf = (char *)createStringFromMMBitmap(bitmap, &err);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
MMBitmapRef aGetPortion(MMBitmapRef bit_map, MMRect rect){
|
||||
// MMRect rect;
|
||||
MMBitmapRef portion = NULL;
|
||||
|
||||
portion = copyMMBitmapFromPortion(bit_map, rect);
|
||||
return portion;
|
||||
}
|
30
circle.yml
30
circle.yml
@ -1,30 +0,0 @@
|
||||
# circle.yml #
|
||||
# machine:
|
||||
# go:
|
||||
# version: 1.7.4
|
||||
|
||||
# customize dependencies
|
||||
dependencies:
|
||||
pre:
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install libx11-dev
|
||||
# - sudo apt-get install libgtkglextmm-x11-dev
|
||||
# - sudo apt-get install libghc6-x11-dev
|
||||
# - sudo apt-get install libgl1-mesa-swx11-dev
|
||||
- sudo apt-get install xorg-dev
|
||||
- sudo apt-get install libxtst-dev libpng++-dev
|
||||
# Event:
|
||||
- sudo apt-get install xcb libxcb-xkb-dev x11-xkb-utils libx11-xcb-dev libxkbcommon-x11-dev
|
||||
- sudo apt-get install libxkbcommon-dev
|
||||
# Clipboard:
|
||||
- sudo apt-get install xsel
|
||||
- sudo apt-get install xclip
|
||||
override:
|
||||
# './...' is a relative pattern which means all subdirectories
|
||||
- go get -u github.com/go-vgo/robotgo
|
||||
- go get -u github.com/shirou/gopsutil
|
||||
- go get -t -d -v ./...
|
||||
|
||||
test:
|
||||
post:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
@ -1,18 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- go1.2.2
|
||||
- go1.3.3
|
||||
- go1.4.3
|
||||
- go1.5.3
|
||||
- go1.6
|
||||
|
||||
before_install:
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
|
||||
script:
|
||||
- sudo apt-get install xsel
|
||||
- go test -v .
|
||||
- sudo apt-get install xclip
|
||||
- go test -v .
|
@ -1,27 +0,0 @@
|
||||
Copyright (c) 2013 Ato Araki. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of @atotto. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -2,7 +2,9 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package clipboard read/write on clipboard
|
||||
/*
|
||||
Package clipboard read/write on clipboard
|
||||
*/
|
||||
package clipboard
|
||||
|
||||
// import ()
|
||||
@ -17,6 +19,7 @@ func WriteAll(text string) error {
|
||||
return writeAll(text)
|
||||
}
|
||||
|
||||
// Unsupported might be set true during clipboard init, to help callers decide
|
||||
// whether or not to offer clipboard options.
|
||||
// Unsupported might be set true during clipboard init,
|
||||
// to help callers decide whether or not to
|
||||
// offer clipboard options.
|
||||
var Unsupported bool
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build darwin
|
||||
// +build darwin
|
||||
|
||||
package clipboard
|
||||
@ -48,5 +49,6 @@ func writeAll(text string) error {
|
||||
if err := in.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return copyCmd.Wait()
|
||||
}
|
||||
|
@ -2,72 +2,75 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build darwin || windows
|
||||
// +build darwin windows
|
||||
|
||||
package clipboard_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/go-vgo/robotgo/clipboard"
|
||||
"github.com/go-vgo/robotgo/clipboard"
|
||||
)
|
||||
|
||||
// func TestCopyAndPaste(t *testing.T) {
|
||||
// expected := "日本語"
|
||||
func TestCopyAndPaste(t *testing.T) {
|
||||
expected := "日本語"
|
||||
|
||||
// err := WriteAll(expected)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
err := clipboard.WriteAll(expected)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// actual, err := ReadAll()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
actual, err := clipboard.ReadAll()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// if actual != expected {
|
||||
// t.Errorf("want %s, got %s", expected, actual)
|
||||
// }
|
||||
// }
|
||||
if actual != expected {
|
||||
t.Errorf("want %s, got %s", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
// func TestMultiCopyAndPaste(t *testing.T) {
|
||||
// expected1 := "French: éèêëàùœç"
|
||||
// expected2 := "Weird UTF-8: 💩☃"
|
||||
func TestMultiCopyAndPaste(t *testing.T) {
|
||||
expected1 := "French: éèêëàùœç"
|
||||
expected2 := "Weird UTF-8: 💩☃"
|
||||
|
||||
// err := WriteAll(expected1)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
err := clipboard.WriteAll(expected1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// actual1, err := ReadAll()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// if actual1 != expected1 {
|
||||
// t.Errorf("want %s, got %s", expected1, actual1)
|
||||
// }
|
||||
actual1, err := clipboard.ReadAll()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if actual1 != expected1 {
|
||||
t.Errorf("want %s, got %s", expected1, actual1)
|
||||
}
|
||||
|
||||
// err = WriteAll(expected2)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
err = clipboard.WriteAll(expected2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// actual2, err := ReadAll()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// if actual2 != expected2 {
|
||||
// t.Errorf("want %s, got %s", expected2, actual2)
|
||||
// }
|
||||
// }
|
||||
actual2, err := clipboard.ReadAll()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if actual2 != expected2 {
|
||||
t.Errorf("want %s, got %s", expected2, actual2)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkReadAll(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ReadAll()
|
||||
clipboard.ReadAll()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkWriteAll(b *testing.B) {
|
||||
text := "いろはにほへと"
|
||||
for i := 0; i < b.N; i++ {
|
||||
WriteAll(text)
|
||||
clipboard.WriteAll(text)
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build freebsd linux netbsd openbsd solaris
|
||||
//go:build freebsd || linux || netbsd || openbsd || solaris || dragonfly
|
||||
// +build freebsd linux netbsd openbsd solaris dragonfly
|
||||
|
||||
package clipboard
|
||||
|
||||
@ -17,8 +18,10 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
pasteCmdArgs []string
|
||||
copyCmdArgs []string
|
||||
// Primary choose primary mode on unix
|
||||
Primary bool
|
||||
|
||||
pasteCmdArgs, copyCmdArgs []string
|
||||
|
||||
xselPasteArgs = []string{xsel, "--output", "--clipboard"}
|
||||
xselCopyArgs = []string{xsel, "--input", "--clipboard"}
|
||||
@ -26,7 +29,7 @@ var (
|
||||
xclipPasteArgs = []string{xclip, "-out", "-selection", "clipboard"}
|
||||
xclipCopyArgs = []string{xclip, "-in", "-selection", "clipboard"}
|
||||
|
||||
missingCommands = errors.New("No clipboard utilities available. Please install xsel or xclip.")
|
||||
errMissingCommands = errors.New("No clipboard utilities available. Please install xsel or xclip")
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -48,28 +51,36 @@ func init() {
|
||||
}
|
||||
|
||||
func getPasteCommand() *exec.Cmd {
|
||||
if Primary {
|
||||
pasteCmdArgs = pasteCmdArgs[:1]
|
||||
}
|
||||
return exec.Command(pasteCmdArgs[0], pasteCmdArgs[1:]...)
|
||||
}
|
||||
|
||||
func getCopyCommand() *exec.Cmd {
|
||||
if Primary {
|
||||
copyCmdArgs = copyCmdArgs[:1]
|
||||
}
|
||||
return exec.Command(copyCmdArgs[0], copyCmdArgs[1:]...)
|
||||
}
|
||||
|
||||
func readAll() (string, error) {
|
||||
if Unsupported {
|
||||
return "", missingCommands
|
||||
return "", errMissingCommands
|
||||
}
|
||||
|
||||
pasteCmd := getPasteCommand()
|
||||
out, err := pasteCmd.Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
func writeAll(text string) error {
|
||||
if Unsupported {
|
||||
return missingCommands
|
||||
return errMissingCommands
|
||||
}
|
||||
copyCmd := getCopyCommand()
|
||||
in, err := copyCmd.StdinPipe()
|
||||
@ -86,5 +97,6 @@ func writeAll(text string) error {
|
||||
if err := in.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return copyCmd.Wait()
|
||||
}
|
||||
|
@ -2,18 +2,21 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package clipboard
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
cfUnicodetext = 13
|
||||
gmemFixed = 0x0000
|
||||
// gmemFixed = 0x0000
|
||||
gmemMoveable = 0x0002
|
||||
)
|
||||
|
||||
var (
|
||||
@ -32,15 +35,35 @@ var (
|
||||
lstrcpy = kernel32.NewProc("lstrcpyW")
|
||||
)
|
||||
|
||||
// waitOpenClipboard opens the clipboard, waiting for up to a second to do so.
|
||||
func waitOpenClipboard() error {
|
||||
started := time.Now()
|
||||
limit := started.Add(time.Second)
|
||||
var (
|
||||
r uintptr
|
||||
err error
|
||||
)
|
||||
for time.Now().Before(limit) {
|
||||
r, _, err = openClipboard.Call(0)
|
||||
if r != 0 {
|
||||
return nil
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func readAll() (string, error) {
|
||||
r, _, err := openClipboard.Call(0)
|
||||
if r == 0 {
|
||||
// r, _, err := openClipboard.Call(0)
|
||||
err := waitOpenClipboard()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer closeClipboard.Call()
|
||||
|
||||
h, _, err := getClipboardData.Call(cfUnicodetext)
|
||||
if r == 0 {
|
||||
if h == 0 {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@ -51,7 +74,7 @@ func readAll() (string, error) {
|
||||
|
||||
text := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(l))[:])
|
||||
|
||||
r, _, err = globalUnlock.Call(h)
|
||||
r, _, err := globalUnlock.Call(h)
|
||||
if r == 0 {
|
||||
return "", err
|
||||
}
|
||||
@ -60,24 +83,33 @@ func readAll() (string, error) {
|
||||
}
|
||||
|
||||
func writeAll(text string) error {
|
||||
r, _, err := openClipboard.Call(0)
|
||||
if r == 0 {
|
||||
err := waitOpenClipboard()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer closeClipboard.Call()
|
||||
|
||||
r, _, err = emptyClipboard.Call(0)
|
||||
r, _, err := emptyClipboard.Call(0)
|
||||
if r == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
data := syscall.StringToUTF16(text)
|
||||
|
||||
h, _, err := globalAlloc.Call(gmemFixed, uintptr(len(data)*int(unsafe.Sizeof(data[0]))))
|
||||
// "If the hMem parameter identifies a memory object, the object must have
|
||||
// been allocated using the function with the GMEM_MOVEABLE flag."
|
||||
h, _, err := globalAlloc.Call(gmemMoveable,
|
||||
uintptr(len(data)*int(unsafe.Sizeof(data[0]))))
|
||||
if h == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if h != 0 {
|
||||
globalFree.Call(h)
|
||||
}
|
||||
}()
|
||||
|
||||
l, _, err := globalLock.Call(h)
|
||||
if l == 0 {
|
||||
return err
|
||||
@ -90,12 +122,16 @@ func writeAll(text string) error {
|
||||
|
||||
r, _, err = globalUnlock.Call(h)
|
||||
if r == 0 {
|
||||
return err
|
||||
if err.(syscall.Errno) != 0 {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
r, _, err = setClipboardData.Call(cfUnicodetext, h)
|
||||
if r == 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
h = 0 // suppress deferred cleanup
|
||||
return nil
|
||||
}
|
||||
|
@ -1,15 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/go-vgo/robotgo/clipboard"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
out, err := ioutil.ReadAll(os.Stdin)
|
||||
out, err := io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
25
clipboard/example/example.go
Normal file
25
clipboard/example/example.go
Normal file
@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/go-vgo/robotgo/clipboard"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := clipboard.WriteAll("日本語")
|
||||
if err != nil {
|
||||
log.Println("clipboard write all error: ", err)
|
||||
}
|
||||
|
||||
text, err := clipboard.ReadAll()
|
||||
if err != nil {
|
||||
log.Println("clipboard read all error: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
if text != "" {
|
||||
log.Println("text is: ", text)
|
||||
// Output: 日本語
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package clipboard_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-vgo/robotgo/clipboard"
|
||||
)
|
||||
|
||||
func Example() {
|
||||
clipboard.WriteAll("日本語")
|
||||
text, err := clipboard.ReadAll()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
} else {
|
||||
fmt.Println(text)
|
||||
// Output:
|
||||
// 日本語
|
||||
}
|
||||
}
|
2
cv/README.md
Normal file
2
cv/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
robotgo gocv
|
||||
|
1
cv/gocv.go
Normal file
1
cv/gocv.go
Normal file
@ -0,0 +1 @@
|
||||
package cv
|
119
doc.go
Normal file
119
doc.go
Normal file
@ -0,0 +1,119 @@
|
||||
// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// https://github.com/go-vgo/robotgo/blob/master/LICENSE
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0>
|
||||
//
|
||||
// This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
package robotgo
|
||||
|
||||
/*
|
||||
Keys are supported:
|
||||
"A-Z a-z 0-9"
|
||||
|
||||
"backspace"
|
||||
"delete"
|
||||
"enter"
|
||||
"tab"
|
||||
"esc"
|
||||
"escape"
|
||||
"up" Up arrow key
|
||||
"down" Down arrow key
|
||||
"right" Right arrow key
|
||||
"left" Left arrow key
|
||||
"home"
|
||||
"end"
|
||||
"pageup"
|
||||
"pagedown"
|
||||
|
||||
"f1"
|
||||
"f2"
|
||||
"f3"
|
||||
"f4"
|
||||
"f5"
|
||||
"f6"
|
||||
"f7"
|
||||
"f8"
|
||||
"f9"
|
||||
"f10"
|
||||
"f11"
|
||||
"f12"
|
||||
"f13"
|
||||
"f14"
|
||||
"f15"
|
||||
"f16"
|
||||
"f17"
|
||||
"f18"
|
||||
"f19"
|
||||
"f20"
|
||||
"f21"
|
||||
"f22"
|
||||
"f23"
|
||||
"f24"
|
||||
|
||||
"cmd" this is the "win" key for windows
|
||||
"lcmd" left command
|
||||
"rcmd" right command
|
||||
// "command"
|
||||
"alt"
|
||||
"lalt" left alt
|
||||
"ralt" right alt
|
||||
"ctrl"
|
||||
"lctrl" left ctrl
|
||||
"rctrl" right ctrl
|
||||
"control"
|
||||
"shift"
|
||||
"lshift" left shift
|
||||
"rshift" right shift
|
||||
// "right_shift"
|
||||
"capslock"
|
||||
"space"
|
||||
"print"
|
||||
"printscreen" // No Mac support
|
||||
"insert"
|
||||
"menu" Windows only
|
||||
|
||||
"audio_mute" Mute the volume
|
||||
"audio_vol_down" Lower the volume
|
||||
"audio_vol_up" Increase the volume
|
||||
"audio_play"
|
||||
"audio_stop"
|
||||
"audio_pause"
|
||||
"audio_prev" Previous Track
|
||||
"audio_next" Next Track
|
||||
"audio_rewind" Linux only
|
||||
"audio_forward" Linux only
|
||||
"audio_repeat" Linux only
|
||||
"audio_random" Linux only
|
||||
|
||||
|
||||
"num0"
|
||||
"num1"
|
||||
"num2"
|
||||
"num3"
|
||||
"num4"
|
||||
"num5"
|
||||
"num6"
|
||||
"num7"
|
||||
"num8"
|
||||
"num9"
|
||||
"num_lock"
|
||||
|
||||
"num."
|
||||
"num+"
|
||||
"num-"
|
||||
"num*"
|
||||
"num/"
|
||||
"num_clear"
|
||||
"num_enter"
|
||||
"num_equal"
|
||||
|
||||
"lights_mon_up" Turn up monitor brightness No Windows support
|
||||
"lights_mon_down" Turn down monitor brightness No Windows support
|
||||
"lights_kbd_toggle" Toggle keyboard backlight on/off No Windows support
|
||||
"lights_kbd_up" Turn up keyboard backlight brightness No Windows support
|
||||
"lights_kbd_down" Turn down keyboard backlight brightness No Windows support
|
||||
*/
|
@ -1,41 +1,841 @@
|
||||
# CHANGELOG
|
||||
|
||||
<!--### RobotGo-->
|
||||
|
||||
## RobotGo v0.45.0, Mount Qomolangma
|
||||
## RobotGo v0.100.0, MT. Baker; Enhancement bitmap and image, add arm support...
|
||||
|
||||
### Add
|
||||
- Add Process
|
||||
- Add TypeStr
|
||||
- Add DeepCopyBit
|
||||
- Add CopyBitpb
|
||||
- Add ReadBitmap
|
||||
- Add vendor.json
|
||||
- Add ReadAll: clipboard
|
||||
- Add WriteAll: clipboard
|
||||
- Add Pids : get the all process id
|
||||
- Add FindName: find the process name by the process id
|
||||
- Add FindNames: find the all process name
|
||||
- Add PidExists: determine whether the process exists
|
||||
- Add FindIds: find the process id by the process name
|
||||
- Add FreeBitmap and Update docs
|
||||
|
||||
- [NEW] add more image function support
|
||||
- [NEW] add ImgToBitmap(), ToImg(), FindEveryBitmap(), FindEveryColor(), Svae(), Read(), SaveJpeg() and other function support
|
||||
|
||||
- [NEW] add func ToImage: convert C.MMBitmapRef to standard image.Image
|
||||
- [NEW] add ToImage examples code
|
||||
- [NEW] add more image function and update go mod
|
||||
|
||||
- [NEW] add find every color function
|
||||
- [NEW] add free and find all point function
|
||||
- [NEW] update find bitmap and color
|
||||
|
||||
- [NEW] add byte to image function
|
||||
- [NEW] add more image convert function
|
||||
|
||||
- [NEW] add mac os M1 support
|
||||
- [NEW] add windows arm support
|
||||
|
||||
- [NEW] add more key toggle and press function
|
||||
|
||||
- [NEW] add ToRGBA() function support
|
||||
- [NEW] add ImgToBitmap and RGBAToBitmap support
|
||||
- [NEW] Update and move image function to img.go
|
||||
|
||||
- [NEW] add more img to bitmap examples code and Update file name
|
||||
|
||||
### Update
|
||||
|
||||
- [NEW] Update README.md and CHANGELOG.md
|
||||
- [NEW] add macOS to .travis.yml
|
||||
- [NEW] update go mod pkg
|
||||
|
||||
- [NEW] Update hook examples link to newest
|
||||
- [NEW] update dockerfile and appveyor to go1.14.4
|
||||
|
||||
- [NEW] Update README.md, add more examples
|
||||
- [NEW] move hook and event to gohook
|
||||
- [NEW] move cbitmap and bitmap-bytes to bitmap dir
|
||||
|
||||
- [NEW] update some name
|
||||
- [NEW] update dockerfile and appveyor.yml
|
||||
|
||||
- [NEW] update clipboard code
|
||||
- [NEW] update hook code and more API
|
||||
|
||||
- [NEW] optimize code and update version
|
||||
- [NEW] add paste string err return and optimize code
|
||||
- [NEW] update go.yml and travis.yml to go1.15
|
||||
- [NEW] Update Ubuntu apt-get to apt
|
||||
|
||||
- [NEW] update go version and key code
|
||||
- [NEW] update test code and go mod
|
||||
- [NEW] Update README.md and test code
|
||||
|
||||
- [NEW] update parameter name and version
|
||||
- [NEW] update dockerfile and appveyor.yml
|
||||
- [NEW] update error return and print
|
||||
|
||||
- [NEW] update ShowAlert optimize return code
|
||||
- [NEW] add more test and update go mod
|
||||
|
||||
- [NEW] compatible with 32-bit platforms
|
||||
- [NEW] add more bitmap examples
|
||||
|
||||
- [NEW] update point structure to public
|
||||
|
||||
- [NEW] add more examples
|
||||
- [NEW] update examples and version
|
||||
- [NEW] Update clipboard example code
|
||||
|
||||
- [NEW] Update README.md Section ####Other to windows (#348) …
|
||||
- [NEW] Update png.h path
|
||||
- [NEW] Update go mod
|
||||
- [NEW] Update circle.yml and travis.yml
|
||||
|
||||
- [NEW] Remove unless example code and update circle.yml
|
||||
- [NEW] Removed drop api example code and Update README.md
|
||||
|
||||
- [NEW] Update go mod and xx.yml
|
||||
- [NEW] Update README.md and example
|
||||
|
||||
- [NEW] add more bitmap examples code
|
||||
- [NEW] Update go mod and Update README.md
|
||||
- [NEW] gofmt to 1.17 build tag
|
||||
- [NEW] Update bitmap examples code
|
||||
|
||||
- [NEW] Update version and keycode
|
||||
- [NEW] Update docs remove drop API
|
||||
|
||||
|
||||
### Fixed
|
||||
|
||||
- [FIX] Update go mod and fixed #290
|
||||
- [FIX] Update gohook to v0.30.2 fixed bug
|
||||
- [FIX] Fixed Mouse buttons reversed type
|
||||
- [FIX] Fixed returns "Invalid key code specified." if specified character is not v… … add keyCodeForCharFallBack
|
||||
|
||||
- [FIX] This fixes the disappearing backslash issue #351
|
||||
- [FIX] Export ToUC function and update test code
|
||||
- [FIX] Fixes #258: char* arrays in C not being copied correctly
|
||||
- [FIX] Fixed Linux TypeStr() function double quote
|
||||
|
||||
- [FIX] update free bitmap fixed #333
|
||||
- [FIX] update gops to v0.20.0 fixed bug and other mod pkg
|
||||
- [FIX] update gohook fixed warning
|
||||
|
||||
|
||||
|
||||
## RobotGo v0.90.0, MT. Rainier
|
||||
|
||||
### Add
|
||||
|
||||
add gohook modern and concurrent API
|
||||
add new gohook examples, thks for cauefcr
|
||||
|
||||
Support for multiple screens
|
||||
add getMousePos() multiple screens support
|
||||
add move smooth multiple screens support
|
||||
|
||||
add all platform system scale support
|
||||
add get screen size test code
|
||||
|
||||
add screen and bitmap multiple screens support
|
||||
add int32_t types support
|
||||
|
||||
update keycode type use uint16 with gohook, not type convert
|
||||
add ToBitmapBytes func (#204)
|
||||
|
||||
gohook: sched_yield support for non-POSIX windows gcc
|
||||
|
||||
add gops test code support
|
||||
add Process() function test code
|
||||
add more gops test code
|
||||
|
||||
add more win32 function export
|
||||
|
||||
add get mouse color function
|
||||
|
||||
add uint32 to Chex function support
|
||||
|
||||
add key_Toggles() c function
|
||||
add keyTap and keyToggle "...string" parameters support, Fixed #209
|
||||
|
||||
add robotgo simple test code
|
||||
|
||||
add Is64Bit() c and go function
|
||||
|
||||
add process FindPath() function
|
||||
|
||||
add keycode "delete" support and fixed "\\" error
|
||||
add more keycode support, "up, down, left, right"...
|
||||
|
||||
export hook keycode and add godoc
|
||||
|
||||
use robotn fork xgb and update go mod
|
||||
|
||||
add hook example to robotgo examples
|
||||
|
||||
update gohook and tt mod file
|
||||
|
||||
add more and update test code
|
||||
|
||||
add drag smooth function support and examples
|
||||
|
||||
add ShowAlert() test support
|
||||
|
||||
update keypress rand sleep [reduce] and update code style, update c delay default value to 0
|
||||
|
||||
add mouse toggle return and add more test
|
||||
|
||||
add SetDelay function code and update other code
|
||||
|
||||
add scaled function code
|
||||
|
||||
add go opencv file
|
||||
|
||||
add readme.md file
|
||||
|
||||
add move mouse and move smooth relative code
|
||||
|
||||
add move mouse and move smooth relative examples
|
||||
|
||||
add more test code and update go tt mod
|
||||
|
||||
add more bitmap test code
|
||||
|
||||
add SaveImg function code
|
||||
|
||||
add drop function hint print support
|
||||
|
||||
add more key test code
|
||||
add more test code
|
||||
add paste string test code
|
||||
add xvfb run codecov test
|
||||
|
||||
add keycode test support
|
||||
|
||||
add FindPath example code
|
||||
|
||||
add KeyTap() args[2] delay support
|
||||
|
||||
add find bitmap nil args support
|
||||
|
||||
add find color nil args support
|
||||
|
||||
add drag and move mouse multiple screens support
|
||||
|
||||
add drag mouse test code
|
||||
|
||||
Use CGDisplayBounds not CGDisplayPixelsWide, optimize get mac display size …
|
||||
|
||||
Update TypeStr function, add type delay and speed support
|
||||
|
||||
update PasteStr function code return error
|
||||
|
||||
### Update
|
||||
|
||||
Update robot info test code and Add go.yml test support
|
||||
|
||||
use while not for match special key map
|
||||
remove unless x11 special key and sort
|
||||
|
||||
update go mod pkg
|
||||
update mod vendor
|
||||
remove vendor and update .gitignore
|
||||
|
||||
update and fmt config.yml, add Linux go test support
|
||||
update Linux CI support x11 test
|
||||
|
||||
move hook to hook.go
|
||||
|
||||
update appveyor and test code
|
||||
update version and code style
|
||||
|
||||
update move mouse smooth test code
|
||||
|
||||
update clipboard code and add test code
|
||||
|
||||
update test code and add codecov support
|
||||
|
||||
update show alert test code
|
||||
|
||||
update keycode.go
|
||||
|
||||
update window examples code
|
||||
|
||||
update test code remove windows alert test
|
||||
|
||||
move gops code to ps.go
|
||||
|
||||
update version
|
||||
|
||||
update unix get title type
|
||||
|
||||
gofmt go code and update code style
|
||||
|
||||
add ToBitmapBytes examples code
|
||||
|
||||
update example code, fixed golint warning
|
||||
|
||||
update bitmap example code
|
||||
|
||||
Update CHANGELOG.md
|
||||
|
||||
update code style
|
||||
|
||||
update godoc
|
||||
|
||||
update keytap code and code style
|
||||
|
||||
update Bitmap struct delete fuzzy api
|
||||
|
||||
update key examples code
|
||||
|
||||
add bitmap from string clear api
|
||||
|
||||
update go mod vendor
|
||||
update go mod pkg not proxy
|
||||
|
||||
update bitmap example code
|
||||
|
||||
update test code fixed appveyor CI
|
||||
|
||||
update test code fixed equal error
|
||||
|
||||
update hook godoc
|
||||
|
||||
update event example code
|
||||
|
||||
update godoc and code style
|
||||
|
||||
update key example code
|
||||
|
||||
Update example README.md
|
||||
|
||||
update and tidy go mod
|
||||
|
||||
update code remove duplicate code and update godoc
|
||||
|
||||
update xgb getXid log
|
||||
|
||||
update GetBounds x11 error log
|
||||
|
||||
update cgo code and version
|
||||
|
||||
update TypeString function code [Drop]
|
||||
|
||||
update key example code
|
||||
|
||||
Update TypeStr function, optimize x11 type string
|
||||
|
||||
Update TypeStrDelay function, remove unused code
|
||||
|
||||
update code fixed x11 type sleep
|
||||
|
||||
Update key example code
|
||||
|
||||
use gops to simplify code
|
||||
update key examples code
|
||||
|
||||
update bitmap examples code
|
||||
|
||||
update colorpicker and findcolor example code
|
||||
|
||||
update bitmap example code
|
||||
|
||||
update robotgo test code, add more test
|
||||
|
||||
Update README.md
|
||||
|
||||
rename type names make clearer
|
||||
|
||||
update types.h code and fixed bug
|
||||
|
||||
remove unused code fixed x11 build error
|
||||
|
||||
update robot info test code and appveyor
|
||||
|
||||
Update README.md, Add more CI badge
|
||||
|
||||
update gohook pkg and robot info test code
|
||||
|
||||
Update linux upper code, add more special key support
|
||||
|
||||
Create go.yml
|
||||
Update go.yml
|
||||
add more test and update go.yml
|
||||
Update dockerfile to go1.13.5
|
||||
|
||||
update dockerfile and appveyor.yml
|
||||
Update dockerfile and appveyor.yml to go1.14.3
|
||||
|
||||
remove Travis go1.11.x
|
||||
update appveyor and dockerfile to go1.13.1
|
||||
update dockerfile, go.yml and appveyor.yml to go1.14
|
||||
|
||||
update travis.yml to go1.14.x and remove go1.13.x
|
||||
Update and fmt appveyor.ymlu
|
||||
update dockerfile and appveyor to go1.12.5
|
||||
|
||||
update appveyor and dockerfile to go1.12.6
|
||||
|
||||
add CI go1.13 support
|
||||
update config.yml
|
||||
update and fmt travis.yml
|
||||
Update Travis remove go1.12.x
|
||||
|
||||
Update issue and pull request template
|
||||
|
||||
### Fix
|
||||
|
||||
Update to utf-code function Fixed #189
|
||||
|
||||
Update x11 keypress upper code Fixed #243
|
||||
|
||||
type conversion needed in addMouse (#201)
|
||||
|
||||
update hook, Fixed #202 fatal error: concurrent map writes
|
||||
|
||||
add key Kind Fixed #203
|
||||
|
||||
optimize get title code, Fixed #165 and typo
|
||||
|
||||
Fixed gohook#3 mouse is_drag error on x11
|
||||
|
||||
Fixed #213 AddEvents() can't listen correctly multiple times
|
||||
|
||||
update clipboard error hand Fixed #212
|
||||
|
||||
Update go.mod fixing issue "invalid pseudo-version: does not match version …
|
||||
|
||||
update keyboard example code, #238
|
||||
|
||||
update go mod file Fixed #239
|
||||
|
||||
update gops and other mod files fixed bug
|
||||
|
||||
|
||||
## RobotGo v0.80.0, Sierra Nevada
|
||||
|
||||
# Sierra Nevada
|
||||
|
||||
### Add
|
||||
|
||||
- [NEW] Add asynchronous event support
|
||||
- [NEW] Add multiple keypress event listener support
|
||||
- [NEW] Add hook start and end func
|
||||
- [NEW] Add AddEvents, AddMouse, AddMousePos hook function
|
||||
- [NEW] Add mul() scale func and optimize code
|
||||
- [NEW] Refactor AddEvent() func and add keycode.go, update example
|
||||
- [NEW] Add mouse map keycode
|
||||
- [NEW] Add android null file
|
||||
- [NEW] Add AddEvent "center" support
|
||||
- [NEW] Update README.md, Add binding link
|
||||
|
||||
<br/>
|
||||
|
||||
- [NEW] Format README.md and docs markdown
|
||||
- [NEW] Update bitmap_save return code
|
||||
- [NEW] Optimize code not defer and remove useless code
|
||||
- [NEW] Update code style and godoc
|
||||
- [NEW] Update go mod vendor
|
||||
- [NEW] Add more event examples
|
||||
- [NEW] add AddEvents, AddMouse, AddMousePos examples code
|
||||
|
||||
|
||||
### Update
|
||||
- Update docs
|
||||
- Update test
|
||||
- Update godoc
|
||||
- Update CHANGELOG.md
|
||||
- Update .gitignore
|
||||
- Update examples and docs
|
||||
- Update examples link
|
||||
- Update README.md and clipboard
|
||||
|
||||
- [NEW] Update event example code add print hint
|
||||
- [NEW] Update godoc
|
||||
- [NEW] Update CHANGELOG.md
|
||||
- [NEW] Update .gitignore
|
||||
- [NEW] Update code style and examples
|
||||
- [NEW] Update pkg to newest
|
||||
- [NEW] Update CI add go1.12.x support
|
||||
- [NEW] Move GetText() func code
|
||||
|
||||
### Fix
|
||||
|
||||
- [FIX] Add AddEvents func, Fixed #98, #61, #69...
|
||||
- [FIX] Add asynchronous event support, Fixed #196, #89...
|
||||
- [FIX] add AddMouse func, Fixed #138
|
||||
- [FIX] Update _Ctype_char to C.char, Fixed go1.12 build error #191
|
||||
- [FIX] Update hook, Fixed #195 warning and json break bug
|
||||
- [FIX] Fixed color picker, Update README.md and docs
|
||||
|
||||
|
||||
See Commits for more details, after Jan 7.
|
||||
|
||||
|
||||
## RobotGo v0.70.0, Caloris Montes
|
||||
|
||||
# Caloris Montes
|
||||
|
||||
### Add
|
||||
|
||||
- [NEW] Update keyboard use sendInput not keybd_event
|
||||
- [NEW] Update mouse use sendInput not mouse_event
|
||||
- [NEW] Add drag mouse other button support
|
||||
- [NEW] Add more numpad key support
|
||||
- [NEW] Add numpad key and escape abbreviation support
|
||||
- [NEW] Add new window10 zoom ratio
|
||||
- [NEW] Add linux numpad key support
|
||||
- [NEW] Add key "insert, printscreen" mac support
|
||||
- [NEW] Add check mouse button func
|
||||
- [NEW] Add keyTap run error return support and update godoc
|
||||
|
||||
|
||||
<br/>
|
||||
|
||||
- [NEW] Optimize and clearer keytap code
|
||||
- [NEW] Optimize and clean keyToggle code
|
||||
- [NEW] Update dockerfile clean image
|
||||
- [NEW] Add color picker and getMousePos() example
|
||||
- [NEW] Use go mod not dep, add go.mod remove dep files
|
||||
- [NEW] Add GetColors func return string
|
||||
- [NEW] Optimize defer code
|
||||
<br/>
|
||||
|
||||
- [NEW] Add more godoc
|
||||
- [NEW] Add add key "ctrl", "cmd" [ abbreviation ] support
|
||||
- [NEW] Add add key "capslock", "numpad_lock" support
|
||||
- [NEW] Add left and right "Ctrl, Shift, Alt, command" key support
|
||||
- [NEW] Update check key flags support "cmd" and "ctrl"
|
||||
- [NEW] Update key docs
|
||||
- [NEW] Add millisleep func and update godoc
|
||||
- [NEW] Add AddEvent() key "cmd" support
|
||||
- [NEW] Update key example code
|
||||
- [NEW] Update README.md, add Note go1.10.x issue
|
||||
- [NEW] Update keytap and toggle return "" and code style
|
||||
|
||||
|
||||
### Update
|
||||
|
||||
- [NEW] Update issue template more obvious
|
||||
- [NEW] Update godoc
|
||||
- [NEW] Update CHANGELOG.md
|
||||
- [NEW] Update .gitignore
|
||||
- [NEW] Update code style and examples
|
||||
- [NEW] Update pkg to newest
|
||||
- [NEW] Add more scale example
|
||||
- [NEW] Add drag mouse example
|
||||
<br/>
|
||||
|
||||
- [NEW] Update key docs and clear file name
|
||||
- [NEW] Remove old useless code
|
||||
- [NEW] Update README.md
|
||||
- [NEW] Update CI add go1.11.4 version
|
||||
|
||||
### Fix
|
||||
|
||||
- [FIX] Fixed bitmapClick() parameter bug
|
||||
- [FIX] Fixed some README.md typo
|
||||
- [FIX] Update scale example code close #153
|
||||
- [FIX] Update code style and fixed #endif error
|
||||
|
||||
|
||||
See Commits for more details, after Otc 8.
|
||||
|
||||
|
||||
## RobotGo v0.60.0, Mount Olympus: Mytikas
|
||||
|
||||
### Add
|
||||
|
||||
- [NEW] Add GetBounds func support (add get_client and get_frame C_func)
|
||||
- [NEW] Add GetXId and GetXidFromPid func
|
||||
- [NEW] Refactoring GetTitle() func allow by pid
|
||||
- [NEW] Refactoring CloseWindow() allow by pid
|
||||
- [NEW] Add SetHandPid() and GetHandPid() func support
|
||||
- [NEW] Add FindCBitmap func support
|
||||
<br/>
|
||||
|
||||
- [NEW] Refactoring bitmap example code
|
||||
- [NEW] Refactoring key example code
|
||||
- [NEW] Refactoring window example code
|
||||
- [NEW] Add an cbitmap example [#169]
|
||||
- [NEW] Refactoring screen and event example code
|
||||
- [NEW] Refactoring mouse example code
|
||||
<br/>
|
||||
|
||||
- [NEW] Add more godoc
|
||||
- [NEW] Add getTitle example by pid
|
||||
- [NEW] Add close window example by pid
|
||||
- [NEW] Add getBounds example
|
||||
- [NEW] Split func and remove dep more clean
|
||||
- [NEW] Simplify SaveCapture code
|
||||
- [NEW] Update and merged get_pixel_color remove duplicate code
|
||||
- [NEW] Update README.md, add Note go1.10.x
|
||||
|
||||
|
||||
### Update
|
||||
|
||||
- [NEW] Update issue template more obvious
|
||||
- [NEW] Move public mdata to pub
|
||||
- [NEW] Update godoc
|
||||
- [NEW] Update CHANGELOG.md
|
||||
- [NEW] Move some pub method to pub.h and rename some c_func
|
||||
- [NEW] Update code style and name style ( key, window and other )
|
||||
- [NEW] Update robotgo unix export getXidFromPid func
|
||||
- [NEW] Update set handle return use bool
|
||||
<br/>
|
||||
|
||||
- [NEW] Update code style and move scale to win_sys.h
|
||||
- [NEW] Update example add more lang
|
||||
- [NEW] Update pkg to newest
|
||||
- [NEW] Remove duplicate code and old useless code
|
||||
- [NEW] Update and clean README.md
|
||||
- [NEW] Update CI add go1.11.x version
|
||||
- [NEW] Update scroll godoc and clearer parameter name
|
||||
- [NEW] Update hint and code style
|
||||
- [NEW] Update FindIds doc and only set name once in loop
|
||||
|
||||
### Fix
|
||||
|
||||
- [FIX] Update type_string fixed #155, fixed window missing some character
|
||||
- [FIX] Fixed GetWindowText return address of local variable and not use ternary operator ( GetTittle )
|
||||
- [FIX] Update README.md Fixed Release badge
|
||||
|
||||
See Commits for more details, after Aug 8.
|
||||
|
||||
|
||||
## RobotGo v0.50.0, The Appalachian Mountains
|
||||
|
||||
## Add
|
||||
|
||||
- [NEW] Add simple ocr support
|
||||
- [NEW] Add max and min window api and win32.h file
|
||||
- [NEW] Automatic free internal bitmap and add bitmapStr example
|
||||
- [NEW] Update findBitmap and findColor default tolerance 0.5 to 0.01, [improve find accuracy and time]
|
||||
- [NEW] Add more Window example
|
||||
- [NEW] Add cross compile docs
|
||||
- [NEW] Add free bitmap and tolerance godoc
|
||||
- [NEW] Add GetForegroundWindow and FindWindow func support
|
||||
- [NEW] Add bitmap to CBitmap func, Add ToCBitmap example to examples
|
||||
- [NEW] Add get Scale and GetScaleSize func, get primary display DPI scale factor fix #129, #135
|
||||
Add Custom DPI Scaling support,
|
||||
Add scale default value,
|
||||
Add scale example
|
||||
|
||||
## Update
|
||||
|
||||
- [NEW] Update README.md [add freeBitmap example]
|
||||
- [NEW] Optimize findColor and uniform API with findBitmap
|
||||
- [NEW] Update godoc, CI and README.md
|
||||
- [NEW] Update CHANGELOG.md
|
||||
- [NEW] Update examples [add freeBitmap and update findColor]
|
||||
- [NEW] Optimize bitmap code, optimize args and not try [many methods]
|
||||
- [NEW] Update getPid type to int32
|
||||
- [NEW] Update var and other code style, fix non-standard code
|
||||
Update code and update some name
|
||||
- [NEW] Update pkg to newest
|
||||
- [NEW] Remove duplicate code and old useless code
|
||||
- [NEW] Update mouse click and fix moveClick and movesClick args
|
||||
- [NEW] Update code style use if not try
|
||||
- [NEW] Update clipboard example
|
||||
- [NEW] Update typestr use return not else
|
||||
- [NEW] Update mouse toggle, keytap and savebitmap func args
|
||||
- [NEW] Update examples remove duplicate code
|
||||
- [NEW] Update bitmap and other examples
|
||||
- [NEW] Simplify linux dependency installation commands
|
||||
- [NEW] Update issue_template.md
|
||||
-[NEW] Update pull_request_template.md
|
||||
- [NEW] Move govendor to dep
|
||||
- [NEW] Update robotgo ci to 1.10.3
|
||||
|
||||
## Fix
|
||||
|
||||
- [FIX] Update active pid to fix #140, fixed linux activePid
|
||||
- [FIX] Fixed findBitmap and findPic memory leak
|
||||
- [FIX] Add getPxColor destroyMMBitmap fix memory leak
|
||||
- [FIX] Fix float args not float32
|
||||
- [FIX] Fix windows clipboard memory leak
|
||||
- [FIX] Update macos .a downgrade to 10.10 just warning not exit [fix #102, #128, #134]
|
||||
- [FIX] use 10.10 to compile .a verifyed multi os
|
||||
- [FIX] Fix #145 not assert
|
||||
- [FIX] Fix some warning use supplemental code
|
||||
|
||||
See Commits for more details, after Apr 30.
|
||||
|
||||
|
||||
## RobotGo v0.49.0, Olympus Mons
|
||||
|
||||
### Add
|
||||
|
||||
- [NEW] Add get image size func
|
||||
- [NEW] Add linux type string utf-8 support
|
||||
- [NEW] Add scroll mouse support x, y
|
||||
- [NEW] Add AddEvent() "esc" support fix #105
|
||||
- [NEW] Add AddEvent "space" fix #110
|
||||
- [NEW] Add clipboard choose primary mode on unix
|
||||
- [NEW] Add move smooth return
|
||||
- [NEW] Add more bitmap func and examples
|
||||
- [NEW] Add MicroSleep func
|
||||
- [NEW] Add find image by path
|
||||
|
||||
|
||||
### Update
|
||||
|
||||
- [NEW] Update KeyToggle code
|
||||
- [NEW] Update activePid allow Windows via hwnd
|
||||
- [NEW] Update godoc and README.md
|
||||
- [NEW] Update CHANGELOG.md
|
||||
- [NEW] Update Kill() parameter and examples
|
||||
- [NEW] Update examples and remove useless function
|
||||
- [NEW] Update appveyor, circle and dockerfile
|
||||
- [NEW] Update code style
|
||||
- [NEW] Update and optimize func
|
||||
- [NEW] Update travis support go 1.10
|
||||
- [NEW] Update CI (use custom go image) and add func internalFindBitmap
|
||||
- [NEW] Update godoc and deprecated GetBHandle
|
||||
- [NEW] Optimize code func args and name
|
||||
|
||||
|
||||
### Fix
|
||||
|
||||
- Fix release key
|
||||
- Fix godoc error
|
||||
- [FIX] Fix mac input method keytap not work
|
||||
- [FIX] Fix clipboard golint
|
||||
- [FIX] Update move smooth fix #96 (set mouse smooth speed)
|
||||
- [FIX] Fix Getportion param to go type
|
||||
- [FIX] Fix XFlush wait for events flushing
|
||||
|
||||
See Commits for more details, after Jan 25.
|
||||
|
||||
## RobotGo v0.48.0, Ben Nevis
|
||||
|
||||
### Add
|
||||
|
||||
- [NEW] Add active window by name func ActiveName
|
||||
- [NEW] Add type string utf-8 support
|
||||
|
||||
Add func CharCodeAt, UnicodeType, PasteStr and update TypeStr, TypeString
|
||||
|
||||
- [NEW] Add count of bitmap func CountBitmap
|
||||
- [NEW] Add func SaveCapture and examples
|
||||
- [NEW] Add time sleep func Sleep
|
||||
- [NEW] Add more key listen
|
||||
- [NEW] Add func PointInBounds and examples
|
||||
- [NEW] Add func GetPxColor return C.MMRGBHex
|
||||
- [NEW] Add FindColorCS param tolerance
|
||||
- [NEW] Add func ToBitmap and examples
|
||||
- [NEW] Add CBitmap type and examples
|
||||
- [NEW] Add more examples
|
||||
- [NEW] Add func ToMMBitmapRef
|
||||
- [NEW] Add func BitmapClick and MovesClick
|
||||
- [NEW] Add func ToMMRGBHex convert color hex
|
||||
- [NEW] Add func count bitmap color and CountColorCS
|
||||
- [NEW] Add more color processing and conversion
|
||||
|
||||
Add func ToMMRGBHex, U32ToHex, U8ToHex, PadHex, HexToRgb, RgbToHex and examples
|
||||
|
||||
- [NEW] Add func tochar bitmap and gostring and fmt code
|
||||
|
||||
|
||||
### Update
|
||||
- [NEW] Remove robot and examples
|
||||
- [NEW] Update vendor and appveyor.yml
|
||||
- [NEW] Update keyboard code
|
||||
- [NEW] Update godoc
|
||||
- [NEW] Update CHANGELOG.md
|
||||
- [NEW] Change TostringBitmap return string
|
||||
- [NEW] Update C language code and other naming
|
||||
- [NEW] Update code and code style
|
||||
- [NEW] Update move mouse smooth
|
||||
|
||||
|
||||
### Fix
|
||||
|
||||
- [FIX] Fix mac set active and active by pid
|
||||
- [FIX] Fix windows active by pid #101
|
||||
- [FIX] Fix FindColor param tolerance
|
||||
- [FIX] Fix find bitmap float args
|
||||
- [FIX] Fix some range error
|
||||
- [FIX] Update doc fix #97
|
||||
- [FIX] Update README.md fix link error
|
||||
|
||||
See Commits for more details, after Dec 13.
|
||||
|
||||
## RobotGo v0.47.0, Mount Cook
|
||||
|
||||
### Add
|
||||
|
||||
- [NEW] Add windows 32bit and 64bit dependency
|
||||
- [NEW] Add macOs dependency
|
||||
- [NEW] Add pkg to vendor
|
||||
|
||||
Solve the problem of dependence, remove zlib/libpng dependencies
|
||||
|
||||
- [NEW] Add FindColorCS(x, y, w, h int, color CHex), CHex type and examples #84
|
||||
- [NEW] Add kill the process
|
||||
- [NEW] Add public event and update code
|
||||
- [NEW] Add Windows 32bit and 64bit Appveyor CI
|
||||
|
||||
|
||||
### Update
|
||||
- [NEW] Update png io
|
||||
- [NEW] Update cgo link
|
||||
- [NEW] Update .gitignore
|
||||
- [NEW] Update README.md and godoc
|
||||
- [NEW] Update CHANGELOG.md
|
||||
- [NEW] Update circle to 2.0, add robotgo Dockerfile custom image
|
||||
- [NEW] Update and fmt C code
|
||||
- [NEW] Update GetTitle default value "null" to ""
|
||||
|
||||
|
||||
### Fix
|
||||
|
||||
- [FIX] Fix FindColor inconvenient parameters
|
||||
- [FIX] Fix installation requirements #72
|
||||
- [FIX] Fix GetTitle `return address of local variable` in the higher gcc version. #81
|
||||
|
||||
See Commits for more details, after Nov 10.
|
||||
|
||||
|
||||
## RobotGo v0.46.6, Pyrenees Mountains: Aneto Peak
|
||||
|
||||
## RobotGo v0.46.0, Pyrenees Mountains
|
||||
|
||||
### Add
|
||||
|
||||
- [NEW] Add ActivePID
|
||||
- [NEW] Add FindBit
|
||||
- [NEW] Add robot branch, where there is no zlib and libpng dependency
|
||||
|
||||
### Update
|
||||
|
||||
- [NEW] Update README.md
|
||||
- [NEW] Update FindIds
|
||||
- [NEW] Update examples
|
||||
- [NEW] Update vendor
|
||||
- [NEW] Update godoc and docs
|
||||
- [NEW] Update and fix bitmap
|
||||
|
||||
### Fix
|
||||
|
||||
- [FIX] Fix MoveMouseSmooth args
|
||||
- [FIX] Fix name err
|
||||
- [FIX] Fix FindBitmap
|
||||
|
||||
## RobotGo v0.45.0, Mount Qomolangma
|
||||
|
||||
### Add
|
||||
- [NEW] Add Process
|
||||
- [NEW] Add TypeStr
|
||||
- [NEW] Add DeepCopyBit
|
||||
- [NEW] Add CopyBitpb
|
||||
- [NEW] Add ReadBitmap
|
||||
- [NEW] Add vendor.json
|
||||
- [NEW] Add ReadAll: clipboard
|
||||
- [NEW] Add WriteAll: clipboard
|
||||
- [NEW] Add Pids : get the all process id
|
||||
- [NEW] Add FindName: find the process name by the process id
|
||||
- [NEW] Add FindNames: find the all process name
|
||||
- [NEW] Add PidExists: determine whether the process exists
|
||||
- [NEW] Add FindIds: find the process id by the process name
|
||||
- [NEW] Add FreeBitmap and Update docs
|
||||
|
||||
|
||||
### Update
|
||||
- [NEW] Update docs
|
||||
- [NEW] Update test
|
||||
- [NEW] Update godoc
|
||||
- [NEW] Update CHANGELOG.md
|
||||
- [NEW] Update .gitignore
|
||||
- [NEW] Update examples and docs
|
||||
- [NEW] Update examples link
|
||||
- [NEW] Update README.md and clipboard
|
||||
|
||||
|
||||
### Fix
|
||||
|
||||
- [FIX] Fix release key
|
||||
- [FIX] Fix godoc error
|
||||
|
||||
|
||||
## RobotGo v0.44.0, Mount Kailash
|
||||
@ -43,7 +843,7 @@
|
||||
### Add
|
||||
|
||||
- Add CHANGELOG.md
|
||||
- Format some code
|
||||
- Format some code
|
||||
- Add fedora dependencies
|
||||
|
||||
### Update
|
||||
@ -58,6 +858,6 @@
|
||||
### Fix
|
||||
|
||||
- Fix typesetting and MD error
|
||||
- Fix fedora dependencies #55
|
||||
- Fix fedora dependencies #55
|
||||
- Fix doc.md and README.md
|
||||
|
||||
|
3
docs/README.md
Normal file
3
docs/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Docs
|
||||
|
||||
Documents are not necessarily updated synchronously, slower than godoc, please see examples and godoc.
|
58
docs/doc.md
58
docs/doc.md
@ -5,14 +5,14 @@
|
||||
## [Keyboard](#Keyboard)
|
||||
|
||||
##### [Keys](https://github.com/go-vgo/robotgo/blob/master/docs/keys.md)
|
||||
##### [SetKeyboardDelay](#SetKeyDelay) (Equivalent to SetKeyDelay, Wno-deprecated)
|
||||
<!-- ##### [SetKeyboardDelay](#SetKeyDelay) (Equivalent to SetKeyDelay, Wno-deprecated) -->
|
||||
##### [SetKeyDelay](#SetKeyDelay)
|
||||
##### [KeyTap](#KeyTap)
|
||||
##### [KeyToggle](#KeyToggle)
|
||||
##### [TypeString](#TypeString)
|
||||
##### [TypeStringDelayed](#TypeStrDelay) (Equivalent to TypeStrDelay, Wno-deprecated)
|
||||
##### [TypeStrDelay](#TypeStrDelay)
|
||||
<!-- ##### [TypeString](#TypeString)
|
||||
##### [TypeStringDelayed](#TypeStrDelay) (Equivalent to TypeStrDelay, Wno-deprecated) -->
|
||||
##### [TypeStr](#TypeStr)
|
||||
##### [TypeStrDelay](#TypeStrDelay)
|
||||
##### [WriteAll](#WriteAll)
|
||||
##### [ReadAll](#ReadAll)
|
||||
|
||||
@ -57,7 +57,7 @@
|
||||
|
||||
## [Event](#Event)
|
||||
|
||||
##### [LEvent](#LEvent) (Equivalent to AddEvent, Wno-deprecated)
|
||||
<!-- ##### [LEvent](#LEvent) (Equivalent to AddEvent, Wno-deprecated) -->
|
||||
##### [AddEvent](#AddEvent)
|
||||
##### [StopEvent](#StopEvent)
|
||||
|
||||
@ -71,7 +71,7 @@
|
||||
##### [GetActive](#GetActive)
|
||||
##### [SetHandle](#SetHandle)
|
||||
##### [GetHandle](#GetHandle)
|
||||
##### [GetBHandle](#GetHandle)
|
||||
<!-- ##### [GetBHandle](#GetHandle) -->
|
||||
##### [GetTitle](#GetTitle)
|
||||
##### [GetPID](#GetPID)
|
||||
##### [Pids](#Pids)
|
||||
@ -80,6 +80,7 @@
|
||||
##### [FindName](#FindName)
|
||||
##### [FindNames](#FindNames)
|
||||
##### [FindIds](#FindIds)
|
||||
#### [ActivePID](#ActivePID)
|
||||
|
||||
### <h3 id="GetVersion">.GetVersion()</h3>
|
||||
Get robotgo version
|
||||
@ -125,7 +126,13 @@ modifier (optional, string or array) - Accepts alt, command (mac), control, and
|
||||
#### Return:
|
||||
return KeyToggle status
|
||||
|
||||
### <h3 id="TypeString">.TypeString(string)</h3>
|
||||
<!-- ### <h3 id="TypeString">.TypeString(string)</h3>
|
||||
|
||||
#### Arguments:
|
||||
|
||||
string - The string to send. -->
|
||||
|
||||
### <h3 id="TypeStr">.TypeStr(string)</h3>
|
||||
|
||||
#### Arguments:
|
||||
|
||||
@ -138,12 +145,6 @@ modifier (optional, string or array) - Accepts alt, command (mac), control, and
|
||||
string - The string to send.
|
||||
cpm - Characters per minute.
|
||||
|
||||
### <h3 id="TypeStr">.TypeStr(string)</h3>
|
||||
|
||||
#### Arguments:
|
||||
|
||||
string - The string to send.
|
||||
|
||||
### <h3 id="WriteAll">.WriteAll(text string)</h3>
|
||||
|
||||
#### Arguments:
|
||||
@ -212,8 +213,8 @@ robotgo.MoveMouseSmooth(100, 200, 1.0, 100.0)
|
||||
#### Examples:
|
||||
|
||||
```Go
|
||||
robogo.MouseClick()
|
||||
robogo.MouseClick("left", true)
|
||||
robotgo.MouseClick()
|
||||
robotgo.MouseClick("left", true)
|
||||
```
|
||||
|
||||
### <h3 id="MoveClick">.MoveClick(x, y, button, double)</h3>
|
||||
@ -230,8 +231,8 @@ robogo.MouseClick("left", true)
|
||||
#### Examples:
|
||||
|
||||
```Go
|
||||
robogo.MoveClick(10, 20)
|
||||
robogo.MoveClick(10, 20, "left", true)
|
||||
robotgo.MoveClick(10, 20)
|
||||
robotgo.MoveClick(10, 20, "left", true)
|
||||
```
|
||||
|
||||
### <h3 id="MouseToggle">.MouseToggle(down, button)</h3>
|
||||
@ -329,8 +330,8 @@ robotgo.ScrollMouse(50, "down")
|
||||
|
||||
Gets part or all of the screen.
|
||||
|
||||
BCaptureScreen Returns a go struct
|
||||
Capture_Screen (Drop support)
|
||||
GoCaptureScreen Returns a go struct
|
||||
<!-- Capture_Screen (Drop support) -->
|
||||
|
||||
#### Arguments:
|
||||
|
||||
@ -511,7 +512,7 @@ robotgo.DeepCopyBit(bitmap)
|
||||
|
||||
string
|
||||
|
||||
(mosue arguments: mleft, mright, wheelDown, wheelUp, wheelLeft, wheelRight)
|
||||
(mouse arguments: mleft, mright, wheelDown, wheelUp, wheelLeft, wheelRight)
|
||||
|
||||
#### Return:
|
||||
|
||||
@ -530,12 +531,12 @@ import (
|
||||
|
||||
func main() {
|
||||
keve := robotgo.AddEvent("k")
|
||||
if keve == 0 {
|
||||
if keve {
|
||||
fmt.Println("you press...", "k")
|
||||
}
|
||||
|
||||
mleft := robotgo.AddEvent("mleft")
|
||||
if mleft == 0 {
|
||||
if mleft {
|
||||
fmt.Println("you press...", "mouse left button")
|
||||
}
|
||||
}
|
||||
@ -701,4 +702,15 @@ func main() {
|
||||
name string
|
||||
|
||||
#### Return:
|
||||
Returns []int32, error
|
||||
Returns []int32, error
|
||||
|
||||
|
||||
### <h3 id="ActivePID">.ActivePID()</h3>
|
||||
|
||||
ActivePID window active by PID
|
||||
|
||||
#### Arguments:
|
||||
pid int32
|
||||
|
||||
#### Return:
|
||||
none
|
@ -5,14 +5,14 @@
|
||||
## [键盘](#Keyboard)
|
||||
|
||||
##### [Keys](https://github.com/go-vgo/robotgo/blob/master/docs/keys.md)
|
||||
##### [SetKeyboardDelay](#SetKeyDelay) (相当于 SetKeyDelay, 废弃 API)
|
||||
<!-- ##### [SetKeyboardDelay](#SetKeyDelay) (相当于 SetKeyDelay, 废弃 API) -->
|
||||
##### [SetKeyDelay](#SetKeyDelay)
|
||||
##### [KeyTap](#KeyTap)
|
||||
##### [KeyToggle](#KeyToggle)
|
||||
##### [TypeString](#TypeString)
|
||||
##### [TypeStringDelayed](#TypeStrDelay) (相当于 TypeStrDelay, 废弃 API)
|
||||
##### [TypeStrDelay](#TypeStrDelay)
|
||||
<!-- ##### [TypeString](#TypeString)
|
||||
##### [TypeStringDelayed](#TypeStrDelay) (相当于 TypeStrDelay, 废弃 API) -->
|
||||
##### [TypeStr](#TypeStr)
|
||||
##### [TypeStrDelay](#TypeStrDelay)
|
||||
##### [WriteAll](#WriteAll)
|
||||
##### [ReadAll](#ReadAll)
|
||||
|
||||
@ -59,7 +59,7 @@
|
||||
|
||||
## [事件](#Event)
|
||||
|
||||
##### [LEvent](#AddEvent) (相当于 AddEvent, 废弃 API)
|
||||
<!-- ##### [LEvent](#AddEvent) (相当于 AddEvent, 废弃 API) -->
|
||||
##### [AddEvent](#AddEvent)
|
||||
##### [StopEvent](#StopEvent)
|
||||
|
||||
@ -73,7 +73,7 @@
|
||||
##### [GetActive](#GetActive)
|
||||
##### [SetHandle](#SetHandle)
|
||||
##### [GetHandle](#GetHandle)
|
||||
##### [GetBHandle](#GetHandle)
|
||||
<!-- ##### [GetBHandle](#GetHandle) -->
|
||||
##### [GetTitle](#GetTitle)
|
||||
##### [GetPID](#GetPID)
|
||||
##### [Pids](#Pids)
|
||||
@ -82,6 +82,7 @@
|
||||
##### [FindName](#FindName)
|
||||
##### [FindNames](#FindNames)
|
||||
##### [FindIds](#FindIds)
|
||||
#### [ActivePID](#ActivePID)
|
||||
|
||||
|
||||
## <h2 id="Keyboard">键盘</h2>
|
||||
@ -130,12 +131,19 @@ modifier (optional, string or array) - Accepts alt, command (mac), control, and
|
||||
|
||||
返回 KeyToggle 状态
|
||||
|
||||
### <h3 id="TypeString">.TypeString(string)</h3>
|
||||
<!-- ### <h3 id="TypeString">.TypeString(string)</h3>
|
||||
|
||||
#### 参数:
|
||||
|
||||
string - The string to send. -->
|
||||
|
||||
### <h3 id="TypeStr">.TypeStr(string)</h3>
|
||||
|
||||
#### 参数:
|
||||
|
||||
string - The string to send.
|
||||
|
||||
|
||||
### <h3 id="TypeStrDelay">.TypeStrDelay(string, cpm)</h3>
|
||||
|
||||
#### 参数:
|
||||
@ -143,12 +151,6 @@ modifier (optional, string or array) - Accepts alt, command (mac), control, and
|
||||
string - The string to send.
|
||||
cpm - Characters per minute.
|
||||
|
||||
### <h3 id="TypeStr">.TypeStr(string)</h3>
|
||||
|
||||
#### 参数:
|
||||
|
||||
string - The string to send.
|
||||
|
||||
### <h3 id="WriteAll">.WriteAll(text string)</h3>
|
||||
|
||||
#### 参数:
|
||||
@ -219,8 +221,8 @@ robotgo.MoveMouseSmooth(100, 200, 1.0, 100.0)
|
||||
#### 示例:
|
||||
|
||||
```Go
|
||||
robogo.MouseClick()
|
||||
robogo.MouseClick("left", true)
|
||||
robotgo.MouseClick()
|
||||
robotgo.MouseClick("left", true)
|
||||
```
|
||||
|
||||
### <h3 id="MoveClick">.MoveClick(x, y, button, double)</h3>
|
||||
@ -237,8 +239,8 @@ robogo.MouseClick("left", true)
|
||||
#### 示例:
|
||||
|
||||
```Go
|
||||
robogo.MoveClick(10, 20)
|
||||
robogo.MoveClick(10, 20, "left", true)
|
||||
robotgo.MoveClick(10, 20)
|
||||
robotgo.MoveClick(10, 20, "left", true)
|
||||
```
|
||||
|
||||
### <h3 id="MouseToggle">.MouseToggle(down, button)</h3>
|
||||
@ -343,8 +345,8 @@ robotgo.ScrollMouse(50, "down")
|
||||
获取部分或者全部屏幕
|
||||
Gets part or all of the screen.
|
||||
|
||||
BCaptureScreen Returns a go struct
|
||||
Capture_Screen (废弃)
|
||||
GoCaptureScreen Returns a go struct
|
||||
<!-- Capture_Screen (废弃) -->
|
||||
|
||||
#### 参数:
|
||||
|
||||
@ -544,12 +546,12 @@ import (
|
||||
|
||||
func main() {
|
||||
keve := robotgo.AddEvent("k")
|
||||
if keve == 0 {
|
||||
if keve {
|
||||
fmt.Println("you press...", "k")
|
||||
}
|
||||
|
||||
mleft := robotgo.AddEvent("mleft")
|
||||
if mleft == 0 {
|
||||
if mleft {
|
||||
fmt.Println("you press...", "mouse left button")
|
||||
}
|
||||
}
|
||||
@ -716,4 +718,14 @@ func main() {
|
||||
name string
|
||||
|
||||
#### Return:
|
||||
Returns []int32, error
|
||||
Returns []int32, error
|
||||
|
||||
### <h3 id="ActivePID">.ActivePID()</h3>
|
||||
|
||||
ActivePID window active by PID
|
||||
|
||||
#### Arguments:
|
||||
pid int32
|
||||
|
||||
#### Return:
|
||||
none
|
33
docs/install.md
Normal file
33
docs/install.md
Normal file
@ -0,0 +1,33 @@
|
||||
## CrossCompiling
|
||||
|
||||
##### Windows64 to windows32
|
||||
|
||||
```Go
|
||||
SET CGO_ENABLED=1
|
||||
SET GOARCH=386
|
||||
go build main.go
|
||||
```
|
||||
|
||||
#### Other to windows
|
||||
|
||||
Install Requirements (Ubuntu):
|
||||
|
||||
```bash
|
||||
sudo apt install gcc-multilib
|
||||
sudo apt install gcc-mingw-w64
|
||||
# fix err: zlib.h: No such file or directory, Just used by bitmap.
|
||||
sudo apt install libz-mingw-w64-dev
|
||||
```
|
||||
|
||||
Build the binary:
|
||||
|
||||
```Go
|
||||
GOOS=windows GOARCH=amd64 CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ go build -x ./
|
||||
```
|
||||
|
||||
```
|
||||
// CC=mingw-w64\x86_64-7.2.0-win32-seh-rt_v5-rev1\mingw64\bin\gcc.exe
|
||||
// CXX=mingw-w64\x86_64-7.2.0-win32-seh-rt_v5-rev1\mingw64\bin\g++.exe
|
||||
```
|
||||
|
||||
Some discussions and questions, please see [issues/228](https://github.com/go-vgo/robotgo/issues/228), [issues/143](https://github.com/go-vgo/robotgo/issues/143).
|
101
docs/keys.md
101
docs/keys.md
@ -1,12 +1,37 @@
|
||||
## Type Conversion
|
||||
|
||||
| | type conversion | func |
|
||||
| --- | --------------------------------- | ----------------------- |
|
||||
| \* | robotgo.Bitmap -> robotgo.CBitmap | robotgo.ToCBitmap() |
|
||||
| | robotgo.Bitmap -> \*image.RGBA | robotgo.ToRGBAGo() |
|
||||
| \* | robotgo.CBitmap -> C.MMBitmapRef | robotgo.ToMMBitmapRef() |
|
||||
| | robotgo.CBitmap -> robotgo.Bitmap | robotgo.ToBitmap() |
|
||||
| | robotgo.CBitmap -> image.Image | robotgo.ToImage() |
|
||||
| | robotgo.CBitmap -> \*image.RGBA | robotgo.ToRGBA() |
|
||||
| \* | C.MMBitmapRef -> robotgo.CBitmap | robotgo.CBitmap() |
|
||||
| \* | image.Image -> robotgo.Bitmap | robotgo.ImgToBitmap() |
|
||||
| | image.Image -> robotgo.CBitmap | robotgo.ImgToCBitmap() |
|
||||
| | image.Image -> []byte | robotgo.ToByteImg() |
|
||||
| | image.Image -> string | robotgo.ToStringImg() |
|
||||
| \* | \*image.RGBA -> robotgo.Bitmap | robotgo.RGBAToBitmap() |
|
||||
| \* | []byte -> image.Image | robotgo.ByteToImg() |
|
||||
| | []byte-> robotgo.CBitmap | robotgo.ByteToCBitmap() |
|
||||
| | []byte -> string | string() |
|
||||
| \* | string -> image.Image | robotgo.StrToImg() |
|
||||
| | string -> byte | []byte() |
|
||||
|
||||
# Keys
|
||||
|
||||
```Go
|
||||
"A-Z a-z 0-9"
|
||||
|
||||
"backspace"
|
||||
"delete"
|
||||
"enter"
|
||||
"delete"
|
||||
"enter"
|
||||
"tab"
|
||||
"esc"
|
||||
"escape"
|
||||
"up" Up arrow key
|
||||
"up" Up arrow key
|
||||
"down" Down arrow key
|
||||
"right" Right arrow key
|
||||
"left" Left arrow key
|
||||
@ -14,6 +39,7 @@
|
||||
"end"
|
||||
"pageup"
|
||||
"pagedown"
|
||||
|
||||
"f1"
|
||||
"f2"
|
||||
"f3"
|
||||
@ -38,14 +64,27 @@
|
||||
"f22"
|
||||
"f23"
|
||||
"f24"
|
||||
"command"
|
||||
|
||||
"cmd" is the "win" key for windows
|
||||
"lcmd" left command
|
||||
"rcmd" right command
|
||||
// "command"
|
||||
"alt"
|
||||
"lalt" left alt
|
||||
"ralt" right alt
|
||||
"ctrl"
|
||||
"lctrl" left ctrl
|
||||
"rctrl" right ctrl
|
||||
"control"
|
||||
"shift"
|
||||
"right_shift"
|
||||
"lshift" left shift
|
||||
"rshift" right shift
|
||||
// "right_shift"
|
||||
"capslock"
|
||||
"space"
|
||||
"printscreen" No Mac support
|
||||
"insert" No Mac support
|
||||
"print"
|
||||
"printscreen" // No Mac support
|
||||
"insert"
|
||||
"menu" Windows only
|
||||
|
||||
"audio_mute" Mute the volume
|
||||
@ -53,7 +92,7 @@
|
||||
"audio_vol_up" Increase the volume
|
||||
"audio_play"
|
||||
"audio_stop"
|
||||
"audio_pause"
|
||||
"audio_pause"
|
||||
"audio_prev" Previous Track
|
||||
"audio_next" Next Track
|
||||
"audio_rewind" Linux only
|
||||
@ -61,20 +100,44 @@
|
||||
"audio_repeat" Linux only
|
||||
"audio_random" Linux only
|
||||
|
||||
"numpad_0" No Linux support
|
||||
"numpad_1" No Linux support
|
||||
"numpad_2" No Linux support
|
||||
"numpad_3" No Linux support
|
||||
"numpad_4" No Linux support
|
||||
"numpad_5" No Linux support
|
||||
"numpad_6" No Linux support
|
||||
"numpad_7" No Linux support
|
||||
"numpad_8" No Linux support
|
||||
"numpad_9" No Linux support
|
||||
|
||||
"num0"
|
||||
"num1"
|
||||
"num2"
|
||||
"num3"
|
||||
"num4"
|
||||
"num5"
|
||||
"num6"
|
||||
"num7"
|
||||
"num8"
|
||||
"num9"
|
||||
"num_lock"
|
||||
|
||||
"num."
|
||||
"num+"
|
||||
"num-"
|
||||
"num*"
|
||||
"num/"
|
||||
"num_clear"
|
||||
"num_enter"
|
||||
"num_equal"
|
||||
|
||||
// // "numpad_0" No Linux support
|
||||
// "numpad_0"
|
||||
// "numpad_1"
|
||||
// "numpad_2"
|
||||
// "numpad_3"
|
||||
// "numpad_4"
|
||||
// "numpad_5"
|
||||
// "numpad_6"
|
||||
// "numpad_7"
|
||||
// "numpad_8"
|
||||
// "numpad_9"
|
||||
// "numpad_lock"
|
||||
|
||||
"lights_mon_up" Turn up monitor brightness No Windows support
|
||||
"lights_mon_down" Turn down monitor brightness No Windows support
|
||||
"lights_kbd_toggle" Toggle keyboard backlight on/off No Windows support
|
||||
"lights_kbd_up" Turn up keyboard backlight brightness No Windows support
|
||||
"lights_kbd_down" Turn down keyboard backlight brightness No Windows support
|
||||
```
|
||||
```
|
||||
|
321
event/goEvent.h
321
event/goEvent.h
@ -1,321 +0,0 @@
|
||||
// Copyright 2016 The go-vgo Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// https://github.com/go-vgo/robotgo/blob/master/LICENSE
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// The hook directory link from the https://github.com/robotn/gohook/
|
||||
// event/hook, you need to follow the relevant agreement and LICENSE.
|
||||
// See the LICENSE file at the top-level directory of this distribution and at
|
||||
// https://github.com/robotn/gohook/blob/master/LICENSE
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "../base/os.h"
|
||||
|
||||
#if defined(IS_MACOSX)
|
||||
#include "hook/darwin/input_c.h"
|
||||
#include "hook/darwin/hook_c.h"
|
||||
#include "hook/darwin/event_c.h"
|
||||
#include "hook/darwin/properties_c.h"
|
||||
#elif defined(USE_X11)
|
||||
//#define USE_XKBCOMMON 0
|
||||
#include "hook/x11/input_c.h"
|
||||
#include "hook/x11/hook_c.h"
|
||||
#include "hook/x11/event_c.h"
|
||||
#include "hook/x11/properties_c.h"
|
||||
#elif defined(IS_WINDOWS)
|
||||
#include "hook/windows/input_c.h"
|
||||
#include "hook/windows/hook_c.h"
|
||||
#include "hook/windows/event_c.h"
|
||||
#include "hook/windows/properties_c.h"
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "hook/iohook.h"
|
||||
|
||||
int aStop();
|
||||
int aEvent(char *aevent);
|
||||
|
||||
bool loggerProc(unsigned int level, const char *format, ...) {
|
||||
bool status = false;
|
||||
|
||||
va_list args;
|
||||
switch (level) {
|
||||
#ifdef USE_DEBUG
|
||||
case LOG_LEVEL_DEBUG:
|
||||
case LOG_LEVEL_INFO:
|
||||
va_start(args, format);
|
||||
status = vfprintf(stdout, format, args) >= 0;
|
||||
va_end(args);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case LOG_LEVEL_WARN:
|
||||
case LOG_LEVEL_ERROR:
|
||||
va_start(args, format);
|
||||
status = vfprintf(stderr, format, args) >= 0;
|
||||
va_end(args);
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// NOTE: The following callback executes on the same thread that hook_run() is called
|
||||
// from.
|
||||
|
||||
struct _MEvent {
|
||||
uint8_t id;
|
||||
size_t mask;
|
||||
uint16_t keychar;
|
||||
// char *keychar;
|
||||
size_t x;
|
||||
uint8_t y;
|
||||
uint8_t bytesPerPixel;
|
||||
};
|
||||
|
||||
typedef struct _MEvent MEvent;
|
||||
// typedef MMBitmap *MMBitmapRef;
|
||||
char *cevent;
|
||||
// uint16_t *cevent;
|
||||
int cstatus = 1;
|
||||
|
||||
MEvent mEvent;
|
||||
void dispatch_proc(iohook_event * const event) {
|
||||
char buffer[256] = { 0 };
|
||||
size_t length = snprintf(buffer, sizeof(buffer),
|
||||
"id=%i,when=%" PRIu64 ",mask=0x%X",
|
||||
event->type, event->time, event->mask);
|
||||
|
||||
switch (event->type) {
|
||||
case EVENT_KEY_PRESSED:
|
||||
// If the escape key is pressed, naturally terminate the program.
|
||||
if (event->data.keyboard.keycode == VC_ESCAPE) {
|
||||
int status = hook_stop();
|
||||
switch (status) {
|
||||
// System level errors.
|
||||
case IOHOOK_ERROR_OUT_OF_MEMORY:
|
||||
loggerProc(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)", status);
|
||||
break;
|
||||
|
||||
case IOHOOK_ERROR_X_RECORD_GET_CONTEXT:
|
||||
// NOTE This is the only platform specific error that occurs on hook_stop().
|
||||
loggerProc(LOG_LEVEL_ERROR, "Failed to get XRecord context. (%#X)", status);
|
||||
break;
|
||||
|
||||
// Default error.
|
||||
case IOHOOK_FAILURE:
|
||||
default:
|
||||
loggerProc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
case EVENT_KEY_RELEASED:
|
||||
snprintf(buffer + length, sizeof(buffer) - length,
|
||||
",keycode=%u,rawcode=0x%X",
|
||||
event->data.keyboard.keycode, event->data.keyboard.rawcode);
|
||||
int akeycode = (uint16_t) event->data.keyboard.keycode;
|
||||
// printf("atoi(str)---%d\n", atoi(cevent));
|
||||
if (akeycode == atoi(cevent)){
|
||||
int astop = aStop();
|
||||
// printf("%d\n",astop);
|
||||
cstatus = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case EVENT_KEY_TYPED:
|
||||
snprintf(buffer + length, sizeof(buffer) - length,
|
||||
",keychar=%lc,rawcode=%u",
|
||||
(uint16_t) event->data.keyboard.keychar,
|
||||
event->data.keyboard.rawcode);
|
||||
|
||||
#ifdef WE_REALLY_WANT_A_POINTER
|
||||
char *buf = malloc (6);
|
||||
#else
|
||||
char buf[6];
|
||||
#endif
|
||||
|
||||
sprintf(buf, "%lc", (uint16_t) event->data.keyboard.keychar);
|
||||
|
||||
#ifdef WE_REALLY_WANT_A_POINTER
|
||||
free (buf);
|
||||
#endif
|
||||
|
||||
if (strcmp(buf, cevent) == 0){
|
||||
int astop = aStop();
|
||||
// printf("%d\n",astop);
|
||||
cstatus = 0;
|
||||
}
|
||||
// return (char*) event->data.keyboard.keychar;
|
||||
break;
|
||||
|
||||
case EVENT_MOUSE_PRESSED:
|
||||
case EVENT_MOUSE_RELEASED:
|
||||
case EVENT_MOUSE_CLICKED:
|
||||
case EVENT_MOUSE_MOVED:
|
||||
case EVENT_MOUSE_DRAGGED:
|
||||
snprintf(buffer + length, sizeof(buffer) - length,
|
||||
",x=%i,y=%i,button=%i,clicks=%i",
|
||||
event->data.mouse.x, event->data.mouse.y,
|
||||
event->data.mouse.button, event->data.mouse.clicks);
|
||||
|
||||
int abutton = event->data.mouse.button;
|
||||
int aclicks = event->data.mouse.clicks;
|
||||
int amouse = -1;
|
||||
|
||||
if (strcmp(cevent, "mleft") == 0){
|
||||
amouse = 1;
|
||||
}
|
||||
if (strcmp(cevent, "mright") == 0){
|
||||
amouse = 2;
|
||||
}
|
||||
if (strcmp(cevent, "wheelDown") == 0){
|
||||
amouse = 4;
|
||||
}
|
||||
if (strcmp(cevent, "wheelUp") == 0){
|
||||
amouse = 5;
|
||||
}
|
||||
if (strcmp(cevent, "wheelLeft") == 0){
|
||||
amouse = 6;
|
||||
}
|
||||
if (strcmp(cevent, "wheelRight") == 0){
|
||||
amouse = 7;
|
||||
}
|
||||
if (abutton == amouse && aclicks == 1){
|
||||
int astop = aStop();
|
||||
cstatus = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case EVENT_MOUSE_WHEEL:
|
||||
snprintf(buffer + length, sizeof(buffer) - length,
|
||||
",type=%i,amount=%i,rotation=%i",
|
||||
event->data.wheel.type, event->data.wheel.amount,
|
||||
event->data.wheel.rotation);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// fprintf(stdout, "----%s\n", buffer);
|
||||
}
|
||||
|
||||
int aEvent(char *aevent) {
|
||||
// (uint16_t *)
|
||||
cevent = aevent;
|
||||
// Set the logger callback for library output.
|
||||
hookSetlogger(&loggerProc);
|
||||
|
||||
// Set the event callback for IOhook events.
|
||||
hook_set_dispatch_proc(&dispatch_proc);
|
||||
// Start the hook and block.
|
||||
// NOTE If EVENT_HOOK_ENABLED was delivered, the status will always succeed.
|
||||
int status = hook_run();
|
||||
|
||||
switch (status) {
|
||||
case IOHOOK_SUCCESS:
|
||||
// Everything is ok.
|
||||
break;
|
||||
|
||||
// System level errors.
|
||||
case IOHOOK_ERROR_OUT_OF_MEMORY:
|
||||
loggerProc(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)", status);
|
||||
break;
|
||||
|
||||
|
||||
// X11 specific errors.
|
||||
case IOHOOK_ERROR_X_OPEN_DISPLAY:
|
||||
loggerProc(LOG_LEVEL_ERROR, "Failed to open X11 display. (%#X)", status);
|
||||
break;
|
||||
|
||||
case IOHOOK_ERROR_X_RECORD_NOT_FOUND:
|
||||
loggerProc(LOG_LEVEL_ERROR, "Unable to locate XRecord extension. (%#X)", status);
|
||||
break;
|
||||
|
||||
case IOHOOK_ERROR_X_RECORD_ALLOC_RANGE:
|
||||
loggerProc(LOG_LEVEL_ERROR, "Unable to allocate XRecord range. (%#X)", status);
|
||||
break;
|
||||
|
||||
case IOHOOK_ERROR_X_RECORD_CREATE_CONTEXT:
|
||||
loggerProc(LOG_LEVEL_ERROR, "Unable to allocate XRecord context. (%#X)", status);
|
||||
break;
|
||||
|
||||
case IOHOOK_ERROR_X_RECORD_ENABLE_CONTEXT:
|
||||
loggerProc(LOG_LEVEL_ERROR, "Failed to enable XRecord context. (%#X)", status);
|
||||
break;
|
||||
|
||||
|
||||
// Windows specific errors.
|
||||
case IOHOOK_ERROR_SET_WINDOWS_HOOK_EX:
|
||||
loggerProc(LOG_LEVEL_ERROR, "Failed to register low level windows hook. (%#X)", status);
|
||||
break;
|
||||
|
||||
|
||||
// Darwin specific errors.
|
||||
case IOHOOK_ERROR_AXAPI_DISABLED:
|
||||
loggerProc(LOG_LEVEL_ERROR, "Failed to enable access for assistive devices. (%#X)", status);
|
||||
break;
|
||||
|
||||
case IOHOOK_ERROR_CREATE_EVENT_PORT:
|
||||
loggerProc(LOG_LEVEL_ERROR, "Failed to create apple event port. (%#X)", status);
|
||||
break;
|
||||
|
||||
case IOHOOK_ERROR_CREATE_RUN_LOOP_SOURCE:
|
||||
loggerProc(LOG_LEVEL_ERROR, "Failed to create apple run loop source. (%#X)", status);
|
||||
break;
|
||||
|
||||
case IOHOOK_ERROR_GET_RUNLOOP:
|
||||
loggerProc(LOG_LEVEL_ERROR, "Failed to acquire apple run loop. (%#X)", status);
|
||||
break;
|
||||
|
||||
case IOHOOK_ERROR_CREATE_OBSERVER:
|
||||
loggerProc(LOG_LEVEL_ERROR, "Failed to create apple run loop observer. (%#X)", status);
|
||||
break;
|
||||
|
||||
// Default error.
|
||||
case IOHOOK_FAILURE:
|
||||
default:
|
||||
loggerProc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status);
|
||||
break;
|
||||
}
|
||||
|
||||
// return status;
|
||||
// printf("%d\n",status);
|
||||
return cstatus;
|
||||
}
|
||||
|
||||
int aStop(){
|
||||
int status = hook_stop();
|
||||
switch (status) {
|
||||
// System level errors.
|
||||
case IOHOOK_ERROR_OUT_OF_MEMORY:
|
||||
loggerProc(LOG_LEVEL_ERROR, "Failed to allocate memory. (%#X)", status);
|
||||
break;
|
||||
|
||||
case IOHOOK_ERROR_X_RECORD_GET_CONTEXT:
|
||||
// NOTE This is the only platform specific error that occurs on hook_stop().
|
||||
loggerProc(LOG_LEVEL_ERROR, "Failed to get XRecord context. (%#X)", status);
|
||||
break;
|
||||
|
||||
// Default error.
|
||||
case IOHOOK_FAILURE:
|
||||
default:
|
||||
// loggerProc(LOG_LEVEL_ERROR, "An unknown hook error occurred. (%#X)", status);
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
@ -1,235 +0,0 @@
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "../iohook.h"
|
||||
|
||||
// #include "../logger_c.h"
|
||||
#include "input.h"
|
||||
|
||||
// TODO Possibly relocate to input helper.
|
||||
static inline CGEventFlags get_key_event_mask(iohook_event * const event) {
|
||||
CGEventFlags native_mask = 0x00;
|
||||
|
||||
if (event->mask & (MASK_SHIFT)) { native_mask |= kCGEventFlagMaskShift; }
|
||||
if (event->mask & (MASK_CTRL)) { native_mask |= kCGEventFlagMaskControl; }
|
||||
if (event->mask & (MASK_META)) { native_mask |= kCGEventFlagMaskControl; }
|
||||
if (event->mask & (MASK_ALT)) { native_mask |= kCGEventFlagMaskAlternate; }
|
||||
|
||||
if (event->type == EVENT_KEY_PRESSED || event->type == EVENT_KEY_RELEASED || event->type == EVENT_KEY_TYPED) {
|
||||
switch (event->data.keyboard.keycode) {
|
||||
case VC_KP_0:
|
||||
case VC_KP_1:
|
||||
case VC_KP_2:
|
||||
case VC_KP_3:
|
||||
case VC_KP_4:
|
||||
case VC_KP_5:
|
||||
case VC_KP_6:
|
||||
case VC_KP_7:
|
||||
case VC_KP_8:
|
||||
case VC_KP_9:
|
||||
|
||||
case VC_NUM_LOCK:
|
||||
case VC_KP_ENTER:
|
||||
case VC_KP_MULTIPLY:
|
||||
case VC_KP_ADD:
|
||||
case VC_KP_SEPARATOR:
|
||||
case VC_KP_SUBTRACT:
|
||||
case VC_KP_DIVIDE:
|
||||
case VC_KP_COMMA:
|
||||
native_mask |= kCGEventFlagMaskNumericPad;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return native_mask;
|
||||
}
|
||||
|
||||
static inline void post_key_event(iohook_event * const event) {
|
||||
bool is_pressed = event->type == EVENT_KEY_PRESSED;
|
||||
|
||||
CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
|
||||
CGEventRef cg_event = CGEventCreateKeyboardEvent(src,
|
||||
(CGKeyCode) scancode_to_keycode(event->data.keyboard.keycode),
|
||||
is_pressed);
|
||||
|
||||
CGEventSetFlags(cg_event, get_key_event_mask(event));
|
||||
CGEventPost(kCGHIDEventTap, cg_event); // kCGSessionEventTap also works.
|
||||
CFRelease(cg_event);
|
||||
CFRelease(src);
|
||||
}
|
||||
|
||||
static inline void post_mouse_button_event(iohook_event * const event, bool is_pressed) {
|
||||
CGMouseButton mouse_button;
|
||||
CGEventType mouse_type;
|
||||
if (event->data.mouse.button == MOUSE_BUTTON1) {
|
||||
if (is_pressed) {
|
||||
mouse_type = kCGEventLeftMouseDown;
|
||||
}
|
||||
else {
|
||||
mouse_type = kCGEventLeftMouseUp;
|
||||
}
|
||||
mouse_button = kCGMouseButtonLeft;
|
||||
}
|
||||
else if (event->data.mouse.button == MOUSE_BUTTON2) {
|
||||
if (is_pressed) {
|
||||
mouse_type = kCGEventRightMouseDown;
|
||||
}
|
||||
else {
|
||||
mouse_type = kCGEventRightMouseUp;
|
||||
}
|
||||
mouse_button = kCGMouseButtonRight;
|
||||
}
|
||||
else {
|
||||
if (is_pressed) {
|
||||
mouse_type = kCGEventOtherMouseDown;
|
||||
}
|
||||
else {
|
||||
mouse_type = kCGEventOtherMouseUp;
|
||||
}
|
||||
mouse_button = event->data.mouse.button - 1;
|
||||
}
|
||||
|
||||
CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
|
||||
CGEventRef cg_event = CGEventCreateMouseEvent(src,
|
||||
mouse_type,
|
||||
CGPointMake(
|
||||
(CGFloat) event->data.mouse.x,
|
||||
(CGFloat) event->data.mouse.y
|
||||
),
|
||||
mouse_button
|
||||
);
|
||||
CGEventPost(kCGHIDEventTap, cg_event); // kCGSessionEventTap also works.
|
||||
CFRelease(cg_event);
|
||||
CFRelease(src);
|
||||
}
|
||||
|
||||
static inline void post_mouse_wheel_event(iohook_event * const event) {
|
||||
// FIXME Should I create a source event with the coords?
|
||||
// It seems to automagically use the current location of the cursor.
|
||||
// Two options: Query the mouse, move it to x/y, scroll, then move back
|
||||
// OR disable x/y for scroll events on Windows & X11.
|
||||
CGScrollEventUnit scroll_unit;
|
||||
if (event->data.wheel.type == WHEEL_BLOCK_SCROLL) {
|
||||
// Scrolling data is line-based.
|
||||
scroll_unit = kCGScrollEventUnitLine;
|
||||
}
|
||||
else {
|
||||
// Scrolling data is pixel-based.
|
||||
scroll_unit = kCGScrollEventUnitPixel;
|
||||
}
|
||||
|
||||
CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
|
||||
CGEventRef cg_event = CGEventCreateScrollWheelEvent(src,
|
||||
kCGScrollEventUnitLine,
|
||||
// TODO Currently only support 1 wheel axis.
|
||||
(CGWheelCount) 1, // 1 for Y-only, 2 for Y-X, 3 for Y-X-Z
|
||||
event->data.wheel.amount * event->data.wheel.rotation);
|
||||
|
||||
CGEventPost(kCGHIDEventTap, cg_event); // kCGSessionEventTap also works.
|
||||
CFRelease(cg_event);
|
||||
CFRelease(src);
|
||||
}
|
||||
|
||||
static inline void post_mouse_motion_event(iohook_event * const event) {
|
||||
CGEventSourceRef src = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
|
||||
CGEventRef cg_event;
|
||||
if (event->mask >> 8 == 0x00) {
|
||||
// No mouse flags.
|
||||
cg_event = CGEventCreateMouseEvent(src,
|
||||
kCGEventMouseMoved,
|
||||
CGPointMake(
|
||||
(CGFloat) event->data.mouse.x,
|
||||
(CGFloat) event->data.mouse.y
|
||||
),
|
||||
0
|
||||
);
|
||||
}
|
||||
else if (event->mask & MASK_BUTTON1) {
|
||||
cg_event = CGEventCreateMouseEvent(src,
|
||||
kCGEventLeftMouseDragged,
|
||||
CGPointMake(
|
||||
(CGFloat) event->data.mouse.x,
|
||||
(CGFloat) event->data.mouse.y
|
||||
),
|
||||
kCGMouseButtonLeft
|
||||
);
|
||||
}
|
||||
else if (event->mask & MASK_BUTTON2) {
|
||||
cg_event = CGEventCreateMouseEvent(src,
|
||||
kCGEventRightMouseDragged,
|
||||
CGPointMake(
|
||||
(CGFloat) event->data.mouse.x,
|
||||
(CGFloat) event->data.mouse.y
|
||||
),
|
||||
kCGMouseButtonRight
|
||||
);
|
||||
}
|
||||
else {
|
||||
cg_event = CGEventCreateMouseEvent(src,
|
||||
kCGEventOtherMouseDragged,
|
||||
CGPointMake(
|
||||
(CGFloat) event->data.mouse.x,
|
||||
(CGFloat) event->data.mouse.y
|
||||
),
|
||||
(event->mask >> 8) - 1
|
||||
);
|
||||
}
|
||||
|
||||
// kCGSessionEventTap also works.
|
||||
CGEventPost(kCGHIDEventTap, cg_event);
|
||||
CFRelease(cg_event);
|
||||
CFRelease(src);
|
||||
}
|
||||
|
||||
IOHOOK_API void hook_post_event(iohook_event * const event) {
|
||||
switch (event->type) {
|
||||
case EVENT_KEY_PRESSED:
|
||||
case EVENT_KEY_RELEASED:
|
||||
post_key_event(event);
|
||||
break;
|
||||
|
||||
|
||||
case EVENT_MOUSE_PRESSED:
|
||||
post_mouse_button_event(event, true);
|
||||
break;
|
||||
|
||||
case EVENT_MOUSE_RELEASED:
|
||||
post_mouse_button_event(event, false);
|
||||
break;
|
||||
|
||||
case EVENT_MOUSE_CLICKED:
|
||||
post_mouse_button_event(event, true);
|
||||
post_mouse_button_event(event, false);
|
||||
break;
|
||||
|
||||
case EVENT_MOUSE_WHEEL:
|
||||
post_mouse_wheel_event(event);
|
||||
break;
|
||||
|
||||
|
||||
case EVENT_MOUSE_MOVED:
|
||||
case EVENT_MOUSE_DRAGGED:
|
||||
post_mouse_motion_event(event);
|
||||
break;
|
||||
|
||||
|
||||
case EVENT_KEY_TYPED:
|
||||
// FIXME Ignoreing EVENT_KEY_TYPED events.
|
||||
|
||||
case EVENT_HOOK_ENABLED:
|
||||
case EVENT_HOOK_DISABLED:
|
||||
// Ignore hook enabled / disabled events.
|
||||
|
||||
default:
|
||||
// Ignore any other garbage.
|
||||
logger(LOG_LEVEL_WARN, "%s [%u]: Ignoring post event type %#X\n",
|
||||
__FUNCTION__, __LINE__, event->type);
|
||||
break;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,103 +0,0 @@
|
||||
|
||||
#ifndef _included_input_helper
|
||||
#define _included_input_helper
|
||||
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#include <Carbon/Carbon.h> // For HIToolbox kVK_ keycodes and TIS funcitons.
|
||||
#ifdef USE_IOKIT
|
||||
#include <IOKit/hidsystem/ev_keymap.h>
|
||||
#endif
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
#ifndef USE_IOKIT
|
||||
// Some of the system key codes that are needed from IOKit.
|
||||
// #define NX_KEYTYPE_SOUND_UP 0x00
|
||||
// #define NX_KEYTYPE_SOUND_DOWN 0x01
|
||||
// #define NX_KEYTYPE_MUTE 0x07
|
||||
|
||||
/* Display controls...
|
||||
#define NX_KEYTYPE_BRIGHTNESS_UP 0x02
|
||||
#define NX_KEYTYPE_BRIGHTNESS_DOWN 0x03
|
||||
#define NX_KEYTYPE_CONTRAST_UP 0x0B
|
||||
#define NX_KEYTYPE_CONTRAST_DOWN 0x0C
|
||||
#define NX_KEYTYPE_ILLUMINATION_UP 0x15
|
||||
#define NX_KEYTYPE_ILLUMINATION_DOWN 0x16
|
||||
#define NX_KEYTYPE_ILLUMINATION_TOGGLE 0x17
|
||||
*/
|
||||
|
||||
// #define NX_KEYTYPE_CAPS_LOCK 0x04
|
||||
// #define NX_KEYTYPE_HELP 0x05
|
||||
// #define NX_POWER_KEY 0x06
|
||||
|
||||
// #define NX_KEYTYPE_EJECT 0x0E
|
||||
// #define NX_KEYTYPE_PLAY 0x10
|
||||
// #define NX_KEYTYPE_NEXT 0x12
|
||||
// #define NX_KEYTYPE_PREVIOUS 0x13
|
||||
|
||||
/* There is no official fast-forward or rewind scan code support.*/
|
||||
// #define NX_KEYTYPE_FAST 0x14
|
||||
// #define NX_KEYTYPE_REWIND 0x15
|
||||
|
||||
#endif
|
||||
|
||||
// These virtual key codes do not appear to be defined anywhere by Apple.
|
||||
#define kVK_NX_Power 0xE0 | NX_POWER_KEY /* 0xE6 */
|
||||
#define kVK_NX_Eject 0xE0 | NX_KEYTYPE_EJECT /* 0xEE */
|
||||
|
||||
#define kVK_MEDIA_Play 0xE0 | NX_KEYTYPE_PLAY /* 0xF0 */
|
||||
#define kVK_MEDIA_Next 0xE0 | NX_KEYTYPE_NEXT /* 0xF1 */
|
||||
#define kVK_MEDIA_Previous 0xE0 | NX_KEYTYPE_PREVIOUS /* 0xF2 */
|
||||
|
||||
#define kVK_RightCommand 0x36
|
||||
#define kVK_ContextMenu 0x6E // AKA kMenuPowerGlyph
|
||||
#define kVK_Undefined 0xFF
|
||||
|
||||
// These button codes do not appear to be defined anywhere by Apple.
|
||||
#define kVK_LBUTTON kCGMouseButtonLeft
|
||||
#define kVK_RBUTTON kCGMouseButtonRight
|
||||
#define kVK_MBUTTON kCGMouseButtonCenter
|
||||
#define kVK_XBUTTON1 3
|
||||
#define kVK_XBUTTON2 4
|
||||
|
||||
// These button masks do not appear to be defined anywhere by Apple.
|
||||
#define kCGEventFlagMaskButtonLeft 1 << 0
|
||||
#define kCGEventFlagMaskButtonRight 1 << 1
|
||||
#define kCGEventFlagMaskButtonCenter 1 << 2
|
||||
#define kCGEventFlagMaskXButton1 1 << 3
|
||||
#define kCGEventFlagMaskXButton2 1 << 4
|
||||
|
||||
|
||||
/* Check for access to Apples accessibility API.
|
||||
*/
|
||||
extern bool is_accessibility_enabled();
|
||||
|
||||
/* Converts an OSX key code and event mask to the appropriate Unicode character
|
||||
* representation.
|
||||
*/
|
||||
extern UniCharCount keycode_to_unicode(CGEventRef event_ref, UniChar *buffer, UniCharCount size);
|
||||
|
||||
/* Converts an OSX keycode to the appropriate IOHook scancode constant.
|
||||
*/
|
||||
extern uint16_t keycode_to_scancode(UInt64 keycode);
|
||||
|
||||
/* Converts a IOHook scancode constant to the appropriate OSX keycode.
|
||||
*/
|
||||
extern UInt64 scancode_to_keycode(uint16_t keycode);
|
||||
|
||||
|
||||
/* Initialize items required for KeyCodeToKeySym() and KeySymToUnicode()
|
||||
* functionality. This method is called by OnLibraryLoad() and may need to be
|
||||
* called in combination with UnloadInputHelper() if the native keyboard layout
|
||||
* is changed.
|
||||
*/
|
||||
extern void load_input_helper();
|
||||
|
||||
/* De-initialize items required for KeyCodeToKeySym() and KeySymToUnicode()
|
||||
* functionality. This method is called by OnLibraryUnload() and may need to be
|
||||
* called in combination with LoadInputHelper() if the native keyboard layout
|
||||
* is changed.
|
||||
*/
|
||||
extern void unload_input_helper();
|
||||
|
||||
#endif
|
@ -1,542 +0,0 @@
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_COREFOUNDATION
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
#ifndef USE_WEAK_IMPORT
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "input.h"
|
||||
#include "../iohook.h"
|
||||
#include "../logger_c.h"
|
||||
|
||||
// Current dead key state.
|
||||
#if defined(USE_CARBON_LEGACY) || defined(USE_COREFOUNDATION)
|
||||
static UInt32 deadkey_state;
|
||||
#endif
|
||||
|
||||
// Input source data for the keyboard.
|
||||
#if defined(USE_CARBON_LEGACY)
|
||||
static KeyboardLayoutRef prev_keyboard_layout = NULL;
|
||||
#elif defined(USE_COREFOUNDATION)
|
||||
static TISInputSourceRef prev_keyboard_layout = NULL;
|
||||
#endif
|
||||
|
||||
#ifdef USE_WEAK_IMPORT
|
||||
// Required to dynamically check for AXIsProcessTrustedWithOptions availability.
|
||||
extern Boolean AXIsProcessTrustedWithOptions(CFDictionaryRef options) __attribute__((weak_import));
|
||||
extern CFStringRef kAXTrustedCheckOptionPrompt __attribute__((weak_import));
|
||||
#else
|
||||
static Boolean (*AXIsProcessTrustedWithOptions_t)(CFDictionaryRef);
|
||||
#endif
|
||||
|
||||
bool is_accessibility_enabled() {
|
||||
bool is_enabled = false;
|
||||
|
||||
#ifdef USE_WEAK_IMPORT
|
||||
// Check and make sure assistive devices is enabled.
|
||||
if (AXIsProcessTrustedWithOptions != NULL) {
|
||||
// New accessibility API 10.9 and later.
|
||||
const void * keys[] = { kAXTrustedCheckOptionPrompt };
|
||||
const void * values[] = { kCFBooleanTrue };
|
||||
|
||||
CFDictionaryRef options = CFDictionaryCreate(
|
||||
kCFAllocatorDefault,
|
||||
keys,
|
||||
values,
|
||||
sizeof(keys) / sizeof(*keys),
|
||||
&kCFCopyStringDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
|
||||
is_enabled = AXIsProcessTrustedWithOptions(options);
|
||||
}
|
||||
#else
|
||||
// Dynamically load the application services framework for examination.
|
||||
*(void **) (&AXIsProcessTrustedWithOptions_t) = dlsym(RTLD_DEFAULT, "AXIsProcessTrustedWithOptions");
|
||||
const char *dlError = dlerror();
|
||||
if (AXIsProcessTrustedWithOptions_t != NULL && dlError == NULL) {
|
||||
// Check for property CFStringRef kAXTrustedCheckOptionPrompt
|
||||
void ** kAXTrustedCheckOptionPrompt_t = dlsym(RTLD_DEFAULT, "kAXTrustedCheckOptionPrompt");
|
||||
|
||||
dlError = dlerror();
|
||||
if (kAXTrustedCheckOptionPrompt_t != NULL && dlError == NULL) {
|
||||
// New accessibility API 10.9 and later.
|
||||
const void * keys[] = { *kAXTrustedCheckOptionPrompt_t };
|
||||
const void * values[] = { kCFBooleanTrue };
|
||||
|
||||
CFDictionaryRef options = CFDictionaryCreate(
|
||||
kCFAllocatorDefault,
|
||||
keys,
|
||||
values,
|
||||
sizeof(keys) / sizeof(*keys),
|
||||
&kCFCopyStringDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
|
||||
is_enabled = (*AXIsProcessTrustedWithOptions_t)(options);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
#ifndef USE_WEAK_IMPORT
|
||||
if (dlError != NULL) {
|
||||
// Could not load the AXIsProcessTrustedWithOptions function!
|
||||
logger(LOG_LEVEL_DEBUG, "%s [%u]: %s.\n",
|
||||
__FUNCTION__, __LINE__, dlError);
|
||||
}
|
||||
#endif
|
||||
|
||||
logger(LOG_LEVEL_DEBUG, "%s [%u]: Weak import AXIsProcessTrustedWithOptions not found.\n",
|
||||
__FUNCTION__, __LINE__, dlError);
|
||||
|
||||
logger(LOG_LEVEL_DEBUG, "%s [%u]: Falling back to AXAPIEnabled().\n",
|
||||
__FUNCTION__, __LINE__, dlError);
|
||||
|
||||
// Old accessibility check 10.8 and older.
|
||||
is_enabled = AXAPIEnabled();
|
||||
}
|
||||
|
||||
return is_enabled;
|
||||
}
|
||||
|
||||
|
||||
UniCharCount keycode_to_unicode(CGEventRef event_ref, UniChar *buffer, UniCharCount size) {
|
||||
UniCharCount count = 0;
|
||||
|
||||
#if defined(USE_CARBON_LEGACY)
|
||||
KeyboardLayoutRef curr_keyboard_layout;
|
||||
void *inputData = NULL;
|
||||
if (KLGetCurrentKeyboardLayout(&curr_keyboard_layout) == noErr) {
|
||||
if (KLGetKeyboardLayoutProperty(curr_keyboard_layout, kKLuchrData, (const void **) &inputData) != noErr) {
|
||||
inputData = NULL;
|
||||
}
|
||||
}
|
||||
#elif defined(USE_COREFOUNDATION)
|
||||
CFDataRef inputData = NULL;
|
||||
if (CFEqual(CFRunLoopGetCurrent(), CFRunLoopGetMain())) {
|
||||
// NOTE The following block must execute on the main runloop,
|
||||
// Ex: CFEqual(CFRunLoopGetCurrent(), CFRunLoopGetMain()) to avoid
|
||||
// Exception detected while handling key input and TSMProcessRawKeyCode failed
|
||||
// (-192) errors.
|
||||
TISInputSourceRef curr_keyboard_layout = TISCopyCurrentKeyboardLayoutInputSource();
|
||||
if (curr_keyboard_layout != NULL && CFGetTypeID(curr_keyboard_layout) == TISInputSourceGetTypeID()) {
|
||||
CFDataRef data = (CFDataRef) TISGetInputSourceProperty(curr_keyboard_layout, kTISPropertyUnicodeKeyLayoutData);
|
||||
if (data != NULL && CFGetTypeID(data) == CFDataGetTypeID() && CFDataGetLength(data) > 0) {
|
||||
inputData = (CFDataRef) data;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the keyboard layout has changed to see if the dead key state needs to be discarded.
|
||||
if (prev_keyboard_layout != NULL && curr_keyboard_layout != NULL && CFEqual(curr_keyboard_layout, prev_keyboard_layout) == false) {
|
||||
deadkey_state = 0x00;
|
||||
}
|
||||
|
||||
// Release the previous keyboard layout.
|
||||
if (prev_keyboard_layout != NULL) {
|
||||
CFRelease(prev_keyboard_layout);
|
||||
prev_keyboard_layout = NULL;
|
||||
}
|
||||
|
||||
// Set the previous keyboard layout to the current layout.
|
||||
if (curr_keyboard_layout != NULL) {
|
||||
prev_keyboard_layout = curr_keyboard_layout;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(USE_CARBON_LEGACY) || defined(USE_COREFOUNDATION)
|
||||
if (inputData != NULL) {
|
||||
#ifdef USE_CARBON_LEGACY
|
||||
const UCKeyboardLayout *keyboard_layout = (const UCKeyboardLayout *) inputData;
|
||||
#else
|
||||
const UCKeyboardLayout *keyboard_layout = (const UCKeyboardLayout*) CFDataGetBytePtr(inputData);
|
||||
#endif
|
||||
|
||||
if (keyboard_layout != NULL) {
|
||||
//Extract keycode and modifier information.
|
||||
CGKeyCode keycode = CGEventGetIntegerValueField(event_ref, kCGKeyboardEventKeycode);
|
||||
CGEventFlags modifiers = CGEventGetFlags(event_ref);
|
||||
|
||||
// Disable all command modifiers for translation. This is required
|
||||
// so UCKeyTranslate will provide a keysym for the separate event.
|
||||
static const CGEventFlags cmd_modifiers = kCGEventFlagMaskCommand |
|
||||
kCGEventFlagMaskControl | kCGEventFlagMaskAlternate;
|
||||
modifiers &= ~cmd_modifiers;
|
||||
|
||||
// I don't know why but UCKeyTranslate does not process the
|
||||
// kCGEventFlagMaskAlphaShift (A.K.A. Caps Lock Mask) correctly.
|
||||
// We need to basically turn off the mask and process the capital
|
||||
// letters after UCKeyTranslate().
|
||||
bool is_caps_lock = modifiers & kCGEventFlagMaskAlphaShift;
|
||||
modifiers &= ~kCGEventFlagMaskAlphaShift;
|
||||
|
||||
// Run the translation with the saved deadkey_state.
|
||||
OSStatus status = UCKeyTranslate(
|
||||
keyboard_layout,
|
||||
keycode,
|
||||
kUCKeyActionDown, //kUCKeyActionDisplay,
|
||||
(modifiers >> 16) & 0xFF, //(modifiers >> 16) & 0xFF, || (modifiers >> 8) & 0xFF,
|
||||
LMGetKbdType(),
|
||||
kNilOptions, //kNilOptions, //kUCKeyTranslateNoDeadKeysMask
|
||||
&deadkey_state,
|
||||
size,
|
||||
&count,
|
||||
buffer);
|
||||
|
||||
if (status == noErr && count > 0) {
|
||||
if (is_caps_lock) {
|
||||
// We *had* a caps lock mask so we need to convert to uppercase.
|
||||
CFMutableStringRef keytxt = CFStringCreateMutableWithExternalCharactersNoCopy(kCFAllocatorDefault, buffer, count, size, kCFAllocatorNull);
|
||||
if (keytxt != NULL) {
|
||||
CFLocaleRef locale = CFLocaleCopyCurrent();
|
||||
CFStringUppercase(keytxt, locale);
|
||||
CFRelease(locale);
|
||||
CFRelease(keytxt);
|
||||
}
|
||||
else {
|
||||
// There was an problem creating the CFMutableStringRef.
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Make sure the buffer count is zero if an error occurred.
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
CGEventKeyboardGetUnicodeString(event_ref, size, &count, buffer);
|
||||
#endif
|
||||
|
||||
// The following codes should not be processed because they are invalid.
|
||||
if (count == 1) {
|
||||
switch (buffer[0]) {
|
||||
case 0x01: // Home
|
||||
case 0x04: // End
|
||||
case 0x05: // Help Key
|
||||
case 0x10: // Function Keys
|
||||
case 0x0B: // Page Up
|
||||
case 0x0C: // Page Down
|
||||
case 0x1F: // Volume Up
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
static const uint16_t keycode_scancode_table[][2] = {
|
||||
/* idx { keycode, scancode }, */
|
||||
/* 0 */ { VC_A, kVK_Undefined }, // 0x00
|
||||
/* 1 */ { VC_S, kVK_Escape }, // 0x01
|
||||
/* 2 */ { VC_D, kVK_ANSI_1 }, // 0x02
|
||||
/* 3 */ { VC_F, kVK_ANSI_2 }, // 0x03
|
||||
/* 4 */ { VC_H, kVK_ANSI_3 }, // 0x04
|
||||
/* 5 */ { VC_G, kVK_ANSI_4 }, // 0x05
|
||||
/* 6 */ { VC_Z, kVK_ANSI_5 }, // 0x07
|
||||
/* 7 */ { VC_X, kVK_ANSI_6 }, // 0x08
|
||||
/* 8 */ { VC_C, kVK_ANSI_7 }, // 0x09
|
||||
/* 9 */ { VC_V, kVK_ANSI_8 }, // 0x0A
|
||||
/* 10 */ { VC_UNDEFINED, kVK_ANSI_9 }, // 0x0B
|
||||
/* 11 */ { VC_B, kVK_ANSI_0 }, // 0x0C
|
||||
/* 12 */ { VC_Q, kVK_ANSI_Minus }, // 0x0D
|
||||
/* 13 */ { VC_W, kVK_ANSI_Equal }, // 0x0E
|
||||
/* 14 */ { VC_E, kVK_Delete }, // 0x0F
|
||||
/* 15 */ { VC_R, kVK_Tab }, // 0x10
|
||||
/* 16 */ { VC_Y, kVK_ANSI_Q }, // 0x11
|
||||
/* 17 */ { VC_T, kVK_ANSI_W }, // 0x12
|
||||
/* 18 */ { VC_1, kVK_ANSI_E }, // 0x13
|
||||
/* 19 */ { VC_2, kVK_ANSI_R }, // 0x14
|
||||
/* 20 */ { VC_3, kVK_ANSI_T }, // 0x15
|
||||
/* 21 */ { VC_4, kVK_ANSI_Y }, // 0x16
|
||||
/* 22 */ { VC_6, kVK_ANSI_U }, // 0x17
|
||||
/* 23 */ { VC_5, kVK_ANSI_I }, // 0x18
|
||||
/* 24 */ { VC_EQUALS, kVK_ANSI_O }, // 0x19
|
||||
/* 25 */ { VC_9, kVK_ANSI_P }, // 0x19
|
||||
/* 26 */ { VC_7, kVK_ANSI_LeftBracket }, // 0x1A
|
||||
/* 27 */ { VC_MINUS, kVK_ANSI_RightBracket }, // 0x1B
|
||||
/* 28 */ { VC_8, kVK_Return }, // 0x1C
|
||||
/* 29 */ { VC_0, kVK_Control }, // 0x1D
|
||||
/* 30 */ { VC_CLOSE_BRACKET, kVK_ANSI_A }, // 0x1E
|
||||
/* 31 */ { VC_O, kVK_ANSI_S }, // 0x1F
|
||||
/* 32 */ { VC_U, kVK_ANSI_D }, // 0x20
|
||||
/* 33 */ { VC_OPEN_BRACKET, kVK_ANSI_F }, // 0x21
|
||||
/* 34 */ { VC_I, kVK_ANSI_G }, // 0x22
|
||||
/* 35 */ { VC_P, kVK_ANSI_H }, // 0x23
|
||||
/* 36 */ { VC_ENTER, kVK_ANSI_J }, // 0x24
|
||||
/* 37 */ { VC_L, kVK_ANSI_K }, // 0x25
|
||||
/* 38 */ { VC_J, kVK_ANSI_L }, // 0x26
|
||||
/* 39 */ { VC_QUOTE, kVK_ANSI_Semicolon }, // 0x27
|
||||
/* 40 */ { VC_K, kVK_ANSI_Quote }, // 0x28
|
||||
/* 41 */ { VC_SEMICOLON, kVK_ANSI_Grave }, // 0x29
|
||||
/* 42 */ { VC_BACK_SLASH, kVK_Shift }, // 0x2A
|
||||
/* 43 */ { VC_COMMA, kVK_ANSI_Backslash }, // 0x2B
|
||||
/* 44 */ { VC_SLASH, kVK_ANSI_Z }, // 0x2C
|
||||
/* 45 */ { VC_N, kVK_ANSI_X }, // 0x2D
|
||||
/* 46 */ { VC_M, kVK_ANSI_C }, // 0x2E
|
||||
/* 47 */ { VC_PERIOD, kVK_ANSI_V }, // 0x2F
|
||||
/* 48 */ { VC_TAB, kVK_ANSI_B }, // 0x30
|
||||
/* 49 */ { VC_SPACE, kVK_ANSI_N }, // 0x31
|
||||
/* 50 */ { VC_BACKQUOTE, kVK_ANSI_M }, // 0x32
|
||||
/* 51 */ { VC_BACKSPACE, kVK_ANSI_Comma }, // 0x33
|
||||
/* 52 */ { VC_UNDEFINED, kVK_ANSI_Period }, // 0x34
|
||||
/* 53 */ { VC_ESCAPE, kVK_ANSI_Slash }, // 0x35
|
||||
/* 54 */ { VC_META_R, kVK_RightShift }, // 0x36
|
||||
/* 55 */ { VC_META_L, kVK_ANSI_KeypadMultiply }, // 0x37
|
||||
/* 56 */ { VC_SHIFT_L, kVK_Option }, // 0x38
|
||||
/* 57 */ { VC_CAPS_LOCK, kVK_Space }, // 0x39
|
||||
/* 58 */ { VC_ALT_L, kVK_CapsLock }, // 0x3A
|
||||
/* 59 */ { VC_CONTROL_L, kVK_F1 }, // 0x3B
|
||||
/* 60 */ { VC_SHIFT_R, kVK_F2 }, // 0x3C
|
||||
/* 61 */ { VC_ALT_R, kVK_F3 }, // 0x3D
|
||||
/* 62 */ { VC_CONTROL_R, kVK_F4 }, // 0x3E
|
||||
/* 63 */ { VC_UNDEFINED, kVK_F5 }, // 0x3F
|
||||
/* 64 */ { VC_F17, kVK_F6 }, // 0x40
|
||||
/* 65 */ { VC_KP_SEPARATOR, kVK_F7 }, // 0x41
|
||||
/* 66 */ { VC_UNDEFINED, kVK_F8 }, // 0x42
|
||||
/* 67 */ { VC_KP_MULTIPLY, kVK_F9 }, // 0x43
|
||||
/* 68 */ { VC_UNDEFINED, kVK_F10 }, // 0x44
|
||||
/* 69 */ { VC_KP_ADD, kVK_ANSI_KeypadClear }, // 0x45
|
||||
/* 70 */ { VC_UNDEFINED, kVK_Undefined }, // 0x46
|
||||
/* 71 */ { VC_NUM_LOCK, kVK_ANSI_Keypad7 }, // 0x47
|
||||
/* 72 */ { VC_VOLUME_UP, kVK_ANSI_Keypad8 }, // 0x48
|
||||
/* 73 */ { VC_VOLUME_DOWN, kVK_ANSI_Keypad9 }, // 0x49
|
||||
/* 74 */ { VC_VOLUME_MUTE, kVK_ANSI_KeypadMinus }, // 0x4A
|
||||
/* 75 */ { VC_KP_DIVIDE, kVK_ANSI_Keypad4 }, // 0x4B
|
||||
/* 76 */ { VC_KP_ENTER, kVK_ANSI_Keypad5 }, // 0x4C
|
||||
/* 77 */ { VC_UNDEFINED, kVK_ANSI_Keypad6 }, // 0x4D
|
||||
/* 78 */ { VC_KP_SUBTRACT, kVK_ANSI_KeypadPlus }, // 0x4E
|
||||
/* 79 */ { VC_F18, kVK_ANSI_Keypad1 }, // 0x4F
|
||||
/* 80 */ { VC_F19, kVK_ANSI_Keypad2 }, // 0x50
|
||||
/* 81 */ { VC_KP_EQUALS, kVK_ANSI_Keypad3 }, // 0x51
|
||||
/* 82 */ { VC_KP_0, kVK_ANSI_Keypad0 }, // 0x52
|
||||
/* 83 */ { VC_KP_1, kVK_ANSI_KeypadDecimal }, // 0x53
|
||||
/* 84 */ { VC_KP_2, kVK_Undefined }, // 0x54
|
||||
/* 85 */ { VC_KP_3, kVK_Undefined }, // 0x55
|
||||
/* 86 */ { VC_KP_4, kVK_Undefined }, // 0x56
|
||||
/* 87 */ { VC_KP_5, kVK_F11 }, // 0x57
|
||||
/* 88 */ { VC_KP_6, kVK_F12 }, // 0x58
|
||||
/* 89 */ { VC_KP_7, kVK_Undefined }, // 0x59
|
||||
/* 90 */ { VC_F20, kVK_Undefined }, // 0x5A
|
||||
/* 91 */ { VC_KP_8, kVK_F13 }, // 0x5B
|
||||
/* 92 */ { VC_KP_9, kVK_F14 }, // 0x5C
|
||||
/* 93 */ { VC_YEN, kVK_F15 }, // 0x5D
|
||||
/* 94 */ { VC_UNDERSCORE, kVK_Undefined }, // 0x5E
|
||||
/* 95 */ { VC_KP_COMMA, kVK_Undefined }, // 0x5F
|
||||
/* 96 */ { VC_F5, kVK_Undefined }, // 0x60
|
||||
/* 97 */ { VC_F6, kVK_Undefined }, // 0x61
|
||||
/* 98 */ { VC_F7, kVK_Undefined }, // 0x62
|
||||
/* 99 */ { VC_F3, kVK_F16 }, // 0x63
|
||||
/* 100 */ { VC_F8, kVK_F17 }, // 0x64
|
||||
/* 101 */ { VC_F9, kVK_F18 }, // 0x65
|
||||
/* 102 */ { VC_UNDEFINED, kVK_F19 }, // 0x66
|
||||
/* 103 */ { VC_F11, kVK_F20 }, // 0x67
|
||||
/* 104 */ { VC_KATAKANA, kVK_Undefined }, // 0x68
|
||||
/* 105 */ { VC_F13, kVK_Undefined }, // 0x69
|
||||
/* 106 */ { VC_F16, kVK_Undefined }, // 0x6A
|
||||
/* 107 */ { VC_F14, kVK_Undefined }, // 0x6B
|
||||
/* 108 */ { VC_UNDEFINED, kVK_Undefined }, // 0x6C FIXME kVK_JIS_Eisu same as Caps Lock ?
|
||||
/* 109 */ { VC_F10, kVK_Undefined }, // 0x6D
|
||||
/* 110 */ { VC_UNDEFINED, kVK_Undefined }, // 0x6E
|
||||
/* 111 */ { VC_F12, kVK_Undefined }, // 0x6F
|
||||
/* 112 */ { VC_UNDEFINED, kVK_JIS_Kana }, // 0x70
|
||||
/* 113 */ { VC_F15, kVK_Undefined }, // 0x71
|
||||
/* 114 */ { VC_INSERT, kVK_Undefined }, // 0x72
|
||||
/* 115 */ { VC_HOME, kVK_JIS_Underscore }, // 0x73
|
||||
/* 116 */ { VC_PAGE_UP, kVK_Undefined }, // 0x74
|
||||
/* 117 */ { VC_DELETE, kVK_Undefined }, // 0x75
|
||||
/* 118 */ { VC_F4, kVK_Undefined }, // 0x76
|
||||
/* 119 */ { VC_END, kVK_Undefined }, // 0x77
|
||||
/* 120 */ { VC_F2, kVK_Undefined }, // 0x78
|
||||
/* 121 */ { VC_PAGE_DOWN, kVK_Undefined }, // 0x79
|
||||
/* 122 */ { VC_F1, kVK_Undefined }, // 0x7A
|
||||
/* 123 */ { VC_LEFT, kVK_Undefined }, // 0x7B
|
||||
/* 124 */ { VC_RIGHT, kVK_Undefined }, // 0x7C
|
||||
/* 125 */ { VC_DOWN, kVK_JIS_Yen }, // 0x7D
|
||||
/* 126 */ { VC_UP, kVK_JIS_KeypadComma }, // 0x7E
|
||||
/* 127 */ { VC_UNDEFINED, kVK_Undefined }, // 0x7F
|
||||
|
||||
// No Offset Offset (i & 0x007F) + 128
|
||||
|
||||
/* 128 */ { VC_UNDEFINED, kVK_Undefined }, // 0x80
|
||||
/* 129 */ { VC_UNDEFINED, kVK_Undefined }, // 0x81
|
||||
/* 130 */ { VC_UNDEFINED, kVK_Undefined }, // 0x82
|
||||
/* 131 */ { VC_UNDEFINED, kVK_Undefined }, // 0x83
|
||||
/* 132 */ { VC_UNDEFINED, kVK_Undefined }, // 0x84
|
||||
/* 133 */ { VC_UNDEFINED, kVK_Undefined }, // 0x85
|
||||
/* 134 */ { VC_UNDEFINED, kVK_Undefined }, // 0x86
|
||||
/* 135 */ { VC_UNDEFINED, kVK_Undefined }, // 0x87
|
||||
/* 136 */ { VC_UNDEFINED, kVK_Undefined }, // 0x88
|
||||
/* 137 */ { VC_UNDEFINED, kVK_Undefined }, // 0x89
|
||||
/* 138 */ { VC_UNDEFINED, kVK_Undefined }, // 0x8A
|
||||
/* 139 */ { VC_UNDEFINED, kVK_Undefined }, // 0x8B
|
||||
/* 140 */ { VC_UNDEFINED, kVK_Undefined }, // 0x8C
|
||||
/* 141 */ { VC_UNDEFINED, kVK_ANSI_KeypadEquals }, // 0x8D
|
||||
/* 142 */ { VC_UNDEFINED, kVK_Undefined }, // 0x8E
|
||||
/* 143 */ { VC_UNDEFINED, kVK_Undefined }, // 0x8F
|
||||
/* 144 */ { VC_UNDEFINED, kVK_MEDIA_Previous }, // 0x90
|
||||
/* 145 */ { VC_UNDEFINED, kVK_Undefined }, // 0x91
|
||||
/* 146 */ { VC_UNDEFINED, kVK_Undefined }, // 0x92
|
||||
/* 147 */ { VC_UNDEFINED, kVK_Undefined }, // 0x93
|
||||
/* 148 */ { VC_UNDEFINED, kVK_Undefined }, // 0x94
|
||||
/* 149 */ { VC_UNDEFINED, kVK_Undefined }, // 0x95
|
||||
/* 150 */ { VC_UNDEFINED, kVK_Undefined }, // 0x96
|
||||
/* 151 */ { VC_UNDEFINED, kVK_Undefined }, // 0x97
|
||||
/* 152 */ { VC_UNDEFINED, kVK_Undefined }, // 0x98
|
||||
/* 153 */ { VC_UNDEFINED, kVK_MEDIA_Next }, // 0x99
|
||||
/* 154 */ { VC_UNDEFINED, kVK_Undefined }, // 0x9A
|
||||
/* 155 */ { VC_UNDEFINED, kVK_Undefined }, // 0x9B
|
||||
/* 156 */ { VC_UNDEFINED, kVK_ANSI_KeypadEnter }, // 0x9C
|
||||
/* 157 */ { VC_UNDEFINED, kVK_RightControl }, // 0x9D
|
||||
/* 158 */ { VC_UNDEFINED, kVK_Undefined }, // 0x9E
|
||||
/* 159 */ { VC_UNDEFINED, kVK_Undefined }, // 0x9F
|
||||
/* 160 */ { VC_UNDEFINED, kVK_Mute }, // 0xA0
|
||||
/* 161 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA1
|
||||
/* 162 */ { VC_UNDEFINED, kVK_MEDIA_Play }, // 0xA2
|
||||
/* 163 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA3
|
||||
/* 164 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA4
|
||||
/* 165 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA5
|
||||
/* 166 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA6
|
||||
/* 167 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA7
|
||||
/* 168 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA8
|
||||
/* 169 */ { VC_UNDEFINED, kVK_Undefined }, // 0xA9
|
||||
/* 170 */ { VC_UNDEFINED, kVK_Undefined }, // 0xAA
|
||||
/* 171 */ { VC_UNDEFINED, kVK_Undefined }, // 0xAB
|
||||
/* 172 */ { VC_UNDEFINED, kVK_NX_Eject }, // 0xAC
|
||||
/* 173 */ { VC_UNDEFINED, kVK_Undefined }, // 0xAD
|
||||
/* 174 */ { VC_UNDEFINED, kVK_VolumeDown }, // 0xAE
|
||||
/* 175 */ { VC_UNDEFINED, kVK_Undefined }, // 0xAF
|
||||
/* 176 */ { VC_UNDEFINED, kVK_VolumeUp }, // 0xB0
|
||||
/* 177 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB1
|
||||
/* 178 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB2
|
||||
/* 179 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB3
|
||||
/* 180 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB4
|
||||
/* 181 */ { VC_UNDEFINED, kVK_ANSI_KeypadDivide }, // 0xB5
|
||||
/* 182 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB6
|
||||
/* 183 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB7
|
||||
/* 184 */ { VC_UNDEFINED, kVK_RightOption }, // 0xB8
|
||||
/* 185 */ { VC_UNDEFINED, kVK_Undefined }, // 0xB9
|
||||
/* 186 */ { VC_UNDEFINED, kVK_Undefined }, // 0xBA
|
||||
/* 187 */ { VC_UNDEFINED, kVK_Undefined }, // 0xBB
|
||||
/* 188 */ { VC_UNDEFINED, kVK_Undefined }, // 0xBC
|
||||
/* 189 */ { VC_UNDEFINED, kVK_Undefined }, // 0xBD
|
||||
/* 190 */ { VC_UNDEFINED, kVK_Undefined }, // 0xBE
|
||||
/* 191 */ { VC_UNDEFINED, kVK_Undefined }, // 0xBF
|
||||
/* 192 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC0
|
||||
/* 193 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC1
|
||||
/* 194 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC2
|
||||
/* 195 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC3
|
||||
/* 196 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC4
|
||||
/* 197 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC5
|
||||
/* 198 */ { VC_UNDEFINED, kVK_Undefined }, // 0xC6
|
||||
/* 199 */ { VC_UNDEFINED, kVK_Home }, // 0xC7
|
||||
/* 200 */ { VC_UNDEFINED, kVK_UpArrow }, // 0xC8
|
||||
/* 201 */ { VC_UNDEFINED, kVK_PageUp }, // 0xC9
|
||||
/* 202 */ { VC_UNDEFINED, kVK_Undefined }, // 0xCA
|
||||
/* 203 */ { VC_UNDEFINED, kVK_LeftArrow }, // 0xCB
|
||||
/* 204 */ { VC_UNDEFINED, kVK_Undefined }, // 0xCC
|
||||
/* 205 */ { VC_UNDEFINED, kVK_RightArrow }, // 0xCD
|
||||
/* 206 */ { VC_UNDEFINED, kVK_Undefined }, // 0xCE
|
||||
/* 207 */ { VC_UNDEFINED, kVK_End }, // 0xCF
|
||||
/* 208 */ { VC_UNDEFINED, kVK_DownArrow }, // 0xD0
|
||||
/* 209 */ { VC_UNDEFINED, kVK_PageDown }, // 0xD1
|
||||
/* 210 */ { VC_UNDEFINED, kVK_Help }, // 0xD2
|
||||
/* 211 */ { VC_UNDEFINED, kVK_ForwardDelete }, // 0xD3
|
||||
/* 212 */ { VC_UNDEFINED, kVK_Undefined }, // 0xD4
|
||||
/* 213 */ { VC_UNDEFINED, kVK_Undefined }, // 0xD5
|
||||
/* 214 */ { VC_UNDEFINED, kVK_Undefined }, // 0xD6
|
||||
/* 215 */ { VC_UNDEFINED, kVK_Undefined }, // 0xD7
|
||||
/* 216 */ { VC_UNDEFINED, kVK_Undefined }, // 0xD8
|
||||
/* 217 */ { VC_UNDEFINED, kVK_Undefined }, // 0xD9
|
||||
/* 218 */ { VC_UNDEFINED, kVK_Undefined }, // 0xDA
|
||||
/* 219 */ { VC_UNDEFINED, kVK_Command }, // 0xDB
|
||||
/* 220 */ { VC_UNDEFINED, kVK_RightCommand }, // 0xDC
|
||||
/* 221 */ { VC_UNDEFINED, kVK_Undefined }, // 0xDD
|
||||
/* 222 */ { VC_UNDEFINED, kVK_NX_Power }, // 0xDE
|
||||
/* 223 */ { VC_UNDEFINED, kVK_Undefined }, // 0xDF
|
||||
/* 224 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE0
|
||||
/* 225 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE1
|
||||
/* 226 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE2
|
||||
/* 227 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE3
|
||||
/* 228 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE4
|
||||
/* 229 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE5
|
||||
/* 230 */ { VC_POWER, kVK_Undefined }, // 0xE6
|
||||
/* 231 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE7
|
||||
/* 232 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE8
|
||||
/* 233 */ { VC_UNDEFINED, kVK_Undefined }, // 0xE9
|
||||
/* 234 */ { VC_UNDEFINED, kVK_Undefined }, // 0xEA
|
||||
/* 235 */ { VC_UNDEFINED, kVK_Undefined }, // 0xEB
|
||||
/* 236 */ { VC_UNDEFINED, kVK_Undefined }, // 0xEC
|
||||
/* 237 */ { VC_UNDEFINED, kVK_Undefined }, // 0xED
|
||||
/* 238 */ { VC_MEDIA_EJECT, kVK_Undefined }, // 0xEE
|
||||
/* 239 */ { VC_UNDEFINED, kVK_Undefined }, // 0xEF
|
||||
/* 240 */ { VC_MEDIA_PLAY, kVK_Undefined }, // 0xF0
|
||||
/* 241 */ { VC_MEDIA_NEXT, kVK_Undefined }, // 0xF1
|
||||
/* 242 */ { VC_MEDIA_PREVIOUS, kVK_Undefined }, // 0xF2
|
||||
/* 243 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF3
|
||||
/* 244 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF4
|
||||
/* 245 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF5
|
||||
/* 246 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF6
|
||||
/* 247 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF7
|
||||
/* 248 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF8
|
||||
/* 249 */ { VC_UNDEFINED, kVK_Undefined }, // 0xF9
|
||||
/* 250 */ { VC_UNDEFINED, kVK_Undefined }, // 0xFA
|
||||
/* 251 */ { VC_UNDEFINED, kVK_Undefined }, // 0xFB
|
||||
/* 252 */ { VC_UNDEFINED, kVK_Undefined }, // 0xFC
|
||||
/* 253 */ { VC_UNDEFINED, kVK_Undefined }, // 0xFD
|
||||
/* 254 */ { VC_UNDEFINED, kVK_Undefined }, // 0xFE
|
||||
/* 255 */ { VC_UNDEFINED, kVK_Undefined }, // 0xFF
|
||||
};
|
||||
|
||||
uint16_t keycode_to_scancode(UInt64 keycode) {
|
||||
uint16_t scancode = VC_UNDEFINED;
|
||||
|
||||
// Bound check 0 <= keycode < 256
|
||||
if (keycode < sizeof(keycode_scancode_table) / sizeof(keycode_scancode_table[0])) {
|
||||
scancode = keycode_scancode_table[keycode][0];
|
||||
}
|
||||
|
||||
return scancode;
|
||||
}
|
||||
|
||||
UInt64 scancode_to_keycode(uint16_t scancode) {
|
||||
UInt64 keycode = kVK_Undefined;
|
||||
|
||||
// Bound check 0 <= keycode < 128
|
||||
if (scancode < 128) {
|
||||
keycode = keycode_scancode_table[scancode][1];
|
||||
}
|
||||
else {
|
||||
// Calculate the upper offset.
|
||||
unsigned short i = (scancode & 0x007F) | 0x80;
|
||||
|
||||
if (i < sizeof(keycode_scancode_table) / sizeof(keycode_scancode_table[1])) {
|
||||
keycode = keycode_scancode_table[i][1];
|
||||
}
|
||||
}
|
||||
|
||||
return keycode;
|
||||
}
|
||||
|
||||
void load_input_helper() {
|
||||
#if defined(USE_CARBON_LEGACY) || defined(USE_COREFOUNDATION)
|
||||
// Start with a fresh dead key state.
|
||||
//curr_deadkey_state = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void unload_input_helper() {
|
||||
#if defined(USE_CARBON_LEGACY) || defined(USE_COREFOUNDATION)
|
||||
if (prev_keyboard_layout != NULL) {
|
||||
// Cleanup tracking of the previous layout.
|
||||
CFRelease(prev_keyboard_layout);
|
||||
prev_keyboard_layout = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
@ -1,522 +0,0 @@
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_CARBON_LEGACY
|
||||
#include <Carbon/Carbon.h>
|
||||
#endif
|
||||
#ifdef USE_COREFOUNDATION
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
#ifdef USE_IOKIT
|
||||
#include <IOKit/hidsystem/IOHIDLib.h>
|
||||
#include <IOKit/hidsystem/IOHIDParameter.h>
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "../iohook.h"
|
||||
#include "input.h"
|
||||
// #include "../logger_c.h"
|
||||
|
||||
IOHOOK_API screen_data* hook_create_screen_info(unsigned char *count) {
|
||||
CGError status = kCGErrorFailure;
|
||||
screen_data* screens = NULL;
|
||||
|
||||
// Initialize count to zero.
|
||||
*count = 0;
|
||||
|
||||
// Allocate memory to hold each display id. We will just allocate our MAX
|
||||
// because its only about 1K of memory.
|
||||
// TODO This can probably be realistically cut to something like 16 or 32....
|
||||
// If you have more than 32 monitors, send me a picture and make a donation ;)
|
||||
CGDirectDisplayID *display_ids = malloc(sizeof(CGDirectDisplayID) * UCHAR_MAX);
|
||||
if (display_ids != NULL) {
|
||||
// NOTE Pass UCHAR_MAX to make sure uint32_t doesn't overflow uint8_t.
|
||||
// TOOD Test/Check whether CGGetOnlineDisplayList is more suitable...
|
||||
status = CGGetActiveDisplayList(UCHAR_MAX, display_ids, (uint32_t *) count);
|
||||
|
||||
// If there is no error and at least one monitor.
|
||||
if (status == kCGErrorSuccess && *count > 0) {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: CGGetActiveDisplayList: %li.\n",
|
||||
__FUNCTION__, __LINE__, *count);
|
||||
|
||||
// Allocate memory for the number of screens found.
|
||||
screens = malloc(sizeof(screen_data) * (*count));
|
||||
if (screens != NULL) {
|
||||
uint8_t i;
|
||||
for (i = 0; i < *count; i++) {
|
||||
//size_t width = CGDisplayPixelsWide(display_ids[i]);
|
||||
//size_t height = CGDisplayPixelsHigh(display_ids[i]);
|
||||
CGRect boundsDisp = CGDisplayBounds(display_ids[i]);
|
||||
if (boundsDisp.size.width > 0 && boundsDisp.size.height > 0) {
|
||||
screens[i] = (screen_data) {
|
||||
.number = i + 1,
|
||||
//TODO: make sure we follow the same convention for the origin
|
||||
//in all other platform implementations (upper-left)
|
||||
//TODO: document the approach with examples in order to show different
|
||||
//cases -> different resolutions (secondary monitors origin might be
|
||||
//negative)
|
||||
.x = boundsDisp.origin.x,
|
||||
.y = boundsDisp.origin.y,
|
||||
.width = boundsDisp.size.width,
|
||||
.height = boundsDisp.size.height
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: multiple_get_screen_info failed: %ld. Fallback.\n",
|
||||
__FUNCTION__, __LINE__, status);
|
||||
|
||||
size_t width = CGDisplayPixelsWide(CGMainDisplayID());
|
||||
size_t height = CGDisplayPixelsHigh(CGMainDisplayID());
|
||||
|
||||
if (width > 0 && height > 0) {
|
||||
screens = malloc(sizeof(screen_data));
|
||||
|
||||
if (screens != NULL) {
|
||||
*count = 1;
|
||||
screens[0] = (screen_data) {
|
||||
.number = 1,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = width,
|
||||
.height = height
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Free the id's after we are done.
|
||||
free(display_ids);
|
||||
}
|
||||
|
||||
return screens;
|
||||
}
|
||||
|
||||
/*
|
||||
* Apple's documentation is not very good. I was finally able to find this
|
||||
* information after many hours of googling. Value is the slider value in the
|
||||
* system preferences. That value * 15 is the rate in MS. 66 / the value is the
|
||||
* chars per second rate.
|
||||
*
|
||||
* Value MS Char/Sec
|
||||
*
|
||||
* 1 15 66 * Out of standard range *
|
||||
*
|
||||
* 2 30 33
|
||||
* 6 90 11
|
||||
* 12 180 5.5
|
||||
* 30 450 2.2
|
||||
* 60 900 1.1
|
||||
* 90 1350 0.73
|
||||
* 120 1800 0.55
|
||||
*
|
||||
* V = MS / 15
|
||||
* V = 66 / CharSec
|
||||
*
|
||||
* MS = V * 15
|
||||
* MS = (66 / CharSec) * 15
|
||||
*
|
||||
* CharSec = 66 / V
|
||||
* CharSec = 66 / (MS / 15)
|
||||
*/
|
||||
|
||||
IOHOOK_API long int hook_get_auto_repeat_rate() {
|
||||
#if defined USE_IOKIT || defined USE_COREFOUNDATION || defined USE_CARBON_LEGACY
|
||||
bool successful = false;
|
||||
SInt64 rate;
|
||||
#endif
|
||||
|
||||
long int value = -1;
|
||||
|
||||
#ifdef USE_IOKIT
|
||||
if (!successful) {
|
||||
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass));
|
||||
if (service) {
|
||||
kern_return_t kren_ret = kIOReturnError;
|
||||
io_connect_t connection;
|
||||
|
||||
kren_ret = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &connection);
|
||||
if (kren_ret == kIOReturnSuccess) {
|
||||
IOByteCount size = sizeof(rate);
|
||||
|
||||
kren_ret = IOHIDGetParameter(connection, CFSTR(kIOHIDKeyRepeatKey), (IOByteCount) sizeof(rate), &rate, &size);
|
||||
if (kren_ret == kIOReturnSuccess) {
|
||||
/* This is in some undefined unit of time that if we happen
|
||||
* to multiply by 900 gives us the time in milliseconds. We
|
||||
* add 0.5 to the result so that when we cast to long we
|
||||
* actually get a rounded result. Saves the math.h depend.
|
||||
*
|
||||
* 33,333,333.0 / 1000.0 / 1000.0 / 1000.0 == 0.033333333 * Fast *
|
||||
* 100,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 0.1
|
||||
* 200,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 0.2
|
||||
* 500,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 0.5
|
||||
* 1,000,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 1
|
||||
* 1,500,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 1.5
|
||||
* 2,000,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 2 * Slow *
|
||||
*/
|
||||
value = (long) (900.0 * ((double) rate) / 1000.0 / 1000.0 / 1000.0 + 0.5);
|
||||
successful = true;
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: IOHIDGetParameter: %li.\n",
|
||||
__FUNCTION__, __LINE__, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_COREFOUNDATION
|
||||
if (!successful) {
|
||||
CFTypeRef pref_val = CFPreferencesCopyValue(CFSTR("KeyRepeat"), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
|
||||
if (pref_val != NULL && CFGetTypeID(pref_val) == CFNumberGetTypeID()) {
|
||||
if (CFNumberGetValue((CFNumberRef) pref_val, kCFNumberSInt32Type, &rate)) {
|
||||
// This is the slider value, we must multiply by 15 to convert to milliseconds.
|
||||
value = (long) rate * 15;
|
||||
successful = true;
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: CFPreferencesCopyValue: %li.\n",
|
||||
__FUNCTION__, __LINE__, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_CARBON_LEGACY
|
||||
if (!successful) {
|
||||
// Apple documentation states that value is in 'ticks'. I am not sure
|
||||
// what that means, but it looks a lot like the arbitrary slider value.
|
||||
rate = LMGetKeyRepThresh();
|
||||
if (rate > -1) {
|
||||
/* This is the slider value, we must multiply by 15 to convert to
|
||||
* milliseconds.
|
||||
*/
|
||||
value = (long) rate * 15;
|
||||
successful = true;
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: LMGetKeyRepThresh: %li.\n",
|
||||
__FUNCTION__, __LINE__, value);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
IOHOOK_API long int hook_get_auto_repeat_delay() {
|
||||
#if defined USE_IOKIT || defined USE_COREFOUNDATION || defined USE_CARBON_LEGACY
|
||||
bool successful = false;
|
||||
SInt64 delay;
|
||||
#endif
|
||||
|
||||
long int value = -1;
|
||||
|
||||
#ifdef USE_IOKIT
|
||||
if (!successful) {
|
||||
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass));
|
||||
if (service) {
|
||||
kern_return_t kren_ret = kIOReturnError;
|
||||
io_connect_t connection;
|
||||
|
||||
kren_ret = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &connection);
|
||||
if (kren_ret == kIOReturnSuccess) {
|
||||
IOByteCount size = sizeof(delay);
|
||||
|
||||
kren_ret = IOHIDGetParameter(connection, CFSTR(kIOHIDInitialKeyRepeatKey), (IOByteCount) sizeof(delay), &delay, &size);
|
||||
if (kren_ret == kIOReturnSuccess) {
|
||||
/* This is in some undefined unit of time that if we happen
|
||||
* to multiply by 900 gives us the time in milliseconds. We
|
||||
* add 0.5 to the result so that when we cast to long we
|
||||
* actually get a rounded result. Saves the math.h depend.
|
||||
*
|
||||
* 33,333,333.0 / 1000.0 / 1000.0 / 1000.0 == 0.033333333 * Fast *
|
||||
* 100,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 0.1
|
||||
* 200,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 0.2
|
||||
* 500,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 0.5
|
||||
* 1,000,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 1
|
||||
* 1,500,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 1.5
|
||||
* 2,000,000,000.0 / 1000.0 / 1000.0 / 1000.0 == 2 * Slow *
|
||||
*/
|
||||
value = (long) (900.0 * ((double) delay) / 1000.0 / 1000.0 / 1000.0 + 0.5);
|
||||
successful = true;
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: IOHIDGetParameter: %li.\n",
|
||||
__FUNCTION__, __LINE__, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_COREFOUNDATION
|
||||
if (!successful) {
|
||||
CFTypeRef pref_val = CFPreferencesCopyValue(CFSTR("InitialKeyRepeat"), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
|
||||
if (pref_val != NULL && CFGetTypeID(pref_val) == CFNumberGetTypeID()) {
|
||||
if (CFNumberGetValue((CFNumberRef) pref_val, kCFNumberSInt32Type, &delay)) {
|
||||
// This is the slider value, we must multiply by 15 to convert to
|
||||
// milliseconds.
|
||||
value = (long) delay * 15;
|
||||
successful = true;
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: CFPreferencesCopyValue: %li.\n",
|
||||
__FUNCTION__, __LINE__, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_CARBON_LEGACY
|
||||
if (!successful) {
|
||||
// Apple documentation states that value is in 'ticks'. I am not sure
|
||||
// what that means, but it looks a lot like the arbitrary slider value.
|
||||
delay = LMGetKeyThresh();
|
||||
if (delay > -1) {
|
||||
// This is the slider value, we must multiply by 15 to convert to
|
||||
// milliseconds.
|
||||
value = (long) delay * 15;
|
||||
successful = true;
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: LMGetKeyThresh: %li.\n",
|
||||
__FUNCTION__, __LINE__, value);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
IOHOOK_API long int hook_get_pointer_acceleration_multiplier() {
|
||||
#if defined USE_IOKIT || defined USE_COREFOUNDATION
|
||||
bool successful = false;
|
||||
double multiplier;
|
||||
#endif
|
||||
|
||||
long int value = -1;
|
||||
|
||||
#ifdef USE_IOKIT
|
||||
if (!successful) {
|
||||
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass));
|
||||
|
||||
if (service) {
|
||||
kern_return_t kren_ret = kIOReturnError;
|
||||
io_connect_t connection;
|
||||
|
||||
kren_ret = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &connection);
|
||||
if (kren_ret == kIOReturnSuccess) {
|
||||
// IOByteCount size = sizeof(multiplier);
|
||||
|
||||
kren_ret = IOHIDGetAccelerationWithKey(connection, CFSTR(kIOHIDMouseAccelerationType), &multiplier);
|
||||
if (kren_ret == kIOReturnSuccess) {
|
||||
// Calculate the greatest common factor.
|
||||
|
||||
unsigned long denominator = 1000000, d = denominator;
|
||||
unsigned long numerator = multiplier * denominator, gcf = numerator;
|
||||
|
||||
while (d != 0) {
|
||||
unsigned long i = gcf % d;
|
||||
gcf = d;
|
||||
d = i;
|
||||
}
|
||||
|
||||
value = denominator / gcf;
|
||||
successful = true;
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: IOHIDGetAccelerationWithKey: %li.\n",
|
||||
__FUNCTION__, __LINE__, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_COREFOUNDATION
|
||||
if (!successful) {
|
||||
CFTypeRef pref_val = CFPreferencesCopyValue(CFSTR("com.apple.mouse.scaling"), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
|
||||
if (pref_val != NULL && CFGetTypeID(pref_val) == CFNumberGetTypeID()) {
|
||||
if (CFNumberGetValue((CFNumberRef) pref_val, kCFNumberSInt32Type, &multiplier)) {
|
||||
value = (long) multiplier;
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: CFPreferencesCopyValue: %li.\n",
|
||||
__FUNCTION__, __LINE__, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
IOHOOK_API long int hook_get_pointer_acceleration_threshold() {
|
||||
#if defined USE_COREFOUNDATION
|
||||
bool successful = false;
|
||||
SInt32 threshold;
|
||||
#endif
|
||||
|
||||
long int value = -1;
|
||||
|
||||
#ifdef USE_COREFOUNDATION
|
||||
if (!successful) {
|
||||
CFTypeRef pref_val = CFPreferencesCopyValue(CFSTR("mouseDriverMaxSpeed"), CFSTR("com.apple.universalaccess"), kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
|
||||
if (pref_val != NULL && CFGetTypeID(pref_val) == CFNumberGetTypeID()) {
|
||||
if (CFNumberGetValue((CFNumberRef) pref_val, kCFNumberSInt32Type, &threshold)) {
|
||||
value = (long) threshold;
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: CFPreferencesCopyValue: %li.\n",
|
||||
__FUNCTION__, __LINE__, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
IOHOOK_API long int hook_get_pointer_sensitivity() {
|
||||
#ifdef USE_IOKIT
|
||||
bool successful = false;
|
||||
double sensitivity;
|
||||
#endif
|
||||
|
||||
long int value = -1;
|
||||
|
||||
#ifdef USE_IOKIT
|
||||
if (!successful) {
|
||||
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass));
|
||||
|
||||
if (service) {
|
||||
kern_return_t kren_ret = kIOReturnError;
|
||||
io_connect_t connection;
|
||||
|
||||
kren_ret = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &connection);
|
||||
if (kren_ret == kIOReturnSuccess) {
|
||||
// IOByteCount size = sizeof(multiplier);
|
||||
|
||||
kren_ret = IOHIDGetAccelerationWithKey(connection, CFSTR(kIOHIDMouseAccelerationType), &sensitivity);
|
||||
if (kren_ret == kIOReturnSuccess) {
|
||||
// Calculate the greatest common factor.
|
||||
|
||||
unsigned long denominator = 1000000, d = denominator;
|
||||
unsigned long numerator = sensitivity * denominator, gcf = numerator;
|
||||
|
||||
while (d != 0) {
|
||||
unsigned long i = gcf % d;
|
||||
gcf = d;
|
||||
d = i;
|
||||
}
|
||||
|
||||
value = numerator / gcf;
|
||||
successful = true;
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: IOHIDGetAccelerationWithKey: %li.\n",
|
||||
__FUNCTION__, __LINE__, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
IOHOOK_API long int hook_get_multi_click_time() {
|
||||
#if defined USE_IOKIT || defined USE_COREFOUNDATION || defined USE_CARBON_LEGACY
|
||||
bool successful = false;
|
||||
#if defined USE_IOKIT || defined USE_CARBON_LEGACY
|
||||
// This needs to be defined only if we have USE_IOKIT or USE_CARBON_LEGACY.
|
||||
SInt64 time;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
long int value = -1;
|
||||
|
||||
#ifdef USE_IOKIT
|
||||
if (!successful) {
|
||||
io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching(kIOHIDSystemClass));
|
||||
if (service) {
|
||||
kern_return_t kren_ret = kIOReturnError;
|
||||
io_connect_t connection;
|
||||
|
||||
kren_ret = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &connection);
|
||||
if (kren_ret == kIOReturnSuccess) {
|
||||
IOByteCount size = sizeof(time);
|
||||
|
||||
kren_ret = IOHIDGetParameter(connection, CFSTR(kIOHIDClickTimeKey), (IOByteCount) sizeof(time), &time, &size);
|
||||
if (kren_ret == kIOReturnSuccess) {
|
||||
/* This is in some undefined unit of time that if we happen
|
||||
* to multiply by 900 gives us the time in milliseconds. We
|
||||
* add 0.5 to the result so that when we cast to long we
|
||||
* actually get a rounded result. Saves the math.h depend.
|
||||
*/
|
||||
value = (long) (900.0 * ((double) time) / 1000.0 / 1000.0 / 1000.0 + 0.5);
|
||||
successful = true;
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: IOHIDGetParameter: %li.\n",
|
||||
__FUNCTION__, __LINE__, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_COREFOUNDATION
|
||||
if (!successful) {
|
||||
Float32 clicktime;
|
||||
CFTypeRef pref_val = CFPreferencesCopyValue(CFSTR("com.apple.mouse.doubleClickThreshold"), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
|
||||
if (pref_val != NULL && CFGetTypeID(pref_val) == CFNumberGetTypeID()) {
|
||||
if (CFNumberGetValue((CFNumberRef) pref_val, kCFNumberFloat32Type, &clicktime)) {
|
||||
/* This is in some undefined unit of time that if we happen
|
||||
* to multiply by 900 gives us the time in milliseconds. It is
|
||||
* completely possible that this value is in seconds and should be
|
||||
* multiplied by 1000 but because IOKit values are undocumented and
|
||||
* I have no idea what a Carbon 'tick' is so there really is no way
|
||||
* to confirm this.
|
||||
*/
|
||||
value = (long) (clicktime * 900);
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: CFPreferencesCopyValue: %li.\n",
|
||||
__FUNCTION__, __LINE__, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_CARBON_LEGACY
|
||||
if (!successful) {
|
||||
// Apple documentation states that value is in 'ticks'. I am not sure
|
||||
// what that means, but it looks a lot like the arbitrary slider value.
|
||||
time = GetDblTime();
|
||||
if (time > -1) {
|
||||
// This is the slider value, we must multiply by 15 to convert to
|
||||
// milliseconds.
|
||||
value = (long) time * 15;
|
||||
successful = true;
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: GetDblTime: %li.\n",
|
||||
__FUNCTION__, __LINE__, value);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
// Create a shared object constructor.
|
||||
__attribute__ ((constructor))
|
||||
void on_library_load() {
|
||||
// Initialize Native Input Functions.
|
||||
load_input_helper();
|
||||
}
|
||||
|
||||
// Create a shared object destructor.
|
||||
__attribute__ ((destructor))
|
||||
void on_library_unload() {
|
||||
// Disable the event hook.
|
||||
//hook_stop();
|
||||
|
||||
// Cleanup native input functions.
|
||||
unload_input_helper();
|
||||
}
|
@ -1,441 +0,0 @@
|
||||
|
||||
#ifndef __IOHOOK_H
|
||||
#define __IOHOOK_H
|
||||
|
||||
// #include "../../base/os.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Begin Error Codes */
|
||||
#define IOHOOK_SUCCESS 0x00
|
||||
#define IOHOOK_FAILURE 0x01
|
||||
|
||||
// System level errors.
|
||||
#define IOHOOK_ERROR_OUT_OF_MEMORY 0x02
|
||||
|
||||
// Unix specific errors.
|
||||
#define IOHOOK_ERROR_X_OPEN_DISPLAY 0x20
|
||||
#define IOHOOK_ERROR_X_RECORD_NOT_FOUND 0x21
|
||||
#define IOHOOK_ERROR_X_RECORD_ALLOC_RANGE 0x22
|
||||
#define IOHOOK_ERROR_X_RECORD_CREATE_CONTEXT 0x23
|
||||
#define IOHOOK_ERROR_X_RECORD_ENABLE_CONTEXT 0x24
|
||||
#define IOHOOK_ERROR_X_RECORD_GET_CONTEXT 0x25
|
||||
|
||||
// Windows specific errors.
|
||||
#define IOHOOK_ERROR_SET_WINDOWS_HOOK_EX 0x30
|
||||
#define IOHOOK_ERROR_GET_MODULE_HANDLE 0x31
|
||||
|
||||
// Darwin specific errors.
|
||||
#define IOHOOK_ERROR_AXAPI_DISABLED 0x40
|
||||
#define IOHOOK_ERROR_CREATE_EVENT_PORT 0x41
|
||||
#define IOHOOK_ERROR_CREATE_RUN_LOOP_SOURCE 0x42
|
||||
#define IOHOOK_ERROR_GET_RUNLOOP 0x43
|
||||
#define IOHOOK_ERROR_CREATE_OBSERVER 0x44
|
||||
/* End Error Codes */
|
||||
|
||||
/* Begin Log Levels and Function Prototype */
|
||||
typedef enum _log_level {
|
||||
LOG_LEVEL_DEBUG = 1,
|
||||
LOG_LEVEL_INFO,
|
||||
LOG_LEVEL_WARN,
|
||||
LOG_LEVEL_ERROR
|
||||
} log_level;
|
||||
|
||||
// Logger callback function prototype.
|
||||
typedef bool (*logger_t)(unsigned int, const char *, ...);
|
||||
/* End Log Levels and Function Prototype */
|
||||
|
||||
/* Begin Virtual Event Types and Data Structures */
|
||||
typedef enum _event_type {
|
||||
EVENT_HOOK_ENABLED = 1,
|
||||
EVENT_HOOK_DISABLED,
|
||||
EVENT_KEY_TYPED,
|
||||
EVENT_KEY_PRESSED,
|
||||
EVENT_KEY_RELEASED,
|
||||
EVENT_MOUSE_CLICKED,
|
||||
EVENT_MOUSE_PRESSED,
|
||||
EVENT_MOUSE_RELEASED,
|
||||
EVENT_MOUSE_MOVED,
|
||||
EVENT_MOUSE_DRAGGED,
|
||||
EVENT_MOUSE_WHEEL
|
||||
} event_type;
|
||||
|
||||
typedef struct _screen_data {
|
||||
uint8_t number;
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
} screen_data;
|
||||
|
||||
typedef struct _keyboard_event_data {
|
||||
uint16_t keycode;
|
||||
uint16_t rawcode;
|
||||
uint16_t keychar;
|
||||
// char *keychar;
|
||||
} keyboard_event_data,
|
||||
key_pressed_event_data,
|
||||
key_released_event_data,
|
||||
key_typed_event_data;
|
||||
|
||||
typedef struct _mouse_event_data {
|
||||
uint16_t button;
|
||||
uint16_t clicks;
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
} mouse_event_data,
|
||||
mouse_pressed_event_data,
|
||||
mouse_released_event_data,
|
||||
mouse_clicked_event_data;
|
||||
|
||||
typedef struct _mouse_wheel_event_data {
|
||||
uint16_t clicks;
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
uint8_t type;
|
||||
uint16_t amount;
|
||||
int16_t rotation;
|
||||
uint8_t direction;
|
||||
} mouse_wheel_event_data;
|
||||
|
||||
typedef struct _iohook_event {
|
||||
event_type type;
|
||||
uint64_t time;
|
||||
uint16_t mask;
|
||||
uint16_t reserved;
|
||||
union {
|
||||
keyboard_event_data keyboard;
|
||||
mouse_event_data mouse;
|
||||
mouse_wheel_event_data wheel;
|
||||
} data;
|
||||
} iohook_event;
|
||||
|
||||
typedef void (*dispatcher_t)(iohook_event *const);
|
||||
/* End Virtual Event Types and Data Structures */
|
||||
|
||||
|
||||
/* Begin Virtual Key Codes */
|
||||
#define VC_ESCAPE 0x0001
|
||||
|
||||
// Begin Function Keys
|
||||
#define VC_F1 0x003B
|
||||
#define VC_F2 0x003C
|
||||
#define VC_F3 0x003D
|
||||
#define VC_F4 0x003E
|
||||
#define VC_F5 0x003F
|
||||
#define VC_F6 0x0040
|
||||
#define VC_F7 0x0041
|
||||
#define VC_F8 0x0042
|
||||
#define VC_F9 0x0043
|
||||
#define VC_F10 0x0044
|
||||
#define VC_F11 0x0057
|
||||
#define VC_F12 0x0058
|
||||
|
||||
#define VC_F13 0x005B
|
||||
#define VC_F14 0x005C
|
||||
#define VC_F15 0x005D
|
||||
#define VC_F16 0x0063
|
||||
#define VC_F17 0x0064
|
||||
#define VC_F18 0x0065
|
||||
#define VC_F19 0x0066
|
||||
#define VC_F20 0x0067
|
||||
#define VC_F21 0x0068
|
||||
#define VC_F22 0x0069
|
||||
#define VC_F23 0x006A
|
||||
#define VC_F24 0x006B
|
||||
// End Function Keys
|
||||
|
||||
|
||||
// Begin Alphanumeric Zone
|
||||
#define VC_BACKQUOTE 0x0029
|
||||
|
||||
#define VC_1 0x0002
|
||||
#define VC_2 0x0003
|
||||
#define VC_3 0x0004
|
||||
#define VC_4 0x0005
|
||||
#define VC_5 0x0006
|
||||
#define VC_6 0x0007
|
||||
#define VC_7 0x0008
|
||||
#define VC_8 0x0009
|
||||
#define VC_9 0x000A
|
||||
#define VC_0 0x000B
|
||||
|
||||
#define VC_MINUS 0x000C // '-'
|
||||
#define VC_EQUALS 0x000D // '='
|
||||
#define VC_BACKSPACE 0x000E
|
||||
|
||||
#define VC_TAB 0x000F
|
||||
#define VC_CAPS_LOCK 0x003A
|
||||
|
||||
#define VC_A 0x001E
|
||||
#define VC_B 0x0030
|
||||
#define VC_C 0x002E
|
||||
#define VC_D 0x0020
|
||||
#define VC_E 0x0012
|
||||
#define VC_F 0x0021
|
||||
#define VC_G 0x0022
|
||||
#define VC_H 0x0023
|
||||
#define VC_I 0x0017
|
||||
#define VC_J 0x0024
|
||||
#define VC_K 0x0025
|
||||
#define VC_L 0x0026
|
||||
#define VC_M 0x0032
|
||||
#define VC_N 0x0031
|
||||
#define VC_O 0x0018
|
||||
#define VC_P 0x0019
|
||||
#define VC_Q 0x0010
|
||||
#define VC_R 0x0013
|
||||
#define VC_S 0x001F
|
||||
#define VC_T 0x0014
|
||||
#define VC_U 0x0016
|
||||
#define VC_V 0x002F
|
||||
#define VC_W 0x0011
|
||||
#define VC_X 0x002D
|
||||
#define VC_Y 0x0015
|
||||
#define VC_Z 0x002C
|
||||
|
||||
#define VC_OPEN_BRACKET 0x001A // '['
|
||||
#define VC_CLOSE_BRACKET 0x001B // ']'
|
||||
#define VC_BACK_SLASH 0x002B // '\'
|
||||
|
||||
#define VC_SEMICOLON 0x0027 // ';'
|
||||
#define VC_QUOTE 0x0028
|
||||
#define VC_ENTER 0x001C
|
||||
|
||||
#define VC_COMMA 0x0033 // ','
|
||||
#define VC_PERIOD 0x0034 // '.'
|
||||
#define VC_SLASH 0x0035 // '/'
|
||||
|
||||
#define VC_SPACE 0x0039
|
||||
// End Alphanumeric Zone
|
||||
|
||||
|
||||
#define VC_PRINTSCREEN 0x0E37
|
||||
#define VC_SCROLL_LOCK 0x0046
|
||||
#define VC_PAUSE 0x0E45
|
||||
|
||||
|
||||
// Begin Edit Key Zone
|
||||
#define VC_INSERT 0x0E52
|
||||
#define VC_DELETE 0x0E53
|
||||
#define VC_HOME 0x0E47
|
||||
#define VC_END 0x0E4F
|
||||
#define VC_PAGE_UP 0x0E49
|
||||
#define VC_PAGE_DOWN 0x0E51
|
||||
// End Edit Key Zone
|
||||
|
||||
|
||||
// Begin Cursor Key Zone
|
||||
#define VC_UP 0xE048
|
||||
#define VC_LEFT 0xE04B
|
||||
#define VC_CLEAR 0xE04C
|
||||
#define VC_RIGHT 0xE04D
|
||||
#define VC_DOWN 0xE050
|
||||
// End Cursor Key Zone
|
||||
|
||||
|
||||
// Begin Numeric Zone
|
||||
#define VC_NUM_LOCK 0x0045
|
||||
#define VC_KP_DIVIDE 0x0E35
|
||||
#define VC_KP_MULTIPLY 0x0037
|
||||
#define VC_KP_SUBTRACT 0x004A
|
||||
#define VC_KP_EQUALS 0x0E0D
|
||||
#define VC_KP_ADD 0x004E
|
||||
#define VC_KP_ENTER 0x0E1C
|
||||
#define VC_KP_SEPARATOR 0x0053
|
||||
|
||||
#define VC_KP_1 0x004F
|
||||
#define VC_KP_2 0x0050
|
||||
#define VC_KP_3 0x0051
|
||||
#define VC_KP_4 0x004B
|
||||
#define VC_KP_5 0x004C
|
||||
#define VC_KP_6 0x004D
|
||||
#define VC_KP_7 0x0047
|
||||
#define VC_KP_8 0x0048
|
||||
#define VC_KP_9 0x0049
|
||||
#define VC_KP_0 0x0052
|
||||
|
||||
#define VC_KP_END 0xEE00 | VC_KP_1
|
||||
#define VC_KP_DOWN 0xEE00 | VC_KP_2
|
||||
#define VC_KP_PAGE_DOWN 0xEE00 | VC_KP_3
|
||||
#define VC_KP_LEFT 0xEE00 | VC_KP_4
|
||||
#define VC_KP_CLEAR 0xEE00 | VC_KP_5
|
||||
#define VC_KP_RIGHT 0xEE00 | VC_KP_6
|
||||
#define VC_KP_HOME 0xEE00 | VC_KP_7
|
||||
#define VC_KP_UP 0xEE00 | VC_KP_8
|
||||
#define VC_KP_PAGE_UP 0xEE00 | VC_KP_9
|
||||
#define VC_KP_INSERT 0xEE00 | VC_KP_0
|
||||
#define VC_KP_DELETE 0xEE00 | VC_KP_SEPARATOR
|
||||
// End Numeric Zone
|
||||
|
||||
|
||||
// Begin Modifier and Control Keys
|
||||
#define VC_SHIFT_L 0x002A
|
||||
#define VC_SHIFT_R 0x0036
|
||||
#define VC_CONTROL_L 0x001D
|
||||
#define VC_CONTROL_R 0x0E1D
|
||||
#define VC_ALT_L 0x0038 // Option or Alt Key
|
||||
#define VC_ALT_R 0x0E38 // Option or Alt Key
|
||||
#define VC_META_L 0x0E5B // Windows or Command Key
|
||||
#define VC_META_R 0x0E5C // Windows or Command Key
|
||||
#define VC_CONTEXT_MENU 0x0E5D
|
||||
// End Modifier and Control Keys
|
||||
|
||||
|
||||
// Begin Media Control Keys
|
||||
#define VC_POWER 0xE05E
|
||||
#define VC_SLEEP 0xE05F
|
||||
#define VC_WAKE 0xE063
|
||||
|
||||
#define VC_MEDIA_PLAY 0xE022
|
||||
#define VC_MEDIA_STOP 0xE024
|
||||
#define VC_MEDIA_PREVIOUS 0xE010
|
||||
#define VC_MEDIA_NEXT 0xE019
|
||||
#define VC_MEDIA_SELECT 0xE06D
|
||||
#define VC_MEDIA_EJECT 0xE02C
|
||||
|
||||
#define VC_VOLUME_MUTE 0xE020
|
||||
#define VC_VOLUME_UP 0xE030
|
||||
#define VC_VOLUME_DOWN 0xE02E
|
||||
|
||||
#define VC_APP_MAIL 0xE06C
|
||||
#define VC_APP_CALCULATOR 0xE021
|
||||
#define VC_APP_MUSIC 0xE03C
|
||||
#define VC_APP_PICTURES 0xE064
|
||||
|
||||
#define VC_BROWSER_SEARCH 0xE065
|
||||
#define VC_BROWSER_HOME 0xE032
|
||||
#define VC_BROWSER_BACK 0xE06A
|
||||
#define VC_BROWSER_FORWARD 0xE069
|
||||
#define VC_BROWSER_STOP 0xE068
|
||||
#define VC_BROWSER_REFRESH 0xE067
|
||||
#define VC_BROWSER_FAVORITES 0xE066
|
||||
// End Media Control Keys
|
||||
|
||||
// Begin Japanese Language Keys
|
||||
#define VC_KATAKANA 0x0070
|
||||
#define VC_UNDERSCORE 0x0073
|
||||
#define VC_FURIGANA 0x0077
|
||||
#define VC_KANJI 0x0079
|
||||
#define VC_HIRAGANA 0x007B
|
||||
#define VC_YEN 0x007D
|
||||
#define VC_KP_COMMA 0x007E
|
||||
// End Japanese Language Keys
|
||||
|
||||
// Begin Sun keyboards
|
||||
#define VC_SUN_HELP 0xFF75
|
||||
|
||||
#define VC_SUN_STOP 0xFF78
|
||||
#define VC_SUN_PROPS 0xFF76
|
||||
#define VC_SUN_FRONT 0xFF77
|
||||
#define VC_SUN_OPEN 0xFF74
|
||||
#define VC_SUN_FIND 0xFF7E
|
||||
#define VC_SUN_AGAIN 0xFF79
|
||||
#define VC_SUN_UNDO 0xFF7A
|
||||
#define VC_SUN_COPY 0xFF7C
|
||||
#define VC_SUN_INSERT 0xFF7D
|
||||
#define VC_SUN_CUT 0xFF7B
|
||||
// End Sun keyboards
|
||||
|
||||
#define VC_UNDEFINED 0x0000 // KeyCode Unknown
|
||||
|
||||
#define CHAR_UNDEFINED 0xFFFF // CharCode Unknown
|
||||
/* End Virtual Key Codes */
|
||||
|
||||
|
||||
/* Begin Virtual Modifier Masks */
|
||||
#define MASK_SHIFT_L 1 << 0
|
||||
#define MASK_CTRL_L 1 << 1
|
||||
#define MASK_META_L 1 << 2
|
||||
#define MASK_ALT_L 1 << 3
|
||||
|
||||
#define MASK_SHIFT_R 1 << 4
|
||||
#define MASK_CTRL_R 1 << 5
|
||||
#define MASK_META_R 1 << 6
|
||||
#define MASK_ALT_R 1 << 7
|
||||
|
||||
#define MASK_SHIFT MASK_SHIFT_L | MASK_SHIFT_R
|
||||
#define MASK_CTRL MASK_CTRL_L | MASK_CTRL_R
|
||||
#define MASK_META MASK_META_L | MASK_META_R
|
||||
#define MASK_ALT MASK_ALT_L | MASK_ALT_R
|
||||
|
||||
#define MASK_BUTTON1 1 << 8
|
||||
#define MASK_BUTTON2 1 << 9
|
||||
#define MASK_BUTTON3 1 << 10
|
||||
#define MASK_BUTTON4 1 << 11
|
||||
#define MASK_BUTTON5 1 << 12
|
||||
|
||||
#define MASK_NUM_LOCK 1 << 13
|
||||
#define MASK_CAPS_LOCK 1 << 14
|
||||
#define MASK_SCROLL_LOCK 1 << 15
|
||||
/* End Virtual Modifier Masks */
|
||||
|
||||
|
||||
/* Begin Virtual Mouse Buttons */
|
||||
#define MOUSE_NOBUTTON 0 // Any Button
|
||||
#define MOUSE_BUTTON1 1 // Left Button
|
||||
#define MOUSE_BUTTON2 2 // Right Button
|
||||
#define MOUSE_BUTTON3 3 // Middle Button
|
||||
#define MOUSE_BUTTON4 4 // Extra Mouse Button
|
||||
#define MOUSE_BUTTON5 5 // Extra Mouse Button
|
||||
|
||||
#define WHEEL_UNIT_SCROLL 1
|
||||
#define WHEEL_BLOCK_SCROLL 2
|
||||
|
||||
#define WHEEL_VERTICAL_DIRECTION 3
|
||||
#define WHEEL_HORIZONTAL_DIRECTION 4
|
||||
/* End Virtual Mouse Buttons */
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#define IOHOOK_API __declspec(dllexport)
|
||||
#else
|
||||
#define IOHOOK_API
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Set the logger callback functions.
|
||||
IOHOOK_API void hook_set_logger_proc(logger_t logger_proc);
|
||||
|
||||
// Send a virtual event back to the system.
|
||||
IOHOOK_API void hook_post_event(iohook_event * const event);
|
||||
|
||||
// Set the event callback function.
|
||||
IOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc);
|
||||
|
||||
// Insert the event hook.
|
||||
IOHOOK_API int hook_run();
|
||||
|
||||
// Withdraw the event hook.
|
||||
IOHOOK_API int hook_stop();
|
||||
|
||||
// Retrieves an array of screen data for each available monitor.
|
||||
IOHOOK_API screen_data* hook_create_screen_info(unsigned char *count);
|
||||
|
||||
// Retrieves the keyboard auto repeat rate.
|
||||
IOHOOK_API long int hook_get_auto_repeat_rate();
|
||||
|
||||
// Retrieves the keyboard auto repeat delay.
|
||||
IOHOOK_API long int hook_get_auto_repeat_delay();
|
||||
|
||||
// Retrieves the mouse acceleration multiplier.
|
||||
IOHOOK_API long int hook_get_pointer_acceleration_multiplier();
|
||||
|
||||
// Retrieves the mouse acceleration threshold.
|
||||
IOHOOK_API long int hook_get_pointer_acceleration_threshold();
|
||||
|
||||
// Retrieves the mouse sensitivity.
|
||||
IOHOOK_API long int hook_get_pointer_sensitivity();
|
||||
|
||||
// Retrieves the double/triple click interval.
|
||||
IOHOOK_API long int hook_get_multi_click_time();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,15 +0,0 @@
|
||||
|
||||
#ifndef _included_logger
|
||||
#define _included_logger
|
||||
|
||||
#include "iohook.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef __FUNCTION__
|
||||
#define __FUNCTION__ __func__
|
||||
#endif
|
||||
|
||||
// logger(level, message)
|
||||
extern logger_t logger;
|
||||
|
||||
#endif
|
@ -1,53 +0,0 @@
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "iohook.h"
|
||||
#include "logger.h"
|
||||
|
||||
static bool default_logger(unsigned int level, const char *format, ...) {
|
||||
bool status = false;
|
||||
|
||||
#ifndef USE_QUIET
|
||||
va_list args;
|
||||
switch (level) {
|
||||
#ifdef USE_DEBUG
|
||||
case LOG_LEVEL_DEBUG:
|
||||
#endif
|
||||
case LOG_LEVEL_INFO:
|
||||
va_start(args, format);
|
||||
status = vfprintf(stdout, format, args) >= 0;
|
||||
va_end(args);
|
||||
break;
|
||||
|
||||
case LOG_LEVEL_WARN:
|
||||
case LOG_LEVEL_ERROR:
|
||||
va_start(args, format);
|
||||
status = vfprintf(stderr, format, args) >= 0;
|
||||
va_end(args);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// Current logger function pointer, this should never be null.
|
||||
// FIXME This should be static and wrapped with a public facing function.
|
||||
logger_t logger = &default_logger;
|
||||
|
||||
|
||||
IOHOOK_API void hookSetlogger(logger_t logger_proc) {
|
||||
if (logger_proc == NULL) {
|
||||
logger = &default_logger;
|
||||
}
|
||||
else {
|
||||
logger = logger_proc;
|
||||
}
|
||||
}
|
@ -1,328 +0,0 @@
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include "../iohook.h"
|
||||
#include <windows.h>
|
||||
|
||||
// #include "logger.h"
|
||||
#include "input.h"
|
||||
|
||||
// Some buggy versions of MinGW and MSys do not include these constants in winuser.h.
|
||||
#ifndef MAPVK_VK_TO_VSC
|
||||
#define MAPVK_VK_TO_VSC 0
|
||||
#define MAPVK_VSC_TO_VK 1
|
||||
#define MAPVK_VK_TO_CHAR 2
|
||||
#define MAPVK_VSC_TO_VK_EX 3
|
||||
#endif
|
||||
// Some buggy versions of MinGW and MSys only define this value for Windows
|
||||
// versions >= 0x0600 (Windows Vista) when it should be 0x0500 (Windows 2000).
|
||||
#ifndef MAPVK_VK_TO_VSC_EX
|
||||
#define MAPVK_VK_TO_VSC_EX 4
|
||||
#endif
|
||||
|
||||
#ifndef KEYEVENTF_SCANCODE
|
||||
#define KEYEVENTF_EXTENDEDKEY 0x0001
|
||||
#define KEYEVENTF_KEYUP 0x0002
|
||||
#define KEYEVENTF_UNICODE 0x0004
|
||||
#define KEYEVENTF_SCANCODE 0x0008
|
||||
#endif
|
||||
|
||||
#ifndef KEYEVENTF_KEYDOWN
|
||||
#define KEYEVENTF_KEYDOWN 0x0000
|
||||
#endif
|
||||
|
||||
#define MAX_WINDOWS_COORD_VALUE 65535
|
||||
|
||||
static UINT keymask_lookup[8] = {
|
||||
VK_LSHIFT,
|
||||
VK_LCONTROL,
|
||||
VK_LWIN,
|
||||
VK_LMENU,
|
||||
|
||||
VK_RSHIFT,
|
||||
VK_RCONTROL,
|
||||
VK_RWIN,
|
||||
VK_RMENU
|
||||
};
|
||||
|
||||
IOHOOK_API void hook_post_event(iohook_event * const event) {
|
||||
//FIXME implement multiple monitor support
|
||||
uint16_t screen_width = GetSystemMetrics( SM_CXSCREEN );
|
||||
uint16_t screen_height = GetSystemMetrics( SM_CYSCREEN );
|
||||
|
||||
unsigned char events_size = 0, events_max = 28;
|
||||
INPUT *events = malloc(sizeof(INPUT) * events_max);
|
||||
|
||||
if (event->mask & (MASK_SHIFT | MASK_CTRL | MASK_META | MASK_ALT)) {
|
||||
unsigned int i;
|
||||
for (i = 0; i < sizeof(keymask_lookup) / sizeof(UINT); i++) {
|
||||
if (event->mask & 1 << i) {
|
||||
events[events_size].type = INPUT_KEYBOARD;
|
||||
events[events_size].ki.wVk = keymask_lookup[i];
|
||||
events[events_size].ki.dwFlags = KEYEVENTF_KEYDOWN;
|
||||
events[events_size].ki.time = 0; // Use current system time.
|
||||
events_size++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event->mask & (MASK_BUTTON1 | MASK_BUTTON2 | MASK_BUTTON3 | MASK_BUTTON4 | MASK_BUTTON5)) {
|
||||
events[events_size].type = INPUT_MOUSE;
|
||||
events[events_size].mi.dx = 0; // Relative mouse movement due to
|
||||
events[events_size].mi.dy = 0; // MOUSEEVENTF_ABSOLUTE not being set.
|
||||
events[events_size].mi.mouseData = 0x00;
|
||||
events[events_size].mi.time = 0; // Use current system time.
|
||||
|
||||
if (event->mask & MASK_BUTTON1) {
|
||||
events[events_size].mi.mouseData |= MOUSEEVENTF_LEFTDOWN;
|
||||
}
|
||||
|
||||
if (event->mask & MASK_BUTTON2) {
|
||||
events[events_size].mi.mouseData |= MOUSEEVENTF_RIGHTDOWN;
|
||||
}
|
||||
|
||||
if (event->mask & MASK_BUTTON3) {
|
||||
events[events_size].mi.mouseData |= MOUSEEVENTF_MIDDLEDOWN;
|
||||
}
|
||||
|
||||
if (event->mask & MASK_BUTTON4) {
|
||||
events[events_size].mi.mouseData = XBUTTON1;
|
||||
events[events_size].mi.mouseData |= MOUSEEVENTF_XDOWN;
|
||||
}
|
||||
|
||||
if (event->mask & MASK_BUTTON5) {
|
||||
events[events_size].mi.mouseData = XBUTTON2;
|
||||
events[events_size].mi.dwFlags |= MOUSEEVENTF_XDOWN;
|
||||
}
|
||||
|
||||
events_size++;
|
||||
}
|
||||
|
||||
|
||||
switch (event->type) {
|
||||
case EVENT_KEY_PRESSED:
|
||||
events[events_size].ki.wVk = scancode_to_keycode(event->data.keyboard.keycode);
|
||||
if (events[events_size].ki.wVk != 0x0000) {
|
||||
events[events_size].type = INPUT_KEYBOARD;
|
||||
events[events_size].ki.dwFlags = KEYEVENTF_KEYDOWN; // |= KEYEVENTF_SCANCODE;
|
||||
events[events_size].ki.wScan = 0; // event->data.keyboard.keycode;
|
||||
events[events_size].ki.time = 0; // GetSystemTime()
|
||||
events_size++;
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: Unable to lookup scancode: %li\n",
|
||||
__FUNCTION__, __LINE__,
|
||||
event->data.keyboard.keycode);
|
||||
}
|
||||
break;
|
||||
|
||||
case EVENT_KEY_RELEASED:
|
||||
events[events_size].ki.wVk = scancode_to_keycode(event->data.keyboard.keycode);
|
||||
if (events[events_size].ki.wVk != 0x0000) {
|
||||
events[events_size].type = INPUT_KEYBOARD;
|
||||
events[events_size].ki.dwFlags = KEYEVENTF_KEYUP; // |= KEYEVENTF_SCANCODE;
|
||||
events[events_size].ki.wVk = scancode_to_keycode(event->data.keyboard.keycode);
|
||||
events[events_size].ki.wScan = 0; // event->data.keyboard.keycode;
|
||||
events[events_size].ki.time = 0; // GetSystemTime()
|
||||
events_size++;
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: Unable to lookup scancode: %li\n",
|
||||
__FUNCTION__, __LINE__,
|
||||
event->data.keyboard.keycode);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case EVENT_MOUSE_PRESSED:
|
||||
events[events_size].type = INPUT_MOUSE;
|
||||
events[events_size].mi.dwFlags = MOUSEEVENTF_XDOWN;
|
||||
|
||||
switch (event->data.mouse.button) {
|
||||
case MOUSE_BUTTON1:
|
||||
events[events_size].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
|
||||
break;
|
||||
|
||||
case MOUSE_BUTTON2:
|
||||
events[events_size].mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
|
||||
break;
|
||||
|
||||
case MOUSE_BUTTON3:
|
||||
events[events_size].mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN;
|
||||
break;
|
||||
|
||||
case MOUSE_BUTTON4:
|
||||
events[events_size].mi.mouseData = XBUTTON1;
|
||||
break;
|
||||
|
||||
case MOUSE_BUTTON5:
|
||||
events[events_size].mi.mouseData = XBUTTON2;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Extra buttons.
|
||||
if (event->data.mouse.button > 3) {
|
||||
events[events_size].mi.mouseData = event->data.mouse.button - 3;
|
||||
}
|
||||
}
|
||||
|
||||
events[events_size].mi.dx = event->data.mouse.x * (MAX_WINDOWS_COORD_VALUE / screen_width) + 1;
|
||||
events[events_size].mi.dy = event->data.mouse.y * (MAX_WINDOWS_COORD_VALUE / screen_height) + 1;
|
||||
|
||||
events[events_size].mi.dwFlags |= MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
|
||||
events[events_size].mi.time = 0; // GetSystemTime()
|
||||
|
||||
events_size++;
|
||||
break;
|
||||
|
||||
case EVENT_MOUSE_RELEASED:
|
||||
events[events_size].type = INPUT_MOUSE;
|
||||
events[events_size].mi.dwFlags = MOUSEEVENTF_XUP;
|
||||
|
||||
switch (event->data.mouse.button) {
|
||||
case MOUSE_BUTTON1:
|
||||
events[events_size].mi.dwFlags = MOUSEEVENTF_LEFTUP;
|
||||
break;
|
||||
|
||||
case MOUSE_BUTTON2:
|
||||
events[events_size].mi.dwFlags = MOUSEEVENTF_RIGHTUP;
|
||||
break;
|
||||
|
||||
case MOUSE_BUTTON3:
|
||||
events[events_size].mi.dwFlags = MOUSEEVENTF_MIDDLEUP;
|
||||
break;
|
||||
|
||||
case MOUSE_BUTTON4:
|
||||
events[events_size].mi.mouseData = XBUTTON1;
|
||||
break;
|
||||
|
||||
case MOUSE_BUTTON5:
|
||||
events[events_size].mi.mouseData = XBUTTON2;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Extra buttons.
|
||||
if (event->data.mouse.button > 3) {
|
||||
events[events_size].mi.mouseData = event->data.mouse.button - 3;
|
||||
}
|
||||
}
|
||||
|
||||
events[events_size].mi.dx = event->data.mouse.x * (MAX_WINDOWS_COORD_VALUE / screen_width) + 1;
|
||||
events[events_size].mi.dy = event->data.mouse.y * (MAX_WINDOWS_COORD_VALUE / screen_height) + 1;
|
||||
|
||||
events[events_size].mi.dwFlags |= MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
|
||||
events[events_size].mi.time = 0; // GetSystemTime()
|
||||
events_size++;
|
||||
break;
|
||||
|
||||
|
||||
case EVENT_MOUSE_WHEEL:
|
||||
events[events_size].type = INPUT_MOUSE;
|
||||
events[events_size].mi.dwFlags = MOUSEEVENTF_WHEEL;
|
||||
|
||||
// type, amount and rotation?
|
||||
events[events_size].mi.mouseData = event->data.wheel.amount * event->data.wheel.rotation * WHEEL_DELTA;
|
||||
|
||||
events[events_size].mi.dx = event->data.wheel.x * (MAX_WINDOWS_COORD_VALUE / screen_width) + 1;
|
||||
events[events_size].mi.dy = event->data.wheel.y * (MAX_WINDOWS_COORD_VALUE / screen_height) + 1;
|
||||
|
||||
events[events_size].mi.dwFlags |= MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
|
||||
events[events_size].mi.time = 0; // GetSystemTime()
|
||||
events_size++;
|
||||
break;
|
||||
|
||||
|
||||
case EVENT_MOUSE_DRAGGED:
|
||||
// The button masks are all applied with the modifier masks.
|
||||
|
||||
case EVENT_MOUSE_MOVED:
|
||||
events[events_size].type = INPUT_MOUSE;
|
||||
events[events_size].mi.dwFlags = MOUSEEVENTF_MOVE;
|
||||
|
||||
events[events_size].mi.dx = event->data.mouse.x * (MAX_WINDOWS_COORD_VALUE / screen_width) + 1;
|
||||
events[events_size].mi.dy = event->data.mouse.y * (MAX_WINDOWS_COORD_VALUE / screen_height) + 1;
|
||||
|
||||
events[events_size].mi.dwFlags |= MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
|
||||
events[events_size].mi.time = 0; // GetSystemTime()
|
||||
events_size++;
|
||||
break;
|
||||
|
||||
|
||||
case EVENT_MOUSE_CLICKED:
|
||||
case EVENT_KEY_TYPED:
|
||||
// Ignore clicked and typed events.
|
||||
|
||||
case EVENT_HOOK_ENABLED:
|
||||
case EVENT_HOOK_DISABLED:
|
||||
// Ignore hook enabled / disabled events.
|
||||
|
||||
default:
|
||||
// Ignore any other garbage.
|
||||
logger(LOG_LEVEL_WARN, "%s [%u]: Ignoring post event type %#X\n",
|
||||
__FUNCTION__, __LINE__, event->type);
|
||||
break;
|
||||
}
|
||||
|
||||
// Release the previously held modifier keys used to fake the event mask.
|
||||
if (event->mask & (MASK_SHIFT | MASK_CTRL | MASK_META | MASK_ALT)) {
|
||||
unsigned int i;
|
||||
for (i = 0; i < sizeof(keymask_lookup) / sizeof(UINT); i++) {
|
||||
if (event->mask & 1 << i) {
|
||||
events[events_size].type = INPUT_KEYBOARD;
|
||||
events[events_size].ki.wVk = keymask_lookup[i];
|
||||
events[events_size].ki.dwFlags = KEYEVENTF_KEYUP;
|
||||
events[events_size].ki.time = 0; // Use current system time.
|
||||
events_size++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (event->mask & (MASK_BUTTON1 | MASK_BUTTON2 | MASK_BUTTON3 | MASK_BUTTON4 | MASK_BUTTON5)) {
|
||||
events[events_size].type = INPUT_MOUSE;
|
||||
events[events_size].mi.dx = 0; // Relative mouse movement due to
|
||||
events[events_size].mi.dy = 0; // MOUSEEVENTF_ABSOLUTE not being set.
|
||||
events[events_size].mi.mouseData = 0x00;
|
||||
events[events_size].mi.time = 0; // Use current system time.
|
||||
|
||||
// If dwFlags does not contain MOUSEEVENTF_WHEEL, MOUSEEVENTF_XDOWN, or MOUSEEVENTF_XUP,
|
||||
// then mouseData should be zero.
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms646273%28v=vs.85%29.aspx
|
||||
if (event->mask & MASK_BUTTON1) {
|
||||
events[events_size].mi.dwFlags |= MOUSEEVENTF_LEFTUP;
|
||||
}
|
||||
|
||||
if (event->mask & MASK_BUTTON2) {
|
||||
events[events_size].mi.dwFlags |= MOUSEEVENTF_RIGHTUP;
|
||||
}
|
||||
|
||||
if (event->mask & MASK_BUTTON3) {
|
||||
events[events_size].mi.dwFlags |= MOUSEEVENTF_MIDDLEUP;
|
||||
}
|
||||
|
||||
if (event->mask & MASK_BUTTON4) {
|
||||
events[events_size].mi.mouseData = XBUTTON1;
|
||||
events[events_size].mi.dwFlags |= MOUSEEVENTF_XUP;
|
||||
}
|
||||
|
||||
if (event->mask & MASK_BUTTON5) {
|
||||
events[events_size].mi.mouseData = XBUTTON2;
|
||||
events[events_size].mi.dwFlags |= MOUSEEVENTF_XUP;
|
||||
}
|
||||
|
||||
events_size++;
|
||||
}
|
||||
|
||||
// Create the key release input
|
||||
// memcpy(key_events + 1, key_events, sizeof(INPUT));
|
||||
// key_events[1].ki.dwFlags |= KEYEVENTF_KEYUP;
|
||||
|
||||
if (! SendInput(events_size, events, sizeof(INPUT)) ) {
|
||||
logger(LOG_LEVEL_ERROR, "%s [%u]: SendInput() failed! (%#lX)\n",
|
||||
__FUNCTION__, __LINE__, (unsigned long) GetLastError());
|
||||
}
|
||||
|
||||
free(events);
|
||||
}
|
@ -1,747 +0,0 @@
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "../iohook.h"
|
||||
#include "input.h"
|
||||
// #include "logger.h"
|
||||
|
||||
// Thread and hook handles.
|
||||
static DWORD hook_thread_id = 0;
|
||||
static HHOOK keyboard_event_hhook = NULL, mouse_event_hhook = NULL;
|
||||
static HWINEVENTHOOK win_event_hhook = NULL;
|
||||
|
||||
// The handle to the DLL module pulled in DllMain on DLL_PROCESS_ATTACH.
|
||||
extern HINSTANCE hInst;
|
||||
|
||||
// Modifiers for tracking key masks.
|
||||
static unsigned short int current_modifiers = 0x0000;
|
||||
|
||||
// Click count globals.
|
||||
static unsigned short click_count = 0;
|
||||
static DWORD click_time = 0;
|
||||
static unsigned short int click_button = MOUSE_NOBUTTON;
|
||||
static POINT last_click;
|
||||
|
||||
// Static event memory.
|
||||
static iohook_event event;
|
||||
|
||||
// Event dispatch callback.
|
||||
static dispatcher_t dispatcher = NULL;
|
||||
|
||||
IOHOOK_API void hook_set_dispatch_proc(dispatcher_t dispatch_proc) {
|
||||
logger(LOG_LEVEL_DEBUG, "%s [%u]: Setting new dispatch callback to %#p.\n",
|
||||
__FUNCTION__, __LINE__, dispatch_proc);
|
||||
|
||||
dispatcher = dispatch_proc;
|
||||
}
|
||||
|
||||
// Send out an event if a dispatcher was set.
|
||||
static inline void dispatch_event(iohook_event *const event) {
|
||||
if (dispatcher != NULL) {
|
||||
logger(LOG_LEVEL_DEBUG, "%s [%u]: Dispatching event type %u.\n",
|
||||
__FUNCTION__, __LINE__, event->type);
|
||||
|
||||
dispatcher(event);
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_WARN, "%s [%u]: No dispatch callback set!\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Set the native modifier mask for future events.
|
||||
static inline void set_modifier_mask(unsigned short int mask) {
|
||||
current_modifiers |= mask;
|
||||
}
|
||||
|
||||
// Unset the native modifier mask for future events.
|
||||
static inline void unset_modifier_mask(unsigned short int mask) {
|
||||
current_modifiers ^= mask;
|
||||
}
|
||||
|
||||
// Get the current native modifier mask state.
|
||||
static inline unsigned short int get_modifiers() {
|
||||
return current_modifiers;
|
||||
}
|
||||
|
||||
// Initialize the modifier mask to the current modifiers.
|
||||
static void initialize_modifiers() {
|
||||
current_modifiers = 0x0000;
|
||||
|
||||
// NOTE We are checking the high order bit, so it will be < 0 for a singed short.
|
||||
if (GetKeyState(VK_LSHIFT) < 0) { set_modifier_mask(MASK_SHIFT_L); }
|
||||
if (GetKeyState(VK_RSHIFT) < 0) { set_modifier_mask(MASK_SHIFT_R); }
|
||||
if (GetKeyState(VK_LCONTROL) < 0) { set_modifier_mask(MASK_CTRL_L); }
|
||||
if (GetKeyState(VK_RCONTROL) < 0) { set_modifier_mask(MASK_CTRL_R); }
|
||||
if (GetKeyState(VK_LMENU) < 0) { set_modifier_mask(MASK_ALT_L); }
|
||||
if (GetKeyState(VK_RMENU) < 0) { set_modifier_mask(MASK_ALT_R); }
|
||||
if (GetKeyState(VK_LWIN) < 0) { set_modifier_mask(MASK_META_L); }
|
||||
if (GetKeyState(VK_RWIN) < 0) { set_modifier_mask(MASK_META_R); }
|
||||
|
||||
if (GetKeyState(VK_LBUTTON) < 0) { set_modifier_mask(MASK_BUTTON1); }
|
||||
if (GetKeyState(VK_RBUTTON) < 0) { set_modifier_mask(MASK_BUTTON2); }
|
||||
if (GetKeyState(VK_MBUTTON) < 0) { set_modifier_mask(MASK_BUTTON3); }
|
||||
if (GetKeyState(VK_XBUTTON1) < 0) { set_modifier_mask(MASK_BUTTON4); }
|
||||
if (GetKeyState(VK_XBUTTON2) < 0) { set_modifier_mask(MASK_BUTTON5); }
|
||||
|
||||
if (GetKeyState(VK_NUMLOCK) < 0) { set_modifier_mask(MASK_NUM_LOCK); }
|
||||
if (GetKeyState(VK_CAPITAL) < 0) { set_modifier_mask(MASK_CAPS_LOCK); }
|
||||
if (GetKeyState(VK_SCROLL) < 0) { set_modifier_mask(MASK_SCROLL_LOCK); }
|
||||
}
|
||||
|
||||
|
||||
/* Retrieves the mouse wheel scroll type. This function cannot be included as
|
||||
* part of the input.h due to platform specific calling restrictions.
|
||||
*/
|
||||
static unsigned short int get_scroll_wheel_type() {
|
||||
unsigned short int value;
|
||||
UINT wheel_type;
|
||||
|
||||
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &wheel_type, 0);
|
||||
if (wheel_type == WHEEL_PAGESCROLL) {
|
||||
value = WHEEL_BLOCK_SCROLL;
|
||||
}
|
||||
else {
|
||||
value = WHEEL_UNIT_SCROLL;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* Retrieves the mouse wheel scroll amount. This function cannot be included as
|
||||
* part of the input.h due to platform specific calling restrictions.
|
||||
*/
|
||||
static unsigned short int get_scroll_wheel_amount() {
|
||||
unsigned short int value;
|
||||
UINT wheel_amount;
|
||||
|
||||
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &wheel_amount, 0);
|
||||
if (wheel_amount == WHEEL_PAGESCROLL) {
|
||||
value = 1;
|
||||
}
|
||||
else {
|
||||
value = (unsigned short int) wheel_amount;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void unregister_running_hooks() {
|
||||
// Stop the event hook and any timer still running.
|
||||
if (win_event_hhook != NULL) {
|
||||
UnhookWinEvent(win_event_hhook);
|
||||
win_event_hhook = NULL;
|
||||
}
|
||||
|
||||
// Destroy the native hooks.
|
||||
if (keyboard_event_hhook != NULL) {
|
||||
UnhookWindowsHookEx(keyboard_event_hhook);
|
||||
keyboard_event_hhook = NULL;
|
||||
}
|
||||
|
||||
if (mouse_event_hhook != NULL) {
|
||||
UnhookWindowsHookEx(mouse_event_hhook);
|
||||
mouse_event_hhook = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void hook_start_proc() {
|
||||
// Get the local system time in UNIX epoch form.
|
||||
uint64_t timestamp = GetMessageTime();
|
||||
|
||||
// Populate the hook start event.
|
||||
event.time = timestamp;
|
||||
event.reserved = 0x00;
|
||||
|
||||
event.type = EVENT_HOOK_ENABLED;
|
||||
event.mask = 0x00;
|
||||
|
||||
// Fire the hook start event.
|
||||
dispatch_event(&event);
|
||||
}
|
||||
|
||||
void hook_stop_proc() {
|
||||
// Get the local system time in UNIX epoch form.
|
||||
uint64_t timestamp = GetMessageTime();
|
||||
|
||||
// Populate the hook stop event.
|
||||
event.time = timestamp;
|
||||
event.reserved = 0x00;
|
||||
|
||||
event.type = EVENT_HOOK_DISABLED;
|
||||
event.mask = 0x00;
|
||||
|
||||
// Fire the hook stop event.
|
||||
dispatch_event(&event);
|
||||
}
|
||||
|
||||
static void process_key_pressed(KBDLLHOOKSTRUCT *kbhook) {
|
||||
// Check and setup modifiers.
|
||||
if (kbhook->vkCode == VK_LSHIFT) { set_modifier_mask(MASK_SHIFT_L); }
|
||||
else if (kbhook->vkCode == VK_RSHIFT) { set_modifier_mask(MASK_SHIFT_R); }
|
||||
else if (kbhook->vkCode == VK_LCONTROL) { set_modifier_mask(MASK_CTRL_L); }
|
||||
else if (kbhook->vkCode == VK_RCONTROL) { set_modifier_mask(MASK_CTRL_R); }
|
||||
else if (kbhook->vkCode == VK_LMENU) { set_modifier_mask(MASK_ALT_L); }
|
||||
else if (kbhook->vkCode == VK_RMENU) { set_modifier_mask(MASK_ALT_R); }
|
||||
else if (kbhook->vkCode == VK_LWIN) { set_modifier_mask(MASK_META_L); }
|
||||
else if (kbhook->vkCode == VK_RWIN) { set_modifier_mask(MASK_META_R); }
|
||||
else if (kbhook->vkCode == VK_NUMLOCK) { set_modifier_mask(MASK_NUM_LOCK); }
|
||||
else if (kbhook->vkCode == VK_CAPITAL) { set_modifier_mask(MASK_CAPS_LOCK); }
|
||||
else if (kbhook->vkCode == VK_SCROLL) { set_modifier_mask(MASK_SCROLL_LOCK); }
|
||||
|
||||
// Populate key pressed event.
|
||||
event.time = kbhook->time;
|
||||
event.reserved = 0x00;
|
||||
|
||||
event.type = EVENT_KEY_PRESSED;
|
||||
event.mask = get_modifiers();
|
||||
|
||||
event.data.keyboard.keycode = keycode_to_scancode(kbhook->vkCode, kbhook->flags);
|
||||
event.data.keyboard.rawcode = kbhook->vkCode;
|
||||
event.data.keyboard.keychar = CHAR_UNDEFINED;
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X pressed. (%#X)\n",
|
||||
__FUNCTION__, __LINE__, event.data.keyboard.keycode, event.data.keyboard.rawcode);
|
||||
|
||||
// Populate key pressed event.
|
||||
dispatch_event(&event);
|
||||
|
||||
// If the pressed event was not consumed...
|
||||
if (event.reserved ^ 0x01) {
|
||||
// Buffer for unicode typed chars. No more than 2 needed.
|
||||
WCHAR buffer[2]; // = { WCH_NONE };
|
||||
|
||||
// If the pressed event was not consumed and a unicode char exists...
|
||||
SIZE_T count = keycode_to_unicode(kbhook->vkCode, buffer, sizeof(buffer));
|
||||
unsigned int i;
|
||||
for (i = 0; i < count; i++) {
|
||||
// Populate key typed event.
|
||||
event.time = kbhook->time;
|
||||
event.reserved = 0x00;
|
||||
|
||||
event.type = EVENT_KEY_TYPED;
|
||||
event.mask = get_modifiers();
|
||||
|
||||
event.data.keyboard.keycode = VC_UNDEFINED;
|
||||
event.data.keyboard.rawcode = kbhook->vkCode;
|
||||
event.data.keyboard.keychar = buffer[i];
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X typed. (%lc)\n",
|
||||
__FUNCTION__, __LINE__, event.data.keyboard.keycode, (wint_t) event.data.keyboard.keychar);
|
||||
|
||||
// Fire key typed event.
|
||||
dispatch_event(&event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void process_key_released(KBDLLHOOKSTRUCT *kbhook) {
|
||||
// Check and setup modifiers.
|
||||
if (kbhook->vkCode == VK_LSHIFT) { unset_modifier_mask(MASK_SHIFT_L); }
|
||||
else if (kbhook->vkCode == VK_RSHIFT) { unset_modifier_mask(MASK_SHIFT_R); }
|
||||
else if (kbhook->vkCode == VK_LCONTROL) { unset_modifier_mask(MASK_CTRL_L); }
|
||||
else if (kbhook->vkCode == VK_RCONTROL) { unset_modifier_mask(MASK_CTRL_R); }
|
||||
else if (kbhook->vkCode == VK_LMENU) { unset_modifier_mask(MASK_ALT_L); }
|
||||
else if (kbhook->vkCode == VK_RMENU) { unset_modifier_mask(MASK_ALT_R); }
|
||||
else if (kbhook->vkCode == VK_LWIN) { unset_modifier_mask(MASK_META_L); }
|
||||
else if (kbhook->vkCode == VK_RWIN) { unset_modifier_mask(MASK_META_R); }
|
||||
else if (kbhook->vkCode == VK_NUMLOCK) { unset_modifier_mask(MASK_NUM_LOCK); }
|
||||
else if (kbhook->vkCode == VK_CAPITAL) { unset_modifier_mask(MASK_CAPS_LOCK); }
|
||||
else if (kbhook->vkCode == VK_SCROLL) { unset_modifier_mask(MASK_SCROLL_LOCK); }
|
||||
|
||||
// Populate key pressed event.
|
||||
event.time = kbhook->time;
|
||||
event.reserved = 0x00;
|
||||
|
||||
event.type = EVENT_KEY_RELEASED;
|
||||
event.mask = get_modifiers();
|
||||
|
||||
event.data.keyboard.keycode = keycode_to_scancode(kbhook->vkCode, kbhook->flags);
|
||||
event.data.keyboard.rawcode = kbhook->vkCode;
|
||||
event.data.keyboard.keychar = CHAR_UNDEFINED;
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: Key %#X released. (%#X)\n",
|
||||
__FUNCTION__, __LINE__, event.data.keyboard.keycode, event.data.keyboard.rawcode);
|
||||
|
||||
// Fire key released event.
|
||||
dispatch_event(&event);
|
||||
}
|
||||
|
||||
LRESULT CALLBACK keyboard_hook_event_proc(int nCode, WPARAM wParam, LPARAM lParam) {
|
||||
KBDLLHOOKSTRUCT *kbhook = (KBDLLHOOKSTRUCT *) lParam;
|
||||
switch (wParam) {
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN:
|
||||
process_key_pressed(kbhook);
|
||||
break;
|
||||
|
||||
case WM_KEYUP:
|
||||
case WM_SYSKEYUP:
|
||||
process_key_released(kbhook);
|
||||
break;
|
||||
|
||||
default:
|
||||
// In theory this *should* never execute.
|
||||
logger(LOG_LEVEL_DEBUG, "%s [%u]: Unhandled Windows keyboard event: %#X.\n",
|
||||
__FUNCTION__, __LINE__, (unsigned int) wParam);
|
||||
break;
|
||||
}
|
||||
|
||||
LRESULT hook_result = -1;
|
||||
if (nCode < 0 || event.reserved ^ 0x01) {
|
||||
hook_result = CallNextHookEx(keyboard_event_hhook, nCode, wParam, lParam);
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_DEBUG, "%s [%u]: Consuming the current event. (%li)\n",
|
||||
__FUNCTION__, __LINE__, (long) hook_result);
|
||||
}
|
||||
|
||||
return hook_result;
|
||||
}
|
||||
|
||||
|
||||
static void process_button_pressed(MSLLHOOKSTRUCT *mshook, uint16_t button) {
|
||||
uint64_t timestamp = GetMessageTime();
|
||||
|
||||
// Track the number of clicks, the button must match the previous button.
|
||||
if (button == click_button && (long int) (timestamp - click_time) <= hook_get_multi_click_time()) {
|
||||
if (click_count < USHRT_MAX) {
|
||||
click_count++;
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_WARN, "%s [%u]: Click count overflow detected!\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Reset the click count.
|
||||
click_count = 1;
|
||||
|
||||
// Set the previous button.
|
||||
click_button = button;
|
||||
}
|
||||
|
||||
// Save this events time to calculate the click_count.
|
||||
click_time = timestamp;
|
||||
|
||||
// Store the last click point.
|
||||
last_click.x = mshook->pt.x;
|
||||
last_click.y = mshook->pt.y;
|
||||
|
||||
// Populate mouse pressed event.
|
||||
event.time = timestamp;
|
||||
event.reserved = 0x00;
|
||||
|
||||
event.type = EVENT_MOUSE_PRESSED;
|
||||
event.mask = get_modifiers();
|
||||
|
||||
event.data.mouse.button = button;
|
||||
event.data.mouse.clicks = click_count;
|
||||
|
||||
event.data.mouse.x = mshook->pt.x;
|
||||
event.data.mouse.y = mshook->pt.y;
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: Button %u pressed %u time(s). (%u, %u)\n",
|
||||
__FUNCTION__, __LINE__, event.data.mouse.button, event.data.mouse.clicks,
|
||||
event.data.mouse.x, event.data.mouse.y);
|
||||
|
||||
// Fire mouse pressed event.
|
||||
dispatch_event(&event);
|
||||
}
|
||||
|
||||
static void process_button_released(MSLLHOOKSTRUCT *mshook, uint16_t button) {
|
||||
// Populate mouse released event.
|
||||
event.time = GetMessageTime();
|
||||
event.reserved = 0x00;
|
||||
|
||||
event.type = EVENT_MOUSE_RELEASED;
|
||||
event.mask = get_modifiers();
|
||||
|
||||
event.data.mouse.button = button;
|
||||
event.data.mouse.clicks = click_count;
|
||||
|
||||
event.data.mouse.x = mshook->pt.x;
|
||||
event.data.mouse.y = mshook->pt.y;
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: Button %u released %u time(s). (%u, %u)\n",
|
||||
__FUNCTION__, __LINE__, event.data.mouse.button,
|
||||
event.data.mouse.clicks,
|
||||
event.data.mouse.x, event.data.mouse.y);
|
||||
|
||||
// Fire mouse released event.
|
||||
dispatch_event(&event);
|
||||
|
||||
// If the pressed event was not consumed...
|
||||
if (event.reserved ^ 0x01 && last_click.x == mshook->pt.x && last_click.y == mshook->pt.y) {
|
||||
// Populate mouse clicked event.
|
||||
event.time = GetMessageTime();
|
||||
event.reserved = 0x00;
|
||||
|
||||
event.type = EVENT_MOUSE_CLICKED;
|
||||
event.mask = get_modifiers();
|
||||
|
||||
event.data.mouse.button = button;
|
||||
event.data.mouse.clicks = click_count;
|
||||
event.data.mouse.x = mshook->pt.x;
|
||||
event.data.mouse.y = mshook->pt.y;
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: Button %u clicked %u time(s). (%u, %u)\n",
|
||||
__FUNCTION__, __LINE__, event.data.mouse.button, event.data.mouse.clicks,
|
||||
event.data.mouse.x, event.data.mouse.y);
|
||||
|
||||
// Fire mouse clicked event.
|
||||
dispatch_event(&event);
|
||||
}
|
||||
|
||||
// Reset the number of clicks.
|
||||
if (button == click_button && (long int) (event.time - click_time) > hook_get_multi_click_time()) {
|
||||
// Reset the click count.
|
||||
click_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void process_mouse_moved(MSLLHOOKSTRUCT *mshook) {
|
||||
uint64_t timestamp = GetMessageTime();
|
||||
|
||||
// We received a mouse move event with the mouse actually moving.
|
||||
// This verifies that the mouse was moved after being depressed.
|
||||
if (last_click.x != mshook->pt.x || last_click.y != mshook->pt.y) {
|
||||
// Reset the click count.
|
||||
if (click_count != 0 && (long) (timestamp - click_time) > hook_get_multi_click_time()) {
|
||||
click_count = 0;
|
||||
}
|
||||
|
||||
// Populate mouse move event.
|
||||
event.time = timestamp;
|
||||
event.reserved = 0x00;
|
||||
|
||||
event.mask = get_modifiers();
|
||||
|
||||
// Check the modifier mask range for MASK_BUTTON1 - 5.
|
||||
bool mouse_dragged = event.mask & (MASK_BUTTON1 | MASK_BUTTON2 | MASK_BUTTON3 | MASK_BUTTON4 | MASK_BUTTON5);
|
||||
if (mouse_dragged) {
|
||||
// Create Mouse Dragged event.
|
||||
event.type = EVENT_MOUSE_DRAGGED;
|
||||
}
|
||||
else {
|
||||
// Create a Mouse Moved event.
|
||||
event.type = EVENT_MOUSE_MOVED;
|
||||
}
|
||||
|
||||
event.data.mouse.button = MOUSE_NOBUTTON;
|
||||
event.data.mouse.clicks = click_count;
|
||||
event.data.mouse.x = mshook->pt.x;
|
||||
event.data.mouse.y = mshook->pt.y;
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: Mouse %s to %u, %u.\n",
|
||||
__FUNCTION__, __LINE__, mouse_dragged ? "dragged" : "moved",
|
||||
event.data.mouse.x, event.data.mouse.y);
|
||||
|
||||
// Fire mouse move event.
|
||||
dispatch_event(&event);
|
||||
}
|
||||
}
|
||||
|
||||
static void process_mouse_wheel(MSLLHOOKSTRUCT *mshook, uint8_t direction) {
|
||||
// Track the number of clicks.
|
||||
// Reset the click count and previous button.
|
||||
click_count = 1;
|
||||
click_button = MOUSE_NOBUTTON;
|
||||
|
||||
// Populate mouse wheel event.
|
||||
event.time = GetMessageTime();
|
||||
event.reserved = 0x00;
|
||||
|
||||
event.type = EVENT_MOUSE_WHEEL;
|
||||
event.mask = get_modifiers();
|
||||
|
||||
event.data.wheel.clicks = click_count;
|
||||
event.data.wheel.x = mshook->pt.x;
|
||||
event.data.wheel.y = mshook->pt.y;
|
||||
|
||||
event.data.wheel.type = get_scroll_wheel_type();
|
||||
event.data.wheel.amount = get_scroll_wheel_amount();
|
||||
|
||||
/* Delta HIWORD(mshook->mouseData)
|
||||
* A positive value indicates that the wheel was rotated
|
||||
* forward, away from the user; a negative value indicates that
|
||||
* the wheel was rotated backward, toward the user. One wheel
|
||||
* click is defined as WHEEL_DELTA, which is 120. */
|
||||
event.data.wheel.rotation = ((int16_t) HIWORD(mshook->mouseData) / WHEEL_DELTA) * -1;
|
||||
|
||||
// Set the direction based on what event was received.
|
||||
event.data.wheel.direction = direction;
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: Mouse wheel type %u, rotated %i units in the %u direction at %u, %u.\n",
|
||||
__FUNCTION__, __LINE__, event.data.wheel.type,
|
||||
event.data.wheel.amount * event.data.wheel.rotation,
|
||||
event.data.wheel.direction,
|
||||
event.data.wheel.x, event.data.wheel.y);
|
||||
|
||||
// Fire mouse wheel event.
|
||||
dispatch_event(&event);
|
||||
}
|
||||
|
||||
LRESULT CALLBACK mouse_hook_event_proc(int nCode, WPARAM wParam, LPARAM lParam) {
|
||||
MSLLHOOKSTRUCT *mshook = (MSLLHOOKSTRUCT *) lParam;
|
||||
switch (wParam) {
|
||||
case WM_LBUTTONDOWN:
|
||||
set_modifier_mask(MASK_BUTTON1);
|
||||
process_button_pressed(mshook, MOUSE_BUTTON1);
|
||||
break;
|
||||
|
||||
case WM_RBUTTONDOWN:
|
||||
set_modifier_mask(MASK_BUTTON2);
|
||||
process_button_pressed(mshook, MOUSE_BUTTON2);
|
||||
break;
|
||||
|
||||
case WM_MBUTTONDOWN:
|
||||
set_modifier_mask(MASK_BUTTON3);
|
||||
process_button_pressed(mshook, MOUSE_BUTTON3);
|
||||
break;
|
||||
|
||||
case WM_XBUTTONDOWN:
|
||||
case WM_NCXBUTTONDOWN:
|
||||
if (HIWORD(mshook->mouseData) == XBUTTON1) {
|
||||
set_modifier_mask(MASK_BUTTON4);
|
||||
process_button_pressed(mshook, MOUSE_BUTTON4);
|
||||
}
|
||||
else if (HIWORD(mshook->mouseData) == XBUTTON2) {
|
||||
set_modifier_mask(MASK_BUTTON5);
|
||||
process_button_pressed(mshook, MOUSE_BUTTON5);
|
||||
}
|
||||
else {
|
||||
// Extra mouse buttons.
|
||||
uint16_t button = HIWORD(mshook->mouseData);
|
||||
|
||||
// Add support for mouse 4 & 5.
|
||||
if (button == 4) {
|
||||
set_modifier_mask(MOUSE_BUTTON4);
|
||||
}
|
||||
else if (button == 5) {
|
||||
set_modifier_mask(MOUSE_BUTTON5);
|
||||
}
|
||||
|
||||
process_button_pressed(mshook, button);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case WM_LBUTTONUP:
|
||||
unset_modifier_mask(MASK_BUTTON1);
|
||||
process_button_released(mshook, MOUSE_BUTTON1);
|
||||
break;
|
||||
|
||||
case WM_RBUTTONUP:
|
||||
unset_modifier_mask(MASK_BUTTON2);
|
||||
process_button_released(mshook, MOUSE_BUTTON2);
|
||||
break;
|
||||
|
||||
case WM_MBUTTONUP:
|
||||
unset_modifier_mask(MASK_BUTTON3);
|
||||
process_button_released(mshook, MOUSE_BUTTON3);
|
||||
break;
|
||||
|
||||
case WM_XBUTTONUP:
|
||||
case WM_NCXBUTTONUP:
|
||||
if (HIWORD(mshook->mouseData) == XBUTTON1) {
|
||||
unset_modifier_mask(MASK_BUTTON4);
|
||||
process_button_released(mshook, MOUSE_BUTTON4);
|
||||
}
|
||||
else if (HIWORD(mshook->mouseData) == XBUTTON2) {
|
||||
unset_modifier_mask(MASK_BUTTON5);
|
||||
process_button_released(mshook, MOUSE_BUTTON5);
|
||||
}
|
||||
else {
|
||||
// Extra mouse buttons.
|
||||
uint16_t button = HIWORD(mshook->mouseData);
|
||||
|
||||
// Add support for mouse 4 & 5.
|
||||
if (button == 4) {
|
||||
unset_modifier_mask(MOUSE_BUTTON4);
|
||||
}
|
||||
else if (button == 5) {
|
||||
unset_modifier_mask(MOUSE_BUTTON5);
|
||||
}
|
||||
|
||||
process_button_released(mshook, MOUSE_BUTTON5);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_MOUSEMOVE:
|
||||
process_mouse_moved(mshook);
|
||||
break;
|
||||
|
||||
case WM_MOUSEWHEEL:
|
||||
process_mouse_wheel(mshook, WHEEL_VERTICAL_DIRECTION);
|
||||
break;
|
||||
|
||||
/* For horizontal scroll wheel support.
|
||||
* NOTE Windows >= Vista
|
||||
* case 0x020E:
|
||||
*/
|
||||
case WM_MOUSEHWHEEL:
|
||||
process_mouse_wheel(mshook, WHEEL_HORIZONTAL_DIRECTION);
|
||||
break;
|
||||
|
||||
default:
|
||||
// In theory this *should* never execute.
|
||||
logger(LOG_LEVEL_DEBUG, "%s [%u]: Unhandled Windows mouse event: %#X.\n",
|
||||
__FUNCTION__, __LINE__, (unsigned int) wParam);
|
||||
break;
|
||||
}
|
||||
|
||||
LRESULT hook_result = -1;
|
||||
if (nCode < 0 || event.reserved ^ 0x01) {
|
||||
hook_result = CallNextHookEx(mouse_event_hhook, nCode, wParam, lParam);
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_DEBUG, "%s [%u]: Consuming the current event. (%li)\n",
|
||||
__FUNCTION__, __LINE__, (long) hook_result);
|
||||
}
|
||||
|
||||
return hook_result;
|
||||
}
|
||||
|
||||
|
||||
// Callback function that handles events.
|
||||
void CALLBACK win_hook_event_proc(HWINEVENTHOOK hook, DWORD event, HWND hWnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime) {
|
||||
switch (event) {
|
||||
case EVENT_OBJECT_NAMECHANGE:
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: Restarting Windows input hook on window event: %#X.\n",
|
||||
__FUNCTION__, __LINE__, event);
|
||||
|
||||
// Remove any keyboard or mouse hooks that are still running.
|
||||
if (keyboard_event_hhook != NULL) {
|
||||
UnhookWindowsHookEx(keyboard_event_hhook);
|
||||
}
|
||||
|
||||
if (mouse_event_hhook != NULL) {
|
||||
UnhookWindowsHookEx(mouse_event_hhook);
|
||||
}
|
||||
|
||||
// Restart the event hooks.
|
||||
keyboard_event_hhook = SetWindowsHookEx(WH_KEYBOARD_LL, keyboard_hook_event_proc, hInst, 0);
|
||||
mouse_event_hhook = SetWindowsHookEx(WH_MOUSE_LL, mouse_hook_event_proc, hInst, 0);
|
||||
|
||||
// Re-initialize modifier masks.
|
||||
initialize_modifiers();
|
||||
|
||||
// FIXME We should compare the modifier mask before and after the restart
|
||||
// to determine if we should synthesize missing events.
|
||||
|
||||
// Check for event hook error.
|
||||
if (keyboard_event_hhook == NULL || mouse_event_hhook == NULL) {
|
||||
logger(LOG_LEVEL_ERROR, "%s [%u]: SetWindowsHookEx() failed! (%#lX)\n",
|
||||
__FUNCTION__, __LINE__, (unsigned long) GetLastError());
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: Unhandled Windows window event: %#X.\n",
|
||||
__FUNCTION__, __LINE__, event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
IOHOOK_API int hook_run() {
|
||||
int status = IOHOOK_FAILURE;
|
||||
|
||||
// Set the thread id we want to signal later.
|
||||
hook_thread_id = GetCurrentThreadId();
|
||||
|
||||
// Spot check the hInst incase the library was statically linked and DllMain
|
||||
// did not receive a pointer on load.
|
||||
if (hInst == NULL) {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: hInst was not set by DllMain().\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
|
||||
hInst = GetModuleHandle(NULL);
|
||||
if (hInst != NULL) {
|
||||
// Initialize native input helper functions.
|
||||
load_input_helper();
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_ERROR, "%s [%u]: Could not determine hInst for SetWindowsHookEx()! (%#lX)\n",
|
||||
__FUNCTION__, __LINE__, (unsigned long) GetLastError());
|
||||
|
||||
status = IOHOOK_ERROR_GET_MODULE_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the native hooks.
|
||||
keyboard_event_hhook = SetWindowsHookEx(WH_KEYBOARD_LL, keyboard_hook_event_proc, hInst, 0);
|
||||
mouse_event_hhook = SetWindowsHookEx(WH_MOUSE_LL, mouse_hook_event_proc, hInst, 0);
|
||||
|
||||
// Create a window event hook to listen for capture change.
|
||||
win_event_hhook = SetWinEventHook(
|
||||
EVENT_OBJECT_NAMECHANGE, EVENT_OBJECT_NAMECHANGE,
|
||||
NULL,
|
||||
win_hook_event_proc,
|
||||
0, 0,
|
||||
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
|
||||
|
||||
// If we did not encounter a problem, start processing events.
|
||||
if (keyboard_event_hhook != NULL && mouse_event_hhook != NULL) {
|
||||
if (win_event_hhook == NULL) {
|
||||
logger(LOG_LEVEL_WARN, "%s [%u]: SetWinEventHook() failed!\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
logger(LOG_LEVEL_DEBUG, "%s [%u]: SetWindowsHookEx() successful.\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
|
||||
// Check and setup modifiers.
|
||||
initialize_modifiers();
|
||||
|
||||
// Set the exit status.
|
||||
status = IOHOOK_SUCCESS;
|
||||
|
||||
// Windows does not have a hook start event or callback so we need to
|
||||
// manually fake it.
|
||||
hook_start_proc();
|
||||
|
||||
// Block until the thread receives an WM_QUIT request.
|
||||
MSG message;
|
||||
while (GetMessage(&message, (HWND) NULL, 0, 0) > 0) {
|
||||
TranslateMessage(&message);
|
||||
DispatchMessage(&message);
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_ERROR, "%s [%u]: SetWindowsHookEx() failed! (%#lX)\n",
|
||||
__FUNCTION__, __LINE__, (unsigned long) GetLastError());
|
||||
|
||||
status = IOHOOK_ERROR_SET_WINDOWS_HOOK_EX;
|
||||
}
|
||||
|
||||
|
||||
// Unregister any hooks that may still be installed.
|
||||
unregister_running_hooks();
|
||||
|
||||
// We must explicitly call the cleanup handler because Windows does not
|
||||
// provide a thread cleanup method like POSIX pthread_cleanup_push/pop.
|
||||
hook_stop_proc();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
IOHOOK_API int hook_stop() {
|
||||
int status = IOHOOK_FAILURE;
|
||||
|
||||
// Try to exit the thread naturally.
|
||||
if (PostThreadMessage(hook_thread_id, WM_QUIT, (WPARAM) NULL, (LPARAM) NULL)) {
|
||||
status = IOHOOK_SUCCESS;
|
||||
}
|
||||
|
||||
logger(LOG_LEVEL_DEBUG, "%s [%u]: Status: %#X.\n",
|
||||
__FUNCTION__, __LINE__, status);
|
||||
|
||||
return status;
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
/***********************************************************************
|
||||
Input
|
||||
***********************************************************************/
|
||||
|
||||
#ifndef _included_input_helper
|
||||
#define _included_input_helper
|
||||
|
||||
#include <limits.h>
|
||||
#include <windows.h>
|
||||
|
||||
#ifndef LPFN_ISWOW64PROCESS
|
||||
typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
|
||||
#endif
|
||||
|
||||
typedef void* (CALLBACK *KbdLayerDescriptor) (VOID);
|
||||
|
||||
#define CAPLOK 0x01
|
||||
#define WCH_NONE 0xF000
|
||||
#define WCH_DEAD 0xF001
|
||||
|
||||
#ifndef WM_MOUSEHWHEEL
|
||||
#define WM_MOUSEHWHEEL 0x020E
|
||||
#endif
|
||||
|
||||
typedef struct _VK_TO_WCHARS {
|
||||
BYTE VirtualKey;
|
||||
BYTE Attributes;
|
||||
WCHAR wch[];
|
||||
} VK_TO_WCHARS, *PVK_TO_WCHARS;
|
||||
|
||||
typedef struct _LIGATURE {
|
||||
BYTE VirtualKey;
|
||||
WORD ModificationNumber;
|
||||
WCHAR wch[];
|
||||
} LIGATURE, *PLIGATURE;
|
||||
|
||||
typedef struct _VK_TO_BIT {
|
||||
BYTE Vk;
|
||||
BYTE ModBits;
|
||||
} VK_TO_BIT, *PVK_TO_BIT;
|
||||
|
||||
typedef struct _MODIFIERS {
|
||||
PVK_TO_BIT pVkToBit; // __ptr64
|
||||
WORD wMaxModBits;
|
||||
BYTE ModNumber[];
|
||||
} MODIFIERS, *PMODIFIERS;
|
||||
|
||||
typedef struct _VSC_VK {
|
||||
BYTE Vsc;
|
||||
USHORT Vk;
|
||||
} VSC_VK, *PVSC_VK;
|
||||
|
||||
typedef struct _VK_TO_WCHAR_TABLE {
|
||||
PVK_TO_WCHARS pVkToWchars; // __ptr64
|
||||
BYTE nModifications;
|
||||
BYTE cbSize;
|
||||
} VK_TO_WCHAR_TABLE, *PVK_TO_WCHAR_TABLE;
|
||||
|
||||
typedef struct _DEADKEY {
|
||||
DWORD dwBoth;
|
||||
WCHAR wchComposed;
|
||||
USHORT uFlags;
|
||||
} DEADKEY, *PDEADKEY;
|
||||
|
||||
typedef struct _VSC_LPWSTR {
|
||||
BYTE vsc;
|
||||
WCHAR *pwsz; // __ptr64
|
||||
} VSC_LPWSTR, *PVSC_LPWSTR;
|
||||
|
||||
typedef struct tagKbdLayer {
|
||||
PMODIFIERS pCharModifiers; // __ptr64
|
||||
PVK_TO_WCHAR_TABLE pVkToWcharTable; // __ptr64
|
||||
PDEADKEY pDeadKey; // __ptr64
|
||||
PVSC_LPWSTR pKeyNames; // __ptr64
|
||||
PVSC_LPWSTR pKeyNamesExt; // __ptr64
|
||||
WCHAR **pKeyNamesDead; // __ptr64
|
||||
USHORT *pusVSCtoVK; // __ptr64
|
||||
BYTE bMaxVSCtoVK;
|
||||
PVSC_VK pVSCtoVK_E0; // __ptr64
|
||||
PVSC_VK pVSCtoVK_E1; // __ptr64
|
||||
DWORD fLocaleFlags;
|
||||
BYTE nLgMax;
|
||||
BYTE cbLgEntry;
|
||||
PLIGATURE pLigature; // __ptr64
|
||||
DWORD dwType;
|
||||
DWORD dwSubType;
|
||||
} KBDTABLES, *PKBDTABLES; // __ptr64
|
||||
|
||||
|
||||
extern SIZE_T keycode_to_unicode(DWORD keycode, PWCHAR buffer, SIZE_T size);
|
||||
|
||||
//extern DWORD unicode_to_keycode(wchar_t unicode);
|
||||
|
||||
extern unsigned short keycode_to_scancode(DWORD vk_code, DWORD flags);
|
||||
|
||||
extern DWORD scancode_to_keycode(unsigned short scancode);
|
||||
|
||||
// Initialize the locale list and wow64 pointer size.
|
||||
extern int load_input_helper();
|
||||
|
||||
// Cleanup the initialized locales.
|
||||
extern int unload_input_helper();
|
||||
|
||||
#endif
|
@ -1,832 +0,0 @@
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "../iohook.h"
|
||||
#include "../logger_c.h"
|
||||
#include "input.h"
|
||||
|
||||
static const uint16_t keycode_scancode_table[][2] = {
|
||||
/* idx { vk_code, scancode }, */
|
||||
/* 0 */ { VC_UNDEFINED, 0x0000 }, // 0x00
|
||||
/* 1 */ { MOUSE_BUTTON1, VK_ESCAPE }, // 0x01
|
||||
/* 2 */ { MOUSE_BUTTON2, 0x0031 }, // 0x02
|
||||
/* 3 */ { VC_UNDEFINED, 0x0032 }, // 0x03 VK_CANCEL
|
||||
/* 4 */ { MOUSE_BUTTON3, 0x0033 }, // 0x04
|
||||
/* 5 */ { MOUSE_BUTTON4, 0x0034 }, // 0x05
|
||||
/* 6 */ { MOUSE_BUTTON5, 0x0035 }, // 0x06
|
||||
/* 7 */ { VC_UNDEFINED, 0x0036 }, // 0x07 Undefined
|
||||
/* 8 */ { VC_BACKSPACE, 0x0037 }, // 0x08 VK_BACK
|
||||
/* 9 */ { VC_TAB, 0x0038 }, // 0x09 VK_TAB
|
||||
/* 10 */ { VC_UNDEFINED, 0x0039 }, // 0x0A Reserved
|
||||
/* 11 */ { VC_UNDEFINED, 0x0030 }, // 0x0B Reserved
|
||||
/* 12 */ { VC_CLEAR, VK_OEM_MINUS }, // 0x0C VK_CLEAR
|
||||
/* 13 */ { VC_ENTER, VK_OEM_PLUS }, // 0x0D VK_RETURN
|
||||
/* 14 */ { VC_UNDEFINED, VK_BACK }, // 0x0E Undefined
|
||||
/* 15 */ { VC_UNDEFINED, VK_TAB }, // 0x0F Undefined
|
||||
/* 16 */ { VC_SHIFT_L, 0x0051 }, // 0x10 VK_SHIFT
|
||||
/* 17 */ { VC_CONTROL_L, 0x0057 }, // 0x11 VK_CONTROL
|
||||
/* 18 */ { VC_ALT_L, 0x0045 }, // 0x12 VK_MENU ALT key
|
||||
/* 19 */ { VC_PAUSE, 0x0052 }, // 0x13 VK_PAUSE
|
||||
/* 20 */ { VC_CAPS_LOCK, 0x0054 }, // 0x14 VK_CAPITAL CAPS LOCK key
|
||||
/* 21 */ { VC_KATAKANA, 0x0059 }, // 0x15 VK_KANA IME Kana mode
|
||||
/* 22 */ { VC_UNDEFINED, 0x0055 }, // 0x16 Undefined
|
||||
/* 23 */ { VC_UNDEFINED, 0x0049 }, // 0x17 VK_JUNJA IME Junja mode
|
||||
/* 24 */ { VC_UNDEFINED, 0x004F }, // 0x18 VK_FINAL
|
||||
/* 25 */ { VC_KANJI, 0x0050 }, // 0x19 VK_KANJI / VK_HANJA IME Kanji / Hanja mode
|
||||
/* 26 */ { VC_UNDEFINED, 0x00DB }, // 0x1A Undefined
|
||||
/* 27 */ { VC_ESCAPE, 0x00DD }, // 0x1B VK_ESCAPE ESC key
|
||||
/* 28 */ { VC_UNDEFINED, VK_RETURN }, // 0x1C VK_CONVERT IME convert// 0x1C
|
||||
/* 29 */ { VC_UNDEFINED, VK_LCONTROL }, // 0x1D VK_NONCONVERT IME nonconvert
|
||||
/* 30 */ { VC_UNDEFINED, 0x0041 }, // 0x1E VK_ACCEPT IME accept
|
||||
/* 31 */ { VC_UNDEFINED, 0x0053 }, // 0x1F VK_MODECHANGE IME mode change request
|
||||
/* 32 */ { VC_SPACE, 0x0044 }, // 0x20 VK_SPACE SPACEBAR
|
||||
/* 33 */ { VC_PAGE_UP, 0x0046 }, // 0x21 VK_PRIOR PAGE UP key
|
||||
/* 34 */ { VC_PAGE_DOWN, 0x0047 }, // 0x22 VK_NEXT PAGE DOWN key
|
||||
/* 35 */ { VC_END, 0x0048 }, // 0x23 VK_END END key
|
||||
/* 36 */ { VC_HOME, 0x004A }, // 0x24 VK_HOME HOME key
|
||||
/* 37 */ { VC_LEFT, 0x004B }, // 0x25 VK_LEFT LEFT ARROW key
|
||||
/* 38 */ { VC_UP, 0x004C }, // 0x26 VK_UP UP ARROW key
|
||||
/* 39 */ { VC_RIGHT, VK_OEM_1 }, // 0x27 VK_RIGHT RIGHT ARROW key
|
||||
/* 40 */ { VC_DOWN, VK_OEM_7 }, // 0x28 VK_DOWN DOWN ARROW key
|
||||
/* 41 */ { VC_UNDEFINED, VK_OEM_3 }, // 0x29 VK_SELECT SELECT key
|
||||
/* 42 */ { VC_UNDEFINED, VK_LSHIFT }, // 0x2A VK_PRINT PRINT key
|
||||
/* 43 */ { VC_UNDEFINED, VK_OEM_5 }, // 0x2B VK_EXECUTE EXECUTE key
|
||||
/* 44 */ { VC_PRINTSCREEN, 0x005A }, // 0x2C VK_SNAPSHOT PRINT SCREEN key
|
||||
/* 45 */ { VC_INSERT, 0x0058 }, // 0x2D VK_INSERT INS key
|
||||
/* 46 */ { VC_DELETE, 0x0043 }, // 0x2E VK_DELETE DEL key
|
||||
/* 47 */ { VC_UNDEFINED, 0x0056 }, // 0x2F VK_HELP HELP key
|
||||
/* 48 */ { VC_0, 0x0042 }, // 0x30 0 key
|
||||
/* 49 */ { VC_1, 0x004E }, // 0x31 1 key
|
||||
/* 50 */ { VC_2, 0x004D }, // 0x32 2 key
|
||||
/* 51 */ { VC_3, VK_OEM_COMMA }, // 0x33 3 key
|
||||
/* 52 */ { VC_4, VK_OEM_PERIOD }, // 0x34 4 key
|
||||
/* 53 */ { VC_5, VK_OEM_2 }, // 0x35 5 key
|
||||
/* 54 */ { VC_6, VK_RSHIFT }, // 0x36 6 key
|
||||
/* 55 */ { VC_7, VK_MULTIPLY }, // 0x37 7 key
|
||||
/* 56 */ { VC_8, VK_LMENU }, // 0x38 8 key
|
||||
/* 57 */ { VC_9, VK_SPACE }, // 0x39 9 key
|
||||
/* 58 */ { VC_UNDEFINED, VK_CAPITAL }, // 0x3A Undefined
|
||||
/* 59 */ { VC_UNDEFINED, VK_F1 }, // 0x3B Undefined
|
||||
/* 60 */ { VC_UNDEFINED, VK_F2 }, // 0x3C Undefined
|
||||
/* 61 */ { VC_UNDEFINED, VK_F3 }, // 0x3D Undefined
|
||||
/* 62 */ { VC_UNDEFINED, VK_F4 }, // 0x3E Undefined
|
||||
/* 63 */ { VC_UNDEFINED, VK_F5 }, // 0x3F Undefined
|
||||
/* 64 */ { VC_UNDEFINED, VK_F6 }, // 0x40 Undefined
|
||||
/* 65 */ { VC_A, VK_F7 }, // 0x41 A key
|
||||
/* 66 */ { VC_B, VK_F8 }, // 0x42 B key
|
||||
/* 67 */ { VC_C, VK_F9 }, // 0x43 C key
|
||||
/* 68 */ { VC_D, VK_F10 }, // 0x44 D key
|
||||
/* 69 */ { VC_E, VK_NUMLOCK }, // 0x45 E key
|
||||
/* 70 */ { VC_F, VK_SCROLL }, // 0x46 F key
|
||||
/* 71 */ { VC_G, VK_NUMPAD7 }, // 0x47 G key
|
||||
/* 72 */ { VC_H, VK_NUMPAD8 }, // 0x48 H key
|
||||
/* 73 */ { VC_I, VK_NUMPAD9 }, // 0x49 I key
|
||||
/* 74 */ { VC_J, VK_SUBTRACT }, // 0x4A J key
|
||||
/* 75 */ { VC_K, VK_NUMPAD4 }, // 0x4B K key
|
||||
/* 76 */ { VC_L, VK_NUMPAD5 }, // 0x4C L key
|
||||
/* 77 */ { VC_M, VK_NUMPAD6 }, // 0x4D M key
|
||||
/* 78 */ { VC_N, VK_ADD }, // 0x4E N key
|
||||
/* 79 */ { VC_O, VK_NUMPAD1 }, // 0x4F O key
|
||||
/* 80 */ { VC_P, VK_NUMPAD2 }, // 0x50 P key
|
||||
/* 81 */ { VC_Q, VK_NUMPAD3 }, // 0x51 Q key
|
||||
/* 82 */ { VC_R, VK_NUMPAD0 }, // 0x52 R key
|
||||
/* 83 */ { VC_S, VK_DECIMAL }, // 0x53 S key
|
||||
/* 84 */ { VC_T, 0x0000 }, // 0x54 T key
|
||||
/* 85 */ { VC_U, 0x0000 }, // 0x55 U key
|
||||
/* 86 */ { VC_V, 0x0000 }, // 0x56 V key
|
||||
/* 87 */ { VC_W, VK_F11 }, // 0x57 W key
|
||||
/* 88 */ { VC_X, VK_F12 }, // 0x58 X key
|
||||
/* 89 */ { VC_Y, 0x0000 }, // 0x59 Y key
|
||||
/* 90 */ { VC_Z, 0x0000 }, // 0x5A Z key
|
||||
/* 91 */ { VC_META_L, VK_F13 }, // 0x5B VK_LWIN Left Windows key (Natural keyboard)
|
||||
/* 92 */ { VC_META_R, VK_F14 }, // 0x5C VK_RWIN Right Windows key (Natural keyboard)
|
||||
/* 93 */ { VC_CONTEXT_MENU, VK_F15 }, // 0x5D VK_APPS Applications key (Natural keyboard)
|
||||
/* 94 */ { VC_UNDEFINED, 0x0000 }, // 0x5E Reserved
|
||||
/* 95 */ { VC_SLEEP, 0x0000 }, // 0x5F VK_SLEEP Computer Sleep key
|
||||
/* 96 */ { VC_KP_0, 0x0000 }, // 0x60 VK_NUMPAD0 Numeric keypad 0 key
|
||||
/* 97 */ { VC_KP_1, 0x0000 }, // 0x61 VK_NUMPAD1 Numeric keypad 1 key
|
||||
/* 98 */ { VC_KP_2, 0x0000 }, // 0x62 VK_NUMPAD2 Numeric keypad 2 key
|
||||
/* 99 */ { VC_KP_3, VK_F16 }, // 0x63 VK_NUMPAD3 Numeric keypad 3 key
|
||||
/* 100 */ { VC_KP_4, VK_F17 }, // 0x64 VK_NUMPAD4 Numeric keypad 4 key
|
||||
/* 101 */ { VC_KP_5, VK_F18 }, // 0x65 VK_NUMPAD5 Numeric keypad 5 key
|
||||
/* 102 */ { VC_KP_6, VK_F19 }, // 0x66 VK_NUMPAD6 Numeric keypad 6 key
|
||||
/* 103 */ { VC_KP_7, VK_F20 }, // 0x67 VK_NUMPAD7 Numeric keypad 7 key
|
||||
/* 104 */ { VC_KP_8, VK_F21 }, // 0x68 VK_NUMPAD8 Numeric keypad 8 key
|
||||
/* 105 */ { VC_KP_9, VK_F22 }, // 0x69 VK_NUMPAD9 Numeric keypad 9 key
|
||||
/* 106 */ { VC_KP_MULTIPLY, VK_F23 }, // 0x6A VK_MULTIPLY Multiply key
|
||||
/* 107 */ { VC_KP_ADD, VK_F24 }, // 0x6B VK_ADD Add key
|
||||
/* 108 */ { VC_UNDEFINED, 0x0000 }, // 0x6C VK_SEPARATOR Separator key
|
||||
/* 109 */ { VC_KP_SUBTRACT, 0x0000 }, // 0x6D VK_SUBTRACT Subtract key
|
||||
/* 110 */ { VC_KP_SEPARATOR, 0x0000 }, // 0x6E VK_DECIMAL Decimal key
|
||||
/* 111 */ { VC_KP_DIVIDE, 0x0000 }, // 0x6F VK_DIVIDE Divide key
|
||||
/* 112 */ { VC_F1, VK_KANA }, // 0x70 VK_F1 F1 key
|
||||
/* 113 */ { VC_F2, 0x0000 }, // 0x71 VK_F2 F2 key
|
||||
/* 114 */ { VC_F3, 0x0000 }, // 0x72 VK_F3 F3 key
|
||||
/* 115 */ { VC_F4, 0x0000 }, // 0x73 VK_F4 F4 key
|
||||
/* 116 */ { VC_F5, 0x0000 }, // 0x74 VK_F5 F5 key
|
||||
/* 117 */ { VC_F6, 0x0000 }, // 0x75 VK_F6 F6 key
|
||||
/* 118 */ { VC_F7, 0x0000 }, // 0x76 VK_F7 F7 key
|
||||
/* 119 */ { VC_F8, 0x0000 }, // 0x77 VK_F8 F8 key
|
||||
/* 120 */ { VC_F9, 0x0000 }, // 0x78 VK_F9 F9 key
|
||||
/* 121 */ { VC_F10, VK_KANJI }, // 0x79 VK_F10 F10 key
|
||||
/* 122 */ { VC_F11, 0x0000 }, // 0x7A VK_F11 F11 key
|
||||
/* 123 */ { VC_F12, 0x0000 }, // 0x7B VK_F12 F12 key
|
||||
/* 124 */ { VC_F13, 0x0000 }, // 0x7C VK_F13 F13 key
|
||||
/* 125 */ { VC_F14, VK_OEM_8 }, // 0x7D VK_F14 F14 key
|
||||
/* 126 */ { VC_F15, 0x0000 }, // 0x7E VK_F15 F15 key
|
||||
/* 127 */ { VC_F16, 0x0000 }, // 0x7F VK_F16 F16 key
|
||||
|
||||
// No Offset Offset (i & 0x007F) | 0x80
|
||||
|
||||
/* 128 */ { VC_F17, 0x0000 }, // 0x80 VK_F17 F17 key
|
||||
/* 129 */ { VC_F18, 0x0000 }, // 0x81 VK_F18 F18 key
|
||||
/* 130 */ { VC_F19, 0x0000 }, // 0x82 VK_F19 F19 key
|
||||
/* 131 */ { VC_F20, 0x0000 }, // 0x83 VK_F20 F20 key
|
||||
/* 132 */ { VC_F21, 0x0000 }, // 0x84 VK_F21 F21 key
|
||||
/* 133 */ { VC_F22, 0x0000 }, // 0x85 VK_F22 F22 key
|
||||
/* 134 */ { VC_F23, 0x0000 }, // 0x86 VK_F23 F23 key
|
||||
/* 135 */ { VC_F24, 0x0000 }, // 0x87 VK_F24 F24 key
|
||||
/* 136 */ { VC_UNDEFINED, 0x0000 }, // 0x88 Unassigned
|
||||
/* 137 */ { VC_UNDEFINED, 0x0000 }, // 0x89 Unassigned
|
||||
/* 138 */ { VC_UNDEFINED, 0x0000 }, // 0x8A Unassigned
|
||||
/* 139 */ { VC_UNDEFINED, 0x0000 }, // 0x8B Unassigned
|
||||
/* 140 */ { VC_UNDEFINED, 0x0000 }, // 0x8C Unassigned
|
||||
/* 141 */ { VC_UNDEFINED, 0x0000 }, // 0x8D Unassigned
|
||||
/* 142 */ { VC_UNDEFINED, 0x0000 }, // 0x8E Unassigned
|
||||
/* 143 */ { VC_UNDEFINED, 0x0000 }, // 0x8F Unassigned
|
||||
/* 144 */ { VC_NUM_LOCK, VK_MEDIA_PREV_TRACK }, // 0x90 VK_NUMLOCK NUM LOCK key
|
||||
/* 145 */ { VC_SCROLL_LOCK, 0x0000 }, // 0x91 VK_SCROLL SCROLL LOCK key
|
||||
/* 146 */ { VC_UNDEFINED, 0x0000 }, // 0x92 OEM specific
|
||||
/* 147 */ { VC_UNDEFINED, 0x0000 }, // 0x93 OEM specific
|
||||
/* 148 */ { VC_UNDEFINED, 0x0000 }, // 0x94 OEM specific
|
||||
/* 149 */ { VC_UNDEFINED, 0x0000 }, // 0x95 OEM specific
|
||||
/* 150 */ { VC_UNDEFINED, 0x0000 }, // 0x96 OEM specific
|
||||
/* 151 */ { VC_UNDEFINED, 0x0000 }, // 0x97 Unassigned
|
||||
/* 152 */ { VC_UNDEFINED, 0x0000 }, // 0x98 Unassigned
|
||||
/* 153 */ { VC_UNDEFINED, VK_MEDIA_NEXT_TRACK }, // 0x99 Unassigned
|
||||
/* 154 */ { VC_UNDEFINED, 0x0000 }, // 0x9A Unassigned
|
||||
/* 155 */ { VC_UNDEFINED, 0x0000 }, // 0x9B Unassigned
|
||||
/* 156 */ { VC_UNDEFINED, 0x0000 }, // 0x9C Unassigned
|
||||
/* 157 */ { VC_UNDEFINED, VK_RCONTROL }, // 0x9D Unassigned
|
||||
/* 158 */ { VC_UNDEFINED, 0x0000 }, // 0x9E Unassigned
|
||||
/* 159 */ { VC_UNDEFINED, 0x0000 }, // 0x9F Unassigned
|
||||
/* 160 */ { VC_SHIFT_L, VK_VOLUME_MUTE }, // 0xA0 VK_LSHIFT Left SHIFT key
|
||||
/* 161 */ { VC_SHIFT_R, VK_LAUNCH_APP2 }, // 0xA1 VK_RSHIFT Right SHIFT key
|
||||
/* 162 */ { VC_CONTROL_L, VK_MEDIA_PLAY_PAUSE }, // 0xA2 VK_LCONTROL Left CONTROL key
|
||||
/* 163 */ { VC_CONTROL_R, 0x0000 }, // 0xA3 VK_RCONTROL Right CONTROL key
|
||||
/* 164 */ { VC_ALT_L, VK_MEDIA_STOP }, // 0xA4 VK_LMENU Left MENU key
|
||||
/* 165 */ { VC_ALT_R, 0x0000 }, // 0xA5 VK_RMENU Right MENU key
|
||||
/* 166 */ { VC_BROWSER_BACK, 0x0000 }, // 0xA6 VK_BROWSER_BACK Browser Back key
|
||||
/* 167 */ { VC_BROWSER_FORWARD, 0x0000 }, // 0xA7 VK_BROWSER_FORWARD Browser Forward key
|
||||
/* 168 */ { VC_BROWSER_REFRESH, 0x0000 }, // 0xA8 VK_BROWSER_REFRESH Browser Refresh key
|
||||
/* 169 */ { VC_BROWSER_STOP, 0x0000 }, // 0xA9 VK_BROWSER_STOP Browser Stop key
|
||||
/* 170 */ { VC_BROWSER_SEARCH, 0x0000 }, // 0xAA VK_BROWSER_SEARCH Browser Search key
|
||||
/* 171 */ { VC_BROWSER_FAVORITES, 0x0000 }, // 0xAB VK_BROWSER_FAVORITES Browser Favorites key
|
||||
/* 172 */ { VC_BROWSER_HOME, 0x0000 }, // 0xAC VK_BROWSER_HOME Browser Start and Home key
|
||||
/* 173 */ { VC_VOLUME_MUTE, 0x0000 }, // 0xAD VK_VOLUME_MUTE Volume Mute key
|
||||
/* 174 */ { VC_VOLUME_DOWN, VK_VOLUME_DOWN }, // 0xAE VK_VOLUME_DOWN Volume Down key
|
||||
/* 175 */ { VC_VOLUME_UP, 0x0000 }, // 0xAF VK_VOLUME_UP Volume Up key
|
||||
/* 176 */ { VC_MEDIA_NEXT, VK_VOLUME_UP }, // 0xB0 VK_MEDIA_NEXT_TRACK Next Track key
|
||||
/* 177 */ { VC_MEDIA_PREVIOUS, 0x0000 }, // 0xB1 VK_MEDIA_PREV_TRACK Previous Track key
|
||||
/* 178 */ { VC_MEDIA_STOP, VK_BROWSER_HOME }, // 0xB2 VK_MEDIA_STOP Stop Media key
|
||||
/* 179 */ { VC_MEDIA_PLAY, 0x0000 }, // 0xB3 VK_MEDIA_PLAY_PAUSE Play/Pause Media key
|
||||
/* 180 */ { VC_UNDEFINED, 0x0000 }, // 0xB4 VK_LAUNCH_MAIL Start Mail key
|
||||
/* 181 */ { VC_MEDIA_SELECT, VK_DIVIDE }, // 0xB5 VK_LAUNCH_MEDIA_SELECT Select Media key
|
||||
/* 182 */ { VC_APP_MAIL, 0x0000 }, // 0xB6 VK_LAUNCH_APP1 Start Application 1 key
|
||||
/* 183 */ { VC_APP_CALCULATOR, VK_SNAPSHOT }, // 0xB7 VK_LAUNCH_APP2 Start Application 2 key
|
||||
/* 184 */ { VC_UNDEFINED, VK_RMENU }, // 0xB8 Reserved
|
||||
/* 185 */ { VC_UNDEFINED, 0x0000 }, // 0xB9 Reserved
|
||||
/* 186 */ { VC_SEMICOLON, 0x0000 }, // 0xBA VK_OEM_1 Varies by keyboard. For the US standard keyboard, the ';:' key
|
||||
/* 187 */ { VC_EQUALS, 0x0000 }, // 0xBB VK_OEM_PLUS For any country/region, the '+' key
|
||||
/* 188 */ { VC_COMMA, 0x00E6 }, // 0xBC VK_OEM_COMMA For any country/region, the ',' key
|
||||
/* 189 */ { VC_MINUS, 0x0000 }, // 0xBD VK_OEM_MINUS For any country/region, the '-' key
|
||||
/* 190 */ { VC_PERIOD, 0x0000 }, // 0xBE VK_OEM_PERIOD For any country/region, the '.' key
|
||||
/* 191 */ { VC_SLASH, 0x0000 }, // 0xBF VK_OEM_2 Varies by keyboard. For the US standard keyboard, the '/?' key
|
||||
/* 192 */ { VC_BACKQUOTE, 0x0000 }, // 0xC0 VK_OEM_3 Varies by keyboard. For the US standard keyboard, the '`~' key
|
||||
/* 193 */ { VC_UNDEFINED, 0x0000 }, // 0xC1 Reserved
|
||||
/* 194 */ { VC_UNDEFINED, 0x0000 }, // 0xC2 Reserved
|
||||
/* 195 */ { VC_UNDEFINED, 0x0000 }, // 0xC3 Reserved
|
||||
/* 196 */ { VC_UNDEFINED, 0x0000 }, // 0xC4 Reserved
|
||||
/* 197 */ { VC_UNDEFINED, VK_PAUSE }, // 0xC5 Reserved
|
||||
/* 198 */ { VC_UNDEFINED, 0x0000 }, // 0xC6 Reserved
|
||||
/* 199 */ { VC_UNDEFINED, VK_HOME }, // 0xC7 Reserved
|
||||
/* 200 */ { VC_UNDEFINED, VK_UP }, // 0xC8 Reserved
|
||||
/* 201 */ { VC_UNDEFINED, VK_PRIOR }, // 0xC9 Reserved
|
||||
/* 202 */ { VC_UNDEFINED, 0x0000 }, // 0xCA Reserved
|
||||
/* 203 */ { VC_UNDEFINED, VK_LEFT }, // 0xCB Reserved
|
||||
/* 204 */ { VC_UNDEFINED, VK_CLEAR }, // 0xCC Reserved
|
||||
/* 205 */ { VC_UNDEFINED, VK_RIGHT }, // 0xCD Reserved
|
||||
/* 206 */ { VC_UNDEFINED, 0x0000 }, // 0xCE Reserved
|
||||
/* 207 */ { VC_UNDEFINED, VK_END }, // 0xCF Reserved
|
||||
/* 208 */ { VC_UNDEFINED, VK_DOWN }, // 0xD0 Reserved
|
||||
/* 209 */ { VC_UNDEFINED, VK_NEXT }, // 0xD1 Reserved
|
||||
/* 210 */ { VC_UNDEFINED, VK_INSERT }, // 0xD2 Reserved
|
||||
/* 211 */ { VC_UNDEFINED, VK_DELETE }, // 0xD3 Reserved
|
||||
/* 212 */ { VC_UNDEFINED, 0x0000 }, // 0xD4 Reserved
|
||||
/* 213 */ { VC_UNDEFINED, 0x0000 }, // 0xD5 Reserved
|
||||
/* 214 */ { VC_UNDEFINED, 0x0000 }, // 0xD6 Reserved
|
||||
/* 215 */ { VC_UNDEFINED, 0x0000 }, // 0xD7 Reserved
|
||||
/* 216 */ { VC_UNDEFINED, 0x0000 }, // 0xD8 Unassigned
|
||||
/* 217 */ { VC_UNDEFINED, 0x0000 }, // 0xD9 Unassigned
|
||||
/* 218 */ { VC_UNDEFINED, 0x0000 }, // 0xDA Unassigned
|
||||
/* 219 */ { VC_OPEN_BRACKET, VK_LWIN }, // 0xDB VK_OEM_4 Varies by keyboard. For the US standard keyboard, the '[{' key
|
||||
/* 220 */ { VC_BACK_SLASH, VK_RWIN }, // 0xDC VK_OEM_5 Varies by keyboard. For the US standard keyboard, the '\|' key
|
||||
/* 221 */ { VC_CLOSE_BRACKET, VK_APPS }, // 0xDD VK_OEM_6 Varies by keyboard. For the US standard keyboard, the ']}' key
|
||||
/* 222 */ { VC_QUOTE, 0x0000 }, // 0xDE VK_OEM_7 Varies by keyboard. For the US standard keyboard, the 'single-quote/double-quote' key
|
||||
/* 223 */ { VC_YEN, VK_SLEEP }, // 0xDF VK_OEM_8 Varies by keyboard.
|
||||
/* 224 */ { VC_UNDEFINED, 0x0000 }, // 0xE0 Reserved
|
||||
/* 225 */ { VC_UNDEFINED, 0x0000 }, // 0xE1 OEM specific
|
||||
/* 226 */ { VC_UNDEFINED, 0x0000 }, // 0xE2 VK_OEM_102 Either the angle bracket key or the backslash key on the RT 102-key keyboard
|
||||
/* 227 */ { VC_UNDEFINED, 0x0000 }, // 0xE3 OEM specific
|
||||
/* 228 */ { VC_UNDEFINED, 0x00E5 }, // 0xE4 VC_APP_PICTURES OEM specific
|
||||
/* 229 */ { VC_APP_PICTURES, VK_BROWSER_SEARCH }, // 0xE5 VK_PROCESSKEY IME PROCESS key
|
||||
/* 230 */ { VC_APP_MUSIC, VK_BROWSER_FAVORITES }, // 0xE6 OEM specific
|
||||
/* 231 */ { VC_UNDEFINED, VK_BROWSER_REFRESH }, // 0xE7 VK_PACKET Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods.
|
||||
/* 232 */ { VC_UNDEFINED, VK_BROWSER_STOP }, // 0xE8 Unassigned
|
||||
/* 233 */ { VC_UNDEFINED, VK_BROWSER_FORWARD }, // 0xE9 OEM specific
|
||||
/* 234 */ { VC_UNDEFINED, VK_BROWSER_BACK }, // 0xEA OEM specific
|
||||
/* 235 */ { VC_UNDEFINED, 0x0000 }, // 0xEB OEM specific
|
||||
/* 236 */ { VC_UNDEFINED, VK_LAUNCH_APP1 }, // 0xEC OEM specific
|
||||
/* 237 */ { VC_UNDEFINED, VK_LAUNCH_MEDIA_SELECT }, // 0xED OEM specific
|
||||
/* 238 */ { VC_UNDEFINED, 0x0000 }, // 0xEE OEM specific
|
||||
/* 239 */ { VC_UNDEFINED, 0x0000 }, // 0xEF OEM specific
|
||||
/* 240 */ { VC_UNDEFINED, 0x0000 }, // 0xF0 OEM specific
|
||||
/* 241 */ { VC_UNDEFINED, 0x0000 }, // 0xF1 OEM specific
|
||||
/* 242 */ { VC_UNDEFINED, 0x0000 }, // 0xF2 OEM specific
|
||||
/* 243 */ { VC_UNDEFINED, 0x0000 }, // 0xF3 OEM specific
|
||||
/* 244 */ { VC_UNDEFINED, 0x0000 }, // 0xF4 OEM specific
|
||||
/* 245 */ { VC_UNDEFINED, 0x0000 }, // 0xF5 OEM specific
|
||||
/* 246 */ { VC_UNDEFINED, 0x0000 }, // 0xF6 VK_ATTN Attn key
|
||||
/* 247 */ { VC_UNDEFINED, 0x0000 }, // 0xF7 VK_CRSEL CrSel key
|
||||
/* 248 */ { VC_UNDEFINED, 0x0000 }, // 0xF8 VK_EXSEL ExSel key
|
||||
/* 249 */ { VC_UNDEFINED, 0x0000 }, // 0xF9 VK_EREOF Erase EOF key
|
||||
/* 250 */ { VC_UNDEFINED, 0x0000 }, // 0xFA VK_PLAY Play key
|
||||
/* 251 */ { VC_UNDEFINED, 0x0000 }, // 0xFB VK_ZOOM Zoom key
|
||||
/* 252 */ { VC_UNDEFINED, 0x0000 }, // 0xFC VK_NONAME Reserved
|
||||
/* 253 */ { VC_UNDEFINED, 0x0000 }, // 0xFD
|
||||
/* 254 */ { VC_CLEAR, 0x0000 }, // 0xFE VK_OEM_CLEAR Clear key
|
||||
/* 255 */ { VC_UNDEFINED, 0x0000 } // 0xFE Unassigned
|
||||
};
|
||||
|
||||
unsigned short keycode_to_scancode(DWORD vk_code, DWORD flags) {
|
||||
unsigned short scancode = VC_UNDEFINED;
|
||||
|
||||
// Check the vk_code is in range.
|
||||
// NOTE vk_code >= 0 is assumed because DWORD is unsigned.
|
||||
if (vk_code < sizeof(keycode_scancode_table) / sizeof(keycode_scancode_table[0])) {
|
||||
scancode = keycode_scancode_table[vk_code][0];
|
||||
|
||||
if (flags & LLKHF_EXTENDED) {
|
||||
logger(LOG_LEVEL_WARN, "%s [%u]: EXTD2, vk_code %li\n",
|
||||
__FUNCTION__, __LINE__, vk_code);
|
||||
|
||||
switch (vk_code) {
|
||||
case VK_PRIOR:
|
||||
case VK_NEXT:
|
||||
case VK_END:
|
||||
case VK_HOME:
|
||||
case VK_LEFT:
|
||||
case VK_UP:
|
||||
case VK_RIGHT:
|
||||
case VK_DOWN:
|
||||
|
||||
case VK_INSERT:
|
||||
case VK_DELETE:
|
||||
scancode |= 0xEE00;
|
||||
break;
|
||||
|
||||
case VK_RETURN:
|
||||
scancode |= 0x0E00;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// logger(LOG_LEVEL_WARN, "%s [%u]: Test2, vk_code %li\n",
|
||||
// __FUNCTION__, __LINE__, vk_code);
|
||||
}
|
||||
}
|
||||
|
||||
return scancode;
|
||||
}
|
||||
|
||||
DWORD scancode_to_keycode(unsigned short scancode) {
|
||||
unsigned short keycode = 0x0000;
|
||||
|
||||
// Check the vk_code is in range.
|
||||
// NOTE vk_code >= 0 is assumed because the scancode is unsigned.
|
||||
if (scancode < 128) {
|
||||
keycode = keycode_scancode_table[scancode][1];
|
||||
}
|
||||
else {
|
||||
// Calculate the upper offset based on the lower half of the scancode + 128.
|
||||
unsigned short int i = (scancode & 0x007F) | 0x80;
|
||||
|
||||
if (i < sizeof(keycode_scancode_table) / sizeof(keycode_scancode_table[1])) {
|
||||
keycode = keycode_scancode_table[i][1];
|
||||
}
|
||||
}
|
||||
|
||||
return keycode;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
|
||||
// Structure and pointers for the keyboard locale cache.
|
||||
typedef struct _KeyboardLocale {
|
||||
HKL id; // Locale ID
|
||||
HINSTANCE library; // Keyboard DLL instance.
|
||||
PVK_TO_BIT pVkToBit; // Pointers struct arrays.
|
||||
PVK_TO_WCHAR_TABLE pVkToWcharTable;
|
||||
PDEADKEY pDeadKey;
|
||||
struct _KeyboardLocale* next;
|
||||
} KeyboardLocale;
|
||||
|
||||
static KeyboardLocale* locale_first = NULL;
|
||||
static KeyboardLocale* locale_current = NULL;
|
||||
static WCHAR deadChar = WCH_NONE;
|
||||
|
||||
// Amount of pointer padding to apply for Wow64 instances.
|
||||
static unsigned short int ptr_padding = 0;
|
||||
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
// Small function to check and see if we are executing under Wow64.
|
||||
static BOOL is_wow64() {
|
||||
BOOL status = FALSE;
|
||||
|
||||
LPFN_ISWOW64PROCESS pIsWow64Process = (LPFN_ISWOW64PROCESS)
|
||||
GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process");
|
||||
|
||||
if (pIsWow64Process != NULL) {
|
||||
HANDLE current_proc = GetCurrentProcess();
|
||||
|
||||
if (!pIsWow64Process(current_proc, &status)) {
|
||||
status = FALSE;
|
||||
|
||||
logger(LOG_LEVEL_DEBUG, "%s [%u]: pIsWow64Process(%#p, %#p) failed!\n",
|
||||
__FUNCTION__, __LINE__, current_proc, &status);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Locate the DLL that contains the current keyboard layout.
|
||||
static int get_keyboard_layout_file(char *layoutFile, DWORD bufferSize) {
|
||||
int status = IOHOOK_FAILURE;
|
||||
HKEY hKey;
|
||||
DWORD varType = REG_SZ;
|
||||
|
||||
char kbdName[KL_NAMELENGTH];
|
||||
if (GetKeyboardLayoutName(kbdName)) {
|
||||
char kbdKeyPath[51 + KL_NAMELENGTH];
|
||||
snprintf(kbdKeyPath, 51 + KL_NAMELENGTH, "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\%s", kbdName);
|
||||
|
||||
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, (LPCTSTR) kbdKeyPath, 0, KEY_QUERY_VALUE, &hKey) == ERROR_SUCCESS) {
|
||||
if (RegQueryValueEx(hKey, "Layout File", NULL, &varType, (LPBYTE) layoutFile, &bufferSize) == ERROR_SUCCESS) {
|
||||
RegCloseKey(hKey);
|
||||
status = IOHOOK_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int refresh_locale_list() {
|
||||
int count = 0;
|
||||
|
||||
// Get the number of layouts the user has activated.
|
||||
int hkl_size = GetKeyboardLayoutList(0, NULL);
|
||||
if (hkl_size > 0) {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: GetKeyboardLayoutList(0, NULL) found %i layouts.\n",
|
||||
__FUNCTION__, __LINE__, hkl_size);
|
||||
|
||||
// Get the thread id that currently has focus for our default.
|
||||
DWORD focus_pid = GetWindowThreadProcessId(GetForegroundWindow(), NULL);
|
||||
HKL hlk_focus = GetKeyboardLayout(focus_pid);
|
||||
HKL hlk_default = GetKeyboardLayout(0);
|
||||
HKL *hkl_list = malloc(sizeof(HKL) * hkl_size);
|
||||
|
||||
int new_size = GetKeyboardLayoutList(hkl_size, hkl_list);
|
||||
if (new_size > 0) {
|
||||
if (new_size != hkl_size) {
|
||||
logger(LOG_LEVEL_WARN, "%s [%u]: Locale size mismatch! "
|
||||
"Expected %i, received %i!\n",
|
||||
__FUNCTION__, __LINE__, hkl_size, new_size);
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: Received %i locales.\n",
|
||||
__FUNCTION__, __LINE__, new_size);
|
||||
}
|
||||
|
||||
KeyboardLocale* locale_previous = NULL;
|
||||
KeyboardLocale* locale_item = locale_first;
|
||||
|
||||
// Go though the linked list and remove KeyboardLocale's that are
|
||||
// no longer loaded.
|
||||
while (locale_item != NULL) {
|
||||
// Check to see if the old HKL is in the new list.
|
||||
bool is_loaded = false;
|
||||
int i;
|
||||
for (i = 0; i < new_size && !is_loaded; i++) {
|
||||
if (locale_item->id == hkl_list[i]) {
|
||||
// Flag and jump out of the loop.
|
||||
hkl_list[i] = NULL;
|
||||
is_loaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (is_loaded) {
|
||||
logger(LOG_LEVEL_DEBUG, "%s [%u]: Found locale ID %#p in the cache.\n",
|
||||
__FUNCTION__, __LINE__, locale_item->id);
|
||||
|
||||
// Set the previous local to the current locale.
|
||||
locale_previous = locale_item;
|
||||
|
||||
// Check and see if the locale is our current active locale.
|
||||
if (locale_item->id == hlk_focus) {
|
||||
locale_current = locale_item;
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_DEBUG, "%s [%u]: Removing locale ID %#p from the cache.\n",
|
||||
__FUNCTION__, __LINE__, locale_item->id);
|
||||
|
||||
// If the old id is not in the new list, remove it.
|
||||
locale_previous->next = locale_item->next;
|
||||
|
||||
// Make sure the locale_current points NULL or something valid.
|
||||
if (locale_item == locale_current) {
|
||||
locale_current = NULL;
|
||||
}
|
||||
|
||||
// Free the memory used by locale_item;
|
||||
free(locale_item);
|
||||
|
||||
// Set the item to the pervious item to guarantee a next.
|
||||
locale_item = locale_previous;
|
||||
}
|
||||
|
||||
// Iterate to the next linked list item.
|
||||
locale_item = locale_item->next;
|
||||
}
|
||||
|
||||
|
||||
// Insert anything new into the linked list.
|
||||
int i;
|
||||
for (i = 0; i < new_size; i++) {
|
||||
// Check to see if the item was already in the list.
|
||||
if (hkl_list[i] != NULL) {
|
||||
// Set the active keyboard layout for this thread to the HKL.
|
||||
ActivateKeyboardLayout(hkl_list[i], 0x00);
|
||||
|
||||
// Try to pull the current keyboard layout DLL from the registry.
|
||||
char layoutFile[MAX_PATH];
|
||||
if (get_keyboard_layout_file(layoutFile, sizeof(layoutFile)) == IOHOOK_SUCCESS) {
|
||||
// You can't trust the %SYSPATH%, look it up manually.
|
||||
char systemDirectory[MAX_PATH];
|
||||
if (GetSystemDirectory(systemDirectory, MAX_PATH) != 0) {
|
||||
char kbdLayoutFilePath[MAX_PATH];
|
||||
snprintf(kbdLayoutFilePath, MAX_PATH, "%s\\%s", systemDirectory, layoutFile);
|
||||
|
||||
logger(LOG_LEVEL_DEBUG, "%s [%u]: Loading layout for %#p: %s.\n",
|
||||
__FUNCTION__, __LINE__, hkl_list[i], layoutFile);
|
||||
|
||||
// Create the new locale item.
|
||||
locale_item = malloc(sizeof(KeyboardLocale));
|
||||
locale_item->id = hkl_list[i];
|
||||
locale_item->library = LoadLibrary(kbdLayoutFilePath);
|
||||
|
||||
// Get the function pointer from the library to get the keyboard layer descriptor.
|
||||
KbdLayerDescriptor pKbdLayerDescriptor = (KbdLayerDescriptor) GetProcAddress(locale_item->library, "KbdLayerDescriptor");
|
||||
if (pKbdLayerDescriptor != NULL) {
|
||||
PKBDTABLES pKbd = pKbdLayerDescriptor();
|
||||
|
||||
// Store the memory address of the following 3 structures.
|
||||
BYTE *base = (BYTE *) pKbd;
|
||||
|
||||
// First element of each structure, no offset adjustment needed.
|
||||
locale_item->pVkToBit = pKbd->pCharModifiers->pVkToBit;
|
||||
|
||||
// Second element of pKbd, +4 byte offset on wow64.
|
||||
locale_item->pVkToWcharTable = *((PVK_TO_WCHAR_TABLE *) (base + offsetof(KBDTABLES, pVkToWcharTable) + ptr_padding));
|
||||
|
||||
// Third element of pKbd, +8 byte offset on wow64.
|
||||
locale_item->pDeadKey = *((PDEADKEY *) (base + offsetof(KBDTABLES, pDeadKey) + (ptr_padding * 2)));
|
||||
|
||||
// This will always be added to the end of the list.
|
||||
locale_item->next = NULL;
|
||||
|
||||
// Insert the item into the linked list.
|
||||
if (locale_previous == NULL) {
|
||||
// If nothing came before, the list is empty.
|
||||
locale_first = locale_item;
|
||||
}
|
||||
else {
|
||||
// Append the new locale to the end of the list.
|
||||
locale_previous->next = locale_item;
|
||||
}
|
||||
|
||||
// Check and see if the locale is our current active locale.
|
||||
if (locale_item->id == hlk_focus) {
|
||||
locale_current = locale_item;
|
||||
}
|
||||
|
||||
// Set the pervious locale item to the new one.
|
||||
locale_previous = locale_item;
|
||||
|
||||
count++;
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_ERROR,
|
||||
"%s [%u]: GetProcAddress() failed for KbdLayerDescriptor!\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
|
||||
FreeLibrary(locale_item->library);
|
||||
free(locale_item);
|
||||
locale_item = NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_ERROR,
|
||||
"%s [%u]: GetSystemDirectory() failed!\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_ERROR,
|
||||
"%s [%u]: Could not find keyboard map for locale %#p!\n",
|
||||
__FUNCTION__, __LINE__, hkl_list[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_ERROR,
|
||||
"%s [%u]: GetKeyboardLayoutList() failed!\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
|
||||
// TODO Try and recover by using the current layout.
|
||||
// Hint: Use locale_id instead of hkl_list[i] in the loop above.
|
||||
}
|
||||
|
||||
free(hkl_list);
|
||||
ActivateKeyboardLayout(hlk_default, 0x00);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
SIZE_T keycode_to_unicode(DWORD keycode, PWCHAR buffer, SIZE_T size) {
|
||||
// Get the thread id that currently has focus and ask for its current
|
||||
// locale.
|
||||
DWORD focus_pid = GetWindowThreadProcessId(GetForegroundWindow(), NULL);
|
||||
HKL locale_id = GetKeyboardLayout(focus_pid);
|
||||
|
||||
// If the current Locale is not the new locale, search the linked list.
|
||||
if (locale_current == NULL || locale_current->id != locale_id) {
|
||||
locale_current = NULL;
|
||||
KeyboardLocale* locale_item = locale_first;
|
||||
|
||||
// Search the linked list...
|
||||
while (locale_item != NULL && locale_item->id != locale_id) {
|
||||
locale_item = locale_item->next;
|
||||
}
|
||||
|
||||
// You may already be a winner!
|
||||
if (locale_item != NULL && locale_item->id != locale_id) {
|
||||
logger(LOG_LEVEL_INFO,
|
||||
"%s [%u]: Activating keyboard layout %#p.\n",
|
||||
__FUNCTION__, __LINE__, locale_item->id);
|
||||
|
||||
// Switch the current locale.
|
||||
locale_current = locale_item;
|
||||
locale_item = NULL;
|
||||
|
||||
// If they layout changes the dead key state needs to be reset.
|
||||
// This is consistent with the way Windows handles locale changes.
|
||||
deadChar = WCH_NONE;
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_DEBUG,
|
||||
"%s [%u]: Refreshing locale cache.\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
|
||||
refresh_locale_list();
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize to empty.
|
||||
SIZE_T charCount = 0;
|
||||
// buffer[i] = WCH_NONE;
|
||||
|
||||
// Check and make sure the Unicode helper was loaded.
|
||||
if (locale_current != NULL) {
|
||||
logger(LOG_LEVEL_INFO,
|
||||
"%s [%u]: Using keyboard layout %#p.\n",
|
||||
__FUNCTION__, __LINE__, locale_current->id);
|
||||
|
||||
int mod = 0;
|
||||
|
||||
int capsLock = (GetKeyState(VK_CAPITAL) & 0x01);
|
||||
|
||||
PVK_TO_BIT pVkToBit = locale_current->pVkToBit;
|
||||
PVK_TO_WCHAR_TABLE pVkToWcharTable = locale_current->pVkToWcharTable;
|
||||
PDEADKEY pDeadKey = locale_current->pDeadKey;
|
||||
|
||||
/* Loop over the modifier keys for this locale and determine what is
|
||||
* currently depressed. Because this is only a structure of two
|
||||
* bytes, we don't need to worry about the structure padding of __ptr64
|
||||
* offsets on Wow64.
|
||||
*/
|
||||
bool is_shift = false, is_ctrl = false, is_alt = false;
|
||||
int i;
|
||||
for (i = 0; pVkToBit[i].Vk != 0; i++) {
|
||||
short state = GetAsyncKeyState(pVkToBit[i].Vk);
|
||||
|
||||
// Check to see if the most significant bit is active.
|
||||
if (state & ~SHRT_MAX) {
|
||||
if (pVkToBit[i].Vk == VK_SHIFT) {
|
||||
is_shift = true;
|
||||
}
|
||||
else if (pVkToBit[i].Vk == VK_CONTROL) {
|
||||
is_ctrl = true;
|
||||
}
|
||||
else if (pVkToBit[i].Vk == VK_MENU) {
|
||||
is_alt = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check the Shift modifier.
|
||||
if (is_shift) {
|
||||
mod = 1;
|
||||
}
|
||||
|
||||
// Check for the AltGr modifier.
|
||||
if (is_ctrl && is_alt) {
|
||||
mod += 3;
|
||||
}
|
||||
|
||||
// Default 32 bit structure size should be 6 bytes (4 for the pointer and 2
|
||||
// additional byte fields) that are padded out to 8 bytes by the compiler.
|
||||
unsigned short sizeVkToWcharTable = sizeof(VK_TO_WCHAR_TABLE);
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
if (is_wow64()) {
|
||||
// If we are running under Wow64 the size of the first pointer will be
|
||||
// 8 bringing the total size to 10 bytes padded out to 16.
|
||||
sizeVkToWcharTable = (sizeVkToWcharTable + ptr_padding + 7) & -8;
|
||||
}
|
||||
#endif
|
||||
|
||||
BYTE *ptrCurrentVkToWcharTable = (BYTE *) pVkToWcharTable;
|
||||
|
||||
int cbSize, n;
|
||||
do {
|
||||
// cbSize is used to calculate n, and n is used for the size of pVkToWchars[j].wch[n]
|
||||
cbSize = *(ptrCurrentVkToWcharTable + offsetof(VK_TO_WCHAR_TABLE, cbSize) + ptr_padding);
|
||||
n = (cbSize - 2) / 2;
|
||||
|
||||
// Same as VK_TO_WCHARS pVkToWchars[] = pVkToWcharTable[i].pVkToWchars
|
||||
PVK_TO_WCHARS pVkToWchars = (PVK_TO_WCHARS) ((PVK_TO_WCHAR_TABLE) ptrCurrentVkToWcharTable)->pVkToWchars;
|
||||
|
||||
if (pVkToWchars != NULL && mod < n) {
|
||||
// pVkToWchars[j].VirtualKey
|
||||
BYTE *pCurrentVkToWchars = (BYTE *) pVkToWchars;
|
||||
|
||||
do {
|
||||
if (((PVK_TO_WCHARS) pCurrentVkToWchars)->VirtualKey == keycode) {
|
||||
if ((((PVK_TO_WCHARS) pCurrentVkToWchars)->Attributes == CAPLOK) && capsLock) {
|
||||
if (is_shift && mod > 0) {
|
||||
mod -= 1;
|
||||
}
|
||||
else {
|
||||
mod += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the initial unicode char.
|
||||
WCHAR unicode = ((PVK_TO_WCHARS) pCurrentVkToWchars)->wch[mod];
|
||||
|
||||
// Increment the pCurrentVkToWchars by the size of wch[n].
|
||||
pCurrentVkToWchars += sizeof(VK_TO_WCHARS) + (sizeof(WCHAR) * n);
|
||||
|
||||
|
||||
if (unicode == WCH_DEAD) {
|
||||
// The current unicode char is a dead key...
|
||||
if (deadChar == WCH_NONE) {
|
||||
// No previous dead key was set so cache the next
|
||||
// wchar so we know what to do next time its pressed.
|
||||
deadChar = ((PVK_TO_WCHARS) pCurrentVkToWchars)->wch[mod];
|
||||
}
|
||||
else {
|
||||
if (size >= 2) {
|
||||
// Received a second dead key.
|
||||
memset(buffer, deadChar, 2);
|
||||
//buffer[0] = deadChar;
|
||||
//buffer[1] = deadChar;
|
||||
|
||||
deadChar = WCH_NONE;
|
||||
charCount = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (unicode != WCH_NONE) {
|
||||
// We are not WCH_NONE or WCH_DEAD
|
||||
if (size >= 1) {
|
||||
buffer[0] = unicode;
|
||||
charCount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// Add sizeof WCHAR because we are really an array of WCHAR[n] not WCHAR[]
|
||||
pCurrentVkToWchars += sizeof(VK_TO_WCHARS) + (sizeof(WCHAR) * n);
|
||||
}
|
||||
} while ( ((PVK_TO_WCHARS) pCurrentVkToWchars)->VirtualKey != 0 );
|
||||
}
|
||||
|
||||
// This is effectively the same as: ptrCurrentVkToWcharTable = pVkToWcharTable[++i];
|
||||
ptrCurrentVkToWcharTable += sizeVkToWcharTable;
|
||||
} while (cbSize != 0);
|
||||
|
||||
|
||||
// If the current local has a dead key set.
|
||||
if (deadChar != WCH_NONE) {
|
||||
// Loop over the pDeadKey lookup table for the locale.
|
||||
int i;
|
||||
for (i = 0; pDeadKey[i].dwBoth != 0; i++) {
|
||||
WCHAR baseChar = (WCHAR) pDeadKey[i].dwBoth;
|
||||
WCHAR diacritic = (WCHAR) (pDeadKey[i].dwBoth >> 16);
|
||||
|
||||
// If we locate an extended dead char, set it.
|
||||
if (size >= 1 && baseChar == buffer[0] && diacritic == deadChar) {
|
||||
deadChar = WCH_NONE;
|
||||
|
||||
if (charCount <= size) {
|
||||
memset(buffer, (WCHAR) pDeadKey[i].wchComposed, charCount);
|
||||
//buffer[i] = (WCHAR) pDeadKey[i].wchComposed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return charCount;
|
||||
}
|
||||
|
||||
int load_input_helper() {
|
||||
int count = 0;
|
||||
|
||||
#if defined(_WIN32) && !defined(_WIN64)
|
||||
if (is_wow64()) {
|
||||
ptr_padding = sizeof(void *);
|
||||
}
|
||||
#endif
|
||||
|
||||
count = refresh_locale_list();
|
||||
|
||||
logger(LOG_LEVEL_INFO,
|
||||
"%s [%u]: refresh_locale_list() found %i locale(s).\n",
|
||||
__FUNCTION__, __LINE__, count);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// This returns the number of locales that were removed.
|
||||
int unload_input_helper() {
|
||||
int count = 0;
|
||||
|
||||
// Cleanup and free memory from the old list.
|
||||
KeyboardLocale* locale_item = locale_first;
|
||||
while (locale_item != NULL) {
|
||||
// Remove the first item from the linked list.
|
||||
FreeLibrary(locale_item->library);
|
||||
locale_first = locale_item->next;
|
||||
free(locale_item);
|
||||
locale_item = locale_first;
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
// Reset the current local.
|
||||
locale_current = NULL;
|
||||
|
||||
return count;
|
||||
}
|
@ -1,211 +0,0 @@
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include "../iohook.h"
|
||||
#include "input.h"
|
||||
// #include "logger.h"
|
||||
|
||||
// The handle to the DLL module pulled in DllMain on DLL_PROCESS_ATTACH.
|
||||
HINSTANCE hInst;
|
||||
|
||||
// input_hook.c
|
||||
extern void unregister_running_hooks();
|
||||
|
||||
|
||||
// Structure for the monitor_enum_proc() callback so we can track the count.
|
||||
typedef struct _screen_info {
|
||||
uint8_t count;
|
||||
screen_data *data;
|
||||
} screen_info;
|
||||
|
||||
|
||||
static BOOL CALLBACK monitor_enum_proc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) {
|
||||
int width = lprcMonitor->right - lprcMonitor->left;
|
||||
int height = lprcMonitor->bottom - lprcMonitor->top;
|
||||
int origin_x = lprcMonitor->left;
|
||||
int origin_y = lprcMonitor->top;
|
||||
|
||||
if (width > 0 && height > 0) {
|
||||
screen_info *screens = (screen_info *) dwData;
|
||||
|
||||
if (screens->data == NULL) {
|
||||
screens->data = (screen_data *) malloc(sizeof(screen_data));
|
||||
}
|
||||
else {
|
||||
screens->data = (screen_data *) realloc(screens, sizeof(screen_data) * screens->count);
|
||||
}
|
||||
|
||||
screens->data[screens->count++] = (screen_data) {
|
||||
// Should monitor count start @ zero? Currently it starts at 1.
|
||||
.number = screens->count,
|
||||
.x = origin_x,
|
||||
.y = origin_y,
|
||||
.width = width,
|
||||
.height = height
|
||||
};
|
||||
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: Monitor %d: %ldx%ld (%ld, %ld)\n",
|
||||
__FUNCTION__, __LINE__, screens->count, width, height, origin_x, origin_y);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
IOHOOK_API screen_data* hook_create_screen_info(unsigned char *count) {
|
||||
// Initialize count to zero.
|
||||
*count = 0;
|
||||
|
||||
// Create a simple structure to make working with monitor_enum_proc easier.
|
||||
screen_info screens = {
|
||||
.count = 0,
|
||||
.data = NULL
|
||||
};
|
||||
|
||||
BOOL status = EnumDisplayMonitors(NULL, NULL, monitor_enum_proc, (LPARAM) &screens);
|
||||
|
||||
if (!status || screens.count == 0) {
|
||||
// Fallback in case EnumDisplayMonitors fails.
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: EnumDisplayMonitors failed. Fallback.\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
|
||||
int width = GetSystemMetrics(SM_CXSCREEN);
|
||||
int height = GetSystemMetrics(SM_CYSCREEN);
|
||||
|
||||
if (width > 0 && height > 0) {
|
||||
screens.data = (screen_data *) malloc(sizeof(screen_data));
|
||||
|
||||
if (screens.data != NULL) {
|
||||
*count = 1;
|
||||
screens.data[0] = (screen_data) {
|
||||
.number = 1,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = width,
|
||||
.height = height
|
||||
};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Populate the count.
|
||||
*count = screens.count;
|
||||
}
|
||||
|
||||
return screens.data;
|
||||
}
|
||||
|
||||
IOHOOK_API long int hook_get_auto_repeat_rate() {
|
||||
long int value = -1;
|
||||
long int rate;
|
||||
|
||||
if (SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &rate, 0)) {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETKEYBOARDSPEED: %li.\n",
|
||||
__FUNCTION__, __LINE__, rate);
|
||||
|
||||
value = rate;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
IOHOOK_API long int hook_get_auto_repeat_delay() {
|
||||
long int value = -1;
|
||||
long int delay;
|
||||
|
||||
if (SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &delay, 0)) {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETKEYBOARDDELAY: %li.\n",
|
||||
__FUNCTION__, __LINE__, delay);
|
||||
|
||||
value = delay;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
IOHOOK_API long int hook_get_pointer_acceleration_multiplier() {
|
||||
long int value = -1;
|
||||
int mouse[3]; // 0-Threshold X, 1-Threshold Y and 2-Speed.
|
||||
|
||||
if (SystemParametersInfo(SPI_GETMOUSE, 0, &mouse, 0)) {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETMOUSE[2]: %i.\n",
|
||||
__FUNCTION__, __LINE__, mouse[2]);
|
||||
|
||||
value = mouse[2];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
IOHOOK_API long int hook_get_pointer_acceleration_threshold() {
|
||||
long int value = -1;
|
||||
int mouse[3]; // 0-Threshold X, 1-Threshold Y and 2-Speed.
|
||||
|
||||
if (SystemParametersInfo(SPI_GETMOUSE, 0, &mouse, 0)) {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETMOUSE[0]: %i.\n",
|
||||
__FUNCTION__, __LINE__, mouse[0]);
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETMOUSE[1]: %i.\n",
|
||||
__FUNCTION__, __LINE__, mouse[1]);
|
||||
|
||||
// Average the x and y thresholds.
|
||||
value = (mouse[0] + mouse[1]) / 2;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
IOHOOK_API long int hook_get_pointer_sensitivity() {
|
||||
long int value = -1;
|
||||
int sensitivity;
|
||||
|
||||
if (SystemParametersInfo(SPI_GETMOUSESPEED, 0, &sensitivity, 0)) {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: SPI_GETMOUSESPEED: %i.\n",
|
||||
__FUNCTION__, __LINE__, sensitivity);
|
||||
|
||||
value = sensitivity;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
IOHOOK_API long int hook_get_multi_click_time() {
|
||||
long int value = -1;
|
||||
UINT clicktime;
|
||||
|
||||
clicktime = GetDoubleClickTime();
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: GetDoubleClickTime: %u.\n",
|
||||
__FUNCTION__, __LINE__, (unsigned int) clicktime);
|
||||
|
||||
value = (long int) clicktime;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// DLL Entry point.
|
||||
BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpReserved) {
|
||||
switch (fdwReason) {
|
||||
case DLL_PROCESS_ATTACH:
|
||||
// Save the DLL address.
|
||||
hInst = hInstDLL;
|
||||
|
||||
// Initialize native input helper functions.
|
||||
load_input_helper();
|
||||
break;
|
||||
|
||||
case DLL_PROCESS_DETACH:
|
||||
// Unregister any hooks that may still be installed.
|
||||
unregister_running_hooks();
|
||||
|
||||
// Deinitialize native input helper functions.
|
||||
unload_input_helper();
|
||||
break;
|
||||
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
// Do Nothing.
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
@ -1,386 +0,0 @@
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#ifdef USE_XTEST
|
||||
#include <X11/extensions/XTest.h>
|
||||
#endif
|
||||
|
||||
#include "../iohook.h"
|
||||
#include "input.h"
|
||||
// #include "../logger.h"
|
||||
|
||||
extern Display *properties_disp;
|
||||
|
||||
// This lookup table must be in the same order the masks are defined.
|
||||
#ifdef USE_XTEST
|
||||
static KeySym keymask_lookup[8] = {
|
||||
XK_Shift_L,
|
||||
XK_Control_L,
|
||||
XK_Meta_L,
|
||||
XK_Alt_L,
|
||||
|
||||
XK_Shift_R,
|
||||
XK_Control_R,
|
||||
XK_Meta_R,
|
||||
XK_Alt_R
|
||||
};
|
||||
|
||||
static unsigned int btnmask_lookup[5] = {
|
||||
MASK_BUTTON1,
|
||||
MASK_BUTTON2,
|
||||
MASK_BUTTON3,
|
||||
MASK_BUTTON4,
|
||||
MASK_BUTTON5
|
||||
};
|
||||
#else
|
||||
// TODO Possibly relocate to input helper.
|
||||
static unsigned int convert_to_native_mask(unsigned int mask) {
|
||||
unsigned int native_mask = 0x00;
|
||||
|
||||
if (mask & (MASK_SHIFT)) { native_mask |= ShiftMask; }
|
||||
if (mask & (MASK_CTRL)) { native_mask |= ControlMask; }
|
||||
if (mask & (MASK_META)) { native_mask |= Mod4Mask; }
|
||||
if (mask & (MASK_ALT)) { native_mask |= Mod1Mask; }
|
||||
|
||||
if (mask & MASK_BUTTON1) { native_mask |= Button1Mask; }
|
||||
if (mask & MASK_BUTTON2) { native_mask |= Button2Mask; }
|
||||
if (mask & MASK_BUTTON3) { native_mask |= Button3Mask; }
|
||||
if (mask & MASK_BUTTON4) { native_mask |= Button4Mask; }
|
||||
if (mask & MASK_BUTTON5) { native_mask |= Button5Mask; }
|
||||
|
||||
return native_mask;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void post_key_event(iohook_event * const event) {
|
||||
#ifdef USE_XTEST
|
||||
// FIXME Currently ignoring EVENT_KEY_TYPED.
|
||||
if (event->type == EVENT_KEY_PRESSED) {
|
||||
XTestFakeKeyEvent(
|
||||
properties_disp,
|
||||
scancode_to_keycode(event->data.keyboard.keycode),
|
||||
True,
|
||||
0);
|
||||
}
|
||||
else if (event->type == EVENT_KEY_RELEASED) {
|
||||
XTestFakeKeyEvent(
|
||||
properties_disp,
|
||||
scancode_to_keycode(event->data.keyboard.keycode),
|
||||
False,
|
||||
0);
|
||||
}
|
||||
#else
|
||||
XKeyEvent key_event;
|
||||
|
||||
key_event.serial = 0x00;
|
||||
key_event.send_event = False;
|
||||
key_event.display = properties_disp;
|
||||
key_event.time = CurrentTime;
|
||||
key_event.same_screen = True;
|
||||
|
||||
unsigned int mask;
|
||||
if (!XQueryPointer(properties_disp, DefaultRootWindow(properties_disp), &(key_event.root), &(key_event.subwindow), &(key_event.x_root), &(key_event.y_root), &(key_event.x), &(key_event.y), &mask)) {
|
||||
key_event.root = DefaultRootWindow(properties_disp);
|
||||
key_event.window = key_event.root;
|
||||
key_event.subwindow = None;
|
||||
|
||||
key_event.x_root = 0;
|
||||
key_event.y_root = 0;
|
||||
key_event.x = 0;
|
||||
key_event.y = 0;
|
||||
}
|
||||
|
||||
key_event.state = convert_to_native_mask(event->mask);
|
||||
key_event.keycode = XKeysymToKeycode(properties_disp, scancode_to_keycode(event->data.keyboard.keycode));
|
||||
|
||||
// FIXME Currently ignoring typed events.
|
||||
if (event->type == EVENT_KEY_PRESSED) {
|
||||
key_event.type = KeyPress;
|
||||
XSendEvent(properties_disp, InputFocus, False, KeyPressMask, (XEvent *) &key_event);
|
||||
}
|
||||
else if (event->type == EVENT_KEY_RELEASED) {
|
||||
key_event.type = KeyRelease;
|
||||
XSendEvent(properties_disp, InputFocus, False, KeyReleaseMask, (XEvent *) &key_event);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void post_mouse_button_event(iohook_event * const event) {
|
||||
#ifdef USE_XTEST
|
||||
Window ret_root;
|
||||
Window ret_child;
|
||||
int root_x;
|
||||
int root_y;
|
||||
int win_x;
|
||||
int win_y;
|
||||
unsigned int mask;
|
||||
|
||||
Window win_root = XDefaultRootWindow(properties_disp);
|
||||
Bool query_status = XQueryPointer(properties_disp, win_root, &ret_root, &ret_child, &root_x, &root_y, &win_x, &win_y, &mask);
|
||||
if (query_status) {
|
||||
if (event->data.mouse.x != root_x || event->data.mouse.y != root_y) {
|
||||
// Move the pointer to the specified position.
|
||||
XTestFakeMotionEvent(properties_disp, -1, event->data.mouse.x, event->data.mouse.y, 0);
|
||||
}
|
||||
else {
|
||||
query_status = False;
|
||||
}
|
||||
}
|
||||
|
||||
if (event->type == EVENT_MOUSE_WHEEL) {
|
||||
// Wheel events should be the same as click events on X11.
|
||||
// type, amount and rotation
|
||||
if (event->data.wheel.rotation < 0) {
|
||||
XTestFakeButtonEvent(properties_disp, WheelUp, True, 0);
|
||||
XTestFakeButtonEvent(properties_disp, WheelUp, False, 0);
|
||||
}
|
||||
else {
|
||||
XTestFakeButtonEvent(properties_disp, WheelDown, True, 0);
|
||||
XTestFakeButtonEvent(properties_disp, WheelDown, False, 0);
|
||||
}
|
||||
}
|
||||
else if (event->type == EVENT_MOUSE_PRESSED) {
|
||||
XTestFakeButtonEvent(properties_disp, event->data.mouse.button, True, 0);
|
||||
}
|
||||
else if (event->type == EVENT_MOUSE_RELEASED) {
|
||||
XTestFakeButtonEvent(properties_disp, event->data.mouse.button, False, 0);
|
||||
}
|
||||
else if (event->type == EVENT_MOUSE_CLICKED) {
|
||||
XTestFakeButtonEvent(properties_disp, event->data.mouse.button, True, 0);
|
||||
XTestFakeButtonEvent(properties_disp, event->data.mouse.button, False, 0);
|
||||
}
|
||||
|
||||
if (query_status) {
|
||||
// Move the pointer back to the original position.
|
||||
XTestFakeMotionEvent(properties_disp, -1, root_x, root_y, 0);
|
||||
}
|
||||
#else
|
||||
XButtonEvent btn_event;
|
||||
|
||||
btn_event.serial = 0x00;
|
||||
btn_event.send_event = False;
|
||||
btn_event.display = properties_disp;
|
||||
btn_event.time = CurrentTime;
|
||||
btn_event.same_screen = True;
|
||||
|
||||
btn_event.root = DefaultRootWindow(properties_disp);
|
||||
btn_event.window = btn_event.root;
|
||||
btn_event.subwindow = None;
|
||||
|
||||
btn_event.type = 0x00;
|
||||
btn_event.state = 0x00;
|
||||
btn_event.x_root = 0;
|
||||
btn_event.y_root = 0;
|
||||
btn_event.x = 0;
|
||||
btn_event.y = 0;
|
||||
btn_event.button = 0x00;
|
||||
|
||||
btn_event.state = convert_to_native_mask(event->mask);
|
||||
|
||||
btn_event.x = event->data.mouse.x;
|
||||
btn_event.y = event->data.mouse.y;
|
||||
|
||||
#if defined(USE_XINERAMA) || defined(USE_XRANDR)
|
||||
uint8_t screen_count;
|
||||
screen_data *screens = hook_create_screen_info(&screen_count);
|
||||
if (screen_count > 1) {
|
||||
btn_event.x += screens[0].x;
|
||||
btn_event.y += screens[0].y;
|
||||
}
|
||||
|
||||
if (screens != NULL) {
|
||||
free(screens);
|
||||
}
|
||||
#endif
|
||||
|
||||
// These are the same because Window == Root Window.
|
||||
btn_event.x_root = btn_event.x;
|
||||
btn_event.y_root = btn_event.y;
|
||||
|
||||
if (event->type == EVENT_MOUSE_WHEEL) {
|
||||
// type, amount and rotation
|
||||
if (event->data.wheel.rotation < 0) {
|
||||
btn_event.button = WheelUp;
|
||||
}
|
||||
else {
|
||||
btn_event.button = WheelDown;
|
||||
}
|
||||
}
|
||||
|
||||
if (event->type != EVENT_MOUSE_RELEASED) {
|
||||
// FIXME Where do we set event->button?
|
||||
btn_event.type = ButtonPress;
|
||||
XSendEvent(properties_disp, InputFocus, False, ButtonPressMask, (XEvent *) &btn_event);
|
||||
}
|
||||
|
||||
if (event->type != EVENT_MOUSE_PRESSED) {
|
||||
btn_event.type = ButtonRelease;
|
||||
XSendEvent(properties_disp, InputFocus, False, ButtonReleaseMask, (XEvent *) &btn_event);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void post_mouse_motion_event(iohook_event * const event) {
|
||||
#ifdef USE_XTEST
|
||||
XTestFakeMotionEvent(properties_disp, -1, event->data.mouse.x, event->data.mouse.y, 0);
|
||||
#else
|
||||
XMotionEvent mov_event;
|
||||
|
||||
mov_event.serial = MotionNotify;
|
||||
mov_event.send_event = False;
|
||||
mov_event.display = properties_disp;
|
||||
mov_event.time = CurrentTime;
|
||||
mov_event.same_screen = True;
|
||||
mov_event.is_hint = NotifyNormal,
|
||||
mov_event.root = DefaultRootWindow(properties_disp);
|
||||
mov_event.window = mov_event.root;
|
||||
mov_event.subwindow = None;
|
||||
|
||||
mov_event.type = 0x00;
|
||||
mov_event.state = 0x00;
|
||||
mov_event.x_root = 0;
|
||||
mov_event.y_root = 0;
|
||||
mov_event.x = 0;
|
||||
mov_event.y = 0;
|
||||
|
||||
mov_event.state = convert_to_native_mask(event->mask);
|
||||
|
||||
mov_event.x = event->data.mouse.x;
|
||||
mov_event.y = event->data.mouse.y;
|
||||
|
||||
#if defined(USE_XINERAMA) || defined(USE_XRANDR)
|
||||
uint8_t screen_count;
|
||||
screen_data *screens = hook_create_screen_info(&screen_count);
|
||||
if (screen_count > 1) {
|
||||
mov_event.x += screens[0].x;
|
||||
mov_event.y += screens[0].y;
|
||||
}
|
||||
|
||||
if (screens != NULL) {
|
||||
free(screens);
|
||||
}
|
||||
#endif
|
||||
|
||||
// These are the same because Window == Root Window.
|
||||
mov_event.x_root = mov_event.x;
|
||||
mov_event.y_root = mov_event.y;
|
||||
|
||||
long int event_mask = NoEventMask;
|
||||
if (event->type == EVENT_MOUSE_DRAGGED) {
|
||||
#if Button1Mask == Button1MotionMask && \
|
||||
Button2Mask == Button2MotionMask && \
|
||||
Button3Mask == Button3MotionMask && \
|
||||
Button4Mask == Button4MotionMask && \
|
||||
Button5Mask == Button5MotionMask
|
||||
// This little trick only works if Button#MotionMasks align with
|
||||
// the Button#Masks.
|
||||
event_mask = mov_event.state &
|
||||
(Button1MotionMask | Button2MotionMask |
|
||||
Button2MotionMask | Button3MotionMask | Button5MotionMask);
|
||||
#else
|
||||
// Fallback to some slightly larger...
|
||||
if (event->state & Button1Mask) {
|
||||
event_mask |= Button1MotionMask;
|
||||
}
|
||||
|
||||
if (event->state & Button2Mask) {
|
||||
event_mask |= Button2MotionMask;
|
||||
}
|
||||
|
||||
if (event->state & Button3Mask) {
|
||||
event_mask |= Button3MotionMask;
|
||||
}
|
||||
|
||||
if (event->state & Button4Mask) {
|
||||
event_mask |= Button4MotionMask;
|
||||
}
|
||||
|
||||
if (event->state & Button5Mask) {
|
||||
event_mask |= Button5MotionMask;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// NOTE x_mask = NoEventMask.
|
||||
XSendEvent(properties_disp, InputFocus, False, event_mask, (XEvent *) &mov_event);
|
||||
#endif
|
||||
}
|
||||
|
||||
IOHOOK_API void hook_post_event(iohook_event * const event) {
|
||||
XLockDisplay(properties_disp);
|
||||
|
||||
#ifdef USE_XTEST
|
||||
// XTest does not have modifier support, so we fake it by depressing the
|
||||
// appropriate modifier keys.
|
||||
unsigned int i;
|
||||
for (i = 0; i < sizeof(keymask_lookup) / sizeof(KeySym); i++) {
|
||||
if (event->mask & 1 << i) {
|
||||
XTestFakeKeyEvent(properties_disp, XKeysymToKeycode(properties_disp, keymask_lookup[i]), True, 0);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int i;
|
||||
for (i = 0; i < sizeof(btnmask_lookup) / sizeof(unsigned int); i++) {
|
||||
if (event->mask & btnmask_lookup[i]) {
|
||||
XTestFakeButtonEvent(properties_disp, i + 1, True, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (event->type) {
|
||||
case EVENT_KEY_PRESSED:
|
||||
case EVENT_KEY_RELEASED:
|
||||
case EVENT_KEY_TYPED:
|
||||
post_key_event(event);
|
||||
break;
|
||||
|
||||
case EVENT_MOUSE_PRESSED:
|
||||
case EVENT_MOUSE_RELEASED:
|
||||
case EVENT_MOUSE_WHEEL:
|
||||
case EVENT_MOUSE_CLICKED:
|
||||
post_mouse_button_event(event);
|
||||
break;
|
||||
|
||||
case EVENT_MOUSE_DRAGGED:
|
||||
case EVENT_MOUSE_MOVED:
|
||||
post_mouse_motion_event(event);
|
||||
break;
|
||||
|
||||
case EVENT_HOOK_ENABLED:
|
||||
case EVENT_HOOK_DISABLED:
|
||||
// Ignore hook enabled / disabled events.
|
||||
|
||||
default:
|
||||
// Ignore any other garbage.
|
||||
logger(LOG_LEVEL_WARN, "%s [%u]: Ignoring post event type %#X\n",
|
||||
__FUNCTION__, __LINE__, event->type);
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef USE_XTEST
|
||||
// Release the previously held modifier keys used to fake the event mask.
|
||||
unsigned int i ;
|
||||
for (i= 0; i < sizeof(keymask_lookup) / sizeof(KeySym); i++) {
|
||||
if (event->mask & 1 << i) {
|
||||
XTestFakeKeyEvent(properties_disp, XKeysymToKeycode(properties_disp, keymask_lookup[i]), False, 0);
|
||||
}
|
||||
}
|
||||
unsigned int i;
|
||||
for (i = 0; i < sizeof(btnmask_lookup) / sizeof(unsigned int); i++) {
|
||||
if (event->mask & btnmask_lookup[i]) {
|
||||
XTestFakeButtonEvent(properties_disp, i + 1, False, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Don't forget to flush!
|
||||
XSync(properties_disp, True);
|
||||
XUnlockDisplay(properties_disp);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,86 +0,0 @@
|
||||
|
||||
#define USE_XKBCOMMON 0
|
||||
//#define _included_input_helper 0
|
||||
#ifndef _included_input_helper
|
||||
#define _included_input_helper
|
||||
|
||||
#include <stdint.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#ifdef USE_XKBCOMMON
|
||||
#include <X11/Xlib-xcb.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <xkbcommon/xkbcommon-x11.h>
|
||||
#endif
|
||||
|
||||
|
||||
// Virtual button codes that are not defined by X11.
|
||||
#define Button1 1
|
||||
#define Button2 2
|
||||
#define Button3 3
|
||||
#define WheelUp 4
|
||||
#define WheelDown 5
|
||||
#define WheelLeft 6
|
||||
#define WheelRight 7
|
||||
#define XButton1 8
|
||||
#define XButton2 9
|
||||
|
||||
/* Converts an X11 key symbol to a single Unicode character. No direct X11
|
||||
* functionality exists to provide this information.
|
||||
*/
|
||||
extern size_t keysym_to_unicode(KeySym keysym, uint16_t *buffer, size_t size);
|
||||
|
||||
/* Convert a single Unicode character to an X11 key symbol. This function
|
||||
* provides a better translation than XStringToKeysym() for Unicode characters.
|
||||
*/
|
||||
extern KeySym unicode_to_keysym(uint16_t unicode);
|
||||
|
||||
/* Converts an X11 key code to the appropriate keyboard scan code.
|
||||
*/
|
||||
extern uint16_t keycode_to_scancode(KeyCode keycode);
|
||||
|
||||
/* Converts a keyboard scan code to the appropriate X11 key code.
|
||||
*/
|
||||
extern KeyCode scancode_to_keycode(uint16_t scancode);
|
||||
|
||||
|
||||
#ifdef USE_XKBCOMMON
|
||||
|
||||
/* Converts an X11 key code to a Unicode character sequence. libXKBCommon support
|
||||
* is required for this method.
|
||||
*/
|
||||
extern size_t keycode_to_unicode(struct xkb_state* state, KeyCode keycode, uint16_t *buffer, size_t size);
|
||||
|
||||
/* Create a xkb_state structure and return a pointer to it.
|
||||
*/
|
||||
extern struct xkb_state * create_xkb_state(struct xkb_context *context, xcb_connection_t *connection);
|
||||
|
||||
/* Release xkb_state structure created by create_xkb_state().
|
||||
*/
|
||||
extern void destroy_xkb_state(struct xkb_state* state);
|
||||
|
||||
#else
|
||||
|
||||
/* Converts an X11 key code and event mask to the appropriate X11 key symbol.
|
||||
* This functions in much the same way as XKeycodeToKeysym() but allows for a
|
||||
* faster and more flexible lookup.
|
||||
*/
|
||||
extern KeySym keycode_to_keysym(KeyCode keycode, unsigned int modifier_mask);
|
||||
|
||||
#endif
|
||||
|
||||
/* Initialize items required for KeyCodeToKeySym() and KeySymToUnicode()
|
||||
* functionality. This method is called by OnLibraryLoad() and may need to be
|
||||
* called in combination with UnloadInputHelper() if the native keyboard layout
|
||||
* is changed.
|
||||
*/
|
||||
extern void load_input_helper();
|
||||
|
||||
/* De-initialize items required for KeyCodeToKeySym() and KeySymToUnicode()
|
||||
* functionality. This method is called by OnLibraryUnload() and may need to be
|
||||
* called in combination with LoadInputHelper() if the native keyboard layout
|
||||
* is changed.
|
||||
*/
|
||||
extern void unload_input_helper();
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
@ -1,499 +0,0 @@
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <X11/Xlib.h>
|
||||
#ifdef USE_XKB
|
||||
#include <X11/XKBlib.h>
|
||||
#endif
|
||||
#ifdef USE_XF86MISC
|
||||
#include <X11/extensions/xf86misc.h>
|
||||
#include <X11/extensions/xf86mscstr.h>
|
||||
#endif
|
||||
#if defined(USE_XINERAMA) && !defined(USE_XRANDR)
|
||||
#include <X11/extensions/Xinerama.h>
|
||||
#elif defined(USE_XRANDR)
|
||||
#include <pthread.h>
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
#endif
|
||||
#ifdef USE_XT
|
||||
#include <X11/Intrinsic.h>
|
||||
|
||||
static XtAppContext xt_context;
|
||||
static Display *xt_disp;
|
||||
#endif
|
||||
|
||||
#include "../iohook.h"
|
||||
#include "input.h"
|
||||
// #include "../logger.h"
|
||||
|
||||
Display *properties_disp;
|
||||
|
||||
#ifdef USE_XRANDR
|
||||
static pthread_mutex_t xrandr_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static XRRScreenResources *xrandr_resources = NULL;
|
||||
|
||||
static void settings_cleanup_proc(void *arg) {
|
||||
if (pthread_mutex_trylock(&xrandr_mutex) == 0) {
|
||||
if (xrandr_resources != NULL) {
|
||||
XRRFreeScreenResources(xrandr_resources);
|
||||
xrandr_resources = NULL;
|
||||
}
|
||||
|
||||
if (arg != NULL) {
|
||||
XCloseDisplay((Display *) arg);
|
||||
arg = NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&xrandr_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void *settings_thread_proc(void *arg) {
|
||||
Display *settings_disp = XOpenDisplay(XDisplayName(NULL));;
|
||||
if (settings_disp != NULL) {
|
||||
logger(LOG_LEVEL_DEBUG, "%s [%u]: %s\n",
|
||||
__FUNCTION__, __LINE__, "XOpenDisplay success.");
|
||||
|
||||
pthread_cleanup_push(settings_cleanup_proc, settings_disp);
|
||||
|
||||
int event_base = 0;
|
||||
int error_base = 0;
|
||||
if (XRRQueryExtension(settings_disp, &event_base, &error_base)) {
|
||||
Window root = XDefaultRootWindow(settings_disp);
|
||||
unsigned long event_mask = RRScreenChangeNotifyMask;
|
||||
XRRSelectInput(settings_disp, root, event_mask);
|
||||
|
||||
XEvent ev;
|
||||
|
||||
while(settings_disp != NULL) {
|
||||
XNextEvent(settings_disp, &ev);
|
||||
|
||||
if (ev.type == event_base + RRScreenChangeNotifyMask) {
|
||||
logger(LOG_LEVEL_DEBUG, "%s [%u]: Received XRRScreenChangeNotifyEvent.\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
|
||||
pthread_mutex_lock(&xrandr_mutex);
|
||||
if (xrandr_resources != NULL) {
|
||||
XRRFreeScreenResources(xrandr_resources);
|
||||
}
|
||||
|
||||
xrandr_resources = XRRGetScreenResources(settings_disp, root);
|
||||
if (xrandr_resources == NULL) {
|
||||
logger(LOG_LEVEL_WARN, "%s [%u]: XRandR could not get screen resources!\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
}
|
||||
pthread_mutex_unlock(&xrandr_mutex);
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_WARN, "%s [%u]: XRandR is not currently available!\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the thread cleanup handler.
|
||||
pthread_cleanup_pop(1);
|
||||
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_ERROR, "%s [%u]: XOpenDisplay failure!\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
IOHOOK_API screen_data* hook_create_screen_info(unsigned char *count) {
|
||||
*count = 0;
|
||||
screen_data *screens = NULL;
|
||||
|
||||
#if defined(USE_XINERAMA) && !defined(USE_XRANDR)
|
||||
if (XineramaIsActive(properties_disp)) {
|
||||
int xine_count = 0;
|
||||
XineramaScreenInfo *xine_info = XineramaQueryScreens(properties_disp, &xine_count);
|
||||
|
||||
if (xine_info != NULL) {
|
||||
if (xine_count > UINT8_MAX) {
|
||||
*count = UINT8_MAX;
|
||||
|
||||
logger(LOG_LEVEL_WARN, "%s [%u]: Screen count overflow detected!\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
}
|
||||
else {
|
||||
*count = (uint8_t) xine_count;
|
||||
}
|
||||
|
||||
screens = malloc(sizeof(screen_data) * xine_count);
|
||||
|
||||
if (screens != NULL) {
|
||||
int i;
|
||||
for (i = 0; i < xine_count; i++) {
|
||||
screens[i] = (screen_data) {
|
||||
.number = xine_info[i].screen_number,
|
||||
.x = xine_info[i].x_org,
|
||||
.y = xine_info[i].y_org,
|
||||
.width = xine_info[i].width,
|
||||
.height = xine_info[i].height
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
XFree(xine_info);
|
||||
}
|
||||
}
|
||||
#elif defined(USE_XRANDR)
|
||||
pthread_mutex_lock(&xrandr_mutex);
|
||||
if (xrandr_resources != NULL) {
|
||||
int xrandr_count = xrandr_resources->ncrtc;
|
||||
if (xrandr_count > UINT8_MAX) {
|
||||
*count = UINT8_MAX;
|
||||
|
||||
logger(LOG_LEVEL_WARN, "%s [%u]: Screen count overflow detected!\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
}
|
||||
else {
|
||||
*count = (uint8_t) xrandr_count;
|
||||
}
|
||||
|
||||
screens = malloc(sizeof(screen_data) * xrandr_count);
|
||||
|
||||
if (screens != NULL) {
|
||||
int i;
|
||||
for (i = 0; i < xrandr_count; i++) {
|
||||
XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(properties_disp, xrandr_resources, xrandr_resources->crtcs[i]);
|
||||
|
||||
if (crtc_info != NULL) {
|
||||
screens[i] = (screen_data) {
|
||||
.number = i + 1,
|
||||
.x = crtc_info->x,
|
||||
.y = crtc_info->y,
|
||||
.width = crtc_info->width,
|
||||
.height = crtc_info->height
|
||||
};
|
||||
|
||||
XRRFreeCrtcInfo(crtc_info);
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_WARN, "%s [%u]: XRandr failed to return crtc information! (%#X)\n",
|
||||
__FUNCTION__, __LINE__, xrandr_resources->crtcs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&xrandr_mutex);
|
||||
#else
|
||||
Screen* default_screen = DefaultScreenOfDisplay(properties_disp);
|
||||
|
||||
if (default_screen->width > 0 && default_screen->height > 0) {
|
||||
screens = malloc(sizeof(screen_data));
|
||||
|
||||
if (screens != NULL) {
|
||||
*count = 1;
|
||||
screens[0] = (screen_data) {
|
||||
.number = 1,
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.width = default_screen->width,
|
||||
.height = default_screen->height
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return screens;
|
||||
}
|
||||
|
||||
IOHOOK_API long int hook_get_auto_repeat_rate() {
|
||||
bool successful = false;
|
||||
long int value = -1;
|
||||
unsigned int delay = 0, rate = 0;
|
||||
|
||||
// Check and make sure we could connect to the x server.
|
||||
if (properties_disp != NULL) {
|
||||
#ifdef USE_XKB
|
||||
// Attempt to acquire the keyboard auto repeat rate using the XKB extension.
|
||||
if (!successful) {
|
||||
successful = XkbGetAutoRepeatRate(properties_disp, XkbUseCoreKbd, &delay, &rate);
|
||||
|
||||
if (successful) {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: XkbGetAutoRepeatRate: %u.\n",
|
||||
__FUNCTION__, __LINE__, rate);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_XF86MISC
|
||||
// Fallback to the XF86 Misc extension if available and other efforts failed.
|
||||
if (!successful) {
|
||||
XF86MiscKbdSettings kb_info;
|
||||
successful = (bool) XF86MiscGetKbdSettings(properties_disp, &kb_info);
|
||||
if (successful) {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: XF86MiscGetKbdSettings: %i.\n",
|
||||
__FUNCTION__, __LINE__, kbdinfo.rate);
|
||||
|
||||
delay = (unsigned int) kbdinfo.delay;
|
||||
rate = (unsigned int) kbdinfo.rate;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_ERROR, "%s [%u]: %s\n",
|
||||
__FUNCTION__, __LINE__, "XOpenDisplay failure!");
|
||||
}
|
||||
|
||||
if (successful) {
|
||||
value = (long int) rate;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
IOHOOK_API long int hook_get_auto_repeat_delay() {
|
||||
bool successful = false;
|
||||
long int value = -1;
|
||||
unsigned int delay = 0, rate = 0;
|
||||
|
||||
// Check and make sure we could connect to the x server.
|
||||
if (properties_disp != NULL) {
|
||||
#ifdef USE_XKB
|
||||
// Attempt to acquire the keyboard auto repeat rate using the XKB extension.
|
||||
if (!successful) {
|
||||
successful = XkbGetAutoRepeatRate(properties_disp, XkbUseCoreKbd, &delay, &rate);
|
||||
|
||||
if (successful) {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: XkbGetAutoRepeatRate: %u.\n",
|
||||
__FUNCTION__, __LINE__, delay);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_XF86MISC
|
||||
// Fallback to the XF86 Misc extension if available and other efforts failed.
|
||||
if (!successful) {
|
||||
XF86MiscKbdSettings kb_info;
|
||||
successful = (bool) XF86MiscGetKbdSettings(properties_disp, &kb_info);
|
||||
if (successful) {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: XF86MiscGetKbdSettings: %i.\n",
|
||||
__FUNCTION__, __LINE__, kbdinfo.delay);
|
||||
|
||||
delay = (unsigned int) kbdinfo.delay;
|
||||
rate = (unsigned int) kbdinfo.rate;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_ERROR, "%s [%u]: %s\n",
|
||||
__FUNCTION__, __LINE__, "XOpenDisplay failure!");
|
||||
}
|
||||
|
||||
if (successful) {
|
||||
value = (long int) delay;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
IOHOOK_API long int hook_get_pointer_acceleration_multiplier() {
|
||||
long int value = -1;
|
||||
int accel_numerator, accel_denominator, threshold;
|
||||
|
||||
// Check and make sure we could connect to the x server.
|
||||
if (properties_disp != NULL) {
|
||||
XGetPointerControl(properties_disp, &accel_numerator, &accel_denominator, &threshold);
|
||||
if (accel_denominator >= 0) {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: XGetPointerControl: %i.\n",
|
||||
__FUNCTION__, __LINE__, accel_denominator);
|
||||
|
||||
value = (long int) accel_denominator;
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_ERROR, "%s [%u]: %s\n",
|
||||
__FUNCTION__, __LINE__, "XOpenDisplay failure!");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
IOHOOK_API long int hook_get_pointer_acceleration_threshold() {
|
||||
long int value = -1;
|
||||
int accel_numerator, accel_denominator, threshold;
|
||||
|
||||
// Check and make sure we could connect to the x server.
|
||||
if (properties_disp != NULL) {
|
||||
XGetPointerControl(properties_disp, &accel_numerator, &accel_denominator, &threshold);
|
||||
if (threshold >= 0) {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: XGetPointerControl: %i.\n",
|
||||
__FUNCTION__, __LINE__, threshold);
|
||||
|
||||
value = (long int) threshold;
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_ERROR, "%s [%u]: %s\n",
|
||||
__FUNCTION__, __LINE__, "XOpenDisplay failure!");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
IOHOOK_API long int hook_get_pointer_sensitivity() {
|
||||
long int value = -1;
|
||||
int accel_numerator, accel_denominator, threshold;
|
||||
|
||||
// Check and make sure we could connect to the x server.
|
||||
if (properties_disp != NULL) {
|
||||
XGetPointerControl(properties_disp, &accel_numerator, &accel_denominator, &threshold);
|
||||
if (accel_numerator >= 0) {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: XGetPointerControl: %i.\n",
|
||||
__FUNCTION__, __LINE__, accel_numerator);
|
||||
|
||||
value = (long int) accel_numerator;
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_ERROR, "%s [%u]: %s\n",
|
||||
__FUNCTION__, __LINE__, "XOpenDisplay failure!");
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
IOHOOK_API long int hook_get_multi_click_time() {
|
||||
long int value = 200;
|
||||
int click_time;
|
||||
bool successful = false;
|
||||
|
||||
#ifdef USE_XT
|
||||
// Check and make sure we could connect to the x server.
|
||||
if (xt_disp != NULL) {
|
||||
// Try and use the Xt extention to get the current multi-click.
|
||||
if (!successful) {
|
||||
// Fall back to the X Toolkit extension if available and other efforts failed.
|
||||
click_time = XtGetMultiClickTime(xt_disp);
|
||||
if (click_time >= 0) {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: XtGetMultiClickTime: %i.\n",
|
||||
__FUNCTION__, __LINE__, click_time);
|
||||
|
||||
successful = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_ERROR, "%s [%u]: %s\n",
|
||||
__FUNCTION__, __LINE__, "XOpenDisplay failure!");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check and make sure we could connect to the x server.
|
||||
if (properties_disp != NULL) {
|
||||
// Try and acquire the multi-click time from the user defined X defaults.
|
||||
if (!successful) {
|
||||
char *xprop = XGetDefault(properties_disp, "*", "multiClickTime");
|
||||
if (xprop != NULL && sscanf(xprop, "%4i", &click_time) != EOF) {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: X default 'multiClickTime' property: %i.\n",
|
||||
__FUNCTION__, __LINE__, click_time);
|
||||
|
||||
successful = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!successful) {
|
||||
char *xprop = XGetDefault(properties_disp, "OpenWindows", "MultiClickTimeout");
|
||||
if (xprop != NULL && sscanf(xprop, "%4i", &click_time) != EOF) {
|
||||
logger(LOG_LEVEL_INFO, "%s [%u]: X default 'MultiClickTimeout' property: %i.\n",
|
||||
__FUNCTION__, __LINE__, click_time);
|
||||
|
||||
successful = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_ERROR, "%s [%u]: %s\n",
|
||||
__FUNCTION__, __LINE__, "XOpenDisplay failure!");
|
||||
}
|
||||
|
||||
if (successful) {
|
||||
value = (long int) click_time;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// Create a shared object constructor.
|
||||
__attribute__ ((constructor))
|
||||
void on_library_load() {
|
||||
// Make sure we are initialized for threading.
|
||||
XInitThreads();
|
||||
|
||||
// Open local display.
|
||||
properties_disp = XOpenDisplay(XDisplayName(NULL));
|
||||
if (properties_disp == NULL) {
|
||||
logger(LOG_LEVEL_ERROR, "%s [%u]: %s\n",
|
||||
__FUNCTION__, __LINE__, "XOpenDisplay failure!");
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_DEBUG, "%s [%u]: %s\n",
|
||||
__FUNCTION__, __LINE__, "XOpenDisplay success.");
|
||||
}
|
||||
|
||||
#ifdef USE_XRANDR
|
||||
// Create the thread attribute.
|
||||
pthread_attr_t settings_thread_attr;
|
||||
pthread_attr_init(&settings_thread_attr);
|
||||
|
||||
pthread_t settings_thread_id;
|
||||
if (pthread_create(&settings_thread_id, &settings_thread_attr, settings_thread_proc, NULL) == 0) {
|
||||
logger(LOG_LEVEL_DEBUG, "%s [%u]: Successfully created settings thread.\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
}
|
||||
else {
|
||||
logger(LOG_LEVEL_ERROR, "%s [%u]: Failed to create settings thread!\n",
|
||||
__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
// Make sure the thread attribute is removed.
|
||||
pthread_attr_destroy(&settings_thread_attr);
|
||||
#endif
|
||||
|
||||
#ifdef USE_XT
|
||||
XtToolkitInitialize();
|
||||
xt_context = XtCreateApplicationContext();
|
||||
|
||||
int argc = 0;
|
||||
char ** argv = { NULL };
|
||||
xt_disp = XtOpenDisplay(xt_context, NULL, "IOHook", "libIOhook", NULL, 0, &argc, argv);
|
||||
#endif
|
||||
|
||||
// Initialize.
|
||||
load_input_helper(properties_disp);
|
||||
}
|
||||
|
||||
// Create a shared object destructor.
|
||||
__attribute__ ((destructor))
|
||||
void on_library_unload() {
|
||||
// Disable the event hook.
|
||||
//hook_stop();
|
||||
|
||||
// Cleanup.
|
||||
unload_input_helper();
|
||||
|
||||
#ifdef USE_XT
|
||||
XtCloseDisplay(xt_disp);
|
||||
XtDestroyApplicationContext(xt_context);
|
||||
#endif
|
||||
|
||||
// Destroy the native displays.
|
||||
if (properties_disp != NULL) {
|
||||
XCloseDisplay(properties_disp);
|
||||
properties_disp = NULL;
|
||||
}
|
||||
}
|
@ -17,9 +17,10 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
robotgo.ScrollMouse(10, "up")
|
||||
// robotgo.ScrollMouse(10, "up")
|
||||
robotgo.Scroll(0, 10)
|
||||
robotgo.MouseClick("left", true)
|
||||
robotgo.MoveMouseSmooth(100, 200, 1.0, 100.0)
|
||||
robotgo.MoveSmooth(100, 200, 1.0, 100.0)
|
||||
}
|
||||
```
|
||||
|
||||
@ -30,18 +31,24 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/go-vgo/robotgo"
|
||||
|
||||
"github.com/go-vgo/robotgo"
|
||||
)
|
||||
|
||||
func main() {
|
||||
robotgo.TypeString("Hello World")
|
||||
robotgo.TypeStr("Hello World")
|
||||
// robotgo.TypeStr("だんしゃり")
|
||||
robotgo.TypeStr("だんしゃり")
|
||||
// ustr := uint32(robotgo.CharCodeAt("だんしゃり", 0))
|
||||
// robotgo.UnicodeType(ustr)
|
||||
|
||||
robotgo.KeyTap("enter")
|
||||
robotgo.TypeString("en")
|
||||
robotgo.TypeStr("en")
|
||||
robotgo.KeyTap("i", "alt", "command")
|
||||
arr := []string{"alt", "command"}
|
||||
robotgo.KeyTap("i", arr)
|
||||
|
||||
robotgo.WriteAll("测试")
|
||||
robotgo.WriteAll("Test")
|
||||
text, err := robotgo.ReadAll()
|
||||
if err == nil {
|
||||
fmt.Println(text)
|
||||
@ -61,14 +68,14 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
x, y := robotgo.GetMousePos()
|
||||
x, y := robotgo.Location()
|
||||
fmt.Println("pos:", x, y)
|
||||
color := robotgo.GetPixelColor(100, 200)
|
||||
fmt.Println("color----", color)
|
||||
}
|
||||
```
|
||||
|
||||
#### [Bitmap](https://github.com/go-vgo/robotgo/blob/master/examples/bitmap/mian.go)
|
||||
#### [Bitmap](https://github.com/go-vgo/robotgo/blob/master/examples/bitmap/main.go)
|
||||
|
||||
```Go
|
||||
package main
|
||||
@ -81,6 +88,8 @@ import (
|
||||
|
||||
func main() {
|
||||
bitmap := robotgo.CaptureScreen(10, 20, 30, 40)
|
||||
// use `defer robotgo.FreeBitmap(bit)` to free the bitmap
|
||||
defer robotgo.FreeBitmap(bitmap)
|
||||
fmt.Println("...", bitmap)
|
||||
|
||||
fx, fy := robotgo.FindBitmap(bitmap)
|
||||
@ -103,12 +112,12 @@ import (
|
||||
|
||||
func main() {
|
||||
keve := robotgo.AddEvent("k")
|
||||
if keve == 0 {
|
||||
if keve {
|
||||
fmt.Println("you press...", "k")
|
||||
}
|
||||
|
||||
mleft := robotgo.AddEvent("mleft")
|
||||
if mleft == 0 {
|
||||
if mleft {
|
||||
fmt.Println("you press...", "mouse left button")
|
||||
}
|
||||
}
|
||||
@ -129,11 +138,21 @@ func main() {
|
||||
fpid, err := robotgo.FindIds("Google")
|
||||
if err == nil {
|
||||
fmt.Println("pids...", fpid)
|
||||
|
||||
if len(fpid) > 0 {
|
||||
robotgo.ActivePID(fpid[0])
|
||||
|
||||
robotgo.Kill(fpid[0])
|
||||
}
|
||||
}
|
||||
|
||||
robotgo.ActiveName("chrome")
|
||||
|
||||
isExist, err := robotgo.PidExists(100)
|
||||
if err == nil {
|
||||
if err == nil && isExist {
|
||||
fmt.Println("pid exists is", isExist)
|
||||
|
||||
robotgo.Kill(100)
|
||||
}
|
||||
|
||||
abool := robotgo.ShowAlert("test", "robotgo")
|
||||
@ -144,4 +163,4 @@ func main() {
|
||||
title := robotgo.GetTitle()
|
||||
fmt.Println("title@@@", title)
|
||||
}
|
||||
```
|
||||
```
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user