mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-27 07:03:15 -04:00
Compare commits
555 Commits
v0.22.0
...
v0.29.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
84ef89eb1c | ||
|
|
a320702a71 | ||
|
|
b0106898f7 | ||
|
|
924f3323b5 | ||
|
|
b97df599c5 | ||
|
|
0defd747c8 | ||
|
|
e23186db8e | ||
|
|
059abb06fc | ||
|
|
bc216b8f67 | ||
|
|
864a1d5924 | ||
|
|
05444628e6 | ||
|
|
66ff52b012 | ||
|
|
7929999c1c | ||
|
|
7094a223af | ||
|
|
b2a46d0439 | ||
|
|
4748890935 | ||
|
|
6300cf915e | ||
|
|
9a9c9b15ba | ||
|
|
7ce86c3d2a | ||
|
|
a444637b18 | ||
|
|
f0d88c52a3 | ||
|
|
e17977d7c7 | ||
|
|
a7a8ff0bbb | ||
|
|
fc046add78 | ||
|
|
ab4a4a89e6 | ||
|
|
f7a400ddf6 | ||
|
|
07d39abddd | ||
|
|
0c8bf25ae4 | ||
|
|
b5785ba785 | ||
|
|
9797ed86f0 | ||
|
|
e220a75556 | ||
|
|
29d3729ac8 | ||
|
|
4a36741f9c | ||
|
|
ab46aa5b79 | ||
|
|
12fb37d827 | ||
|
|
c88a4ab221 | ||
|
|
8f7f3efc0d | ||
|
|
eb2d3894ef | ||
|
|
3f4f580181 | ||
|
|
d3aeff8838 | ||
|
|
0786d534f4 | ||
|
|
b4b2389d0a | ||
|
|
964e342f69 | ||
|
|
a134a59917 | ||
|
|
fbba203c4a | ||
|
|
61bd8b8254 | ||
|
|
587fa67571 | ||
|
|
7500a88230 | ||
|
|
82d0380ea6 | ||
|
|
642ce2bfa7 | ||
|
|
5bbe87960e | ||
|
|
cf77f82ae3 | ||
|
|
72cf4e577f | ||
|
|
4f3eacf01e | ||
|
|
31ebc5caf4 | ||
|
|
d273518ce9 | ||
|
|
2ade772ab0 | ||
|
|
4ac2006cbc | ||
|
|
ba5ad3be13 | ||
|
|
8092fa2440 | ||
|
|
8bb004a1d9 | ||
|
|
de5327477a | ||
|
|
8f959714cc | ||
|
|
035eebb19a | ||
|
|
b5af6bb266 | ||
|
|
1805124c54 | ||
|
|
0f64589dba | ||
|
|
5438a2a524 | ||
|
|
918430979f | ||
|
|
f3f46cb3f6 | ||
|
|
3c3be71a77 | ||
|
|
a7986b077f | ||
|
|
8a0edde5c8 | ||
|
|
a9e168e10d | ||
|
|
bd9cc2a9da | ||
|
|
596c0edf0f | ||
|
|
92592ec605 | ||
|
|
25c4e2e451 | ||
|
|
ad52c72e41 | ||
|
|
d15feb5cfa | ||
|
|
9938327066 | ||
|
|
f980ed7b83 | ||
|
|
2496098890 | ||
|
|
60e91b187a | ||
|
|
2486f0f1a1 | ||
|
|
fbea75d31f | ||
|
|
d4c9535af9 | ||
|
|
f0fcb346b0 | ||
|
|
77f8e511e9 | ||
|
|
3217eaa416 | ||
|
|
b18295a1ce | ||
|
|
fb9695d56d | ||
|
|
08bdca19b1 | ||
|
|
79ac236721 | ||
|
|
b870a11a99 | ||
|
|
2af1550bbb | ||
|
|
ed796dcd15 | ||
|
|
a006cd7dc8 | ||
|
|
a31f71ee07 | ||
|
|
0f89aac9f6 | ||
|
|
82df9531f4 | ||
|
|
265152355e | ||
|
|
37c0f615cf | ||
|
|
5ba6bdef49 | ||
|
|
69d6076310 | ||
|
|
7029ce6ecd | ||
|
|
1eb1a13a77 | ||
|
|
3fd73848dd | ||
|
|
4e1c46fe9e | ||
|
|
180a4c7a16 | ||
|
|
13613931cf | ||
|
|
483c1d40ae | ||
|
|
1b4045dcb2 | ||
|
|
8f8da0f8bb | ||
|
|
23b821285c | ||
|
|
c984476687 | ||
|
|
42c395e49d | ||
|
|
422c6b1987 | ||
|
|
b457329003 | ||
|
|
930df0ec45 | ||
|
|
e1b7fda409 | ||
|
|
e423802ed3 | ||
|
|
a82f66826b | ||
|
|
0f2fbe373b | ||
|
|
7341ee80ea | ||
|
|
d448d3e14f | ||
|
|
a867032e1e | ||
|
|
b711a11549 | ||
|
|
809162fbd0 | ||
|
|
de782504ab | ||
|
|
1886949efe | ||
|
|
b1a5fae1f5 | ||
|
|
a88d2e079d | ||
|
|
067535eb38 | ||
|
|
7d626d9dfd | ||
|
|
62ce14a013 | ||
|
|
6f60c7a6cc | ||
|
|
6cf0bf76da | ||
|
|
9225b2812e | ||
|
|
08ce3af3e1 | ||
|
|
490abcad14 | ||
|
|
4b22ca8daf | ||
|
|
66ca445caa | ||
|
|
2f52c23fa9 | ||
|
|
ee88e38f13 | ||
|
|
5e77d70245 | ||
|
|
58ec458877 | ||
|
|
94e4c394e7 | ||
|
|
f43ce2a131 | ||
|
|
402cbd55f9 | ||
|
|
da7422c6e1 | ||
|
|
8934d2765d | ||
|
|
89eea64a4a | ||
|
|
9f781bc422 | ||
|
|
4ed4e918f3 | ||
|
|
2e4d79f57a | ||
|
|
bf92f3e97b | ||
|
|
2a58b785fe | ||
|
|
32784af3c4 | ||
|
|
94688a62f0 | ||
|
|
28e34c2e1b | ||
|
|
9ae7498a8a | ||
|
|
1786c877ec | ||
|
|
101ac8908c | ||
|
|
ba4bf03675 | ||
|
|
a63b066ed5 | ||
|
|
f77f858e9b | ||
|
|
6d0cf6a275 | ||
|
|
12df8b6c0c | ||
|
|
65baae75c4 | ||
|
|
418cc44e93 | ||
|
|
ce6c6e8c95 | ||
|
|
bdcbd7d1f9 | ||
|
|
05484c5888 | ||
|
|
50bbc85dc3 | ||
|
|
8669c2e8df | ||
|
|
97d4c7b303 | ||
|
|
08f9e374e0 | ||
|
|
a7a7cc64cd | ||
|
|
04d9e081b8 | ||
|
|
8fc24c959a | ||
|
|
2fb15dbe8a | ||
|
|
92fdf5ba85 | ||
|
|
4f06cfcf5b | ||
|
|
462bb4d324 | ||
|
|
f6ca8515ab | ||
|
|
71094e5703 | ||
|
|
bb0f965c57 | ||
|
|
4d48c76da9 | ||
|
|
fafdedfb7d | ||
|
|
25b129362f | ||
|
|
48b843e42d | ||
|
|
58f2455aa9 | ||
|
|
155f1f9720 | ||
|
|
3b56b0e76f | ||
|
|
5d2aca90bd | ||
|
|
ba49db2cb9 | ||
|
|
a4695c5397 | ||
|
|
92ddb3483e | ||
|
|
fec52b028e | ||
|
|
d8c0ee733b | ||
|
|
fb248eaadc | ||
|
|
da7bf8e29b | ||
|
|
a6a8b12537 | ||
|
|
340f951d10 | ||
|
|
05dd31b8ea | ||
|
|
0fca8b088d | ||
|
|
ab56e9f57d | ||
|
|
97d2aaa953 | ||
|
|
29419d6c38 | ||
|
|
e517e468f8 | ||
|
|
d67c928120 | ||
|
|
112965b4ff | ||
|
|
e0018d0710 | ||
|
|
1ca8b65e85 | ||
|
|
a43a15b4a0 | ||
|
|
66aa6c945d | ||
|
|
dfecdc5762 | ||
|
|
8729119536 | ||
|
|
ec7e935248 | ||
|
|
fd72000a9a | ||
|
|
e91ee811cb | ||
|
|
b3b80166ce | ||
|
|
f9b41fd819 | ||
|
|
0d9c39029c | ||
|
|
da2cef97a3 | ||
|
|
76f158d310 | ||
|
|
2e4338bb8d | ||
|
|
ec2888b8b7 | ||
|
|
fa83bace12 | ||
|
|
ee7dc48e3b | ||
|
|
11d4a301e4 | ||
|
|
ad41eaf151 | ||
|
|
9b71df9f97 | ||
|
|
b1c9e4a6fa | ||
|
|
cdbaf4816a | ||
|
|
6b7ceedc91 | ||
|
|
c53a574bff | ||
|
|
95246d81c1 | ||
|
|
bf537009d9 | ||
|
|
50035643f7 | ||
|
|
64c22f9075 | ||
|
|
4895a29e92 | ||
|
|
6cdb3179c8 | ||
|
|
4fd52af682 | ||
|
|
5a0bad130d | ||
|
|
08d025968e | ||
|
|
1cd0e94c26 | ||
|
|
1ec976f95e | ||
|
|
f10ef5f331 | ||
|
|
653bc59813 | ||
|
|
3e991e13dc | ||
|
|
5397b53e04 | ||
|
|
f09259f6de | ||
|
|
430a49ebc2 | ||
|
|
1091a8ba1a | ||
|
|
9116b6c8cd | ||
|
|
990e34a129 | ||
|
|
472d7b9376 | ||
|
|
50dd7881b1 | ||
|
|
aa8f8db305 | ||
|
|
cdd9b1e1eb | ||
|
|
2d2ce70edc | ||
|
|
78f1d1df38 | ||
|
|
a06bb3f992 | ||
|
|
e289f30e5d | ||
|
|
4b10993970 | ||
|
|
d78a870e66 | ||
|
|
cb41c58f21 | ||
|
|
c55d97183d | ||
|
|
8646cbc9f5 | ||
|
|
64c1d4c5bb | ||
|
|
76b949c196 | ||
|
|
c93ef47b9b | ||
|
|
2b414cd825 | ||
|
|
ac42447459 | ||
|
|
401d20fa1f | ||
|
|
6b5b570b45 | ||
|
|
9e6f666616 | ||
|
|
8ef9fe44c7 | ||
|
|
3e0a544eb8 | ||
|
|
6474891f1e | ||
|
|
40abb526cc | ||
|
|
c532d910c0 | ||
|
|
44288f6280 | ||
|
|
eec84ade86 | ||
|
|
10419ff441 | ||
|
|
57981b533d | ||
|
|
c5eaa0ab69 | ||
|
|
2c01e9e747 | ||
|
|
4c39b3188c | ||
|
|
224872ce03 | ||
|
|
f10a984ba3 | ||
|
|
c7f7181388 | ||
|
|
92530299eb | ||
|
|
58cd23d1ac | ||
|
|
5d85c10a2c | ||
|
|
f11270dac0 | ||
|
|
bcd76d4718 | ||
|
|
4dd2b66aaa | ||
|
|
829a140d9b | ||
|
|
f04fa5d54f | ||
|
|
b4175c1454 | ||
|
|
0728105b2b | ||
|
|
ea09d1d10e | ||
|
|
e7f88588bf | ||
|
|
ce890c3455 | ||
|
|
cbba00d360 | ||
|
|
bf366cb99d | ||
|
|
142d55ff24 | ||
|
|
a58400a82c | ||
|
|
aac28d24ac | ||
|
|
d624a3e648 | ||
|
|
c57294b41a | ||
|
|
485e82dcb1 | ||
|
|
e8d910ffd3 | ||
|
|
ab1f636960 | ||
|
|
52c4670237 | ||
|
|
2ae12fb0a0 | ||
|
|
6c1d3c4fd8 | ||
|
|
08de2b3fc4 | ||
|
|
945a9e3122 | ||
|
|
6e28ba8927 | ||
|
|
7369551c02 | ||
|
|
e22c76b3ac | ||
|
|
a438091266 | ||
|
|
85baf79d17 | ||
|
|
1c68be0631 | ||
|
|
b222dde835 | ||
|
|
78e5a395da | ||
|
|
7846e6a31e | ||
|
|
b7e7755edd | ||
|
|
40f48cbeb4 | ||
|
|
fb8313aa97 | ||
|
|
f9643917d3 | ||
|
|
ac1c9b1218 | ||
|
|
daf0d6b9a7 | ||
|
|
fa14863284 | ||
|
|
cd9ec0afc7 | ||
|
|
f3f6f1008a | ||
|
|
0e52672f4a | ||
|
|
bc1dc1fd63 | ||
|
|
f93f2c158b | ||
|
|
9229e2d88b | ||
|
|
51bb6b751e | ||
|
|
2cc87cab65 | ||
|
|
7cd273ae58 | ||
|
|
001fb7ef60 | ||
|
|
cf4660841a | ||
|
|
a52f755ce8 | ||
|
|
2a2abc4843 | ||
|
|
8af222c1e3 | ||
|
|
d3e6949007 | ||
|
|
a033b25ecb | ||
|
|
39dd30c239 | ||
|
|
c5c99d2357 | ||
|
|
25ff30ee8c | ||
|
|
6b250a74f8 | ||
|
|
5331397c6c | ||
|
|
0b39024133 | ||
|
|
438d286fd5 | ||
|
|
18a61f1058 | ||
|
|
20d012ae3f | ||
|
|
efc54ab8ba | ||
|
|
ea1c031b54 | ||
|
|
11a44081df | ||
|
|
5eb9c9504b | ||
|
|
29a078f65c | ||
|
|
be61ca13fe | ||
|
|
e9d5b2007a | ||
|
|
f2de8475fc | ||
|
|
3ecbea3c39 | ||
|
|
c4df7ad7a5 | ||
|
|
387567a917 | ||
|
|
cfbe8462cc | ||
|
|
5f4df54895 | ||
|
|
ed698f2462 | ||
|
|
b4774861db | ||
|
|
9768f73bb7 | ||
|
|
805249e27e | ||
|
|
5f24c40d05 | ||
|
|
1b3b82a3c1 | ||
|
|
9e72396709 | ||
|
|
125ee0b446 | ||
|
|
3bfb580d7a | ||
|
|
b54d47796d | ||
|
|
b5d0d6ff3e | ||
|
|
c9520deef8 | ||
|
|
ceab0f8c40 | ||
|
|
b87757c552 | ||
|
|
1972eb952d | ||
|
|
f92803d80e | ||
|
|
8afeb910bd | ||
|
|
f16ed98af4 | ||
|
|
2a9916103b | ||
|
|
63ad47a7bf | ||
|
|
27e6548343 | ||
|
|
8c91986dd3 | ||
|
|
5a65347c4e | ||
|
|
635180c8be | ||
|
|
019ce9862f | ||
|
|
c7f46876a7 | ||
|
|
c916eb6137 | ||
|
|
67cca71524 | ||
|
|
1eff7ae004 | ||
|
|
982ad46c83 | ||
|
|
657b4fd59e | ||
|
|
b371b406d5 | ||
|
|
91591c4e94 | ||
|
|
078b9719cc | ||
|
|
41d9826ee9 | ||
|
|
0152508a39 | ||
|
|
cdeb1c3828 | ||
|
|
0986fae066 | ||
|
|
277515636d | ||
|
|
45aacd8407 | ||
|
|
e8cdf8b092 | ||
|
|
1c4d6e7613 | ||
|
|
04b4e48265 | ||
|
|
dabcb1834d | ||
|
|
629cd86c7c | ||
|
|
ba704c4eb4 | ||
|
|
0487876826 | ||
|
|
ca9c05368e | ||
|
|
0d634a0061 | ||
|
|
86748fbc68 | ||
|
|
599477d754 | ||
|
|
889258f538 | ||
|
|
ffe2143d14 | ||
|
|
98470393d1 | ||
|
|
4192d04a53 | ||
|
|
3571dcd68c | ||
|
|
952edcb804 | ||
|
|
10a94c0794 | ||
|
|
dd32ace9ab | ||
|
|
7e0c6ee097 | ||
|
|
b1be34c6a0 | ||
|
|
b9307a9967 | ||
|
|
b1d353180b | ||
|
|
bd99eb1347 | ||
|
|
f79c01b0cf | ||
|
|
3f1e09ec0e | ||
|
|
05125029c6 | ||
|
|
05fe983757 | ||
|
|
d1a7749df5 | ||
|
|
9d63fc7ca0 | ||
|
|
38fccebe1f | ||
|
|
c05952b813 | ||
|
|
932cbe40bf | ||
|
|
39573d65d0 | ||
|
|
6db308f1e9 | ||
|
|
6f70fd90b9 | ||
|
|
db038d943c | ||
|
|
c5620efc9c | ||
|
|
8fb7aa5cef | ||
|
|
6ddee9a8ac | ||
|
|
5700359a61 | ||
|
|
0861a353d6 | ||
|
|
f79efec7ef | ||
|
|
77d5d20391 | ||
|
|
165e51d850 | ||
|
|
1c38f113b3 | ||
|
|
66859607a3 | ||
|
|
edf396b1a4 | ||
|
|
cbeb51b436 | ||
|
|
45e4fd6ec1 | ||
|
|
3a077ff211 | ||
|
|
be850e483a | ||
|
|
33fb62bb25 | ||
|
|
66c117e599 | ||
|
|
8aa1be8336 | ||
|
|
037d4121a1 | ||
|
|
fbd3918d3a | ||
|
|
7c543a43a9 | ||
|
|
ee3996cac6 | ||
|
|
96809ac659 | ||
|
|
6343059bc0 | ||
|
|
5a78fe33e8 | ||
|
|
676fb947f2 | ||
|
|
d18afb4a50 | ||
|
|
fc336a76bf | ||
|
|
b9f3d333e4 | ||
|
|
3d85af04be | ||
|
|
471b1e003a | ||
|
|
be2e17d605 | ||
|
|
9d6b9797c0 | ||
|
|
3cd6a18048 | ||
|
|
c9558c5f0e | ||
|
|
71e3d25422 | ||
|
|
644dc13e00 | ||
|
|
47e7aa4209 | ||
|
|
1c97a310b1 | ||
|
|
d612a1b5a1 | ||
|
|
386ead15a3 | ||
|
|
83c95e774d | ||
|
|
e4754999b7 | ||
|
|
c66489dbb1 | ||
|
|
21f9aefc7e | ||
|
|
d103dc2631 | ||
|
|
cac627ed05 | ||
|
|
e2cf2a5754 | ||
|
|
658a9a4ea8 | ||
|
|
a2db4c0a32 | ||
|
|
02a34a167a | ||
|
|
bea60930b6 | ||
|
|
0f7c82d38f | ||
|
|
6ba583d198 | ||
|
|
89d4c06dec | ||
|
|
9c72cc2a98 | ||
|
|
412bd94ea4 | ||
|
|
514ab043f2 | ||
|
|
68100102be | ||
|
|
05fdcb5b27 | ||
|
|
7a49c88200 | ||
|
|
40232d48ba | ||
|
|
55dff53a98 | ||
|
|
6919c2fb2d | ||
|
|
3d5d05eac7 | ||
|
|
dd866a74a6 | ||
|
|
b1e22aa559 | ||
|
|
2191e9ecd5 | ||
|
|
bf62103417 | ||
|
|
4b1b314ce2 | ||
|
|
c1ea0dde92 | ||
|
|
5a6cfc314e | ||
|
|
a4121a2c2e | ||
|
|
03335cef85 | ||
|
|
ff66bdda7c | ||
|
|
6cfddfea21 | ||
|
|
49bcec1d27 | ||
|
|
878c179761 | ||
|
|
bc19c04339 | ||
|
|
c7a33f926b | ||
|
|
3c38afdb47 | ||
|
|
b8828105cf | ||
|
|
007b195a5e | ||
|
|
b4c6cdf9a3 | ||
|
|
26775fa0b6 | ||
|
|
114fe9d502 | ||
|
|
54bc41f68b | ||
|
|
47ff8d61d1 | ||
|
|
849b8f5dce | ||
|
|
aabe42d252 | ||
|
|
78a62ec547 | ||
|
|
6dae994bb4 | ||
|
|
4c4d0916fd | ||
|
|
d5609729cc | ||
|
|
1f24a09570 | ||
|
|
a8e777a5df | ||
|
|
0bc58f695b | ||
|
|
28023d9f5b | ||
|
|
c2aed1979d | ||
|
|
7e04273719 | ||
|
|
0683bdcd42 | ||
|
|
29ab0bb629 |
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[alias]
|
||||
run-wasm = ["run", "--release", "--package", "run-wasm", "--"]
|
||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -20,5 +20,3 @@
|
||||
*.PDF diff=astextplain
|
||||
*.rtf diff=astextplain
|
||||
*.RTF diff=astextplain
|
||||
|
||||
/CHANGELOG.md merge=union
|
||||
|
||||
40
.github/CODEOWNERS
vendored
Normal file
40
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
# Core maintainers:
|
||||
# - @msiglreith
|
||||
# - @kchibisov
|
||||
# - @madsmtm
|
||||
# - @maroider
|
||||
|
||||
# Android
|
||||
/src/platform/android.rs @msiglreith
|
||||
/src/platform_impl/android @msiglreith
|
||||
|
||||
# iOS
|
||||
/src/platform/ios.rs @madsmtm
|
||||
/src/platform_impl/ios @madsmtm
|
||||
|
||||
# Unix
|
||||
/src/platform_impl/linux/mod.rs @kchibisov
|
||||
|
||||
# Wayland
|
||||
/src/platform/wayland.rs @kchibisov
|
||||
/src/platform_impl/linux/wayland @kchibisov
|
||||
|
||||
# X11
|
||||
/src/platform/x11.rs @kchibisov
|
||||
/src/platform_impl/linux/x11 @kchibisov
|
||||
|
||||
# macOS
|
||||
/src/platform/macos.rs @madsmtm
|
||||
/src/platform_impl/macos @madsmtm
|
||||
|
||||
# Web (no maintainer)
|
||||
/src/platform/web.rs @daxpedda
|
||||
/src/platform_impl/web @daxpedda
|
||||
|
||||
# Windows
|
||||
/src/platform/windows.rs @msiglreith
|
||||
/src/platform_impl/windows @msiglreith
|
||||
|
||||
# Orbital (Redox OS)
|
||||
/src/platform/orbital.rs @jackpot51
|
||||
/src/platform_impl/orbital @jackpot51
|
||||
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,7 +1,4 @@
|
||||
- [ ] Tested on all platforms changed
|
||||
- [ ] Compilation warnings were addressed
|
||||
- [ ] `cargo fmt` has been run on this branch
|
||||
- [ ] `cargo doc` builds successfully
|
||||
- [ ] Added an entry to `CHANGELOG.md` if knowledge of this change could be valuable to users
|
||||
- [ ] Updated documentation to reflect any user-facing changes, including notes of platform-specific behavior
|
||||
- [ ] Created or updated an example program if it would help users understand this functionality
|
||||
|
||||
111
.github/workflows/ci.yml
vendored
111
.github/workflows/ci.yml
vendored
@@ -2,59 +2,88 @@ name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.rs'
|
||||
- '**.toml'
|
||||
- '.github/workflows/ci.yml'
|
||||
push:
|
||||
branches: [master]
|
||||
paths:
|
||||
- '**.rs'
|
||||
- '**.toml'
|
||||
- '.github/workflows/ci.yml'
|
||||
|
||||
jobs:
|
||||
Check_Formatting:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: hecrj/setup-rust-action@v1
|
||||
with:
|
||||
rust-version: stable
|
||||
components: rustfmt
|
||||
- name: Check Formatting
|
||||
run: cargo +stable fmt --all -- --check
|
||||
|
||||
cargo-deny:
|
||||
name: cargo-deny
|
||||
|
||||
Tests:
|
||||
# TODO: remove this matrix when https://github.com/EmbarkStudios/cargo-deny/issues/324 is resolved
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
rust_version: [stable, nightly]
|
||||
platform:
|
||||
- aarch64-apple-ios
|
||||
- aarch64-linux-android
|
||||
- i686-pc-windows-gnu
|
||||
- i686-pc-windows-msvc
|
||||
- i686-unknown-linux-gnu
|
||||
- wasm32-unknown-unknown
|
||||
- x86_64-apple-darwin
|
||||
- x86_64-apple-ios
|
||||
- x86_64-pc-windows-gnu
|
||||
- x86_64-pc-windows-msvc
|
||||
- x86_64-unknown-linux-gnu
|
||||
- x86_64-unknown-redox
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||
with:
|
||||
command: check
|
||||
log-level: error
|
||||
arguments: --all-features --target ${{ matrix.platform }}
|
||||
|
||||
tests:
|
||||
name: Tests
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
rust_version: ['1.64.0', stable, nightly]
|
||||
platform:
|
||||
# Note: Make sure that we test all the `docs.rs` targets defined in Cargo.toml!
|
||||
- { target: x86_64-pc-windows-msvc, os: windows-latest, }
|
||||
- { target: i686-pc-windows-msvc, os: windows-latest, }
|
||||
- { target: x86_64-pc-windows-gnu, os: windows-latest, host: -x86_64-pc-windows-gnu }
|
||||
- { target: i686-pc-windows-gnu, os: windows-latest, host: -i686-pc-windows-gnu }
|
||||
- { target: i686-unknown-linux-gnu, os: ubuntu-latest, }
|
||||
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, }
|
||||
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: x11 }
|
||||
- { target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: --no-default-features, features: "wayland,wayland-dlopen" }
|
||||
- { target: aarch64-linux-android, os: ubuntu-latest, options: -p winit, cmd: 'apk --', features: "android-native-activity" }
|
||||
- { target: x86_64-unknown-redox, os: ubuntu-latest, }
|
||||
- { target: x86_64-apple-darwin, os: macos-latest, }
|
||||
- { target: x86_64-apple-ios, os: macos-latest, }
|
||||
- { target: aarch64-apple-ios, os: macos-latest, }
|
||||
# We're using Windows rather than Ubuntu to run the wasm tests because caching cargo-web
|
||||
# doesn't currently work on Linux.
|
||||
- { target: wasm32-unknown-unknown, os: windows-latest, features: stdweb, web: web }
|
||||
- { target: wasm32-unknown-unknown, os: windows-latest, features: web-sys, web: web }
|
||||
- { target: wasm32-unknown-unknown, os: windows-latest, }
|
||||
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
CARGO_INCREMENTAL: 0
|
||||
RUSTFLAGS: "-C debuginfo=0"
|
||||
RUSTFLAGS: "-C debuginfo=0 --deny warnings"
|
||||
OPTIONS: ${{ matrix.platform.options }}
|
||||
FEATURES: ${{ format(',{0}', matrix.platform.features ) }}
|
||||
WEB: ${{ matrix.platform.web }}
|
||||
CMD: ${{ matrix.platform.cmd }}
|
||||
RUSTDOCFLAGS: -Dwarnings
|
||||
|
||||
runs-on: ${{ matrix.platform.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
# Used to cache cargo-web
|
||||
- name: Cache cargo folder
|
||||
uses: actions/cache@v1
|
||||
@@ -66,41 +95,57 @@ jobs:
|
||||
with:
|
||||
rust-version: ${{ matrix.rust_version }}${{ matrix.platform.host }}
|
||||
targets: ${{ matrix.platform.target }}
|
||||
components: clippy
|
||||
|
||||
- name: Install GCC Multilib
|
||||
if: (matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')
|
||||
run: sudo apt-get update && sudo apt-get install gcc-multilib
|
||||
- name: Install cargo-web
|
||||
continue-on-error: true
|
||||
if: contains(matrix.platform.target, 'wasm32')
|
||||
run: cargo install cargo-web
|
||||
- name: Install cargo-apk
|
||||
if: contains(matrix.platform.target, 'android')
|
||||
run: cargo install cargo-apk
|
||||
|
||||
- name: Check documentation
|
||||
shell: bash
|
||||
if: matrix.platform.target != 'wasm32-unknown-unknown'
|
||||
run: cargo doc --no-deps --target ${{ matrix.platform.target }} --features $FEATURES
|
||||
run: cargo doc --no-deps --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES --document-private-items
|
||||
|
||||
- name: Build
|
||||
- name: Build crate
|
||||
shell: bash
|
||||
run: cargo $WEB build --verbose --target ${{ matrix.platform.target }} --features $FEATURES
|
||||
run: cargo $CMD build --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
|
||||
|
||||
- name: Build tests
|
||||
shell: bash
|
||||
run: cargo $WEB test --no-run --verbose --target ${{ matrix.platform.target }} --features $FEATURES
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.rust_version != '1.64.0'
|
||||
run: cargo $CMD test --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
|
||||
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
if: (!contains(matrix.platform.target, 'ios') && !contains(matrix.platform.target, 'wasm32'))
|
||||
run: cargo $WEB test --verbose --target ${{ matrix.platform.target }} --features $FEATURES
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'android') &&
|
||||
!contains(matrix.platform.target, 'ios') &&
|
||||
!contains(matrix.platform.target, 'wasm32') &&
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.rust_version != '1.64.0'
|
||||
run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
|
||||
|
||||
|
||||
- name: Build with serde enabled
|
||||
- name: Lint with clippy
|
||||
shell: bash
|
||||
run: cargo $WEB build --verbose --target ${{ matrix.platform.target }} --features serde,$FEATURES
|
||||
if: (matrix.rust_version == 'stable') && !contains(matrix.platform.options, '--no-default-features')
|
||||
run: cargo clippy --all-targets --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES -- -Dwarnings
|
||||
|
||||
- name: Build tests with serde enabled
|
||||
shell: bash
|
||||
run: cargo $WEB test --no-run --verbose --target ${{ matrix.platform.target }} --features serde,$FEATURES
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.rust_version != '1.64.0'
|
||||
run: cargo $CMD test --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES
|
||||
- name: Run tests with serde enabled
|
||||
shell: bash
|
||||
if: (!contains(matrix.platform.target, 'ios') && !contains(matrix.platform.target, 'wasm32'))
|
||||
run: cargo $WEB test --verbose --target ${{ matrix.platform.target }} --features serde,$FEATURES
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'android') &&
|
||||
!contains(matrix.platform.target, 'ios') &&
|
||||
!contains(matrix.platform.target, 'wasm32') &&
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.rust_version != '1.64.0'
|
||||
run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES
|
||||
|
||||
18
.github/workflows/publish.yml
vendored
18
.github/workflows/publish.yml
vendored
@@ -1,18 +0,0 @@
|
||||
name: Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
paths: "Cargo.toml"
|
||||
|
||||
jobs:
|
||||
Publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: hecrj/setup-rust-action@v1
|
||||
with:
|
||||
rust-version: stable
|
||||
components: rustfmt
|
||||
- name: Publish to crates.io
|
||||
run: cargo publish --token ${{ secrets.cratesio_token }}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,3 +7,4 @@ rls/
|
||||
*.ts
|
||||
*.js
|
||||
#*#
|
||||
.DS_Store
|
||||
|
||||
499
CHANGELOG.md
499
CHANGELOG.md
@@ -1,4 +1,482 @@
|
||||
# 0.22.0 (2020-03-07)
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
Please keep one empty line before and after all headers. (This is required for `git` to produce a conflict when a release is made while a PR is open and the PR's changelog entry would go into the wrong section).
|
||||
|
||||
And please only add new entries to the top of this list, right below the `# Unreleased` header.
|
||||
|
||||
# Unreleased
|
||||
|
||||
# 0.29.0-beta.0
|
||||
|
||||
- On Web, allow event loops to be recreated with `spawn`.
|
||||
- **Breaking:** Rename `Window::set_ime_position` to `Window::set_ime_cursor_area` adding a way to set exclusive zone.
|
||||
- On Android, changed default behavior of Android to ignore volume keys letting the operating system handle them.
|
||||
- On Android, added `EventLoopBuilderExtAndroid::handle_volume_keys` to indicate that the application will handle the volume keys manually.
|
||||
- **Breaking:** Rename `DeviceEventFilter` to `DeviceEvents` reversing the behavior of variants.
|
||||
- **Breaking:** Rename `EventLoopWindowTarget::set_device_event_filter` to `listen_device_events`.
|
||||
- On X11, fix `EventLoopWindowTarget::listen_device_events` effect being reversed.
|
||||
- **Breaking:** Remove all deprecated `modifiers` fields.
|
||||
- **Breaking:** Overhaul keyboard input handling.
|
||||
- Replace `KeyboardInput` with `KeyEvent` and `RawKeyEvent`.
|
||||
- Change `WindowEvent::KeyboardInput` to contain a `KeyEvent`.
|
||||
- Change `Event::Key` to contain a `RawKeyEvent`.
|
||||
- Remove `Event::ReceivedCharacter`. In its place, you should use
|
||||
`KeyEvent.text` in combination with `WindowEvent::Ime`.
|
||||
- Replace `VirtualKeyCode` with the `Key` enum.
|
||||
- Replace `ScanCode` with the `KeyCode` enum.
|
||||
- Rename `ModifiersState::LOGO` to `SUPER` and `ModifiersState::CTRL` to `CONTROL`.
|
||||
- Add `KeyCode` to refer to keys (roughly) by their physical location.
|
||||
- Add `NativeKeyCode` to represent raw `KeyCode`s which Winit doesn't
|
||||
understand.
|
||||
- Add `Key` to represent the keys after they've been interpreted by the
|
||||
active (software) keyboard layout.
|
||||
- Add `NativeKey` to represent raw `Key`s which Winit doesn't understand.
|
||||
- Add `KeyLocation` to tell apart `Key`s which usually "mean" the same thing,
|
||||
but can appear simultaneously in different spots on the same keyboard
|
||||
layout.
|
||||
- Add `Window::reset_dead_keys` to enable application-controlled cancellation
|
||||
of dead key sequences.
|
||||
- Add `KeyEventExtModifierSupplement` to expose additional (and less
|
||||
portable) interpretations of a given key-press.
|
||||
- Add `KeyCodeExtScancode`, which lets you convert between raw keycodes and
|
||||
`KeyCode`.
|
||||
- `ModifiersChanged` now uses dedicated `Modifiers` struct.
|
||||
- On Orbital, fix `ModifiersChanged` not being sent.
|
||||
- **Breaking:** `CursorIcon` is now used from the `cursor-icon` crate.
|
||||
- **Breaking:** `CursorIcon::Hand` is now named `CursorIcon::Pointer`.
|
||||
- **Breaking:** `CursorIcon::Arrow` was removed.
|
||||
- On Wayland, fix maximized startup not taking full size on GNOME.
|
||||
- On Wayland, fix initial window size not restored for maximized/fullscreened on startup window.
|
||||
- On Wayland, `Window::outer_size` now accounts for **client side** decorations.
|
||||
- On Wayland, fix window not checking that it actually got initial configure event.
|
||||
- On Wayland, fix maximized window creation and window geometry handling.
|
||||
- On Wayland, fix forward compatibility issues.
|
||||
- On Wayland, add `Window::drag_resize_window` method.
|
||||
- On Wayland, drop `WINIT_WAYLAND_CSD_THEME` variable.
|
||||
- Implement `PartialOrd` and `Ord` on types in the `dpi` module.
|
||||
- **Breaking:** Bump MSRV from `1.60` to `1.64`.
|
||||
- **Breaking:** On Web, the canvas output bitmap size is no longer adjusted.
|
||||
- On Web: fix `Window::request_redraw` not waking the event loop when called from outside the loop.
|
||||
- On Web: fix position of touch events to be relative to the canvas.
|
||||
- On Web, fix `Window:::set_fullscreen` doing nothing when called outside the event loop but during
|
||||
a transient activation.
|
||||
- On Web, fix pointer button events not being processed when a buttons is already pressed.
|
||||
- **Breaking:** Updated `bitflags` crate version to `2`, which changes the API on exposed types.
|
||||
- On Web, handle coalesced pointer events, which increases the resolution of pointer inputs.
|
||||
- **Breaking:** On Web, `instant` is now replaced by `web_time`.
|
||||
- On Windows, port to `windows-sys` version 0.48.0.
|
||||
- On Web, fix pen treated as mouse input.
|
||||
- On Web, send mouse position on button release as well.
|
||||
- On Web, fix touch input not gaining or loosing focus.
|
||||
- **Breaking:** On Web, dropped support for Safari versions below 13.1.
|
||||
- On Web, prevent clicks on the canvas to select text.
|
||||
- On Web, `EventLoopProxy` now implements `Send`.
|
||||
- On Web, `Window` now implements `Send` and `Sync`.
|
||||
- **Breaking:** `WindowExtWebSys::canvas()` now returns an `Option`.
|
||||
- On Web, use the correct canvas size when calculating the new size during scale factor change,
|
||||
instead of using the output bitmap size.
|
||||
- On Web, scale factor and dark mode detection are now more robust.
|
||||
- On Web, fix the bfcache by not using the `beforeunload` event and map bfcache loading/unloading to `Suspended`/`Resumed` events.
|
||||
- On Web, fix scale factor resize suggestion always overwriting the canvas size.
|
||||
- On macOS, fix crash when dropping `Window`.
|
||||
- On Web, use `Window.requestIdleCallback()` for `ControlFlow::Poll` when available.
|
||||
- **Breaking:** On Web, the canvas size is not controlled by Winit anymore and external changes to
|
||||
the canvas size will be reported through `WindowEvent::Resized`.
|
||||
- On Web, respect `EventLoopWindowTarget::listen_device_events()` settings.
|
||||
- On Web, fix `DeviceEvent::MouseMotion` only being emitted for each canvas instead of the whole window.
|
||||
- On Web, add `DeviceEvent::Motion`, `DeviceEvent::MouseWheel`, `DeviceEvent::Button` and
|
||||
`DeviceEvent::Key` support.
|
||||
- **Breaking** `MouseButton` now supports `Back` and `Forward` variants, emitted from mouse events
|
||||
on Wayland, X11, Windows, macOS and Web.
|
||||
|
||||
# 0.28.6
|
||||
|
||||
- On macOS, fixed memory leak when getting monitor handle.
|
||||
- On macOS, fix `Backspace` being emitted when clearing preedit with it.
|
||||
|
||||
# 0.28.5
|
||||
|
||||
- On macOS, fix `key_up` being ignored when `Ime` is disabled.
|
||||
|
||||
# 0.28.4
|
||||
|
||||
- On macOS, fix empty marked text blocking regular input.
|
||||
- On macOS, fix potential panic when getting refresh rate.
|
||||
- On macOS, fix crash when calling `Window::set_ime_position` from another thread.
|
||||
|
||||
# 0.28.3
|
||||
|
||||
- Fix macOS memory leaks.
|
||||
|
||||
# 0.28.2
|
||||
|
||||
- Implement `HasRawDisplayHandle` for `EventLoop`.
|
||||
- On macOS, set resize increments only for live resizes.
|
||||
- On Wayland, fix rare crash on DPI change
|
||||
- Web: Added support for `Window::theme`.
|
||||
- On Wayland, fix rounding issues when doing resize.
|
||||
- On macOS, fix wrong focused state on startup.
|
||||
- On Windows, fix crash on setting taskbar when using Visual Studio debugger.
|
||||
- On macOS, resize simple fullscreen windows on windowDidChangeScreen events.
|
||||
|
||||
# 0.28.1
|
||||
|
||||
- On Wayland, fix crash when dropping a window in multi-window setup.
|
||||
|
||||
# 0.28.0
|
||||
|
||||
- On macOS, fixed `Ime::Commit` persisting for all input after interacting with `Ime`.
|
||||
- On macOS, added `WindowExtMacOS::option_as_alt` and `WindowExtMacOS::set_option_as_alt`.
|
||||
- On Windows, fix window size for maximized, undecorated windows.
|
||||
- On Windows and macOS, add `WindowBuilder::with_active`.
|
||||
- Add `Window::is_minimized`.
|
||||
- On X11, fix errors handled during `register_xlib_error_hook` invocation bleeding into winit.
|
||||
- Add `Window::has_focus`.
|
||||
- On Windows, fix `Window::set_minimized(false)` not working for windows minimized by `Win + D` hotkey.
|
||||
- **Breaking:** On Web, touch input no longer fires `WindowEvent::Cursor*`, `WindowEvent::MouseInput`, or `DeviceEvent::MouseMotion` like other platforms, but instead it fires `WindowEvent::Touch`.
|
||||
- **Breaking:** Removed platform specific `WindowBuilder::with_parent` API in favor of `WindowBuilder::with_parent_window`.
|
||||
- On Windows, retain `WS_MAXIMIZE` window style when un-minimizing a maximized window.
|
||||
- On Windows, fix left mouse button release event not being sent after `Window::drag_window`.
|
||||
- On macOS, run most actions on the main thread, which is strictly more correct, but might make multithreaded applications block slightly more.
|
||||
- On macOS, fix panic when getting current monitor without any monitor attached.
|
||||
- On Windows and MacOS, add API to enable/disable window buttons (close, minimize, ...etc).
|
||||
- On Windows, macOS, X11 and Wayland, add `Window::set_theme`.
|
||||
- **Breaking:** Remove `WindowExtWayland::wayland_set_csd_theme` and `WindowBuilderExtX11::with_gtk_theme_variant`.
|
||||
- On Windows, revert window background to an empty brush to avoid white flashes when changing scaling.
|
||||
- **Breaking:** Removed `Window::set_always_on_top` and related APIs in favor of `Window::set_window_level`.
|
||||
- On Windows, MacOS and X11, add always on bottom APIs.
|
||||
- On Windows, fix the value in `MouseButton::Other`.
|
||||
- On macOS, add `WindowExtMacOS::is_document_edited` and `WindowExtMacOS::set_document_edited` APIs.
|
||||
- **Breaking:** Removed `WindowBuilderExtIOS::with_root_view_class`; instead, you should use `[[view layer] addSublayer: ...]` to add an instance of the desired layer class (e.g. `CAEAGLLayer` or `CAMetalLayer`). See `vulkano-win` or `wgpu` for examples of this.
|
||||
- On MacOS and Windows, add `Window::set_content_protected`.
|
||||
- On MacOS, add `EventLoopBuilderExtMacOS::with_activate_ignoring_other_apps`.
|
||||
- On Windows, fix icons specified on `WindowBuilder` not taking effect for windows created after the first one.
|
||||
- On Windows and macOS, add `Window::title` to query the current window title.
|
||||
- On Windows, fix focusing menubar when pressing `Alt`.
|
||||
- On MacOS, made `accepts_first_mouse` configurable.
|
||||
- Migrated `WindowBuilderExtUnix::with_resize_increments` to `WindowBuilder`.
|
||||
- Added `Window::resize_increments`/`Window::set_resize_increments` to update resize increments at runtime for X11/macOS.
|
||||
- macOS/iOS: Use `objc2` instead of `objc` internally.
|
||||
- **Breaking:** Bump MSRV from `1.57` to `1.60`.
|
||||
- **Breaking:** Split the `platform::unix` module into `platform::x11` and `platform::wayland`. The extension types are similarly renamed.
|
||||
- **Breaking:**: Removed deprecated method `platform::unix::WindowExtUnix::is_ready`.
|
||||
- Removed `parking_lot` dependency.
|
||||
- **Breaking:** On macOS, add support for two-finger touchpad magnification and rotation gestures with new events `WindowEvent::TouchpadMagnify` and `WindowEvent::TouchpadRotate`. Also add support for touchpad smart-magnification gesture with a new event `WindowEvent::SmartMagnify`.
|
||||
- **Breaking:** On web, the `WindowBuilderExtWebSys::with_prevent_default` setting (enabled by default), now additionally prevents scrolling of the webpage in mobile browsers, previously it only disabled scrolling on desktop.
|
||||
- On Wayland, `wayland-csd-adwaita` now uses `ab_glyph` instead of `crossfont` to render the title for decorations.
|
||||
- On Wayland, a new `wayland-csd-adwaita-crossfont` feature was added to use `crossfont` instead of `ab_glyph` for decorations.
|
||||
- On Wayland, if not otherwise specified use upstream automatic CSD theme selection.
|
||||
- On X11, added `WindowExtX11::with_parent` to create child windows.
|
||||
- Added support for `WindowBuilder::with_theme` and `Window::theme` to support per-window dark/light/system theme configuration on macos, windows and wayland.
|
||||
- On macOS, added support for `WindowEvent::ThemeChanged`.
|
||||
- **Breaking:** Removed `WindowBuilderExtWindows::with_theme` and `WindowBuilderExtWayland::with_wayland_csd_theme` in favour of `WindowBuilder::with_theme`.
|
||||
- **Breaking:** Removed `WindowExtWindows::theme` in favour of `Window::theme`.
|
||||
- Enabled `doc_auto_cfg` when generating docs on docs.rs for feature labels.
|
||||
- **Breaking:** On Android, switched to using [`android-activity`](https://github.com/rib/android-activity) crate as a glue layer instead of [`ndk-glue`](https://github.com/rust-windowing/android-ndk-rs/tree/master/ndk-glue). See [README.md#Android](https://github.com/rust-windowing/winit#Android) for more details. ([#2444](https://github.com/rust-windowing/winit/pull/2444))
|
||||
- **Breaking:** Removed support for `raw-window-handle` version `0.4`
|
||||
- On Wayland, `RedrawRequested` not emitted during resize.
|
||||
- Add a `set_wait_timeout` function to `ControlFlow` to allow waiting for a `Duration`.
|
||||
- **Breaking:** Remove the unstable `xlib_xconnection()` function from the private interface.
|
||||
- Added Orbital support for Redox OS
|
||||
- On X11, added `drag_resize_window` method.
|
||||
- Added `Window::set_transparent` to provide a hint about transparency of the window on Wayland and macOS.
|
||||
- On macOS, fix the mouse buttons other than left/right/middle being reported as middle.
|
||||
- On Wayland, support fractional scaling via the wp-fractional-scale protocol.
|
||||
- On web, fix removal of mouse event listeners from the global object upon window distruction.
|
||||
- Add WindowAttributes getter to WindowBuilder to allow introspection of default values.
|
||||
- Added `Window::set_ime_purpose` for setting the IME purpose, currently implemented on Wayland only.
|
||||
|
||||
# 0.27.5
|
||||
|
||||
- On Wayland, fix byte offset in `Ime::Preedit` pointing to invalid bytes.
|
||||
|
||||
# 0.27.4
|
||||
|
||||
- On Windows, emit `ReceivedCharacter` events on system keybindings.
|
||||
- On Windows, fixed focus event emission on minimize.
|
||||
- On X11, fixed IME crashing during reload.
|
||||
|
||||
# 0.27.3
|
||||
|
||||
- On Windows, added `WindowExtWindows::set_undecorated_shadow` and `WindowBuilderExtWindows::with_undecorated_shadow` to draw the drop shadow behind a borderless window.
|
||||
- On Windows, fixed default window features (ie snap, animations, shake, etc.) when decorations are disabled.
|
||||
- On Windows, fixed ALT+Space shortcut to open window menu.
|
||||
- On Wayland, fixed `Ime::Preedit` not being sent on IME reset.
|
||||
- Fixed unbound version specified for `raw-window-handle` leading to compilation failures.
|
||||
- Empty `Ime::Preedit` event will be sent before `Ime::Commit` to help clearing preedit.
|
||||
- On X11, fixed IME context picking by querying for supported styles beforehand.
|
||||
|
||||
# 0.27.2 (2022-8-12)
|
||||
|
||||
- On macOS, fixed touch phase reporting when scrolling.
|
||||
- On X11, fix min, max and resize increment hints not persisting for resizable windows (e.g. on DPI change).
|
||||
- On Windows, respect min/max inner sizes when creating the window.
|
||||
- For backwards compatibility, `Window` now (additionally) implements the old version (`0.4`) of the `HasRawWindowHandle` trait
|
||||
- On Windows, added support for `EventLoopWindowTarget::set_device_event_filter`.
|
||||
- On Wayland, fix user requested `WindowEvent::RedrawRequested` being delayed by a frame.
|
||||
|
||||
# 0.27.1 (2022-07-30)
|
||||
|
||||
- The minimum supported Rust version was lowered to `1.57.0` and now explicitly tested.
|
||||
- On X11, fix crash on start due to inability to create an IME context without any preedit.
|
||||
|
||||
# 0.27.0 (2022-07-26)
|
||||
|
||||
- On Windows, fix hiding a maximized window.
|
||||
- On Android, `ndk-glue`'s `NativeWindow` lock is now held between `Event::Resumed` and `Event::Suspended`.
|
||||
- On Web, added `EventLoopExtWebSys` with a `spawn` method to start the event loop without throwing an exception.
|
||||
- Added `WindowEvent::Occluded(bool)`, currently implemented on macOS and X11.
|
||||
- On X11, fix events for caps lock key not being sent
|
||||
- Build docs on `docs.rs` for iOS and Android as well.
|
||||
- **Breaking:** Removed the `WindowAttributes` struct, since all its functionality is accessible from `WindowBuilder`.
|
||||
- Added `WindowBuilder::transparent` getter to check if the user set `transparent` attribute.
|
||||
- On macOS, Fix emitting `Event::LoopDestroyed` on CMD+Q.
|
||||
- On macOS, fixed an issue where having multiple windows would prevent run_return from ever returning.
|
||||
- On Wayland, fix bug where the cursor wouldn't hide in GNOME.
|
||||
- On macOS, Windows, and Wayland, add `set_cursor_hittest` to let the window ignore mouse events.
|
||||
- On Windows, added `WindowExtWindows::set_skip_taskbar` and `WindowBuilderExtWindows::with_skip_taskbar`.
|
||||
- On Windows, added `EventLoopBuilderExtWindows::with_msg_hook`.
|
||||
- On Windows, remove internally unique DC per window.
|
||||
- On macOS, remove the need to call `set_ime_position` after moving the window.
|
||||
- Added `Window::is_visible`.
|
||||
- Added `Window::is_resizable`.
|
||||
- Added `Window::is_decorated`.
|
||||
- On X11, fix for repeated event loop iteration when `ControlFlow` was `Wait`
|
||||
- On X11, fix scale factor calculation when the only monitor is reconnected
|
||||
- On Wayland, report unaccelerated mouse deltas in `DeviceEvent::MouseMotion`.
|
||||
- On Web, a focused event is manually generated when a click occurs to emulate behaviour of other backends.
|
||||
- **Breaking:** Bump `ndk` version to 0.6, ndk-sys to `v0.3`, `ndk-glue` to `0.6`.
|
||||
- Remove no longer needed `WINIT_LINK_COLORSYNC` environment variable.
|
||||
- **Breaking:** Rename the `Exit` variant of `ControlFlow` to `ExitWithCode`, which holds a value to control the exit code after running. Add an `Exit` constant which aliases to `ExitWithCode(0)` instead to avoid major breakage. This shouldn't affect most existing programs.
|
||||
- Add `EventLoopBuilder`, which allows you to create and tweak the settings of an event loop before creating it.
|
||||
- Deprecated `EventLoop::with_user_event`; use `EventLoopBuilder::with_user_event` instead.
|
||||
- **Breaking:** Replaced `EventLoopExtMacOS` with `EventLoopBuilderExtMacOS` (which also has renamed methods).
|
||||
- **Breaking:** Replaced `EventLoopExtWindows` with `EventLoopBuilderExtWindows` (which also has renamed methods).
|
||||
- **Breaking:** Replaced `EventLoopExtUnix` with `EventLoopBuilderExtUnix` (which also has renamed methods).
|
||||
- **Breaking:** The platform specific extensions for Windows `winit::platform::windows` have changed. All `HANDLE`-like types e.g. `HWND` and `HMENU` were converted from winapi types or `*mut c_void` to `isize`. This was done to be consistent with the type definitions in windows-sys and to not expose internal dependencies.
|
||||
- The internal bindings to the [Windows API](https://docs.microsoft.com/en-us/windows/) were changed from the unofficial [winapi](https://github.com/retep998/winapi-rs) bindings to the official Microsoft [windows-sys](https://github.com/microsoft/windows-rs) bindings.
|
||||
- On Wayland, fix polling during consecutive `EventLoop::run_return` invocations.
|
||||
- On Windows, fix race issue creating fullscreen windows with `WindowBuilder::with_fullscreen`
|
||||
- On Android, `virtual_keycode` for `KeyboardInput` events is now filled in where a suitable match is found.
|
||||
- Added helper methods on `ControlFlow` to set its value.
|
||||
- On Wayland, fix `TouchPhase::Ended` always reporting the location of the first touch down, unless the compositor
|
||||
sent a cancel or frame event.
|
||||
- On iOS, send `RedrawEventsCleared` even if there are no redraw events, consistent with other platforms.
|
||||
- **Breaking:** Replaced `Window::with_app_id` and `Window::with_class` with `Window::with_name` on `WindowBuilderExtUnix`.
|
||||
- On Wayland, fallback CSD was replaced with proper one:
|
||||
- `WindowBuilderExtUnix::with_wayland_csd_theme` to set color theme in builder.
|
||||
- `WindowExtUnix::wayland_set_csd_theme` to set color theme when creating a window.
|
||||
- `WINIT_WAYLAND_CSD_THEME` env variable was added, it can be used to set "dark"/"light" theme in apps that don't expose theme setting.
|
||||
- `wayland-csd-adwaita` feature that enables proper CSD with title rendering using FreeType system library.
|
||||
- `wayland-csd-adwaita-notitle` feature that enables CSD but without title rendering.
|
||||
- On Wayland and X11, fix window not resizing with `Window::set_inner_size` after calling `Window:set_resizable(false)`.
|
||||
- On Windows, fix wrong fullscreen monitors being recognized when handling WM_WINDOWPOSCHANGING messages
|
||||
- **Breaking:** Added new `WindowEvent::Ime` supported on desktop platforms.
|
||||
- Added `Window::set_ime_allowed` supported on desktop platforms.
|
||||
- **Breaking:** IME input on desktop platforms won't be received unless it's explicitly allowed via `Window::set_ime_allowed` and new `WindowEvent::Ime` events are handled.
|
||||
- On macOS, `WindowEvent::Resized` is now emitted in `frameDidChange` instead of `windowDidResize`.
|
||||
- **Breaking:** On X11, device events are now ignored for unfocused windows by default, use `EventLoopWindowTarget::set_device_event_filter` to set the filter level.
|
||||
- Implemented `Default` on `EventLoop<()>`.
|
||||
- Implemented `Eq` for `Fullscreen`, `Theme`, and `UserAttentionType`.
|
||||
- **Breaking:** `Window::set_cursor_grab` now accepts `CursorGrabMode` to control grabbing behavior.
|
||||
- On Wayland, add support for `Window::set_cursor_position`.
|
||||
- Fix on macOS `WindowBuilder::with_disallow_hidpi`, setting true or false by the user no matter the SO default value.
|
||||
- `EventLoopBuilder::build` will now panic when the `EventLoop` is being created more than once.
|
||||
- Added `From<u64>` for `WindowId` and `From<WindowId>` for `u64`.
|
||||
- Added `MonitorHandle::refresh_rate_millihertz` to get monitor's refresh rate.
|
||||
- **Breaking**, Replaced `VideoMode::refresh_rate` with `VideoMode::refresh_rate_millihertz` providing better precision.
|
||||
- On Web, add `with_prevent_default` and `with_focusable` to `WindowBuilderExtWebSys` to control whether events should be propagated.
|
||||
- On Windows, fix focus events being sent to inactive windows.
|
||||
- **Breaking**, update `raw-window-handle` to `v0.5` and implement `HasRawDisplayHandle` for `Window` and `EventLoopWindowTarget`.
|
||||
- On X11, add function `register_xlib_error_hook` into `winit::platform::unix` to subscribe for errors comming from Xlib.
|
||||
- On Android, upgrade `ndk` and `ndk-glue` dependencies to the recently released `0.7.0`.
|
||||
- All platforms can now be relied on to emit a `Resumed` event. Applications are recommended to lazily initialize graphics state and windows on first resume for portability.
|
||||
- **Breaking:**: Reverse horizontal scrolling sign in `MouseScrollDelta` to match the direction of vertical scrolling. A positive X value now means moving the content to the right. The meaning of vertical scrolling stays the same: a positive Y value means moving the content down.
|
||||
- On MacOS, fix deadlock when calling `set_maximized` from event loop.
|
||||
|
||||
# 0.26.1 (2022-01-05)
|
||||
|
||||
- Fix linking to the `ColorSync` framework on macOS 10.7, and in newer Rust versions.
|
||||
- On Web, implement cursor grabbing through the pointer lock API.
|
||||
- On X11, add mappings for numpad comma, numpad enter, numlock and pause.
|
||||
- On macOS, fix Pinyin IME input by reverting a change that intended to improve IME.
|
||||
- On Windows, fix a crash with transparent windows on Windows 11.
|
||||
|
||||
# 0.26.0 (2021-12-01)
|
||||
|
||||
- Update `raw-window-handle` to `v0.4`. This is _not_ a breaking change, we still implement `HasRawWindowHandle` from `v0.3`, see [rust-windowing/raw-window-handle#74](https://github.com/rust-windowing/raw-window-handle/pull/74). Note that you might have to run `cargo update -p raw-window-handle` after upgrading.
|
||||
- On X11, bump `mio` to 0.8.
|
||||
- On Android, fixed `WindowExtAndroid::config` initially returning an empty `Configuration`.
|
||||
- On Android, fixed `Window::scale_factor` and `MonitorHandle::scale_factor` initially always returning 1.0.
|
||||
- On X11, select an appropriate visual for transparency if is requested
|
||||
- On Wayland and X11, fix diagonal window resize cursor orientation.
|
||||
- On macOS, drop the event callback before exiting.
|
||||
- On Android, implement `Window::request_redraw`
|
||||
- **Breaking:** On Web, remove the `stdweb` backend.
|
||||
- Added `Window::focus_window`to bring the window to the front and set input focus.
|
||||
- On Wayland and X11, implement `is_maximized` method on `Window`.
|
||||
- On Windows, prevent ghost window from showing up in the taskbar after either several hours of use or restarting `explorer.exe`.
|
||||
- On macOS, fix issue where `ReceivedCharacter` was not being emitted during some key repeat events.
|
||||
- On Wayland, load cursor icons `hand2` and `hand1` for `CursorIcon::Hand`.
|
||||
- **Breaking:** On Wayland, Theme trait and its support types are dropped.
|
||||
- On Wayland, bump `smithay-client-toolkit` to 0.15.1.
|
||||
- On Wayland, implement `request_user_attention` with `xdg_activation_v1`.
|
||||
- On X11, emit missing `WindowEvent::ScaleFactorChanged` when the only monitor gets reconnected.
|
||||
- On X11, if RANDR based scale factor is higher than 20 reset it to 1
|
||||
- On Wayland, add an enabled-by-default feature called `wayland-dlopen` so users can opt out of using `dlopen` to load system libraries.
|
||||
- **Breaking:** On Android, bump `ndk` and `ndk-glue` to 0.5.
|
||||
- On Windows, increase wait timer resolution for more accurate timing when using `WaitUntil`.
|
||||
- On macOS, fix native file dialogs hanging the event loop.
|
||||
- On Wayland, implement a workaround for wrong configure size when using `xdg_decoration` in `kwin_wayland`
|
||||
- On macOS, fix an issue that prevented the menu bar from showing in borderless fullscreen mode.
|
||||
- On X11, EINTR while polling for events no longer causes a panic. Instead it will be treated as a spurious wakeup.
|
||||
|
||||
# 0.25.0 (2021-05-15)
|
||||
|
||||
- **Breaking:** On macOS, replace `WindowBuilderExtMacOS::with_activation_policy` with `EventLoopExtMacOS::set_activation_policy`
|
||||
- On macOS, wait with activating the application until the application has initialized.
|
||||
- On macOS, fix creating new windows when the application has a main menu.
|
||||
- On Windows, fix fractional deltas for mouse wheel device events.
|
||||
- On macOS, fix segmentation fault after dropping the main window.
|
||||
- On Android, `InputEvent::KeyEvent` is partially implemented providing the key scancode.
|
||||
- Added `is_maximized` method to `Window`.
|
||||
- On Windows, fix bug where clicking the decoration bar would make the cursor blink.
|
||||
- On Windows, fix bug causing newly created windows to erroneously display the "wait" (spinning) cursor.
|
||||
- On macOS, wake up the event loop immediately when a redraw is requested.
|
||||
- On Windows, change the default window size (1024x768) to match the default on other desktop platforms (800x600).
|
||||
- On Windows, fix bug causing mouse capture to not be released.
|
||||
- On Windows, fix fullscreen not preserving minimized/maximized state.
|
||||
- On Android, unimplemented events are marked as unhandled on the native event loop.
|
||||
- On Windows, added `WindowBuilderExtWindows::with_menu` to set a custom menu at window creation time.
|
||||
- On Android, bump `ndk` and `ndk-glue` to 0.3: use predefined constants for event `ident`.
|
||||
- On macOS, fix objects captured by the event loop closure not being dropped on panic.
|
||||
- On Windows, fixed `WindowEvent::ThemeChanged` not properly firing and fixed `Window::theme` returning the wrong theme.
|
||||
- On Web, added support for `DeviceEvent::MouseMotion` to listen for relative mouse movements.
|
||||
- Added `WindowBuilder::with_position` to allow setting the position of a `Window` on creation. Supported on Windows, macOS and X11.
|
||||
- Added `Window::drag_window`. Implemented on Windows, macOS, X11 and Wayland.
|
||||
- On X11, bump `mio` to 0.7.
|
||||
- On Windows, added `WindowBuilderExtWindows::with_owner_window` to allow creating popup windows.
|
||||
- On Windows, added `WindowExtWindows::set_enable` to allow creating modal popup windows.
|
||||
- On macOS, emit `RedrawRequested` events immediately while the window is being resized.
|
||||
- Implement `Default`, `Hash`, and `Eq` for `LogicalPosition`, `PhysicalPosition`, `LogicalSize`, and `PhysicalSize`.
|
||||
- On macOS, initialize the Menu Bar with minimal defaults. (Can be prevented using `enable_default_menu_creation`)
|
||||
- On macOS, change the default behavior for first click when the window was unfocused. Now the window becomes focused and then emits a `MouseInput` event on a "first mouse click".
|
||||
- Implement mint (math interoperability standard types) conversions (under feature flag `mint`).
|
||||
|
||||
# 0.24.0 (2020-12-09)
|
||||
|
||||
- On Windows, fix applications not exiting gracefully due to thread_event_target_callback accessing corrupted memory.
|
||||
- On Windows, implement `Window::set_ime_position`.
|
||||
- **Breaking:** On Windows, Renamed `WindowBuilderExtWindows`'s `is_dark_mode` to `theme`.
|
||||
- **Breaking:** On Windows, renamed `WindowBuilderExtWindows::is_dark_mode` to `theme`.
|
||||
- On Windows, add `WindowBuilderExtWindows::with_theme` to set a preferred theme.
|
||||
- On Windows, fix bug causing message boxes to appear delayed.
|
||||
- On Android, calling `WindowEvent::Focused` now works properly instead of always returning false.
|
||||
- On Windows, fix Alt-Tab behaviour by removing borderless fullscreen "always on top" flag.
|
||||
- On Windows, fix bug preventing windows with transparency enabled from having fully-opaque regions.
|
||||
- **Breaking:** On Windows, include prefix byte in scancodes.
|
||||
- On Wayland, fix window not being resizeable when using `WindowBuilder::with_min_inner_size`.
|
||||
- On Unix, fix cross-compiling to wasm32 without enabling X11 or Wayland.
|
||||
- On Windows, fix use-after-free crash during window destruction.
|
||||
- On Web, fix `WindowEvent::ReceivedCharacter` never being sent on key input.
|
||||
- On macOS, fix compilation when targeting aarch64.
|
||||
- On X11, fix `Window::request_redraw` not waking the event loop.
|
||||
- On Wayland, the keypad arrow keys are now recognized.
|
||||
- **Breaking** Rename `desktop::EventLoopExtDesktop` to `run_return::EventLoopExtRunReturn`.
|
||||
- Added `request_user_attention` method to `Window`.
|
||||
- **Breaking:** On macOS, removed `WindowExt::request_user_attention`, use `Window::request_user_attention`.
|
||||
- **Breaking:** On X11, removed `WindowExt::set_urgent`, use `Window::request_user_attention`.
|
||||
- On Wayland, default font size in CSD increased from 11 to 17.
|
||||
- On Windows, fix bug causing message boxes to appear delayed.
|
||||
- On Android, support multi-touch.
|
||||
- On Wayland, extra mouse buttons are not dropped anymore.
|
||||
- **Breaking**: `MouseButton::Other` now uses `u16`.
|
||||
|
||||
# 0.23.0 (2020-10-02)
|
||||
|
||||
- On iOS, fixed support for the "Debug View Hierarchy" feature in Xcode.
|
||||
- On all platforms, `available_monitors` and `primary_monitor` are now on `EventLoopWindowTarget` rather than `EventLoop` to list monitors event in the event loop.
|
||||
- On Unix, X11 and Wayland are now optional features (enabled by default)
|
||||
- On X11, fix deadlock when calling `set_fullscreen_inner`.
|
||||
- On Web, prevent the webpage from scrolling when the user is focused on a winit canvas
|
||||
- On Web, calling `window.set_cursor_icon` no longer breaks HiDPI scaling
|
||||
- On Windows, drag and drop is now optional (enabled by default) and can be disabled with `WindowBuilderExtWindows::with_drag_and_drop(false)`.
|
||||
- On Wayland, fix deadlock when calling to `set_inner_size` from a callback.
|
||||
- On macOS, add `hide__other_applications` to `EventLoopWindowTarget` via existing `EventLoopWindowTargetExtMacOS` trait. `hide_other_applications` will hide other applications by calling `-[NSApplication hideOtherApplications: nil]`.
|
||||
- On android added support for `run_return`.
|
||||
- On MacOS, Fixed fullscreen and dialog support for `run_return`.
|
||||
- On Windows, fix bug where we'd try to emit `MainEventsCleared` events during nested win32 event loops.
|
||||
- On Web, use mouse events if pointer events aren't supported. This affects Safari.
|
||||
- On Windows, `set_ime_position` is now a no-op instead of a runtime crash.
|
||||
- On Android, `set_fullscreen` is now a no-op instead of a runtime crash.
|
||||
- On iOS and Android, `set_inner_size` is now a no-op instead of a runtime crash.
|
||||
- On Android, fix `ControlFlow::Poll` not polling the Android event queue.
|
||||
- On macOS, add `NSWindow.hasShadow` support.
|
||||
- On Web, fix vertical mouse wheel scrolling being inverted.
|
||||
- On Web, implement mouse capturing for click-dragging out of the canvas.
|
||||
- On Web, fix `ControlFlow::Exit` not properly handled.
|
||||
- On Web (web-sys only), send `WindowEvent::ScaleFactorChanged` event when `window.devicePixelRatio` is changed.
|
||||
- **Breaking:** On Web, `set_cursor_position` and `set_cursor_grab` will now always return an error.
|
||||
- **Breaking:** `PixelDelta` scroll events now return a `PhysicalPosition`.
|
||||
- On NetBSD, fixed crash due to incorrect detection of the main thread.
|
||||
- **Breaking:** On X11, `-` key is mapped to the `Minus` virtual key code, instead of `Subtract`.
|
||||
- On macOS, fix inverted horizontal scroll.
|
||||
- **Breaking:** `current_monitor` now returns `Option<MonitorHandle>`.
|
||||
- **Breaking:** `primary_monitor` now returns `Option<MonitorHandle>`.
|
||||
- On macOS, updated core-* dependencies and cocoa.
|
||||
- Bump `parking_lot` to 0.11
|
||||
- On Android, bump `ndk`, `ndk-sys` and `ndk-glue` to 0.2. Checkout the new ndk-glue main proc attribute.
|
||||
- On iOS, fixed starting the app in landscape where the view still had portrait dimensions.
|
||||
- Deprecate the stdweb backend, to be removed in a future release
|
||||
- **Breaking:** Prefixed virtual key codes `Add`, `Multiply`, `Divide`, `Decimal`, and `Subtract` with `Numpad`.
|
||||
- Added `Asterisk` and `Plus` virtual key codes.
|
||||
- On Web (web-sys only), the `Event::LoopDestroyed` event is correctly emitted when leaving the page.
|
||||
- On Web, the `WindowEvent::Destroyed` event now gets emitted when a `Window` is dropped.
|
||||
- On Web (web-sys only), the event listeners are now removed when a `Window` is dropped or when the event loop is destroyed.
|
||||
- On Web, the event handler closure passed to `EventLoop::run` now gets dropped after the event loop is destroyed.
|
||||
- **Breaking:** On Web, the canvas element associated to a `Window` is no longer removed from the DOM when the `Window` is dropped.
|
||||
- On Web, `WindowEvent::Resized` is now emitted when `Window::set_inner_size` is called.
|
||||
- **Breaking:** `Fullscreen` enum now uses `Borderless(Option<MonitorHandle>)` instead of `Borderless(MonitorHandle)` to allow picking the current monitor.
|
||||
- On MacOS, fix `WindowEvent::Moved` ignoring the scale factor.
|
||||
- On Wayland, add missing virtual keycodes.
|
||||
- On Wayland, implement proper `set_cursor_grab`.
|
||||
- On Wayland, the cursor will use similar icons if the requested one isn't available.
|
||||
- On Wayland, right clicking on client side decorations will request application menu.
|
||||
- On Wayland, fix tracking of window size after state changes.
|
||||
- On Wayland, fix client side decorations not being hidden properly in fullscreen.
|
||||
- On Wayland, fix incorrect size event when entering fullscreen with client side decorations.
|
||||
- On Wayland, fix `resizable` attribute not being applied properly on startup.
|
||||
- On Wayland, fix disabled repeat rate not being handled.
|
||||
- On Wayland, fix decoration buttons not working after tty switch.
|
||||
- On Wayland, fix scaling not being applied on output re-enable.
|
||||
- On Wayland, fix crash when `XCURSOR_SIZE` is `0`.
|
||||
- On Wayland, fix pointer getting created in some cases without pointer capability.
|
||||
- On Wayland, on kwin, fix space between window and decorations on startup.
|
||||
- **Breaking:** On Wayland, `Theme` trait was reworked.
|
||||
- On Wayland, disable maximize button for non-resizable window.
|
||||
- On Wayland, added support for `set_ime_position`.
|
||||
- On Wayland, fix crash on startup since GNOME 3.37.90.
|
||||
- On X11, fix incorrect modifiers state on startup.
|
||||
|
||||
# 0.22.2 (2020-05-16)
|
||||
|
||||
- Added Clone implementation for 'static events.
|
||||
- On Windows, fix window intermittently hanging when `ControlFlow` was set to `Poll`.
|
||||
- On Windows, fix `WindowBuilder::with_maximized` being ignored.
|
||||
- On Android, minimal platform support.
|
||||
- On iOS, touch positions are now properly converted to physical pixels.
|
||||
- On macOS, updated core-* dependencies and cocoa
|
||||
|
||||
# 0.22.1 (2020-04-16)
|
||||
|
||||
- On X11, fix `ResumeTimeReached` being fired too early.
|
||||
- On Web, replaced zero timeout for `ControlFlow::Poll` with `requestAnimationFrame`
|
||||
- On Web, fix a possible panic during event handling
|
||||
- On macOS, fix `EventLoopProxy` leaking memory for every instance.
|
||||
|
||||
# 0.22.0 (2020-03-09)
|
||||
|
||||
- On Windows, fix minor timing issue in wait_until_time_or_msg
|
||||
- On Windows, rework handling of request_redraw() to address panics.
|
||||
@@ -18,6 +496,7 @@
|
||||
- Revert On macOS, fix not sending ReceivedCharacter event for specific keys combinations.
|
||||
- on macOS, fix incorrect ReceivedCharacter events for some key combinations.
|
||||
- **Breaking:** Use `i32` instead of `u32` for position type in `WindowEvent::Moved`.
|
||||
- On macOS, a mouse motion event is now generated before every mouse click.
|
||||
|
||||
# 0.21.0 (2020-02-04)
|
||||
|
||||
@@ -40,13 +519,13 @@
|
||||
|
||||
- On X11, fix `ModifiersChanged` emitting incorrect modifier change events
|
||||
- **Breaking**: Overhaul how Winit handles DPI:
|
||||
+ Window functions and events now return `PhysicalSize` instead of `LogicalSize`.
|
||||
+ Functions that take `Size` or `Position` types can now take either `Logical` or `Physical` types.
|
||||
+ `hidpi_factor` has been renamed to `scale_factor`.
|
||||
+ `HiDpiFactorChanged` has been renamed to `ScaleFactorChanged`, and lets you control how the OS
|
||||
- Window functions and events now return `PhysicalSize` instead of `LogicalSize`.
|
||||
- Functions that take `Size` or `Position` types can now take either `Logical` or `Physical` types.
|
||||
- `hidpi_factor` has been renamed to `scale_factor`.
|
||||
- `HiDpiFactorChanged` has been renamed to `ScaleFactorChanged`, and lets you control how the OS
|
||||
resizes the window in response to the change.
|
||||
+ On X11, deprecate `WINIT_HIDPI_FACTOR` environment variable in favor of `WINIT_X11_SCALE_FACTOR`.
|
||||
+ `Size` and `Position` types are now generic over their exact pixel type.
|
||||
- On X11, deprecate `WINIT_HIDPI_FACTOR` environment variable in favor of `WINIT_X11_SCALE_FACTOR`.
|
||||
- `Size` and `Position` types are now generic over their exact pixel type.
|
||||
|
||||
# 0.20.0 Alpha 6 (2020-01-03)
|
||||
|
||||
@@ -151,7 +630,7 @@
|
||||
- `Window::set_fullscreen` now takes `Option<Fullscreen>` where `Fullscreen`
|
||||
consists of `Fullscreen::Exclusive(VideoMode)` and
|
||||
`Fullscreen::Borderless(MonitorHandle)` variants.
|
||||
- Adds support for exclusive fullscreen mode.
|
||||
- Adds support for exclusive fullscreen mode.
|
||||
- On iOS, add support for hiding the home indicator.
|
||||
- On iOS, add support for deferring system gestures.
|
||||
- On iOS, fix a crash that occurred while acquiring a monitor's name.
|
||||
@@ -350,7 +829,7 @@ and `WindowEvent::HoveredFile`.
|
||||
# Version 0.16.1 (2018-07-02)
|
||||
|
||||
- Added logging through `log`. Logging will become more extensive over time.
|
||||
- On X11 and Windows, the window's DPI factor is guessed before creating the window. This *greatly* cuts back on unsightly auto-resizing that would occur immediately after window creation.
|
||||
- On X11 and Windows, the window's DPI factor is guessed before creating the window. This _greatly_ cuts back on unsightly auto-resizing that would occur immediately after window creation.
|
||||
- Fixed X11 backend compilation for environments where `c_char` is unsigned.
|
||||
|
||||
# Version 0.16.0 (2018-06-25)
|
||||
@@ -500,7 +979,7 @@ and `WindowEvent::HoveredFile`.
|
||||
|
||||
# Version 0.10.1 (2018-02-05)
|
||||
|
||||
*Yanked*
|
||||
_Yanked_
|
||||
|
||||
# Version 0.10.0 (2017-12-27)
|
||||
|
||||
|
||||
@@ -20,12 +20,16 @@ your description of the issue as detailed as possible:
|
||||
|
||||
When making a code contribution to winit, before opening your pull request, please make sure that:
|
||||
|
||||
- your patch builds with Winit's minimal supported rust version - Rust 1.64.
|
||||
- you tested your modifications on all the platforms impacted, or if not possible detail which platforms
|
||||
were not tested, and what should be tested, so that a maintainer or another contributor can test them
|
||||
- you updated any relevant documentation in winit
|
||||
- you left comments in your code explaining any part that is not straightforward, so that the
|
||||
maintainers and future contributors don't have to try to guess what your code is supposed to do
|
||||
- your PR adds an entry to the changelog file if the introduced change is relevant to winit users
|
||||
- your PR adds an entry to the changelog file if the introduced change is relevant to winit users.
|
||||
|
||||
You needn't worry about the added entry causing conflicts, the maintainer that merges the PR will
|
||||
handle those for you when merging (see below).
|
||||
- if your PR affects the platform compatibility of one or more features or adds another feature, the
|
||||
relevant sections in [`FEATURES.md`](https://github.com/rust-windowing/winit/blob/master/FEATURES.md#features)
|
||||
should be updated.
|
||||
@@ -34,9 +38,35 @@ Once your PR is open, you can ask for review by a maintainer of your platform. W
|
||||
is that a PR must be approved by at least two maintainers of winit before being merged, including
|
||||
at least a maintainer of the platform (a maintainer making a PR themselves counts as approving it).
|
||||
|
||||
Once your PR is deemed ready, the merging maintainer will take care of resolving conflicts in
|
||||
`CHANGELOG.md` (but you must resolve other conflicts yourself). Doing this requires that you check the
|
||||
"give contributors write access to the branch" checkbox when creating the PR.
|
||||
|
||||
## Maintainers & Testers
|
||||
|
||||
The current [list of testers and contributors](https://github.com/rust-windowing/winit/wiki/Testers-and-Contributors)
|
||||
can be found on the Wiki.
|
||||
The current maintainers are listed in the [CODEOWNERS](.github/CODEOWNERS) file.
|
||||
|
||||
If you are interested in contributing or testing on a platform, please add yourself to that table!
|
||||
If you are interested in being pinged when testing is needed for a certain platform, please add yourself to the [Testers and Contributors](https://github.com/rust-windowing/winit/wiki/Testers-and-Contributors) table!
|
||||
|
||||
## Release process
|
||||
|
||||
Given that winit is a widely used library we should be able to make a patch
|
||||
releases at any time we want without blocking the development of new features.
|
||||
|
||||
To achieve these goals, a new branch is created for every new release. Releases
|
||||
and later patch releases are committed and tagged in this branch.
|
||||
|
||||
The exact steps for an exemplary `0.2.0` release might look like this:
|
||||
1. Initially the version on the latest master is `0.1.0`
|
||||
2. A new `v0.2.x` branch is created for the release
|
||||
3. In the branch, the version is bumped to `v0.2.0`
|
||||
4. The new commit in the branch is tagged `v0.2.0`
|
||||
5. The version is pushed to crates.io
|
||||
6. A GitHub release is created for the `v0.2.0` tag
|
||||
7. On master, the version is bumped to `0.2.0` and the CHANGELOG is updated
|
||||
|
||||
When doing a patch release the process is similar:
|
||||
1. Initially the version of the latest release is `0.2.0`
|
||||
2. Checkout the `v0.2.x` branch
|
||||
3. Cherry-pick the required non-breaking changes into the `v0.2.x`
|
||||
4. Follow steps 3-7 of the regular release example
|
||||
|
||||
190
Cargo.toml
190
Cargo.toml
@@ -1,99 +1,144 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.22.0"
|
||||
version = "0.29.0-beta.0"
|
||||
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
description = "Cross-platform window creation library."
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
keywords = ["windowing"]
|
||||
license = "Apache-2.0"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/rust-windowing/winit"
|
||||
documentation = "https://docs.rs/winit"
|
||||
categories = ["gui"]
|
||||
rust-version = "1.64.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["serde"]
|
||||
default-target = "x86_64-unknown-linux-gnu"
|
||||
# These are all tested in CI
|
||||
targets = [
|
||||
# Windows
|
||||
"i686-pc-windows-msvc",
|
||||
"x86_64-pc-windows-msvc",
|
||||
# macOS
|
||||
"x86_64-apple-darwin",
|
||||
# Unix (X11 & Wayland)
|
||||
"i686-unknown-linux-gnu",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
# iOS
|
||||
"x86_64-apple-ios",
|
||||
# Android
|
||||
"aarch64-linux-android",
|
||||
# WebAssembly
|
||||
"wasm32-unknown-unknown",
|
||||
]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[features]
|
||||
web-sys = ["web_sys", "wasm-bindgen", "instant/wasm-bindgen"]
|
||||
stdweb = ["std_web", "instant/stdweb"]
|
||||
default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
|
||||
x11 = ["x11-dl", "percent-encoding", "xkbcommon-dl/x11"]
|
||||
wayland = ["wayland-client", "wayland-backend", "wayland-protocols", "sctk", "fnv", "memmap2"]
|
||||
wayland-dlopen = ["wayland-backend/dlopen"]
|
||||
wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"]
|
||||
wayland-csd-adwaita-crossfont = ["sctk-adwaita", "sctk-adwaita/crossfont"]
|
||||
wayland-csd-adwaita-notitle = ["sctk-adwaita"]
|
||||
android-native-activity = ["android-activity/native-activity"]
|
||||
android-game-activity = ["android-activity/game-activity"]
|
||||
serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde"]
|
||||
|
||||
[build-dependencies]
|
||||
cfg_aliases = "0.1.1"
|
||||
|
||||
[dependencies]
|
||||
instant = "0.1"
|
||||
lazy_static = "1"
|
||||
libc = "0.2.64"
|
||||
bitflags = "2"
|
||||
cursor-icon = "1.0.0"
|
||||
log = "0.4"
|
||||
mint = { version = "0.5.6", optional = true }
|
||||
once_cell = "1.12"
|
||||
raw_window_handle = { package = "raw-window-handle", version = "0.5" }
|
||||
serde = { version = "1", optional = true, features = ["serde_derive"] }
|
||||
raw-window-handle = "0.3"
|
||||
bitflags = "1"
|
||||
smol_str = "0.2.0"
|
||||
|
||||
[dev-dependencies]
|
||||
image = "0.23"
|
||||
simple_logger = "1"
|
||||
image = { version = "0.24.0", default-features = false, features = ["png"] }
|
||||
simple_logger = { version = "2.1.0", default_features = false }
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies.android_glue]
|
||||
version = "0.2"
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies]
|
||||
softbuffer = "0.3.0"
|
||||
|
||||
[target.'cfg(target_os = "ios")'.dependencies]
|
||||
objc = "0.2.3"
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
# Coordinate the next winit release with android-ndk-rs: https://github.com/rust-windowing/winit/issues/1995
|
||||
android-activity = "0.4.0"
|
||||
ndk = "0.7.0"
|
||||
ndk-sys = "0.4.0"
|
||||
|
||||
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
|
||||
core-foundation = "0.9.3"
|
||||
objc2 = ">=0.3.0-beta.3, <0.3.0-beta.4" # Allow `0.3.0-beta.3.patch-leaks`
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
cocoa = "0.19.1"
|
||||
core-foundation = "0.6"
|
||||
core-graphics = "0.17.3"
|
||||
core-graphics = "0.22.3"
|
||||
dispatch = "0.2.0"
|
||||
objc = "0.2.6"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies.core-video-sys]
|
||||
version = "0.1.3"
|
||||
default_features = false
|
||||
features = ["display_link"]
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
unicode-segmentation = "1.7.1"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies.winapi]
|
||||
version = "0.3.6"
|
||||
[target.'cfg(target_os = "windows")'.dependencies.windows-sys]
|
||||
version = "0.48"
|
||||
features = [
|
||||
"combaseapi",
|
||||
"commctrl",
|
||||
"dwmapi",
|
||||
"errhandlingapi",
|
||||
"hidusage",
|
||||
"libloaderapi",
|
||||
"objbase",
|
||||
"ole2",
|
||||
"processthreadsapi",
|
||||
"shellapi",
|
||||
"shellscalingapi",
|
||||
"shobjidl_core",
|
||||
"unknwnbase",
|
||||
"winbase",
|
||||
"windowsx",
|
||||
"winerror",
|
||||
"wingdi",
|
||||
"winnt",
|
||||
"winuser",
|
||||
"Win32_Devices_HumanInterfaceDevice",
|
||||
"Win32_Foundation",
|
||||
"Win32_Globalization",
|
||||
"Win32_Graphics_Dwm",
|
||||
"Win32_Graphics_Gdi",
|
||||
"Win32_Media",
|
||||
"Win32_System_Com_StructuredStorage",
|
||||
"Win32_System_Com",
|
||||
"Win32_System_LibraryLoader",
|
||||
"Win32_System_Ole",
|
||||
"Win32_System_SystemInformation",
|
||||
"Win32_System_SystemServices",
|
||||
"Win32_System_Threading",
|
||||
"Win32_System_WindowsProgramming",
|
||||
"Win32_UI_Accessibility",
|
||||
"Win32_UI_Controls",
|
||||
"Win32_UI_HiDpi",
|
||||
"Win32_UI_Input_Ime",
|
||||
"Win32_UI_Input_KeyboardAndMouse",
|
||||
"Win32_UI_Input_Pointer",
|
||||
"Win32_UI_Input_Touch",
|
||||
"Win32_UI_Shell",
|
||||
"Win32_UI_TextServices",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
]
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies]
|
||||
wayland-client = { version = "0.23.0", features = [ "dlopen", "egl", "cursor", "eventloop"] }
|
||||
mio = "0.6"
|
||||
mio-extras = "2.0"
|
||||
smithay-client-toolkit = "^0.6.6"
|
||||
x11-dl = "2.18.3"
|
||||
percent-encoding = "2.0"
|
||||
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_os = "ios", target_os = "macos"))))'.dependencies]
|
||||
libc = "0.2.64"
|
||||
percent-encoding = { version = "2.0", optional = true }
|
||||
fnv = { version = "1.0.3", optional = true }
|
||||
sctk = { package = "smithay-client-toolkit", version = "0.17.0", default-features = false, features = ["calloop"], optional = true }
|
||||
sctk-adwaita = { version = "0.6.0", default_features = false, optional = true }
|
||||
wayland-client = { version = "0.30.0", optional = true }
|
||||
wayland-backend = { version = "0.1.0", default_features = false, features = ["client_system"], optional = true }
|
||||
wayland-protocols = { version = "0.30.0", features = [ "staging"], optional = true }
|
||||
calloop = "0.10.5"
|
||||
x11-dl = { version = "2.18.5", optional = true }
|
||||
xkbcommon-dl = "0.4.0"
|
||||
memmap2 = { version = "0.5.0", optional = true }
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "windows"))'.dependencies.parking_lot]
|
||||
version = "0.10"
|
||||
[target.'cfg(target_os = "redox")'.dependencies]
|
||||
orbclient = { version = "0.3.42", default-features = false }
|
||||
redox_syscall = "0.3"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies.web_sys]
|
||||
[target.'cfg(target_family = "wasm")'.dependencies.web_sys]
|
||||
package = "web-sys"
|
||||
version = "0.3.22"
|
||||
optional = true
|
||||
version = "0.3.64"
|
||||
features = [
|
||||
'console',
|
||||
'CssStyleDeclaration',
|
||||
'BeforeUnloadEvent',
|
||||
'Document',
|
||||
'DomRect',
|
||||
'DomRectReadOnly',
|
||||
'Element',
|
||||
'Event',
|
||||
'EventTarget',
|
||||
@@ -102,23 +147,30 @@ features = [
|
||||
'HtmlElement',
|
||||
'KeyboardEvent',
|
||||
'MediaQueryList',
|
||||
'MediaQueryListEvent',
|
||||
'MouseEvent',
|
||||
'Node',
|
||||
'PageTransitionEvent',
|
||||
'PointerEvent',
|
||||
'ResizeObserver',
|
||||
'ResizeObserverBoxOptions',
|
||||
'ResizeObserverEntry',
|
||||
'ResizeObserverOptions',
|
||||
'ResizeObserverSize',
|
||||
'Window',
|
||||
'WheelEvent'
|
||||
]
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies.wasm-bindgen]
|
||||
version = "0.2.45"
|
||||
optional = true
|
||||
[target.'cfg(target_family = "wasm")'.dependencies]
|
||||
atomic-waker = "1"
|
||||
js-sys = "0.3.64"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
web-time = "0.2"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies.std_web]
|
||||
package = "stdweb"
|
||||
version = "=0.4.20"
|
||||
optional = true
|
||||
features = ["experimental_features_which_may_break_on_minor_version_bumps"]
|
||||
[target.'cfg(target_family = "wasm")'.dev-dependencies]
|
||||
console_log = "1"
|
||||
web-sys = { version = "0.3.22", features = ['CanvasRenderingContext2d'] }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
||||
console_log = "0.1"
|
||||
[workspace]
|
||||
members = [
|
||||
"run-wasm",
|
||||
]
|
||||
|
||||
114
FEATURES.md
114
FEATURES.md
@@ -1,13 +1,14 @@
|
||||
# Winit Scope
|
||||
|
||||
Winit aims to expose an interface that abstracts over window creation and input handling, and can
|
||||
be used to create both games and applications. It supports the main graphical platforms:
|
||||
be used to create both games and applications. It supports the following main graphical platforms:
|
||||
- Desktop
|
||||
- Windows
|
||||
- macOS
|
||||
- Windows 7+ (10+ is tested regularly)
|
||||
- macOS 10.7+ (10.14+ is tested regularly)
|
||||
- Unix
|
||||
- via X11
|
||||
- via Wayland
|
||||
- Redox OS, via Orbital
|
||||
- Mobile
|
||||
- iOS
|
||||
- Android
|
||||
@@ -100,8 +101,10 @@ If your PR makes notable changes to Winit's features, please update this section
|
||||
### Input Handling
|
||||
- **Mouse events**: Generating mouse events associated with pointer motion, click, and scrolling events.
|
||||
- **Mouse set location**: Forcibly changing the location of the pointer.
|
||||
- **Cursor grab**: Locking the cursor so it cannot exit the client area of a window.
|
||||
- **Cursor locking**: Locking the cursor inside the window so it cannot move.
|
||||
- **Cursor confining**: Confining the cursor to the window bounds so it cannot leave them.
|
||||
- **Cursor icon**: Changing the cursor icon, or hiding the cursor.
|
||||
- **Cursor hittest**: Handle or ignore mouse events for a window.
|
||||
- **Touch events**: Single-touch events.
|
||||
- **Touch pressure**: Touch events contain information about the amount of force being applied.
|
||||
- **Multitouch**: Multi-touch events, including cancellation of a gesture.
|
||||
@@ -109,15 +112,16 @@ If your PR makes notable changes to Winit's features, please update this section
|
||||
translating keypresses into UTF-8 characters, handling dead keys and IMEs.
|
||||
- **Drag & Drop**: Dragging content into winit, detecting when content enters, drops, or if the drop is cancelled.
|
||||
- **Raw Device Events**: Capturing input from input devices without any OS filtering.
|
||||
- **Gamepad/Joystick events**: Capturing input from gampads and joysticks.
|
||||
- **Device movement events:**: Capturing input from the device gyroscope and accelerometer.
|
||||
- **Gamepad/Joystick events**: Capturing input from gamepads and joysticks.
|
||||
- **Device movement events**: Capturing input from the device gyroscope and accelerometer.
|
||||
|
||||
## Platform
|
||||
### Windows
|
||||
* Setting the taskbar icon
|
||||
* Setting the parent window
|
||||
* Setting a menu bar
|
||||
* `WS_EX_NOREDIRECTIONBITMAP` support
|
||||
* Theme the title bar according to Windows 10 Dark Mode setting
|
||||
* Theme the title bar according to Windows 10 Dark Mode setting or set a preferred theme
|
||||
|
||||
### macOS
|
||||
* Window activation policy
|
||||
@@ -126,6 +130,8 @@ If your PR makes notable changes to Winit's features, please update this section
|
||||
* Hidden titlebar
|
||||
* Hidden titlebar buttons
|
||||
* Full-size content view
|
||||
* Accepts first mouse
|
||||
* Set a preferred theme and get current theme.
|
||||
|
||||
### Unix
|
||||
* Window urgency
|
||||
@@ -133,6 +139,7 @@ If your PR makes notable changes to Winit's features, please update this section
|
||||
* X11 Override Redirect Flag
|
||||
* GTK Theme Variant
|
||||
* Base window size
|
||||
* Setting the X11 parent window
|
||||
|
||||
### iOS
|
||||
* `winit` has a minimum OS requirement of iOS 8
|
||||
@@ -145,7 +152,6 @@ If your PR makes notable changes to Winit's features, please update this section
|
||||
* Home indicator visibility
|
||||
* Status bar visibility
|
||||
* Deferrring system gestures
|
||||
* Support for custom `UIView` derived class
|
||||
* Getting the device idiom
|
||||
* Getting the preferred video mode
|
||||
|
||||
@@ -166,59 +172,63 @@ Legend:
|
||||
- ❓: Unknown status
|
||||
|
||||
### Windowing
|
||||
|Feature |Windows|MacOS |Linux x11 |Linux Wayland |Android|iOS |WASM |
|
||||
|-------------------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
|
||||
|Window initialization |✔️ |✔️ |▢[#5] |✔️ |▢[#33]|▢[#33] |✔️ |
|
||||
|Providing pointer to init OpenGL |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**|
|
||||
|Providing pointer to init Vulkan |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |**N/A**|
|
||||
|Window decorations |✔️ |✔️ |✔️ |▢[#306] |**N/A**|**N/A**|**N/A**|
|
||||
|Window decorations toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|
|
||||
|Window resizing |✔️ |▢[#219]|✔️ |▢[#306] |**N/A**|**N/A**|✔️ |
|
||||
|Window resize increments |❌ |❌ |❌ |❌ |❌ |❌ |**N/A**|
|
||||
|Window transparency |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|N/A |
|
||||
|Window maximization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|
|
||||
|Window maximization toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|
|
||||
|Window minimization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|
|
||||
|Fullscreen |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |✔️ |
|
||||
|Fullscreen toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |✔️ |
|
||||
|Exclusive fullscreen |✔️ |✔️ |✔️ |**N/A** |❌ |✔️ |**N/A**|
|
||||
|HiDPI support |✔️ |✔️ |✔️ |✔️ |▢[#721]|✔️ |**N/A**|
|
||||
|Popup windows |❌ |❌ |❌ |❌ |❌ |❌ |**N/A**|
|
||||
|Feature |Windows|MacOS |Linux x11 |Linux Wayland |Android|iOS |WASM |Redox OS|
|
||||
|-------------------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|
||||
|Window initialization |✔️ |✔️ |▢[#5] |✔️ |▢[#33]|▢[#33] |✔️ |✔️ |
|
||||
|Providing pointer to init OpenGL |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |
|
||||
|Providing pointer to init Vulkan |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |**N/A**|**N/A** |
|
||||
|Window decorations |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|✔️ |
|
||||
|Window decorations toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||
|Window resizing |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |✔️ |
|
||||
|Window resize increments |❌ |✔️ |✔️ |❌ |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||
|Window transparency |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|N/A |✔️ |
|
||||
|Window maximization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||
|Window maximization toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||
|Window minimization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||
|Fullscreen |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |✔️ |**N/A** |
|
||||
|Fullscreen toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |✔️ |**N/A** |
|
||||
|Exclusive fullscreen |✔️ |✔️ |✔️ |**N/A** |❌ |✔️ |**N/A**|**N/A** |
|
||||
|HiDPI support |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❌ |
|
||||
|Popup windows |❌ |❌ |❌ |❌ |❌ |❌ |**N/A**|**N/A** |
|
||||
|
||||
### System information
|
||||
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |
|
||||
|---------------- | ----- | ---- | ------- | ----------- | ----- | ------- | -------- |
|
||||
|Monitor list |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |**N/A**|
|
||||
|Video mode query |✔️ |✔️ |✔️ |✔️ |❌ |✔️ |**N/A**|
|
||||
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |Redox OS|
|
||||
|---------------- | ----- | ---- | ------- | ----------- | ----- | ------- | -------- | ------ |
|
||||
|Monitor list |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**|❌ |
|
||||
|Video mode query |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**|❌ |
|
||||
|
||||
### Input handling
|
||||
|Feature |Windows |MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |
|
||||
|----------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
|
||||
|Mouse events |✔️ |▢[#63] |✔️ |✔️ |**N/A**|**N/A**|✔️ |
|
||||
|Mouse set location |✔️ |✔️ |✔️ |❓ |**N/A**|**N/A**|**N/A**|
|
||||
|Cursor grab |✔️ |▢[#165] |▢[#242] |✔️ |**N/A**|**N/A**|❓ |
|
||||
|Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |
|
||||
|Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |✔️ |
|
||||
|Touch pressure |✔️ |❌ |❌ |❌ |❌ |✔️ |✔️ |
|
||||
|Multitouch |✔️ |❌ |✔️ |✔️ |❓ |✔️ |✔️ |
|
||||
|Keyboard events |✔️ |✔️ |✔️ |✔️ |❓ |❌ |✔️ |
|
||||
|Drag & Drop |▢[#720] |▢[#720] |▢[#720] |❌[#306] |**N/A**|**N/A**|❓ |
|
||||
|Raw Device Events |▢[#750] |▢[#750] |▢[#750] |❌ |❌ |❌ |❓ |
|
||||
|Gamepad/Joystick events |❌[#804] |❌ |❌ |❌ |❌ |❌ |❓ |
|
||||
|Device movement events |❓ |❓ |❓ |❓ |❌ |❌ |❓ |
|
||||
|Feature |Windows |MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |Redox OS|
|
||||
|----------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|
||||
|Mouse events |✔️ |▢[#63] |✔️ |✔️ |**N/A**|**N/A**|✔️ |✔️ |
|
||||
|Mouse set location |✔️ |✔️ |✔️ |✔️(when locked) |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||
|Cursor locking |❌ |✔️ |❌ |✔️ |**N/A**|**N/A**|✔️ |❌ |
|
||||
|Cursor confining |✔️ |❌ |✔️ |✔️ |**N/A**|**N/A**|❌ |❌ |
|
||||
|Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |**N/A** |
|
||||
|Cursor hittest |✔️ |✔️ |❌ |✔️ |**N/A**|**N/A**|❌ |❌ |
|
||||
|Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A** |
|
||||
|Touch pressure |✔️ |❌ |❌ |❌ |❌ |✔️ |✔️ |**N/A** |
|
||||
|Multitouch |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |❌ |**N/A** |
|
||||
|Keyboard events |✔️ |✔️ |✔️ |✔️ |✔️ |❌ |✔️ |✔️ |
|
||||
|Drag & Drop |▢[#720] |▢[#720] |▢[#720] |▢[#720] |**N/A**|**N/A**|❓ |**N/A** |
|
||||
|Raw Device Events |▢[#750] |▢[#750] |▢[#750] |❌ |❌ |❌ |❓ |**N/A** |
|
||||
|Gamepad/Joystick events |❌[#804] |❌ |❌ |❌ |❌ |❌ |❓ |**N/A** |
|
||||
|Device movement events |❓ |❓ |❓ |❓ |❌ |❌ |❓ |**N/A** |
|
||||
|Drag window with cursor |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A** |**N/A** |
|
||||
|Resize with cursor |❌ |❌ |✔️ |❌ |**N/A**|**N/A**|**N/A** |**N/A** |
|
||||
|
||||
### Pending API Reworks
|
||||
Changes in the API that have been agreed upon but aren't implemented across all platforms.
|
||||
|
||||
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |
|
||||
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
|
||||
|New API for HiDPI ([#315] [#319]) |✔️ |✔️ |✔️ |✔️ |▢[#721]|✔️ |❓ |
|
||||
|Event Loop 2.0 ([#459]) |✔️ |✔️ |❌ |✔️ |❌ |✔️ |❓ |
|
||||
|Keyboard Input ([#812]) |❌ |❌ |❌ |❌ |❌ |❌ |❓ |
|
||||
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |Redox OS|
|
||||
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|
||||
|New API for HiDPI ([#315] [#319]) |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |❓ |
|
||||
|Event Loop 2.0 ([#459]) |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |✔️ |
|
||||
|Keyboard Input 2.0 ([#753]) |✔️ |✔️ |✔️ |✔️ |✔️ |❌ |✔️ |✔️ |
|
||||
|
||||
### Completed API Reworks
|
||||
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |
|
||||
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
|
||||
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |Redox OS|
|
||||
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|
||||
|
||||
[#165]: https://github.com/rust-windowing/winit/issues/165
|
||||
[#219]: https://github.com/rust-windowing/winit/issues/219
|
||||
@@ -233,5 +243,5 @@ Changes in the API that have been agreed upon but aren't implemented across all
|
||||
[#720]: https://github.com/rust-windowing/winit/issues/720
|
||||
[#721]: https://github.com/rust-windowing/winit/issues/721
|
||||
[#750]: https://github.com/rust-windowing/winit/issues/750
|
||||
[#753]: https://github.com/rust-windowing/winit/issues/753
|
||||
[#804]: https://github.com/rust-windowing/winit/issues/804
|
||||
[#812]: https://github.com/rust-windowing/winit/issues/812
|
||||
|
||||
@@ -1,14 +1,26 @@
|
||||
# Hall of Champions
|
||||
|
||||
The Winit maintainers would like to recognize the following former Winit
|
||||
contributors, without whom Winit would not exist in its current form. We thank
|
||||
The winit maintainers would like to recognize the following former winit
|
||||
contributors, without whom winit would not exist in its current form. We thank
|
||||
them deeply for their time and efforts, and wish them best of luck in their
|
||||
future endeavors:
|
||||
|
||||
* [@tomaka]: For creating the Winit project and guiding it through its early
|
||||
* [@tomaka]: For creating the winit project and guiding it through its early
|
||||
years of existence.
|
||||
* [@vberger]: For diligently creating the Wayland backend, and being its
|
||||
extremely helpful and benevolent maintainer for years.
|
||||
* [@francesca64]: For taking over the responsibility of maintaining almost every
|
||||
Winit backend, and standardizing HiDPI support across all of them
|
||||
winit backend, and standardizing HiDPI support across all of them.
|
||||
* [@Osspial]: For heroically landing EventLoop 2.0, and valiantly ushering in a
|
||||
vastly more sustainable era of winit.
|
||||
* [@goddessfreya]: For selflessly taking over maintainership of glutin, and her
|
||||
stellar dedication to improving both winit and glutin.
|
||||
* [@ArturKovacs]: For consistently maintaining the macOS backend, and his
|
||||
immense involvement in designing and implementing the new keyboard API.
|
||||
|
||||
[@tomaka]: https://github.com/tomaka
|
||||
[@vberger]: https://github.com/vberger
|
||||
[@francesca64]: https://github.com/francesca64
|
||||
[@Osspial]: https://github.com/Osspial
|
||||
[@goddessfreya]: https://github.com/goddessfreya
|
||||
[@ArturKovacs]: https://github.com/ArturKovacs
|
||||
|
||||
157
README.md
157
README.md
@@ -2,12 +2,11 @@
|
||||
|
||||
[](https://crates.io/crates/winit)
|
||||
[](https://docs.rs/winit)
|
||||
[](https://travis-ci.org/rust-windowing/winit)
|
||||
[](https://ci.appveyor.com/project/Osspial/winit/branch/master)
|
||||
[](https://github.com/rust-windowing/winit/actions)
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.22.0"
|
||||
winit = "0.29.0-beta.0"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
@@ -20,9 +19,8 @@ For features _outside_ the scope of winit, see [Missing features provided by oth
|
||||
|
||||
Join us in any of these:
|
||||
|
||||
[](http://webchat.freenode.net?channels=%23glutin&uio=MTY9dHJ1ZSYyPXRydWUmND10cnVlJjExPTE4NSYxMj10cnVlJjE1PXRydWU7a)
|
||||
[](https://matrix.to/#/#Glutin:matrix.org)
|
||||
[](https://gitter.im/tomaka/glutin?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://matrix.to/#/#rust-windowing:matrix.org)
|
||||
[](https://web.libera.chat/#winit)
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -65,15 +63,150 @@ Winit is only officially supported on the latest stable version of the Rust comp
|
||||
|
||||
Winit provides the following features, which can be enabled in your `Cargo.toml` file:
|
||||
* `serde`: Enables serialization/deserialization of certain types with [Serde](https://crates.io/crates/serde).
|
||||
* `x11` (enabled by default): On Unix platform, compiles with the X11 backend
|
||||
* `wayland` (enabled by default): On Unix platform, compiles with the Wayland backend
|
||||
* `mint`: Enables mint (math interoperability standard types) conversions.
|
||||
|
||||
### Platform-specific usage
|
||||
|
||||
#### Wayland
|
||||
|
||||
Note that windows don't appear on Wayland until you draw/present to them.
|
||||
|
||||
#### WebAssembly
|
||||
|
||||
Building a binary will yield a `.js` file. In order to use it in an HTML file, you need to:
|
||||
To run the web example: `cargo run-wasm --example web`
|
||||
|
||||
- Put a `<canvas id="my_id"></canvas>` element somewhere. A canvas corresponds to a winit "window".
|
||||
- Write a Javascript code that creates a global variable named `Module`. Set `Module.canvas` to
|
||||
the element of the `<canvas>` element (in the example you would retrieve it via `document.getElementById("my_id")`).
|
||||
More information [here](https://kripken.github.io/emscripten-site/docs/api_reference/module.html).
|
||||
- Make sure that you insert the `.js` file generated by Rust after the `Module` variable is created.
|
||||
Winit supports compiling to the `wasm32-unknown-unknown` target with `web-sys`.
|
||||
|
||||
On the web platform, a Winit window is backed by a `<canvas>` element. You can
|
||||
either [provide Winit with a `<canvas>` element][web with_canvas], or [let Winit
|
||||
create a `<canvas>` element which you can then retrieve][web canvas getter] and
|
||||
insert it into the DOM yourself.
|
||||
|
||||
For example code using Winit with WebAssembly, check out the [web example]. For
|
||||
information on using Rust on WebAssembly, check out the [Rust and WebAssembly
|
||||
book].
|
||||
|
||||
[web with_canvas]: https://docs.rs/winit/latest/wasm32-unknown-unknown/winit/platform/web/trait.WindowBuilderExtWebSys.html#tymethod.with_canvas
|
||||
[web canvas getter]: https://docs.rs/winit/latest/wasm32-unknown-unknown/winit/platform/web/trait.WindowExtWebSys.html#tymethod.canvas
|
||||
[web example]: ./examples/web.rs
|
||||
[Rust and WebAssembly book]: https://rustwasm.github.io/book/
|
||||
|
||||
#### Android
|
||||
|
||||
The Android backend builds on (and exposes types from) the [`ndk`](https://docs.rs/ndk/0.7.0/ndk/) crate.
|
||||
|
||||
Native Android applications need some form of "glue" crate that is responsible
|
||||
for defining the main entry point for your Rust application as well as tracking
|
||||
various life-cycle events and synchronizing with the main JVM thread.
|
||||
|
||||
Winit uses the [android-activity](https://github.com/rib/android-activity) as a
|
||||
glue crate (prior to `0.28` it used
|
||||
[ndk-glue](https://github.com/rust-windowing/android-ndk-rs/tree/master/ndk-glue)).
|
||||
|
||||
The version of the glue crate that your application depends on _must_ match the
|
||||
version that Winit depends on because the glue crate is responsible for your
|
||||
application's main entrypoint. If Cargo resolves multiple versions they will
|
||||
clash.
|
||||
|
||||
`winit` glue compatibility table:
|
||||
|
||||
| winit | ndk-glue |
|
||||
| :---: | :--------------------------: |
|
||||
| 0.28 | `android-activity = "0.4"` |
|
||||
| 0.27 | `ndk-glue = "0.7"` |
|
||||
| 0.26 | `ndk-glue = "0.5"` |
|
||||
| 0.25 | `ndk-glue = "0.3"` |
|
||||
| 0.24 | `ndk-glue = "0.2"` |
|
||||
|
||||
The recommended way to avoid a conflict with the glue version is to avoid explicitly
|
||||
depending on the `android-activity` crate, and instead consume the API that
|
||||
is re-exported by Winit under `winit::platform::android::activity::*`
|
||||
|
||||
Running on an Android device needs a dynamic system library, add this to Cargo.toml:
|
||||
|
||||
```toml
|
||||
[lib]
|
||||
name = "main"
|
||||
crate-type = ["cdylib"]
|
||||
```
|
||||
|
||||
All Android applications are based on an `Activity` subclass and the
|
||||
`android-activity` crate is designed to support different choices for this base
|
||||
class. Your application _must_ specify the base class it needs via a feature flag:
|
||||
|
||||
| Base Class | Feature Flag | Notes |
|
||||
| :--------------: | :---------------: | :-----: |
|
||||
| `NativeActivity` | `android-native-activity` | Built-in to Android - it is possible to use without compiling any Java or Kotlin code. Java or Kotlin code may be needed to subclass `NativeActivity` to access some platform features. It does not derive from the [`AndroidAppCompat`] base class.|
|
||||
| [`GameActivity`] | `android-game-activity` | Derives from [`AndroidAppCompat`] which is a defacto standard `Activity` base class that helps support a wider range of Android versions. Requires a build system that can compile Java or Kotlin and fetch Android dependencies from a [Maven repository][agdk_jetpack] (or link with an embedded [release][agdk_releases] of [`GameActivity`]) |
|
||||
|
||||
[`GameActivity`]: https://developer.android.com/games/agdk/game-activity
|
||||
[`GameTextInput`]: https://developer.android.com/games/agdk/add-support-for-text-input
|
||||
[`AndroidAppCompat`]: https://developer.android.com/reference/androidx/appcompat/app/AppCompatActivity
|
||||
[agdk_jetpack]: https://developer.android.com/jetpack/androidx/releases/games
|
||||
[agdk_releases]: https://developer.android.com/games/agdk/download#agdk-libraries
|
||||
[Gradle]: https://developer.android.com/studio/build
|
||||
|
||||
For example, add this to Cargo.toml:
|
||||
```toml
|
||||
winit = { version = "0.28", features = [ "android-native-activity" ] }
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
android_logger = "0.11.0"
|
||||
```
|
||||
|
||||
And, for example, define an entry point for your library like this:
|
||||
```rust
|
||||
#[cfg(target_os = "android")]
|
||||
use winit::platform::android::activity::AndroidApp;
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
#[no_mangle]
|
||||
fn android_main(app: AndroidApp) {
|
||||
use winit::platform::android::EventLoopBuilderExtAndroid;
|
||||
|
||||
android_logger::init_once(android_logger::Config::default().with_min_level(log::Level::Trace));
|
||||
|
||||
let event_loop = EventLoopBuilder::with_user_event()
|
||||
.with_android_app(app)
|
||||
.build();
|
||||
_main(event_loop);
|
||||
}
|
||||
```
|
||||
|
||||
For more details, refer to these `android-activity` [example applications](https://github.com/rib/android-activity/tree/main/examples).
|
||||
|
||||
##### Converting from `ndk-glue` to `android-activity`
|
||||
|
||||
If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building with `cargo apk` then the minimal changes would be:
|
||||
1. Remove `ndk-glue` from your `Cargo.toml`
|
||||
2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.28", features = [ "android-native-activity" ] }`
|
||||
3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize logging as above).
|
||||
4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your event loop (as shown above).
|
||||
|
||||
#### MacOS
|
||||
|
||||
A lot of functionality expects the application to be ready before you start
|
||||
doing anything; this includes creating windows, fetching monitors, drawing,
|
||||
and so on, see issues [#2238], [#2051] and [#2087].
|
||||
|
||||
If you encounter problems, you should try doing your initialization inside
|
||||
`Event::NewEvents(StartCause::Init)`.
|
||||
|
||||
#### iOS
|
||||
|
||||
Similar to macOS, iOS's main `UIApplicationMain` does some init work that's required
|
||||
by all UI related code, see issue [#1705]. You should consider creating your windows
|
||||
inside `Event::NewEvents(StartCause::Init)`.
|
||||
|
||||
|
||||
[#2238]: https://github.com/rust-windowing/winit/issues/2238
|
||||
[#2051]: https://github.com/rust-windowing/winit/issues/2051
|
||||
[#2087]: https://github.com/rust-windowing/winit/issues/2087
|
||||
[#1705]: https://github.com/rust-windowing/winit/issues/1705
|
||||
|
||||
#### Redox OS
|
||||
|
||||
Redox OS has some functionality not present yet, that will be implemented when
|
||||
its orbital display server provides it.
|
||||
|
||||
24
build.rs
Normal file
24
build.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
use cfg_aliases::cfg_aliases;
|
||||
|
||||
fn main() {
|
||||
// The script doesn't depend on our code
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
|
||||
// Setup cfg aliases
|
||||
cfg_aliases! {
|
||||
// Systems.
|
||||
android_platform: { target_os = "android" },
|
||||
wasm_platform: { target_family = "wasm" },
|
||||
macos_platform: { target_os = "macos" },
|
||||
ios_platform: { target_os = "ios" },
|
||||
windows_platform: { target_os = "windows" },
|
||||
apple: { any(target_os = "ios", target_os = "macos") },
|
||||
free_unix: { all(unix, not(apple), not(android_platform)) },
|
||||
redox: { target_os = "redox" },
|
||||
|
||||
// Native displays.
|
||||
x11_platform: { all(feature = "x11", free_unix, not(wasm), not(redox)) },
|
||||
wayland_platform: { all(feature = "wayland", free_unix, not(wasm), not(redox)) },
|
||||
orbital_platform: { redox },
|
||||
}
|
||||
}
|
||||
7
clippy.toml
Normal file
7
clippy.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
disallowed-methods = [
|
||||
{ path = "web_sys::window", reason = "is not available in every context" },
|
||||
{ path = "web_sys::HtmlCanvasElement::width", reason = "Winit shouldn't touch the internal canvas size" },
|
||||
{ path = "web_sys::HtmlCanvasElement::height", reason = "Winit shouldn't touch the internal canvas size" },
|
||||
{ path = "web_sys::HtmlCanvasElement::set_width", reason = "Winit shouldn't touch the internal canvas size" },
|
||||
{ path = "web_sys::HtmlCanvasElement::set_height", reason = "Winit shouldn't touch the internal canvas size" },
|
||||
]
|
||||
70
deny.toml
Normal file
70
deny.toml
Normal file
@@ -0,0 +1,70 @@
|
||||
# https://embarkstudios.github.io/cargo-deny/
|
||||
# cargo install cargo-deny
|
||||
# cargo update && cargo deny --all-features --log-level error --target aarch64-apple-ios check
|
||||
# Note: running just `cargo deny check` without a `--target` will result in
|
||||
# false positives due to https://github.com/EmbarkStudios/cargo-deny/issues/324
|
||||
targets = [
|
||||
{ triple = "aarch64-apple-ios" },
|
||||
{ triple = "aarch64-linux-android" },
|
||||
{ triple = "i686-pc-windows-gnu" },
|
||||
{ triple = "i686-pc-windows-msvc" },
|
||||
{ triple = "i686-unknown-linux-gnu" },
|
||||
{ triple = "wasm32-unknown-unknown" },
|
||||
{ triple = "x86_64-apple-darwin" },
|
||||
{ triple = "x86_64-apple-ios" },
|
||||
{ triple = "x86_64-pc-windows-gnu" },
|
||||
{ triple = "x86_64-pc-windows-msvc" },
|
||||
{ triple = "x86_64-unknown-linux-gnu" },
|
||||
{ triple = "x86_64-unknown-redox" },
|
||||
]
|
||||
|
||||
|
||||
[advisories]
|
||||
vulnerability = "deny"
|
||||
unmaintained = "warn"
|
||||
yanked = "deny"
|
||||
ignore = []
|
||||
|
||||
|
||||
[bans]
|
||||
multiple-versions = "deny"
|
||||
wildcards = "allow" # at least until https://github.com/EmbarkStudios/cargo-deny/issues/241 is fixed
|
||||
deny = []
|
||||
skip = [
|
||||
{ name = "bitflags" }, # the ecosystem is in the process of migrating.
|
||||
{ name = "nix" }, # differing version - as of 2023-03-02 whis can be solved with `cargo update && cargo update -p calloop --precise 0.10.2`
|
||||
{ name = "memoffset"}, # due to different nix versions.
|
||||
{ name = "memmap2" }, # sctk uses a different version until the next update
|
||||
{ name = "libloading" }, # x11rb uses a different version until the next update
|
||||
{ name = "syn" }, # https://github.com/rust-mobile/ndk/issues/392 and https://github.com/rustwasm/wasm-bindgen/issues/3390
|
||||
{ name = "num_enum"}, # See above ^, waiting for release
|
||||
{ name = "num_enum_derive"},# See above ^, waiting for release
|
||||
{ name = "miniz_oxide"}, # https://github.com/rust-lang/flate2-rs/issues/340
|
||||
{ name = "redox_syscall" }, # https://gitlab.redox-os.org/redox-os/orbclient/-/issues/46
|
||||
]
|
||||
skip-tree = []
|
||||
|
||||
|
||||
[licenses]
|
||||
private = { ignore = true }
|
||||
unlicensed = "deny"
|
||||
allow-osi-fsf-free = "neither"
|
||||
confidence-threshold = 0.92 # We want really high confidence when inferring licenses from text
|
||||
copyleft = "deny"
|
||||
allow = [
|
||||
"Apache-2.0 WITH LLVM-exception", # https://spdx.org/licenses/LLVM-exception.html
|
||||
"Apache-2.0", # https://tldrlegal.com/license/apache-license-2.0-(apache-2.0)
|
||||
"BSD-2-Clause", # https://tldrlegal.com/license/bsd-2-clause-license-(freebsd)
|
||||
"BSD-3-Clause", # https://tldrlegal.com/license/bsd-3-clause-license-(revised)
|
||||
"BSL-1.0", # https://tldrlegal.com/license/boost-software-license-1.0-explained
|
||||
"CC0-1.0", # https://creativecommons.org/publicdomain/zero/1.0/
|
||||
"ISC", # https://tldrlegal.com/license/-isc-license
|
||||
"LicenseRef-UFL-1.0", # https://tldrlegal.com/license/ubuntu-font-license,-1.0 - no official SPDX, see https://github.com/emilk/egui/issues/2321
|
||||
"MIT-0", # https://choosealicense.com/licenses/mit-0/
|
||||
"MIT", # https://tldrlegal.com/license/mit-license
|
||||
"MPL-2.0", # https://www.mozilla.org/en-US/MPL/2.0/FAQ/ - see Q11. Used by webpki-roots on Linux.
|
||||
"OFL-1.1", # https://spdx.org/licenses/OFL-1.1.html
|
||||
"OpenSSL", # https://www.openssl.org/source/license.html - used on Linux
|
||||
"Unicode-DFS-2016", # https://spdx.org/licenses/Unicode-DFS-2016.html
|
||||
"Zlib", # https://tldrlegal.com/license/zlib-libpng-license-(zlib)
|
||||
]
|
||||
11
docs/res/ATTRIBUTION.md
Normal file
11
docs/res/ATTRIBUTION.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Image Attribution
|
||||
|
||||
These images are used in the documentation of `winit`.
|
||||
|
||||
## keyboard_*.svg
|
||||
|
||||
These files are a modified version of "[ANSI US QWERTY (Windows)](https://commons.wikimedia.org/wiki/File:ANSI_US_QWERTY_(Windows).svg)"
|
||||
by [Tomiĉo] (https://commons.wikimedia.org/wiki/User:Tomi%C4%89o). It is
|
||||
originally released under the [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.en)
|
||||
License. Minor modifications have been made by [John Nunley](https://github.com/notgull),
|
||||
which have been released under the same license as a derivative work.
|
||||
1
docs/res/keyboard_left_shift_key.svg
Normal file
1
docs/res/keyboard_left_shift_key.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 73 KiB |
1
docs/res/keyboard_numpad_1_key.svg
Normal file
1
docs/res/keyboard_numpad_1_key.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 73 KiB |
1
docs/res/keyboard_right_shift_key.svg
Normal file
1
docs/res/keyboard_right_shift_key.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 73 KiB |
1
docs/res/keyboard_standard_1_key.svg
Normal file
1
docs/res/keyboard_standard_1_key.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 73 KiB |
88
examples/child_window.rs
Normal file
88
examples/child_window.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
#[cfg(any(x11_platform, macos_platform, windows_platform))]
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
#[cfg(any(x11_platform, macos_platform, windows_platform))]
|
||||
fn main() {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use raw_window_handle::HasRawWindowHandle;
|
||||
use winit::{
|
||||
dpi::{LogicalPosition, LogicalSize, Position},
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
|
||||
window::{Window, WindowBuilder, WindowId},
|
||||
};
|
||||
|
||||
fn spawn_child_window(
|
||||
parent: &Window,
|
||||
event_loop: &EventLoopWindowTarget<()>,
|
||||
windows: &mut HashMap<WindowId, Window>,
|
||||
) {
|
||||
let parent = parent.raw_window_handle();
|
||||
let mut builder = WindowBuilder::new()
|
||||
.with_title("child window")
|
||||
.with_inner_size(LogicalSize::new(200.0f32, 200.0f32))
|
||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||
.with_visible(true);
|
||||
// `with_parent_window` is unsafe. Parent window must be a valid window.
|
||||
builder = unsafe { builder.with_parent_window(Some(parent)) };
|
||||
let child_window = builder.build(event_loop).unwrap();
|
||||
|
||||
let id = child_window.id();
|
||||
windows.insert(id, child_window);
|
||||
println!("child window created with id: {id:?}");
|
||||
}
|
||||
|
||||
let mut windows = HashMap::new();
|
||||
|
||||
let event_loop: EventLoop<()> = EventLoop::new();
|
||||
let parent_window = WindowBuilder::new()
|
||||
.with_title("parent window")
|
||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||
.with_inner_size(LogicalSize::new(640.0f32, 480.0f32))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
println!("parent window: {parent_window:?})");
|
||||
|
||||
event_loop.run(move |event: Event<'_, ()>, event_loop, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
if let Event::WindowEvent { event, window_id } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
windows.clear();
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
WindowEvent::CursorEntered { device_id: _ } => {
|
||||
// On x11, println when the cursor entered in a window even if the child window is created
|
||||
// by some key inputs.
|
||||
// the child windows are always placed at (0, 0) with size (200, 200) in the parent window,
|
||||
// so we also can see this log when we move the cursor arround (200, 200) in parent window.
|
||||
println!("cursor entered in the window {window_id:?}");
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
spawn_child_window(&parent_window, event_loop, &mut windows);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
} else if let Event::RedrawRequested(wid) = event {
|
||||
if let Some(window) = windows.get(&wid) {
|
||||
fill::fill_window(window);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(any(x11_platform, macos_platform, windows_platform)))]
|
||||
fn main() {
|
||||
panic!("This example is supported only on x11, macOS, and Windows.");
|
||||
}
|
||||
@@ -1,11 +1,22 @@
|
||||
use std::{thread, time};
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use std::thread;
|
||||
#[cfg(not(wasm_platform))]
|
||||
use std::time;
|
||||
#[cfg(wasm_platform)]
|
||||
use web_time as time;
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, KeyboardInput, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::Key,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Mode {
|
||||
Wait,
|
||||
@@ -17,7 +28,7 @@ const WAIT_TIME: time::Duration = time::Duration::from_millis(100);
|
||||
const POLL_SLEEP_TIME: time::Duration = time::Duration::from_millis(100);
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
|
||||
println!("Press '1' to switch to Wait mode.");
|
||||
println!("Press '2' to switch to WaitUntil mode.");
|
||||
@@ -37,19 +48,13 @@ fn main() {
|
||||
let mut close_requested = false;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
use winit::event::{ElementState, StartCause, VirtualKeyCode};
|
||||
println!("{:?}", event);
|
||||
use winit::event::StartCause;
|
||||
println!("{event:?}");
|
||||
match event {
|
||||
Event::NewEvents(start_cause) => {
|
||||
wait_cancelled = mode == Mode::WaitUntil;
|
||||
match start_cause {
|
||||
StartCause::ResumeTimeReached {
|
||||
start: _,
|
||||
requested_resume: _,
|
||||
} => {
|
||||
wait_cancelled = false;
|
||||
}
|
||||
_ => (),
|
||||
wait_cancelled = match start_cause {
|
||||
StartCause::WaitCancelled { .. } => mode == Mode::WaitUntil,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
@@ -57,31 +62,33 @@ fn main() {
|
||||
close_requested = true;
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
virtual_keycode: Some(virtual_code),
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: key,
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match virtual_code {
|
||||
VirtualKeyCode::Key1 => {
|
||||
} => match key.as_ref() {
|
||||
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||
// See the `key_binding` example
|
||||
Key::Character("1") => {
|
||||
mode = Mode::Wait;
|
||||
println!("\nmode: {:?}\n", mode);
|
||||
println!("\nmode: {mode:?}\n");
|
||||
}
|
||||
VirtualKeyCode::Key2 => {
|
||||
Key::Character("2") => {
|
||||
mode = Mode::WaitUntil;
|
||||
println!("\nmode: {:?}\n", mode);
|
||||
println!("\nmode: {mode:?}\n");
|
||||
}
|
||||
VirtualKeyCode::Key3 => {
|
||||
Key::Character("3") => {
|
||||
mode = Mode::Poll;
|
||||
println!("\nmode: {:?}\n", mode);
|
||||
println!("\nmode: {mode:?}\n");
|
||||
}
|
||||
VirtualKeyCode::R => {
|
||||
Key::Character("r") => {
|
||||
request_redraw = !request_redraw;
|
||||
println!("\nrequest_redraw: {}\n", request_redraw);
|
||||
println!("\nrequest_redraw: {request_redraw}\n");
|
||||
}
|
||||
VirtualKeyCode::Escape => {
|
||||
Key::Escape => {
|
||||
close_requested = true;
|
||||
}
|
||||
_ => (),
|
||||
@@ -93,23 +100,23 @@ fn main() {
|
||||
window.request_redraw();
|
||||
}
|
||||
if close_requested {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
control_flow.set_exit();
|
||||
}
|
||||
}
|
||||
Event::RedrawRequested(_window_id) => {}
|
||||
Event::RedrawRequested(_window_id) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
Event::RedrawEventsCleared => {
|
||||
*control_flow = match mode {
|
||||
Mode::Wait => ControlFlow::Wait,
|
||||
match mode {
|
||||
Mode::Wait => control_flow.set_wait(),
|
||||
Mode::WaitUntil => {
|
||||
if wait_cancelled {
|
||||
*control_flow
|
||||
} else {
|
||||
ControlFlow::WaitUntil(time::Instant::now() + WAIT_TIME)
|
||||
if !wait_cancelled {
|
||||
control_flow.set_wait_until(time::Instant::now() + WAIT_TIME);
|
||||
}
|
||||
}
|
||||
Mode::Poll => {
|
||||
thread::sleep(POLL_SLEEP_TIME);
|
||||
ControlFlow::Poll
|
||||
control_flow.set_poll();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyboardInput, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::{CursorIcon, WindowBuilder},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
@@ -14,14 +20,14 @@ fn main() {
|
||||
let mut cursor_idx = 0;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event:
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
@@ -41,8 +47,10 @@ fn main() {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
return;
|
||||
control_flow.set_exit();
|
||||
}
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
@@ -52,8 +60,7 @@ fn main() {
|
||||
const CURSORS: &[CursorIcon] = &[
|
||||
CursorIcon::Default,
|
||||
CursorIcon::Crosshair,
|
||||
CursorIcon::Hand,
|
||||
CursorIcon::Arrow,
|
||||
CursorIcon::Pointer,
|
||||
CursorIcon::Move,
|
||||
CursorIcon::Text,
|
||||
CursorIcon::Wait,
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
event::{DeviceEvent, ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::{Key, ModifiersState},
|
||||
window::{CursorGrabMode, WindowBuilder},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
@@ -16,39 +23,54 @@ fn main() {
|
||||
let mut modifiers = ModifiersState::default();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::CloseRequested => control_flow.set_exit(),
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: key,
|
||||
state: ElementState::Released,
|
||||
virtual_keycode: Some(key),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
use winit::event::VirtualKeyCode::*;
|
||||
match key {
|
||||
Escape => *control_flow = ControlFlow::Exit,
|
||||
G => window.set_cursor_grab(!modifiers.shift()).unwrap(),
|
||||
H => window.set_cursor_visible(modifiers.shift()),
|
||||
_ => (),
|
||||
let result = match key {
|
||||
Key::Escape => {
|
||||
control_flow.set_exit();
|
||||
Ok(())
|
||||
}
|
||||
Key::Character(ch) => match ch.to_lowercase().as_str() {
|
||||
"g" => window.set_cursor_grab(CursorGrabMode::Confined),
|
||||
"l" => window.set_cursor_grab(CursorGrabMode::Locked),
|
||||
"a" => window.set_cursor_grab(CursorGrabMode::None),
|
||||
"h" => {
|
||||
window.set_cursor_visible(modifiers.shift_key());
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
},
|
||||
_ => Ok(()),
|
||||
};
|
||||
|
||||
if let Err(err) = result {
|
||||
println!("error: {err}");
|
||||
}
|
||||
}
|
||||
WindowEvent::ModifiersChanged(m) => modifiers = m,
|
||||
WindowEvent::ModifiersChanged(new) => modifiers = new.state(),
|
||||
_ => (),
|
||||
},
|
||||
Event::DeviceEvent { event, .. } => match event {
|
||||
DeviceEvent::MouseMotion { delta } => println!("mouse moved: {:?}", delta),
|
||||
DeviceEvent::MouseMotion { delta } => println!("mouse moved: {delta:?}"),
|
||||
DeviceEvent::Button { button, state } => match state {
|
||||
ElementState::Pressed => println!("mouse button {} pressed", button),
|
||||
ElementState::Released => println!("mouse button {} released", button),
|
||||
ElementState::Pressed => println!("mouse button {button} pressed"),
|
||||
ElementState::Released => println!("mouse button {button} released"),
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
Event::RedrawRequested(_) => fill::fill_window(&window),
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
#[cfg(not(wasm_platform))]
|
||||
fn main() {
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
event_loop::EventLoopBuilder,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum CustomEvent {
|
||||
Timer,
|
||||
}
|
||||
|
||||
simple_logger::init().unwrap();
|
||||
let event_loop = EventLoop::<CustomEvent>::with_user_event();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoopBuilder::<CustomEvent>::with_user_event().build();
|
||||
|
||||
let _window = WindowBuilder::new()
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
@@ -33,20 +39,23 @@ fn main() {
|
||||
});
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::UserEvent(event) => println!("user event: {:?}", event),
|
||||
Event::UserEvent(event) => println!("user event: {event:?}"),
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => *control_flow = ControlFlow::Exit,
|
||||
} => control_flow.set_exit(),
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(wasm_platform)]
|
||||
fn main() {
|
||||
panic!("This example is not supported on web.");
|
||||
}
|
||||
|
||||
84
examples/drag_window.rs
Normal file
84
examples/drag_window.rs
Normal file
@@ -0,0 +1,84 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::Key,
|
||||
window::{Window, WindowBuilder, WindowId},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window_1 = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
let window_2 = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
|
||||
let mut switched = false;
|
||||
let mut entered_id = window_2.id();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| match event {
|
||||
Event::NewEvents(StartCause::Init) => {
|
||||
eprintln!("Switch which window is to be dragged by pressing \"x\".")
|
||||
}
|
||||
Event::WindowEvent { event, window_id } => match event {
|
||||
WindowEvent::CloseRequested => control_flow.set_exit(),
|
||||
WindowEvent::MouseInput {
|
||||
state: ElementState::Pressed,
|
||||
button: MouseButton::Left,
|
||||
..
|
||||
} => {
|
||||
let window = if (window_id == window_1.id() && switched)
|
||||
|| (window_id == window_2.id() && !switched)
|
||||
{
|
||||
&window_2
|
||||
} else {
|
||||
&window_1
|
||||
};
|
||||
|
||||
window.drag_window().unwrap()
|
||||
}
|
||||
WindowEvent::CursorEntered { .. } => {
|
||||
entered_id = window_id;
|
||||
name_windows(entered_id, switched, &window_1, &window_2)
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Released,
|
||||
logical_key: Key::Character(c),
|
||||
..
|
||||
},
|
||||
..
|
||||
} if c == "x" => {
|
||||
switched = !switched;
|
||||
name_windows(entered_id, switched, &window_1, &window_2);
|
||||
println!("Switched!")
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::RedrawRequested(wid) => {
|
||||
if wid == window_1.id() {
|
||||
fill::fill_window(&window_1);
|
||||
} else if wid == window_2.id() {
|
||||
fill::fill_window(&window_2);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
});
|
||||
}
|
||||
|
||||
fn name_windows(window_id: WindowId, switched: bool, window_1: &Window, window_2: &Window) {
|
||||
let (drag_target, other) =
|
||||
if (window_id == window_1.id() && switched) || (window_id == window_2.id() && !switched) {
|
||||
(&window_2, &window_1)
|
||||
} else {
|
||||
(&window_1, &window_2)
|
||||
};
|
||||
drag_target.set_title("drag target");
|
||||
other.set_title("winit window");
|
||||
}
|
||||
@@ -1,117 +1,135 @@
|
||||
use std::io::{stdin, stdout, Write};
|
||||
use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
|
||||
use winit::event_loop::{ControlFlow, EventLoop};
|
||||
use winit::monitor::{MonitorHandle, VideoMode};
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::keyboard::Key;
|
||||
use winit::window::{Fullscreen, WindowBuilder};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use winit::platform::macos::WindowExtMacOS;
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
print!("Please choose the fullscreen mode: (1) exclusive, (2) borderless: ");
|
||||
stdout().flush().unwrap();
|
||||
|
||||
let mut num = String::new();
|
||||
stdin().read_line(&mut num).unwrap();
|
||||
let num = num.trim().parse().ok().expect("Please enter a number");
|
||||
|
||||
let fullscreen = Some(match num {
|
||||
1 => Fullscreen::Exclusive(prompt_for_video_mode(&prompt_for_monitor(&event_loop))),
|
||||
2 => Fullscreen::Borderless(prompt_for_monitor(&event_loop)),
|
||||
_ => panic!("Please enter a valid number"),
|
||||
});
|
||||
|
||||
let mut is_maximized = false;
|
||||
let mut decorations = true;
|
||||
let mut minimized = false;
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("Hello world!")
|
||||
.with_fullscreen(fullscreen.clone())
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
let mut monitor_index = 0;
|
||||
let mut monitor = event_loop
|
||||
.available_monitors()
|
||||
.next()
|
||||
.expect("no monitor found!");
|
||||
println!("Monitor: {:?}", monitor.name());
|
||||
|
||||
let mut mode_index = 0;
|
||||
let mut mode = monitor.video_modes().next().expect("no mode found");
|
||||
println!("Mode: {mode}");
|
||||
|
||||
println!("Keys:");
|
||||
println!("- Esc\tExit");
|
||||
println!("- F\tToggle exclusive fullscreen mode");
|
||||
println!("- B\tToggle borderless mode");
|
||||
#[cfg(target_os = "macos")]
|
||||
println!("- C\tToggle simple fullscreen mode");
|
||||
println!("- S\tNext screen");
|
||||
println!("- M\tNext mode for this screen");
|
||||
println!("- D\tToggle window decorations");
|
||||
println!("- X\tMaximize window");
|
||||
println!("- Z\tMinimize window");
|
||||
|
||||
event_loop.run(move |event, elwt, control_flow| {
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::CloseRequested => control_flow.set_exit(),
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
virtual_keycode: Some(virtual_code),
|
||||
state,
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: key,
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match (virtual_code, state) {
|
||||
(VirtualKeyCode::Escape, _) => *control_flow = ControlFlow::Exit,
|
||||
(VirtualKeyCode::F, ElementState::Pressed) => {
|
||||
if window.fullscreen().is_some() {
|
||||
} => match key {
|
||||
Key::Escape => control_flow.set_exit(),
|
||||
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||
// See the `key_binding` example
|
||||
Key::Character(ch) => match ch.to_lowercase().as_str() {
|
||||
"f" | "b" if window.fullscreen().is_some() => {
|
||||
window.set_fullscreen(None);
|
||||
} else {
|
||||
window.set_fullscreen(fullscreen.clone());
|
||||
}
|
||||
}
|
||||
(VirtualKeyCode::S, ElementState::Pressed) => {
|
||||
println!("window.fullscreen {:?}", window.fullscreen());
|
||||
}
|
||||
(VirtualKeyCode::M, ElementState::Pressed) => {
|
||||
is_maximized = !is_maximized;
|
||||
window.set_maximized(is_maximized);
|
||||
}
|
||||
(VirtualKeyCode::D, ElementState::Pressed) => {
|
||||
decorations = !decorations;
|
||||
window.set_decorations(decorations);
|
||||
}
|
||||
"f" => {
|
||||
let fullscreen = Some(Fullscreen::Exclusive(mode.clone()));
|
||||
println!("Setting mode: {fullscreen:?}");
|
||||
window.set_fullscreen(fullscreen);
|
||||
}
|
||||
"b" => {
|
||||
let fullscreen = Some(Fullscreen::Borderless(Some(monitor.clone())));
|
||||
println!("Setting mode: {fullscreen:?}");
|
||||
window.set_fullscreen(fullscreen);
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
"c" => {
|
||||
window.set_simple_fullscreen(!window.simple_fullscreen());
|
||||
}
|
||||
"s" => {
|
||||
monitor_index += 1;
|
||||
if let Some(mon) = elwt.available_monitors().nth(monitor_index) {
|
||||
monitor = mon;
|
||||
} else {
|
||||
monitor_index = 0;
|
||||
monitor =
|
||||
elwt.available_monitors().next().expect("no monitor found!");
|
||||
}
|
||||
println!("Monitor: {:?}", monitor.name());
|
||||
|
||||
mode_index = 0;
|
||||
mode = monitor.video_modes().next().expect("no mode found");
|
||||
println!("Mode: {mode}");
|
||||
}
|
||||
"m" => {
|
||||
mode_index += 1;
|
||||
if let Some(m) = monitor.video_modes().nth(mode_index) {
|
||||
mode = m;
|
||||
} else {
|
||||
mode_index = 0;
|
||||
mode = monitor.video_modes().next().expect("no mode found");
|
||||
}
|
||||
println!("Mode: {mode}");
|
||||
}
|
||||
"d" => {
|
||||
decorations = !decorations;
|
||||
window.set_decorations(decorations);
|
||||
}
|
||||
"x" => {
|
||||
let is_maximized = window.is_maximized();
|
||||
window.set_maximized(!is_maximized);
|
||||
}
|
||||
"z" => {
|
||||
minimized = !minimized;
|
||||
window.set_minimized(minimized);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Enumerate monitors and prompt user to choose one
|
||||
fn prompt_for_monitor(event_loop: &EventLoop<()>) -> MonitorHandle {
|
||||
for (num, monitor) in event_loop.available_monitors().enumerate() {
|
||||
println!("Monitor #{}: {:?}", num, monitor.name());
|
||||
}
|
||||
|
||||
print!("Please write the number of the monitor to use: ");
|
||||
stdout().flush().unwrap();
|
||||
|
||||
let mut num = String::new();
|
||||
stdin().read_line(&mut num).unwrap();
|
||||
let num = num.trim().parse().ok().expect("Please enter a number");
|
||||
let monitor = event_loop
|
||||
.available_monitors()
|
||||
.nth(num)
|
||||
.expect("Please enter a valid ID");
|
||||
|
||||
println!("Using {:?}", monitor.name());
|
||||
|
||||
monitor
|
||||
}
|
||||
|
||||
fn prompt_for_video_mode(monitor: &MonitorHandle) -> VideoMode {
|
||||
for (i, video_mode) in monitor.video_modes().enumerate() {
|
||||
println!("Video mode #{}: {}", i, video_mode);
|
||||
}
|
||||
|
||||
print!("Please write the number of the video mode to use: ");
|
||||
stdout().flush().unwrap();
|
||||
|
||||
let mut num = String::new();
|
||||
stdin().read_line(&mut num).unwrap();
|
||||
let num = num.trim().parse().ok().expect("Please enter a number");
|
||||
let video_mode = monitor
|
||||
.video_modes()
|
||||
.nth(num)
|
||||
.expect("Please enter a valid ID");
|
||||
|
||||
println!("Using {}", video_mode);
|
||||
|
||||
video_mode
|
||||
}
|
||||
|
||||
@@ -1,14 +1,21 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, KeyboardInput, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::Key,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let _window = WindowBuilder::new()
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("Your faithful window")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
@@ -16,11 +23,7 @@ fn main() {
|
||||
let mut close_requested = false;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
use winit::event::{
|
||||
ElementState::Released,
|
||||
VirtualKeyCode::{N, Y},
|
||||
};
|
||||
*control_flow = ControlFlow::Wait;
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => {
|
||||
@@ -43,16 +46,18 @@ fn main() {
|
||||
// the Y key.
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
virtual_keycode: Some(virtual_code),
|
||||
state: Released,
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: key,
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
match virtual_code {
|
||||
Y => {
|
||||
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||
// See the `key_binding` example
|
||||
match key.as_ref() {
|
||||
Key::Character("y") => {
|
||||
if close_requested {
|
||||
// This is where you'll want to do any cleanup you need.
|
||||
println!("Buh-bye!");
|
||||
@@ -62,10 +67,10 @@ fn main() {
|
||||
// event loop (i.e. if it's a multi-window application), you need to
|
||||
// drop the window. That closes it, and results in `Destroyed` being
|
||||
// sent.
|
||||
*control_flow = ControlFlow::Exit;
|
||||
control_flow.set_exit();
|
||||
}
|
||||
}
|
||||
N => {
|
||||
Key::Character("n") => {
|
||||
if close_requested {
|
||||
println!("Your window will continue to stay by your side.");
|
||||
close_requested = false;
|
||||
@@ -77,6 +82,9 @@ fn main() {
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
|
||||
109
examples/ime.rs
Normal file
109
examples/ime.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use log::LevelFilter;
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
event::{ElementState, Event, Ime, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
keyboard::{Key, KeyCode},
|
||||
window::{ImePurpose, WindowBuilder},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new()
|
||||
.with_level(LevelFilter::Trace)
|
||||
.init()
|
||||
.unwrap();
|
||||
|
||||
println!("IME position will system default");
|
||||
println!("Click to set IME position to cursor's");
|
||||
println!("Press F2 to toggle IME. See the documentation of `set_ime_allowed` for more info");
|
||||
println!("Press F3 to cycle through IME purposes.");
|
||||
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(256f64, 128f64))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut ime_purpose = ImePurpose::Normal;
|
||||
let mut ime_allowed = true;
|
||||
window.set_ime_allowed(ime_allowed);
|
||||
|
||||
let mut may_show_ime = false;
|
||||
let mut cursor_position = PhysicalPosition::new(0.0, 0.0);
|
||||
let mut ime_pos = PhysicalPosition::new(0.0, 0.0);
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => *control_flow = ControlFlow::Exit,
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CursorMoved { position, .. },
|
||||
..
|
||||
} => {
|
||||
cursor_position = position;
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event:
|
||||
WindowEvent::MouseInput {
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
println!(
|
||||
"Setting ime position to {}, {}",
|
||||
cursor_position.x, cursor_position.y
|
||||
);
|
||||
ime_pos = cursor_position;
|
||||
if may_show_ime {
|
||||
window.set_ime_cursor_area(ime_pos, PhysicalSize::new(10, 10));
|
||||
}
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::Ime(event),
|
||||
..
|
||||
} => {
|
||||
println!("{event:?}");
|
||||
may_show_ime = event != Ime::Disabled;
|
||||
if may_show_ime {
|
||||
window.set_ime_cursor_area(ime_pos, PhysicalSize::new(10, 10));
|
||||
}
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::KeyboardInput { event, .. },
|
||||
..
|
||||
} => {
|
||||
println!("key: {event:?}");
|
||||
|
||||
if event.state == ElementState::Pressed && event.physical_key == KeyCode::F2 {
|
||||
ime_allowed = !ime_allowed;
|
||||
window.set_ime_allowed(ime_allowed);
|
||||
println!("\nIME allowed: {ime_allowed}\n");
|
||||
}
|
||||
if event.state == ElementState::Pressed && event.logical_key == Key::F3 {
|
||||
ime_purpose = match ime_purpose {
|
||||
ImePurpose::Normal => ImePurpose::Password,
|
||||
ImePurpose::Password => ImePurpose::Terminal,
|
||||
_ => ImePurpose::Normal,
|
||||
};
|
||||
window.set_ime_purpose(ime_purpose);
|
||||
println!("\nIME purpose: {ime_purpose:?}\n");
|
||||
}
|
||||
}
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
65
examples/key_binding.rs
Normal file
65
examples/key_binding.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))]
|
||||
use winit::{
|
||||
dpi::LogicalSize,
|
||||
event::{ElementState, Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
keyboard::{Key, ModifiersState},
|
||||
// WARNING: This is not available on all platforms (for example on the web).
|
||||
platform::modifier_supplement::KeyEventExtModifierSupplement,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
|
||||
fn main() {
|
||||
println!("This example is not supported on this platform");
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))]
|
||||
fn main() {
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
simple_logger::SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_inner_size(LogicalSize::new(400.0, 200.0))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut modifiers = ModifiersState::default();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::ModifiersChanged(new) => {
|
||||
modifiers = new.state();
|
||||
}
|
||||
WindowEvent::KeyboardInput { event, .. } => {
|
||||
if event.state == ElementState::Pressed && !event.repeat {
|
||||
match event.key_without_modifiers().as_ref() {
|
||||
Key::Character("1") => {
|
||||
if modifiers.shift_key() {
|
||||
println!("Shift + 1 | logical_key: {:?}", event.logical_key);
|
||||
} else {
|
||||
println!("1");
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
use winit::{
|
||||
dpi::LogicalSize,
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
|
||||
window.set_min_inner_size(Some(LogicalSize::new(400.0, 200.0)));
|
||||
window.set_max_inner_size(Some(LogicalSize::new(800.0, 400.0)));
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
println!("{:?}", event);
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => *control_flow = ControlFlow::Exit,
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
extern crate winit;
|
||||
|
||||
use winit::event::{Event, VirtualKeyCode, WindowEvent};
|
||||
use winit::event_loop::{ControlFlow, EventLoop};
|
||||
use winit::window::WindowBuilder;
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => *control_flow = ControlFlow::Exit,
|
||||
|
||||
// Keyboard input event to handle minimize via a hotkey
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::KeyboardInput { input, .. },
|
||||
window_id,
|
||||
} => {
|
||||
if window_id == window.id() {
|
||||
// Pressing the 'M' key will minimize the window
|
||||
if input.virtual_keycode == Some(VirtualKeyCode::M) {
|
||||
window.set_minimized(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,10 +1,58 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use winit::monitor::MonitorHandle;
|
||||
use winit::{event_loop::EventLoop, window::WindowBuilder};
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
|
||||
dbg!(window.available_monitors().collect::<Vec<_>>());
|
||||
dbg!(window.primary_monitor());
|
||||
if let Some(mon) = window.primary_monitor() {
|
||||
print_info("Primary output", mon);
|
||||
}
|
||||
|
||||
for mon in window.available_monitors() {
|
||||
if Some(&mon) == window.primary_monitor().as_ref() {
|
||||
continue;
|
||||
}
|
||||
|
||||
println!();
|
||||
print_info("Output", mon);
|
||||
}
|
||||
}
|
||||
|
||||
fn print_info(intro: &str, monitor: MonitorHandle) {
|
||||
if let Some(name) = monitor.name() {
|
||||
println!("{intro}: {name}");
|
||||
} else {
|
||||
println!("{intro}: [no name]");
|
||||
}
|
||||
|
||||
let PhysicalSize { width, height } = monitor.size();
|
||||
print!(" Current mode: {width}x{height}");
|
||||
if let Some(m_hz) = monitor.refresh_rate_millihertz() {
|
||||
println!(" @ {}.{} Hz", m_hz / 1000, m_hz % 1000);
|
||||
} else {
|
||||
println!();
|
||||
}
|
||||
|
||||
let PhysicalPosition { x, y } = monitor.position();
|
||||
println!(" Position: {x},{y}");
|
||||
|
||||
println!(" Scale factor: {}", monitor.scale_factor());
|
||||
|
||||
println!(" Available modes (width x height x bit-depth):");
|
||||
for mode in monitor.video_modes() {
|
||||
let PhysicalSize { width, height } = mode.size();
|
||||
let bits = mode.bit_depth();
|
||||
let m_hz = mode.refresh_rate_millihertz();
|
||||
println!(
|
||||
" {width}x{height}x{bits} @ {}.{} Hz",
|
||||
m_hz / 1000,
|
||||
m_hz % 1000
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
68
examples/mouse_wheel.rs
Normal file
68
examples/mouse_wheel.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("Mouse Wheel events")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
println!(
|
||||
r"
|
||||
When using so called 'natural scrolling' (scrolling that acts like on a touch screen), this is what to expect:
|
||||
|
||||
Moving your finger downwards on a scroll wheel should make the window move down, and you should see a positive Y scroll value.
|
||||
|
||||
When moving fingers on a trackpad down and to the right, you should see positive X and Y deltas, and the window should move down and to the right.
|
||||
|
||||
With reverse scrolling, you should see the inverse behavior.
|
||||
|
||||
In both cases the example window should move like the content of a scroll area in any other application.
|
||||
|
||||
In other words, the deltas indicate the direction in which to move the content (in this case the window)."
|
||||
);
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => control_flow.set_exit(),
|
||||
WindowEvent::MouseWheel { delta, .. } => match delta {
|
||||
winit::event::MouseScrollDelta::LineDelta(x, y) => {
|
||||
println!("mouse wheel Line Delta: ({x},{y})");
|
||||
let pixels_per_line = 120.0;
|
||||
let mut pos = window.outer_position().unwrap();
|
||||
pos.x += (x * pixels_per_line) as i32;
|
||||
pos.y += (y * pixels_per_line) as i32;
|
||||
window.set_outer_position(pos)
|
||||
}
|
||||
winit::event::MouseScrollDelta::PixelDelta(p) => {
|
||||
println!("mouse wheel Pixel Delta: ({},{})", p.x, p.y);
|
||||
let mut pos = window.outer_position().unwrap();
|
||||
pos.x += p.x as i32;
|
||||
pos.y += p.y as i32;
|
||||
window.set_outer_position(pos)
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,18 +1,22 @@
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
#[cfg(not(wasm_platform))]
|
||||
fn main() {
|
||||
use std::{collections::HashMap, sync::mpsc, thread, time::Duration};
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
||||
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::{CursorIcon, Fullscreen, WindowBuilder},
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::{Key, ModifiersState},
|
||||
window::{CursorGrabMode, CursorIcon, Fullscreen, WindowBuilder, WindowLevel},
|
||||
};
|
||||
|
||||
const WINDOW_COUNT: usize = 3;
|
||||
const WINDOW_SIZE: PhysicalSize<u32> = PhysicalSize::new(600, 400);
|
||||
|
||||
simple_logger::init().unwrap();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
let mut window_senders = HashMap::with_capacity(WINDOW_COUNT);
|
||||
for _ in 0..WINDOW_COUNT {
|
||||
@@ -21,11 +25,12 @@ fn main() {
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut video_modes: Vec<_> = window.current_monitor().video_modes().collect();
|
||||
let mut video_modes: Vec<_> = window.current_monitor().unwrap().video_modes().collect();
|
||||
let mut video_mode_id = 0usize;
|
||||
|
||||
let (tx, rx) = mpsc::channel();
|
||||
window_senders.insert(window.id(), tx);
|
||||
let mut modifiers = ModifiersState::default();
|
||||
thread::spawn(move || {
|
||||
while let Ok(event) = rx.recv() {
|
||||
match event {
|
||||
@@ -33,10 +38,10 @@ fn main() {
|
||||
// We need to update our chosen video mode if the window
|
||||
// was moved to an another monitor, so that the window
|
||||
// appears on this monitor instead when we go fullscreen
|
||||
let previous_video_mode = video_modes.iter().cloned().nth(video_mode_id);
|
||||
video_modes = window.current_monitor().video_modes().collect();
|
||||
let previous_video_mode = video_modes.get(video_mode_id).cloned();
|
||||
video_modes = window.current_monitor().unwrap().video_modes().collect();
|
||||
video_mode_id = video_mode_id.min(video_modes.len());
|
||||
let video_mode = video_modes.iter().nth(video_mode_id);
|
||||
let video_mode = video_modes.get(video_mode_id);
|
||||
|
||||
// Different monitors may support different video modes,
|
||||
// and the index we chose previously may now point to a
|
||||
@@ -44,100 +49,120 @@ fn main() {
|
||||
if video_mode != previous_video_mode.as_ref() {
|
||||
println!(
|
||||
"Window moved to another monitor, picked video mode: {}",
|
||||
video_modes.iter().nth(video_mode_id).unwrap()
|
||||
video_modes.get(video_mode_id).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
#[allow(deprecated)]
|
||||
WindowEvent::ModifiersChanged(new) => {
|
||||
modifiers = new.state();
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Released,
|
||||
virtual_keycode: Some(key),
|
||||
modifiers,
|
||||
logical_key: key,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
window.set_title(&format!("{:?}", key));
|
||||
let state = !modifiers.shift();
|
||||
use VirtualKeyCode::*;
|
||||
use Key::{ArrowLeft, ArrowRight};
|
||||
window.set_title(&format!("{key:?}"));
|
||||
let state = !modifiers.shift_key();
|
||||
match key {
|
||||
A => window.set_always_on_top(state),
|
||||
C => window.set_cursor_icon(match state {
|
||||
true => CursorIcon::Progress,
|
||||
false => CursorIcon::Default,
|
||||
}),
|
||||
D => window.set_decorations(!state),
|
||||
// Cycle through video modes
|
||||
Right | Left => {
|
||||
Key::ArrowRight | Key::ArrowLeft => {
|
||||
video_mode_id = match key {
|
||||
Left => video_mode_id.saturating_sub(1),
|
||||
Right => (video_modes.len() - 1).min(video_mode_id + 1),
|
||||
ArrowLeft => video_mode_id.saturating_sub(1),
|
||||
ArrowRight => (video_modes.len() - 1).min(video_mode_id + 1),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
println!(
|
||||
"Picking video mode: {}",
|
||||
video_modes.iter().nth(video_mode_id).unwrap()
|
||||
);
|
||||
println!("Picking video mode: {}", video_modes[video_mode_id]);
|
||||
}
|
||||
F => window.set_fullscreen(match (state, modifiers.alt()) {
|
||||
(true, false) => {
|
||||
Some(Fullscreen::Borderless(window.current_monitor()))
|
||||
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||
// See the `key_binding` example
|
||||
Key::Character(ch) => match ch.to_lowercase().as_str() {
|
||||
"1" => window.set_window_level(WindowLevel::AlwaysOnTop),
|
||||
"2" => window.set_window_level(WindowLevel::AlwaysOnBottom),
|
||||
"3" => window.set_window_level(WindowLevel::Normal),
|
||||
"c" => window.set_cursor_icon(match state {
|
||||
true => CursorIcon::Progress,
|
||||
false => CursorIcon::Default,
|
||||
}),
|
||||
"d" => window.set_decorations(!state),
|
||||
"f" => window.set_fullscreen(match (state, modifiers.alt_key()) {
|
||||
(true, false) => Some(Fullscreen::Borderless(None)),
|
||||
(true, true) => Some(Fullscreen::Exclusive(
|
||||
video_modes[video_mode_id].clone(),
|
||||
)),
|
||||
(false, _) => None,
|
||||
}),
|
||||
"l" if state => {
|
||||
if let Err(err) = window.set_cursor_grab(CursorGrabMode::Locked)
|
||||
{
|
||||
println!("error: {err}");
|
||||
}
|
||||
}
|
||||
(true, true) => Some(Fullscreen::Exclusive(
|
||||
video_modes.iter().nth(video_mode_id).unwrap().clone(),
|
||||
)),
|
||||
(false, _) => None,
|
||||
}),
|
||||
G => window.set_cursor_grab(state).unwrap(),
|
||||
H => window.set_cursor_visible(!state),
|
||||
I => {
|
||||
println!("Info:");
|
||||
println!("-> outer_position : {:?}", window.outer_position());
|
||||
println!("-> inner_position : {:?}", window.inner_position());
|
||||
println!("-> outer_size : {:?}", window.outer_size());
|
||||
println!("-> inner_size : {:?}", window.inner_size());
|
||||
println!("-> fullscreen : {:?}", window.fullscreen());
|
||||
}
|
||||
L => window.set_min_inner_size(match state {
|
||||
true => Some(WINDOW_SIZE),
|
||||
false => None,
|
||||
}),
|
||||
M => window.set_maximized(state),
|
||||
P => window.set_outer_position({
|
||||
let mut position = window.outer_position().unwrap();
|
||||
let sign = if state { 1 } else { -1 };
|
||||
position.x += 10 * sign;
|
||||
position.y += 10 * sign;
|
||||
position
|
||||
}),
|
||||
Q => window.request_redraw(),
|
||||
R => window.set_resizable(state),
|
||||
S => window.set_inner_size(match state {
|
||||
true => PhysicalSize::new(
|
||||
WINDOW_SIZE.width + 100,
|
||||
WINDOW_SIZE.height + 100,
|
||||
),
|
||||
false => WINDOW_SIZE,
|
||||
}),
|
||||
W => {
|
||||
if let Size::Physical(size) = WINDOW_SIZE.into() {
|
||||
window
|
||||
.set_cursor_position(Position::Physical(
|
||||
PhysicalPosition::new(
|
||||
size.width as i32 / 2,
|
||||
size.height as i32 / 2,
|
||||
),
|
||||
))
|
||||
.unwrap()
|
||||
"g" if state => {
|
||||
if let Err(err) =
|
||||
window.set_cursor_grab(CursorGrabMode::Confined)
|
||||
{
|
||||
println!("error: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Z => {
|
||||
window.set_visible(false);
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
window.set_visible(true);
|
||||
}
|
||||
"g" | "l" if !state => {
|
||||
if let Err(err) = window.set_cursor_grab(CursorGrabMode::None) {
|
||||
println!("error: {err}");
|
||||
}
|
||||
}
|
||||
"h" => window.set_cursor_visible(!state),
|
||||
"i" => {
|
||||
println!("Info:");
|
||||
println!("-> outer_position : {:?}", window.outer_position());
|
||||
println!("-> inner_position : {:?}", window.inner_position());
|
||||
println!("-> outer_size : {:?}", window.outer_size());
|
||||
println!("-> inner_size : {:?}", window.inner_size());
|
||||
println!("-> fullscreen : {:?}", window.fullscreen());
|
||||
}
|
||||
"l" => window.set_min_inner_size(match state {
|
||||
true => Some(WINDOW_SIZE),
|
||||
false => None,
|
||||
}),
|
||||
"m" => window.set_maximized(state),
|
||||
"p" => window.set_outer_position({
|
||||
let mut position = window.outer_position().unwrap();
|
||||
let sign = if state { 1 } else { -1 };
|
||||
position.x += 10 * sign;
|
||||
position.y += 10 * sign;
|
||||
position
|
||||
}),
|
||||
"q" => window.request_redraw(),
|
||||
"r" => window.set_resizable(state),
|
||||
"s" => window.set_inner_size(match state {
|
||||
true => PhysicalSize::new(
|
||||
WINDOW_SIZE.width + 100,
|
||||
WINDOW_SIZE.height + 100,
|
||||
),
|
||||
false => WINDOW_SIZE,
|
||||
}),
|
||||
"w" => {
|
||||
if let Size::Physical(size) = WINDOW_SIZE.into() {
|
||||
window
|
||||
.set_cursor_position(Position::Physical(
|
||||
PhysicalPosition::new(
|
||||
size.width as i32 / 2,
|
||||
size.height as i32 / 2,
|
||||
),
|
||||
))
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
"z" => {
|
||||
window.set_visible(false);
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
window.set_visible(true);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
@@ -147,19 +172,19 @@ fn main() {
|
||||
});
|
||||
}
|
||||
event_loop.run(move |event, _event_loop, control_flow| {
|
||||
*control_flow = match !window_senders.is_empty() {
|
||||
true => ControlFlow::Wait,
|
||||
false => ControlFlow::Exit,
|
||||
match !window_senders.is_empty() {
|
||||
true => control_flow.set_wait(),
|
||||
false => control_flow.set_exit(),
|
||||
};
|
||||
match event {
|
||||
Event::WindowEvent { event, window_id } => match event {
|
||||
WindowEvent::CloseRequested
|
||||
| WindowEvent::Destroyed
|
||||
| WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Released,
|
||||
virtual_keycode: Some(VirtualKeyCode::Escape),
|
||||
logical_key: Key::Escape,
|
||||
..
|
||||
},
|
||||
..
|
||||
@@ -174,12 +199,12 @@ fn main() {
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[cfg(wasm_platform)]
|
||||
fn main() {
|
||||
panic!("Example not supported on Wasm");
|
||||
}
|
||||
|
||||
@@ -1,50 +1,69 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyboardInput, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::Key,
|
||||
window::Window,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let mut windows = HashMap::new();
|
||||
for _ in 0..3 {
|
||||
let window = Window::new(&event_loop).unwrap();
|
||||
println!("Opened a new window: {:?}", window.id());
|
||||
windows.insert(window.id(), window);
|
||||
}
|
||||
|
||||
println!("Press N to open a new window.");
|
||||
|
||||
event_loop.run(move |event, event_loop, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, window_id } => {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
println!("Window {:?} has received the signal to close", window_id);
|
||||
println!("Window {window_id:?} has received the signal to close");
|
||||
|
||||
// This drops the window, causing it to close.
|
||||
windows.remove(&window_id);
|
||||
|
||||
if windows.is_empty() {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
control_flow.set_exit();
|
||||
}
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Pressed,
|
||||
logical_key: Key::Character(c),
|
||||
..
|
||||
},
|
||||
is_synthetic: false,
|
||||
..
|
||||
} => {
|
||||
let window = Window::new(&event_loop).unwrap();
|
||||
} if matches!(c.as_ref(), "n" | "N") => {
|
||||
let window = Window::new(event_loop).unwrap();
|
||||
println!("Opened a new window: {:?}", window.id());
|
||||
windows.insert(window.id(), window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
Event::RedrawRequested(window_id) => {
|
||||
if let Some(window) = windows.get(&window_id) {
|
||||
fill::fill_window(window);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
@@ -14,13 +20,13 @@ fn main() {
|
||||
.unwrap();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
println!("{:?}", event);
|
||||
println!("{event:?}");
|
||||
|
||||
*control_flow = ControlFlow::Wait;
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::CloseRequested => control_flow.set_exit(),
|
||||
WindowEvent::MouseInput {
|
||||
state: ElementState::Released,
|
||||
..
|
||||
@@ -31,6 +37,7 @@ fn main() {
|
||||
},
|
||||
Event::RedrawRequested(_) => {
|
||||
println!("\nredrawing!\n");
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@@ -1,39 +1,58 @@
|
||||
use std::{thread, time};
|
||||
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
#[cfg(not(wasm_platform))]
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
use std::{sync::Arc, thread, time};
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
let window = {
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
Arc::new(window)
|
||||
};
|
||||
|
||||
thread::spawn(move || loop {
|
||||
thread::sleep(time::Duration::from_secs(1));
|
||||
window.request_redraw();
|
||||
thread::spawn({
|
||||
let window = window.clone();
|
||||
move || loop {
|
||||
thread::sleep(time::Duration::from_secs(1));
|
||||
window.request_redraw();
|
||||
}
|
||||
});
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
println!("{:?}", event);
|
||||
println!("{event:?}");
|
||||
|
||||
*control_flow = ControlFlow::Wait;
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
_ => (),
|
||||
},
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => control_flow.set_exit(),
|
||||
Event::RedrawRequested(_) => {
|
||||
println!("\nredrawing!\n");
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(wasm_platform)]
|
||||
fn main() {
|
||||
unimplemented!() // `Window` can't be sent between threads
|
||||
}
|
||||
|
||||
@@ -1,44 +1,56 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
dpi::LogicalSize,
|
||||
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::KeyCode,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let mut resizable = false;
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("Hit space to toggle resizability.")
|
||||
.with_inner_size(LogicalSize::new(400.0, 200.0))
|
||||
.with_inner_size(LogicalSize::new(600.0, 300.0))
|
||||
.with_min_inner_size(LogicalSize::new(400.0, 200.0))
|
||||
.with_max_inner_size(LogicalSize::new(800.0, 400.0))
|
||||
.with_resizable(resizable)
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::CloseRequested => control_flow.set_exit(),
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
virtual_keycode: Some(VirtualKeyCode::Space),
|
||||
event:
|
||||
KeyEvent {
|
||||
physical_key: KeyCode::Space,
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
resizable = !resizable;
|
||||
println!("Resizable: {}", resizable);
|
||||
println!("Resizable: {resizable}");
|
||||
window.set_resizable(resizable);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
});
|
||||
|
||||
79
examples/theme.rs
Normal file
79
examples/theme.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
keyboard::Key,
|
||||
window::{Theme, WindowBuilder},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.with_theme(Some(Theme::Dark))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
println!("Initial theme: {:?}", window.theme());
|
||||
println!("debugging keys:");
|
||||
println!(" (A) Automatic theme");
|
||||
println!(" (L) Light theme");
|
||||
println!(" (D) Dark theme");
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => *control_flow = ControlFlow::Exit,
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::ThemeChanged(theme),
|
||||
window_id,
|
||||
..
|
||||
} if window_id == window.id() => {
|
||||
println!("Theme is changed: {theme:?}")
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event:
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: key,
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match key.as_ref() {
|
||||
Key::Character("A" | "a") => {
|
||||
println!("Theme was: {:?}", window.theme());
|
||||
window.set_theme(None);
|
||||
}
|
||||
Key::Character("L" | "l") => {
|
||||
println!("Theme was: {:?}", window.theme());
|
||||
window.set_theme(Some(Theme::Light));
|
||||
}
|
||||
Key::Character("D" | "d") => {
|
||||
println!("Theme was: {:?}", window.theme());
|
||||
window.set_theme(Some(Theme::Dark));
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::RedrawRequested(_) => {
|
||||
println!("\nredrawing!\n");
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,16 +1,26 @@
|
||||
use instant::Instant;
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use std::time::Duration;
|
||||
#[cfg(not(wasm_platform))]
|
||||
use std::time::Instant;
|
||||
#[cfg(wasm_platform)]
|
||||
use web_time::Instant;
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, StartCause, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let _window = WindowBuilder::new()
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
@@ -18,20 +28,23 @@ fn main() {
|
||||
let timer_length = Duration::new(1, 0);
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
println!("{:?}", event);
|
||||
println!("{event:?}");
|
||||
|
||||
match event {
|
||||
Event::NewEvents(StartCause::Init) => {
|
||||
*control_flow = ControlFlow::WaitUntil(Instant::now() + timer_length)
|
||||
control_flow.set_wait_until(Instant::now() + timer_length);
|
||||
}
|
||||
Event::NewEvents(StartCause::ResumeTimeReached { .. }) => {
|
||||
*control_flow = ControlFlow::WaitUntil(Instant::now() + timer_length);
|
||||
control_flow.set_wait_until(Instant::now() + timer_length);
|
||||
println!("\nTimer\n");
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => *control_flow = ControlFlow::Exit,
|
||||
} => control_flow.set_exit(),
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
|
||||
51
examples/touchpad_gestures.rs
Normal file
51
examples/touchpad_gestures.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("Touchpad gestures")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
println!("Only supported on macOS at the moment.");
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::TouchpadMagnify { delta, .. } => {
|
||||
if delta > 0.0 {
|
||||
println!("Zoomed in {delta}");
|
||||
} else {
|
||||
println!("Zoomed out {delta}");
|
||||
}
|
||||
}
|
||||
WindowEvent::SmartMagnify { .. } => {
|
||||
println!("Smart zoom");
|
||||
}
|
||||
WindowEvent::TouchpadRotate { delta, .. } => {
|
||||
if delta > 0.0 {
|
||||
println!("Rotated counterclockwise {delta}");
|
||||
} else {
|
||||
println!("Rotated clockwise {delta}");
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
} else if let Event::RedrawRequested(_) = event {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,11 +1,17 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
@@ -17,14 +23,17 @@ fn main() {
|
||||
window.set_title("A fantastic window!");
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
println!("{:?}", event);
|
||||
control_flow.set_wait();
|
||||
println!("{event:?}");
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => *control_flow = ControlFlow::Exit,
|
||||
} => control_flow.set_exit(),
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
|
||||
86
examples/util/fill.rs
Normal file
86
examples/util/fill.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
//! Fill the window buffer with a solid color.
|
||||
//!
|
||||
//! Launching a window without drawing to it has unpredictable results varying from platform to
|
||||
//! platform. In order to have well-defined examples, this module provides an easy way to
|
||||
//! fill the window buffer with a solid color.
|
||||
//!
|
||||
//! The `softbuffer` crate is used, largely because of its ease of use. `glutin` or `wgpu` could
|
||||
//! also be used to fill the window buffer, but they are more complicated to use.
|
||||
|
||||
use winit::window::Window;
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
pub(super) fn fill_window(window: &Window) {
|
||||
use softbuffer::{Context, Surface};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::num::NonZeroU32;
|
||||
use winit::window::WindowId;
|
||||
|
||||
/// The graphics context used to draw to a window.
|
||||
struct GraphicsContext {
|
||||
/// The global softbuffer context.
|
||||
context: Context,
|
||||
|
||||
/// The hash map of window IDs to surfaces.
|
||||
surfaces: HashMap<WindowId, Surface>,
|
||||
}
|
||||
|
||||
impl GraphicsContext {
|
||||
fn new(w: &Window) -> Self {
|
||||
Self {
|
||||
context: unsafe { Context::new(w) }.expect("Failed to create a softbuffer context"),
|
||||
surfaces: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn surface(&mut self, w: &Window) -> &mut Surface {
|
||||
self.surfaces.entry(w.id()).or_insert_with(|| {
|
||||
unsafe { Surface::new(&self.context, w) }
|
||||
.expect("Failed to create a softbuffer surface")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
// NOTE: You should never do things like that, create context and drop it before
|
||||
// you drop the event loop. We do this for brevity to not blow up examples. We use
|
||||
// ManuallyDrop to prevent destructors from running.
|
||||
//
|
||||
// A static, thread-local map of graphics contexts to open windows.
|
||||
static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = ManuallyDrop::new(RefCell::new(None));
|
||||
}
|
||||
|
||||
GC.with(|gc| {
|
||||
// Either get the last context used or create a new one.
|
||||
let mut gc = gc.borrow_mut();
|
||||
let surface = gc
|
||||
.get_or_insert_with(|| GraphicsContext::new(window))
|
||||
.surface(window);
|
||||
|
||||
// Fill a buffer with a solid color.
|
||||
const DARK_GRAY: u32 = 0xFF181818;
|
||||
let size = window.inner_size();
|
||||
|
||||
surface
|
||||
.resize(
|
||||
NonZeroU32::new(size.width).expect("Width must be greater than zero"),
|
||||
NonZeroU32::new(size.height).expect("Height must be greater than zero"),
|
||||
)
|
||||
.expect("Failed to resize the softbuffer surface");
|
||||
|
||||
let mut buffer = surface
|
||||
.buffer_mut()
|
||||
.expect("Failed to get the softbuffer buffer");
|
||||
buffer.fill(DARK_GRAY);
|
||||
buffer
|
||||
.present()
|
||||
.expect("Failed to present the softbuffer buffer");
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
pub(super) fn fill_window(_window: &Window) {
|
||||
// No-op on mobile platforms.
|
||||
}
|
||||
@@ -1,13 +1,22 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::event_loop::EventLoop;
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
let monitor = event_loop.primary_monitor();
|
||||
let monitor = match event_loop.primary_monitor() {
|
||||
Some(monitor) => monitor,
|
||||
None => {
|
||||
println!("No primary monitor detected.");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
println!("Listing available video modes:");
|
||||
|
||||
for mode in monitor.video_modes() {
|
||||
println!("{}", mode);
|
||||
println!("{mode}");
|
||||
}
|
||||
}
|
||||
|
||||
115
examples/web.rs
115
examples/web.rs
@@ -1,7 +1,10 @@
|
||||
#![allow(clippy::disallowed_methods, clippy::single_match)]
|
||||
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::KeyCode,
|
||||
window::{Fullscreen, WindowBuilder},
|
||||
};
|
||||
|
||||
pub fn main() {
|
||||
@@ -12,63 +15,99 @@ pub fn main() {
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
#[cfg(feature = "web-sys")]
|
||||
{
|
||||
use winit::platform::web::WindowExtWebSys;
|
||||
|
||||
let canvas = window.canvas();
|
||||
|
||||
let window = web_sys::window().unwrap();
|
||||
let document = window.document().unwrap();
|
||||
let body = document.body().unwrap();
|
||||
|
||||
body.append_child(&canvas)
|
||||
.expect("Append canvas to HTML body");
|
||||
}
|
||||
|
||||
#[cfg(feature = "stdweb")]
|
||||
{
|
||||
use std_web::web::INode;
|
||||
use winit::platform::web::WindowExtStdweb;
|
||||
|
||||
let canvas = window.canvas();
|
||||
|
||||
let document = std_web::web::document();
|
||||
let body: std_web::web::Node = document.body().expect("Get HTML body").into();
|
||||
|
||||
body.append_child(&canvas);
|
||||
}
|
||||
#[cfg(wasm_platform)]
|
||||
let log_list = wasm::insert_canvas_and_create_log_list(&window);
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
control_flow.set_wait();
|
||||
|
||||
#[cfg(feature = "web-sys")]
|
||||
log::debug!("{:?}", event);
|
||||
|
||||
#[cfg(feature = "stdweb")]
|
||||
std_web::console!(log, "%s", format!("{:?}", event));
|
||||
#[cfg(wasm_platform)]
|
||||
wasm::log_event(&log_list, &event);
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => *control_flow = ControlFlow::Exit,
|
||||
} if window_id == window.id() => control_flow.set_exit(),
|
||||
Event::MainEventsCleared => {
|
||||
window.request_redraw();
|
||||
}
|
||||
Event::WindowEvent {
|
||||
window_id,
|
||||
event:
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
physical_key: KeyCode::KeyF,
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
} if window_id == window.id() => {
|
||||
if window.fullscreen().is_some() {
|
||||
window.set_fullscreen(None);
|
||||
} else {
|
||||
window.set_fullscreen(Some(Fullscreen::Borderless(None)));
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(feature = "web-sys")]
|
||||
#[cfg(wasm_platform)]
|
||||
mod wasm {
|
||||
use wasm_bindgen::prelude::*;
|
||||
use winit::{event::Event, window::Window};
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn run() {
|
||||
console_log::init_with_level(log::Level::Debug);
|
||||
console_log::init_with_level(log::Level::Debug).expect("error initializing logger");
|
||||
|
||||
#[allow(clippy::main_recursion)]
|
||||
super::main();
|
||||
}
|
||||
|
||||
pub fn insert_canvas_and_create_log_list(window: &Window) -> web_sys::Element {
|
||||
use winit::platform::web::WindowExtWebSys;
|
||||
|
||||
let canvas = window.canvas().unwrap();
|
||||
|
||||
let window = web_sys::window().unwrap();
|
||||
let document = window.document().unwrap();
|
||||
let body = document.body().unwrap();
|
||||
|
||||
// Set a background color for the canvas to make it easier to tell where the canvas is for debugging purposes.
|
||||
canvas
|
||||
.style()
|
||||
.set_property("background-color", "crimson")
|
||||
.unwrap();
|
||||
body.append_child(&canvas).unwrap();
|
||||
|
||||
let log_header = document.create_element("h2").unwrap();
|
||||
log_header.set_text_content(Some("Event Log"));
|
||||
body.append_child(&log_header).unwrap();
|
||||
|
||||
let log_list = document.create_element("ul").unwrap();
|
||||
body.append_child(&log_list).unwrap();
|
||||
log_list
|
||||
}
|
||||
|
||||
pub fn log_event(log_list: &web_sys::Element, event: &Event<()>) {
|
||||
log::debug!("{:?}", event);
|
||||
|
||||
// Getting access to browser logs requires a lot of setup on mobile devices.
|
||||
// So we implement this basic logging system into the page to give developers an easy alternative.
|
||||
// As a bonus its also kind of handy on desktop.
|
||||
if let Event::WindowEvent { event, .. } = &event {
|
||||
let window = web_sys::window().unwrap();
|
||||
let document = window.document().unwrap();
|
||||
let log = document.create_element("li").unwrap();
|
||||
log.set_text_content(Some(&format!("{event:?}")));
|
||||
log_list
|
||||
.insert_before(&log, log_list.first_child().as_ref())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
105
examples/web_aspect_ratio.rs
Normal file
105
examples/web_aspect_ratio.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
#![allow(clippy::disallowed_methods)]
|
||||
|
||||
pub fn main() {
|
||||
println!("This example must be run with cargo run-wasm --example web_aspect_ratio")
|
||||
}
|
||||
|
||||
#[cfg(wasm_platform)]
|
||||
mod wasm {
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::HtmlCanvasElement;
|
||||
use winit::{
|
||||
dpi::PhysicalSize,
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::{Window, WindowBuilder},
|
||||
};
|
||||
|
||||
const EXPLANATION: &str = "
|
||||
This example draws a circle in the middle of a 4/1 aspect ratio canvas which acts as a useful demonstration of winit's resize handling on web.
|
||||
Even when the browser window is resized or aspect-ratio of the canvas changed the circle should always:
|
||||
* Fill the entire width or height of the canvas (whichever is smaller) without exceeding it.
|
||||
* Be perfectly round
|
||||
* Not be blurry or pixelated (there is no antialiasing so you may still see jagged edges depending on the DPI of your monitor)
|
||||
|
||||
Currently winit does not handle resizes on web so the circle is rendered incorrectly.
|
||||
This example demonstrates the desired future functionality which will possibly be provided by https://github.com/rust-windowing/winit/pull/2074
|
||||
";
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn run() {
|
||||
console_log::init_with_level(log::Level::Debug).expect("error initializing logger");
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
// When running in a non-wasm environment this would set the window size to 100x100.
|
||||
// However in this example it just sets a default initial size of 100x100 that is immediately overwritten due to the layout + styling of the page.
|
||||
.with_inner_size(PhysicalSize::new(100, 100))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let canvas = create_canvas(&window);
|
||||
|
||||
// Render once with the size info we currently have
|
||||
render_circle(&canvas, window.inner_size());
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::Resized(resize),
|
||||
window_id,
|
||||
} if window_id == window.id() => {
|
||||
render_circle(&canvas, resize);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn create_canvas(window: &Window) -> HtmlCanvasElement {
|
||||
use winit::platform::web::WindowExtWebSys;
|
||||
|
||||
let web_window = web_sys::window().unwrap();
|
||||
let document = web_window.document().unwrap();
|
||||
let body = document.body().unwrap();
|
||||
|
||||
// Set a background color for the canvas to make it easier to tell the where the canvas is for debugging purposes.
|
||||
let canvas = window.canvas().unwrap();
|
||||
canvas
|
||||
.style()
|
||||
.set_css_text("display: block; background-color: crimson; margin: auto; width: 50%; aspect-ratio: 4 / 1;");
|
||||
body.append_child(&canvas).unwrap();
|
||||
|
||||
let explanation = document.create_element("pre").unwrap();
|
||||
explanation.set_text_content(Some(EXPLANATION));
|
||||
body.append_child(&explanation).unwrap();
|
||||
|
||||
canvas
|
||||
}
|
||||
|
||||
pub fn render_circle(canvas: &HtmlCanvasElement, size: PhysicalSize<u32>) {
|
||||
log::info!("rendering circle with canvas size: {:?}", size);
|
||||
let context = canvas
|
||||
.get_context("2d")
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.dyn_into::<web_sys::CanvasRenderingContext2d>()
|
||||
.unwrap();
|
||||
|
||||
context.begin_path();
|
||||
context
|
||||
.arc(
|
||||
size.width as f64 / 2.0,
|
||||
size.height as f64 / 2.0,
|
||||
size.width.min(size.height) as f64 / 2.0,
|
||||
0.0,
|
||||
std::f64::consts::PI * 2.0,
|
||||
)
|
||||
.unwrap();
|
||||
context.fill();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,17 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
@@ -15,17 +21,20 @@ fn main() {
|
||||
.unwrap();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
println!("{:?}", event);
|
||||
control_flow.set_wait();
|
||||
println!("{event:?}");
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => *control_flow = ControlFlow::Exit,
|
||||
} if window_id == window.id() => control_flow.set_exit(),
|
||||
Event::MainEventsCleared => {
|
||||
window.request_redraw();
|
||||
}
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
|
||||
75
examples/window_buttons.rs
Normal file
75
examples/window_buttons.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
// This example is used by developers to test various window functions.
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
dpi::LogicalSize,
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::{DeviceEvents, EventLoop},
|
||||
keyboard::Key,
|
||||
window::{WindowBuilder, WindowButtons},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.with_inner_size(LogicalSize::new(300.0, 300.0))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
eprintln!("Window Button keys:");
|
||||
eprintln!(" (F) Toggle close button");
|
||||
eprintln!(" (G) Toggle maximize button");
|
||||
eprintln!(" (H) Toggle minimize button");
|
||||
|
||||
event_loop.listen_device_events(DeviceEvents::Always);
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event:
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: key,
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match key.as_ref() {
|
||||
Key::Character("F" | "f") => {
|
||||
let buttons = window.enabled_buttons();
|
||||
window.set_enabled_buttons(buttons ^ WindowButtons::CLOSE);
|
||||
}
|
||||
Key::Character("G" | "g") => {
|
||||
let buttons = window.enabled_buttons();
|
||||
window.set_enabled_buttons(buttons ^ WindowButtons::MAXIMIZE);
|
||||
}
|
||||
Key::Character("H" | "h") => {
|
||||
let buttons = window.enabled_buttons();
|
||||
window.set_enabled_buttons(buttons ^ WindowButtons::MINIMIZE);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => control_flow.set_exit(),
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,14 +1,21 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
// This example is used by developers to test various window functions.
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
dpi::{LogicalSize, PhysicalSize},
|
||||
event::{DeviceEvent, ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
event::{DeviceEvent, ElementState, Event, KeyEvent, RawKeyEvent, WindowEvent},
|
||||
event_loop::{DeviceEvents, EventLoop},
|
||||
keyboard::{Key, KeyCode},
|
||||
window::{Fullscreen, WindowBuilder},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
@@ -20,35 +27,40 @@ fn main() {
|
||||
eprintln!("debugging keys:");
|
||||
eprintln!(" (E) Enter exclusive fullscreen");
|
||||
eprintln!(" (F) Toggle borderless fullscreen");
|
||||
eprintln!(" (P) Toggle borderless fullscreen on system's preffered monitor");
|
||||
eprintln!(" (M) Toggle minimized");
|
||||
eprintln!(" (Q) Quit event loop");
|
||||
eprintln!(" (V) Toggle visibility");
|
||||
eprintln!(" (X) Toggle maximized");
|
||||
|
||||
let mut minimized = false;
|
||||
let mut maximized = false;
|
||||
let mut visible = true;
|
||||
|
||||
event_loop.listen_device_events(DeviceEvents::Always);
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
// This used to use the virtual key, but the new API
|
||||
// only provides the `physical_key` (`Code`).
|
||||
Event::DeviceEvent {
|
||||
event:
|
||||
DeviceEvent::Key(KeyboardInput {
|
||||
virtual_keycode: Some(key),
|
||||
state: ElementState::Pressed,
|
||||
DeviceEvent::Key(RawKeyEvent {
|
||||
physical_key,
|
||||
state: ElementState::Released,
|
||||
..
|
||||
}),
|
||||
..
|
||||
} => match key {
|
||||
VirtualKeyCode::M => {
|
||||
} => match physical_key {
|
||||
KeyCode::KeyM => {
|
||||
if minimized {
|
||||
minimized = !minimized;
|
||||
window.set_minimized(minimized);
|
||||
window.focus_window();
|
||||
}
|
||||
}
|
||||
VirtualKeyCode::V => {
|
||||
KeyCode::KeyV => {
|
||||
if !visible {
|
||||
visible = !visible;
|
||||
window.set_visible(visible);
|
||||
@@ -57,60 +69,74 @@ fn main() {
|
||||
_ => (),
|
||||
},
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::KeyboardInput { input, .. },
|
||||
event:
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: Key::Character(key_str),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match input {
|
||||
KeyboardInput {
|
||||
virtual_keycode: Some(key),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
} => match key {
|
||||
VirtualKeyCode::E => {
|
||||
fn area(size: PhysicalSize<u32>) -> u32 {
|
||||
size.width * size.height
|
||||
}
|
||||
} => match key_str.as_ref() {
|
||||
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||
// See the `key_binding` example
|
||||
"e" => {
|
||||
fn area(size: PhysicalSize<u32>) -> u32 {
|
||||
size.width * size.height
|
||||
}
|
||||
|
||||
let monitor = window.current_monitor().unwrap();
|
||||
if let Some(mode) = monitor
|
||||
.video_modes()
|
||||
.max_by(|a, b| area(a.size()).cmp(&area(b.size())))
|
||||
{
|
||||
window.set_fullscreen(Some(Fullscreen::Exclusive(mode)));
|
||||
} else {
|
||||
eprintln!("no video modes available");
|
||||
}
|
||||
}
|
||||
"f" => {
|
||||
if window.fullscreen().is_some() {
|
||||
window.set_fullscreen(None);
|
||||
} else {
|
||||
let monitor = window.current_monitor();
|
||||
if let Some(mode) = monitor
|
||||
.video_modes()
|
||||
.max_by(|a, b| area(a.size()).cmp(&area(b.size())))
|
||||
{
|
||||
window.set_fullscreen(Some(Fullscreen::Exclusive(mode)));
|
||||
} else {
|
||||
eprintln!("no video modes available");
|
||||
}
|
||||
window.set_fullscreen(Some(Fullscreen::Borderless(monitor)));
|
||||
}
|
||||
VirtualKeyCode::F => {
|
||||
if window.fullscreen().is_some() {
|
||||
window.set_fullscreen(None);
|
||||
} else {
|
||||
let monitor = window.current_monitor();
|
||||
window.set_fullscreen(Some(Fullscreen::Borderless(monitor)));
|
||||
}
|
||||
}
|
||||
"p" => {
|
||||
if window.fullscreen().is_some() {
|
||||
window.set_fullscreen(None);
|
||||
} else {
|
||||
window.set_fullscreen(Some(Fullscreen::Borderless(None)));
|
||||
}
|
||||
VirtualKeyCode::M => {
|
||||
minimized = !minimized;
|
||||
window.set_minimized(minimized);
|
||||
}
|
||||
VirtualKeyCode::Q => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
VirtualKeyCode::V => {
|
||||
visible = !visible;
|
||||
window.set_visible(visible);
|
||||
}
|
||||
VirtualKeyCode::X => {
|
||||
maximized = !maximized;
|
||||
window.set_maximized(maximized);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
}
|
||||
"m" => {
|
||||
minimized = !minimized;
|
||||
window.set_minimized(minimized);
|
||||
}
|
||||
"q" => {
|
||||
control_flow.set_exit();
|
||||
}
|
||||
"v" => {
|
||||
visible = !visible;
|
||||
window.set_visible(visible);
|
||||
}
|
||||
"x" => {
|
||||
let is_maximized = window.is_maximized();
|
||||
window.set_maximized(!is_maximized);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => *control_flow = ControlFlow::Exit,
|
||||
} if window_id == window.id() => control_flow.set_exit(),
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
|
||||
146
examples/window_drag_resize.rs
Normal file
146
examples/window_drag_resize.rs
Normal file
@@ -0,0 +1,146 @@
|
||||
//! Demonstrates capability to create in-app draggable regions for client-side decoration support.
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
keyboard::Key,
|
||||
window::{CursorIcon, ResizeDirection, WindowBuilder},
|
||||
};
|
||||
|
||||
const BORDER: f64 = 8.0;
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(600.0, 400.0))
|
||||
.with_min_inner_size(winit::dpi::LogicalSize::new(400.0, 200.0))
|
||||
.with_decorations(false)
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut border = false;
|
||||
let mut cursor_location = None;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| match event {
|
||||
Event::NewEvents(StartCause::Init) => {
|
||||
eprintln!("Press 'B' to toggle borderless")
|
||||
}
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
if !window.is_decorated() {
|
||||
let new_location =
|
||||
cursor_resize_direction(window.inner_size(), position, BORDER);
|
||||
|
||||
if new_location != cursor_location {
|
||||
cursor_location = new_location;
|
||||
window.set_cursor_icon(cursor_direction_icon(cursor_location))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WindowEvent::MouseInput {
|
||||
state: ElementState::Pressed,
|
||||
button: MouseButton::Left,
|
||||
..
|
||||
} => {
|
||||
if let Some(dir) = cursor_location {
|
||||
let _res = window.drag_resize_window(dir);
|
||||
}
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Released,
|
||||
logical_key: Key::Character(c),
|
||||
..
|
||||
},
|
||||
..
|
||||
} if matches!(c.as_ref(), "B" | "b") => {
|
||||
border = !border;
|
||||
window.set_decorations(border);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
});
|
||||
}
|
||||
|
||||
fn cursor_direction_icon(resize_direction: Option<ResizeDirection>) -> CursorIcon {
|
||||
match resize_direction {
|
||||
Some(resize_direction) => match resize_direction {
|
||||
ResizeDirection::East => CursorIcon::EResize,
|
||||
ResizeDirection::North => CursorIcon::NResize,
|
||||
ResizeDirection::NorthEast => CursorIcon::NeResize,
|
||||
ResizeDirection::NorthWest => CursorIcon::NwResize,
|
||||
ResizeDirection::South => CursorIcon::SResize,
|
||||
ResizeDirection::SouthEast => CursorIcon::SeResize,
|
||||
ResizeDirection::SouthWest => CursorIcon::SwResize,
|
||||
ResizeDirection::West => CursorIcon::WResize,
|
||||
},
|
||||
None => CursorIcon::Default,
|
||||
}
|
||||
}
|
||||
|
||||
fn cursor_resize_direction(
|
||||
win_size: winit::dpi::PhysicalSize<u32>,
|
||||
position: winit::dpi::PhysicalPosition<f64>,
|
||||
border_size: f64,
|
||||
) -> Option<ResizeDirection> {
|
||||
enum XDirection {
|
||||
West,
|
||||
East,
|
||||
Default,
|
||||
}
|
||||
|
||||
enum YDirection {
|
||||
North,
|
||||
South,
|
||||
Default,
|
||||
}
|
||||
|
||||
let xdir = if position.x < border_size {
|
||||
XDirection::West
|
||||
} else if position.x > (win_size.width as f64 - border_size) {
|
||||
XDirection::East
|
||||
} else {
|
||||
XDirection::Default
|
||||
};
|
||||
|
||||
let ydir = if position.y < border_size {
|
||||
YDirection::North
|
||||
} else if position.y > (win_size.height as f64 - border_size) {
|
||||
YDirection::South
|
||||
} else {
|
||||
YDirection::Default
|
||||
};
|
||||
|
||||
Some(match xdir {
|
||||
XDirection::West => match ydir {
|
||||
YDirection::North => ResizeDirection::NorthWest,
|
||||
YDirection::South => ResizeDirection::SouthWest,
|
||||
YDirection::Default => ResizeDirection::West,
|
||||
},
|
||||
|
||||
XDirection::East => match ydir {
|
||||
YDirection::North => ResizeDirection::NorthEast,
|
||||
YDirection::South => ResizeDirection::SouthEast,
|
||||
YDirection::Default => ResizeDirection::East,
|
||||
},
|
||||
|
||||
XDirection::Default => match ydir {
|
||||
YDirection::North => ResizeDirection::North,
|
||||
YDirection::South => ResizeDirection::South,
|
||||
YDirection::Default => return None,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -1,13 +1,19 @@
|
||||
extern crate image;
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::Event,
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
event_loop::EventLoop,
|
||||
window::{Icon, WindowBuilder},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() {
|
||||
simple_logger::init().unwrap();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
|
||||
// You'll have to choose an icon size at your own discretion. On X11, the desired size varies
|
||||
// by WM, and on Windows, you still have to account for screen scaling. Here we use 32px,
|
||||
@@ -28,17 +34,19 @@ fn main() {
|
||||
.unwrap();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
control_flow.set_wait();
|
||||
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
use winit::event::WindowEvent::*;
|
||||
match event {
|
||||
CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
CloseRequested => control_flow.set_exit(),
|
||||
DroppedFile(path) => {
|
||||
window.set_window_icon(Some(load_icon(&path)));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
} else if let Event::RedrawRequested(_) = event {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -47,7 +55,7 @@ fn load_icon(path: &Path) -> Icon {
|
||||
let (icon_rgba, icon_width, icon_height) = {
|
||||
let image = image::open(path)
|
||||
.expect("Failed to open icon path")
|
||||
.into_rgba();
|
||||
.into_rgba8();
|
||||
let (width, height) = image.dimensions();
|
||||
let rgba = image.into_raw();
|
||||
(rgba, width, height)
|
||||
|
||||
75
examples/window_option_as_alt.rs
Normal file
75
examples/window_option_as_alt.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use winit::platform::macos::{OptionAsAlt, WindowExtMacOS};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use winit::{
|
||||
event::ElementState,
|
||||
event::{Event, MouseButton, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
/// Prints the keyboard events characters received when option_is_alt is true versus false.
|
||||
/// A left mouse click will toggle option_is_alt.
|
||||
#[cfg(target_os = "macos")]
|
||||
fn main() {
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
window.set_ime_allowed(true);
|
||||
|
||||
let mut option_as_alt = window.option_as_alt();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => control_flow.set_exit(),
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::MouseInput {
|
||||
state: ElementState::Pressed,
|
||||
button: MouseButton::Left,
|
||||
..
|
||||
} => {
|
||||
option_as_alt = match option_as_alt {
|
||||
OptionAsAlt::None => OptionAsAlt::OnlyLeft,
|
||||
OptionAsAlt::OnlyLeft => OptionAsAlt::OnlyRight,
|
||||
OptionAsAlt::OnlyRight => OptionAsAlt::Both,
|
||||
OptionAsAlt::Both => OptionAsAlt::None,
|
||||
};
|
||||
|
||||
println!("Received Mouse click, toggling option_as_alt to: {option_as_alt:?}");
|
||||
window.set_option_as_alt(option_as_alt);
|
||||
}
|
||||
WindowEvent::KeyboardInput { .. } => println!("KeyboardInput: {event:?}"),
|
||||
_ => (),
|
||||
},
|
||||
Event::MainEventsCleared => {
|
||||
window.request_redraw();
|
||||
}
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
fn main() {
|
||||
println!("This example is only supported on MacOS");
|
||||
}
|
||||
64
examples/window_resize_increments.rs
Normal file
64
examples/window_resize_increments.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
use log::debug;
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
dpi::LogicalSize,
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::Key,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.with_inner_size(LogicalSize::new(128.0, 128.0))
|
||||
.with_resize_increments(LogicalSize::new(25.0, 25.0))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut has_increments = true;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
control_flow.set_wait();
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => control_flow.set_exit(),
|
||||
Event::WindowEvent {
|
||||
event:
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: Key::Space,
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
window_id,
|
||||
} if window_id == window.id() => {
|
||||
has_increments = !has_increments;
|
||||
|
||||
let new_increments = match window.resize_increments() {
|
||||
Some(_) => None,
|
||||
None => Some(LogicalSize::new(25.0, 25.0)),
|
||||
};
|
||||
debug!("Had increments: {}", new_increments.is_none());
|
||||
window.set_resize_increments(new_increments);
|
||||
}
|
||||
Event::MainEventsCleared => window.request_redraw(),
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,25 +1,32 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
// Limit this example to only compatible platforms.
|
||||
#[cfg(any(
|
||||
target_os = "windows",
|
||||
target_os = "macos",
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
windows_platform,
|
||||
macos_platform,
|
||||
x11_platform,
|
||||
wayland_platform,
|
||||
android_platform,
|
||||
orbital_platform,
|
||||
))]
|
||||
fn main() {
|
||||
use std::{thread::sleep, time::Duration};
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
platform::desktop::EventLoopExtDesktop,
|
||||
event_loop::EventLoop,
|
||||
platform::run_return::EventLoopExtRunReturn,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
let mut event_loop = EventLoop::new();
|
||||
|
||||
simple_logger::init().unwrap();
|
||||
let _window = WindowBuilder::new()
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
@@ -28,11 +35,11 @@ fn main() {
|
||||
|
||||
while !quit {
|
||||
event_loop.run_return(|event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
control_flow.set_wait();
|
||||
|
||||
if let Event::WindowEvent { event, .. } = &event {
|
||||
// Print only Window events to reduce noise
|
||||
println!("{:?}", event);
|
||||
println!("{event:?}");
|
||||
}
|
||||
|
||||
match event {
|
||||
@@ -43,7 +50,10 @@ fn main() {
|
||||
quit = true;
|
||||
}
|
||||
Event::MainEventsCleared => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
control_flow.set_exit();
|
||||
}
|
||||
Event::RedrawRequested(_) => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
@@ -55,7 +65,7 @@ fn main() {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))]
|
||||
#[cfg(any(ios_platform, wasm_platform))]
|
||||
fn main() {
|
||||
println!("This platform doesn't support run_return.");
|
||||
}
|
||||
|
||||
9
run-wasm/Cargo.toml
Normal file
9
run-wasm/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "run-wasm"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cargo-run-wasm = "0.2.0"
|
||||
3
run-wasm/src/main.rs
Normal file
3
run-wasm/src/main.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
cargo_run_wasm::run_wasm_with_css("body { margin: 0px; }");
|
||||
}
|
||||
166
src/dpi.rs
166
src/dpi.rs
@@ -35,8 +35,9 @@
|
||||
//!
|
||||
//! ### Position and Size types
|
||||
//!
|
||||
//! Winit's `Physical(Position|Size)` types correspond with the actual pixels on the device, and the
|
||||
//! `Logical(Position|Size)` types correspond to the physical pixels divided by the scale factor.
|
||||
//! Winit's [`PhysicalPosition`] / [`PhysicalSize`] types correspond with the actual pixels on the
|
||||
//! device, and the [`LogicalPosition`] / [`LogicalSize`] types correspond to the physical pixels
|
||||
//! divided by the scale factor.
|
||||
//! All of Winit's functions return physical types, but can take either logical or physical
|
||||
//! coordinates as input, allowing you to use the most convenient coordinate system for your
|
||||
//! particular application.
|
||||
@@ -46,19 +47,18 @@
|
||||
//! floating precision when necessary (e.g. logical sizes for fractional scale factors and touch
|
||||
//! input). If `P` is a floating-point type, please do not cast the values with `as {int}`. Doing so
|
||||
//! will truncate the fractional part of the float, rather than properly round to the nearest
|
||||
//! integer. Use the provided `cast` function or `From`/`Into` conversions, which handle the
|
||||
//! integer. Use the provided `cast` function or [`From`]/[`Into`] conversions, which handle the
|
||||
//! rounding properly. Note that precision loss will still occur when rounding from a float to an
|
||||
//! int, although rounding lessens the problem.
|
||||
//!
|
||||
//! ### Events
|
||||
//!
|
||||
//! Winit will dispatch a [`ScaleFactorChanged`](crate::event::WindowEvent::ScaleFactorChanged)
|
||||
//! event whenever a window's scale factor has changed. This can happen if the user drags their
|
||||
//! window from a standard-resolution monitor to a high-DPI monitor, or if the user changes their
|
||||
//! DPI settings. This gives you a chance to rescale your application's UI elements and adjust how
|
||||
//! the platform changes the window's size to reflect the new scale factor. If a window hasn't
|
||||
//! received a [`ScaleFactorChanged`](crate::event::WindowEvent::ScaleFactorChanged) event,
|
||||
//! then its scale factor is `1.0`.
|
||||
//! Winit will dispatch a [`ScaleFactorChanged`] event whenever a window's scale factor has changed.
|
||||
//! This can happen if the user drags their window from a standard-resolution monitor to a high-DPI
|
||||
//! monitor, or if the user changes their DPI settings. This gives you a chance to rescale your
|
||||
//! application's UI elements and adjust how the platform changes the window's size to reflect the new
|
||||
//! scale factor. If a window hasn't received a [`ScaleFactorChanged`] event, then its scale factor
|
||||
//! can be found by calling [`window.scale_factor()`].
|
||||
//!
|
||||
//! ## How is the scale factor calculated?
|
||||
//!
|
||||
@@ -69,14 +69,15 @@
|
||||
//! selection of "nice" scale factors, i.e. 1.0, 1.25, 1.5... on Windows 7, the scale factor is
|
||||
//! global and changing it requires logging out. See [this article][windows_1] for technical
|
||||
//! details.
|
||||
//! - **macOS:** "retina displays" have a scale factor of 2.0. Otherwise, the scale factor is 1.0.
|
||||
//! Intermediate scale factors are never used. It's possible for any display to use that 2.0 scale
|
||||
//! factor, given the use of the command line.
|
||||
//! - **macOS:** Recent versions of macOS allow the user to change the scaling factor for certain
|
||||
//! displays. When this is available, the user may pick a per-monitor scaling factor from a set
|
||||
//! of pre-defined settings. All "retina displays" have a scaling factor above 1.0 by default but
|
||||
//! the specific value varies across devices.
|
||||
//! - **X11:** Many man-hours have been spent trying to figure out how to handle DPI in X11. Winit
|
||||
//! currently uses a three-pronged approach:
|
||||
//! + Use the value in the `WINIT_X11_SCALE_FACTOR` environment variable, if present.
|
||||
//! + If not present, use the value set in `Xft.dpi` in Xresources.
|
||||
//! + Otherwise, calcuate the scale factor based on the millimeter monitor dimensions provided by XRandR.
|
||||
//! + Otherwise, calculate the scale factor based on the millimeter monitor dimensions provided by XRandR.
|
||||
//!
|
||||
//! If `WINIT_X11_SCALE_FACTOR` is set to `randr`, it'll ignore the `Xft.dpi` field and use the
|
||||
//! XRandR scaling method. Generally speaking, you should try to configure the standard system
|
||||
@@ -89,13 +90,19 @@
|
||||
//! - **Android:** Scale factors are set by the manufacturer to the value that best suits the
|
||||
//! device, and range from `1.0` to `4.0`. See [this article][android_1] for more information.
|
||||
//! - **Web:** The scale factor is the ratio between CSS pixels and the physical device pixels.
|
||||
//! In other words, it is the value of [`window.devicePixelRatio`][web_1]. It is affected by
|
||||
//! both the screen scaling and the browser zoom level and can go below `1.0`.
|
||||
//!
|
||||
//!
|
||||
//! [points]: https://en.wikipedia.org/wiki/Point_(typography)
|
||||
//! [picas]: https://en.wikipedia.org/wiki/Pica_(typography)
|
||||
//! [`ScaleFactorChanged`]: crate::event::WindowEvent::ScaleFactorChanged
|
||||
//! [`window.scale_factor()`]: crate::window::Window::scale_factor
|
||||
//! [windows_1]: https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows
|
||||
//! [apple_1]: https://developer.apple.com/library/archive/documentation/DeviceInformation/Reference/iOSDeviceCompatibility/Displays/Displays.html
|
||||
//! [apple_2]: https://developer.apple.com/design/human-interface-guidelines/macos/icons-and-images/image-size-and-resolution/
|
||||
//! [android_1]: https://developer.android.com/training/multiscreen/screendensities
|
||||
//! [web_1]: https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio
|
||||
|
||||
pub trait Pixel: Copy + Into<f64> {
|
||||
fn from_f64(f: f64) -> Self;
|
||||
@@ -160,7 +167,7 @@ pub fn validate_scale_factor(scale_factor: f64) -> bool {
|
||||
/// The position is stored as floats, so please be careful. Casting floats to integers truncates the
|
||||
/// fractional part, which can cause noticable issues. To help with that, an `Into<(i32, i32)>`
|
||||
/// implementation is provided which does the rounding for you.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct LogicalPosition<P> {
|
||||
pub x: P,
|
||||
@@ -206,9 +213,9 @@ impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalPosition<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> Into<(X, X)> for LogicalPosition<P> {
|
||||
fn into(self: Self) -> (X, X) {
|
||||
(self.x.cast(), self.y.cast())
|
||||
impl<P: Pixel, X: Pixel> From<LogicalPosition<P>> for (X, X) {
|
||||
fn from(p: LogicalPosition<P>) -> (X, X) {
|
||||
(p.x.cast(), p.y.cast())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,14 +225,28 @@ impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalPosition<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> Into<[X; 2]> for LogicalPosition<P> {
|
||||
fn into(self: Self) -> [X; 2] {
|
||||
[self.x.cast(), self.y.cast()]
|
||||
impl<P: Pixel, X: Pixel> From<LogicalPosition<P>> for [X; 2] {
|
||||
fn from(p: LogicalPosition<P>) -> [X; 2] {
|
||||
[p.x.cast(), p.y.cast()]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<P: Pixel> From<mint::Point2<P>> for LogicalPosition<P> {
|
||||
fn from(p: mint::Point2<P>) -> Self {
|
||||
Self::new(p.x, p.y)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<P: Pixel> From<LogicalPosition<P>> for mint::Point2<P> {
|
||||
fn from(p: LogicalPosition<P>) -> Self {
|
||||
mint::Point2 { x: p.x, y: p.y }
|
||||
}
|
||||
}
|
||||
|
||||
/// A position represented in physical pixels.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct PhysicalPosition<P> {
|
||||
pub x: P,
|
||||
@@ -271,9 +292,9 @@ impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalPosition<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> Into<(X, X)> for PhysicalPosition<P> {
|
||||
fn into(self: Self) -> (X, X) {
|
||||
(self.x.cast(), self.y.cast())
|
||||
impl<P: Pixel, X: Pixel> From<PhysicalPosition<P>> for (X, X) {
|
||||
fn from(p: PhysicalPosition<P>) -> (X, X) {
|
||||
(p.x.cast(), p.y.cast())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,14 +304,28 @@ impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalPosition<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> Into<[X; 2]> for PhysicalPosition<P> {
|
||||
fn into(self: Self) -> [X; 2] {
|
||||
[self.x.cast(), self.y.cast()]
|
||||
impl<P: Pixel, X: Pixel> From<PhysicalPosition<P>> for [X; 2] {
|
||||
fn from(p: PhysicalPosition<P>) -> [X; 2] {
|
||||
[p.x.cast(), p.y.cast()]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<P: Pixel> From<mint::Point2<P>> for PhysicalPosition<P> {
|
||||
fn from(p: mint::Point2<P>) -> Self {
|
||||
Self::new(p.x, p.y)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<P: Pixel> From<PhysicalPosition<P>> for mint::Point2<P> {
|
||||
fn from(p: PhysicalPosition<P>) -> Self {
|
||||
mint::Point2 { x: p.x, y: p.y }
|
||||
}
|
||||
}
|
||||
|
||||
/// A size represented in logical pixels.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct LogicalSize<P> {
|
||||
pub width: P,
|
||||
@@ -336,9 +371,9 @@ impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalSize<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> Into<(X, X)> for LogicalSize<P> {
|
||||
fn into(self: LogicalSize<P>) -> (X, X) {
|
||||
(self.width.cast(), self.height.cast())
|
||||
impl<P: Pixel, X: Pixel> From<LogicalSize<P>> for (X, X) {
|
||||
fn from(s: LogicalSize<P>) -> (X, X) {
|
||||
(s.width.cast(), s.height.cast())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,14 +383,31 @@ impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalSize<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> Into<[X; 2]> for LogicalSize<P> {
|
||||
fn into(self: Self) -> [X; 2] {
|
||||
[self.width.cast(), self.height.cast()]
|
||||
impl<P: Pixel, X: Pixel> From<LogicalSize<P>> for [X; 2] {
|
||||
fn from(s: LogicalSize<P>) -> [X; 2] {
|
||||
[s.width.cast(), s.height.cast()]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<P: Pixel> From<mint::Vector2<P>> for LogicalSize<P> {
|
||||
fn from(v: mint::Vector2<P>) -> Self {
|
||||
Self::new(v.x, v.y)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<P: Pixel> From<LogicalSize<P>> for mint::Vector2<P> {
|
||||
fn from(s: LogicalSize<P>) -> Self {
|
||||
mint::Vector2 {
|
||||
x: s.width,
|
||||
y: s.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A size represented in physical pixels.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Default, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct PhysicalSize<P> {
|
||||
pub width: P,
|
||||
@@ -398,9 +450,9 @@ impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalSize<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> Into<(X, X)> for PhysicalSize<P> {
|
||||
fn into(self: Self) -> (X, X) {
|
||||
(self.width.cast(), self.height.cast())
|
||||
impl<P: Pixel, X: Pixel> From<PhysicalSize<P>> for (X, X) {
|
||||
fn from(s: PhysicalSize<P>) -> (X, X) {
|
||||
(s.width.cast(), s.height.cast())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,9 +462,26 @@ impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalSize<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> Into<[X; 2]> for PhysicalSize<P> {
|
||||
fn into(self: Self) -> [X; 2] {
|
||||
[self.width.cast(), self.height.cast()]
|
||||
impl<P: Pixel, X: Pixel> From<PhysicalSize<P>> for [X; 2] {
|
||||
fn from(s: PhysicalSize<P>) -> [X; 2] {
|
||||
[s.width.cast(), s.height.cast()]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<P: Pixel> From<mint::Vector2<P>> for PhysicalSize<P> {
|
||||
fn from(v: mint::Vector2<P>) -> Self {
|
||||
Self::new(v.x, v.y)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "mint")]
|
||||
impl<P: Pixel> From<PhysicalSize<P>> for mint::Vector2<P> {
|
||||
fn from(s: PhysicalSize<P>) -> Self {
|
||||
mint::Vector2 {
|
||||
x: s.width,
|
||||
y: s.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,6 +511,19 @@ impl Size {
|
||||
Size::Logical(size) => size.to_physical(scale_factor),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clamp<S: Into<Size>>(input: S, min: S, max: S, scale_factor: f64) -> Size {
|
||||
let (input, min, max) = (
|
||||
input.into().to_physical::<f64>(scale_factor),
|
||||
min.into().to_physical::<f64>(scale_factor),
|
||||
max.into().to_physical::<f64>(scale_factor),
|
||||
);
|
||||
|
||||
let width = input.width.clamp(min.width, max.width);
|
||||
let height = input.height.clamp(min.height, max.height);
|
||||
|
||||
PhysicalSize::new(width, height).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<PhysicalSize<P>> for Size {
|
||||
|
||||
1089
src/event.rs
1089
src/event.rs
File diff suppressed because it is too large
Load Diff
@@ -1,48 +1,137 @@
|
||||
//! The `EventLoop` struct and assorted supporting types, including `ControlFlow`.
|
||||
//! The [`EventLoop`] struct and assorted supporting types, including
|
||||
//! [`ControlFlow`].
|
||||
//!
|
||||
//! If you want to send custom events to the event loop, use [`EventLoop::create_proxy()`][create_proxy]
|
||||
//! to acquire an [`EventLoopProxy`][event_loop_proxy] and call its [`send_event`][send_event] method.
|
||||
//! If you want to send custom events to the event loop, use
|
||||
//! [`EventLoop::create_proxy`] to acquire an [`EventLoopProxy`] and call its
|
||||
//! [`send_event`](`EventLoopProxy::send_event`) method.
|
||||
//!
|
||||
//! See the root-level documentation for information on how to create and use an event loop to
|
||||
//! handle events.
|
||||
//!
|
||||
//! [create_proxy]: crate::event_loop::EventLoop::create_proxy
|
||||
//! [event_loop_proxy]: crate::event_loop::EventLoopProxy
|
||||
//! [send_event]: crate::event_loop::EventLoopProxy::send_event
|
||||
use instant::Instant;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::{error, fmt};
|
||||
|
||||
use raw_window_handle::{HasRawDisplayHandle, RawDisplayHandle};
|
||||
#[cfg(not(wasm_platform))]
|
||||
use std::time::{Duration, Instant};
|
||||
#[cfg(wasm_platform)]
|
||||
use web_time::{Duration, Instant};
|
||||
|
||||
use crate::{event::Event, monitor::MonitorHandle, platform_impl};
|
||||
|
||||
/// Provides a way to retrieve events from the system and from the windows that were registered to
|
||||
/// the events loop.
|
||||
///
|
||||
/// An `EventLoop` can be seen more or less as a "context". Calling `EventLoop::new()`
|
||||
/// An `EventLoop` can be seen more or less as a "context". Calling [`EventLoop::new`]
|
||||
/// initializes everything that will be required to create windows. For example on Linux creating
|
||||
/// an event loop opens a connection to the X or Wayland server.
|
||||
///
|
||||
/// To wake up an `EventLoop` from a another thread, see the `EventLoopProxy` docs.
|
||||
/// To wake up an `EventLoop` from a another thread, see the [`EventLoopProxy`] docs.
|
||||
///
|
||||
/// Note that the `EventLoop` cannot be shared across threads (due to platform-dependant logic
|
||||
/// forbidding it), as such it is neither `Send` nor `Sync`. If you need cross-thread access, the
|
||||
/// `Window` created from this `EventLoop` _can_ be sent to an other thread, and the
|
||||
/// `EventLoopProxy` allows you to wake up an `EventLoop` from another thread.
|
||||
/// Note that this cannot be shared across threads (due to platform-dependant logic
|
||||
/// forbidding it), as such it is neither [`Send`] nor [`Sync`]. If you need cross-thread access, the
|
||||
/// [`Window`] created from this _can_ be sent to an other thread, and the
|
||||
/// [`EventLoopProxy`] allows you to wake up an `EventLoop` from another thread.
|
||||
///
|
||||
/// [`Window`]: crate::window::Window
|
||||
pub struct EventLoop<T: 'static> {
|
||||
pub(crate) event_loop: platform_impl::EventLoop<T>,
|
||||
pub(crate) _marker: ::std::marker::PhantomData<*mut ()>, // Not Send nor Sync
|
||||
pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync
|
||||
}
|
||||
|
||||
/// Target that associates windows with an `EventLoop`.
|
||||
/// Target that associates windows with an [`EventLoop`].
|
||||
///
|
||||
/// This type exists to allow you to create new windows while Winit executes
|
||||
/// your callback. `EventLoop` will coerce into this type (`impl<T> Deref for
|
||||
/// your callback. [`EventLoop`] will coerce into this type (`impl<T> Deref for
|
||||
/// EventLoop<T>`), so functions that take this as a parameter can also take
|
||||
/// `&EventLoop`.
|
||||
pub struct EventLoopWindowTarget<T: 'static> {
|
||||
pub(crate) p: platform_impl::EventLoopWindowTarget<T>,
|
||||
pub(crate) _marker: ::std::marker::PhantomData<*mut ()>, // Not Send nor Sync
|
||||
pub(crate) _marker: PhantomData<*mut ()>, // Not Send nor Sync
|
||||
}
|
||||
|
||||
/// Object that allows building the event loop.
|
||||
///
|
||||
/// This is used to make specifying options that affect the whole application
|
||||
/// easier. But note that constructing multiple event loops is not supported.
|
||||
#[derive(Default)]
|
||||
pub struct EventLoopBuilder<T: 'static> {
|
||||
pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes,
|
||||
_p: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl EventLoopBuilder<()> {
|
||||
/// Start building a new event loop.
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
Self::with_user_event()
|
||||
}
|
||||
}
|
||||
|
||||
static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
impl<T> EventLoopBuilder<T> {
|
||||
/// Start building a new event loop, with the given type as the user event
|
||||
/// type.
|
||||
#[inline]
|
||||
pub fn with_user_event() -> Self {
|
||||
Self {
|
||||
platform_specific: Default::default(),
|
||||
_p: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a new event loop.
|
||||
///
|
||||
/// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread,
|
||||
/// and only once per application.***
|
||||
///
|
||||
/// Attempting to create the event loop on a different thread, or multiple event loops in
|
||||
/// the same application, will panic. This restriction isn't
|
||||
/// strictly necessary on all platforms, but is imposed to eliminate any nasty surprises when
|
||||
/// porting to platforms that require it. `EventLoopBuilderExt::any_thread` functions are exposed
|
||||
/// in the relevant [`platform`] module if the target platform supports creating an event loop on
|
||||
/// any thread.
|
||||
///
|
||||
/// Calling this function will result in display backend initialisation.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Backend type can be controlled using an environment variable
|
||||
/// `WINIT_UNIX_BACKEND`. Legal values are `x11` and `wayland`.
|
||||
/// If it is not set, winit will try to connect to a Wayland connection, and if that fails,
|
||||
/// will fall back on X11. If this variable is set with any other value, winit will panic.
|
||||
/// - **Android:** Must be configured with an `AndroidApp` from `android_main()` by calling
|
||||
/// [`.with_android_app(app)`] before calling `.build()`.
|
||||
///
|
||||
/// [`platform`]: crate::platform
|
||||
#[cfg_attr(
|
||||
android,
|
||||
doc = "[`.with_android_app(app)`]: crate::platform::android::EventLoopBuilderExtAndroid::with_android_app"
|
||||
)]
|
||||
#[cfg_attr(
|
||||
not(android),
|
||||
doc = "[`.with_android_app(app)`]: #only-available-on-android"
|
||||
)]
|
||||
#[inline]
|
||||
pub fn build(&mut self) -> EventLoop<T> {
|
||||
if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) {
|
||||
panic!("Creating EventLoop multiple times is not supported.");
|
||||
}
|
||||
|
||||
// Certain platforms accept a mutable reference in their API.
|
||||
#[allow(clippy::unnecessary_mut_passed)]
|
||||
EventLoop {
|
||||
event_loop: platform_impl::EventLoop::new(&mut self.platform_specific),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(wasm_platform)]
|
||||
pub(crate) fn allow_event_loop_recreation() {
|
||||
EVENT_LOOP_CREATED.store(false, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for EventLoop<T> {
|
||||
@@ -57,77 +146,143 @@ impl<T> fmt::Debug for EventLoopWindowTarget<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Set by the user callback given to the `EventLoop::run` method.
|
||||
/// Set by the user callback given to the [`EventLoop::run`] method.
|
||||
///
|
||||
/// Indicates the desired behavior of the event loop after [`Event::RedrawEventsCleared`][events_cleared]
|
||||
/// is emitted. Defaults to `Poll`.
|
||||
/// Indicates the desired behavior of the event loop after [`Event::RedrawEventsCleared`] is emitted.
|
||||
///
|
||||
/// Defaults to [`Poll`].
|
||||
///
|
||||
/// ## Persistency
|
||||
/// Almost every change is persistent between multiple calls to the event loop closure within a
|
||||
/// given run loop. The only exception to this is `Exit` which, once set, cannot be unset. Changes
|
||||
/// are **not** persistent between multiple calls to `run_return` - issuing a new call will reset
|
||||
/// the control flow to `Poll`.
|
||||
///
|
||||
/// [events_cleared]: crate::event::Event::RedrawEventsCleared
|
||||
/// Almost every change is persistent between multiple calls to the event loop closure within a
|
||||
/// given run loop. The only exception to this is [`ExitWithCode`] which, once set, cannot be unset.
|
||||
/// Changes are **not** persistent between multiple calls to `run_return` - issuing a new call will
|
||||
/// reset the control flow to [`Poll`].
|
||||
///
|
||||
/// [`ExitWithCode`]: Self::ExitWithCode
|
||||
/// [`Poll`]: Self::Poll
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ControlFlow {
|
||||
/// When the current loop iteration finishes, immediately begin a new iteration regardless of
|
||||
/// whether or not new events are available to process.
|
||||
Poll,
|
||||
|
||||
/// When the current loop iteration finishes, suspend the thread until another event arrives.
|
||||
Wait,
|
||||
|
||||
/// When the current loop iteration finishes, suspend the thread until either another event
|
||||
/// arrives or the given time is reached.
|
||||
///
|
||||
/// Useful for implementing efficient timers. Applications which want to render at the display's
|
||||
/// native refresh rate should instead use [`Poll`] and the VSync functionality of a graphics API
|
||||
/// to reduce odds of missed frames.
|
||||
///
|
||||
/// [`Poll`]: Self::Poll
|
||||
WaitUntil(Instant),
|
||||
/// Send a `LoopDestroyed` event and stop the event loop. This variant is *sticky* - once set,
|
||||
/// `control_flow` cannot be changed from `Exit`, and any future attempts to do so will result
|
||||
/// in the `control_flow` parameter being reset to `Exit`.
|
||||
Exit,
|
||||
|
||||
/// Send a [`LoopDestroyed`] event and stop the event loop. This variant is *sticky* - once set,
|
||||
/// `control_flow` cannot be changed from `ExitWithCode`, and any future attempts to do so will
|
||||
/// result in the `control_flow` parameter being reset to `ExitWithCode`.
|
||||
///
|
||||
/// The contained number will be used as exit code. The [`Exit`] constant is a shortcut for this
|
||||
/// with exit code 0.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Android / iOS / WASM:** The supplied exit code is unused.
|
||||
/// - **Unix:** On most Unix-like platforms, only the 8 least significant bits will be used,
|
||||
/// which can cause surprises with negative exit values (`-42` would end up as `214`). See
|
||||
/// [`std::process::exit`].
|
||||
///
|
||||
/// [`LoopDestroyed`]: Event::LoopDestroyed
|
||||
/// [`Exit`]: ControlFlow::Exit
|
||||
ExitWithCode(i32),
|
||||
}
|
||||
|
||||
impl ControlFlow {
|
||||
/// Alias for [`ExitWithCode`]`(0)`.
|
||||
///
|
||||
/// [`ExitWithCode`]: Self::ExitWithCode
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const Exit: Self = Self::ExitWithCode(0);
|
||||
|
||||
/// Sets this to [`Poll`].
|
||||
///
|
||||
/// [`Poll`]: Self::Poll
|
||||
pub fn set_poll(&mut self) {
|
||||
*self = Self::Poll;
|
||||
}
|
||||
|
||||
/// Sets this to [`Wait`].
|
||||
///
|
||||
/// [`Wait`]: Self::Wait
|
||||
pub fn set_wait(&mut self) {
|
||||
*self = Self::Wait;
|
||||
}
|
||||
|
||||
/// Sets this to [`WaitUntil`]`(instant)`.
|
||||
///
|
||||
/// [`WaitUntil`]: Self::WaitUntil
|
||||
pub fn set_wait_until(&mut self, instant: Instant) {
|
||||
*self = Self::WaitUntil(instant);
|
||||
}
|
||||
|
||||
/// Sets this to wait until a timeout has expired.
|
||||
///
|
||||
/// In most cases, this is set to [`WaitUntil`]. However, if the timeout overflows, it is
|
||||
/// instead set to [`Wait`].
|
||||
///
|
||||
/// [`WaitUntil`]: Self::WaitUntil
|
||||
/// [`Wait`]: Self::Wait
|
||||
pub fn set_wait_timeout(&mut self, timeout: Duration) {
|
||||
match Instant::now().checked_add(timeout) {
|
||||
Some(instant) => self.set_wait_until(instant),
|
||||
None => self.set_wait(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets this to [`ExitWithCode`]`(code)`.
|
||||
///
|
||||
/// [`ExitWithCode`]: Self::ExitWithCode
|
||||
pub fn set_exit_with_code(&mut self, code: i32) {
|
||||
*self = Self::ExitWithCode(code);
|
||||
}
|
||||
|
||||
/// Sets this to [`Exit`].
|
||||
///
|
||||
/// [`Exit`]: Self::Exit
|
||||
pub fn set_exit(&mut self) {
|
||||
*self = Self::Exit;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ControlFlow {
|
||||
#[inline(always)]
|
||||
fn default() -> ControlFlow {
|
||||
ControlFlow::Poll
|
||||
fn default() -> Self {
|
||||
Self::Poll
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoop<()> {
|
||||
/// Builds a new event loop with a `()` as the user event type.
|
||||
/// Alias for [`EventLoopBuilder::new().build()`].
|
||||
///
|
||||
/// ***For cross-platform compatibility, the `EventLoop` must be created on the main thread.***
|
||||
/// Attempting to create the event loop on a different thread will panic. This restriction isn't
|
||||
/// strictly necessary on all platforms, but is imposed to eliminate any nasty surprises when
|
||||
/// porting to platforms that require it. `EventLoopExt::new_any_thread` functions are exposed
|
||||
/// in the relevant `platform` module if the target platform supports creating an event loop on
|
||||
/// any thread.
|
||||
///
|
||||
/// Usage will result in display backend initialisation, this can be controlled on linux
|
||||
/// using an environment variable `WINIT_UNIX_BACKEND`. Legal values are `x11` and `wayland`.
|
||||
/// If it is not set, winit will try to connect to a wayland connection, and if it fails will
|
||||
/// fallback on x11. If this variable is set with any other value, winit will panic.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS:** Can only be called on the main thread.
|
||||
/// [`EventLoopBuilder::new().build()`]: EventLoopBuilder::build
|
||||
#[inline]
|
||||
pub fn new() -> EventLoop<()> {
|
||||
EventLoop::<()>::with_user_event()
|
||||
EventLoopBuilder::new().build()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EventLoop<()> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EventLoop<T> {
|
||||
/// Builds a new event loop.
|
||||
///
|
||||
/// All caveats documented in [`EventLoop::new`] apply to this function.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS:** Can only be called on the main thread.
|
||||
#[deprecated = "Use `EventLoopBuilder::<T>::with_user_event().build()` instead."]
|
||||
pub fn with_user_event() -> EventLoop<T> {
|
||||
EventLoop {
|
||||
event_loop: platform_impl::EventLoop::new(),
|
||||
_marker: ::std::marker::PhantomData,
|
||||
}
|
||||
EventLoopBuilder::<T>::with_user_event().build()
|
||||
}
|
||||
|
||||
/// Hijacks the calling thread and initializes the winit event loop with the provided
|
||||
@@ -139,6 +294,11 @@ impl<T> EventLoop<T> {
|
||||
///
|
||||
/// Any values not passed to this function will *not* be dropped.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **X11 / Wayland:** The program terminates with exit code 1 if the display server
|
||||
/// disconnects.
|
||||
///
|
||||
/// [`ControlFlow`]: crate::event_loop::ControlFlow
|
||||
#[inline]
|
||||
pub fn run<F>(self, event_handler: F) -> !
|
||||
@@ -148,28 +308,18 @@ impl<T> EventLoop<T> {
|
||||
self.event_loop.run(event_handler)
|
||||
}
|
||||
|
||||
/// Creates an `EventLoopProxy` that can be used to dispatch user events to the main event loop.
|
||||
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events to the main event loop.
|
||||
pub fn create_proxy(&self) -> EventLoopProxy<T> {
|
||||
EventLoopProxy {
|
||||
event_loop_proxy: self.event_loop.create_proxy(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the list of all the monitors available on the system.
|
||||
#[inline]
|
||||
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
|
||||
self.event_loop
|
||||
.available_monitors()
|
||||
.into_iter()
|
||||
.map(|inner| MonitorHandle { inner })
|
||||
}
|
||||
|
||||
/// Returns the primary monitor of the system.
|
||||
#[inline]
|
||||
pub fn primary_monitor(&self) -> MonitorHandle {
|
||||
MonitorHandle {
|
||||
inner: self.event_loop.primary_monitor(),
|
||||
}
|
||||
unsafe impl<T> HasRawDisplayHandle for EventLoop<T> {
|
||||
/// Returns a [`raw_window_handle::RawDisplayHandle`] for the event loop.
|
||||
fn raw_display_handle(&self) -> RawDisplayHandle {
|
||||
self.event_loop.window_target().p.raw_display_handle()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +330,56 @@ impl<T> Deref for EventLoop<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to send custom events to `EventLoop`.
|
||||
impl<T> EventLoopWindowTarget<T> {
|
||||
/// Returns the list of all the monitors available on the system.
|
||||
#[inline]
|
||||
pub fn available_monitors(&self) -> impl Iterator<Item = MonitorHandle> {
|
||||
#[allow(clippy::useless_conversion)] // false positive on some platforms
|
||||
self.p
|
||||
.available_monitors()
|
||||
.into_iter()
|
||||
.map(|inner| MonitorHandle { inner })
|
||||
}
|
||||
|
||||
/// Returns the primary monitor of the system.
|
||||
///
|
||||
/// Returns `None` if it can't identify any monitor as a primary one.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// **Wayland:** Always returns `None`.
|
||||
#[inline]
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
self.p
|
||||
.primary_monitor()
|
||||
.map(|inner| MonitorHandle { inner })
|
||||
}
|
||||
|
||||
/// Change if or when [`DeviceEvent`]s are captured.
|
||||
///
|
||||
/// Since the [`DeviceEvent`] capture can lead to high CPU usage for unfocused windows, winit
|
||||
/// will ignore them by default for unfocused windows on Linux/BSD. This method allows changing
|
||||
/// this at runtime to explicitly capture them again.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Wayland / macOS / iOS / Android / Orbital:** Unsupported.
|
||||
///
|
||||
/// [`DeviceEvent`]: crate::event::DeviceEvent
|
||||
pub fn listen_device_events(&self, _allowed: DeviceEvents) {
|
||||
#[cfg(any(x11_platform, wasm_platform, wayland_platform, windows))]
|
||||
self.p.listen_device_events(_allowed);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T> HasRawDisplayHandle for EventLoopWindowTarget<T> {
|
||||
/// Returns a [`raw_window_handle::RawDisplayHandle`] for the event loop.
|
||||
fn raw_display_handle(&self) -> RawDisplayHandle {
|
||||
self.p.raw_display_handle()
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to send custom events to [`EventLoop`].
|
||||
pub struct EventLoopProxy<T: 'static> {
|
||||
event_loop_proxy: platform_impl::EventLoopProxy<T>,
|
||||
}
|
||||
@@ -194,11 +393,13 @@ impl<T: 'static> Clone for EventLoopProxy<T> {
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopProxy<T> {
|
||||
/// Send an event to the `EventLoop` from which this proxy was created. This emits a
|
||||
/// Send an event to the [`EventLoop`] from which this proxy was created. This emits a
|
||||
/// `UserEvent(event)` event in the event loop, where `event` is the value passed to this
|
||||
/// function.
|
||||
///
|
||||
/// Returns an `Err` if the associated `EventLoop` no longer exists.
|
||||
/// Returns an `Err` if the associated [`EventLoop`] no longer exists.
|
||||
///
|
||||
/// [`UserEvent(event)`]: Event::UserEvent
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
self.event_loop_proxy.send_event(event)
|
||||
}
|
||||
@@ -210,8 +411,10 @@ impl<T: 'static> fmt::Debug for EventLoopProxy<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The error that is returned when an `EventLoopProxy` attempts to wake up an `EventLoop` that
|
||||
/// no longer exists. Contains the original event given to `send_event`.
|
||||
/// The error that is returned when an [`EventLoopProxy`] attempts to wake up an [`EventLoop`] that
|
||||
/// no longer exists.
|
||||
///
|
||||
/// Contains the original event given to [`EventLoopProxy::send_event`].
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct EventLoopClosed<T>(pub T);
|
||||
|
||||
@@ -222,3 +425,15 @@ impl<T> fmt::Display for EventLoopClosed<T> {
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> error::Error for EventLoopClosed<T> {}
|
||||
|
||||
/// Control when device events are captured.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
|
||||
pub enum DeviceEvents {
|
||||
/// Report device events regardless of window focus.
|
||||
Always,
|
||||
/// Only capture device events while the window is focused.
|
||||
#[default]
|
||||
WhenFocused,
|
||||
/// Never capture device events.
|
||||
Never,
|
||||
}
|
||||
|
||||
16
src/icon.rs
16
src/icon.rs
@@ -13,7 +13,7 @@ pub(crate) struct Pixel {
|
||||
pub(crate) const PIXEL_SIZE: usize = mem::size_of::<Pixel>();
|
||||
|
||||
#[derive(Debug)]
|
||||
/// An error produced when using `Icon::from_rgba` with invalid arguments.
|
||||
/// An error produced when using [`Icon::from_rgba`] with invalid arguments.
|
||||
pub enum BadIcon {
|
||||
/// Produced when the length of the `rgba` argument isn't divisible by 4, thus `rgba` can't be
|
||||
/// safely interpreted as 32bpp RGBA pixels.
|
||||
@@ -34,8 +34,7 @@ impl fmt::Display for BadIcon {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
BadIcon::ByteCountNotDivisibleBy4 { byte_count } => write!(f,
|
||||
"The length of the `rgba` argument ({:?}) isn't divisible by 4, making it impossible to interpret as 32bpp RGBA pixels.",
|
||||
byte_count,
|
||||
"The length of the `rgba` argument ({byte_count:?}) isn't divisible by 4, making it impossible to interpret as 32bpp RGBA pixels.",
|
||||
),
|
||||
BadIcon::DimensionsVsPixelCount {
|
||||
width,
|
||||
@@ -43,10 +42,9 @@ impl fmt::Display for BadIcon {
|
||||
width_x_height,
|
||||
pixel_count,
|
||||
} => write!(f,
|
||||
"The specified dimensions ({:?}x{:?}) don't match the number of pixels supplied by the `rgba` argument ({:?}). For those dimensions, the expected pixel count is {:?}.",
|
||||
width, height, pixel_count, width_x_height,
|
||||
"The specified dimensions ({width:?}x{height:?}) don't match the number of pixels supplied by the `rgba` argument ({pixel_count:?}). For those dimensions, the expected pixel count is {width_x_height:?}.",
|
||||
),
|
||||
BadIcon::OsError(e) => write!(f, "OS error when instantiating the icon: {:?}", e),
|
||||
BadIcon::OsError(e) => write!(f, "OS error when instantiating the icon: {e:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,10 +71,6 @@ mod constructors {
|
||||
use super::*;
|
||||
|
||||
impl RgbaIcon {
|
||||
/// Creates an `Icon` from 32bpp RGBA data.
|
||||
///
|
||||
/// The length of `rgba` must be divisible by 4, and `width * height` must equal
|
||||
/// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error.
|
||||
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
|
||||
if rgba.len() % PIXEL_SIZE != 0 {
|
||||
return Err(BadIcon::ByteCountNotDivisibleBy4 {
|
||||
@@ -123,7 +117,7 @@ impl fmt::Debug for Icon {
|
||||
}
|
||||
|
||||
impl Icon {
|
||||
/// Creates an `Icon` from 32bpp RGBA data.
|
||||
/// Creates an icon from 32bpp RGBA data.
|
||||
///
|
||||
/// The length of `rgba` must be divisible by 4, and `width * height` must equal
|
||||
/// `rgba.len() / 4`. Otherwise, this will return a `BadIcon` error.
|
||||
|
||||
1688
src/keyboard.rs
Normal file
1688
src/keyboard.rs
Normal file
File diff suppressed because it is too large
Load Diff
64
src/lib.rs
64
src/lib.rs
@@ -28,23 +28,23 @@
|
||||
//! Some user activity, like mouse movement, can generate both a [`WindowEvent`] *and* a
|
||||
//! [`DeviceEvent`]. You can also create and handle your own custom [`UserEvent`]s, if desired.
|
||||
//!
|
||||
//! You can retreive events by calling [`EventLoop::run`][event_loop_run]. This function will
|
||||
//! You can retrieve events by calling [`EventLoop::run`][event_loop_run]. This function will
|
||||
//! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and
|
||||
//! will run until the `control_flow` argument given to the closure is set to
|
||||
//! [`ControlFlow`]`::`[`Exit`], at which point [`Event`]`::`[`LoopDestroyed`] is emitted and the
|
||||
//! entire program terminates.
|
||||
//! [`ControlFlow`]`::`[`ExitWithCode`] (which [`ControlFlow`]`::`[`Exit`] aliases to), at which
|
||||
//! point [`Event`]`::`[`LoopDestroyed`] is emitted and the entire program terminates.
|
||||
//!
|
||||
//! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop
|
||||
//! model, since that can't be implemented properly on web and mobile platforms and works poorly on
|
||||
//! most desktop platforms. However, this model can be re-implemented to an extent on desktops with
|
||||
//! [`EventLoopExtDesktop::run_return`]. See that method's documentation for more reasons about why
|
||||
//! it's discouraged, beyond mobile/web compatibility reasons.
|
||||
//! model, since that can't be implemented properly on some platforms (e.g web, iOS) and works poorly on
|
||||
//! most other platforms. However, this model can be re-implemented to an extent with
|
||||
//! [`EventLoopExtRunReturn::run_return`]. See that method's documentation for more reasons about why
|
||||
//! it's discouraged, beyond compatibility reasons.
|
||||
//!
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use winit::{
|
||||
//! event::{Event, WindowEvent},
|
||||
//! event_loop::{ControlFlow, EventLoop},
|
||||
//! event_loop::EventLoop,
|
||||
//! window::WindowBuilder,
|
||||
//! };
|
||||
//!
|
||||
@@ -54,12 +54,12 @@
|
||||
//! event_loop.run(move |event, _, control_flow| {
|
||||
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
|
||||
//! // dispatched any events. This is ideal for games and similar applications.
|
||||
//! *control_flow = ControlFlow::Poll;
|
||||
//! control_flow.set_poll();
|
||||
//!
|
||||
//! // ControlFlow::Wait pauses the event loop if no events are available to process.
|
||||
//! // This is ideal for non-game applications that only update in response to user
|
||||
//! // input, and uses significantly less power/CPU time than ControlFlow::Poll.
|
||||
//! *control_flow = ControlFlow::Wait;
|
||||
//! control_flow.set_wait();
|
||||
//!
|
||||
//! match event {
|
||||
//! Event::WindowEvent {
|
||||
@@ -67,20 +67,24 @@
|
||||
//! ..
|
||||
//! } => {
|
||||
//! println!("The close button was pressed; stopping");
|
||||
//! *control_flow = ControlFlow::Exit
|
||||
//! control_flow.set_exit();
|
||||
//! },
|
||||
//! Event::MainEventsCleared => {
|
||||
//! // Application update code.
|
||||
//!
|
||||
//! // Queue a RedrawRequested event.
|
||||
//! //
|
||||
//! // You only need to call this if you've determined that you need to redraw, in
|
||||
//! // applications which do not always need to. Applications that redraw continuously
|
||||
//! // can just render here instead.
|
||||
//! window.request_redraw();
|
||||
//! },
|
||||
//! Event::RedrawRequested(_) => {
|
||||
//! // Redraw the application.
|
||||
//! //
|
||||
//! // It's preferrable to render in this event rather than in MainEventsCleared, since
|
||||
//! // rendering in here allows the program to gracefully handle redraws requested
|
||||
//! // by the OS.
|
||||
//! // It's preferable for applications that do not render continuously to render in
|
||||
//! // this event rather than in MainEventsCleared, since rendering in here allows
|
||||
//! // the program to gracefully handle redraws requested by the OS.
|
||||
//! },
|
||||
//! _ => ()
|
||||
//! }
|
||||
@@ -94,15 +98,23 @@
|
||||
//! # Drawing on the window
|
||||
//!
|
||||
//! Winit doesn't directly provide any methods for drawing on a [`Window`]. However it allows you to
|
||||
//! retrieve the raw handle of the window (see the [`platform`] module), which in turn allows you
|
||||
//! to create an OpenGL/Vulkan/DirectX/Metal/etc. context that can be used to render graphics.
|
||||
//! retrieve the raw handle of the window and display (see the [`platform`] module and/or the
|
||||
//! [`raw_window_handle`] and [`raw_display_handle`] methods), which in turn allows
|
||||
//! you to create an OpenGL/Vulkan/DirectX/Metal/etc. context that can be used to render graphics.
|
||||
//!
|
||||
//! Note that many platforms will display garbage data in the window's client area if the
|
||||
//! application doesn't render anything to the window by the time the desktop compositor is ready to
|
||||
//! display the window to the user. If you notice this happening, you should create the window with
|
||||
//! [`visible` set to `false`](crate::window::WindowBuilder::with_visible) and explicitly make the
|
||||
//! window visible only once you're ready to render into it.
|
||||
//!
|
||||
//! [`EventLoop`]: event_loop::EventLoop
|
||||
//! [`EventLoopExtDesktop::run_return`]: ./platform/desktop/trait.EventLoopExtDesktop.html#tymethod.run_return
|
||||
//! [`EventLoopExtRunReturn::run_return`]: ./platform/run_return/trait.EventLoopExtRunReturn.html#tymethod.run_return
|
||||
//! [`EventLoop::new()`]: event_loop::EventLoop::new
|
||||
//! [event_loop_run]: event_loop::EventLoop::run
|
||||
//! [`ControlFlow`]: event_loop::ControlFlow
|
||||
//! [`Exit`]: event_loop::ControlFlow::Exit
|
||||
//! [`ExitWithCode`]: event_loop::ControlFlow::ExitWithCode
|
||||
//! [`Window`]: window::Window
|
||||
//! [`WindowId`]: window::WindowId
|
||||
//! [`WindowBuilder`]: window::WindowBuilder
|
||||
@@ -116,13 +128,17 @@
|
||||
//! [`UserEvent`]: event::Event::UserEvent
|
||||
//! [`LoopDestroyed`]: event::Event::LoopDestroyed
|
||||
//! [`platform`]: platform
|
||||
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
|
||||
//! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle
|
||||
|
||||
#![deny(rust_2018_idioms)]
|
||||
#![deny(intra_doc_link_resolution_failure)]
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
#![deny(clippy::all)]
|
||||
#![cfg_attr(feature = "cargo-clippy", deny(warnings))]
|
||||
// Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
@@ -131,11 +147,6 @@ extern crate log;
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
#[macro_use]
|
||||
extern crate objc;
|
||||
#[cfg(all(target_arch = "wasm32", feature = "std_web"))]
|
||||
extern crate std_web as stdweb;
|
||||
|
||||
pub mod dpi;
|
||||
#[macro_use]
|
||||
@@ -143,6 +154,7 @@ pub mod error;
|
||||
pub mod event;
|
||||
pub mod event_loop;
|
||||
mod icon;
|
||||
pub mod keyboard;
|
||||
pub mod monitor;
|
||||
mod platform_impl;
|
||||
pub mod window;
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
//! Types useful for interacting with a user's monitors.
|
||||
//!
|
||||
//! If you want to get basic information about a monitor, you can use the [`MonitorHandle`][monitor_handle]
|
||||
//! type. This is retreived from one of the following methods, which return an iterator of
|
||||
//! [`MonitorHandle`][monitor_handle]:
|
||||
//! - [`EventLoop::available_monitors`][loop_get]
|
||||
//! - [`Window::available_monitors`][window_get].
|
||||
//!
|
||||
//! [monitor_handle]: crate::monitor::MonitorHandle
|
||||
//! [loop_get]: crate::event_loop::EventLoop::available_monitors
|
||||
//! [window_get]: crate::window::Window::available_monitors
|
||||
//! If you want to get basic information about a monitor, you can use the
|
||||
//! [`MonitorHandle`] type. This is retrieved from one of the following
|
||||
//! methods, which return an iterator of [`MonitorHandle`]:
|
||||
//! - [`EventLoopWindowTarget::available_monitors`](crate::event_loop::EventLoopWindowTarget::available_monitors).
|
||||
//! - [`Window::available_monitors`](crate::window::Window::available_monitors).
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
platform_impl,
|
||||
@@ -16,10 +12,7 @@ use crate::{
|
||||
|
||||
/// Describes a fullscreen video mode of a monitor.
|
||||
///
|
||||
/// Can be acquired with:
|
||||
/// - [`MonitorHandle::video_modes`][monitor_get].
|
||||
///
|
||||
/// [monitor_get]: crate::monitor::MonitorHandle::video_modes
|
||||
/// Can be acquired with [`MonitorHandle::video_modes`].
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct VideoMode {
|
||||
pub(crate) video_mode: platform_impl::VideoMode,
|
||||
@@ -39,15 +32,12 @@ impl PartialOrd for VideoMode {
|
||||
|
||||
impl Ord for VideoMode {
|
||||
fn cmp(&self, other: &VideoMode) -> std::cmp::Ordering {
|
||||
// TODO: we can impl `Ord` for `PhysicalSize` once we switch from `f32`
|
||||
// to `u32` there
|
||||
let size: (u32, u32) = self.size().into();
|
||||
let other_size: (u32, u32) = other.size().into();
|
||||
self.monitor().cmp(&other.monitor()).then(
|
||||
size.cmp(&other_size)
|
||||
self.size()
|
||||
.cmp(&other.size())
|
||||
.then(
|
||||
self.refresh_rate()
|
||||
.cmp(&other.refresh_rate())
|
||||
self.refresh_rate_millihertz()
|
||||
.cmp(&other.refresh_rate_millihertz())
|
||||
.then(self.bit_depth().cmp(&other.bit_depth())),
|
||||
)
|
||||
.reverse(),
|
||||
@@ -68,26 +58,26 @@ impl VideoMode {
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Wayland:** Always returns 32.
|
||||
/// - **Wayland / Orbital:** Always returns 32.
|
||||
/// - **iOS:** Always returns 32.
|
||||
#[inline]
|
||||
pub fn bit_depth(&self) -> u16 {
|
||||
self.video_mode.bit_depth()
|
||||
}
|
||||
|
||||
/// Returns the refresh rate of this video mode. **Note**: the returned
|
||||
/// refresh rate is an integer approximation, and you shouldn't rely on this
|
||||
/// value to be exact.
|
||||
/// Returns the refresh rate of this video mode in mHz.
|
||||
#[inline]
|
||||
pub fn refresh_rate(&self) -> u16 {
|
||||
self.video_mode.refresh_rate()
|
||||
pub fn refresh_rate_millihertz(&self) -> u32 {
|
||||
self.video_mode.refresh_rate_millihertz()
|
||||
}
|
||||
|
||||
/// Returns the monitor that this video mode is valid for. Each monitor has
|
||||
/// a separate set of valid video modes.
|
||||
#[inline]
|
||||
pub fn monitor(&self) -> MonitorHandle {
|
||||
self.video_mode.monitor()
|
||||
MonitorHandle {
|
||||
inner: self.video_mode.monitor(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,10 +85,10 @@ impl std::fmt::Display for VideoMode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}x{} @ {} Hz ({} bpp)",
|
||||
"{}x{} @ {} mHz ({} bpp)",
|
||||
self.size().width,
|
||||
self.size().height,
|
||||
self.refresh_rate(),
|
||||
self.refresh_rate_millihertz(),
|
||||
self.bit_depth()
|
||||
)
|
||||
}
|
||||
@@ -148,6 +138,18 @@ impl MonitorHandle {
|
||||
self.inner.position()
|
||||
}
|
||||
|
||||
/// The monitor refresh rate used by the system.
|
||||
///
|
||||
/// Return `Some` if succeed, or `None` if failed, which usually happens when the monitor
|
||||
/// the window is on is removed.
|
||||
///
|
||||
/// When using exclusive fullscreen, the refresh rate of the [`VideoMode`] that was used to
|
||||
/// enter fullscreen should be used instead.
|
||||
#[inline]
|
||||
pub fn refresh_rate_millihertz(&self) -> Option<u32> {
|
||||
self.inner.refresh_rate_millihertz()
|
||||
}
|
||||
|
||||
/// Returns the scale factor that can be used to map logical pixels to physical pixels, and vice versa.
|
||||
///
|
||||
/// See the [`dpi`](crate::dpi) module for more information.
|
||||
@@ -169,6 +171,8 @@ impl MonitorHandle {
|
||||
/// - **Web:** Always returns an empty iterator
|
||||
#[inline]
|
||||
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
|
||||
self.inner.video_modes()
|
||||
self.inner
|
||||
.video_modes()
|
||||
.map(|video_mode| VideoMode { video_mode })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,88 @@
|
||||
#![cfg(any(target_os = "android"))]
|
||||
use crate::{
|
||||
event_loop::{EventLoop, EventLoopBuilder, EventLoopWindowTarget},
|
||||
window::{Window, WindowBuilder},
|
||||
};
|
||||
|
||||
use crate::{EventLoop, Window, WindowBuilder};
|
||||
use std::os::raw::c_void;
|
||||
use android_activity::{AndroidApp, ConfigurationRef, Rect};
|
||||
|
||||
/// Additional methods on `EventLoop` that are specific to Android.
|
||||
pub trait EventLoopExtAndroid {
|
||||
/// Makes it possible for glutin to register a callback when a suspend event happens on Android
|
||||
fn set_suspend_callback(&self, cb: Option<Box<dyn Fn(bool) -> ()>>);
|
||||
}
|
||||
/// Additional methods on [`EventLoop`] that are specific to Android.
|
||||
pub trait EventLoopExtAndroid {}
|
||||
|
||||
impl EventLoopExtAndroid for EventLoop {
|
||||
fn set_suspend_callback(&self, cb: Option<Box<dyn Fn(bool) -> ()>>) {
|
||||
self.event_loop.set_suspend_callback(cb);
|
||||
}
|
||||
}
|
||||
impl<T> EventLoopExtAndroid for EventLoop<T> {}
|
||||
|
||||
/// Additional methods on `Window` that are specific to Android.
|
||||
/// Additional methods on [`EventLoopWindowTarget`] that are specific to Android.
|
||||
pub trait EventLoopWindowTargetExtAndroid {}
|
||||
|
||||
/// Additional methods on [`Window`] that are specific to Android.
|
||||
pub trait WindowExtAndroid {
|
||||
fn native_window(&self) -> *const c_void;
|
||||
fn content_rect(&self) -> Rect;
|
||||
|
||||
fn config(&self) -> ConfigurationRef;
|
||||
}
|
||||
|
||||
impl WindowExtAndroid for Window {
|
||||
#[inline]
|
||||
fn native_window(&self) -> *const c_void {
|
||||
self.window.native_window()
|
||||
fn content_rect(&self) -> Rect {
|
||||
self.window.content_rect()
|
||||
}
|
||||
|
||||
fn config(&self) -> ConfigurationRef {
|
||||
self.window.config()
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `WindowBuilder` that are specific to Android.
|
||||
impl<T> EventLoopWindowTargetExtAndroid for EventLoopWindowTarget<T> {}
|
||||
|
||||
/// Additional methods on [`WindowBuilder`] that are specific to Android.
|
||||
pub trait WindowBuilderExtAndroid {}
|
||||
|
||||
impl WindowBuilderExtAndroid for WindowBuilder {}
|
||||
|
||||
pub trait EventLoopBuilderExtAndroid {
|
||||
/// Associates the `AndroidApp` that was passed to `android_main()` with the event loop
|
||||
///
|
||||
/// This must be called on Android since the `AndroidApp` is not global state.
|
||||
fn with_android_app(&mut self, app: AndroidApp) -> &mut Self;
|
||||
|
||||
/// Calling this will mark the volume keys to be manually handled by the application
|
||||
///
|
||||
/// Default is to let the operating system handle the volume keys
|
||||
fn handle_volume_keys(&mut self) -> &mut Self;
|
||||
}
|
||||
|
||||
impl<T> EventLoopBuilderExtAndroid for EventLoopBuilder<T> {
|
||||
fn with_android_app(&mut self, app: AndroidApp) -> &mut Self {
|
||||
self.platform_specific.android_app = Some(app);
|
||||
self
|
||||
}
|
||||
|
||||
fn handle_volume_keys(&mut self) -> &mut Self {
|
||||
self.platform_specific.ignore_volume_keys = false;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Re-export of the `android_activity` API
|
||||
///
|
||||
/// Winit re-exports the `android_activity` API for convenience so that most
|
||||
/// applications can rely on the Winit crate to resolve the required version of
|
||||
/// `android_activity` and avoid any chance of a conflict between Winit and the
|
||||
/// application crate.
|
||||
///
|
||||
/// Unlike most libraries there can only be a single implementation
|
||||
/// of the `android_activity` glue crate linked with an application because
|
||||
/// it is responsible for the application's `android_main()` entry point.
|
||||
///
|
||||
/// Since Winit depends on a specific version of `android_activity` the simplest
|
||||
/// way to avoid creating a conflict is for applications to avoid explicitly
|
||||
/// depending on the `android_activity` crate, and instead consume the API that
|
||||
/// is re-exported by Winit.
|
||||
///
|
||||
/// For compatibility applications should then import the `AndroidApp` type for
|
||||
/// their `android_main(app: AndroidApp)` function like:
|
||||
/// ```rust
|
||||
/// #[cfg(target_os="android")]
|
||||
/// use winit::platform::android::activity::AndroidApp;
|
||||
/// ```
|
||||
pub mod activity {
|
||||
pub use android_activity::*;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#![cfg(target_os = "ios")]
|
||||
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use objc2::rc::Id;
|
||||
|
||||
use crate::{
|
||||
event_loop::EventLoop,
|
||||
monitor::{MonitorHandle, VideoMode},
|
||||
@@ -99,17 +99,17 @@ pub trait WindowExtIOS {
|
||||
impl WindowExtIOS for Window {
|
||||
#[inline]
|
||||
fn ui_window(&self) -> *mut c_void {
|
||||
self.window.ui_window() as _
|
||||
self.window.ui_window()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ui_view_controller(&self) -> *mut c_void {
|
||||
self.window.ui_view_controller() as _
|
||||
self.window.ui_view_controller()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ui_view(&self) -> *mut c_void {
|
||||
self.window.ui_view() as _
|
||||
self.window.ui_view()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -141,13 +141,6 @@ impl WindowExtIOS for Window {
|
||||
|
||||
/// Additional methods on [`WindowBuilder`] that are specific to iOS.
|
||||
pub trait WindowBuilderExtIOS {
|
||||
/// Sets the root view class used by the [`Window`], otherwise a barebones [`UIView`] is provided.
|
||||
///
|
||||
/// An instance of the class will be initialized by calling [`-[UIView initWithFrame:]`](https://developer.apple.com/documentation/uikit/uiview/1622488-initwithframe?language=objc).
|
||||
///
|
||||
/// [`UIView`]: https://developer.apple.com/documentation/uikit/uiview?language=objc
|
||||
fn with_root_view_class(self, root_view_class: *const c_void) -> WindowBuilder;
|
||||
|
||||
/// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `scale_factor`.
|
||||
///
|
||||
/// The default value is device dependent, and it's recommended GLES or Metal applications set
|
||||
@@ -197,12 +190,6 @@ pub trait WindowBuilderExtIOS {
|
||||
}
|
||||
|
||||
impl WindowBuilderExtIOS for WindowBuilder {
|
||||
#[inline]
|
||||
fn with_root_view_class(mut self, root_view_class: *const c_void) -> WindowBuilder {
|
||||
self.platform_specific.root_view_class = unsafe { &*(root_view_class as *const _) };
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_scale_factor(mut self, scale_factor: f64) -> WindowBuilder {
|
||||
self.platform_specific.scale_factor = Some(scale_factor);
|
||||
@@ -254,19 +241,22 @@ pub trait MonitorHandleExtIOS {
|
||||
impl MonitorHandleExtIOS for MonitorHandle {
|
||||
#[inline]
|
||||
fn ui_screen(&self) -> *mut c_void {
|
||||
self.inner.ui_screen() as _
|
||||
Id::as_ptr(self.inner.ui_screen()) as *mut c_void
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn preferred_video_mode(&self) -> VideoMode {
|
||||
self.inner.preferred_video_mode()
|
||||
VideoMode {
|
||||
video_mode: self.inner.preferred_video_mode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Valid orientations for a particular [`Window`].
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum ValidOrientations {
|
||||
/// Excludes `PortraitUpsideDown` on iphone
|
||||
#[default]
|
||||
LandscapeAndPortrait,
|
||||
|
||||
Landscape,
|
||||
@@ -275,17 +265,10 @@ pub enum ValidOrientations {
|
||||
Portrait,
|
||||
}
|
||||
|
||||
impl Default for ValidOrientations {
|
||||
#[inline]
|
||||
fn default() -> ValidOrientations {
|
||||
ValidOrientations::LandscapeAndPortrait
|
||||
}
|
||||
}
|
||||
|
||||
/// The device [idiom].
|
||||
///
|
||||
/// [idiom]: https://developer.apple.com/documentation/uikit/uidevice/1620037-userinterfaceidiom?language=objc
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum Idiom {
|
||||
Unspecified,
|
||||
|
||||
@@ -304,14 +287,14 @@ bitflags! {
|
||||
/// The [edges] of a screen.
|
||||
///
|
||||
/// [edges]: https://developer.apple.com/documentation/uikit/uirectedge?language=objc
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ScreenEdge: u8 {
|
||||
const NONE = 0;
|
||||
const TOP = 1 << 0;
|
||||
const LEFT = 1 << 1;
|
||||
const BOTTOM = 1 << 2;
|
||||
const RIGHT = 1 << 3;
|
||||
const ALL = ScreenEdge::TOP.bits | ScreenEdge::LEFT.bits
|
||||
| ScreenEdge::BOTTOM.bits | ScreenEdge::RIGHT.bits;
|
||||
const ALL = ScreenEdge::TOP.bits() | ScreenEdge::LEFT.bits()
|
||||
| ScreenEdge::BOTTOM.bits() | ScreenEdge::RIGHT.bits();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +1,25 @@
|
||||
#![cfg(target_os = "macos")]
|
||||
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use objc2::rc::Id;
|
||||
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
event_loop::EventLoopWindowTarget,
|
||||
event_loop::{EventLoopBuilder, EventLoopWindowTarget},
|
||||
monitor::MonitorHandle,
|
||||
window::{Window, WindowBuilder},
|
||||
};
|
||||
|
||||
/// Corresponds to `NSRequestUserAttentionType`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum RequestUserAttentionType {
|
||||
/// Corresponds to `NSCriticalRequest`.
|
||||
///
|
||||
/// Dock icon will bounce until the application is focused.
|
||||
Critical,
|
||||
|
||||
/// Corresponds to `NSInformationalRequest`.
|
||||
///
|
||||
/// Dock icon will bounce once.
|
||||
Informational,
|
||||
}
|
||||
|
||||
impl Default for RequestUserAttentionType {
|
||||
fn default() -> Self {
|
||||
RequestUserAttentionType::Critical
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `Window` that are specific to MacOS.
|
||||
/// Additional methods on [`Window`] that are specific to MacOS.
|
||||
pub trait WindowExtMacOS {
|
||||
/// Returns a pointer to the cocoa `NSWindow` that is used by this window.
|
||||
///
|
||||
/// The pointer will become invalid when the `Window` is destroyed.
|
||||
/// The pointer will become invalid when the [`Window`] is destroyed.
|
||||
fn ns_window(&self) -> *mut c_void;
|
||||
|
||||
/// Returns a pointer to the cocoa `NSView` that is used by this window.
|
||||
///
|
||||
/// The pointer will become invalid when the `Window` is destroyed.
|
||||
/// The pointer will become invalid when the [`Window`] is destroyed.
|
||||
fn ns_view(&self) -> *mut c_void;
|
||||
|
||||
/// Request user attention, causing the application's dock icon to bounce.
|
||||
/// Note that this has no effect if the application is already focused.
|
||||
fn request_user_attention(&self, request_type: RequestUserAttentionType);
|
||||
|
||||
/// Returns whether or not the window is in simple fullscreen mode.
|
||||
fn simple_fullscreen(&self) -> bool;
|
||||
|
||||
@@ -56,6 +31,42 @@ pub trait WindowExtMacOS {
|
||||
/// And allows the user to have a fullscreen window without using another
|
||||
/// space or taking control over the entire monitor.
|
||||
fn set_simple_fullscreen(&self, fullscreen: bool) -> bool;
|
||||
|
||||
/// Returns whether or not the window has shadow.
|
||||
fn has_shadow(&self) -> bool;
|
||||
|
||||
/// Sets whether or not the window has shadow.
|
||||
fn set_has_shadow(&self, has_shadow: bool);
|
||||
|
||||
/// Get the window's edit state.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// WindowEvent::CloseRequested => {
|
||||
/// if window.is_document_edited() {
|
||||
/// // Show the user a save pop-up or similar
|
||||
/// } else {
|
||||
/// // Close the window
|
||||
/// drop(window);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
fn is_document_edited(&self) -> bool;
|
||||
|
||||
/// Put the window in a state which indicates a file save is required.
|
||||
fn set_document_edited(&self, edited: bool);
|
||||
|
||||
/// Set option as alt behavior as described in [`OptionAsAlt`].
|
||||
///
|
||||
/// This will ignore diacritical marks and accent characters from
|
||||
/// being processed as received characters. Instead, the input
|
||||
/// device's raw character will be placed in event queues with the
|
||||
/// Alt modifier set.
|
||||
fn set_option_as_alt(&self, option_as_alt: OptionAsAlt);
|
||||
|
||||
/// Getter for the [`WindowExtMacOS::set_option_as_alt`].
|
||||
fn option_as_alt(&self) -> OptionAsAlt;
|
||||
}
|
||||
|
||||
impl WindowExtMacOS for Window {
|
||||
@@ -69,11 +80,6 @@ impl WindowExtMacOS for Window {
|
||||
self.window.ns_view()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn request_user_attention(&self, request_type: RequestUserAttentionType) {
|
||||
self.window.request_user_attention(request_type)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn simple_fullscreen(&self) -> bool {
|
||||
self.window.simple_fullscreen()
|
||||
@@ -83,38 +89,61 @@ impl WindowExtMacOS for Window {
|
||||
fn set_simple_fullscreen(&self, fullscreen: bool) -> bool {
|
||||
self.window.set_simple_fullscreen(fullscreen)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn has_shadow(&self) -> bool {
|
||||
self.window.has_shadow()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_has_shadow(&self, has_shadow: bool) {
|
||||
self.window.set_has_shadow(has_shadow)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_document_edited(&self) -> bool {
|
||||
self.window.is_document_edited()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_document_edited(&self, edited: bool) {
|
||||
self.window.set_document_edited(edited)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_option_as_alt(&self, option_as_alt: OptionAsAlt) {
|
||||
self.window.set_option_as_alt(option_as_alt)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn option_as_alt(&self) -> OptionAsAlt {
|
||||
self.window.option_as_alt()
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to `NSApplicationActivationPolicy`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum ActivationPolicy {
|
||||
/// Corresponds to `NSApplicationActivationPolicyRegular`.
|
||||
#[default]
|
||||
Regular,
|
||||
|
||||
/// Corresponds to `NSApplicationActivationPolicyAccessory`.
|
||||
Accessory,
|
||||
|
||||
/// Corresponds to `NSApplicationActivationPolicyProhibited`.
|
||||
Prohibited,
|
||||
}
|
||||
|
||||
impl Default for ActivationPolicy {
|
||||
fn default() -> Self {
|
||||
ActivationPolicy::Regular
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `WindowBuilder` that are specific to MacOS.
|
||||
/// Additional methods on [`WindowBuilder`] that are specific to MacOS.
|
||||
///
|
||||
/// **Note:** Properties dealing with the titlebar will be overwritten by the `with_decorations` method
|
||||
/// on the base `WindowBuilder`:
|
||||
///
|
||||
/// - `with_titlebar_transparent`
|
||||
/// - `with_title_hidden`
|
||||
/// - `with_titlebar_hidden`
|
||||
/// - `with_titlebar_buttons_hidden`
|
||||
/// - `with_fullsize_content_view`
|
||||
/// **Note:** Properties dealing with the titlebar will be overwritten by the [`WindowBuilder::with_decorations`] method:
|
||||
/// - `with_titlebar_transparent`
|
||||
/// - `with_title_hidden`
|
||||
/// - `with_titlebar_hidden`
|
||||
/// - `with_titlebar_buttons_hidden`
|
||||
/// - `with_fullsize_content_view`
|
||||
pub trait WindowBuilderExtMacOS {
|
||||
/// Sets the activation policy for the window being built.
|
||||
fn with_activation_policy(self, activation_policy: ActivationPolicy) -> WindowBuilder;
|
||||
/// Enables click-and-drag behavior for the entire window, not just the titlebar.
|
||||
fn with_movable_by_window_background(self, movable_by_window_background: bool)
|
||||
-> WindowBuilder;
|
||||
@@ -128,18 +157,17 @@ pub trait WindowBuilderExtMacOS {
|
||||
fn with_titlebar_buttons_hidden(self, titlebar_buttons_hidden: bool) -> WindowBuilder;
|
||||
/// Makes the window content appear behind the titlebar.
|
||||
fn with_fullsize_content_view(self, fullsize_content_view: bool) -> WindowBuilder;
|
||||
/// Build window with `resizeIncrements` property. Values must not be 0.
|
||||
fn with_resize_increments(self, increments: LogicalSize<f64>) -> WindowBuilder;
|
||||
fn with_disallow_hidpi(self, disallow_hidpi: bool) -> WindowBuilder;
|
||||
fn with_has_shadow(self, has_shadow: bool) -> WindowBuilder;
|
||||
/// Window accepts click-through mouse events.
|
||||
fn with_accepts_first_mouse(self, accepts_first_mouse: bool) -> WindowBuilder;
|
||||
/// Set how the <kbd>Option</kbd> keys are interpreted.
|
||||
///
|
||||
/// See [`WindowExtMacOS::set_option_as_alt`] for details on what this means if set.
|
||||
fn with_option_as_alt(self, option_as_alt: OptionAsAlt) -> WindowBuilder;
|
||||
}
|
||||
|
||||
impl WindowBuilderExtMacOS for WindowBuilder {
|
||||
#[inline]
|
||||
fn with_activation_policy(mut self, activation_policy: ActivationPolicy) -> WindowBuilder {
|
||||
self.platform_specific.activation_policy = activation_policy;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_movable_by_window_background(
|
||||
mut self,
|
||||
@@ -179,20 +207,104 @@ impl WindowBuilderExtMacOS for WindowBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_resize_increments(mut self, increments: LogicalSize<f64>) -> WindowBuilder {
|
||||
self.platform_specific.resize_increments = Some(increments.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_disallow_hidpi(mut self, disallow_hidpi: bool) -> WindowBuilder {
|
||||
self.platform_specific.disallow_hidpi = disallow_hidpi;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_has_shadow(mut self, has_shadow: bool) -> WindowBuilder {
|
||||
self.platform_specific.has_shadow = has_shadow;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_accepts_first_mouse(mut self, accepts_first_mouse: bool) -> WindowBuilder {
|
||||
self.platform_specific.accepts_first_mouse = accepts_first_mouse;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_option_as_alt(mut self, option_as_alt: OptionAsAlt) -> WindowBuilder {
|
||||
self.platform_specific.option_as_alt = option_as_alt;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `MonitorHandle` that are specific to MacOS.
|
||||
pub trait EventLoopBuilderExtMacOS {
|
||||
/// Sets the activation policy for the application.
|
||||
///
|
||||
/// It is set to [`ActivationPolicy::Regular`] by default.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Set the activation policy to "accessory".
|
||||
///
|
||||
/// ```
|
||||
/// use winit::event_loop::EventLoopBuilder;
|
||||
/// #[cfg(target_os = "macos")]
|
||||
/// use winit::platform::macos::{EventLoopBuilderExtMacOS, ActivationPolicy};
|
||||
///
|
||||
/// let mut builder = EventLoopBuilder::new();
|
||||
/// #[cfg(target_os = "macos")]
|
||||
/// builder.with_activation_policy(ActivationPolicy::Accessory);
|
||||
/// # if false { // We can't test this part
|
||||
/// let event_loop = builder.build();
|
||||
/// # }
|
||||
/// ```
|
||||
fn with_activation_policy(&mut self, activation_policy: ActivationPolicy) -> &mut Self;
|
||||
|
||||
/// Used to control whether a default menubar menu is created.
|
||||
///
|
||||
/// Menu creation is enabled by default.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Disable creating a default menubar.
|
||||
///
|
||||
/// ```
|
||||
/// use winit::event_loop::EventLoopBuilder;
|
||||
/// #[cfg(target_os = "macos")]
|
||||
/// use winit::platform::macos::EventLoopBuilderExtMacOS;
|
||||
///
|
||||
/// let mut builder = EventLoopBuilder::new();
|
||||
/// #[cfg(target_os = "macos")]
|
||||
/// builder.with_default_menu(false);
|
||||
/// # if false { // We can't test this part
|
||||
/// let event_loop = builder.build();
|
||||
/// # }
|
||||
/// ```
|
||||
fn with_default_menu(&mut self, enable: bool) -> &mut Self;
|
||||
|
||||
/// Used to prevent the application from automatically activating when launched if
|
||||
/// another application is already active.
|
||||
///
|
||||
/// The default behavior is to ignore other applications and activate when launched.
|
||||
fn with_activate_ignoring_other_apps(&mut self, ignore: bool) -> &mut Self;
|
||||
}
|
||||
|
||||
impl<T> EventLoopBuilderExtMacOS for EventLoopBuilder<T> {
|
||||
#[inline]
|
||||
fn with_activation_policy(&mut self, activation_policy: ActivationPolicy) -> &mut Self {
|
||||
self.platform_specific.activation_policy = activation_policy;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_default_menu(&mut self, enable: bool) -> &mut Self {
|
||||
self.platform_specific.default_menu = enable;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_activate_ignoring_other_apps(&mut self, ignore: bool) -> &mut Self {
|
||||
self.platform_specific.activate_ignoring_other_apps = ignore;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`MonitorHandle`] that are specific to MacOS.
|
||||
pub trait MonitorHandleExtMacOS {
|
||||
/// Returns the identifier of the monitor for Cocoa.
|
||||
fn native_id(&self) -> u32;
|
||||
@@ -207,20 +319,44 @@ impl MonitorHandleExtMacOS for MonitorHandle {
|
||||
}
|
||||
|
||||
fn ns_screen(&self) -> Option<*mut c_void> {
|
||||
self.inner.ns_screen().map(|s| s as *mut c_void)
|
||||
self.inner.ns_screen().map(|s| Id::as_ptr(&s) as _)
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `EventLoopWindowTarget` that are specific to macOS.
|
||||
/// Additional methods on [`EventLoopWindowTarget`] that are specific to macOS.
|
||||
pub trait EventLoopWindowTargetExtMacOS {
|
||||
/// Hide the entire application. In most applications this is typically triggered with Command-H.
|
||||
fn hide_application(&self);
|
||||
/// Hide the other applications. In most applications this is typically triggered with Command+Option-H.
|
||||
fn hide_other_applications(&self);
|
||||
}
|
||||
|
||||
impl<T> EventLoopWindowTargetExtMacOS for EventLoopWindowTarget<T> {
|
||||
fn hide_application(&self) {
|
||||
let cls = objc::runtime::Class::get("NSApplication").unwrap();
|
||||
let app: cocoa::base::id = unsafe { msg_send![cls, sharedApplication] };
|
||||
unsafe { msg_send![app, hide: 0] }
|
||||
self.p.hide_application()
|
||||
}
|
||||
|
||||
fn hide_other_applications(&self) {
|
||||
self.p.hide_other_applications()
|
||||
}
|
||||
}
|
||||
|
||||
/// Option as alt behavior.
|
||||
///
|
||||
/// The default is `None`.
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum OptionAsAlt {
|
||||
/// The left `Option` key is treated as `Alt`.
|
||||
OnlyLeft,
|
||||
|
||||
/// The right `Option` key is treated as `Alt`.
|
||||
OnlyRight,
|
||||
|
||||
/// Both `Option` keys are treated as `Alt`.
|
||||
Both,
|
||||
|
||||
/// No special handling is applied for `Option` key.
|
||||
#[default]
|
||||
None,
|
||||
}
|
||||
|
||||
@@ -11,15 +11,35 @@
|
||||
//!
|
||||
//! And the following platform-specific module:
|
||||
//!
|
||||
//! - `desktop` (available on `windows`, `unix`, and `macos`)
|
||||
//! - `run_return` (available on `windows`, `unix`, `macos`, and `android`)
|
||||
//!
|
||||
//! However only the module corresponding to the platform you're compiling to will be available.
|
||||
|
||||
#[cfg(android_platform)]
|
||||
pub mod android;
|
||||
#[cfg(ios_platform)]
|
||||
pub mod ios;
|
||||
#[cfg(macos_platform)]
|
||||
pub mod macos;
|
||||
pub mod unix;
|
||||
pub mod windows;
|
||||
|
||||
pub mod desktop;
|
||||
#[cfg(orbital_platform)]
|
||||
pub mod orbital;
|
||||
#[cfg(wayland_platform)]
|
||||
pub mod wayland;
|
||||
#[cfg(wasm_platform)]
|
||||
pub mod web;
|
||||
#[cfg(windows_platform)]
|
||||
pub mod windows;
|
||||
#[cfg(x11_platform)]
|
||||
pub mod x11;
|
||||
|
||||
pub mod modifier_supplement;
|
||||
#[cfg(any(
|
||||
windows_platform,
|
||||
macos_platform,
|
||||
android_platform,
|
||||
x11_platform,
|
||||
wayland_platform,
|
||||
orbital_platform
|
||||
))]
|
||||
pub mod run_return;
|
||||
pub mod scancode;
|
||||
|
||||
24
src/platform/modifier_supplement.rs
Normal file
24
src/platform/modifier_supplement.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
#![cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform))]
|
||||
|
||||
use crate::keyboard::Key;
|
||||
|
||||
/// Additional methods for the `KeyEvent` which cannot be implemented on all
|
||||
/// platforms.
|
||||
pub trait KeyEventExtModifierSupplement {
|
||||
/// Identical to `KeyEvent::text` but this is affected by <kbd>Ctrl</kbd>.
|
||||
///
|
||||
/// For example, pressing <kbd>Ctrl</kbd>+<kbd>a</kbd> produces `Some("\x01")`.
|
||||
fn text_with_all_modifiers(&self) -> Option<&str>;
|
||||
|
||||
/// This value ignores all modifiers including,
|
||||
/// but not limited to <kbd>Shift</kbd>, <kbd>Caps Lock</kbd>,
|
||||
/// and <kbd>Ctrl</kbd>. In most cases this means that the
|
||||
/// unicode character in the resulting string is lowercase.
|
||||
///
|
||||
/// This is useful for key-bindings / shortcut key combinations.
|
||||
///
|
||||
/// In case `logical_key` reports `Dead`, this will still report the
|
||||
/// key as `Character` according to the current keyboard layout. This value
|
||||
/// cannot be `Dead`.
|
||||
fn key_without_modifiers(&self) -> Key;
|
||||
}
|
||||
1
src/platform/orbital.rs
Normal file
1
src/platform/orbital.rs
Normal file
@@ -0,0 +1 @@
|
||||
// There are no Orbital specific traits yet.
|
||||
@@ -1,34 +1,34 @@
|
||||
#![cfg(any(
|
||||
target_os = "windows",
|
||||
target_os = "macos",
|
||||
target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"
|
||||
))]
|
||||
|
||||
use crate::{
|
||||
event::Event,
|
||||
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
|
||||
};
|
||||
|
||||
/// Additional methods on `EventLoop` that are specific to desktop platforms.
|
||||
pub trait EventLoopExtDesktop {
|
||||
/// A type provided by the user that can be passed through `Event::UserEvent`.
|
||||
/// Additional methods on [`EventLoop`] to return control flow to the caller.
|
||||
pub trait EventLoopExtRunReturn {
|
||||
/// A type provided by the user that can be passed through [`Event::UserEvent`].
|
||||
type UserEvent;
|
||||
|
||||
/// Initializes the `winit` event loop.
|
||||
///
|
||||
/// Unlike `run`, this function accepts non-`'static` (i.e. non-`move`) closures and returns
|
||||
/// control flow to the caller when `control_flow` is set to `ControlFlow::Exit`.
|
||||
/// Unlike [`EventLoop::run`], this function accepts non-`'static` (i.e. non-`move`) closures
|
||||
/// and returns control flow to the caller when `control_flow` is set to [`ControlFlow::Exit`].
|
||||
///
|
||||
/// # Caveats
|
||||
/// Despite its apperance at first glance, this is *not* a perfect replacement for
|
||||
///
|
||||
/// Despite its appearance at first glance, this is *not* a perfect replacement for
|
||||
/// `poll_events`. For example, this function will not return on Windows or macOS while a
|
||||
/// window is getting resized, resulting in all application logic outside of the
|
||||
/// `event_handler` closure not running until the resize operation ends. Other OS operations
|
||||
/// may also result in such freezes. This behavior is caused by fundamental limitations in the
|
||||
/// underyling OS APIs, which cannot be hidden by Winit without severe stability reprecussions.
|
||||
/// underlying OS APIs, which cannot be hidden by `winit` without severe stability repercussions.
|
||||
///
|
||||
/// You are strongly encouraged to use `run`, unless the use of this is absolutely necessary.
|
||||
fn run_return<F>(&mut self, event_handler: F)
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **X11 / Wayland:** This function returns `1` upon disconnection from
|
||||
/// the display server.
|
||||
fn run_return<F>(&mut self, event_handler: F) -> i32
|
||||
where
|
||||
F: FnMut(
|
||||
Event<'_, Self::UserEvent>,
|
||||
@@ -37,10 +37,10 @@ pub trait EventLoopExtDesktop {
|
||||
);
|
||||
}
|
||||
|
||||
impl<T> EventLoopExtDesktop for EventLoop<T> {
|
||||
impl<T> EventLoopExtRunReturn for EventLoop<T> {
|
||||
type UserEvent = T;
|
||||
|
||||
fn run_return<F>(&mut self, event_handler: F)
|
||||
fn run_return<F>(&mut self, event_handler: F) -> i32
|
||||
where
|
||||
F: FnMut(
|
||||
Event<'_, Self::UserEvent>,
|
||||
30
src/platform/scancode.rs
Normal file
30
src/platform/scancode.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
#![cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform))]
|
||||
|
||||
use crate::keyboard::KeyCode;
|
||||
|
||||
// TODO: Describe what this value contains for each platform
|
||||
|
||||
/// Additional methods for the [`KeyCode`] type that allow the user to access the platform-specific
|
||||
/// scancode.
|
||||
///
|
||||
/// [`KeyCode`]: crate::keyboard::KeyCode
|
||||
pub trait KeyCodeExtScancode {
|
||||
/// The raw value of the platform-specific physical key identifier.
|
||||
///
|
||||
/// Returns `Some(key_id)` if the conversion was succesful; returns `None` otherwise.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
/// - **Windows:** A 16bit extended scancode
|
||||
/// - **Wayland/X11**: A 32-bit linux scancode, which is X11/Wayland keycode subtracted by 8.
|
||||
fn to_scancode(self) -> Option<u32>;
|
||||
|
||||
/// Constructs a `KeyCode` from a platform-specific physical key identifier.
|
||||
///
|
||||
/// Note that this conversion may be lossy, i.e. converting the returned `KeyCode` back
|
||||
/// using `to_scancode` might not yield the original value.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
/// - **Wayland/X11**: A 32-bit linux scancode. When building from X11/Wayland keycode subtract
|
||||
/// `8` to get the value you wanted.
|
||||
fn from_scancode(scancode: u32) -> KeyCode;
|
||||
}
|
||||
@@ -1,489 +0,0 @@
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
|
||||
|
||||
use std::{os::raw, ptr, sync::Arc};
|
||||
|
||||
use smithay_client_toolkit::window::{ButtonState as SCTKButtonState, Theme as SCTKTheme};
|
||||
|
||||
use crate::{
|
||||
dpi::Size,
|
||||
event_loop::{EventLoop, EventLoopWindowTarget},
|
||||
monitor::MonitorHandle,
|
||||
window::{Window, WindowBuilder},
|
||||
};
|
||||
|
||||
use crate::platform_impl::{
|
||||
x11::{ffi::XVisualInfo, XConnection},
|
||||
EventLoop as LinuxEventLoop, EventLoopWindowTarget as LinuxEventLoopWindowTarget,
|
||||
Window as LinuxWindow,
|
||||
};
|
||||
|
||||
// TODO: stupid hack so that glutin can do its work
|
||||
#[doc(hidden)]
|
||||
pub use crate::platform_impl::x11;
|
||||
|
||||
pub use crate::platform_impl::{x11::util::WindowType as XWindowType, XNotSupported};
|
||||
|
||||
/// Additional methods on `EventLoopWindowTarget` that are specific to Unix.
|
||||
pub trait EventLoopWindowTargetExtUnix {
|
||||
/// True if the `EventLoopWindowTarget` uses Wayland.
|
||||
fn is_wayland(&self) -> bool;
|
||||
///
|
||||
/// True if the `EventLoopWindowTarget` uses X11.
|
||||
fn is_x11(&self) -> bool;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn xlib_xconnection(&self) -> Option<Arc<XConnection>>;
|
||||
|
||||
/// Returns a pointer to the `wl_display` object of wayland that is used by this
|
||||
/// `EventLoopWindowTarget`.
|
||||
///
|
||||
/// Returns `None` if the `EventLoop` doesn't use wayland (if it uses xlib for example).
|
||||
///
|
||||
/// The pointer will become invalid when the winit `EventLoop` is destroyed.
|
||||
fn wayland_display(&self) -> Option<*mut raw::c_void>;
|
||||
}
|
||||
|
||||
impl<T> EventLoopWindowTargetExtUnix for EventLoopWindowTarget<T> {
|
||||
#[inline]
|
||||
fn is_wayland(&self) -> bool {
|
||||
self.p.is_wayland()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_x11(&self) -> bool {
|
||||
!self.p.is_wayland()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
fn xlib_xconnection(&self) -> Option<Arc<XConnection>> {
|
||||
match self.p {
|
||||
LinuxEventLoopWindowTarget::X(ref e) => Some(e.x_connection().clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn wayland_display(&self) -> Option<*mut raw::c_void> {
|
||||
match self.p {
|
||||
LinuxEventLoopWindowTarget::Wayland(ref p) => {
|
||||
Some(p.display().get_display_ptr() as *mut _)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `EventLoop` that are specific to Unix.
|
||||
pub trait EventLoopExtUnix {
|
||||
/// Builds a new `EventLoop` that is forced to use X11.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If called outside the main thread. To initialize an X11 event loop outside
|
||||
/// the main thread, use [`new_x11_any_thread`](#tymethod.new_x11_any_thread).
|
||||
fn new_x11() -> Result<Self, XNotSupported>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Builds a new `EventLoop` that is forced to use Wayland.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If called outside the main thread. To initialize a Wayland event loop outside
|
||||
/// the main thread, use [`new_wayland_any_thread`](#tymethod.new_wayland_any_thread).
|
||||
fn new_wayland() -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Builds a new `EventLoop` on any thread.
|
||||
///
|
||||
/// This method bypasses the cross-platform compatibility requirement
|
||||
/// that `EventLoop` be created on the main thread.
|
||||
fn new_any_thread() -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Builds a new X11 `EventLoop` on any thread.
|
||||
///
|
||||
/// This method bypasses the cross-platform compatibility requirement
|
||||
/// that `EventLoop` be created on the main thread.
|
||||
fn new_x11_any_thread() -> Result<Self, XNotSupported>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Builds a new Wayland `EventLoop` on any thread.
|
||||
///
|
||||
/// This method bypasses the cross-platform compatibility requirement
|
||||
/// that `EventLoop` be created on the main thread.
|
||||
fn new_wayland_any_thread() -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
fn wrap_ev<T>(event_loop: LinuxEventLoop<T>) -> EventLoop<T> {
|
||||
EventLoop {
|
||||
event_loop,
|
||||
_marker: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EventLoopExtUnix for EventLoop<T> {
|
||||
#[inline]
|
||||
fn new_any_thread() -> Self {
|
||||
wrap_ev(LinuxEventLoop::new_any_thread())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_x11_any_thread() -> Result<Self, XNotSupported> {
|
||||
LinuxEventLoop::new_x11_any_thread().map(wrap_ev)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_wayland_any_thread() -> Self {
|
||||
wrap_ev(
|
||||
LinuxEventLoop::new_wayland_any_thread()
|
||||
// TODO: propagate
|
||||
.expect("failed to open Wayland connection"),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_x11() -> Result<Self, XNotSupported> {
|
||||
LinuxEventLoop::new_x11().map(wrap_ev)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_wayland() -> Self {
|
||||
wrap_ev(
|
||||
LinuxEventLoop::new_wayland()
|
||||
// TODO: propagate
|
||||
.expect("failed to open Wayland connection"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `Window` that are specific to Unix.
|
||||
pub trait WindowExtUnix {
|
||||
/// Returns the ID of the `Window` xlib object that is used by this window.
|
||||
///
|
||||
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
|
||||
fn xlib_window(&self) -> Option<raw::c_ulong>;
|
||||
|
||||
/// Returns a pointer to the `Display` object of xlib that is used by this window.
|
||||
///
|
||||
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
|
||||
///
|
||||
/// The pointer will become invalid when the glutin `Window` is destroyed.
|
||||
fn xlib_display(&self) -> Option<*mut raw::c_void>;
|
||||
|
||||
fn xlib_screen_id(&self) -> Option<raw::c_int>;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn xlib_xconnection(&self) -> Option<Arc<XConnection>>;
|
||||
|
||||
/// Set window urgency hint (`XUrgencyHint`). Only relevant on X.
|
||||
fn set_urgent(&self, is_urgent: bool);
|
||||
|
||||
/// This function returns the underlying `xcb_connection_t` of an xlib `Display`.
|
||||
///
|
||||
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
|
||||
///
|
||||
/// The pointer will become invalid when the glutin `Window` is destroyed.
|
||||
fn xcb_connection(&self) -> Option<*mut raw::c_void>;
|
||||
|
||||
/// Returns a pointer to the `wl_surface` object of wayland that is used by this window.
|
||||
///
|
||||
/// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
|
||||
///
|
||||
/// The pointer will become invalid when the glutin `Window` is destroyed.
|
||||
fn wayland_surface(&self) -> Option<*mut raw::c_void>;
|
||||
|
||||
/// Returns a pointer to the `wl_display` object of wayland that is used by this window.
|
||||
///
|
||||
/// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
|
||||
///
|
||||
/// The pointer will become invalid when the glutin `Window` is destroyed.
|
||||
fn wayland_display(&self) -> Option<*mut raw::c_void>;
|
||||
|
||||
/// Sets the color theme of the client side window decorations on wayland
|
||||
fn set_wayland_theme<T: Theme>(&self, theme: T);
|
||||
|
||||
/// Check if the window is ready for drawing
|
||||
///
|
||||
/// It is a remnant of a previous implementation detail for the
|
||||
/// wayland backend, and is no longer relevant.
|
||||
///
|
||||
/// Always return true.
|
||||
#[deprecated]
|
||||
fn is_ready(&self) -> bool;
|
||||
}
|
||||
|
||||
impl WindowExtUnix for Window {
|
||||
#[inline]
|
||||
fn xlib_window(&self) -> Option<raw::c_ulong> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.xlib_window()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn xlib_display(&self) -> Option<*mut raw::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.xlib_display()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn xlib_screen_id(&self) -> Option<raw::c_int> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.xlib_screen_id()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
fn xlib_xconnection(&self) -> Option<Arc<XConnection>> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.xlib_xconnection()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_urgent(&self, is_urgent: bool) {
|
||||
if let LinuxWindow::X(ref w) = self.window {
|
||||
w.set_urgent(is_urgent);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn xcb_connection(&self) -> Option<*mut raw::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.xcb_connection()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn wayland_surface(&self) -> Option<*mut raw::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::Wayland(ref w) => Some(w.surface().as_ref().c_ptr() as *mut _),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn wayland_display(&self) -> Option<*mut raw::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::Wayland(ref w) => Some(w.display().as_ref().c_ptr() as *mut _),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_wayland_theme<T: Theme>(&self, theme: T) {
|
||||
match self.window {
|
||||
LinuxWindow::Wayland(ref w) => w.set_theme(WaylandTheme(theme)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_ready(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `WindowBuilder` that are specific to Unix.
|
||||
pub trait WindowBuilderExtUnix {
|
||||
fn with_x11_visual<T>(self, visual_infos: *const T) -> Self;
|
||||
fn with_x11_screen(self, screen_id: i32) -> Self;
|
||||
|
||||
/// Build window with `WM_CLASS` hint; defaults to the name of the binary. Only relevant on X11.
|
||||
fn with_class(self, class: String, instance: String) -> Self;
|
||||
/// Build window with override-redirect flag; defaults to false. Only relevant on X11.
|
||||
fn with_override_redirect(self, override_redirect: bool) -> Self;
|
||||
/// Build window with `_NET_WM_WINDOW_TYPE` hints; defaults to `Normal`. Only relevant on X11.
|
||||
fn with_x11_window_type(self, x11_window_type: Vec<XWindowType>) -> Self;
|
||||
/// Build window with `_GTK_THEME_VARIANT` hint set to the specified value. Currently only relevant on X11.
|
||||
fn with_gtk_theme_variant(self, variant: String) -> Self;
|
||||
/// Build window with resize increment hint. Only implemented on X11.
|
||||
fn with_resize_increments<S: Into<Size>>(self, increments: S) -> Self;
|
||||
/// Build window with base size hint. Only implemented on X11.
|
||||
fn with_base_size<S: Into<Size>>(self, base_size: S) -> Self;
|
||||
|
||||
/// Build window with a given application ID. It should match the `.desktop` file distributed with
|
||||
/// your program. Only relevant on Wayland.
|
||||
///
|
||||
/// For details about application ID conventions, see the
|
||||
/// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)
|
||||
fn with_app_id(self, app_id: String) -> Self;
|
||||
}
|
||||
|
||||
impl WindowBuilderExtUnix for WindowBuilder {
|
||||
#[inline]
|
||||
fn with_x11_visual<T>(mut self, visual_infos: *const T) -> Self {
|
||||
self.platform_specific.visual_infos =
|
||||
Some(unsafe { ptr::read(visual_infos as *const XVisualInfo) });
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_x11_screen(mut self, screen_id: i32) -> Self {
|
||||
self.platform_specific.screen_id = Some(screen_id);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_class(mut self, instance: String, class: String) -> Self {
|
||||
self.platform_specific.class = Some((instance, class));
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_override_redirect(mut self, override_redirect: bool) -> Self {
|
||||
self.platform_specific.override_redirect = override_redirect;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_x11_window_type(mut self, x11_window_types: Vec<XWindowType>) -> Self {
|
||||
self.platform_specific.x11_window_types = x11_window_types;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_gtk_theme_variant(mut self, variant: String) -> Self {
|
||||
self.platform_specific.gtk_theme_variant = Some(variant);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_resize_increments<S: Into<Size>>(mut self, increments: S) -> Self {
|
||||
self.platform_specific.resize_increments = Some(increments.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_base_size<S: Into<Size>>(mut self, base_size: S) -> Self {
|
||||
self.platform_specific.base_size = Some(base_size.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_app_id(mut self, app_id: String) -> Self {
|
||||
self.platform_specific.app_id = Some(app_id);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `MonitorHandle` that are specific to Linux.
|
||||
pub trait MonitorHandleExtUnix {
|
||||
/// Returns the inner identifier of the monitor.
|
||||
fn native_id(&self) -> u32;
|
||||
}
|
||||
|
||||
impl MonitorHandleExtUnix for MonitorHandle {
|
||||
#[inline]
|
||||
fn native_id(&self) -> u32 {
|
||||
self.inner.native_identifier()
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper for implementing SCTK's theme trait.
|
||||
struct WaylandTheme<T: Theme>(T);
|
||||
|
||||
pub trait Theme: Send + 'static {
|
||||
/// Primary color of the scheme.
|
||||
fn primary_color(&self, window_active: bool) -> [u8; 4];
|
||||
|
||||
/// Secondary color of the scheme.
|
||||
fn secondary_color(&self, window_active: bool) -> [u8; 4];
|
||||
|
||||
/// Color for the close button.
|
||||
fn close_button_color(&self, status: ButtonState) -> [u8; 4];
|
||||
|
||||
/// Icon color for the close button, defaults to the secondary color.
|
||||
#[allow(unused_variables)]
|
||||
fn close_button_icon_color(&self, status: ButtonState) -> [u8; 4] {
|
||||
self.secondary_color(true)
|
||||
}
|
||||
|
||||
/// Background color for the maximize button.
|
||||
fn maximize_button_color(&self, status: ButtonState) -> [u8; 4];
|
||||
|
||||
/// Icon color for the maximize button, defaults to the secondary color.
|
||||
#[allow(unused_variables)]
|
||||
fn maximize_button_icon_color(&self, status: ButtonState) -> [u8; 4] {
|
||||
self.secondary_color(true)
|
||||
}
|
||||
|
||||
/// Background color for the minimize button.
|
||||
fn minimize_button_color(&self, status: ButtonState) -> [u8; 4];
|
||||
|
||||
/// Icon color for the minimize button, defaults to the secondary color.
|
||||
#[allow(unused_variables)]
|
||||
fn minimize_button_icon_color(&self, status: ButtonState) -> [u8; 4] {
|
||||
self.secondary_color(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Theme> SCTKTheme for WaylandTheme<T> {
|
||||
fn get_primary_color(&self, active: bool) -> [u8; 4] {
|
||||
self.0.primary_color(active)
|
||||
}
|
||||
|
||||
fn get_secondary_color(&self, active: bool) -> [u8; 4] {
|
||||
self.0.secondary_color(active)
|
||||
}
|
||||
|
||||
fn get_close_button_color(&self, status: SCTKButtonState) -> [u8; 4] {
|
||||
self.0.close_button_color(ButtonState::from_sctk(status))
|
||||
}
|
||||
|
||||
fn get_close_button_icon_color(&self, status: SCTKButtonState) -> [u8; 4] {
|
||||
self.0
|
||||
.close_button_icon_color(ButtonState::from_sctk(status))
|
||||
}
|
||||
|
||||
fn get_maximize_button_color(&self, status: SCTKButtonState) -> [u8; 4] {
|
||||
self.0.maximize_button_color(ButtonState::from_sctk(status))
|
||||
}
|
||||
|
||||
fn get_maximize_button_icon_color(&self, status: SCTKButtonState) -> [u8; 4] {
|
||||
self.0
|
||||
.maximize_button_icon_color(ButtonState::from_sctk(status))
|
||||
}
|
||||
|
||||
fn get_minimize_button_color(&self, status: SCTKButtonState) -> [u8; 4] {
|
||||
self.0.minimize_button_color(ButtonState::from_sctk(status))
|
||||
}
|
||||
|
||||
fn get_minimize_button_icon_color(&self, status: SCTKButtonState) -> [u8; 4] {
|
||||
self.0
|
||||
.minimize_button_icon_color(ButtonState::from_sctk(status))
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ButtonState {
|
||||
/// Button is being hovered over by pointer.
|
||||
Hovered,
|
||||
/// Button is not being hovered over by pointer.
|
||||
Idle,
|
||||
/// Button is disabled.
|
||||
Disabled,
|
||||
}
|
||||
|
||||
impl ButtonState {
|
||||
fn from_sctk(button_state: SCTKButtonState) -> Self {
|
||||
match button_state {
|
||||
SCTKButtonState::Hovered => Self::Hovered,
|
||||
SCTKButtonState::Idle => Self::Idle,
|
||||
SCTKButtonState::Disabled => Self::Disabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
146
src/platform/wayland.rs
Normal file
146
src/platform/wayland.rs
Normal file
@@ -0,0 +1,146 @@
|
||||
use std::os::raw;
|
||||
|
||||
use sctk::reexports::client::Proxy;
|
||||
|
||||
use crate::{
|
||||
event_loop::{EventLoopBuilder, EventLoopWindowTarget},
|
||||
monitor::MonitorHandle,
|
||||
window::{Window, WindowBuilder},
|
||||
};
|
||||
|
||||
use crate::platform_impl::{
|
||||
ApplicationName, Backend, EventLoopWindowTarget as LinuxEventLoopWindowTarget,
|
||||
Window as LinuxWindow,
|
||||
};
|
||||
|
||||
pub use crate::window::Theme;
|
||||
|
||||
/// Additional methods on [`EventLoopWindowTarget`] that are specific to Wayland.
|
||||
pub trait EventLoopWindowTargetExtWayland {
|
||||
/// True if the [`EventLoopWindowTarget`] uses Wayland.
|
||||
fn is_wayland(&self) -> bool;
|
||||
|
||||
/// Returns a pointer to the `wl_display` object of wayland that is used by this
|
||||
/// [`EventLoopWindowTarget`].
|
||||
///
|
||||
/// Returns `None` if the [`EventLoop`] doesn't use wayland (if it uses xlib for example).
|
||||
///
|
||||
/// The pointer will become invalid when the winit [`EventLoop`] is destroyed.
|
||||
///
|
||||
/// [`EventLoop`]: crate::event_loop::EventLoop
|
||||
fn wayland_display(&self) -> Option<*mut raw::c_void>;
|
||||
}
|
||||
|
||||
impl<T> EventLoopWindowTargetExtWayland for EventLoopWindowTarget<T> {
|
||||
#[inline]
|
||||
fn is_wayland(&self) -> bool {
|
||||
self.p.is_wayland()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn wayland_display(&self) -> Option<*mut raw::c_void> {
|
||||
match self.p {
|
||||
LinuxEventLoopWindowTarget::Wayland(ref p) => {
|
||||
Some(p.connection.display().id().as_ptr() as *mut _)
|
||||
}
|
||||
#[cfg(x11_platform)]
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`EventLoopBuilder`] that are specific to Wayland.
|
||||
pub trait EventLoopBuilderExtWayland {
|
||||
/// Force using Wayland.
|
||||
fn with_wayland(&mut self) -> &mut Self;
|
||||
|
||||
/// Whether to allow the event loop to be created off of the main thread.
|
||||
///
|
||||
/// By default, the window is only allowed to be created on the main
|
||||
/// thread, to make platform compatibility easier.
|
||||
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self;
|
||||
}
|
||||
|
||||
impl<T> EventLoopBuilderExtWayland for EventLoopBuilder<T> {
|
||||
#[inline]
|
||||
fn with_wayland(&mut self) -> &mut Self {
|
||||
self.platform_specific.forced_backend = Some(Backend::Wayland);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self {
|
||||
self.platform_specific.any_thread = any_thread;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`Window`] that are specific to Wayland.
|
||||
pub trait WindowExtWayland {
|
||||
/// Returns a pointer to the `wl_surface` object of wayland that is used by this window.
|
||||
///
|
||||
/// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
|
||||
///
|
||||
/// The pointer will become invalid when the [`Window`] is destroyed.
|
||||
fn wayland_surface(&self) -> Option<*mut raw::c_void>;
|
||||
|
||||
/// Returns a pointer to the `wl_display` object of wayland that is used by this window.
|
||||
///
|
||||
/// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
|
||||
///
|
||||
/// The pointer will become invalid when the [`Window`] is destroyed.
|
||||
fn wayland_display(&self) -> Option<*mut raw::c_void>;
|
||||
}
|
||||
|
||||
impl WindowExtWayland for Window {
|
||||
#[inline]
|
||||
fn wayland_surface(&self) -> Option<*mut raw::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::Wayland(ref w) => Some(w.surface().id().as_ptr() as *mut _),
|
||||
#[cfg(x11_platform)]
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn wayland_display(&self) -> Option<*mut raw::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::Wayland(ref w) => Some(w.display().id().as_ptr() as *mut _),
|
||||
#[cfg(x11_platform)]
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`WindowBuilder`] that are specific to Wayland.
|
||||
pub trait WindowBuilderExtWayland {
|
||||
/// Build window with the given name.
|
||||
///
|
||||
/// The `general` name sets an application ID, which should match the `.desktop`
|
||||
/// file destributed with your program. The `instance` is a `no-op`.
|
||||
///
|
||||
/// For details about application ID conventions, see the
|
||||
/// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)
|
||||
fn with_name(self, general: impl Into<String>, instance: impl Into<String>) -> Self;
|
||||
}
|
||||
|
||||
impl WindowBuilderExtWayland for WindowBuilder {
|
||||
#[inline]
|
||||
fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self {
|
||||
self.platform_specific.name = Some(ApplicationName::new(general.into(), instance.into()));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `MonitorHandle` that are specific to Wayland.
|
||||
pub trait MonitorHandleExtWayland {
|
||||
/// Returns the inner identifier of the monitor.
|
||||
fn native_id(&self) -> u32;
|
||||
}
|
||||
|
||||
impl MonitorHandleExtWayland for MonitorHandle {
|
||||
#[inline]
|
||||
fn native_id(&self) -> u32 {
|
||||
self.inner.native_identifier()
|
||||
}
|
||||
}
|
||||
@@ -1,59 +1,98 @@
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
|
||||
//! The web target does not automatically insert the canvas element object into the web page, to
|
||||
//! allow end users to determine how the page should be laid out. Use the `WindowExtStdweb` or
|
||||
//! `WindowExtWebSys` traits (depending on your web backend) to retrieve the canvas from the
|
||||
//! Window. Alternatively, use the `WindowBuilderExtStdweb` or `WindowBuilderExtWebSys` to provide
|
||||
//! your own canvas.
|
||||
//! allow end users to determine how the page should be laid out. Use the [`WindowExtWebSys`] trait
|
||||
//! to retrieve the canvas from the Window. Alternatively, use the [`WindowBuilderExtWebSys`] trait
|
||||
//! to provide your own canvas.
|
||||
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::ControlFlow;
|
||||
use crate::event_loop::EventLoop;
|
||||
use crate::event_loop::EventLoopWindowTarget;
|
||||
use crate::window::WindowBuilder;
|
||||
|
||||
#[cfg(feature = "stdweb")]
|
||||
use stdweb::web::html_element::CanvasElement;
|
||||
|
||||
#[cfg(feature = "stdweb")]
|
||||
pub trait WindowExtStdweb {
|
||||
fn canvas(&self) -> CanvasElement;
|
||||
|
||||
/// Whether the browser reports the preferred color scheme to be "dark".
|
||||
fn is_dark_mode(&self) -> bool;
|
||||
}
|
||||
|
||||
#[cfg(feature = "web-sys")]
|
||||
use web_sys::HtmlCanvasElement;
|
||||
|
||||
#[cfg(feature = "web-sys")]
|
||||
pub trait WindowExtWebSys {
|
||||
fn canvas(&self) -> HtmlCanvasElement;
|
||||
/// Only returns the canvas if called from inside the window.
|
||||
fn canvas(&self) -> Option<HtmlCanvasElement>;
|
||||
|
||||
/// Whether the browser reports the preferred color scheme to be "dark".
|
||||
fn is_dark_mode(&self) -> bool;
|
||||
}
|
||||
|
||||
#[cfg(feature = "stdweb")]
|
||||
pub trait WindowBuilderExtStdweb {
|
||||
fn with_canvas(self, canvas: Option<CanvasElement>) -> Self;
|
||||
}
|
||||
|
||||
#[cfg(feature = "stdweb")]
|
||||
impl WindowBuilderExtStdweb for WindowBuilder {
|
||||
fn with_canvas(mut self, canvas: Option<CanvasElement>) -> Self {
|
||||
self.platform_specific.canvas = canvas;
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "web-sys")]
|
||||
pub trait WindowBuilderExtWebSys {
|
||||
fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self;
|
||||
|
||||
/// Whether `event.preventDefault` should be automatically called to prevent event propagation
|
||||
/// when appropriate.
|
||||
///
|
||||
/// For example, mouse wheel events are only handled by the canvas by default. This avoids
|
||||
/// the default behavior of scrolling the page.
|
||||
///
|
||||
/// Some events are impossible to prevent. E.g. Firefox allows to access the native browser
|
||||
/// context menu with Shift+Rightclick.
|
||||
fn with_prevent_default(self, prevent_default: bool) -> Self;
|
||||
|
||||
/// Whether the canvas should be focusable using the tab key. This is necessary to capture
|
||||
/// canvas keyboard events.
|
||||
fn with_focusable(self, focusable: bool) -> Self;
|
||||
}
|
||||
|
||||
#[cfg(feature = "web-sys")]
|
||||
impl WindowBuilderExtWebSys for WindowBuilder {
|
||||
fn with_canvas(mut self, canvas: Option<HtmlCanvasElement>) -> Self {
|
||||
self.platform_specific.canvas = canvas;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn with_prevent_default(mut self, prevent_default: bool) -> Self {
|
||||
self.platform_specific.prevent_default = prevent_default;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn with_focusable(mut self, focusable: bool) -> Self {
|
||||
self.platform_specific.focusable = focusable;
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `EventLoop` that are specific to the web.
|
||||
pub trait EventLoopExtWebSys {
|
||||
/// A type provided by the user that can be passed through `Event::UserEvent`.
|
||||
type UserEvent;
|
||||
|
||||
/// Initializes the winit event loop.
|
||||
///
|
||||
/// Unlike `run`, this returns immediately, and doesn't throw an exception in order to
|
||||
/// satisfy its `!` return type.
|
||||
///
|
||||
/// Once the event loop has been destroyed, it's possible to reinitialize another event loop
|
||||
/// by calling this function again. This can be useful if you want to recreate the event loop
|
||||
/// while the WebAssembly module is still loaded. For example, this can be used to recreate the
|
||||
/// event loop when switching between tabs on a single page application.
|
||||
fn spawn<F>(self, event_handler: F)
|
||||
where
|
||||
F: 'static
|
||||
+ FnMut(
|
||||
Event<'_, Self::UserEvent>,
|
||||
&EventLoopWindowTarget<Self::UserEvent>,
|
||||
&mut ControlFlow,
|
||||
);
|
||||
}
|
||||
|
||||
impl<T> EventLoopExtWebSys for EventLoop<T> {
|
||||
type UserEvent = T;
|
||||
|
||||
fn spawn<F>(self, event_handler: F)
|
||||
where
|
||||
F: 'static
|
||||
+ FnMut(
|
||||
Event<'_, Self::UserEvent>,
|
||||
&EventLoopWindowTarget<Self::UserEvent>,
|
||||
&mut ControlFlow,
|
||||
),
|
||||
{
|
||||
self.event_loop.spawn(event_handler)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,99 +1,166 @@
|
||||
#![cfg(target_os = "windows")]
|
||||
|
||||
use std::os::raw::c_void;
|
||||
use std::path::Path;
|
||||
|
||||
use libc;
|
||||
use winapi::shared::minwindef::WORD;
|
||||
use winapi::shared::windef::HWND;
|
||||
use std::{ffi::c_void, path::Path};
|
||||
|
||||
use crate::{
|
||||
dpi::PhysicalSize,
|
||||
event::DeviceId,
|
||||
event_loop::EventLoop,
|
||||
event::{DeviceId, KeyEvent},
|
||||
event_loop::EventLoopBuilder,
|
||||
keyboard::Key,
|
||||
monitor::MonitorHandle,
|
||||
platform_impl::{EventLoop as WindowsEventLoop, WinIcon},
|
||||
platform::modifier_supplement::KeyEventExtModifierSupplement,
|
||||
platform_impl::WinIcon,
|
||||
window::{BadIcon, Icon, Window, WindowBuilder},
|
||||
};
|
||||
|
||||
/// Window Handle type used by Win32 API
|
||||
pub type HWND = isize;
|
||||
/// Menu Handle type used by Win32 API
|
||||
pub type HMENU = isize;
|
||||
/// Monitor Handle type used by Win32 API
|
||||
pub type HMONITOR = isize;
|
||||
/// Instance Handle type used by Win32 API
|
||||
pub type HINSTANCE = isize;
|
||||
|
||||
/// Additional methods on `EventLoop` that are specific to Windows.
|
||||
pub trait EventLoopExtWindows {
|
||||
/// Creates an event loop off of the main thread.
|
||||
pub trait EventLoopBuilderExtWindows {
|
||||
/// Whether to allow the event loop to be created off of the main thread.
|
||||
///
|
||||
/// By default, the window is only allowed to be created on the main
|
||||
/// thread, to make platform compatibility easier.
|
||||
///
|
||||
/// # `Window` caveats
|
||||
///
|
||||
/// Note that any `Window` created on the new thread will be destroyed when the thread
|
||||
/// terminates. Attempting to use a `Window` after its parent thread terminates has
|
||||
/// unspecified, although explicitly not undefined, behavior.
|
||||
fn new_any_thread() -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self;
|
||||
|
||||
/// By default, winit on Windows will attempt to enable process-wide DPI awareness. If that's
|
||||
/// undesirable, you can create an `EventLoop` using this function instead.
|
||||
fn new_dpi_unaware() -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
/// Creates a DPI-unaware event loop off of the main thread.
|
||||
/// Whether to enable process-wide DPI awareness.
|
||||
///
|
||||
/// The `Window` caveats in [`new_any_thread`](EventLoopExtWindows::new_any_thread) also apply here.
|
||||
fn new_dpi_unaware_any_thread() -> Self
|
||||
/// By default, `winit` will attempt to enable process-wide DPI awareness. If
|
||||
/// that's undesirable, you can disable it with this function.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Disable process-wide DPI awareness.
|
||||
///
|
||||
/// ```
|
||||
/// use winit::event_loop::EventLoopBuilder;
|
||||
/// #[cfg(target_os = "windows")]
|
||||
/// use winit::platform::windows::EventLoopBuilderExtWindows;
|
||||
///
|
||||
/// let mut builder = EventLoopBuilder::new();
|
||||
/// #[cfg(target_os = "windows")]
|
||||
/// builder.with_dpi_aware(false);
|
||||
/// # if false { // We can't test this part
|
||||
/// let event_loop = builder.build();
|
||||
/// # }
|
||||
/// ```
|
||||
fn with_dpi_aware(&mut self, dpi_aware: bool) -> &mut Self;
|
||||
|
||||
/// A callback to be executed before dispatching a win32 message to the window procedure.
|
||||
/// Return true to disable winit's internal message dispatching.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use windows_sys::Win32::UI::WindowsAndMessaging::{ACCEL, CreateAcceleratorTableW, TranslateAcceleratorW, DispatchMessageW, TranslateMessage, MSG};
|
||||
/// use winit::event_loop::EventLoopBuilder;
|
||||
/// #[cfg(target_os = "windows")]
|
||||
/// use winit::platform::windows::EventLoopBuilderExtWindows;
|
||||
///
|
||||
/// let mut builder = EventLoopBuilder::new();
|
||||
/// #[cfg(target_os = "windows")]
|
||||
/// builder.with_msg_hook(|msg|{
|
||||
/// let msg = msg as *const MSG;
|
||||
/// # let accels: Vec<ACCEL> = Vec::new();
|
||||
/// let translated = unsafe {
|
||||
/// TranslateAcceleratorW(
|
||||
/// (*msg).hwnd,
|
||||
/// CreateAcceleratorTableW(accels.as_ptr() as _, 1),
|
||||
/// msg,
|
||||
/// ) == 1
|
||||
/// };
|
||||
/// translated
|
||||
/// });
|
||||
/// ```
|
||||
fn with_msg_hook<F>(&mut self, callback: F) -> &mut Self
|
||||
where
|
||||
Self: Sized;
|
||||
F: FnMut(*const c_void) -> bool + 'static;
|
||||
}
|
||||
|
||||
impl<T> EventLoopExtWindows for EventLoop<T> {
|
||||
impl<T> EventLoopBuilderExtWindows for EventLoopBuilder<T> {
|
||||
#[inline]
|
||||
fn new_any_thread() -> Self {
|
||||
EventLoop {
|
||||
event_loop: WindowsEventLoop::new_any_thread(),
|
||||
_marker: ::std::marker::PhantomData,
|
||||
}
|
||||
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self {
|
||||
self.platform_specific.any_thread = any_thread;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_dpi_unaware() -> Self {
|
||||
EventLoop {
|
||||
event_loop: WindowsEventLoop::new_dpi_unaware(),
|
||||
_marker: ::std::marker::PhantomData,
|
||||
}
|
||||
fn with_dpi_aware(&mut self, dpi_aware: bool) -> &mut Self {
|
||||
self.platform_specific.dpi_aware = dpi_aware;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_dpi_unaware_any_thread() -> Self {
|
||||
EventLoop {
|
||||
event_loop: WindowsEventLoop::new_dpi_unaware_any_thread(),
|
||||
_marker: ::std::marker::PhantomData,
|
||||
}
|
||||
fn with_msg_hook<F>(&mut self, callback: F) -> &mut Self
|
||||
where
|
||||
F: FnMut(*const c_void) -> bool + 'static,
|
||||
{
|
||||
self.platform_specific.msg_hook = Some(Box::new(callback));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `Window` that are specific to Windows.
|
||||
pub trait WindowExtWindows {
|
||||
/// Returns the HINSTANCE of the window
|
||||
fn hinstance(&self) -> *mut libc::c_void;
|
||||
fn hinstance(&self) -> HINSTANCE;
|
||||
/// Returns the native handle that is used by this window.
|
||||
///
|
||||
/// The pointer will become invalid when the native window was destroyed.
|
||||
fn hwnd(&self) -> *mut libc::c_void;
|
||||
fn hwnd(&self) -> HWND;
|
||||
|
||||
/// Enables or disables mouse and keyboard input to the specified window.
|
||||
///
|
||||
/// A window must be enabled before it can be activated.
|
||||
/// If an application has create a modal dialog box by disabling its owner window
|
||||
/// (as described in [`WindowBuilderExtWindows::with_owner_window`]), the application must enable
|
||||
/// the owner window before destroying the dialog box.
|
||||
/// Otherwise, another window will receive the keyboard focus and be activated.
|
||||
///
|
||||
/// If a child window is disabled, it is ignored when the system tries to determine which
|
||||
/// window should receive mouse messages.
|
||||
///
|
||||
/// For more information, see <https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enablewindow#remarks>
|
||||
/// and <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#disabled-windows>
|
||||
fn set_enable(&self, enabled: bool);
|
||||
|
||||
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
|
||||
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>);
|
||||
|
||||
/// Whether the system theme is currently Windows 10's "Dark Mode".
|
||||
fn is_dark_mode(&self) -> bool;
|
||||
/// Whether to show or hide the window icon in the taskbar.
|
||||
fn set_skip_taskbar(&self, skip: bool);
|
||||
|
||||
/// Shows or hides the background drop shadow for undecorated windows.
|
||||
///
|
||||
/// Enabling the shadow causes a thin 1px line to appear on the top of the window.
|
||||
fn set_undecorated_shadow(&self, shadow: bool);
|
||||
}
|
||||
|
||||
impl WindowExtWindows for Window {
|
||||
#[inline]
|
||||
fn hinstance(&self) -> *mut libc::c_void {
|
||||
self.window.hinstance() as *mut _
|
||||
fn hinstance(&self) -> HINSTANCE {
|
||||
self.window.hinstance()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hwnd(&self) -> *mut libc::c_void {
|
||||
self.window.hwnd() as *mut _
|
||||
fn hwnd(&self) -> HWND {
|
||||
self.window.hwnd()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_enable(&self, enabled: bool) {
|
||||
self.window.set_enable(enabled)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -102,27 +169,77 @@ impl WindowExtWindows for Window {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_dark_mode(&self) -> bool {
|
||||
self.window.is_dark_mode()
|
||||
fn set_skip_taskbar(&self, skip: bool) {
|
||||
self.window.set_skip_taskbar(skip)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_undecorated_shadow(&self, shadow: bool) {
|
||||
self.window.set_undecorated_shadow(shadow)
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `WindowBuilder` that are specific to Windows.
|
||||
pub trait WindowBuilderExtWindows {
|
||||
/// Sets a parent to the window to be created.
|
||||
fn with_parent_window(self, parent: HWND) -> WindowBuilder;
|
||||
/// Set an owner to the window to be created. Can be used to create a dialog box, for example.
|
||||
/// This only works when [`WindowBuilder::with_parent_window`] isn't called or set to `None`.
|
||||
/// Can be used in combination with [`WindowExtWindows::set_enable(false)`](WindowExtWindows::set_enable)
|
||||
/// on the owner window to create a modal dialog box.
|
||||
///
|
||||
/// From MSDN:
|
||||
/// - An owned window is always above its owner in the z-order.
|
||||
/// - The system automatically destroys an owned window when its owner is destroyed.
|
||||
/// - An owned window is hidden when its owner is minimized.
|
||||
///
|
||||
/// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#owned-windows>
|
||||
fn with_owner_window(self, parent: HWND) -> WindowBuilder;
|
||||
|
||||
/// Sets a menu on the window to be created.
|
||||
///
|
||||
/// Parent and menu are mutually exclusive; a child window cannot have a menu!
|
||||
///
|
||||
/// The menu must have been manually created beforehand with [`CreateMenu`] or similar.
|
||||
///
|
||||
/// Note: Dark mode cannot be supported for win32 menus, it's simply not possible to change how the menus look.
|
||||
/// If you use this, it is recommended that you combine it with `with_theme(Some(Theme::Light))` to avoid a jarring effect.
|
||||
///
|
||||
/// [`CreateMenu`]: windows_sys::Win32::UI::WindowsAndMessaging::CreateMenu
|
||||
fn with_menu(self, menu: HMENU) -> WindowBuilder;
|
||||
|
||||
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
|
||||
fn with_taskbar_icon(self, taskbar_icon: Option<Icon>) -> WindowBuilder;
|
||||
|
||||
/// This sets `WS_EX_NOREDIRECTIONBITMAP`.
|
||||
fn with_no_redirection_bitmap(self, flag: bool) -> WindowBuilder;
|
||||
|
||||
/// Enables or disables drag and drop support (enabled by default). Will interfere with other crates
|
||||
/// that use multi-threaded COM API (`CoInitializeEx` with `COINIT_MULTITHREADED` instead of
|
||||
/// `COINIT_APARTMENTTHREADED`) on the same thread. Note that winit may still attempt to initialize
|
||||
/// COM API regardless of this option. Currently only fullscreen mode does that, but there may be more in the future.
|
||||
/// If you need COM API with `COINIT_MULTITHREADED` you must initialize it before calling any winit functions.
|
||||
/// See <https://docs.microsoft.com/en-us/windows/win32/api/objbase/nf-objbase-coinitialize#remarks> for more information.
|
||||
fn with_drag_and_drop(self, flag: bool) -> WindowBuilder;
|
||||
|
||||
/// Whether show or hide the window icon in the taskbar.
|
||||
fn with_skip_taskbar(self, skip: bool) -> WindowBuilder;
|
||||
|
||||
/// Shows or hides the background drop shadow for undecorated windows.
|
||||
///
|
||||
/// The shadow is hidden by default.
|
||||
/// Enabling the shadow causes a thin 1px line to appear on the top of the window.
|
||||
fn with_undecorated_shadow(self, shadow: bool) -> WindowBuilder;
|
||||
}
|
||||
|
||||
impl WindowBuilderExtWindows for WindowBuilder {
|
||||
#[inline]
|
||||
fn with_parent_window(mut self, parent: HWND) -> WindowBuilder {
|
||||
self.platform_specific.parent = Some(parent);
|
||||
fn with_owner_window(mut self, parent: HWND) -> WindowBuilder {
|
||||
self.platform_specific.owner = Some(parent);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_menu(mut self, menu: HMENU) -> WindowBuilder {
|
||||
self.platform_specific.menu = Some(menu);
|
||||
self
|
||||
}
|
||||
|
||||
@@ -137,6 +254,24 @@ impl WindowBuilderExtWindows for WindowBuilder {
|
||||
self.platform_specific.no_redirection_bitmap = flag;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_drag_and_drop(mut self, flag: bool) -> WindowBuilder {
|
||||
self.platform_specific.drag_and_drop = flag;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_skip_taskbar(mut self, skip: bool) -> WindowBuilder {
|
||||
self.platform_specific.skip_taskbar = skip;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_undecorated_shadow(mut self, shadow: bool) -> WindowBuilder {
|
||||
self.platform_specific.decoration_shadow = shadow;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `MonitorHandle` that are specific to Windows.
|
||||
@@ -145,7 +280,7 @@ pub trait MonitorHandleExtWindows {
|
||||
fn native_id(&self) -> String;
|
||||
|
||||
/// Returns the handle of the monitor - `HMONITOR`.
|
||||
fn hmonitor(&self) -> *mut c_void;
|
||||
fn hmonitor(&self) -> HMONITOR;
|
||||
}
|
||||
|
||||
impl MonitorHandleExtWindows for MonitorHandle {
|
||||
@@ -155,8 +290,8 @@ impl MonitorHandleExtWindows for MonitorHandle {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hmonitor(&self) -> *mut c_void {
|
||||
self.inner.hmonitor() as *mut _
|
||||
fn hmonitor(&self) -> HMONITOR {
|
||||
self.inner.hmonitor()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,7 +329,7 @@ pub trait IconExtWindows: Sized {
|
||||
///
|
||||
/// In cases where the specified size does not exist in the file, Windows may perform scaling
|
||||
/// to get an icon of the desired size.
|
||||
fn from_resource(ordinal: WORD, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon>;
|
||||
fn from_resource(ordinal: u16, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon>;
|
||||
}
|
||||
|
||||
impl IconExtWindows for Icon {
|
||||
@@ -206,8 +341,23 @@ impl IconExtWindows for Icon {
|
||||
Ok(Icon { inner: win_icon })
|
||||
}
|
||||
|
||||
fn from_resource(ordinal: WORD, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon> {
|
||||
fn from_resource(ordinal: u16, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon> {
|
||||
let win_icon = WinIcon::from_resource(ordinal, size)?;
|
||||
Ok(Icon { inner: win_icon })
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyEventExtModifierSupplement for KeyEvent {
|
||||
#[inline]
|
||||
fn text_with_all_modifiers(&self) -> Option<&str> {
|
||||
self.platform_specific
|
||||
.text_with_all_modifers
|
||||
.as_ref()
|
||||
.map(|s| s.as_str())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn key_without_modifiers(&self) -> Key {
|
||||
self.platform_specific.key_without_modifiers.clone()
|
||||
}
|
||||
}
|
||||
|
||||
233
src/platform/x11.rs
Normal file
233
src/platform/x11.rs
Normal file
@@ -0,0 +1,233 @@
|
||||
use std::os::raw;
|
||||
use std::ptr;
|
||||
|
||||
use crate::{
|
||||
event_loop::{EventLoopBuilder, EventLoopWindowTarget},
|
||||
monitor::MonitorHandle,
|
||||
window::{Window, WindowBuilder},
|
||||
};
|
||||
|
||||
use crate::dpi::Size;
|
||||
use crate::platform_impl::{
|
||||
x11::ffi::XVisualInfo, ApplicationName, Backend, Window as LinuxWindow, XLIB_ERROR_HOOKS,
|
||||
};
|
||||
|
||||
pub use crate::platform_impl::{x11::util::WindowType as XWindowType, XNotSupported};
|
||||
|
||||
/// The first argument in the provided hook will be the pointer to `XDisplay`
|
||||
/// and the second one the pointer to [`XErrorEvent`]. The returned `bool` is an
|
||||
/// indicator whether the error was handled by the callback.
|
||||
///
|
||||
/// [`XErrorEvent`]: https://linux.die.net/man/3/xerrorevent
|
||||
pub type XlibErrorHook =
|
||||
Box<dyn Fn(*mut std::ffi::c_void, *mut std::ffi::c_void) -> bool + Send + Sync>;
|
||||
|
||||
/// Hook to winit's xlib error handling callback.
|
||||
///
|
||||
/// This method is provided as a safe way to handle the errors comming from X11
|
||||
/// when using xlib in external crates, like glutin for GLX access. Trying to
|
||||
/// handle errors by speculating with `XSetErrorHandler` is [`unsafe`].
|
||||
///
|
||||
/// **Be aware that your hook is always invoked and returning `true` from it will
|
||||
/// prevent `winit` from getting the error itself. It's wise to always return
|
||||
/// `false` if you're not initiated the `Sync`.**
|
||||
///
|
||||
/// [`unsafe`]: https://www.remlab.net/op/xlib.shtml
|
||||
#[inline]
|
||||
pub fn register_xlib_error_hook(hook: XlibErrorHook) {
|
||||
// Append new hook.
|
||||
unsafe {
|
||||
XLIB_ERROR_HOOKS.lock().unwrap().push(hook);
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`EventLoopWindowTarget`] that are specific to X11.
|
||||
pub trait EventLoopWindowTargetExtX11 {
|
||||
/// True if the [`EventLoopWindowTarget`] uses X11.
|
||||
fn is_x11(&self) -> bool;
|
||||
}
|
||||
|
||||
impl<T> EventLoopWindowTargetExtX11 for EventLoopWindowTarget<T> {
|
||||
#[inline]
|
||||
fn is_x11(&self) -> bool {
|
||||
!self.p.is_wayland()
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`EventLoopBuilder`] that are specific to X11.
|
||||
pub trait EventLoopBuilderExtX11 {
|
||||
/// Force using X11.
|
||||
fn with_x11(&mut self) -> &mut Self;
|
||||
|
||||
/// Whether to allow the event loop to be created off of the main thread.
|
||||
///
|
||||
/// By default, the window is only allowed to be created on the main
|
||||
/// thread, to make platform compatibility easier.
|
||||
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self;
|
||||
}
|
||||
|
||||
impl<T> EventLoopBuilderExtX11 for EventLoopBuilder<T> {
|
||||
#[inline]
|
||||
fn with_x11(&mut self) -> &mut Self {
|
||||
self.platform_specific.forced_backend = Some(Backend::X);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_any_thread(&mut self, any_thread: bool) -> &mut Self {
|
||||
self.platform_specific.any_thread = any_thread;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`Window`] that are specific to X11.
|
||||
pub trait WindowExtX11 {
|
||||
/// Returns the ID of the [`Window`] xlib object that is used by this window.
|
||||
///
|
||||
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
|
||||
fn xlib_window(&self) -> Option<raw::c_ulong>;
|
||||
|
||||
/// Returns a pointer to the `Display` object of xlib that is used by this window.
|
||||
///
|
||||
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
|
||||
///
|
||||
/// The pointer will become invalid when the [`Window`] is destroyed.
|
||||
fn xlib_display(&self) -> Option<*mut raw::c_void>;
|
||||
|
||||
fn xlib_screen_id(&self) -> Option<raw::c_int>;
|
||||
|
||||
/// This function returns the underlying `xcb_connection_t` of an xlib `Display`.
|
||||
///
|
||||
/// Returns `None` if the window doesn't use xlib (if it uses wayland for example).
|
||||
///
|
||||
/// The pointer will become invalid when the [`Window`] is destroyed.
|
||||
fn xcb_connection(&self) -> Option<*mut raw::c_void>;
|
||||
}
|
||||
|
||||
impl WindowExtX11 for Window {
|
||||
#[inline]
|
||||
fn xlib_window(&self) -> Option<raw::c_ulong> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.xlib_window()),
|
||||
#[cfg(wayland_platform)]
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn xlib_display(&self) -> Option<*mut raw::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.xlib_display()),
|
||||
#[cfg(wayland_platform)]
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn xlib_screen_id(&self) -> Option<raw::c_int> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.xlib_screen_id()),
|
||||
#[cfg(wayland_platform)]
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn xcb_connection(&self) -> Option<*mut raw::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.xcb_connection()),
|
||||
#[cfg(wayland_platform)]
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`WindowBuilder`] that are specific to X11.
|
||||
pub trait WindowBuilderExtX11 {
|
||||
fn with_x11_visual<T>(self, visual_infos: *const T) -> Self;
|
||||
|
||||
fn with_x11_screen(self, screen_id: i32) -> Self;
|
||||
|
||||
/// Build window with the given `general` and `instance` names.
|
||||
///
|
||||
/// The `general` sets general class of `WM_CLASS(STRING)`, while `instance` set the
|
||||
/// instance part of it. The resulted property looks like `WM_CLASS(STRING) = "general", "instance"`.
|
||||
///
|
||||
/// For details about application ID conventions, see the
|
||||
/// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id)
|
||||
fn with_name(self, general: impl Into<String>, instance: impl Into<String>) -> Self;
|
||||
|
||||
/// Build window with override-redirect flag; defaults to false. Only relevant on X11.
|
||||
fn with_override_redirect(self, override_redirect: bool) -> Self;
|
||||
|
||||
/// Build window with `_NET_WM_WINDOW_TYPE` hints; defaults to `Normal`. Only relevant on X11.
|
||||
fn with_x11_window_type(self, x11_window_type: Vec<XWindowType>) -> Self;
|
||||
|
||||
/// Build window with base size hint. Only implemented on X11.
|
||||
///
|
||||
/// ```
|
||||
/// # use winit::dpi::{LogicalSize, PhysicalSize};
|
||||
/// # use winit::window::WindowBuilder;
|
||||
/// # use winit::platform::x11::WindowBuilderExtX11;
|
||||
/// // Specify the size in logical dimensions like this:
|
||||
/// WindowBuilder::new().with_base_size(LogicalSize::new(400.0, 200.0));
|
||||
///
|
||||
/// // Or specify the size in physical dimensions like this:
|
||||
/// WindowBuilder::new().with_base_size(PhysicalSize::new(400, 200));
|
||||
/// ```
|
||||
fn with_base_size<S: Into<Size>>(self, base_size: S) -> Self;
|
||||
}
|
||||
|
||||
impl WindowBuilderExtX11 for WindowBuilder {
|
||||
#[inline]
|
||||
fn with_x11_visual<T>(mut self, visual_infos: *const T) -> Self {
|
||||
{
|
||||
self.platform_specific.visual_infos =
|
||||
Some(unsafe { ptr::read(visual_infos as *const XVisualInfo) });
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_x11_screen(mut self, screen_id: i32) -> Self {
|
||||
self.platform_specific.screen_id = Some(screen_id);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_name(mut self, general: impl Into<String>, instance: impl Into<String>) -> Self {
|
||||
self.platform_specific.name = Some(ApplicationName::new(general.into(), instance.into()));
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_override_redirect(mut self, override_redirect: bool) -> Self {
|
||||
self.platform_specific.override_redirect = override_redirect;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_x11_window_type(mut self, x11_window_types: Vec<XWindowType>) -> Self {
|
||||
self.platform_specific.x11_window_types = x11_window_types;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_base_size<S: Into<Size>>(mut self, base_size: S) -> Self {
|
||||
self.platform_specific.base_size = Some(base_size.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `MonitorHandle` that are specific to X11.
|
||||
pub trait MonitorHandleExtX11 {
|
||||
/// Returns the inner identifier of the monitor.
|
||||
fn native_id(&self) -> u32;
|
||||
}
|
||||
|
||||
impl MonitorHandleExtX11 for MonitorHandle {
|
||||
#[inline]
|
||||
fn native_id(&self) -> u32 {
|
||||
self.inner.native_identifier()
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
#![allow(dead_code)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
use libc;
|
||||
use std::os::raw;
|
||||
|
||||
#[link(name = "android")]
|
||||
#[link(name = "EGL")]
|
||||
#[link(name = "GLESv2")]
|
||||
extern "C" {}
|
||||
|
||||
/**
|
||||
** asset_manager.h
|
||||
**/
|
||||
pub type AAssetManager = raw::c_void;
|
||||
|
||||
/**
|
||||
** native_window.h
|
||||
**/
|
||||
pub type ANativeWindow = raw::c_void;
|
||||
|
||||
extern "C" {
|
||||
pub fn ANativeWindow_getHeight(window: *const ANativeWindow) -> libc::int32_t;
|
||||
pub fn ANativeWindow_getWidth(window: *const ANativeWindow) -> libc::int32_t;
|
||||
}
|
||||
|
||||
/**
|
||||
** native_activity.h
|
||||
**/
|
||||
pub type JavaVM = ();
|
||||
pub type JNIEnv = ();
|
||||
pub type jobject = *const libc::c_void;
|
||||
|
||||
pub type AInputQueue = (); // FIXME: wrong
|
||||
pub type ARect = (); // FIXME: wrong
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ANativeActivity {
|
||||
pub callbacks: *mut ANativeActivityCallbacks,
|
||||
pub vm: *mut JavaVM,
|
||||
pub env: *mut JNIEnv,
|
||||
pub clazz: jobject,
|
||||
pub internalDataPath: *const libc::c_char,
|
||||
pub externalDataPath: *const libc::c_char,
|
||||
pub sdkVersion: libc::int32_t,
|
||||
pub instance: *mut libc::c_void,
|
||||
pub assetManager: *mut AAssetManager,
|
||||
pub obbPath: *const libc::c_char,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ANativeActivityCallbacks {
|
||||
pub onStart: extern "C" fn(*mut ANativeActivity),
|
||||
pub onResume: extern "C" fn(*mut ANativeActivity),
|
||||
pub onSaveInstanceState: extern "C" fn(*mut ANativeActivity, *mut libc::size_t),
|
||||
pub onPause: extern "C" fn(*mut ANativeActivity),
|
||||
pub onStop: extern "C" fn(*mut ANativeActivity),
|
||||
pub onDestroy: extern "C" fn(*mut ANativeActivity),
|
||||
pub onWindowFocusChanged: extern "C" fn(*mut ANativeActivity, libc::c_int),
|
||||
pub onNativeWindowCreated: extern "C" fn(*mut ANativeActivity, *const ANativeWindow),
|
||||
pub onNativeWindowResized: extern "C" fn(*mut ANativeActivity, *const ANativeWindow),
|
||||
pub onNativeWindowRedrawNeeded: extern "C" fn(*mut ANativeActivity, *const ANativeWindow),
|
||||
pub onNativeWindowDestroyed: extern "C" fn(*mut ANativeActivity, *const ANativeWindow),
|
||||
pub onInputQueueCreated: extern "C" fn(*mut ANativeActivity, *mut AInputQueue),
|
||||
pub onInputQueueDestroyed: extern "C" fn(*mut ANativeActivity, *mut AInputQueue),
|
||||
pub onContentRectChanged: extern "C" fn(*mut ANativeActivity, *const ARect),
|
||||
pub onConfigurationChanged: extern "C" fn(*mut ANativeActivity),
|
||||
pub onLowMemory: extern "C" fn(*mut ANativeActivity),
|
||||
}
|
||||
|
||||
/**
|
||||
** looper.h
|
||||
**/
|
||||
pub type ALooper = ();
|
||||
|
||||
#[link(name = "android")]
|
||||
extern "C" {
|
||||
pub fn ALooper_forThread() -> *const ALooper;
|
||||
pub fn ALooper_acquire(looper: *const ALooper);
|
||||
pub fn ALooper_release(looper: *const ALooper);
|
||||
pub fn ALooper_prepare(opts: libc::c_int) -> *const ALooper;
|
||||
pub fn ALooper_pollOnce(
|
||||
timeoutMillis: libc::c_int,
|
||||
outFd: *mut libc::c_int,
|
||||
outEvents: *mut libc::c_int,
|
||||
outData: *mut *mut libc::c_void,
|
||||
) -> libc::c_int;
|
||||
pub fn ALooper_pollAll(
|
||||
timeoutMillis: libc::c_int,
|
||||
outFd: *mut libc::c_int,
|
||||
outEvents: *mut libc::c_int,
|
||||
outData: *mut *mut libc::c_void,
|
||||
) -> libc::c_int;
|
||||
pub fn ALooper_wake(looper: *const ALooper);
|
||||
pub fn ALooper_addFd(
|
||||
looper: *const ALooper,
|
||||
fd: libc::c_int,
|
||||
ident: libc::c_int,
|
||||
events: libc::c_int,
|
||||
callback: ALooper_callbackFunc,
|
||||
data: *mut libc::c_void,
|
||||
) -> libc::c_int;
|
||||
pub fn ALooper_removeFd(looper: *const ALooper, fd: libc::c_int) -> libc::c_int;
|
||||
}
|
||||
|
||||
pub const ALOOPER_PREPARE_ALLOW_NON_CALLBACKS: libc::c_int = 1 << 0;
|
||||
|
||||
pub const ALOOPER_POLL_WAKE: libc::c_int = -1;
|
||||
pub const ALOOPER_POLL_CALLBACK: libc::c_int = -2;
|
||||
pub const ALOOPER_POLL_TIMEOUT: libc::c_int = -3;
|
||||
pub const ALOOPER_POLL_ERROR: libc::c_int = -4;
|
||||
|
||||
pub const ALOOPER_EVENT_INPUT: libc::c_int = 1 << 0;
|
||||
pub const ALOOPER_EVENT_OUTPUT: libc::c_int = 1 << 1;
|
||||
pub const ALOOPER_EVENT_ERROR: libc::c_int = 1 << 2;
|
||||
pub const ALOOPER_EVENT_HANGUP: libc::c_int = 1 << 3;
|
||||
pub const ALOOPER_EVENT_INVALID: libc::c_int = 1 << 4;
|
||||
|
||||
pub type ALooper_callbackFunc =
|
||||
extern "C" fn(libc::c_int, libc::c_int, *mut libc::c_void) -> libc::c_int;
|
||||
521
src/platform_impl/android/keycodes.rs
Normal file
521
src/platform_impl/android/keycodes.rs
Normal file
@@ -0,0 +1,521 @@
|
||||
use android_activity::input::Keycode;
|
||||
|
||||
use crate::keyboard::{Key, KeyCode, KeyLocation, NativeKey, NativeKeyCode};
|
||||
|
||||
pub fn to_physical_keycode(keycode: Keycode) -> KeyCode {
|
||||
match keycode {
|
||||
Keycode::A => KeyCode::KeyA,
|
||||
Keycode::B => KeyCode::KeyB,
|
||||
Keycode::C => KeyCode::KeyC,
|
||||
Keycode::D => KeyCode::KeyD,
|
||||
Keycode::E => KeyCode::KeyE,
|
||||
Keycode::F => KeyCode::KeyF,
|
||||
Keycode::G => KeyCode::KeyG,
|
||||
Keycode::H => KeyCode::KeyH,
|
||||
Keycode::I => KeyCode::KeyI,
|
||||
Keycode::J => KeyCode::KeyJ,
|
||||
Keycode::K => KeyCode::KeyK,
|
||||
Keycode::L => KeyCode::KeyL,
|
||||
Keycode::M => KeyCode::KeyM,
|
||||
Keycode::N => KeyCode::KeyN,
|
||||
Keycode::O => KeyCode::KeyO,
|
||||
Keycode::P => KeyCode::KeyP,
|
||||
Keycode::Q => KeyCode::KeyQ,
|
||||
Keycode::R => KeyCode::KeyR,
|
||||
Keycode::S => KeyCode::KeyS,
|
||||
Keycode::T => KeyCode::KeyT,
|
||||
Keycode::U => KeyCode::KeyU,
|
||||
Keycode::V => KeyCode::KeyV,
|
||||
Keycode::W => KeyCode::KeyW,
|
||||
Keycode::X => KeyCode::KeyX,
|
||||
Keycode::Y => KeyCode::KeyY,
|
||||
Keycode::Z => KeyCode::KeyZ,
|
||||
|
||||
Keycode::Keycode0 => KeyCode::Digit0,
|
||||
Keycode::Keycode1 => KeyCode::Digit1,
|
||||
Keycode::Keycode2 => KeyCode::Digit2,
|
||||
Keycode::Keycode3 => KeyCode::Digit3,
|
||||
Keycode::Keycode4 => KeyCode::Digit4,
|
||||
Keycode::Keycode5 => KeyCode::Digit5,
|
||||
Keycode::Keycode6 => KeyCode::Digit6,
|
||||
Keycode::Keycode7 => KeyCode::Digit7,
|
||||
Keycode::Keycode8 => KeyCode::Digit8,
|
||||
Keycode::Keycode9 => KeyCode::Digit9,
|
||||
|
||||
Keycode::Numpad0 => KeyCode::Numpad0,
|
||||
Keycode::Numpad1 => KeyCode::Numpad1,
|
||||
Keycode::Numpad2 => KeyCode::Numpad2,
|
||||
Keycode::Numpad3 => KeyCode::Numpad3,
|
||||
Keycode::Numpad4 => KeyCode::Numpad4,
|
||||
Keycode::Numpad5 => KeyCode::Numpad5,
|
||||
Keycode::Numpad6 => KeyCode::Numpad6,
|
||||
Keycode::Numpad7 => KeyCode::Numpad7,
|
||||
Keycode::Numpad8 => KeyCode::Numpad8,
|
||||
Keycode::Numpad9 => KeyCode::Numpad9,
|
||||
|
||||
Keycode::NumpadAdd => KeyCode::NumpadAdd,
|
||||
Keycode::NumpadSubtract => KeyCode::NumpadSubtract,
|
||||
Keycode::NumpadMultiply => KeyCode::NumpadMultiply,
|
||||
Keycode::NumpadDivide => KeyCode::NumpadDivide,
|
||||
Keycode::NumpadEnter => KeyCode::NumpadEnter,
|
||||
Keycode::NumpadEquals => KeyCode::NumpadEqual,
|
||||
Keycode::NumpadComma => KeyCode::NumpadComma,
|
||||
Keycode::NumpadDot => KeyCode::NumpadDecimal,
|
||||
Keycode::NumLock => KeyCode::NumLock,
|
||||
|
||||
Keycode::DpadLeft => KeyCode::ArrowLeft,
|
||||
Keycode::DpadRight => KeyCode::ArrowRight,
|
||||
Keycode::DpadUp => KeyCode::ArrowUp,
|
||||
Keycode::DpadDown => KeyCode::ArrowDown,
|
||||
|
||||
Keycode::F1 => KeyCode::F1,
|
||||
Keycode::F2 => KeyCode::F2,
|
||||
Keycode::F3 => KeyCode::F3,
|
||||
Keycode::F4 => KeyCode::F4,
|
||||
Keycode::F5 => KeyCode::F5,
|
||||
Keycode::F6 => KeyCode::F6,
|
||||
Keycode::F7 => KeyCode::F7,
|
||||
Keycode::F8 => KeyCode::F8,
|
||||
Keycode::F9 => KeyCode::F9,
|
||||
Keycode::F10 => KeyCode::F10,
|
||||
Keycode::F11 => KeyCode::F11,
|
||||
Keycode::F12 => KeyCode::F12,
|
||||
|
||||
Keycode::Space => KeyCode::Space,
|
||||
Keycode::Escape => KeyCode::Escape,
|
||||
Keycode::Enter => KeyCode::Enter, // not on the Numpad
|
||||
Keycode::Tab => KeyCode::Tab,
|
||||
|
||||
Keycode::PageUp => KeyCode::PageUp,
|
||||
Keycode::PageDown => KeyCode::PageDown,
|
||||
Keycode::MoveHome => KeyCode::Home,
|
||||
Keycode::MoveEnd => KeyCode::End,
|
||||
Keycode::Insert => KeyCode::Insert,
|
||||
|
||||
Keycode::Del => KeyCode::Backspace, // Backspace (above Enter)
|
||||
Keycode::ForwardDel => KeyCode::Delete, // Delete (below Insert)
|
||||
|
||||
Keycode::Copy => KeyCode::Copy,
|
||||
Keycode::Paste => KeyCode::Paste,
|
||||
Keycode::Cut => KeyCode::Cut,
|
||||
|
||||
Keycode::VolumeUp => KeyCode::AudioVolumeUp,
|
||||
Keycode::VolumeDown => KeyCode::AudioVolumeDown,
|
||||
Keycode::VolumeMute => KeyCode::AudioVolumeMute,
|
||||
//Keycode::Mute => None, // Microphone mute
|
||||
Keycode::MediaPlayPause => KeyCode::MediaPlayPause,
|
||||
Keycode::MediaStop => KeyCode::MediaStop,
|
||||
Keycode::MediaNext => KeyCode::MediaTrackNext,
|
||||
Keycode::MediaPrevious => KeyCode::MediaTrackPrevious,
|
||||
|
||||
Keycode::Plus => KeyCode::Equal,
|
||||
Keycode::Minus => KeyCode::Minus,
|
||||
// Winit doesn't differentiate both '+' and '=', considering they are usually
|
||||
// on the same physical key
|
||||
Keycode::Equals => KeyCode::Equal,
|
||||
Keycode::Semicolon => KeyCode::Semicolon,
|
||||
Keycode::Slash => KeyCode::Slash,
|
||||
Keycode::Backslash => KeyCode::Backslash,
|
||||
Keycode::Comma => KeyCode::Comma,
|
||||
Keycode::Period => KeyCode::Period,
|
||||
Keycode::Apostrophe => KeyCode::Quote,
|
||||
Keycode::Grave => KeyCode::Backquote,
|
||||
|
||||
// Winit doesn't expose a SysRq code, so map to PrintScreen since it's
|
||||
// usually the same physical key
|
||||
Keycode::Sysrq => KeyCode::PrintScreen,
|
||||
// These are usually the same (Pause/Break)
|
||||
Keycode::Break => KeyCode::Pause,
|
||||
// These are exactly the same
|
||||
Keycode::ScrollLock => KeyCode::ScrollLock,
|
||||
|
||||
Keycode::Yen => KeyCode::IntlYen,
|
||||
Keycode::Kana => KeyCode::Lang1,
|
||||
Keycode::KatakanaHiragana => KeyCode::KanaMode,
|
||||
|
||||
Keycode::CtrlLeft => KeyCode::ControlLeft,
|
||||
Keycode::CtrlRight => KeyCode::ControlRight,
|
||||
|
||||
Keycode::ShiftLeft => KeyCode::ShiftLeft,
|
||||
Keycode::ShiftRight => KeyCode::ShiftRight,
|
||||
|
||||
Keycode::AltLeft => KeyCode::AltLeft,
|
||||
Keycode::AltRight => KeyCode::AltRight,
|
||||
|
||||
Keycode::MetaLeft => KeyCode::SuperLeft,
|
||||
Keycode::MetaRight => KeyCode::SuperRight,
|
||||
|
||||
Keycode::LeftBracket => KeyCode::BracketLeft,
|
||||
Keycode::RightBracket => KeyCode::BracketRight,
|
||||
|
||||
Keycode::Power => KeyCode::Power,
|
||||
Keycode::Sleep => KeyCode::Sleep, // what about SoftSleep?
|
||||
Keycode::Wakeup => KeyCode::WakeUp,
|
||||
|
||||
keycode => KeyCode::Unidentified(NativeKeyCode::Android(keycode.into())),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: We need to expose getUnicodeChar via android-activity instead of having
|
||||
// a fixed mapping from key codes
|
||||
pub fn to_logical(keycode: Keycode, native: NativeKey) -> Key {
|
||||
use android_activity::input::Keycode::*;
|
||||
|
||||
match keycode {
|
||||
Unknown => Key::Unidentified(native),
|
||||
|
||||
// Can be added on demand
|
||||
SoftLeft => Key::Unidentified(native),
|
||||
SoftRight => Key::Unidentified(native),
|
||||
|
||||
// Using `BrowserHome` instead of `GoHome` according to
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values
|
||||
Home => Key::BrowserHome,
|
||||
Back => Key::BrowserBack,
|
||||
Call => Key::Call,
|
||||
Endcall => Key::EndCall,
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
// Reporting unidentified, because the specific character is layout dependent.
|
||||
// (I'm not sure though)
|
||||
Keycode0 => Key::Character("0".into()),
|
||||
Keycode1 => Key::Character("1".into()),
|
||||
Keycode2 => Key::Character("2".into()),
|
||||
Keycode3 => Key::Character("3".into()),
|
||||
Keycode4 => Key::Character("4".into()),
|
||||
Keycode5 => Key::Character("5".into()),
|
||||
Keycode6 => Key::Character("6".into()),
|
||||
Keycode7 => Key::Character("7".into()),
|
||||
Keycode8 => Key::Character("8".into()),
|
||||
Keycode9 => Key::Character("9".into()),
|
||||
Star => Key::Character("*".into()),
|
||||
Pound => Key::Character("#".into()),
|
||||
A => Key::Character("a".into()),
|
||||
B => Key::Character("b".into()),
|
||||
C => Key::Character("c".into()),
|
||||
D => Key::Character("d".into()),
|
||||
E => Key::Character("e".into()),
|
||||
F => Key::Character("f".into()),
|
||||
G => Key::Character("g".into()),
|
||||
H => Key::Character("h".into()),
|
||||
I => Key::Character("i".into()),
|
||||
J => Key::Character("j".into()),
|
||||
K => Key::Character("k".into()),
|
||||
L => Key::Character("l".into()),
|
||||
M => Key::Character("m".into()),
|
||||
N => Key::Character("n".into()),
|
||||
O => Key::Character("o".into()),
|
||||
P => Key::Character("p".into()),
|
||||
Q => Key::Character("q".into()),
|
||||
R => Key::Character("r".into()),
|
||||
S => Key::Character("s".into()),
|
||||
T => Key::Character("t".into()),
|
||||
U => Key::Character("u".into()),
|
||||
V => Key::Character("v".into()),
|
||||
W => Key::Character("w".into()),
|
||||
X => Key::Character("x".into()),
|
||||
Y => Key::Character("y".into()),
|
||||
Z => Key::Character("z".into()),
|
||||
Comma => Key::Character(",".into()),
|
||||
Period => Key::Character(".".into()),
|
||||
Grave => Key::Character("`".into()),
|
||||
Minus => Key::Character("-".into()),
|
||||
Equals => Key::Character("=".into()),
|
||||
LeftBracket => Key::Character("[".into()),
|
||||
RightBracket => Key::Character("]".into()),
|
||||
Backslash => Key::Character("\\".into()),
|
||||
Semicolon => Key::Character(";".into()),
|
||||
Apostrophe => Key::Character("'".into()),
|
||||
Slash => Key::Character("/".into()),
|
||||
At => Key::Character("@".into()),
|
||||
Plus => Key::Character("+".into()),
|
||||
//-------------------------------------------------------------------------------
|
||||
DpadUp => Key::ArrowUp,
|
||||
DpadDown => Key::ArrowDown,
|
||||
DpadLeft => Key::ArrowLeft,
|
||||
DpadRight => Key::ArrowRight,
|
||||
DpadCenter => Key::Enter,
|
||||
|
||||
VolumeUp => Key::AudioVolumeUp,
|
||||
VolumeDown => Key::AudioVolumeDown,
|
||||
Power => Key::Power,
|
||||
Camera => Key::Camera,
|
||||
Clear => Key::Clear,
|
||||
|
||||
AltLeft => Key::Alt,
|
||||
AltRight => Key::Alt,
|
||||
ShiftLeft => Key::Shift,
|
||||
ShiftRight => Key::Shift,
|
||||
Tab => Key::Tab,
|
||||
Space => Key::Space,
|
||||
Sym => Key::Symbol,
|
||||
Explorer => Key::LaunchWebBrowser,
|
||||
Envelope => Key::LaunchMail,
|
||||
Enter => Key::Enter,
|
||||
Del => Key::Backspace,
|
||||
|
||||
// According to https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_NUM
|
||||
Num => Key::Alt,
|
||||
|
||||
Headsethook => Key::HeadsetHook,
|
||||
Focus => Key::CameraFocus,
|
||||
|
||||
Menu => Key::Unidentified(native),
|
||||
|
||||
Notification => Key::Notification,
|
||||
Search => Key::BrowserSearch,
|
||||
MediaPlayPause => Key::MediaPlayPause,
|
||||
MediaStop => Key::MediaStop,
|
||||
MediaNext => Key::MediaTrackNext,
|
||||
MediaPrevious => Key::MediaTrackPrevious,
|
||||
MediaRewind => Key::MediaRewind,
|
||||
MediaFastForward => Key::MediaFastForward,
|
||||
Mute => Key::MicrophoneVolumeMute,
|
||||
PageUp => Key::PageUp,
|
||||
PageDown => Key::PageDown,
|
||||
Pictsymbols => Key::Unidentified(native),
|
||||
SwitchCharset => Key::Unidentified(native),
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// Gamepad events should be exposed through a separate API, not
|
||||
// keyboard events
|
||||
ButtonA => Key::Unidentified(native),
|
||||
ButtonB => Key::Unidentified(native),
|
||||
ButtonC => Key::Unidentified(native),
|
||||
ButtonX => Key::Unidentified(native),
|
||||
ButtonY => Key::Unidentified(native),
|
||||
ButtonZ => Key::Unidentified(native),
|
||||
ButtonL1 => Key::Unidentified(native),
|
||||
ButtonR1 => Key::Unidentified(native),
|
||||
ButtonL2 => Key::Unidentified(native),
|
||||
ButtonR2 => Key::Unidentified(native),
|
||||
ButtonThumbl => Key::Unidentified(native),
|
||||
ButtonThumbr => Key::Unidentified(native),
|
||||
ButtonStart => Key::Unidentified(native),
|
||||
ButtonSelect => Key::Unidentified(native),
|
||||
ButtonMode => Key::Unidentified(native),
|
||||
// -----------------------------------------------------------------
|
||||
Escape => Key::Escape,
|
||||
ForwardDel => Key::Delete,
|
||||
CtrlLeft => Key::Control,
|
||||
CtrlRight => Key::Control,
|
||||
CapsLock => Key::CapsLock,
|
||||
ScrollLock => Key::ScrollLock,
|
||||
MetaLeft => Key::Super,
|
||||
MetaRight => Key::Super,
|
||||
Function => Key::Fn,
|
||||
Sysrq => Key::PrintScreen,
|
||||
Break => Key::Pause,
|
||||
MoveHome => Key::Home,
|
||||
MoveEnd => Key::End,
|
||||
Insert => Key::Insert,
|
||||
Forward => Key::BrowserForward,
|
||||
MediaPlay => Key::MediaPlay,
|
||||
MediaPause => Key::MediaPause,
|
||||
MediaClose => Key::MediaClose,
|
||||
MediaEject => Key::Eject,
|
||||
MediaRecord => Key::MediaRecord,
|
||||
F1 => Key::F1,
|
||||
F2 => Key::F2,
|
||||
F3 => Key::F3,
|
||||
F4 => Key::F4,
|
||||
F5 => Key::F5,
|
||||
F6 => Key::F6,
|
||||
F7 => Key::F7,
|
||||
F8 => Key::F8,
|
||||
F9 => Key::F9,
|
||||
F10 => Key::F10,
|
||||
F11 => Key::F11,
|
||||
F12 => Key::F12,
|
||||
NumLock => Key::NumLock,
|
||||
Numpad0 => Key::Character("0".into()),
|
||||
Numpad1 => Key::Character("1".into()),
|
||||
Numpad2 => Key::Character("2".into()),
|
||||
Numpad3 => Key::Character("3".into()),
|
||||
Numpad4 => Key::Character("4".into()),
|
||||
Numpad5 => Key::Character("5".into()),
|
||||
Numpad6 => Key::Character("6".into()),
|
||||
Numpad7 => Key::Character("7".into()),
|
||||
Numpad8 => Key::Character("8".into()),
|
||||
Numpad9 => Key::Character("9".into()),
|
||||
NumpadDivide => Key::Character("/".into()),
|
||||
NumpadMultiply => Key::Character("*".into()),
|
||||
NumpadSubtract => Key::Character("-".into()),
|
||||
NumpadAdd => Key::Character("+".into()),
|
||||
NumpadDot => Key::Character(".".into()),
|
||||
NumpadComma => Key::Character(",".into()),
|
||||
NumpadEnter => Key::Enter,
|
||||
NumpadEquals => Key::Character("=".into()),
|
||||
NumpadLeftParen => Key::Character("(".into()),
|
||||
NumpadRightParen => Key::Character(")".into()),
|
||||
|
||||
VolumeMute => Key::AudioVolumeMute,
|
||||
Info => Key::Info,
|
||||
ChannelUp => Key::ChannelUp,
|
||||
ChannelDown => Key::ChannelDown,
|
||||
ZoomIn => Key::ZoomIn,
|
||||
ZoomOut => Key::ZoomOut,
|
||||
Tv => Key::TV,
|
||||
Window => Key::Unidentified(native),
|
||||
Guide => Key::Guide,
|
||||
Dvr => Key::DVR,
|
||||
Bookmark => Key::BrowserFavorites,
|
||||
Captions => Key::ClosedCaptionToggle,
|
||||
Settings => Key::Settings,
|
||||
TvPower => Key::TVPower,
|
||||
TvInput => Key::TVInput,
|
||||
StbPower => Key::STBPower,
|
||||
StbInput => Key::STBInput,
|
||||
AvrPower => Key::AVRPower,
|
||||
AvrInput => Key::AVRInput,
|
||||
ProgRed => Key::ColorF0Red,
|
||||
ProgGreen => Key::ColorF1Green,
|
||||
ProgYellow => Key::ColorF2Yellow,
|
||||
ProgBlue => Key::ColorF3Blue,
|
||||
AppSwitch => Key::AppSwitch,
|
||||
Button1 => Key::Unidentified(native),
|
||||
Button2 => Key::Unidentified(native),
|
||||
Button3 => Key::Unidentified(native),
|
||||
Button4 => Key::Unidentified(native),
|
||||
Button5 => Key::Unidentified(native),
|
||||
Button6 => Key::Unidentified(native),
|
||||
Button7 => Key::Unidentified(native),
|
||||
Button8 => Key::Unidentified(native),
|
||||
Button9 => Key::Unidentified(native),
|
||||
Button10 => Key::Unidentified(native),
|
||||
Button11 => Key::Unidentified(native),
|
||||
Button12 => Key::Unidentified(native),
|
||||
Button13 => Key::Unidentified(native),
|
||||
Button14 => Key::Unidentified(native),
|
||||
Button15 => Key::Unidentified(native),
|
||||
Button16 => Key::Unidentified(native),
|
||||
LanguageSwitch => Key::GroupNext,
|
||||
MannerMode => Key::MannerMode,
|
||||
Keycode3dMode => Key::TV3DMode,
|
||||
Contacts => Key::LaunchContacts,
|
||||
Calendar => Key::LaunchCalendar,
|
||||
Music => Key::LaunchMusicPlayer,
|
||||
Calculator => Key::LaunchApplication2,
|
||||
ZenkakuHankaku => Key::ZenkakuHankaku,
|
||||
Eisu => Key::Eisu,
|
||||
Muhenkan => Key::NonConvert,
|
||||
Henkan => Key::Convert,
|
||||
KatakanaHiragana => Key::HiraganaKatakana,
|
||||
Yen => Key::Unidentified(native),
|
||||
Ro => Key::Unidentified(native),
|
||||
Kana => Key::KanjiMode,
|
||||
Assist => Key::Unidentified(native),
|
||||
BrightnessDown => Key::BrightnessDown,
|
||||
BrightnessUp => Key::BrightnessUp,
|
||||
MediaAudioTrack => Key::MediaAudioTrack,
|
||||
Sleep => Key::Standby,
|
||||
Wakeup => Key::WakeUp,
|
||||
Pairing => Key::Pairing,
|
||||
MediaTopMenu => Key::MediaTopMenu,
|
||||
Keycode11 => Key::Unidentified(native),
|
||||
Keycode12 => Key::Unidentified(native),
|
||||
LastChannel => Key::MediaLast,
|
||||
TvDataService => Key::TVDataService,
|
||||
VoiceAssist => Key::VoiceDial,
|
||||
TvRadioService => Key::TVRadioService,
|
||||
TvTeletext => Key::Teletext,
|
||||
TvNumberEntry => Key::TVNumberEntry,
|
||||
TvTerrestrialAnalog => Key::TVTerrestrialAnalog,
|
||||
TvTerrestrialDigital => Key::TVTerrestrialDigital,
|
||||
TvSatellite => Key::TVSatellite,
|
||||
TvSatelliteBs => Key::TVSatelliteBS,
|
||||
TvSatelliteCs => Key::TVSatelliteCS,
|
||||
TvSatelliteService => Key::TVSatelliteToggle,
|
||||
TvNetwork => Key::TVNetwork,
|
||||
TvAntennaCable => Key::TVAntennaCable,
|
||||
TvInputHdmi1 => Key::TVInputHDMI1,
|
||||
TvInputHdmi2 => Key::TVInputHDMI2,
|
||||
TvInputHdmi3 => Key::TVInputHDMI3,
|
||||
TvInputHdmi4 => Key::TVInputHDMI4,
|
||||
TvInputComposite1 => Key::TVInputComposite1,
|
||||
TvInputComposite2 => Key::TVInputComposite2,
|
||||
TvInputComponent1 => Key::TVInputComponent1,
|
||||
TvInputComponent2 => Key::TVInputComponent2,
|
||||
TvInputVga1 => Key::TVInputVGA1,
|
||||
TvAudioDescription => Key::TVAudioDescription,
|
||||
TvAudioDescriptionMixUp => Key::TVAudioDescriptionMixUp,
|
||||
TvAudioDescriptionMixDown => Key::TVAudioDescriptionMixDown,
|
||||
TvZoomMode => Key::ZoomToggle,
|
||||
TvContentsMenu => Key::TVContentsMenu,
|
||||
TvMediaContextMenu => Key::TVMediaContext,
|
||||
TvTimerProgramming => Key::TVTimer,
|
||||
Help => Key::Help,
|
||||
NavigatePrevious => Key::NavigatePrevious,
|
||||
NavigateNext => Key::NavigateNext,
|
||||
NavigateIn => Key::NavigateIn,
|
||||
NavigateOut => Key::NavigateOut,
|
||||
StemPrimary => Key::Unidentified(native),
|
||||
Stem1 => Key::Unidentified(native),
|
||||
Stem2 => Key::Unidentified(native),
|
||||
Stem3 => Key::Unidentified(native),
|
||||
DpadUpLeft => Key::Unidentified(native),
|
||||
DpadDownLeft => Key::Unidentified(native),
|
||||
DpadUpRight => Key::Unidentified(native),
|
||||
DpadDownRight => Key::Unidentified(native),
|
||||
MediaSkipForward => Key::MediaSkipForward,
|
||||
MediaSkipBackward => Key::MediaSkipBackward,
|
||||
MediaStepForward => Key::MediaStepForward,
|
||||
MediaStepBackward => Key::MediaStepBackward,
|
||||
SoftSleep => Key::Unidentified(native),
|
||||
Cut => Key::Cut,
|
||||
Copy => Key::Copy,
|
||||
Paste => Key::Paste,
|
||||
SystemNavigationUp => Key::Unidentified(native),
|
||||
SystemNavigationDown => Key::Unidentified(native),
|
||||
SystemNavigationLeft => Key::Unidentified(native),
|
||||
SystemNavigationRight => Key::Unidentified(native),
|
||||
AllApps => Key::Unidentified(native),
|
||||
Refresh => Key::BrowserRefresh,
|
||||
ThumbsUp => Key::Unidentified(native),
|
||||
ThumbsDown => Key::Unidentified(native),
|
||||
ProfileSwitch => Key::Unidentified(native),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_location(keycode: Keycode) -> KeyLocation {
|
||||
use android_activity::input::Keycode::*;
|
||||
|
||||
match keycode {
|
||||
AltLeft => KeyLocation::Left,
|
||||
AltRight => KeyLocation::Right,
|
||||
ShiftLeft => KeyLocation::Left,
|
||||
ShiftRight => KeyLocation::Right,
|
||||
|
||||
// According to https://developer.android.com/reference/android/view/KeyEvent#KEYCODE_NUM
|
||||
Num => KeyLocation::Left,
|
||||
|
||||
CtrlLeft => KeyLocation::Left,
|
||||
CtrlRight => KeyLocation::Right,
|
||||
MetaLeft => KeyLocation::Left,
|
||||
MetaRight => KeyLocation::Right,
|
||||
|
||||
NumLock => KeyLocation::Numpad,
|
||||
Numpad0 => KeyLocation::Numpad,
|
||||
Numpad1 => KeyLocation::Numpad,
|
||||
Numpad2 => KeyLocation::Numpad,
|
||||
Numpad3 => KeyLocation::Numpad,
|
||||
Numpad4 => KeyLocation::Numpad,
|
||||
Numpad5 => KeyLocation::Numpad,
|
||||
Numpad6 => KeyLocation::Numpad,
|
||||
Numpad7 => KeyLocation::Numpad,
|
||||
Numpad8 => KeyLocation::Numpad,
|
||||
Numpad9 => KeyLocation::Numpad,
|
||||
NumpadDivide => KeyLocation::Numpad,
|
||||
NumpadMultiply => KeyLocation::Numpad,
|
||||
NumpadSubtract => KeyLocation::Numpad,
|
||||
NumpadAdd => KeyLocation::Numpad,
|
||||
NumpadDot => KeyLocation::Numpad,
|
||||
NumpadComma => KeyLocation::Numpad,
|
||||
NumpadEnter => KeyLocation::Numpad,
|
||||
NumpadEquals => KeyLocation::Numpad,
|
||||
NumpadLeftParen => KeyLocation::Numpad,
|
||||
NumpadRightParen => KeyLocation::Numpad,
|
||||
|
||||
_ => KeyLocation::Standard,
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,20 +9,27 @@ use std::{
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use objc::runtime::{BOOL, YES};
|
||||
use core_foundation::base::CFRelease;
|
||||
use core_foundation::date::CFAbsoluteTimeGetCurrent;
|
||||
use core_foundation::runloop::{
|
||||
kCFRunLoopCommonModes, CFRunLoopAddTimer, CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate,
|
||||
CFRunLoopTimerInvalidate, CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate,
|
||||
};
|
||||
use objc2::foundation::{CGRect, CGSize, NSInteger, NSProcessInfo};
|
||||
use objc2::rc::{Id, Shared};
|
||||
use objc2::runtime::Object;
|
||||
use objc2::{msg_send, sel};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::uikit::UIView;
|
||||
use super::view::WinitUIWindow;
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
event::{Event, StartCause, WindowEvent},
|
||||
event_loop::ControlFlow,
|
||||
platform_impl::platform::{
|
||||
event_loop::{EventHandler, EventProxy, EventWrapper, Never},
|
||||
ffi::{
|
||||
id, kCFRunLoopCommonModes, CFAbsoluteTimeGetCurrent, CFRelease, CFRunLoopAddTimer,
|
||||
CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate, CFRunLoopTimerInvalidate,
|
||||
CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate, CGRect, CGSize, NSInteger,
|
||||
NSOperatingSystemVersion, NSUInteger,
|
||||
},
|
||||
ffi::NSOperatingSystemVersion,
|
||||
},
|
||||
window::WindowId as RootWindowId,
|
||||
};
|
||||
@@ -52,11 +59,7 @@ enum UserCallbackTransitionResult<'a> {
|
||||
|
||||
impl Event<'static, Never> {
|
||||
fn is_redraw(&self) -> bool {
|
||||
if let Event::RedrawRequested(_) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
matches!(self, Event::RedrawRequested(_))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,25 +68,25 @@ impl Event<'static, Never> {
|
||||
#[must_use = "dropping `AppStateImpl` without inspecting it is probably a bug"]
|
||||
enum AppStateImpl {
|
||||
NotLaunched {
|
||||
queued_windows: Vec<id>,
|
||||
queued_windows: Vec<Id<WinitUIWindow, Shared>>,
|
||||
queued_events: Vec<EventWrapper>,
|
||||
queued_gpu_redraws: HashSet<id>,
|
||||
queued_gpu_redraws: HashSet<Id<WinitUIWindow, Shared>>,
|
||||
},
|
||||
Launching {
|
||||
queued_windows: Vec<id>,
|
||||
queued_windows: Vec<Id<WinitUIWindow, Shared>>,
|
||||
queued_events: Vec<EventWrapper>,
|
||||
queued_event_handler: Box<dyn EventHandler>,
|
||||
queued_gpu_redraws: HashSet<id>,
|
||||
queued_gpu_redraws: HashSet<Id<WinitUIWindow, Shared>>,
|
||||
},
|
||||
ProcessingEvents {
|
||||
event_handler: Box<dyn EventHandler>,
|
||||
queued_gpu_redraws: HashSet<id>,
|
||||
queued_gpu_redraws: HashSet<Id<WinitUIWindow, Shared>>,
|
||||
active_control_flow: ControlFlow,
|
||||
},
|
||||
// special state to deal with reentrancy and prevent mutable aliasing.
|
||||
InUserCallback {
|
||||
queued_events: Vec<EventWrapper>,
|
||||
queued_gpu_redraws: HashSet<id>,
|
||||
queued_gpu_redraws: HashSet<Id<WinitUIWindow, Shared>>,
|
||||
},
|
||||
ProcessingRedraws {
|
||||
event_handler: Box<dyn EventHandler>,
|
||||
@@ -106,28 +109,6 @@ struct AppState {
|
||||
waker: EventLoopWaker,
|
||||
}
|
||||
|
||||
impl Drop for AppState {
|
||||
fn drop(&mut self) {
|
||||
match self.state_mut() {
|
||||
&mut AppStateImpl::NotLaunched {
|
||||
ref mut queued_windows,
|
||||
..
|
||||
}
|
||||
| &mut AppStateImpl::Launching {
|
||||
ref mut queued_windows,
|
||||
..
|
||||
} => {
|
||||
for &mut window in queued_windows {
|
||||
unsafe {
|
||||
let () = msg_send![window, release];
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
// requires main thread
|
||||
unsafe fn get_mut() -> RefMut<'static, AppState> {
|
||||
@@ -200,10 +181,10 @@ impl AppState {
|
||||
}
|
||||
|
||||
fn has_launched(&self) -> bool {
|
||||
match self.state() {
|
||||
&AppStateImpl::NotLaunched { .. } | &AppStateImpl::Launching { .. } => false,
|
||||
_ => true,
|
||||
}
|
||||
!matches!(
|
||||
self.state(),
|
||||
AppStateImpl::NotLaunched { .. } | AppStateImpl::Launching { .. }
|
||||
)
|
||||
}
|
||||
|
||||
fn will_launch_transition(&mut self, queued_event_handler: Box<dyn EventHandler>) {
|
||||
@@ -223,7 +204,9 @@ impl AppState {
|
||||
});
|
||||
}
|
||||
|
||||
fn did_finish_launching_transition(&mut self) -> (Vec<id>, Vec<EventWrapper>) {
|
||||
fn did_finish_launching_transition(
|
||||
&mut self,
|
||||
) -> (Vec<Id<WinitUIWindow, Shared>>, Vec<EventWrapper>) {
|
||||
let (windows, events, event_handler, queued_gpu_redraws) = match self.take_state() {
|
||||
AppStateImpl::Launching {
|
||||
queued_windows,
|
||||
@@ -296,7 +279,7 @@ impl AppState {
|
||||
};
|
||||
(waiting_event_handler, event)
|
||||
}
|
||||
(ControlFlow::Exit, _) => bug!("unexpected `ControlFlow` `Exit`"),
|
||||
(ControlFlow::ExitWithCode(_), _) => bug!("unexpected `ControlFlow` `Exit`"),
|
||||
s => bug!("`EventHandler` unexpectedly woke up {:?}", s),
|
||||
};
|
||||
|
||||
@@ -380,7 +363,7 @@ impl AppState {
|
||||
}
|
||||
}
|
||||
|
||||
fn main_events_cleared_transition(&mut self) -> HashSet<id> {
|
||||
fn main_events_cleared_transition(&mut self) -> HashSet<Id<WinitUIWindow, Shared>> {
|
||||
let (event_handler, queued_gpu_redraws, active_control_flow) = match self.take_state() {
|
||||
AppStateImpl::ProcessingEvents {
|
||||
event_handler,
|
||||
@@ -451,7 +434,7 @@ impl AppState {
|
||||
});
|
||||
self.waker.start()
|
||||
}
|
||||
(_, ControlFlow::Exit) => {
|
||||
(_, ControlFlow::ExitWithCode(_)) => {
|
||||
// https://developer.apple.com/library/archive/qa/qa1561/_index.html
|
||||
// it is not possible to quit an iOS app gracefully and programatically
|
||||
warn!("`ControlFlow::Exit` ignored on iOS");
|
||||
@@ -473,20 +456,13 @@ impl AppState {
|
||||
|
||||
// requires main thread and window is a UIWindow
|
||||
// retains window
|
||||
pub unsafe fn set_key_window(window: id) {
|
||||
bug_assert!(
|
||||
{
|
||||
let is_window: BOOL = msg_send![window, isKindOfClass: class!(UIWindow)];
|
||||
is_window == YES
|
||||
},
|
||||
"set_key_window called with an incorrect type"
|
||||
);
|
||||
pub(crate) unsafe fn set_key_window(window: &Id<WinitUIWindow, Shared>) {
|
||||
let mut this = AppState::get_mut();
|
||||
match this.state_mut() {
|
||||
&mut AppStateImpl::NotLaunched {
|
||||
ref mut queued_windows,
|
||||
..
|
||||
} => return queued_windows.push(msg_send![window, retain]),
|
||||
} => return queued_windows.push(window.clone()),
|
||||
&mut AppStateImpl::ProcessingEvents { .. }
|
||||
| &mut AppStateImpl::InUserCallback { .. }
|
||||
| &mut AppStateImpl::ProcessingRedraws { .. } => {}
|
||||
@@ -498,19 +474,12 @@ pub unsafe fn set_key_window(window: id) {
|
||||
}
|
||||
}
|
||||
drop(this);
|
||||
msg_send![window, makeKeyAndVisible]
|
||||
window.makeKeyAndVisible();
|
||||
}
|
||||
|
||||
// requires main thread and window is a UIWindow
|
||||
// retains window
|
||||
pub unsafe fn queue_gl_or_metal_redraw(window: id) {
|
||||
bug_assert!(
|
||||
{
|
||||
let is_window: BOOL = msg_send![window, isKindOfClass: class!(UIWindow)];
|
||||
is_window == YES
|
||||
},
|
||||
"set_key_window called with an incorrect type"
|
||||
);
|
||||
pub(crate) unsafe fn queue_gl_or_metal_redraw(window: Id<WinitUIWindow, Shared>) {
|
||||
let mut this = AppState::get_mut();
|
||||
match this.state_mut() {
|
||||
&mut AppStateImpl::NotLaunched {
|
||||
@@ -528,7 +497,9 @@ pub unsafe fn queue_gl_or_metal_redraw(window: id) {
|
||||
| &mut AppStateImpl::InUserCallback {
|
||||
ref mut queued_gpu_redraws,
|
||||
..
|
||||
} => drop(queued_gpu_redraws.insert(window)),
|
||||
} => {
|
||||
let _ = queued_gpu_redraws.insert(window);
|
||||
}
|
||||
s @ &mut AppStateImpl::ProcessingRedraws { .. }
|
||||
| s @ &mut AppStateImpl::Waiting { .. }
|
||||
| s @ &mut AppStateImpl::PollFinished { .. } => bug!("unexpected state {:?}", s),
|
||||
@@ -536,7 +507,6 @@ pub unsafe fn queue_gl_or_metal_redraw(window: id) {
|
||||
panic!("Attempt to create a `Window` after the app has terminated")
|
||||
}
|
||||
}
|
||||
drop(this);
|
||||
}
|
||||
|
||||
// requires main thread
|
||||
@@ -548,7 +518,7 @@ pub unsafe fn will_launch(queued_event_handler: Box<dyn EventHandler>) {
|
||||
pub unsafe fn did_finish_launching() {
|
||||
let mut this = AppState::get_mut();
|
||||
let windows = match this.state_mut() {
|
||||
AppStateImpl::Launching { queued_windows, .. } => mem::replace(queued_windows, Vec::new()),
|
||||
AppStateImpl::Launching { queued_windows, .. } => mem::take(queued_windows),
|
||||
s => bug!("unexpected state {:?}", s),
|
||||
};
|
||||
|
||||
@@ -563,30 +533,25 @@ pub unsafe fn did_finish_launching() {
|
||||
drop(this);
|
||||
|
||||
for window in windows {
|
||||
let count: NSUInteger = msg_send![window, retainCount];
|
||||
// make sure the window is still referenced
|
||||
if count > 1 {
|
||||
// Do a little screen dance here to account for windows being created before
|
||||
// `UIApplicationMain` is called. This fixes visual issues such as being
|
||||
// offcenter and sized incorrectly. Additionally, to fix orientation issues, we
|
||||
// gotta reset the `rootViewController`.
|
||||
//
|
||||
// relevant iOS log:
|
||||
// ```
|
||||
// [ApplicationLifecycle] Windows were created before application initialzation
|
||||
// completed. This may result in incorrect visual appearance.
|
||||
// ```
|
||||
let screen: id = msg_send![window, screen];
|
||||
let _: id = msg_send![screen, retain];
|
||||
let () = msg_send![window, setScreen:0 as id];
|
||||
let () = msg_send![window, setScreen: screen];
|
||||
let () = msg_send![screen, release];
|
||||
let controller: id = msg_send![window, rootViewController];
|
||||
let () = msg_send![window, setRootViewController:ptr::null::<()>()];
|
||||
let () = msg_send![window, setRootViewController: controller];
|
||||
let () = msg_send![window, makeKeyAndVisible];
|
||||
}
|
||||
let () = msg_send![window, release];
|
||||
// Do a little screen dance here to account for windows being created before
|
||||
// `UIApplicationMain` is called. This fixes visual issues such as being
|
||||
// offcenter and sized incorrectly. Additionally, to fix orientation issues, we
|
||||
// gotta reset the `rootViewController`.
|
||||
//
|
||||
// relevant iOS log:
|
||||
// ```
|
||||
// [ApplicationLifecycle] Windows were created before application initialzation
|
||||
// completed. This may result in incorrect visual appearance.
|
||||
// ```
|
||||
let screen = window.screen();
|
||||
let _: () = msg_send![&window, setScreen: ptr::null::<Object>()];
|
||||
window.setScreen(&screen);
|
||||
|
||||
let controller = window.rootViewController();
|
||||
window.setRootViewController(None);
|
||||
window.setRootViewController(controller.as_deref());
|
||||
|
||||
window.makeKeyAndVisible();
|
||||
}
|
||||
|
||||
let (windows, events) = AppState::get_mut().did_finish_launching_transition();
|
||||
@@ -600,12 +565,7 @@ pub unsafe fn did_finish_launching() {
|
||||
// the above window dance hack, could possibly trigger new windows to be created.
|
||||
// we can just set those windows up normally, as they were created after didFinishLaunching
|
||||
for window in windows {
|
||||
let count: NSUInteger = msg_send![window, retainCount];
|
||||
// make sure the window is still referenced
|
||||
if count > 1 {
|
||||
let () = msg_send![window, makeKeyAndVisible];
|
||||
}
|
||||
let () = msg_send![window, release];
|
||||
window.makeKeyAndVisible();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -623,12 +583,12 @@ pub unsafe fn handle_wakeup_transition() {
|
||||
}
|
||||
|
||||
// requires main thread
|
||||
pub unsafe fn handle_nonuser_event(event: EventWrapper) {
|
||||
pub(crate) unsafe fn handle_nonuser_event(event: EventWrapper) {
|
||||
handle_nonuser_events(std::iter::once(event))
|
||||
}
|
||||
|
||||
// requires main thread
|
||||
pub unsafe fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(events: I) {
|
||||
pub(crate) unsafe fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(events: I) {
|
||||
let mut this = AppState::get_mut();
|
||||
let (mut event_handler, active_control_flow, processing_redraws) =
|
||||
match this.try_user_callback_transition() {
|
||||
@@ -670,7 +630,7 @@ pub unsafe fn handle_nonuser_events<I: IntoIterator<Item = EventWrapper>>(events
|
||||
&mut AppStateImpl::InUserCallback {
|
||||
ref mut queued_events,
|
||||
queued_gpu_redraws: _,
|
||||
} => mem::replace(queued_events, Vec::new()),
|
||||
} => mem::take(queued_events),
|
||||
s => bug!("unexpected state {:?}", s),
|
||||
};
|
||||
if queued_events.is_empty() {
|
||||
@@ -751,7 +711,7 @@ unsafe fn handle_user_events() {
|
||||
&mut AppStateImpl::InUserCallback {
|
||||
ref mut queued_events,
|
||||
queued_gpu_redraws: _,
|
||||
} => mem::replace(queued_events, Vec::new()),
|
||||
} => mem::take(queued_events),
|
||||
s => bug!("unexpected state {:?}", s),
|
||||
};
|
||||
if queued_events.is_empty() {
|
||||
@@ -793,7 +753,7 @@ pub unsafe fn handle_main_events_cleared() {
|
||||
return;
|
||||
}
|
||||
match this.state_mut() {
|
||||
&mut AppStateImpl::ProcessingEvents { .. } => {}
|
||||
AppStateImpl::ProcessingEvents { .. } => {}
|
||||
_ => bug!("`ProcessingRedraws` happened unexpectedly"),
|
||||
};
|
||||
drop(this);
|
||||
@@ -806,14 +766,10 @@ pub unsafe fn handle_main_events_cleared() {
|
||||
let mut redraw_events: Vec<EventWrapper> = this
|
||||
.main_events_cleared_transition()
|
||||
.into_iter()
|
||||
.map(|window| {
|
||||
EventWrapper::StaticEvent(Event::RedrawRequested(RootWindowId(window.into())))
|
||||
})
|
||||
.map(|window| EventWrapper::StaticEvent(Event::RedrawRequested(RootWindowId(window.id()))))
|
||||
.collect();
|
||||
|
||||
if !redraw_events.is_empty() {
|
||||
redraw_events.push(EventWrapper::StaticEvent(Event::RedrawEventsCleared));
|
||||
}
|
||||
redraw_events.push(EventWrapper::StaticEvent(Event::RedrawEventsCleared));
|
||||
drop(this);
|
||||
|
||||
handle_nonuser_events(redraw_events);
|
||||
@@ -843,13 +799,13 @@ fn handle_event_proxy(
|
||||
EventProxy::DpiChangedProxy {
|
||||
suggested_size,
|
||||
scale_factor,
|
||||
window_id,
|
||||
window,
|
||||
} => handle_hidpi_proxy(
|
||||
event_handler,
|
||||
control_flow,
|
||||
suggested_size,
|
||||
scale_factor,
|
||||
window_id,
|
||||
window,
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -859,39 +815,34 @@ fn handle_hidpi_proxy(
|
||||
mut control_flow: ControlFlow,
|
||||
suggested_size: LogicalSize<f64>,
|
||||
scale_factor: f64,
|
||||
window_id: id,
|
||||
window: Id<WinitUIWindow, Shared>,
|
||||
) {
|
||||
let mut size = suggested_size.to_physical(scale_factor);
|
||||
let new_inner_size = &mut size;
|
||||
let event = Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id.into()),
|
||||
window_id: RootWindowId(window.id()),
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
new_inner_size,
|
||||
},
|
||||
};
|
||||
event_handler.handle_nonuser_event(event, &mut control_flow);
|
||||
let (view, screen_frame) = get_view_and_screen_frame(window_id);
|
||||
let (view, screen_frame) = get_view_and_screen_frame(&window);
|
||||
let physical_size = *new_inner_size;
|
||||
let logical_size = physical_size.to_logical(scale_factor);
|
||||
let size = CGSize::new(logical_size);
|
||||
let size = CGSize::new(logical_size.width, logical_size.height);
|
||||
let new_frame: CGRect = CGRect::new(screen_frame.origin, size);
|
||||
unsafe {
|
||||
let () = msg_send![view, setFrame: new_frame];
|
||||
}
|
||||
view.setFrame(new_frame);
|
||||
}
|
||||
|
||||
fn get_view_and_screen_frame(window_id: id) -> (id, CGRect) {
|
||||
unsafe {
|
||||
let view_controller: id = msg_send![window_id, rootViewController];
|
||||
let view: id = msg_send![view_controller, view];
|
||||
let bounds: CGRect = msg_send![window_id, bounds];
|
||||
let screen: id = msg_send![window_id, screen];
|
||||
let screen_space: id = msg_send![screen, coordinateSpace];
|
||||
let screen_frame: CGRect =
|
||||
msg_send![window_id, convertRect:bounds toCoordinateSpace:screen_space];
|
||||
(view, screen_frame)
|
||||
}
|
||||
fn get_view_and_screen_frame(window: &WinitUIWindow) -> (Id<UIView, Shared>, CGRect) {
|
||||
let view_controller = window.rootViewController().unwrap();
|
||||
let view = view_controller.view().unwrap();
|
||||
let bounds = window.bounds();
|
||||
let screen = window.screen();
|
||||
let screen_space = screen.coordinateSpace();
|
||||
let screen_frame = window.convertRect_toCoordinateSpace(bounds, &screen_space);
|
||||
(view, screen_frame)
|
||||
}
|
||||
|
||||
struct EventLoopWaker {
|
||||
@@ -992,20 +943,20 @@ macro_rules! os_capabilities {
|
||||
}
|
||||
|
||||
os_capabilities! {
|
||||
/// https://developer.apple.com/documentation/uikit/uiview/2891103-safeareainsets?language=objc
|
||||
/// <https://developer.apple.com/documentation/uikit/uiview/2891103-safeareainsets?language=objc>
|
||||
#[allow(unused)] // error message unused
|
||||
safe_area_err_msg: "-[UIView safeAreaInsets]",
|
||||
safe_area: 11-0,
|
||||
/// https://developer.apple.com/documentation/uikit/uiviewcontroller/2887509-setneedsupdateofhomeindicatoraut?language=objc
|
||||
/// <https://developer.apple.com/documentation/uikit/uiviewcontroller/2887509-setneedsupdateofhomeindicatoraut?language=objc>
|
||||
home_indicator_hidden_err_msg: "-[UIViewController setNeedsUpdateOfHomeIndicatorAutoHidden]",
|
||||
home_indicator_hidden: 11-0,
|
||||
/// https://developer.apple.com/documentation/uikit/uiviewcontroller/2887507-setneedsupdateofscreenedgesdefer?language=objc
|
||||
/// <https://developer.apple.com/documentation/uikit/uiviewcontroller/2887507-setneedsupdateofscreenedgesdefer?language=objc>
|
||||
defer_system_gestures_err_msg: "-[UIViewController setNeedsUpdateOfScreenEdgesDeferringSystem]",
|
||||
defer_system_gestures: 11-0,
|
||||
/// https://developer.apple.com/documentation/uikit/uiscreen/2806814-maximumframespersecond?language=objc
|
||||
/// <https://developer.apple.com/documentation/uikit/uiscreen/2806814-maximumframespersecond?language=objc>
|
||||
maximum_frames_per_second_err_msg: "-[UIScreen maximumFramesPerSecond]",
|
||||
maximum_frames_per_second: 10-3,
|
||||
/// https://developer.apple.com/documentation/uikit/uitouch/1618110-force?language=objc
|
||||
/// <https://developer.apple.com/documentation/uikit/uitouch/1618110-force?language=objc>
|
||||
#[allow(unused)] // error message unused
|
||||
force_touch_err_msg: "-[UITouch force]",
|
||||
force_touch: 9-0,
|
||||
@@ -1018,29 +969,24 @@ impl NSOperatingSystemVersion {
|
||||
}
|
||||
|
||||
pub fn os_capabilities() -> OSCapabilities {
|
||||
lazy_static! {
|
||||
static ref OS_CAPABILITIES: OSCapabilities = {
|
||||
let version: NSOperatingSystemVersion = unsafe {
|
||||
let process_info: id = msg_send![class!(NSProcessInfo), processInfo];
|
||||
let atleast_ios_8: BOOL = msg_send![
|
||||
process_info,
|
||||
respondsToSelector: sel!(operatingSystemVersion)
|
||||
];
|
||||
// winit requires atleast iOS 8 because no one has put the time into supporting earlier os versions.
|
||||
// Older iOS versions are increasingly difficult to test. For example, Xcode 11 does not support
|
||||
// debugging on devices with an iOS version of less than 8. Another example, in order to use an iOS
|
||||
// simulator older than iOS 8, you must download an older version of Xcode (<9), and at least Xcode 7
|
||||
// has been tested to not even run on macOS 10.15 - Xcode 8 might?
|
||||
//
|
||||
// The minimum required iOS version is likely to grow in the future.
|
||||
assert!(
|
||||
atleast_ios_8 == YES,
|
||||
"`winit` requires iOS version 8 or greater"
|
||||
);
|
||||
msg_send![process_info, operatingSystemVersion]
|
||||
};
|
||||
version.into()
|
||||
static OS_CAPABILITIES: Lazy<OSCapabilities> = Lazy::new(|| {
|
||||
let version: NSOperatingSystemVersion = unsafe {
|
||||
let process_info = NSProcessInfo::process_info();
|
||||
let atleast_ios_8: bool = msg_send![
|
||||
&process_info,
|
||||
respondsToSelector: sel!(operatingSystemVersion)
|
||||
];
|
||||
// winit requires atleast iOS 8 because no one has put the time into supporting earlier os versions.
|
||||
// Older iOS versions are increasingly difficult to test. For example, Xcode 11 does not support
|
||||
// debugging on devices with an iOS version of less than 8. Another example, in order to use an iOS
|
||||
// simulator older than iOS 8, you must download an older version of Xcode (<9), and at least Xcode 7
|
||||
// has been tested to not even run on macOS 10.15 - Xcode 8 might?
|
||||
//
|
||||
// The minimum required iOS version is likely to grow in the future.
|
||||
assert!(atleast_ios_8, "`winit` requires iOS version 8 or greater");
|
||||
msg_send![&process_info, operatingSystemVersion]
|
||||
};
|
||||
}
|
||||
version.into()
|
||||
});
|
||||
OS_CAPABILITIES.clone()
|
||||
}
|
||||
|
||||
@@ -3,10 +3,25 @@ use std::{
|
||||
ffi::c_void,
|
||||
fmt::{self, Debug},
|
||||
marker::PhantomData,
|
||||
mem, ptr,
|
||||
ptr,
|
||||
sync::mpsc::{self, Receiver, Sender},
|
||||
};
|
||||
|
||||
use core_foundation::base::{CFIndex, CFRelease};
|
||||
use core_foundation::runloop::{
|
||||
kCFRunLoopAfterWaiting, kCFRunLoopBeforeWaiting, kCFRunLoopCommonModes, kCFRunLoopDefaultMode,
|
||||
kCFRunLoopExit, CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopAddSource, CFRunLoopGetMain,
|
||||
CFRunLoopObserverCreate, CFRunLoopObserverRef, CFRunLoopSourceContext, CFRunLoopSourceCreate,
|
||||
CFRunLoopSourceInvalidate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
|
||||
};
|
||||
use objc2::foundation::{MainThreadMarker, NSString};
|
||||
use objc2::rc::{Id, Shared};
|
||||
use objc2::ClassType;
|
||||
use raw_window_handle::{RawDisplayHandle, UiKitDisplayHandle};
|
||||
|
||||
use super::uikit::{UIApplication, UIApplicationMain, UIDevice, UIScreen};
|
||||
use super::view::WinitUIWindow;
|
||||
use super::{app_state, monitor, view, MonitorHandle};
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
event::Event,
|
||||
@@ -16,29 +31,16 @@ use crate::{
|
||||
platform::ios::Idiom,
|
||||
};
|
||||
|
||||
use crate::platform_impl::platform::{
|
||||
app_state,
|
||||
ffi::{
|
||||
id, kCFRunLoopAfterWaiting, kCFRunLoopBeforeWaiting, kCFRunLoopCommonModes,
|
||||
kCFRunLoopDefaultMode, kCFRunLoopEntry, kCFRunLoopExit, nil, CFIndex, CFRelease,
|
||||
CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopAddSource, CFRunLoopGetMain,
|
||||
CFRunLoopObserverCreate, CFRunLoopObserverRef, CFRunLoopSourceContext,
|
||||
CFRunLoopSourceCreate, CFRunLoopSourceInvalidate, CFRunLoopSourceRef,
|
||||
CFRunLoopSourceSignal, CFRunLoopWakeUp, NSString, UIApplicationMain, UIUserInterfaceIdiom,
|
||||
},
|
||||
monitor, view, MonitorHandle,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EventWrapper {
|
||||
pub(crate) enum EventWrapper {
|
||||
StaticEvent(Event<'static, Never>),
|
||||
EventProxy(EventProxy),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum EventProxy {
|
||||
pub(crate) enum EventProxy {
|
||||
DpiChangedProxy {
|
||||
window_id: id,
|
||||
window: Id<WinitUIWindow, Shared>,
|
||||
suggested_size: LogicalSize<f64>,
|
||||
scale_factor: f64,
|
||||
},
|
||||
@@ -49,22 +51,41 @@ pub struct EventLoopWindowTarget<T: 'static> {
|
||||
sender_to_clone: Sender<T>,
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopWindowTarget<T> {
|
||||
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||
monitor::uiscreens(MainThreadMarker::new().unwrap())
|
||||
}
|
||||
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
Some(MonitorHandle::new(UIScreen::main(
|
||||
MainThreadMarker::new().unwrap(),
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn raw_display_handle(&self) -> RawDisplayHandle {
|
||||
RawDisplayHandle::UiKit(UiKitDisplayHandle::empty())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventLoop<T: 'static> {
|
||||
window_target: RootEventLoopWindowTarget<T>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct PlatformSpecificEventLoopAttributes {}
|
||||
|
||||
impl<T: 'static> EventLoop<T> {
|
||||
pub fn new() -> EventLoop<T> {
|
||||
pub(crate) fn new(_: &PlatformSpecificEventLoopAttributes) -> EventLoop<T> {
|
||||
assert_main_thread!("`EventLoop` can only be created on the main thread on iOS");
|
||||
|
||||
static mut SINGLETON_INIT: bool = false;
|
||||
unsafe {
|
||||
assert_main_thread!("`EventLoop` can only be created on the main thread on iOS");
|
||||
assert!(
|
||||
!SINGLETON_INIT,
|
||||
"Only one `EventLoop` is supported on iOS. \
|
||||
`EventLoopProxy` might be helpful"
|
||||
);
|
||||
SINGLETON_INIT = true;
|
||||
view::create_delegate_class();
|
||||
}
|
||||
|
||||
let (sender_to_clone, receiver) = mpsc::channel();
|
||||
@@ -88,24 +109,26 @@ impl<T: 'static> EventLoop<T> {
|
||||
F: 'static + FnMut(Event<'_, T>, &RootEventLoopWindowTarget<T>, &mut ControlFlow),
|
||||
{
|
||||
unsafe {
|
||||
let application: *mut c_void = msg_send![class!(UIApplication), sharedApplication];
|
||||
assert_eq!(
|
||||
application,
|
||||
ptr::null_mut(),
|
||||
let application = UIApplication::shared(MainThreadMarker::new().unwrap());
|
||||
assert!(
|
||||
application.is_none(),
|
||||
"\
|
||||
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\
|
||||
Note: `EventLoop::run` calls `UIApplicationMain` on iOS"
|
||||
`EventLoop` cannot be `run` after a call to `UIApplicationMain` on iOS\n\
|
||||
Note: `EventLoop::run` calls `UIApplicationMain` on iOS",
|
||||
);
|
||||
app_state::will_launch(Box::new(EventLoopHandler {
|
||||
f: event_handler,
|
||||
event_loop: self.window_target,
|
||||
}));
|
||||
|
||||
// Ensure application delegate is initialized
|
||||
view::WinitApplicationDelegate::class();
|
||||
|
||||
UIApplicationMain(
|
||||
0,
|
||||
ptr::null(),
|
||||
nil,
|
||||
NSString::alloc(nil).init_str("AppDelegate"),
|
||||
None,
|
||||
Some(&NSString::from_str("WinitApplicationDelegate")),
|
||||
);
|
||||
unreachable!()
|
||||
}
|
||||
@@ -115,16 +138,6 @@ impl<T: 'static> EventLoop<T> {
|
||||
EventLoopProxy::new(self.window_target.p.sender_to_clone.clone())
|
||||
}
|
||||
|
||||
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||
// guaranteed to be on main thread
|
||||
unsafe { monitor::uiscreens() }
|
||||
}
|
||||
|
||||
pub fn primary_monitor(&self) -> MonitorHandle {
|
||||
// guaranteed to be on main thread
|
||||
unsafe { monitor::main_uiscreen() }
|
||||
}
|
||||
|
||||
pub fn window_target(&self) -> &RootEventLoopWindowTarget<T> {
|
||||
&self.window_target
|
||||
}
|
||||
@@ -133,8 +146,9 @@ impl<T: 'static> EventLoop<T> {
|
||||
// EventLoopExtIOS
|
||||
impl<T: 'static> EventLoop<T> {
|
||||
pub fn idiom(&self) -> Idiom {
|
||||
// guaranteed to be on main thread
|
||||
unsafe { self::get_idiom() }
|
||||
UIDevice::current(MainThreadMarker::new().unwrap())
|
||||
.userInterfaceIdiom()
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,14 +178,23 @@ impl<T> EventLoopProxy<T> {
|
||||
fn new(sender: Sender<T>) -> EventLoopProxy<T> {
|
||||
unsafe {
|
||||
// just wake up the eventloop
|
||||
extern "C" fn event_loop_proxy_handler(_: *mut c_void) {}
|
||||
extern "C" fn event_loop_proxy_handler(_: *const c_void) {}
|
||||
|
||||
// adding a Source to the main CFRunLoop lets us wake it up and
|
||||
// process user events through the normal OS EventLoop mechanisms.
|
||||
let rl = CFRunLoopGetMain();
|
||||
// we want all the members of context to be zero/null, except one
|
||||
let mut context: CFRunLoopSourceContext = mem::zeroed();
|
||||
context.perform = Some(event_loop_proxy_handler);
|
||||
let mut context = CFRunLoopSourceContext {
|
||||
version: 0,
|
||||
info: ptr::null_mut(),
|
||||
retain: None,
|
||||
release: None,
|
||||
copyDescription: None,
|
||||
equal: None,
|
||||
hash: None,
|
||||
schedule: None,
|
||||
cancel: None,
|
||||
perform: event_loop_proxy_handler,
|
||||
};
|
||||
let source =
|
||||
CFRunLoopSourceCreate(ptr::null_mut(), CFIndex::max_value() - 1, &mut context);
|
||||
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
|
||||
@@ -207,7 +230,6 @@ fn setup_control_flow_observers() {
|
||||
#[allow(non_upper_case_globals)]
|
||||
match activity {
|
||||
kCFRunLoopAfterWaiting => app_state::handle_wakeup_transition(),
|
||||
kCFRunLoopEntry => unimplemented!(), // not expected to ever happen
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@@ -259,7 +281,7 @@ fn setup_control_flow_observers() {
|
||||
|
||||
let begin_observer = CFRunLoopObserverCreate(
|
||||
ptr::null_mut(),
|
||||
kCFRunLoopEntry | kCFRunLoopAfterWaiting,
|
||||
kCFRunLoopAfterWaiting,
|
||||
1, // repeat = true
|
||||
CFIndex::min_value(),
|
||||
control_flow_begin_handler,
|
||||
@@ -329,10 +351,3 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// must be called on main thread
|
||||
pub unsafe fn get_idiom() -> Idiom {
|
||||
let device: id = msg_send![class!(UIDevice), currentDevice];
|
||||
let raw_idiom: UIUserInterfaceIdiom = msg_send![device, userInterfaceIdiom];
|
||||
raw_idiom.into()
|
||||
}
|
||||
|
||||
@@ -1,24 +1,11 @@
|
||||
#![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)]
|
||||
|
||||
use std::{convert::TryInto, ffi::CString, ops::BitOr, os::raw::*};
|
||||
use std::convert::TryInto;
|
||||
|
||||
use objc::{runtime::Object, Encode, Encoding};
|
||||
use objc2::encode::{Encode, Encoding};
|
||||
use objc2::foundation::{NSInteger, NSUInteger};
|
||||
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
platform::ios::{Idiom, ScreenEdge, ValidOrientations},
|
||||
};
|
||||
|
||||
pub type id = *mut Object;
|
||||
pub const nil: id = 0 as id;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
pub type CGFloat = f32;
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
pub type CGFloat = f64;
|
||||
|
||||
pub type NSInteger = isize;
|
||||
pub type NSUInteger = usize;
|
||||
use crate::platform::ios::{Idiom, ScreenEdge};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -28,91 +15,15 @@ pub struct NSOperatingSystemVersion {
|
||||
pub patch: NSInteger,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CGPoint {
|
||||
pub x: CGFloat,
|
||||
pub y: CGFloat,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CGSize {
|
||||
pub width: CGFloat,
|
||||
pub height: CGFloat,
|
||||
}
|
||||
|
||||
impl CGSize {
|
||||
pub fn new(size: LogicalSize<f64>) -> CGSize {
|
||||
CGSize {
|
||||
width: size.width as _,
|
||||
height: size.height as _,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CGRect {
|
||||
pub origin: CGPoint,
|
||||
pub size: CGSize,
|
||||
}
|
||||
|
||||
impl CGRect {
|
||||
pub fn new(origin: CGPoint, size: CGSize) -> CGRect {
|
||||
CGRect { origin, size }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Encode for CGRect {
|
||||
fn encode() -> Encoding {
|
||||
unsafe {
|
||||
if cfg!(target_pointer_width = "32") {
|
||||
Encoding::from_str("{CGRect={CGPoint=ff}{CGSize=ff}}")
|
||||
} else if cfg!(target_pointer_width = "64") {
|
||||
Encoding::from_str("{CGRect={CGPoint=dd}{CGSize=dd}}")
|
||||
} else {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)]
|
||||
pub enum UITouchPhase {
|
||||
Began = 0,
|
||||
Moved,
|
||||
Stationary,
|
||||
Ended,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)]
|
||||
pub enum UIForceTouchCapability {
|
||||
Unknown = 0,
|
||||
Unavailable,
|
||||
Available,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)]
|
||||
pub enum UITouchType {
|
||||
Direct = 0,
|
||||
Indirect,
|
||||
Pencil,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UIEdgeInsets {
|
||||
pub top: CGFloat,
|
||||
pub left: CGFloat,
|
||||
pub bottom: CGFloat,
|
||||
pub right: CGFloat,
|
||||
unsafe impl Encode for NSOperatingSystemVersion {
|
||||
const ENCODING: Encoding = Encoding::Struct(
|
||||
"NSOperatingSystemVersion",
|
||||
&[
|
||||
NSInteger::ENCODING,
|
||||
NSInteger::ENCODING,
|
||||
NSInteger::ENCODING,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
@@ -120,9 +31,7 @@ pub struct UIEdgeInsets {
|
||||
pub struct UIUserInterfaceIdiom(NSInteger);
|
||||
|
||||
unsafe impl Encode for UIUserInterfaceIdiom {
|
||||
fn encode() -> Encoding {
|
||||
NSInteger::encode()
|
||||
}
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
|
||||
impl UIUserInterfaceIdiom {
|
||||
@@ -144,10 +53,9 @@ impl From<Idiom> for UIUserInterfaceIdiom {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Idiom> for UIUserInterfaceIdiom {
|
||||
fn into(self) -> Idiom {
|
||||
match self {
|
||||
impl From<UIUserInterfaceIdiom> for Idiom {
|
||||
fn from(ui_idiom: UIUserInterfaceIdiom) -> Idiom {
|
||||
match ui_idiom {
|
||||
UIUserInterfaceIdiom::Unspecified => Idiom::Unspecified,
|
||||
UIUserInterfaceIdiom::Phone => Idiom::Phone,
|
||||
UIUserInterfaceIdiom::Pad => Idiom::Pad,
|
||||
@@ -158,65 +66,12 @@ impl Into<Idiom> for UIUserInterfaceIdiom {
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct UIInterfaceOrientationMask(NSUInteger);
|
||||
|
||||
unsafe impl Encode for UIInterfaceOrientationMask {
|
||||
fn encode() -> Encoding {
|
||||
NSUInteger::encode()
|
||||
}
|
||||
}
|
||||
|
||||
impl UIInterfaceOrientationMask {
|
||||
pub const Portrait: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 1);
|
||||
pub const PortraitUpsideDown: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 2);
|
||||
pub const LandscapeLeft: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 4);
|
||||
pub const LandscapeRight: UIInterfaceOrientationMask = UIInterfaceOrientationMask(1 << 3);
|
||||
pub const Landscape: UIInterfaceOrientationMask =
|
||||
UIInterfaceOrientationMask(Self::LandscapeLeft.0 | Self::LandscapeRight.0);
|
||||
pub const AllButUpsideDown: UIInterfaceOrientationMask =
|
||||
UIInterfaceOrientationMask(Self::Landscape.0 | Self::Portrait.0);
|
||||
pub const All: UIInterfaceOrientationMask =
|
||||
UIInterfaceOrientationMask(Self::AllButUpsideDown.0 | Self::PortraitUpsideDown.0);
|
||||
}
|
||||
|
||||
impl BitOr for UIInterfaceOrientationMask {
|
||||
type Output = Self;
|
||||
|
||||
fn bitor(self, rhs: Self) -> Self {
|
||||
UIInterfaceOrientationMask(self.0 | rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl UIInterfaceOrientationMask {
|
||||
pub fn from_valid_orientations_idiom(
|
||||
valid_orientations: ValidOrientations,
|
||||
idiom: Idiom,
|
||||
) -> UIInterfaceOrientationMask {
|
||||
match (valid_orientations, idiom) {
|
||||
(ValidOrientations::LandscapeAndPortrait, Idiom::Phone) => {
|
||||
UIInterfaceOrientationMask::AllButUpsideDown
|
||||
}
|
||||
(ValidOrientations::LandscapeAndPortrait, _) => UIInterfaceOrientationMask::All,
|
||||
(ValidOrientations::Landscape, _) => UIInterfaceOrientationMask::Landscape,
|
||||
(ValidOrientations::Portrait, Idiom::Phone) => UIInterfaceOrientationMask::Portrait,
|
||||
(ValidOrientations::Portrait, _) => {
|
||||
UIInterfaceOrientationMask::Portrait
|
||||
| UIInterfaceOrientationMask::PortraitUpsideDown
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct UIRectEdge(NSUInteger);
|
||||
|
||||
unsafe impl Encode for UIRectEdge {
|
||||
fn encode() -> Encoding {
|
||||
NSUInteger::encode()
|
||||
}
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
|
||||
impl From<ScreenEdge> for UIRectEdge {
|
||||
@@ -230,161 +85,9 @@ impl From<ScreenEdge> for UIRectEdge {
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<ScreenEdge> for UIRectEdge {
|
||||
fn into(self) -> ScreenEdge {
|
||||
let bits: u8 = self.0.try_into().expect("invalid `UIRectEdge`");
|
||||
impl From<UIRectEdge> for ScreenEdge {
|
||||
fn from(ui_rect_edge: UIRectEdge) -> ScreenEdge {
|
||||
let bits: u8 = ui_rect_edge.0.try_into().expect("invalid `UIRectEdge`");
|
||||
ScreenEdge::from_bits(bits).expect("invalid `ScreenEdge`")
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct UIScreenOverscanCompensation(NSInteger);
|
||||
|
||||
unsafe impl Encode for UIScreenOverscanCompensation {
|
||||
fn encode() -> Encoding {
|
||||
NSInteger::encode()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl UIScreenOverscanCompensation {
|
||||
pub const Scale: UIScreenOverscanCompensation = UIScreenOverscanCompensation(0);
|
||||
pub const InsetBounds: UIScreenOverscanCompensation = UIScreenOverscanCompensation(1);
|
||||
pub const None: UIScreenOverscanCompensation = UIScreenOverscanCompensation(2);
|
||||
}
|
||||
|
||||
#[link(name = "UIKit", kind = "framework")]
|
||||
#[link(name = "CoreFoundation", kind = "framework")]
|
||||
extern "C" {
|
||||
pub static kCFRunLoopDefaultMode: CFRunLoopMode;
|
||||
pub static kCFRunLoopCommonModes: CFRunLoopMode;
|
||||
|
||||
pub fn UIApplicationMain(
|
||||
argc: c_int,
|
||||
argv: *const c_char,
|
||||
principalClassName: id,
|
||||
delegateClassName: id,
|
||||
) -> c_int;
|
||||
|
||||
pub fn CFRunLoopGetMain() -> CFRunLoopRef;
|
||||
pub fn CFRunLoopWakeUp(rl: CFRunLoopRef);
|
||||
|
||||
pub fn CFRunLoopObserverCreate(
|
||||
allocator: CFAllocatorRef,
|
||||
activities: CFOptionFlags,
|
||||
repeats: Boolean,
|
||||
order: CFIndex,
|
||||
callout: CFRunLoopObserverCallBack,
|
||||
context: *mut CFRunLoopObserverContext,
|
||||
) -> CFRunLoopObserverRef;
|
||||
pub fn CFRunLoopAddObserver(
|
||||
rl: CFRunLoopRef,
|
||||
observer: CFRunLoopObserverRef,
|
||||
mode: CFRunLoopMode,
|
||||
);
|
||||
|
||||
pub fn CFRunLoopTimerCreate(
|
||||
allocator: CFAllocatorRef,
|
||||
fireDate: CFAbsoluteTime,
|
||||
interval: CFTimeInterval,
|
||||
flags: CFOptionFlags,
|
||||
order: CFIndex,
|
||||
callout: CFRunLoopTimerCallBack,
|
||||
context: *mut CFRunLoopTimerContext,
|
||||
) -> CFRunLoopTimerRef;
|
||||
pub fn CFRunLoopAddTimer(rl: CFRunLoopRef, timer: CFRunLoopTimerRef, mode: CFRunLoopMode);
|
||||
pub fn CFRunLoopTimerSetNextFireDate(timer: CFRunLoopTimerRef, fireDate: CFAbsoluteTime);
|
||||
pub fn CFRunLoopTimerInvalidate(time: CFRunLoopTimerRef);
|
||||
|
||||
pub fn CFRunLoopSourceCreate(
|
||||
allocator: CFAllocatorRef,
|
||||
order: CFIndex,
|
||||
context: *mut CFRunLoopSourceContext,
|
||||
) -> CFRunLoopSourceRef;
|
||||
pub fn CFRunLoopAddSource(rl: CFRunLoopRef, source: CFRunLoopSourceRef, mode: CFRunLoopMode);
|
||||
pub fn CFRunLoopSourceInvalidate(source: CFRunLoopSourceRef);
|
||||
pub fn CFRunLoopSourceSignal(source: CFRunLoopSourceRef);
|
||||
|
||||
pub fn CFAbsoluteTimeGetCurrent() -> CFAbsoluteTime;
|
||||
pub fn CFRelease(cftype: *const c_void);
|
||||
}
|
||||
|
||||
pub type Boolean = u8;
|
||||
pub enum CFAllocator {}
|
||||
pub type CFAllocatorRef = *mut CFAllocator;
|
||||
pub enum CFRunLoop {}
|
||||
pub type CFRunLoopRef = *mut CFRunLoop;
|
||||
pub type CFRunLoopMode = CFStringRef;
|
||||
pub enum CFRunLoopObserver {}
|
||||
pub type CFRunLoopObserverRef = *mut CFRunLoopObserver;
|
||||
pub enum CFRunLoopTimer {}
|
||||
pub type CFRunLoopTimerRef = *mut CFRunLoopTimer;
|
||||
pub enum CFRunLoopSource {}
|
||||
pub type CFRunLoopSourceRef = *mut CFRunLoopSource;
|
||||
pub enum CFString {}
|
||||
pub type CFStringRef = *const CFString;
|
||||
|
||||
pub type CFHashCode = c_ulong;
|
||||
pub type CFIndex = c_long;
|
||||
pub type CFOptionFlags = c_ulong;
|
||||
pub type CFRunLoopActivity = CFOptionFlags;
|
||||
|
||||
pub type CFAbsoluteTime = CFTimeInterval;
|
||||
pub type CFTimeInterval = f64;
|
||||
|
||||
pub const kCFRunLoopEntry: CFRunLoopActivity = 0;
|
||||
pub const kCFRunLoopBeforeWaiting: CFRunLoopActivity = 1 << 5;
|
||||
pub const kCFRunLoopAfterWaiting: CFRunLoopActivity = 1 << 6;
|
||||
pub const kCFRunLoopExit: CFRunLoopActivity = 1 << 7;
|
||||
|
||||
pub type CFRunLoopObserverCallBack =
|
||||
extern "C" fn(observer: CFRunLoopObserverRef, activity: CFRunLoopActivity, info: *mut c_void);
|
||||
pub type CFRunLoopTimerCallBack = extern "C" fn(timer: CFRunLoopTimerRef, info: *mut c_void);
|
||||
|
||||
pub enum CFRunLoopObserverContext {}
|
||||
pub enum CFRunLoopTimerContext {}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct CFRunLoopSourceContext {
|
||||
pub version: CFIndex,
|
||||
pub info: *mut c_void,
|
||||
pub retain: Option<extern "C" fn(*const c_void) -> *const c_void>,
|
||||
pub release: Option<extern "C" fn(*const c_void)>,
|
||||
pub copyDescription: Option<extern "C" fn(*const c_void) -> CFStringRef>,
|
||||
pub equal: Option<extern "C" fn(*const c_void, *const c_void) -> Boolean>,
|
||||
pub hash: Option<extern "C" fn(*const c_void) -> CFHashCode>,
|
||||
pub schedule: Option<extern "C" fn(*mut c_void, CFRunLoopRef, CFRunLoopMode)>,
|
||||
pub cancel: Option<extern "C" fn(*mut c_void, CFRunLoopRef, CFRunLoopMode)>,
|
||||
pub perform: Option<extern "C" fn(*mut c_void)>,
|
||||
}
|
||||
|
||||
pub trait NSString: Sized {
|
||||
unsafe fn alloc(_: Self) -> id {
|
||||
msg_send![class!(NSString), alloc]
|
||||
}
|
||||
|
||||
unsafe fn initWithUTF8String_(self, c_string: *const c_char) -> id;
|
||||
unsafe fn stringByAppendingString_(self, other: id) -> id;
|
||||
unsafe fn init_str(self, string: &str) -> Self;
|
||||
unsafe fn UTF8String(self) -> *const c_char;
|
||||
}
|
||||
|
||||
impl NSString for id {
|
||||
unsafe fn initWithUTF8String_(self, c_string: *const c_char) -> id {
|
||||
msg_send![self, initWithUTF8String: c_string as id]
|
||||
}
|
||||
|
||||
unsafe fn stringByAppendingString_(self, other: id) -> id {
|
||||
msg_send![self, stringByAppendingString: other]
|
||||
}
|
||||
|
||||
unsafe fn init_str(self, string: &str) -> id {
|
||||
let cstring = CString::new(string).unwrap();
|
||||
self.initWithUTF8String_(cstring.as_ptr())
|
||||
}
|
||||
|
||||
unsafe fn UTF8String(self) -> *const c_char {
|
||||
msg_send![self, UTF8String]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,14 +55,15 @@
|
||||
//!
|
||||
//! Also note that app may not receive the LoopDestroyed event if suspended; it might be SIGKILL'ed.
|
||||
|
||||
#![cfg(target_os = "ios")]
|
||||
#![cfg(ios_platform)]
|
||||
#![allow(clippy::let_unit_value)]
|
||||
|
||||
// TODO: (mtak-) UIKit requires main thread for virtually all function/method calls. This could be
|
||||
// worked around in the future by using GCD (grand central dispatch) and/or caching of values like
|
||||
// window size/position.
|
||||
macro_rules! assert_main_thread {
|
||||
($($t:tt)*) => {
|
||||
if !msg_send![class!(NSThread), isMainThread] {
|
||||
if !::objc2::foundation::is_main_thread() {
|
||||
panic!($($t)*);
|
||||
}
|
||||
};
|
||||
@@ -72,28 +73,33 @@ mod app_state;
|
||||
mod event_loop;
|
||||
mod ffi;
|
||||
mod monitor;
|
||||
mod uikit;
|
||||
mod view;
|
||||
mod window;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
pub use self::{
|
||||
event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget},
|
||||
pub(crate) use self::{
|
||||
event_loop::{
|
||||
EventLoop, EventLoopProxy, EventLoopWindowTarget, PlatformSpecificEventLoopAttributes,
|
||||
},
|
||||
monitor::{MonitorHandle, VideoMode},
|
||||
window::{PlatformSpecificWindowBuilderAttributes, Window, WindowId},
|
||||
};
|
||||
|
||||
use self::uikit::UIScreen;
|
||||
pub(crate) use crate::icon::NoIcon as PlatformIcon;
|
||||
pub(self) use crate::platform_impl::Fullscreen;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId {
|
||||
uiscreen: ffi::id,
|
||||
uiscreen: *const UIScreen,
|
||||
}
|
||||
|
||||
impl DeviceId {
|
||||
pub unsafe fn dummy() -> Self {
|
||||
pub const unsafe fn dummy() -> Self {
|
||||
DeviceId {
|
||||
uiscreen: std::ptr::null_mut(),
|
||||
uiscreen: std::ptr::null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -101,13 +107,14 @@ impl DeviceId {
|
||||
unsafe impl Send for DeviceId {}
|
||||
unsafe impl Sync for DeviceId {}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct KeyEventExtra {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum OsError {}
|
||||
|
||||
impl fmt::Display for OsError {
|
||||
fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
_ => unreachable!(),
|
||||
}
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "os error")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,90 +1,48 @@
|
||||
#![allow(clippy::unnecessary_cast)]
|
||||
|
||||
use std::{
|
||||
collections::{BTreeSet, VecDeque},
|
||||
fmt,
|
||||
ops::{Deref, DerefMut},
|
||||
};
|
||||
|
||||
use objc2::foundation::{MainThreadMarker, NSInteger};
|
||||
use objc2::rc::{Id, Shared};
|
||||
|
||||
use super::uikit::{UIScreen, UIScreenMode};
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
monitor::{MonitorHandle as RootMonitorHandle, VideoMode as RootVideoMode},
|
||||
platform_impl::platform::{
|
||||
app_state,
|
||||
ffi::{id, nil, CGFloat, CGRect, CGSize, NSInteger, NSUInteger},
|
||||
},
|
||||
monitor::VideoMode as RootVideoMode,
|
||||
platform_impl::platform::app_state,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
// TODO(madsmtm): Remove or refactor this
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
pub(crate) struct ScreenModeSendSync(pub(crate) Id<UIScreenMode, Shared>);
|
||||
|
||||
unsafe impl Send for ScreenModeSendSync {}
|
||||
unsafe impl Sync for ScreenModeSendSync {}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
pub struct VideoMode {
|
||||
pub(crate) size: (u32, u32),
|
||||
pub(crate) bit_depth: u16,
|
||||
pub(crate) refresh_rate: u16,
|
||||
pub(crate) screen_mode: NativeDisplayMode,
|
||||
pub(crate) refresh_rate_millihertz: u32,
|
||||
pub(crate) screen_mode: ScreenModeSendSync,
|
||||
pub(crate) monitor: MonitorHandle,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct NativeDisplayMode(pub id);
|
||||
|
||||
unsafe impl Send for NativeDisplayMode {}
|
||||
|
||||
impl Drop for NativeDisplayMode {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let () = msg_send![self.0, release];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for NativeDisplayMode {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
let _: id = msg_send![self.0, retain];
|
||||
}
|
||||
NativeDisplayMode(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for VideoMode {
|
||||
fn clone(&self) -> VideoMode {
|
||||
VideoMode {
|
||||
size: self.size,
|
||||
bit_depth: self.bit_depth,
|
||||
refresh_rate: self.refresh_rate,
|
||||
screen_mode: self.screen_mode.clone(),
|
||||
monitor: self.monitor.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VideoMode {
|
||||
unsafe fn retained_new(uiscreen: id, screen_mode: id) -> VideoMode {
|
||||
fn new(uiscreen: Id<UIScreen, Shared>, screen_mode: Id<UIScreenMode, Shared>) -> VideoMode {
|
||||
assert_main_thread!("`VideoMode` can only be created on the main thread on iOS");
|
||||
let os_capabilities = app_state::os_capabilities();
|
||||
let refresh_rate: NSInteger = if os_capabilities.maximum_frames_per_second {
|
||||
msg_send![uiscreen, maximumFramesPerSecond]
|
||||
} else {
|
||||
// https://developer.apple.com/library/archive/technotes/tn2460/_index.html
|
||||
// https://en.wikipedia.org/wiki/IPad_Pro#Model_comparison
|
||||
//
|
||||
// All iOS devices support 60 fps, and on devices where `maximumFramesPerSecond` is not
|
||||
// supported, they are all guaranteed to have 60hz refresh rates. This does not
|
||||
// correctly handle external displays. ProMotion displays support 120fps, but they were
|
||||
// introduced at the same time as the `maximumFramesPerSecond` API.
|
||||
//
|
||||
// FIXME: earlier OSs could calculate the refresh rate using
|
||||
// `-[CADisplayLink duration]`.
|
||||
os_capabilities.maximum_frames_per_second_err_msg("defaulting to 60 fps");
|
||||
60
|
||||
};
|
||||
let size: CGSize = msg_send![screen_mode, size];
|
||||
let screen_mode: id = msg_send![screen_mode, retain];
|
||||
let screen_mode = NativeDisplayMode(screen_mode);
|
||||
let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen);
|
||||
let size = screen_mode.size();
|
||||
VideoMode {
|
||||
size: (size.width as u32, size.height as u32),
|
||||
bit_depth: 32,
|
||||
refresh_rate: refresh_rate as u16,
|
||||
screen_mode,
|
||||
monitor: MonitorHandle::retained_new(uiscreen),
|
||||
refresh_rate_millihertz,
|
||||
screen_mode: ScreenModeSendSync(screen_mode),
|
||||
monitor: MonitorHandle::new(uiscreen),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,55 +54,50 @@ impl VideoMode {
|
||||
self.bit_depth
|
||||
}
|
||||
|
||||
pub fn refresh_rate(&self) -> u16 {
|
||||
self.refresh_rate
|
||||
pub fn refresh_rate_millihertz(&self) -> u32 {
|
||||
self.refresh_rate_millihertz
|
||||
}
|
||||
|
||||
pub fn monitor(&self) -> RootMonitorHandle {
|
||||
RootMonitorHandle {
|
||||
inner: self.monitor.clone(),
|
||||
}
|
||||
pub fn monitor(&self) -> MonitorHandle {
|
||||
self.monitor.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Inner {
|
||||
uiscreen: id,
|
||||
uiscreen: Id<UIScreen, Shared>,
|
||||
}
|
||||
|
||||
impl Drop for Inner {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let () = msg_send![self.uiscreen, release];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct MonitorHandle {
|
||||
inner: Inner,
|
||||
}
|
||||
|
||||
impl PartialOrd for MonitorHandle {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for MonitorHandle {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
// TODO: Make a better ordering
|
||||
(self as *const Self).cmp(&(other as *const Self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for MonitorHandle {
|
||||
type Target = Inner;
|
||||
|
||||
fn deref(&self) -> &Inner {
|
||||
unsafe {
|
||||
assert_main_thread!(
|
||||
"`MonitorHandle` methods can only be run on the main thread on iOS"
|
||||
);
|
||||
}
|
||||
assert_main_thread!("`MonitorHandle` methods can only be run on the main thread on iOS");
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for MonitorHandle {
|
||||
fn deref_mut(&mut self) -> &mut Inner {
|
||||
unsafe {
|
||||
assert_main_thread!(
|
||||
"`MonitorHandle` methods can only be run on the main thread on iOS"
|
||||
);
|
||||
}
|
||||
assert_main_thread!("`MonitorHandle` methods can only be run on the main thread on iOS");
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
@@ -152,23 +105,17 @@ impl DerefMut for MonitorHandle {
|
||||
unsafe impl Send for MonitorHandle {}
|
||||
unsafe impl Sync for MonitorHandle {}
|
||||
|
||||
impl Clone for MonitorHandle {
|
||||
fn clone(&self) -> MonitorHandle {
|
||||
MonitorHandle::retained_new(self.uiscreen)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MonitorHandle {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
assert_main_thread!("`MonitorHandle` can only be dropped on the main thread on iOS");
|
||||
}
|
||||
assert_main_thread!("`MonitorHandle` can only be dropped on the main thread on iOS");
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for MonitorHandle {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// TODO: Do this using the proper fmt API
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
struct MonitorHandle {
|
||||
name: Option<String>,
|
||||
size: PhysicalSize<u32>,
|
||||
@@ -188,12 +135,9 @@ impl fmt::Debug for MonitorHandle {
|
||||
}
|
||||
|
||||
impl MonitorHandle {
|
||||
pub fn retained_new(uiscreen: id) -> MonitorHandle {
|
||||
unsafe {
|
||||
assert_main_thread!("`MonitorHandle` can only be cloned on the main thread on iOS");
|
||||
let () = msg_send![uiscreen, retain];
|
||||
}
|
||||
MonitorHandle {
|
||||
pub(crate) fn new(uiscreen: Id<UIScreen, Shared>) -> Self {
|
||||
assert_main_thread!("`MonitorHandle` can only be created on the main thread on iOS");
|
||||
Self {
|
||||
inner: Inner { uiscreen },
|
||||
}
|
||||
}
|
||||
@@ -201,99 +145,102 @@ impl MonitorHandle {
|
||||
|
||||
impl Inner {
|
||||
pub fn name(&self) -> Option<String> {
|
||||
unsafe {
|
||||
let main = main_uiscreen();
|
||||
if self.uiscreen == main.uiscreen {
|
||||
Some("Primary".to_string())
|
||||
} else if self.uiscreen == mirrored_uiscreen(&main).uiscreen {
|
||||
Some("Mirrored".to_string())
|
||||
} else {
|
||||
uiscreens()
|
||||
.iter()
|
||||
.position(|rhs| rhs.uiscreen == self.uiscreen)
|
||||
.map(|idx| idx.to_string())
|
||||
}
|
||||
let main = UIScreen::main(MainThreadMarker::new().unwrap());
|
||||
if self.uiscreen == main {
|
||||
Some("Primary".to_string())
|
||||
} else if self.uiscreen == main.mirroredScreen() {
|
||||
Some("Mirrored".to_string())
|
||||
} else {
|
||||
UIScreen::screens(MainThreadMarker::new().unwrap())
|
||||
.iter()
|
||||
.position(|rhs| rhs == &*self.uiscreen)
|
||||
.map(|idx| idx.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(&self) -> PhysicalSize<u32> {
|
||||
unsafe {
|
||||
let bounds: CGRect = msg_send![self.ui_screen(), nativeBounds];
|
||||
PhysicalSize::new(bounds.size.width as u32, bounds.size.height as u32)
|
||||
}
|
||||
let bounds = self.uiscreen.nativeBounds();
|
||||
PhysicalSize::new(bounds.size.width as u32, bounds.size.height as u32)
|
||||
}
|
||||
|
||||
pub fn position(&self) -> PhysicalPosition<i32> {
|
||||
unsafe {
|
||||
let bounds: CGRect = msg_send![self.ui_screen(), nativeBounds];
|
||||
(bounds.origin.x as f64, bounds.origin.y as f64).into()
|
||||
}
|
||||
let bounds = self.uiscreen.nativeBounds();
|
||||
(bounds.origin.x as f64, bounds.origin.y as f64).into()
|
||||
}
|
||||
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
unsafe {
|
||||
let scale: CGFloat = msg_send![self.ui_screen(), nativeScale];
|
||||
scale as f64
|
||||
}
|
||||
self.uiscreen.nativeScale() as f64
|
||||
}
|
||||
|
||||
pub fn video_modes(&self) -> impl Iterator<Item = RootVideoMode> {
|
||||
let mut modes = BTreeSet::new();
|
||||
unsafe {
|
||||
let available_modes: id = msg_send![self.uiscreen, availableModes];
|
||||
let available_mode_count: NSUInteger = msg_send![available_modes, count];
|
||||
|
||||
for i in 0..available_mode_count {
|
||||
let mode: id = msg_send![available_modes, objectAtIndex: i];
|
||||
modes.insert(RootVideoMode {
|
||||
video_mode: VideoMode::retained_new(self.uiscreen, mode),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
modes.into_iter()
|
||||
pub fn refresh_rate_millihertz(&self) -> Option<u32> {
|
||||
Some(refresh_rate_millihertz(&self.uiscreen))
|
||||
}
|
||||
|
||||
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
|
||||
// Use Ord impl of RootVideoMode
|
||||
let modes: BTreeSet<_> = self
|
||||
.uiscreen
|
||||
.availableModes()
|
||||
.into_iter()
|
||||
.map(|mode| {
|
||||
let mode: *const UIScreenMode = mode;
|
||||
let mode = unsafe { Id::retain(mode as *mut UIScreenMode).unwrap() };
|
||||
|
||||
RootVideoMode {
|
||||
video_mode: VideoMode::new(self.uiscreen.clone(), mode),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
modes.into_iter().map(|mode| mode.video_mode)
|
||||
}
|
||||
}
|
||||
|
||||
fn refresh_rate_millihertz(uiscreen: &UIScreen) -> u32 {
|
||||
let refresh_rate_millihertz: NSInteger = {
|
||||
let os_capabilities = app_state::os_capabilities();
|
||||
if os_capabilities.maximum_frames_per_second {
|
||||
uiscreen.maximumFramesPerSecond()
|
||||
} else {
|
||||
// https://developer.apple.com/library/archive/technotes/tn2460/_index.html
|
||||
// https://en.wikipedia.org/wiki/IPad_Pro#Model_comparison
|
||||
//
|
||||
// All iOS devices support 60 fps, and on devices where `maximumFramesPerSecond` is not
|
||||
// supported, they are all guaranteed to have 60hz refresh rates. This does not
|
||||
// correctly handle external displays. ProMotion displays support 120fps, but they were
|
||||
// introduced at the same time as the `maximumFramesPerSecond` API.
|
||||
//
|
||||
// FIXME: earlier OSs could calculate the refresh rate using
|
||||
// `-[CADisplayLink duration]`.
|
||||
os_capabilities.maximum_frames_per_second_err_msg("defaulting to 60 fps");
|
||||
60
|
||||
}
|
||||
};
|
||||
|
||||
refresh_rate_millihertz as u32 * 1000
|
||||
}
|
||||
|
||||
// MonitorHandleExtIOS
|
||||
impl Inner {
|
||||
pub fn ui_screen(&self) -> id {
|
||||
self.uiscreen
|
||||
pub(crate) fn ui_screen(&self) -> &Id<UIScreen, Shared> {
|
||||
&self.uiscreen
|
||||
}
|
||||
|
||||
pub fn preferred_video_mode(&self) -> RootVideoMode {
|
||||
unsafe {
|
||||
let mode: id = msg_send![self.uiscreen, preferredMode];
|
||||
RootVideoMode {
|
||||
video_mode: VideoMode::retained_new(self.uiscreen, mode),
|
||||
}
|
||||
}
|
||||
pub fn preferred_video_mode(&self) -> VideoMode {
|
||||
VideoMode::new(
|
||||
self.uiscreen.clone(),
|
||||
self.uiscreen.preferredMode().unwrap(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// requires being run on main thread
|
||||
pub unsafe fn main_uiscreen() -> MonitorHandle {
|
||||
let uiscreen: id = msg_send![class!(UIScreen), mainScreen];
|
||||
MonitorHandle::retained_new(uiscreen)
|
||||
}
|
||||
|
||||
// requires being run on main thread
|
||||
unsafe fn mirrored_uiscreen(monitor: &MonitorHandle) -> MonitorHandle {
|
||||
let uiscreen: id = msg_send![monitor.uiscreen, mirroredScreen];
|
||||
MonitorHandle::retained_new(uiscreen)
|
||||
}
|
||||
|
||||
// requires being run on main thread
|
||||
pub unsafe fn uiscreens() -> VecDeque<MonitorHandle> {
|
||||
let screens: id = msg_send![class!(UIScreen), screens];
|
||||
let count: NSUInteger = msg_send![screens, count];
|
||||
let mut result = VecDeque::with_capacity(count as _);
|
||||
let screens_enum: id = msg_send![screens, objectEnumerator];
|
||||
loop {
|
||||
let screen: id = msg_send![screens_enum, nextObject];
|
||||
if screen == nil {
|
||||
break result;
|
||||
}
|
||||
result.push_back(MonitorHandle::retained_new(screen));
|
||||
}
|
||||
pub fn uiscreens(mtm: MainThreadMarker) -> VecDeque<MonitorHandle> {
|
||||
UIScreen::screens(mtm)
|
||||
.into_iter()
|
||||
.map(|screen| {
|
||||
let screen: *const UIScreen = screen;
|
||||
let screen = unsafe { Id::retain(screen as *mut UIScreen).unwrap() };
|
||||
MonitorHandle::new(screen)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
30
src/platform_impl/ios/uikit/application.rs
Normal file
30
src/platform_impl/ios/uikit/application.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use objc2::foundation::{CGRect, MainThreadMarker, NSArray, NSObject};
|
||||
use objc2::rc::{Id, Shared};
|
||||
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
|
||||
|
||||
use super::{UIResponder, UIWindow};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct UIApplication;
|
||||
|
||||
unsafe impl ClassType for UIApplication {
|
||||
#[inherits(NSObject)]
|
||||
type Super = UIResponder;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl UIApplication {
|
||||
pub fn shared(_mtm: MainThreadMarker) -> Option<Id<Self, Shared>> {
|
||||
unsafe { msg_send_id![Self::class(), sharedApplication] }
|
||||
}
|
||||
|
||||
pub fn windows(&self) -> Id<NSArray<UIWindow, Shared>, Shared> {
|
||||
unsafe { msg_send_id![self, windows] }
|
||||
}
|
||||
|
||||
#[sel(statusBarFrame)]
|
||||
pub fn statusBarFrame(&self) -> CGRect;
|
||||
}
|
||||
);
|
||||
11
src/platform_impl/ios/uikit/coordinate_space.rs
Normal file
11
src/platform_impl/ios/uikit/coordinate_space.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
use objc2::foundation::NSObject;
|
||||
use objc2::{extern_class, ClassType};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct UICoordinateSpace;
|
||||
|
||||
unsafe impl ClassType for UICoordinateSpace {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
25
src/platform_impl/ios/uikit/device.rs
Normal file
25
src/platform_impl/ios/uikit/device.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use objc2::foundation::{MainThreadMarker, NSObject};
|
||||
use objc2::rc::{Id, Shared};
|
||||
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
|
||||
|
||||
use super::super::ffi::UIUserInterfaceIdiom;
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct UIDevice;
|
||||
|
||||
unsafe impl ClassType for UIDevice {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl UIDevice {
|
||||
pub fn current(_mtm: MainThreadMarker) -> Id<Self, Shared> {
|
||||
unsafe { msg_send_id![Self::class(), currentDevice] }
|
||||
}
|
||||
|
||||
#[sel(userInterfaceIdiom)]
|
||||
pub fn userInterfaceIdiom(&self) -> UIUserInterfaceIdiom;
|
||||
}
|
||||
);
|
||||
11
src/platform_impl/ios/uikit/event.rs
Normal file
11
src/platform_impl/ios/uikit/event.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
use objc2::foundation::NSObject;
|
||||
use objc2::{extern_class, ClassType};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct UIEvent;
|
||||
|
||||
unsafe impl ClassType for UIEvent {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
44
src/platform_impl/ios/uikit/mod.rs
Normal file
44
src/platform_impl/ios/uikit/mod.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
use objc2::foundation::NSString;
|
||||
|
||||
mod application;
|
||||
mod coordinate_space;
|
||||
mod device;
|
||||
mod event;
|
||||
mod responder;
|
||||
mod screen;
|
||||
mod screen_mode;
|
||||
mod touch;
|
||||
mod trait_collection;
|
||||
mod view;
|
||||
mod view_controller;
|
||||
mod window;
|
||||
|
||||
pub(crate) use self::application::UIApplication;
|
||||
pub(crate) use self::coordinate_space::UICoordinateSpace;
|
||||
pub(crate) use self::device::UIDevice;
|
||||
pub(crate) use self::event::UIEvent;
|
||||
pub(crate) use self::responder::UIResponder;
|
||||
pub(crate) use self::screen::{UIScreen, UIScreenOverscanCompensation};
|
||||
pub(crate) use self::screen_mode::UIScreenMode;
|
||||
pub(crate) use self::touch::{UITouch, UITouchPhase, UITouchType};
|
||||
pub(crate) use self::trait_collection::{UIForceTouchCapability, UITraitCollection};
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use self::view::{UIEdgeInsets, UIView};
|
||||
pub(crate) use self::view_controller::{UIInterfaceOrientationMask, UIViewController};
|
||||
pub(crate) use self::window::UIWindow;
|
||||
|
||||
#[link(name = "UIKit", kind = "framework")]
|
||||
extern "C" {
|
||||
pub fn UIApplicationMain(
|
||||
argc: c_int,
|
||||
argv: *const c_char,
|
||||
principalClassName: Option<&NSString>,
|
||||
delegateClassName: Option<&NSString>,
|
||||
) -> c_int;
|
||||
}
|
||||
11
src/platform_impl/ios/uikit/responder.rs
Normal file
11
src/platform_impl/ios/uikit/responder.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
use objc2::foundation::NSObject;
|
||||
use objc2::{extern_class, ClassType};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct UIResponder;
|
||||
|
||||
unsafe impl ClassType for UIResponder {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
79
src/platform_impl/ios/uikit/screen.rs
Normal file
79
src/platform_impl/ios/uikit/screen.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use objc2::encode::{Encode, Encoding};
|
||||
use objc2::foundation::{CGFloat, CGRect, MainThreadMarker, NSArray, NSInteger, NSObject};
|
||||
use objc2::rc::{Id, Shared};
|
||||
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
|
||||
|
||||
use super::{UICoordinateSpace, UIScreenMode};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct UIScreen;
|
||||
|
||||
unsafe impl ClassType for UIScreen {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl UIScreen {
|
||||
pub fn main(_mtm: MainThreadMarker) -> Id<Self, Shared> {
|
||||
unsafe { msg_send_id![Self::class(), mainScreen] }
|
||||
}
|
||||
|
||||
pub fn screens(_mtm: MainThreadMarker) -> Id<NSArray<Self, Shared>, Shared> {
|
||||
unsafe { msg_send_id![Self::class(), screens] }
|
||||
}
|
||||
|
||||
#[sel(bounds)]
|
||||
pub fn bounds(&self) -> CGRect;
|
||||
|
||||
#[sel(scale)]
|
||||
pub fn scale(&self) -> CGFloat;
|
||||
|
||||
#[sel(nativeBounds)]
|
||||
pub fn nativeBounds(&self) -> CGRect;
|
||||
|
||||
#[sel(nativeScale)]
|
||||
pub fn nativeScale(&self) -> CGFloat;
|
||||
|
||||
#[sel(maximumFramesPerSecond)]
|
||||
pub fn maximumFramesPerSecond(&self) -> NSInteger;
|
||||
|
||||
pub fn mirroredScreen(&self) -> Id<Self, Shared> {
|
||||
unsafe { msg_send_id![Self::class(), mirroredScreen] }
|
||||
}
|
||||
|
||||
pub fn preferredMode(&self) -> Option<Id<UIScreenMode, Shared>> {
|
||||
unsafe { msg_send_id![self, preferredMode] }
|
||||
}
|
||||
|
||||
#[sel(setCurrentMode:)]
|
||||
pub fn setCurrentMode(&self, mode: Option<&UIScreenMode>);
|
||||
|
||||
pub fn availableModes(&self) -> Id<NSArray<UIScreenMode, Shared>, Shared> {
|
||||
unsafe { msg_send_id![self, availableModes] }
|
||||
}
|
||||
|
||||
#[sel(setOverscanCompensation:)]
|
||||
pub fn setOverscanCompensation(&self, overscanCompensation: UIScreenOverscanCompensation);
|
||||
|
||||
pub fn coordinateSpace(&self) -> Id<UICoordinateSpace, Shared> {
|
||||
unsafe { msg_send_id![self, coordinateSpace] }
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct UIScreenOverscanCompensation(NSInteger);
|
||||
|
||||
unsafe impl Encode for UIScreenOverscanCompensation {
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl UIScreenOverscanCompensation {
|
||||
pub const Scale: Self = Self(0);
|
||||
pub const InsetBounds: Self = Self(1);
|
||||
pub const None: Self = Self(2);
|
||||
}
|
||||
18
src/platform_impl/ios/uikit/screen_mode.rs
Normal file
18
src/platform_impl/ios/uikit/screen_mode.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
use objc2::foundation::{CGSize, NSObject};
|
||||
use objc2::{extern_class, extern_methods, ClassType};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct UIScreenMode;
|
||||
|
||||
unsafe impl ClassType for UIScreenMode {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl UIScreenMode {
|
||||
#[sel(size)]
|
||||
pub fn size(&self) -> CGSize;
|
||||
}
|
||||
);
|
||||
64
src/platform_impl/ios/uikit/touch.rs
Normal file
64
src/platform_impl/ios/uikit/touch.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
use objc2::encode::{Encode, Encoding};
|
||||
use objc2::foundation::{CGFloat, CGPoint, NSInteger, NSObject};
|
||||
use objc2::{extern_class, extern_methods, ClassType};
|
||||
|
||||
use super::UIView;
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct UITouch;
|
||||
|
||||
unsafe impl ClassType for UITouch {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl UITouch {
|
||||
#[sel(locationInView:)]
|
||||
pub fn locationInView(&self, view: Option<&UIView>) -> CGPoint;
|
||||
|
||||
#[sel(type)]
|
||||
pub fn type_(&self) -> UITouchType;
|
||||
|
||||
#[sel(force)]
|
||||
pub fn force(&self) -> CGFloat;
|
||||
|
||||
#[sel(maximumPossibleForce)]
|
||||
pub fn maximumPossibleForce(&self) -> CGFloat;
|
||||
|
||||
#[sel(altitudeAngle)]
|
||||
pub fn altitudeAngle(&self) -> CGFloat;
|
||||
|
||||
#[sel(phase)]
|
||||
pub fn phase(&self) -> UITouchPhase;
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)]
|
||||
pub enum UITouchType {
|
||||
Direct = 0,
|
||||
Indirect,
|
||||
Pencil,
|
||||
}
|
||||
|
||||
unsafe impl Encode for UITouchType {
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)]
|
||||
pub enum UITouchPhase {
|
||||
Began = 0,
|
||||
Moved,
|
||||
Stationary,
|
||||
Ended,
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
unsafe impl Encode for UITouchPhase {
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
32
src/platform_impl/ios/uikit/trait_collection.rs
Normal file
32
src/platform_impl/ios/uikit/trait_collection.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use objc2::encode::{Encode, Encoding};
|
||||
use objc2::foundation::{NSInteger, NSObject};
|
||||
use objc2::{extern_class, extern_methods, ClassType};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct UITraitCollection;
|
||||
|
||||
unsafe impl ClassType for UITraitCollection {
|
||||
type Super = NSObject;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl UITraitCollection {
|
||||
#[sel(forceTouchCapability)]
|
||||
pub fn forceTouchCapability(&self) -> UIForceTouchCapability;
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[allow(dead_code)]
|
||||
#[repr(isize)]
|
||||
pub enum UIForceTouchCapability {
|
||||
Unknown = 0,
|
||||
Unavailable,
|
||||
Available,
|
||||
}
|
||||
|
||||
unsafe impl Encode for UIForceTouchCapability {
|
||||
const ENCODING: Encoding = NSInteger::ENCODING;
|
||||
}
|
||||
89
src/platform_impl/ios/uikit/view.rs
Normal file
89
src/platform_impl/ios/uikit/view.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
use objc2::encode::{Encode, Encoding};
|
||||
use objc2::foundation::{CGFloat, CGRect, NSObject};
|
||||
use objc2::rc::{Id, Shared};
|
||||
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
|
||||
|
||||
use super::{UICoordinateSpace, UIResponder, UIViewController};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct UIView;
|
||||
|
||||
unsafe impl ClassType for UIView {
|
||||
#[inherits(NSObject)]
|
||||
type Super = UIResponder;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl UIView {
|
||||
#[sel(bounds)]
|
||||
pub fn bounds(&self) -> CGRect;
|
||||
|
||||
#[sel(setBounds:)]
|
||||
pub fn setBounds(&self, value: CGRect);
|
||||
|
||||
#[sel(frame)]
|
||||
pub fn frame(&self) -> CGRect;
|
||||
|
||||
#[sel(setFrame:)]
|
||||
pub fn setFrame(&self, value: CGRect);
|
||||
|
||||
#[sel(contentScaleFactor)]
|
||||
pub fn contentScaleFactor(&self) -> CGFloat;
|
||||
|
||||
#[sel(setContentScaleFactor:)]
|
||||
pub fn setContentScaleFactor(&self, val: CGFloat);
|
||||
|
||||
#[sel(setMultipleTouchEnabled:)]
|
||||
pub fn setMultipleTouchEnabled(&self, val: bool);
|
||||
|
||||
pub fn rootViewController(&self) -> Option<Id<UIViewController, Shared>> {
|
||||
unsafe { msg_send_id![self, rootViewController] }
|
||||
}
|
||||
|
||||
#[sel(setRootViewController:)]
|
||||
pub fn setRootViewController(&self, rootViewController: Option<&UIViewController>);
|
||||
|
||||
#[sel(convertRect:toCoordinateSpace:)]
|
||||
pub fn convertRect_toCoordinateSpace(
|
||||
&self,
|
||||
rect: CGRect,
|
||||
coordinateSpace: &UICoordinateSpace,
|
||||
) -> CGRect;
|
||||
|
||||
#[sel(convertRect:fromCoordinateSpace:)]
|
||||
pub fn convertRect_fromCoordinateSpace(
|
||||
&self,
|
||||
rect: CGRect,
|
||||
coordinateSpace: &UICoordinateSpace,
|
||||
) -> CGRect;
|
||||
|
||||
#[sel(safeAreaInsets)]
|
||||
pub fn safeAreaInsets(&self) -> UIEdgeInsets;
|
||||
|
||||
#[sel(setNeedsDisplay)]
|
||||
pub fn setNeedsDisplay(&self);
|
||||
}
|
||||
);
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UIEdgeInsets {
|
||||
pub top: CGFloat,
|
||||
pub left: CGFloat,
|
||||
pub bottom: CGFloat,
|
||||
pub right: CGFloat,
|
||||
}
|
||||
|
||||
unsafe impl Encode for UIEdgeInsets {
|
||||
const ENCODING: Encoding = Encoding::Struct(
|
||||
"UIEdgeInsets",
|
||||
&[
|
||||
CGFloat::ENCODING,
|
||||
CGFloat::ENCODING,
|
||||
CGFloat::ENCODING,
|
||||
CGFloat::ENCODING,
|
||||
],
|
||||
);
|
||||
}
|
||||
56
src/platform_impl/ios/uikit/view_controller.rs
Normal file
56
src/platform_impl/ios/uikit/view_controller.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use objc2::encode::{Encode, Encoding};
|
||||
use objc2::foundation::{NSObject, NSUInteger};
|
||||
use objc2::rc::{Id, Shared};
|
||||
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
|
||||
|
||||
use super::{UIResponder, UIView};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct UIViewController;
|
||||
|
||||
unsafe impl ClassType for UIViewController {
|
||||
#[inherits(NSObject)]
|
||||
type Super = UIResponder;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl UIViewController {
|
||||
#[sel(attemptRotationToDeviceOrientation)]
|
||||
pub fn attemptRotationToDeviceOrientation();
|
||||
|
||||
#[sel(setNeedsStatusBarAppearanceUpdate)]
|
||||
pub fn setNeedsStatusBarAppearanceUpdate(&self);
|
||||
|
||||
#[sel(setNeedsUpdateOfHomeIndicatorAutoHidden)]
|
||||
pub fn setNeedsUpdateOfHomeIndicatorAutoHidden(&self);
|
||||
|
||||
#[sel(setNeedsUpdateOfScreenEdgesDeferringSystemGestures)]
|
||||
pub fn setNeedsUpdateOfScreenEdgesDeferringSystemGestures(&self);
|
||||
|
||||
pub fn view(&self) -> Option<Id<UIView, Shared>> {
|
||||
unsafe { msg_send_id![self, view] }
|
||||
}
|
||||
|
||||
#[sel(setView:)]
|
||||
pub fn setView(&self, view: Option<&UIView>);
|
||||
}
|
||||
);
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct UIInterfaceOrientationMask: NSUInteger {
|
||||
const Portrait = 1 << 1;
|
||||
const PortraitUpsideDown = 1 << 2;
|
||||
const LandscapeRight = 1 << 3;
|
||||
const LandscapeLeft = 1 << 4;
|
||||
const Landscape = Self::LandscapeLeft.bits() | Self::LandscapeRight.bits();
|
||||
const AllButUpsideDown = Self::Landscape.bits() | Self::Portrait.bits();
|
||||
const All = Self::AllButUpsideDown.bits() | Self::PortraitUpsideDown.bits();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Encode for UIInterfaceOrientationMask {
|
||||
const ENCODING: Encoding = NSUInteger::ENCODING;
|
||||
}
|
||||
35
src/platform_impl/ios/uikit/window.rs
Normal file
35
src/platform_impl/ios/uikit/window.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use objc2::foundation::NSObject;
|
||||
use objc2::rc::{Id, Shared};
|
||||
use objc2::{extern_class, extern_methods, msg_send_id, ClassType};
|
||||
|
||||
use super::{UIResponder, UIScreen, UIView};
|
||||
|
||||
extern_class!(
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct UIWindow;
|
||||
|
||||
unsafe impl ClassType for UIWindow {
|
||||
#[inherits(UIResponder, NSObject)]
|
||||
type Super = UIView;
|
||||
}
|
||||
);
|
||||
|
||||
extern_methods!(
|
||||
unsafe impl UIWindow {
|
||||
pub fn screen(&self) -> Id<UIScreen, Shared> {
|
||||
unsafe { msg_send_id![self, screen] }
|
||||
}
|
||||
|
||||
#[sel(setScreen:)]
|
||||
pub fn setScreen(&self, screen: &UIScreen);
|
||||
|
||||
#[sel(setHidden:)]
|
||||
pub fn setHidden(&self, flag: bool);
|
||||
|
||||
#[sel(makeKeyAndVisible)]
|
||||
pub fn makeKeyAndVisible(&self);
|
||||
|
||||
#[sel(isKeyWindow)]
|
||||
pub fn isKeyWindow(&self) -> bool;
|
||||
}
|
||||
);
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user