mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
732 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
7a9c17a520 | ||
|
|
b208daa271 | ||
|
|
e85a80dd65 | ||
|
|
b1d8ce24e9 | ||
|
|
098fd5d602 | ||
|
|
2f27f64cdb | ||
|
|
cbb60d29a2 | ||
|
|
e707052f66 | ||
|
|
71bd6e73ca | ||
|
|
b8326f6452 | ||
|
|
ece2e70a53 | ||
|
|
2b14ec23d5 | ||
|
|
9999f53329 | ||
|
|
522a6e3298 | ||
|
|
76d0dd7ec3 | ||
|
|
d1073dcecb | ||
|
|
e88e8bc194 | ||
|
|
bc29931434 | ||
|
|
505f312d5f | ||
|
|
f0093d3c54 | ||
|
|
83b60beba6 | ||
|
|
5f52d7c9d0 | ||
|
|
a1b65f7080 | ||
|
|
96df858961 | ||
|
|
4eddd1e5bc | ||
|
|
28f0eb598d | ||
|
|
c1eb7f9629 | ||
|
|
2f8aa5c52a | ||
|
|
22dcc19898 | ||
|
|
e295104199 | ||
|
|
66fe69edd9 | ||
|
|
fd946feac4 | ||
|
|
8856b6ecb7 | ||
|
|
0ae78db6cb | ||
|
|
3e3bb8a8f1 | ||
|
|
e48262a797 | ||
|
|
d934f94704 | ||
|
|
1fe4a7a4ea | ||
|
|
9daa0738a9 | ||
|
|
ad7d4939a8 | ||
|
|
c4d07952cb | ||
|
|
dc302b0db4 | ||
|
|
a6d180cefb | ||
|
|
1ddceeb063 | ||
|
|
633d0deeae | ||
|
|
9e3844ddd9 | ||
|
|
4b618bd6a6 | ||
|
|
09c4ed0694 | ||
|
|
d15eb04f9e | ||
|
|
02ac7456e4 | ||
|
|
6b0875728c | ||
|
|
6a330a2894 | ||
|
|
627a127f1b | ||
|
|
ec1ae68cfc | ||
|
|
3aa3880e69 | ||
|
|
a1b8d265d0 | ||
|
|
9b122c3804 | ||
|
|
28b82fb9aa | ||
|
|
7753bbba94 | ||
|
|
ac69a9c0dc | ||
|
|
d29f7f34aa | ||
|
|
85ea3f1d5d | ||
|
|
55166da437 | ||
|
|
777d9edeaa | ||
|
|
28a20aec10 | ||
|
|
3a1e694c2f | ||
|
|
b16042a047 | ||
|
|
cbf61e5cb9 | ||
|
|
077ee4d851 | ||
|
|
7b43b0bc94 | ||
|
|
6bb7db7c11 | ||
|
|
6ffd78767f | ||
|
|
f379d069b9 | ||
|
|
2da24089de | ||
|
|
57f29aa6d7 | ||
|
|
028d3ec16d | ||
|
|
d1c6506865 | ||
|
|
c0b46a03b5 | ||
|
|
114c18e70d | ||
|
|
7367b8be6c | ||
|
|
dd768fe655 | ||
|
|
d9bda3e985 | ||
|
|
fa7a3025ec | ||
|
|
e4451d6786 | ||
|
|
468b6b83ec | ||
|
|
8a3a32f286 | ||
|
|
20e81695ca | ||
|
|
027c52171d | ||
|
|
cc206d31b7 | ||
|
|
5d99316c96 | ||
|
|
d59eec4633 | ||
|
|
25e018d1ce | ||
|
|
25123bed23 | ||
|
|
a8d6db0fc1 | ||
|
|
8a9a9cd92d | ||
|
|
530ff5420b | ||
|
|
133b11fa6d | ||
|
|
5b489284e4 | ||
|
|
cdc32eb817 | ||
|
|
eb38ff453a | ||
|
|
8eb7853a1a | ||
|
|
0c151f9fb3 | ||
|
|
c10c820311 | ||
|
|
82889e2367 | ||
|
|
92741aa4ec | ||
|
|
2f352ca5cf | ||
|
|
38c8cb9f4a | ||
|
|
73248bdced | ||
|
|
01203b247b | ||
|
|
3e1d169160 | ||
|
|
c1b93fc3d0 | ||
|
|
1f81e5c872 | ||
|
|
e5291c9e28 | ||
|
|
35505a3114 | ||
|
|
830d47a5f7 | ||
|
|
1a514dff38 | ||
|
|
2888d5c6cf | ||
|
|
400f75a2b3 | ||
|
|
07bdd3e218 | ||
|
|
35a11ae24f | ||
|
|
3d28283a81 | ||
|
|
aec5a9fa09 | ||
|
|
0f94f62025 | ||
|
|
a95ebc5ee6 | ||
|
|
a70ac1531e | ||
|
|
b6e8dd0d8a | ||
|
|
af80ce842d | ||
|
|
08bae037f0 | ||
|
|
cd39327ea2 | ||
|
|
9828f368d6 | ||
|
|
1ed15c7ec7 | ||
|
|
c66784995d | ||
|
|
dba21c06ed | ||
|
|
72fc6a74ec | ||
|
|
f916311744 | ||
|
|
05a1f4280c | ||
|
|
6608a0241d | ||
|
|
429bbfade0 | ||
|
|
28e3c35547 | ||
|
|
af3ef52252 | ||
|
|
2b5f9c52a6 | ||
|
|
5631cc2528 | ||
|
|
de33a92a1b | ||
|
|
1c6353aa3a | ||
|
|
08cb950226 | ||
|
|
765225d918 | ||
|
|
35bc65f6fa | ||
|
|
676268d461 | ||
|
|
34dce8069f | ||
|
|
f62bb33317 | ||
|
|
a557b3cfb6 | ||
|
|
3ff4834bd5 | ||
|
|
bedb889693 | ||
|
|
157ca9cd17 | ||
|
|
6f5e7e170c | ||
|
|
df7571b369 | ||
|
|
d69e41eba8 | ||
|
|
2d41a7d1b0 | ||
|
|
42e0ccfa1c | ||
|
|
55640a91ae | ||
|
|
4f6ca8792c | ||
|
|
5ced36e319 | ||
|
|
237e7ee2e6 | ||
|
|
02f281569d | ||
|
|
ab4d971c5e | ||
|
|
e21df5831e | ||
|
|
18a0119b06 | ||
|
|
34348435fd | ||
|
|
cf3b0f3b70 | ||
|
|
a336e9e959 | ||
|
|
ea93a0130d | ||
|
|
dcd9ddde50 | ||
|
|
86bafdc104 | ||
|
|
6732fa731d | ||
|
|
8cea3e262b | ||
|
|
3e8669ea7f | ||
|
|
7df040f451 | ||
|
|
472eddcc1b | ||
|
|
c0a7900341 | ||
|
|
28a50817af | ||
|
|
2c47c43f47 | ||
|
|
2ef39651eb | ||
|
|
eb20612d77 | ||
|
|
695547f4ca | ||
|
|
d35ee0d580 | ||
|
|
95581ab92f | ||
|
|
3716f13d8e | ||
|
|
c03ef852a4 | ||
|
|
28a5feef28 | ||
|
|
57a53bda74 | ||
|
|
36f4eccb5c | ||
|
|
b6de19e92e | ||
|
|
e87bc3db20 | ||
|
|
a3739d6bad | ||
|
|
068d114740 | ||
|
|
3273c14dea | ||
|
|
206c3c246c | ||
|
|
bfcd85ab15 | ||
|
|
c99bba1655 | ||
|
|
1e7376847b | ||
|
|
b03e589987 | ||
|
|
0b497b62d8 | ||
|
|
f085b7349c | ||
|
|
dd99b3bd73 | ||
|
|
f53683f01f | ||
|
|
7b707e7d75 | ||
|
|
31110be396 | ||
|
|
604016d69d | ||
|
|
7ee9d5639b | ||
|
|
1aab328e2a | ||
|
|
1366dc326a | ||
|
|
8e73287646 | ||
|
|
7eed52a97a | ||
|
|
31ada5a052 | ||
|
|
30b4f8dc9f | ||
|
|
dbdde3d781 | ||
|
|
cf0b8babbd | ||
|
|
c0c22c8ff1 | ||
|
|
73cf10e4f3 | ||
|
|
8a1c5277eb | ||
|
|
1e4c176506 | ||
|
|
3c27e7d88f | ||
|
|
5bc3cf18d9 | ||
|
|
131e67ddc1 | ||
|
|
e5ba79db04 | ||
|
|
f4e9bf51db | ||
|
|
03f9e8fce0 | ||
|
|
4ae9900363 | ||
|
|
454d4190b7 | ||
|
|
a28b60578d | ||
|
|
5a206de620 | ||
|
|
b547531499 | ||
|
|
39e668ffb0 | ||
|
|
bd1ac6cb1e | ||
|
|
8567758156 | ||
|
|
e897d70733 | ||
|
|
fe12996382 | ||
|
|
e8e4d4ce66 | ||
|
|
44af4f4f52 | ||
|
|
7daf146801 | ||
|
|
7b23d190b1 | ||
|
|
17b8310517 | ||
|
|
5ca828d445 | ||
|
|
1ea29b4de0 | ||
|
|
b00cdadb5b | ||
|
|
53e646dabc | ||
|
|
613fafdfdf | ||
|
|
5d0bc5f607 | ||
|
|
ce5cf97e17 | ||
|
|
3ee59696e5 | ||
|
|
f5c624bcd6 | ||
|
|
026b331ba5 | ||
|
|
93c36ccf78 | ||
|
|
c1f314ccdc | ||
|
|
53a89f28a0 | ||
|
|
f874d76289 | ||
|
|
76645f3b5a | ||
|
|
28775be115 | ||
|
|
7d3ff3d2d9 | ||
|
|
4a5d639d74 | ||
|
|
74a7cf55ea | ||
|
|
9393b14b01 | ||
|
|
f8bd671073 | ||
|
|
2af753f307 | ||
|
|
5cc84f32db | ||
|
|
e89674d337 | ||
|
|
de120280e3 | ||
|
|
7f2ba0ee3e | ||
|
|
5bf303fd26 | ||
|
|
e37e46b155 | ||
|
|
b8192ef6f6 | ||
|
|
23354cf1a5 | ||
|
|
dd38fab2f3 | ||
|
|
ac08601b40 | ||
|
|
ea73dac753 | ||
|
|
2a35646520 | ||
|
|
bb285984da | ||
|
|
34db2d7d4c | ||
|
|
0e20973bdb | ||
|
|
29e2481597 | ||
|
|
1596cc5d9e | ||
|
|
8f66d96915 | ||
|
|
8ad078b964 | ||
|
|
d5368d7979 | ||
|
|
9c5657b86c | ||
|
|
b79089ea57 | ||
|
|
3555de114a | ||
|
|
c5703eb00a | ||
|
|
dbe6a1bcdf | ||
|
|
eea9530f38 | ||
|
|
a195ce8146 | ||
|
|
a0f280e71f | ||
|
|
cf28751ae3 | ||
|
|
9dd15d00d8 | ||
|
|
2442305bb7 | ||
|
|
063648368d | ||
|
|
918b2efce7 | ||
|
|
2467a997f4 | ||
|
|
f457c6a0b8 | ||
|
|
1193cada46 | ||
|
|
b0e09b8ffe | ||
|
|
8d6e8bb8d1 | ||
|
|
e2c84725de | ||
|
|
b571362bf1 | ||
|
|
b1b5aefc4b | ||
|
|
06244dd492 | ||
|
|
de2f0740f7 | ||
|
|
c56a66cb90 | ||
|
|
c1329ff156 | ||
|
|
72509b5b42 | ||
|
|
c35fdc8d61 | ||
|
|
5d31f73302 | ||
|
|
7de1261555 | ||
|
|
91a511ba8c | ||
|
|
94f6294c0a | ||
|
|
77cd3adb01 | ||
|
|
7dabad4d71 | ||
|
|
54b4074369 | ||
|
|
e4d8e22846 | ||
|
|
403dcc02f4 | ||
|
|
64be6e5c5e | ||
|
|
c661006683 | ||
|
|
f879bca21c | ||
|
|
182beb4f8b | ||
|
|
2690306f4a | ||
|
|
b59e3c670b | ||
|
|
2e0bbc091f | ||
|
|
91f05e940f | ||
|
|
db794b976c | ||
|
|
9a11f90a02 | ||
|
|
412516159f | ||
|
|
65587ef43a | ||
|
|
cf713bef31 | ||
|
|
ea5c21950c | ||
|
|
47b5dfa034 | ||
|
|
2b89ddec15 | ||
|
|
f256ff7d58 | ||
|
|
07356b9634 | ||
|
|
f2b6ef2edd | ||
|
|
1409f83fb9 | ||
|
|
37dadab745 | ||
|
|
0eefa3ba42 | ||
|
|
08f8f89702 | ||
|
|
0df436901a | ||
|
|
ae63fbdbbb | ||
|
|
4f29618c73 | ||
|
|
76660f3621 | ||
|
|
3a7350cbb9 | ||
|
|
e5aa906b01 | ||
|
|
4b0162a013 | ||
|
|
5be88e79b0 | ||
|
|
93502e0cda | ||
|
|
8d6a857ba5 | ||
|
|
6ff1370035 | ||
|
|
d5391686ae | ||
|
|
70c7382a09 | ||
|
|
9f801cf79e | ||
|
|
062e0e52ee | ||
|
|
fa99b9ff5a | ||
|
|
8f6e80917f | ||
|
|
e087ebd1c7 | ||
|
|
0d3e75d6b0 | ||
|
|
873f7bcec7 | ||
|
|
e579a03035 | ||
|
|
94f998af0a | ||
|
|
fe5e300062 | ||
|
|
2253565db5 | ||
|
|
2ead1c1c59 | ||
|
|
746e99c958 | ||
|
|
47194b5f3c | ||
|
|
4515b77aa5 | ||
|
|
20b09c4514 | ||
|
|
9874181ccd | ||
|
|
cb93554938 | ||
|
|
6b7bd32c8e | ||
|
|
17a240cd43 | ||
|
|
fc481b6d6d | ||
|
|
09182dc093 | ||
|
|
b682c3dfb5 | ||
|
|
ab0a34012f | ||
|
|
f000b82d74 | ||
|
|
dad8de82fa | ||
|
|
3fea477bfd | ||
|
|
f7d7acb3c5 | ||
|
|
9e25561edf | ||
|
|
7c6bdcc459 | ||
|
|
b09629f1d4 | ||
|
|
85446d81f3 | ||
|
|
96786bbb87 | ||
|
|
a5166baba2 | ||
|
|
d1deba8620 | ||
|
|
aaee72422a | ||
|
|
3dd0e31cc4 | ||
|
|
283a8dec37 | ||
|
|
37d354cf7f | ||
|
|
f698d451df | ||
|
|
c088f8bd03 | ||
|
|
3cd40ef655 | ||
|
|
6513351e0c | ||
|
|
f44e98ddc9 | ||
|
|
fd4db4000c | ||
|
|
9602716ed2 | ||
|
|
7be1d16263 | ||
|
|
c91dfdd6fe | ||
|
|
8e733543cd | ||
|
|
26e37590e8 | ||
|
|
ddf133dd66 | ||
|
|
4584e7629a | ||
|
|
139686ddce | ||
|
|
5a0b4dba47 | ||
|
|
33c8aa660f | ||
|
|
cb76dcae2a | ||
|
|
d622de4797 | ||
|
|
9ae75c0c03 | ||
|
|
45a4281413 | ||
|
|
bfbcab3a01 | ||
|
|
4b4c73cee4 | ||
|
|
fd349f1822 | ||
|
|
cb0a085968 | ||
|
|
aabf0e13b7 | ||
|
|
92873b06ed | ||
|
|
04ca2cf9f4 | ||
|
|
b049a4dc66 | ||
|
|
5be52c9753 | ||
|
|
3c59283b3f | ||
|
|
3ba808e3c6 | ||
|
|
df5d66b5e8 | ||
|
|
7fe90e6c80 | ||
|
|
8dcd514393 | ||
|
|
2c3e420f82 | ||
|
|
917db35a84 | ||
|
|
dd52364d33 | ||
|
|
30aa5a5057 | ||
|
|
a46fcaee31 | ||
|
|
52e2748869 | ||
|
|
d2d127a4c4 | ||
|
|
0fca8e8cb5 | ||
|
|
6bec912961 | ||
|
|
214e157e5d | ||
|
|
da1d479e55 | ||
|
|
062bb0cef2 | ||
|
|
c744b016ce | ||
|
|
f486845f7f | ||
|
|
7baa96c5c7 | ||
|
|
ea07ec1fda | ||
|
|
26b70e457b | ||
|
|
5d5fcb3911 | ||
|
|
50008dff3d | ||
|
|
808638fee3 | ||
|
|
b0e3865562 | ||
|
|
bc03ffb317 | ||
|
|
1edbca1775 | ||
|
|
5a0bc016e7 | ||
|
|
bb66b7f28e | ||
|
|
0331491b2b | ||
|
|
54a782c8ae | ||
|
|
a70bc20829 | ||
|
|
102ed3b800 | ||
|
|
c8e339fe6d | ||
|
|
e4e53fe315 | ||
|
|
1c795c3f1c | ||
|
|
b2b740fed7 | ||
|
|
09550397d7 | ||
|
|
a32f7f2ec5 | ||
|
|
e8e9fa2418 | ||
|
|
1a119bdfe9 | ||
|
|
21ff2e0ffc | ||
|
|
df9b23c96a | ||
|
|
4c117aa282 | ||
|
|
88427262a6 | ||
|
|
72b24a9348 | ||
|
|
01cb8e59e3 | ||
|
|
3910326709 | ||
|
|
7ee46d80e6 | ||
|
|
0cb5450999 | ||
|
|
8c78013257 | ||
|
|
bd944898f0 | ||
|
|
c1ef1acfc0 | ||
|
|
040d3f5d8b | ||
|
|
ec393e4a90 | ||
|
|
1703d0417a | ||
|
|
fad72c0441 | ||
|
|
2f7321a076 | ||
|
|
85ee422acd | ||
|
|
089816d9ba | ||
|
|
c873c2db15 | ||
|
|
047c67baf3 | ||
|
|
8f394f117b | ||
|
|
fb7528c239 | ||
|
|
042f5fe4b3 | ||
|
|
289fb47a34 | ||
|
|
38bc6babb7 | ||
|
|
e7a8efcfa0 | ||
|
|
1b74822cfc | ||
|
|
f083dae328 | ||
|
|
23c384bd30 | ||
|
|
ced1616e51 | ||
|
|
233ac4aed2 | ||
|
|
be5a2b0e87 | ||
|
|
2b4b64f499 | ||
|
|
262490d074 | ||
|
|
8891cfd85e | ||
|
|
2cc8fa1eac | ||
|
|
79aebf06dc | ||
|
|
19dd961752 | ||
|
|
bf413ecb83 | ||
|
|
fd1a3eda1c | ||
|
|
0e2488db32 | ||
|
|
58a00bffbb | ||
|
|
bbfe57400d | ||
|
|
4372f6fdac | ||
|
|
30f798b246 | ||
|
|
282770f11a | ||
|
|
17373a4e91 | ||
|
|
a34147b602 | ||
|
|
cebd15bfd1 | ||
|
|
f51f7c0ca8 | ||
|
|
f6d26df64d | ||
|
|
fddfb2e2d6 | ||
|
|
dec728cfa2 | ||
|
|
8440091a4e | ||
|
|
2464a135b3 | ||
|
|
87fa120ebb | ||
|
|
d86f53a02c | ||
|
|
15a4fec3d9 | ||
|
|
1819be1173 | ||
|
|
ffa9b51d27 | ||
|
|
b4a8c08f43 | ||
|
|
741bcc4672 |
@@ -1,56 +0,0 @@
|
||||
version: 2
|
||||
|
||||
jobs:
|
||||
|
||||
android-test:
|
||||
working_directory: ~/winit
|
||||
docker:
|
||||
- image: tomaka/cargo-apk
|
||||
steps:
|
||||
- run: apt-get -qq update && apt-get install -y git
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: android-test-cache-{{ checksum "Cargo.toml" }}
|
||||
- run: cargo apk build --example window
|
||||
- save_cache:
|
||||
key: android-test-cache-{{ checksum "Cargo.toml" }}
|
||||
paths:
|
||||
- target
|
||||
|
||||
asmjs-test:
|
||||
working_directory: ~/winit
|
||||
docker:
|
||||
- image: tomaka/rustc-emscripten
|
||||
steps:
|
||||
- run: apt-get -qq update && apt-get install -y git
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: asmjs-test-cache-{{ checksum "Cargo.toml" }}
|
||||
- run: cargo build --example window --target asmjs-unknown-emscripten
|
||||
- save_cache:
|
||||
key: asmjs-test-cache-{{ checksum "Cargo.toml" }}
|
||||
paths:
|
||||
- target
|
||||
|
||||
wasm-test:
|
||||
working_directory: ~/winit
|
||||
docker:
|
||||
- image: tomaka/rustc-emscripten
|
||||
steps:
|
||||
- run: apt-get -qq update && apt-get install -y git
|
||||
- checkout
|
||||
- restore_cache:
|
||||
key: wasm-test-cache-{{ checksum "Cargo.toml" }}
|
||||
- run: cargo build --example window --target wasm32-unknown-emscripten
|
||||
- save_cache:
|
||||
key: wasm-test-cache-{{ checksum "Cargo.toml" }}
|
||||
paths:
|
||||
- target
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build-test-and-deploy:
|
||||
jobs:
|
||||
- android-test
|
||||
- asmjs-test
|
||||
- wasm-test
|
||||
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -20,3 +20,5 @@
|
||||
*.PDF diff=astextplain
|
||||
*.rtf diff=astextplain
|
||||
*.RTF diff=astextplain
|
||||
|
||||
/CHANGELOG.md merge=union
|
||||
|
||||
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
- [ ] 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
|
||||
- [ ] Updated [feature matrix](https://github.com/rust-windowing/winit/blob/master/FEATURES.md), if new features were added or implemented
|
||||
106
.github/workflows/ci.yml
vendored
Normal file
106
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
Check_Formatting:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: hecrj/setup-rust-action@v1
|
||||
with:
|
||||
rust-version: stable
|
||||
components: rustfmt
|
||||
- name: Check Formatting
|
||||
run: cargo +stable fmt --all -- --check
|
||||
|
||||
Tests:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
rust_version: [stable, nightly]
|
||||
platform:
|
||||
- { 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, cmd: 'apk --' }
|
||||
- { 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, }
|
||||
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
CARGO_INCREMENTAL: 0
|
||||
RUSTFLAGS: "-C debuginfo=0 --deny warnings"
|
||||
OPTIONS: ${{ matrix.platform.options }}
|
||||
FEATURES: ${{ format(',{0}', matrix.platform.features ) }}
|
||||
CMD: ${{ matrix.platform.cmd }}
|
||||
|
||||
runs-on: ${{ matrix.platform.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
# Used to cache cargo-web
|
||||
- name: Cache cargo folder
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cargo
|
||||
key: ${{ matrix.platform.target }}-cargo-${{ matrix.rust_version }}
|
||||
|
||||
- uses: hecrj/setup-rust-action@v1
|
||||
with:
|
||||
rust-version: ${{ matrix.rust_version }}${{ matrix.platform.host }}
|
||||
targets: ${{ matrix.platform.target }}
|
||||
|
||||
- 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-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 $CMD doc --no-deps --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
|
||||
|
||||
- name: Build
|
||||
shell: bash
|
||||
run: cargo $CMD build --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
|
||||
|
||||
- name: Build tests
|
||||
shell: bash
|
||||
run: cargo $CMD test --no-run --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
|
||||
- name: Run tests
|
||||
shell: bash
|
||||
if: (
|
||||
!contains(matrix.platform.target, 'android') &&
|
||||
!contains(matrix.platform.target, 'ios') &&
|
||||
!contains(matrix.platform.target, 'wasm32'))
|
||||
run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES
|
||||
|
||||
|
||||
- name: Build with serde enabled
|
||||
shell: bash
|
||||
run: cargo $CMD build --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES
|
||||
|
||||
- name: Build tests with serde enabled
|
||||
shell: bash
|
||||
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, 'android') &&
|
||||
!contains(matrix.platform.target, 'ios') &&
|
||||
!contains(matrix.platform.target, 'wasm32'))
|
||||
run: cargo $CMD test --verbose --target ${{ matrix.platform.target }} $OPTIONS --features serde,$FEATURES
|
||||
18
.github/workflows/publish.yml
vendored
Normal file
18
.github/workflows/publish.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
name: Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||
|
||||
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 }}
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,4 +1,10 @@
|
||||
Cargo.lock
|
||||
target/
|
||||
rls/
|
||||
.vscode/
|
||||
*~
|
||||
*.wasm
|
||||
*.ts
|
||||
*.js
|
||||
#*#
|
||||
.DS_Store
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
||||
[submodule "deps/apk-builder"]
|
||||
path = deps/apk-builder
|
||||
url = https://github.com/tomaka/android-rs-glue
|
||||
url = https://github.com/rust-windowing/android-rs-glue
|
||||
|
||||
60
.travis.yml
60
.travis.yml
@@ -1,60 +0,0 @@
|
||||
language: rust
|
||||
|
||||
cache: cargo
|
||||
|
||||
matrix:
|
||||
include:
|
||||
# Linux 32bit
|
||||
- env: TARGET=i686-unknown-linux-gnu
|
||||
os: linux
|
||||
rust: nightly
|
||||
addons:
|
||||
apt:
|
||||
# Cross compiler and cross compiled C libraries
|
||||
packages: &i686_packages
|
||||
- gcc-multilib
|
||||
- env: TARGET=i686-unknown-linux-gnu
|
||||
os: linux
|
||||
rust: stable
|
||||
addons:
|
||||
apt:
|
||||
packages: *i686_packages
|
||||
|
||||
# Linux 64bit
|
||||
- env: TARGET=x86_64-unknown-linux-gnu
|
||||
os: linux
|
||||
rust: nightly
|
||||
- env: TARGET=x86_64-unknown-linux-gnu
|
||||
os: linux
|
||||
rust: stable
|
||||
|
||||
# macOS
|
||||
- env: TARGET=x86_64-apple-darwin
|
||||
os: osx
|
||||
rust: nightly
|
||||
- env: TARGET=x86_64-apple-darwin
|
||||
os: osx
|
||||
rust: stable
|
||||
|
||||
# iOS
|
||||
- env: TARGET=x86_64-apple-ios
|
||||
os: osx
|
||||
rust: nightly
|
||||
- env: TARGET=x86_64-apple-ios
|
||||
os: osx
|
||||
rust: stable
|
||||
|
||||
install:
|
||||
- rustup self update
|
||||
- rustup target add $TARGET; true
|
||||
|
||||
script:
|
||||
- cargo build --target $TARGET --verbose
|
||||
# Running iOS apps on OSX requires the simulator so we skip that for now
|
||||
- if [ "$TARGET" != "x86_64-apple-ios" ]; then cargo test --target $TARGET --verbose; fi
|
||||
|
||||
after_success:
|
||||
- |
|
||||
[ $TRAVIS_BRANCH = master ] &&
|
||||
[ $TRAVIS_PULL_REQUEST = false ] &&
|
||||
cargo publish --token ${CRATESIO_TOKEN}
|
||||
601
CHANGELOG.md
601
CHANGELOG.md
@@ -1,4 +1,603 @@
|
||||
# Unreleased
|
||||
# 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 Heirarchy" 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.
|
||||
- On macOS, fix `set_simple_screen` to remember frame excluding title bar.
|
||||
- On Wayland, fix coordinates in touch events when scale factor isn't 1.
|
||||
- On Wayland, fix color from `close_button_icon_color` not applying.
|
||||
- Ignore locale if unsupported by X11 backend
|
||||
- On Wayland, Add HiDPI cursor support
|
||||
- On Web, add the ability to query "Light" or "Dark" system theme send `ThemeChanged` on change.
|
||||
- Fix `Event::to_static` returning `None` for user events.
|
||||
- On Wayland, Hide CSD for fullscreen windows.
|
||||
- On Windows, ignore spurious mouse move messages.
|
||||
- **Breaking:** Move `ModifiersChanged` variant from `DeviceEvent` to `WindowEvent`.
|
||||
- On Windows, add `IconExtWindows` trait which exposes creating an `Icon` from an external file or embedded resource
|
||||
- Add `BadIcon::OsError` variant for when OS icon functionality fails
|
||||
- On Windows, fix crash at startup on systems that do not properly support Windows' Dark Mode
|
||||
- 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)
|
||||
|
||||
- On Windows, fixed "error: linking with `link.exe` failed: exit code: 1120" error on older versions of windows.
|
||||
- On macOS, fix set_minimized(true) works only with decorations.
|
||||
- On macOS, add `hide_application` to `EventLoopWindowTarget` via a new `EventLoopWindowTargetExtMacOS` trait. `hide_application` will hide the entire application by calling `-[NSApplication hide: nil]`.
|
||||
- On macOS, fix not sending ReceivedCharacter event for specific keys combinations.
|
||||
- On macOS, fix `CursorMoved` event reporting the cursor position using logical coordinates.
|
||||
- On macOS, fix issue where unbundled applications would sometimes open without being focused.
|
||||
- On macOS, fix `run_return` does not return unless it receives a message.
|
||||
- On Windows, fix bug where `RedrawRequested` would only get emitted every other iteration of the event loop.
|
||||
- On X11, fix deadlock on window state when handling certain window events.
|
||||
- `WindowBuilder` now implements `Default`.
|
||||
- **Breaking:** `WindowEvent::CursorMoved` changed to `f64` units, preserving high-precision data supplied by most backends
|
||||
- On Wayland, fix coordinates in mouse events when scale factor isn't 1
|
||||
- On Web, add the ability to provide a custom canvas
|
||||
- **Breaking:** On Wayland, the `WaylandTheme` struct has been replaced with a `Theme` trait, allowing for extra configuration
|
||||
|
||||
# 0.20.0 (2020-01-05)
|
||||
|
||||
- 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
|
||||
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.
|
||||
|
||||
# 0.20.0 Alpha 6 (2020-01-03)
|
||||
|
||||
- On macOS, fix `set_cursor_visible` hides cursor outside of window.
|
||||
- On macOS, fix `CursorEntered` and `CursorLeft` events fired at old window size.
|
||||
- On macOS, fix error when `set_fullscreen` is called during fullscreen transition.
|
||||
- On all platforms except mobile and WASM, implement `Window::set_minimized`.
|
||||
- On X11, fix `CursorEntered` event being generated for non-winit windows.
|
||||
- On macOS, fix crash when starting maximized without decorations.
|
||||
- On macOS, fix application not terminating on `run_return`.
|
||||
- On Wayland, fix cursor icon updates on window borders when using CSD.
|
||||
- On Wayland, under mutter(GNOME Wayland), fix CSD being behind the status bar, when starting window in maximized mode.
|
||||
- On Windows, theme the title bar according to whether the system theme is "Light" or "Dark".
|
||||
- Added `WindowEvent::ThemeChanged` variant to handle changes to the system theme. Currently only implemented on Windows.
|
||||
- **Breaking**: Changes to the `RedrawRequested` event (#1041):
|
||||
- `RedrawRequested` has been moved from `WindowEvent` to `Event`.
|
||||
- `EventsCleared` has been renamed to `MainEventsCleared`.
|
||||
- `RedrawRequested` is now issued only after `MainEventsCleared`.
|
||||
- `RedrawEventsCleared` is issued after each set of `RedrawRequested` events.
|
||||
- Implement synthetic window focus key events on Windows.
|
||||
- **Breaking**: Change `ModifiersState` to a `bitflags` struct.
|
||||
- On Windows, implement `VirtualKeyCode` translation for `LWin` and `RWin`.
|
||||
- On Windows, fix closing the last opened window causing `DeviceEvent`s to stop getting emitted.
|
||||
- On Windows, fix `Window::set_visible` not setting internal flags correctly. This resulted in some weird behavior.
|
||||
- Add `DeviceEvent::ModifiersChanged`.
|
||||
- Deprecate `modifiers` fields in other events in favor of `ModifiersChanged`.
|
||||
- On X11, `WINIT_HIDPI_FACTOR` now dominates `Xft.dpi` when picking DPI factor for output.
|
||||
- On X11, add special value `randr` for `WINIT_HIDPI_FACTOR` to make winit use self computed DPI factor instead of the one from `Xft.dpi`.
|
||||
|
||||
# 0.20.0 Alpha 5 (2019-12-09)
|
||||
|
||||
- On macOS, fix application termination on `ControlFlow::Exit`
|
||||
- On Windows, fix missing `ReceivedCharacter` events when Alt is held.
|
||||
- On macOS, stop emitting private corporate characters in `ReceivedCharacter` events.
|
||||
- On X11, fix misreporting DPI factor at startup.
|
||||
- On X11, fix events not being reported when using `run_return`.
|
||||
- On X11, fix key modifiers being incorrectly reported.
|
||||
- On X11, fix window creation hanging when another window is fullscreen.
|
||||
- On Windows, fix focusing unfocused windows when switching from fullscreen to windowed.
|
||||
- On X11, fix reporting incorrect DPI factor when waking from suspend.
|
||||
- Change `EventLoopClosed` to contain the original event.
|
||||
- **Breaking**: Add `is_synthetic` field to `WindowEvent` variant `KeyboardInput`,
|
||||
indicating that the event is generated by winit.
|
||||
- On X11, generate synthetic key events for keys held when a window gains or loses focus.
|
||||
- On X11, issue a `CursorMoved` event when a `Touch` event occurs,
|
||||
as X11 implicitly moves the cursor for such events.
|
||||
|
||||
# 0.20.0 Alpha 4 (2019-10-18)
|
||||
|
||||
- Add web support via the 'stdweb' or 'web-sys' features
|
||||
- On Windows, implemented function to get HINSTANCE
|
||||
- On macOS, implement `run_return`.
|
||||
- On iOS, fix inverted parameter in `set_prefers_home_indicator_hidden`.
|
||||
- On X11, performance is improved when rapidly calling `Window::set_cursor_icon`.
|
||||
- On iOS, fix improper `msg_send` usage that was UB and/or would break if `!` is stabilized.
|
||||
- On Windows, unset `maximized` when manually changing the window's position or size.
|
||||
- On Windows, add touch pressure information for touch events.
|
||||
- On macOS, differentiate between `CursorIcon::Grab` and `CursorIcon::Grabbing`.
|
||||
- On Wayland, fix event processing sometimes stalling when using OpenGL with vsync.
|
||||
- Officially remove the Emscripten backend.
|
||||
- On Windows, fix handling of surrogate pairs when dispatching `ReceivedCharacter`.
|
||||
- On macOS 10.15, fix freeze upon exiting exclusive fullscreen mode.
|
||||
- On iOS, fix panic upon closing the app.
|
||||
- On X11, allow setting mulitple `XWindowType`s.
|
||||
- On iOS, fix null window on initial `HiDpiFactorChanged` event.
|
||||
- On Windows, fix fullscreen window shrinking upon getting restored to a normal window.
|
||||
- On macOS, fix events not being emitted during modal loops, such as when windows are being resized
|
||||
by the user.
|
||||
- On Windows, fix hovering the mouse over the active window creating an endless stream of CursorMoved events.
|
||||
- Always dispatch a `RedrawRequested` event after creating a new window.
|
||||
- On X11, return dummy monitor data to avoid panicking when no monitors exist.
|
||||
- On X11, prevent stealing input focus when creating a new window.
|
||||
Only steal input focus when entering fullscreen mode.
|
||||
- On Wayland, fixed DeviceEvents for relative mouse movement is not always produced
|
||||
- On Wayland, add support for set_cursor_visible and set_cursor_grab.
|
||||
- On Wayland, fixed DeviceEvents for relative mouse movement is not always produced.
|
||||
- Removed `derivative` crate dependency.
|
||||
- On Wayland, add support for set_cursor_icon.
|
||||
- Use `impl Iterator<Item = MonitorHandle>` instead of `AvailableMonitorsIter` consistently.
|
||||
- On macOS, fix fullscreen state being updated after entering fullscreen instead of before,
|
||||
resulting in `Window::fullscreen` returning the old state in `Resized` events instead of
|
||||
reflecting the new fullscreen state
|
||||
- On X11, fix use-after-free during window creation
|
||||
- On Windows, disable monitor change keyboard shortcut while in exclusive fullscreen.
|
||||
- On Windows, ensure that changing a borderless fullscreen window's monitor via keyboard shortcuts keeps the window fullscreen on the new monitor.
|
||||
- Prevent `EventLoop::new` and `EventLoop::with_user_event` from getting called outside the main thread.
|
||||
- This is because some platforms cannot run the event loop outside the main thread. Preventing this
|
||||
reduces the potential for cross-platform compatibility gotchyas.
|
||||
- On Windows and Linux X11/Wayland, add platform-specific functions for creating an `EventLoop` outside the main thread.
|
||||
- On Wayland, drop resize events identical to the current window size.
|
||||
- On Windows, fix window rectangle not getting set correctly on high-DPI systems.
|
||||
|
||||
# 0.20.0 Alpha 3 (2019-08-14)
|
||||
|
||||
- On macOS, drop the run closure on exit.
|
||||
- On Windows, location of `WindowEvent::Touch` are window client coordinates instead of screen coordinates.
|
||||
- On X11, fix delayed events after window redraw.
|
||||
- On macOS, add `WindowBuilderExt::with_disallow_hidpi` to have the option to turn off best resolution openGL surface.
|
||||
- On Windows, screen saver won't start if the window is in fullscreen mode.
|
||||
- Change all occurrences of the `new_user_event` method to `with_user_event`.
|
||||
- On macOS, the dock and the menu bar are now hidden in fullscreen mode.
|
||||
- `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.
|
||||
- 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.
|
||||
- On iOS, fix armv7-apple-ios compile target.
|
||||
- Removed the `T: Clone` requirement from the `Clone` impl of `EventLoopProxy<T>`.
|
||||
- On iOS, disable overscan compensation for external displays (removes black
|
||||
bars surrounding the image).
|
||||
- On Linux, the functions `is_wayland`, `is_x11`, `xlib_xconnection` and `wayland_display` have been moved to a new `EventLoopWindowTargetExtUnix` trait.
|
||||
- On iOS, add `set_prefers_status_bar_hidden` extension function instead of
|
||||
hijacking `set_decorations` for this purpose.
|
||||
- On macOS and iOS, corrected the auto trait impls of `EventLoopProxy`.
|
||||
- On iOS, add touch pressure information for touch events.
|
||||
- Implement `raw_window_handle::HasRawWindowHandle` for `Window` type on all supported platforms.
|
||||
- On macOS, fix the signature of `-[NSView drawRect:]`.
|
||||
- On iOS, fix the behavior of `ControlFlow::Poll`. It wasn't polling if that was the only mode ever used by the application.
|
||||
- On iOS, fix DPI sent out by views on creation was `0.0` - now it gives a reasonable number.
|
||||
- On iOS, RedrawRequested now works for gl/metal backed views.
|
||||
- On iOS, RedrawRequested is generally ordered after EventsCleared.
|
||||
|
||||
# 0.20.0 Alpha 2 (2019-07-09)
|
||||
|
||||
- On X11, non-resizable windows now have maximize explicitly disabled.
|
||||
- On Windows, support paths longer than MAX_PATH (260 characters) in `WindowEvent::DroppedFile`
|
||||
and `WindowEvent::HoveredFile`.
|
||||
- On Mac, implement `DeviceEvent::Button`.
|
||||
- Change `Event::Suspended(true / false)` to `Event::Suspended` and `Event::Resumed`.
|
||||
- On X11, fix sanity check which checks that a monitor's reported width and height (in millimeters) are non-zero when calculating the DPI factor.
|
||||
- Revert the use of invisible surfaces in Wayland, which introduced graphical glitches with OpenGL (#835)
|
||||
- On X11, implement `_NET_WM_PING` to allow desktop environment to kill unresponsive programs.
|
||||
- On Windows, when a window is initially invisible, it won't take focus from the existing visible windows.
|
||||
- On Windows, fix multiple calls to `request_redraw` during `EventsCleared` sending multiple `RedrawRequested events.`
|
||||
- On Windows, fix edge case where `RedrawRequested` could be dispatched before input events in event loop iteration.
|
||||
- On Windows, fix timing issue that could cause events to be improperly dispatched after `RedrawRequested` but before `EventsCleared`.
|
||||
- On macOS, drop unused Metal dependency.
|
||||
- On Windows, fix the trail effect happening on transparent decorated windows. Borderless (or un-decorated) windows were not affected.
|
||||
- On Windows, fix `with_maximized` not properly setting window size to entire window.
|
||||
- On macOS, change `WindowExtMacOS::request_user_attention()` to take an `enum` instead of a `bool`.
|
||||
|
||||
# 0.20.0 Alpha 1 (2019-06-21)
|
||||
|
||||
- Changes below are considered **breaking**.
|
||||
- Change all occurrences of `EventsLoop` to `EventLoop`.
|
||||
- Previously flat API is now exposed through `event`, `event_loop`, `monitor`, and `window` modules.
|
||||
- `os` module changes:
|
||||
- Renamed to `platform`.
|
||||
- All traits now have platform-specific suffixes.
|
||||
- Exposes new `desktop` module on Windows, Mac, and Linux.
|
||||
- Changes to event loop types:
|
||||
- `EventLoopProxy::wakeup` has been removed in favor of `send_event`.
|
||||
- **Major:** New `run` method drives winit event loop.
|
||||
- Returns `!` to ensure API behaves identically across all supported platforms.
|
||||
- This allows `emscripten` implementation to work without lying about the API.
|
||||
- `ControlFlow`'s variants have been replaced with `Wait`, `WaitUntil(Instant)`, `Poll`, and `Exit`.
|
||||
- Is read after `EventsCleared` is processed.
|
||||
- `Wait` waits until new events are available.
|
||||
- `WaitUntil` waits until either new events are available or the provided time has been reached.
|
||||
- `Poll` instantly resumes the event loop.
|
||||
- `Exit` aborts the event loop.
|
||||
- Takes a closure that implements `'static + FnMut(Event<T>, &EventLoop<T>, &mut ControlFlow)`.
|
||||
- `&EventLoop<T>` is provided to allow new `Window`s to be created.
|
||||
- **Major:** `platform::desktop` module exposes `EventLoopExtDesktop` trait with `run_return` method.
|
||||
- Behaves identically to `run`, but returns control flow to the calling context and can take non-`'static` closures.
|
||||
- `EventLoop`'s `poll_events` and `run_forever` methods have been removed in favor of `run` and `run_return`.
|
||||
- Changes to events:
|
||||
- Remove `Event::Awakened` in favor of `Event::UserEvent(T)`.
|
||||
- Can be sent with `EventLoopProxy::send_event`.
|
||||
- Rename `WindowEvent::Refresh` to `WindowEvent::RedrawRequested`.
|
||||
- `RedrawRequested` can be sent by the user with the `Window::request_redraw` method.
|
||||
- `EventLoop`, `EventLoopProxy`, and `Event` are now generic over `T`, for use in `UserEvent`.
|
||||
- **Major:** Add `NewEvents(StartCause)`, `EventsCleared`, and `LoopDestroyed` variants to `Event`.
|
||||
- `NewEvents` is emitted when new events are ready to be processed by event loop.
|
||||
- `StartCause` describes why new events are available, with `ResumeTimeReached`, `Poll`, `WaitCancelled`, and `Init` (sent once at start of loop).
|
||||
- `EventsCleared` is emitted when all available events have been processed.
|
||||
- Can be used to perform logic that depends on all events being processed (e.g. an iteration of a game loop).
|
||||
- `LoopDestroyed` is emitted when the `run` or `run_return` method is about to exit.
|
||||
- Rename `MonitorId` to `MonitorHandle`.
|
||||
- Removed `serde` implementations from `ControlFlow`.
|
||||
- Rename several functions to improve both internal consistency and compliance with Rust API guidelines.
|
||||
- Remove `WindowBuilder::multitouch` field, since it was only implemented on a few platforms. Multitouch is always enabled now.
|
||||
- **Breaking:** On macOS, change `ns` identifiers to use snake_case for consistency with iOS's `ui` identifiers.
|
||||
- Add `MonitorHandle::video_modes` method for retrieving supported video modes for the given monitor.
|
||||
- On Wayland, the window now exists even if nothing has been drawn.
|
||||
- On Windows, fix initial dimensions of a fullscreen window.
|
||||
- On Windows, Fix transparent borderless windows rendering wrong.
|
||||
|
||||
# Version 0.19.1 (2019-04-08)
|
||||
|
||||
- On Wayland, added a `get_wayland_display` function to `EventsLoopExt`.
|
||||
- On Windows, fix `CursorMoved(0, 0)` getting dispatched on window focus.
|
||||
- On macOS, fix command key event left and right reverse.
|
||||
- On FreeBSD, NetBSD, and OpenBSD, fix build of X11 backend.
|
||||
- On Linux, the numpad's add, subtract and divide keys are now mapped to the `Add`, `Subtract` and `Divide` virtual key codes
|
||||
- On macOS, the numpad's subtract key has been added to the `Subtract` mapping
|
||||
- On Wayland, the numpad's home, end, page up and page down keys are now mapped to the `Home`, `End`, `PageUp` and `PageDown` virtual key codes
|
||||
- On Windows, fix icon not showing up in corner of window.
|
||||
- On X11, change DPI scaling factor behavior. First, winit tries to read it from "Xft.dpi" XResource, and uses DPI calculation from xrandr dimensions as fallback behavior.
|
||||
|
||||
# Version 0.19.0 (2019-03-06)
|
||||
|
||||
- On X11, we will use the faster `XRRGetScreenResourcesCurrent` function instead of `XRRGetScreenResources` when available.
|
||||
- On macOS, fix keycodes being incorrect when using a non-US keyboard layout.
|
||||
- On Wayland, fix `with_title()` not setting the windows title
|
||||
- On Wayland, add `set_wayland_theme()` to control client decoration color theme
|
||||
- Added serde serialization to `os::unix::XWindowType`.
|
||||
- **Breaking:** Remove the `icon_loading` feature and the associated `image` dependency.
|
||||
- On X11, make event loop thread safe by replacing XNextEvent with select(2) and XCheckIfEvent
|
||||
- On Windows, fix malformed function pointer typecast that could invoke undefined behavior.
|
||||
- Refactored Windows state/flag-setting code.
|
||||
- On Windows, hiding the cursor no longer hides the cursor for all Winit windows - just the one `hide_cursor` was called on.
|
||||
- On Windows, cursor grabs used to get perpetually canceled when the grabbing window lost focus. Now, cursor grabs automatically get re-initialized when the window regains focus and the mouse moves over the client area.
|
||||
- On Windows, only vertical mouse wheel events were handled. Now, horizontal mouse wheel events are also handled.
|
||||
- On Windows, ignore the AltGr key when populating the `ModifersState` type.
|
||||
|
||||
# Version 0.18.1 (2018-12-30)
|
||||
|
||||
- On macOS, fix `Yen` (JIS) so applications receive the event.
|
||||
- On X11 with a tiling WM, fixed high CPU usage when moving windows across monitors.
|
||||
- On X11, fixed panic caused by dropping the window before running the event loop.
|
||||
- on macOS, added `WindowExt::set_simple_fullscreen` which does not require a separate space
|
||||
- Introduce `WindowBuilderExt::with_app_id` to allow setting the application ID on Wayland.
|
||||
- On Windows, catch panics in event loop child thread and forward them to the parent thread. This prevents an invocation of undefined behavior due to unwinding into foreign code.
|
||||
- On Windows, fix issue where resizing or moving window combined with grabbing the cursor would freeze program.
|
||||
- On Windows, fix issue where resizing or moving window would eat `Awakened` events.
|
||||
- On Windows, exiting fullscreen after entering fullscreen with disabled decorations no longer shrinks window.
|
||||
- On X11, fixed a segfault when using virtual monitors with XRandR.
|
||||
- Derive `Ord` and `PartialOrd` for `VirtualKeyCode` enum.
|
||||
- On Windows, fix issue where hovering or dropping a non file item would create a panic.
|
||||
- On Wayland, fix resizing and DPI calculation when a `wl_output` is removed without sending a `leave` event to the `wl_surface`, such as disconnecting a monitor from a laptop.
|
||||
- On Wayland, DPI calculation is handled by smithay-client-toolkit.
|
||||
- On X11, `WindowBuilder::with_min_dimensions` and `WindowBuilder::with_max_dimensions` now correctly account for DPI.
|
||||
- Added support for generating dummy `DeviceId`s and `WindowId`s to better support unit testing.
|
||||
- On macOS, fixed unsoundness in drag-and-drop that could result in drops being rejected.
|
||||
- On macOS, implemented `WindowEvent::Refresh`.
|
||||
- On macOS, all `MouseCursor` variants are now implemented and the cursor will no longer reset after unfocusing.
|
||||
- Removed minimum supported Rust version guarantee.
|
||||
|
||||
# Version 0.18.0 (2018-11-07)
|
||||
|
||||
- **Breaking:** `image` crate upgraded to 0.20. This is exposed as part of the `icon_loading` API.
|
||||
- On Wayland, pointer events will now provide the current modifiers state.
|
||||
- On Wayland, titles will now be displayed in the window header decoration.
|
||||
- On Wayland, key repetition is now ended when keyboard loses focus.
|
||||
- On Wayland, windows will now use more stylish and modern client side decorations.
|
||||
- On Wayland, windows will use server-side decorations when available.
|
||||
- **Breaking:** Added support for F16-F24 keys (variants were added to the `VirtualKeyCode` enum).
|
||||
- Fixed graphical glitches when resizing on Wayland.
|
||||
- On Windows, fix freezes when performing certain actions after a window resize has been triggered. Reintroduces some visual artifacts when resizing.
|
||||
- Updated window manager hints under X11 to v1.5 of [Extended Window Manager Hints](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html#idm140200472629520).
|
||||
- Added `WindowBuilderExt::with_gtk_theme_variant` to X11-specific `WindowBuilder` functions.
|
||||
- Fixed UTF8 handling bug in X11 `set_title` function.
|
||||
- On Windows, `Window::set_cursor` now applies immediately instead of requiring specific events to occur first.
|
||||
- On Windows, the `HoveredFile` and `HoveredFileCancelled` events are now implemented.
|
||||
- On Windows, fix `Window::set_maximized`.
|
||||
- On Windows 10, fix transparency (#260).
|
||||
- On macOS, fix modifiers during key repeat.
|
||||
- Implemented the `Debug` trait for `Window`, `EventsLoop`, `EventsLoopProxy` and `WindowBuilder`.
|
||||
- On X11, now a `Resized` event will always be generated after a DPI change to ensure the window's logical size is consistent with the new DPI.
|
||||
- Added further clarifications to the DPI docs.
|
||||
- On Linux, if neither X11 nor Wayland manage to initialize, the corresponding panic now consists of a single line only.
|
||||
- Add optional `serde` feature with implementations of `Serialize`/`Deserialize` for DPI types and various event types.
|
||||
- Add `PartialEq`, `Eq`, and `Hash` implementations on public types that could have them but were missing them.
|
||||
- On X11, drag-and-drop receiving an unsupported drop type can no longer cause the WM to freeze.
|
||||
- Fix issue whereby the OpenGL context would not appear at startup on macOS Mojave (#1069).
|
||||
- **Breaking:** Removed `From<NSApplicationActivationPolicy>` impl from `ActivationPolicy` on macOS.
|
||||
- On macOS, the application can request the user's attention with `WindowExt::request_user_attention`.
|
||||
|
||||
# Version 0.17.2 (2018-08-19)
|
||||
|
||||
- On macOS, fix `<C-Tab>` so applications receive the event.
|
||||
- On macOS, fix `<Cmd-{key}>` so applications receive the event.
|
||||
- On Wayland, key press events will now be repeated.
|
||||
|
||||
# Version 0.17.1 (2018-08-05)
|
||||
|
||||
- On X11, prevent a compilation failure in release mode for versions of Rust greater than or equal to 1.30.
|
||||
- Fixed deadlock that broke fullscreen mode on Windows.
|
||||
|
||||
# Version 0.17.0 (2018-08-02)
|
||||
|
||||
- Cocoa and core-graphics updates.
|
||||
- Fixed thread-safety issues in several `Window` functions on Windows.
|
||||
- On MacOS, the key state for modifiers key events is now properly set.
|
||||
- On iOS, the view is now set correctly. This makes it possible to render things (instead of being stuck on a black screen), and touch events work again.
|
||||
- Added NetBSD support.
|
||||
- **Breaking:** On iOS, `UIView` is now the default root view. `WindowBuilderExt::with_root_view_class` can be used to set the root view objective-c class to `GLKView` (OpenGLES) or `MTKView` (Metal/MoltenVK).
|
||||
- On iOS, the `UIApplication` is not started until `Window::new` is called.
|
||||
- Fixed thread unsafety with cursor hiding on macOS.
|
||||
- On iOS, fixed the size of the `JmpBuf` type used for `setjmp`/`longjmp` calls. Previously this was a buffer overflow on most architectures.
|
||||
- On Windows, use cached window DPI instead of repeatedly querying the system. This fixes sporadic crashes on Windows 7.
|
||||
|
||||
# Version 0.16.2 (2018-07-07)
|
||||
|
||||
- On Windows, non-resizable windows now have the maximization button disabled. This is consistent with behavior on macOS and popular X11 WMs.
|
||||
- Corrected incorrect `unreachable!` usage when guessing the DPI factor with no detected monitors.
|
||||
|
||||
# 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.
|
||||
- Fixed X11 backend compilation for environments where `c_char` is unsigned.
|
||||
|
||||
# Version 0.16.0 (2018-06-25)
|
||||
|
||||
- Windows additionally has `WindowBuilderExt::with_no_redirection_bitmap`.
|
||||
- **Breaking:** Removed `VirtualKeyCode::LMenu` and `VirtualKeyCode::RMenu`; Windows now generates `VirtualKeyCode::LAlt` and `VirtualKeyCode::RAlt` instead.
|
||||
- On X11, exiting fullscreen no longer leaves the window in the monitor's top left corner.
|
||||
- **Breaking:** `Window::hidpi_factor` has been renamed to `Window::get_hidpi_factor` for better consistency. `WindowEvent::HiDPIFactorChanged` has been renamed to `WindowEvent::HiDpiFactorChanged`. DPI factors are always represented as `f64` instead of `f32` now.
|
||||
- The Windows backend is now DPI aware. `WindowEvent::HiDpiFactorChanged` is implemented, and `MonitorId::get_hidpi_factor` and `Window::hidpi_factor` return accurate values.
|
||||
- Implemented `WindowEvent::HiDpiFactorChanged` on X11.
|
||||
- On macOS, `Window::set_cursor_position` is now relative to the client area.
|
||||
- On macOS, setting the maximum and minimum dimensions now applies to the client area dimensions rather than to the window dimensions.
|
||||
- On iOS, `MonitorId::get_dimensions` has been implemented and both `MonitorId::get_hidpi_factor` and `Window::get_hidpi_factor` return accurate values.
|
||||
- On Emscripten, `MonitorId::get_hidpi_factor` now returns the same value as `Window::get_hidpi_factor` (it previously would always return 1.0).
|
||||
- **Breaking:** The entire API for sizes, positions, etc. has changed. In the majority of cases, winit produces and consumes positions and sizes as `LogicalPosition` and `LogicalSize`, respectively. The notable exception is `MonitorId` methods, which deal in `PhysicalPosition` and `PhysicalSize`. See the documentation for specifics and explanations of the types. Additionally, winit automatically conserves logical size when the DPI factor changes.
|
||||
- **Breaking:** All deprecated methods have been removed. For `Window::platform_display` and `Window::platform_window`, switch to the appropriate platform-specific `WindowExt` methods. For `Window::get_inner_size_points` and `Window::get_inner_size_pixels`, use the `LogicalSize` returned by `Window::get_inner_size` and convert as needed.
|
||||
- HiDPI support for Wayland.
|
||||
- `EventsLoop::get_available_monitors` and `EventsLoop::get_primary_monitor` now have identical counterparts on `Window`, so this information can be acquired without an `EventsLoop` borrow.
|
||||
- `AvailableMonitorsIter` now implements `Debug`.
|
||||
- Fixed quirk on macOS where certain keys would generate characters at twice the normal rate when held down.
|
||||
- On X11, all event loops now share the same `XConnection`.
|
||||
- **Breaking:** `Window::set_cursor_state` and `CursorState` enum removed in favor of the more composable `Window::grab_cursor` and `Window::hide_cursor`. As a result, grabbing the cursor no longer automatically hides it; you must call both methods to retain the old behavior on Windows and macOS. `Cursor::NoneCursor` has been removed, as it's no longer useful.
|
||||
- **Breaking:** `Window::set_cursor_position` now returns `Result<(), String>`, thus allowing for `Box<Error>` conversion via `?`.
|
||||
|
||||
# Version 0.15.1 (2018-06-13)
|
||||
|
||||
- On X11, the `Moved` event is no longer sent when the window is resized without changing position.
|
||||
- `MouseCursor` and `CursorState` now implement `Default`.
|
||||
- `WindowBuilder::with_resizable` implemented for Windows, X11, Wayland, and macOS.
|
||||
- `Window::set_resizable` implemented for Windows, X11, Wayland, and macOS.
|
||||
- On X11, if the monitor's width or height in millimeters is reported as 0, the DPI is now 1.0 instead of +inf.
|
||||
- On X11, the environment variable `WINIT_HIDPI_FACTOR` has been added for overriding DPI factor.
|
||||
- On X11, enabling transparency no longer causes the window contents to flicker when resizing.
|
||||
- On X11, `with_override_redirect` now actually enables override redirect.
|
||||
- macOS now generates `VirtualKeyCode::LAlt` and `VirtualKeyCode::RAlt` instead of `None` for both.
|
||||
- On macOS, `VirtualKeyCode::RWin` and `VirtualKeyCode::LWin` are no longer switched.
|
||||
- On macOS, windows without decorations can once again be resized.
|
||||
- Fixed race conditions when creating an `EventsLoop` on X11, most commonly manifesting as "[xcb] Unknown sequence number while processing queue".
|
||||
- On macOS, `CursorMoved` and `MouseInput` events are only generated if they occurs within the window's client area.
|
||||
- On macOS, resizing the window no longer generates a spurious `MouseInput` event.
|
||||
|
||||
# Version 0.15.0 (2018-05-22)
|
||||
|
||||
- `Icon::to_cardinals` is no longer public, since it was never supposed to be.
|
||||
- Wayland: improve diagnostics if initialization fails
|
||||
- Fix some system event key doesn't work when focused, do not block keyevent forward to system on macOS
|
||||
- On X11, the scroll wheel position is now correctly reset on i3 and other WMs that have the same quirk.
|
||||
- On X11, `Window::get_current_monitor` now reliably returns the correct monitor.
|
||||
- On X11, `Window::hidpi_factor` returns values from XRandR rather than the inaccurate values previously queried from the core protocol.
|
||||
- On X11, the primary monitor is detected correctly even when using versions of XRandR less than 1.5.
|
||||
- `MonitorId` now implements `Debug`.
|
||||
- Fixed bug on macOS where using `with_decorations(false)` would cause `set_decorations(true)` to produce a transparent titlebar with no title.
|
||||
- Implemented `MonitorId::get_position` on macOS.
|
||||
- On macOS, `Window::get_current_monitor` now returns accurate values.
|
||||
- Added `WindowBuilderExt::with_resize_increments` to macOS.
|
||||
- **Breaking:** On X11, `WindowBuilderExt::with_resize_increments` and `WindowBuilderExt::with_base_size` now take `u32` values rather than `i32`.
|
||||
- macOS keyboard handling has been overhauled, allowing for the use of dead keys, IME, etc. Right modifier keys are also no longer reported as being left.
|
||||
- Added the `Window::set_ime_spot(x: i32, y: i32)` method, which is implemented on X11 and macOS.
|
||||
- **Breaking**: `os::unix::WindowExt::send_xim_spot(x: i16, y: i16)` no longer exists. Switch to the new `Window::set_ime_spot(x: i32, y: i32)`, which has equivalent functionality.
|
||||
- Fixed detection of `Pause` and `Scroll` keys on Windows.
|
||||
- On Windows, alt-tabbing while the cursor is grabbed no longer makes it impossible to re-grab the cursor.
|
||||
- On Windows, using `CursorState::Hide` when the cursor is grabbed now ungrabs the cursor first.
|
||||
- Implemented `MouseCursor::NoneCursor` on Windows.
|
||||
- Added `WindowBuilder::with_always_on_top` and `Window::set_always_on_top`. Implemented on Windows, macOS, and X11.
|
||||
- On X11, `WindowBuilderExt` now has `with_class`, `with_override_redirect`, and `with_x11_window_type` to allow for more control over window creation. `WindowExt` additionally has `set_urgent`.
|
||||
- More hints are set by default on X11, including `_NET_WM_PID` and `WM_CLIENT_MACHINE`. Note that prior to this, the `WM_CLASS` hint was automatically set to whatever value was passed to `with_title`. It's now set to the executable name to better conform to expectations and the specification; if this is undesirable, you must explicitly use `WindowBuilderExt::with_class`.
|
||||
|
||||
# Version 0.14.0 (2018-05-09)
|
||||
|
||||
|
||||
42
CONTRIBUTING.md
Normal file
42
CONTRIBUTING.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Winit Contributing Guidelines
|
||||
|
||||
## Scope
|
||||
[See `FEATURES.md`](./FEATURES.md). When requesting or implementing a new Winit feature, you should
|
||||
consider whether or not it's directly related to window creation or input handling. If it isn't, it
|
||||
may be worth creating a separate crate that extends Winit's API to add that functionality.
|
||||
|
||||
|
||||
## Reporting an issue
|
||||
|
||||
When reporting an issue, in order to help the maintainers understand what the problem is, please make
|
||||
your description of the issue as detailed as possible:
|
||||
|
||||
- if it is a bug, please provide clear explanation of what happens, what should happen, and how to
|
||||
reproduce the issue, ideally by providing a minimal program exhibiting the problem
|
||||
- if it is a feature request, please provide a clear argumentation about why you believe this feature
|
||||
should be supported by winit
|
||||
|
||||
## Making a pull request
|
||||
|
||||
When making a code contribution to winit, before opening your pull request, please make sure that:
|
||||
|
||||
- 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
|
||||
- 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.
|
||||
|
||||
Once your PR is open, you can ask for review by a maintainer of your platform. Winit's merging policy
|
||||
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).
|
||||
|
||||
## 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.
|
||||
|
||||
If you are interested in contributing or testing on a platform, please add yourself to that table!
|
||||
134
Cargo.toml
134
Cargo.toml
@@ -1,55 +1,127 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.14.0"
|
||||
authors = ["The winit contributors, Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
version = "0.26.1"
|
||||
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
description = "Cross-platform window creation library."
|
||||
edition = "2018"
|
||||
keywords = ["windowing"]
|
||||
license = "Apache-2.0"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/tomaka/winit"
|
||||
repository = "https://github.com/rust-windowing/winit"
|
||||
documentation = "https://docs.rs/winit"
|
||||
categories = ["gui"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["serde"]
|
||||
default-target = "x86_64-unknown-linux-gnu"
|
||||
targets = ["i686-pc-windows-msvc", "x86_64-pc-windows-msvc", "i686-unknown-linux-gnu", "x86_64-unknown-linux-gnu", "x86_64-apple-darwin", "wasm32-unknown-unknown"]
|
||||
|
||||
[features]
|
||||
icon_loading = ["image"]
|
||||
default = ["x11", "wayland", "wayland-dlopen"]
|
||||
x11 = ["x11-dl", "mio", "percent-encoding", "parking_lot"]
|
||||
wayland = ["wayland-client", "wayland-protocols", "sctk"]
|
||||
wayland-dlopen = ["sctk/dlopen", "wayland-client/dlopen"]
|
||||
|
||||
[dependencies]
|
||||
instant = { version = "0.1", features = ["wasm-bindgen"] }
|
||||
lazy_static = "1"
|
||||
libc = "0.2"
|
||||
image = { version = "0.19", optional = true }
|
||||
log = "0.4"
|
||||
serde = { version = "1", optional = true, features = ["serde_derive"] }
|
||||
raw-window-handle = "0.4.2"
|
||||
bitflags = "1"
|
||||
mint = { version = "0.5.6", optional = true }
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies.android_glue]
|
||||
version = "0.2"
|
||||
[dev-dependencies]
|
||||
image = "0.23.12"
|
||||
simple_logger = "1.9"
|
||||
|
||||
[target.'cfg(target_os = "ios")'.dependencies]
|
||||
objc = "0.2"
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
ndk = "0.5"
|
||||
ndk-sys = "0.2.0"
|
||||
ndk-glue = "0.5"
|
||||
|
||||
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
|
||||
objc = "0.2.7"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
objc = "0.2"
|
||||
cocoa = "0.14"
|
||||
core-foundation = "0.5"
|
||||
core-graphics = "0.13"
|
||||
cocoa = "0.24"
|
||||
core-foundation = "0.9"
|
||||
core-graphics = "0.22"
|
||||
dispatch = "0.2.0"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies.core-video-sys]
|
||||
version = "0.1.4"
|
||||
default_features = false
|
||||
features = ["display_link"]
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
parking_lot = "0.11"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies.winapi]
|
||||
version = "0.3"
|
||||
version = "0.3.9"
|
||||
features = [
|
||||
"combaseapi",
|
||||
"commctrl",
|
||||
"dwmapi",
|
||||
"errhandlingapi",
|
||||
"imm",
|
||||
"hidusage",
|
||||
"libloaderapi",
|
||||
"objbase",
|
||||
"ole2",
|
||||
"processthreadsapi",
|
||||
"shellapi",
|
||||
"shellscalingapi",
|
||||
"shobjidl_core",
|
||||
"unknwnbase",
|
||||
"winbase",
|
||||
"windowsx",
|
||||
"winerror",
|
||||
"wingdi",
|
||||
"winnt",
|
||||
"winuser",
|
||||
"wingdi",
|
||||
"shellapi",
|
||||
"dwmapi",
|
||||
"processthreadsapi",
|
||||
"libloaderapi",
|
||||
"windowsx",
|
||||
"hidusage",
|
||||
"combaseapi",
|
||||
"objbase",
|
||||
"unknwnbase",
|
||||
"mmsystem",
|
||||
"timeapi"
|
||||
]
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies]
|
||||
wayland-client = { version = "0.20.4", features = [ "dlopen", "egl", "cursor"] }
|
||||
smithay-client-toolkit = "0.2.1"
|
||||
x11-dl = "2.17.5"
|
||||
parking_lot = "0.5"
|
||||
percent-encoding = "1.0"
|
||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies]
|
||||
wayland-client = { version = "0.29", default_features = false, features = ["use_system_lib"], optional = true }
|
||||
wayland-protocols = { version = "0.29", features = [ "staging_protocols"], optional = true }
|
||||
sctk = { package = "smithay-client-toolkit", version = "0.15.1", default_features = false, features = ["calloop"], optional = true }
|
||||
mio = { version = "0.8", features = ["os-ext"], optional = true }
|
||||
x11-dl = { version = "2.18.5", optional = true }
|
||||
percent-encoding = { version = "2.0", optional = true }
|
||||
parking_lot = { version = "0.11.0", optional = true }
|
||||
libc = "0.2.64"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies.web_sys]
|
||||
package = "web-sys"
|
||||
version = "0.3.22"
|
||||
features = [
|
||||
'console',
|
||||
"AddEventListenerOptions",
|
||||
'CssStyleDeclaration',
|
||||
'BeforeUnloadEvent',
|
||||
'Document',
|
||||
'DomRect',
|
||||
'Element',
|
||||
'Event',
|
||||
'EventTarget',
|
||||
'FocusEvent',
|
||||
'HtmlCanvasElement',
|
||||
'HtmlElement',
|
||||
'KeyboardEvent',
|
||||
'MediaQueryList',
|
||||
'MediaQueryListEvent',
|
||||
'MouseEvent',
|
||||
'Node',
|
||||
'PointerEvent',
|
||||
'Window',
|
||||
'WheelEvent'
|
||||
]
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies.wasm-bindgen]
|
||||
version = "0.2.45"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
||||
console_log = "0.2"
|
||||
|
||||
239
FEATURES.md
Normal file
239
FEATURES.md
Normal file
@@ -0,0 +1,239 @@
|
||||
# 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:
|
||||
- Desktop
|
||||
- Windows
|
||||
- macOS
|
||||
- Unix
|
||||
- via X11
|
||||
- via Wayland
|
||||
- Mobile
|
||||
- iOS
|
||||
- Android
|
||||
- Web
|
||||
- via WASM
|
||||
|
||||
Most platforms expose capabilities that cannot be meaningfully transposed onto others. Winit does not
|
||||
aim to support every single feature of every platform, but rather to abstract over the common features
|
||||
available everywhere. In this context, APIs exposed in winit can be split into different "support tiers":
|
||||
|
||||
- **Core:** Features that are essential to providing a well-formed abstraction over each platform's
|
||||
windowing and input APIs.
|
||||
- **Platform:** Platform-specific features that can't be meaningfully exposed through a common API and
|
||||
cannot be implemented outside of Winit without exposing a significant amount of Winit's internals
|
||||
or interfering with Winit's abstractions.
|
||||
- **Usability:** Features that are not strictly essential to Winit's functionality, but provide meaningful
|
||||
usability improvements and cannot be reasonably implemented in an external crate. These are
|
||||
generally optional and exposed through Cargo features.
|
||||
|
||||
Core features are taken care of by the core Winit maintainers. Platform features are not.
|
||||
When a platform feature is submitted, the submitter is considered the expert in the
|
||||
feature and may be asked to support the feature should it break in the future.
|
||||
|
||||
Winit ***does not*** directly expose functionality for drawing inside windows or creating native
|
||||
menus, but ***does*** commit to providing APIs that higher-level crates can use to implement that
|
||||
functionality.
|
||||
|
||||
## `1.0` and stability
|
||||
|
||||
When all core features are implemented to the satisfaction of the Winit maintainers, Winit 1.0 will
|
||||
be released and the library will enter maintenance mode. For the most part, new core features will not
|
||||
be added past this point. New platform features may be accepted and exposed through point releases.
|
||||
|
||||
### Tier upgrades
|
||||
Some platform features could in theory be exposed across multiple platforms, but have not gone
|
||||
through the implementation work necessary to function on all platforms. When one of these features
|
||||
gets implemented across all platforms, a PR can be opened to upgrade the feature to a core feature.
|
||||
If that gets accepted, the platform-specific functions gets deprecated and become permanently
|
||||
exposed through the core, cross-platform API.
|
||||
|
||||
# Features
|
||||
|
||||
## Extending this section
|
||||
|
||||
If your PR makes notable changes to Winit's features, please update this section as follows:
|
||||
|
||||
- If your PR adds a new feature, add a brief description to the relevant section. If the feature is a core
|
||||
feature, add a row to the feature matrix and describe what platforms the feature has been implemented on.
|
||||
|
||||
- If your PR begins a new API rework, add a row to the `Pending API Reworks` table. If the PR implements the
|
||||
API rework on all relevant platforms, please move it to the `Completed API Reworks` table.
|
||||
|
||||
- If your PR implements an already-existing feature on a new platform, either mark the feature as *completed*,
|
||||
or mark it as *mostly completed* and link to an issue describing the problems with the implementation.
|
||||
|
||||
## Core
|
||||
|
||||
### Windowing
|
||||
- **Window initialization**: Winit allows the creation of a window
|
||||
- **Providing pointer to init OpenGL**: Winit provides the necessary pointers to initialize a working opengl context
|
||||
- **Providing pointer to init Vulkan**: Same as OpenGL but for Vulkan
|
||||
- **Window decorations**: The windows created by winit are properly decorated, and the decorations can
|
||||
be deactivated
|
||||
- **Window decorations toggle**: Decorations can be turned on or off after window creation
|
||||
- **Window resizing**: The windows created by winit can be resized and generate the appropriate events
|
||||
when they are. The application can precisely control its window size if desired.
|
||||
- **Window resize increments**: When the window gets resized, the application can choose to snap the window's
|
||||
size to specific values.
|
||||
- **Window transparency**: Winit allows the creation of windows with a transparent background.
|
||||
- **Window maximization**: The windows created by winit can be maximized upon creation.
|
||||
- **Window maximization toggle**: The windows created by winit can be maximized and unmaximized after
|
||||
creation.
|
||||
- **Window minimization**: The windows created by winit can be minimized after creation.
|
||||
- **Fullscreen**: The windows created by winit can be put into fullscreen mode.
|
||||
- **Fullscreen toggle**: The windows created by winit can be switched to and from fullscreen after
|
||||
creation.
|
||||
- **Exclusive fullscreen**: Winit allows changing the video mode of the monitor
|
||||
for fullscreen windows, and if applicable, captures the monitor for exclusive
|
||||
use by this application.
|
||||
- **HiDPI support**: Winit assists developers in appropriately scaling HiDPI content.
|
||||
- **Popup / modal windows**: Windows can be created relative to the client area of other windows, and parent
|
||||
windows can be disabled in favor of popup windows. This feature also guarantees that popup windows
|
||||
get drawn above their owner.
|
||||
|
||||
|
||||
### System Information
|
||||
- **Monitor list**: Retrieve the list of monitors and their metadata, including which one is primary.
|
||||
- **Video mode query**: Monitors can be queried for their supported fullscreen video modes (consisting of resolution, refresh rate, and bit depth).
|
||||
|
||||
### 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 icon**: Changing the cursor icon, or hiding the cursor.
|
||||
- **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.
|
||||
- **Keyboard events**: Properly processing keyboard events using the user-specified keymap and
|
||||
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 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 or set a preferred theme
|
||||
|
||||
### macOS
|
||||
* Window activation policy
|
||||
* Window movable by background
|
||||
* Transparent titlebar
|
||||
* Hidden titlebar
|
||||
* Hidden titlebar buttons
|
||||
* Full-size content view
|
||||
|
||||
### Unix
|
||||
* Window urgency
|
||||
* X11 Window Class
|
||||
* X11 Override Redirect Flag
|
||||
* GTK Theme Variant
|
||||
* Base window size
|
||||
|
||||
### iOS
|
||||
* `winit` has a minimum OS requirement of iOS 8
|
||||
* Get the `UIWindow` object pointer
|
||||
* Get the `UIViewController` object pointer
|
||||
* Get the `UIView` object pointer
|
||||
* Get the `UIScreen` object pointer
|
||||
* Setting the `UIView` hidpi factor
|
||||
* Valid orientations
|
||||
* Home indicator visibility
|
||||
* Status bar visibility
|
||||
* Deferrring system gestures
|
||||
* Support for custom `UIView` derived class
|
||||
* Getting the device idiom
|
||||
* Getting the preferred video mode
|
||||
|
||||
### Web
|
||||
* Get if systems preferred color scheme is "dark"
|
||||
|
||||
## Usability
|
||||
* `serde`: Enables serialization/deserialization of certain types with Serde. (Maintainer: @Osspial)
|
||||
|
||||
## Compatibility Matrix
|
||||
|
||||
Legend:
|
||||
|
||||
- ✔️: Works as intended
|
||||
- ▢: Mostly works but some bugs are known
|
||||
- ❌: Missing feature or large bugs making it unusable
|
||||
- **N/A**: Not applicable for this platform
|
||||
- ❓: 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]|✔️ |✔️ |
|
||||
|Popup windows |❌ |❌ |❌ |❌ |❌ |❌ |**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**|
|
||||
|
||||
### 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 |❓ |❓ |❓ |❓ |❌ |❌ |❓ |
|
||||
|Drag window with cursor |✔️ |✔️ |✔️ |✔️ |**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]) |❌ |❌ |❌ |❌ |❌ |❌ |❓ |
|
||||
|
||||
### Completed API Reworks
|
||||
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |WASM |
|
||||
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- |
|
||||
|
||||
[#165]: https://github.com/rust-windowing/winit/issues/165
|
||||
[#219]: https://github.com/rust-windowing/winit/issues/219
|
||||
[#242]: https://github.com/rust-windowing/winit/issues/242
|
||||
[#306]: https://github.com/rust-windowing/winit/issues/306
|
||||
[#315]: https://github.com/rust-windowing/winit/issues/315
|
||||
[#319]: https://github.com/rust-windowing/winit/issues/319
|
||||
[#33]: https://github.com/rust-windowing/winit/issues/33
|
||||
[#459]: https://github.com/rust-windowing/winit/issues/459
|
||||
[#5]: https://github.com/rust-windowing/winit/issues/5
|
||||
[#63]: https://github.com/rust-windowing/winit/issues/63
|
||||
[#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
|
||||
[#804]: https://github.com/rust-windowing/winit/issues/804
|
||||
[#812]: https://github.com/rust-windowing/winit/issues/812
|
||||
23
HALL_OF_CHAMPIONS.md
Normal file
23
HALL_OF_CHAMPIONS.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# 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
|
||||
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
|
||||
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.
|
||||
* [@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.
|
||||
|
||||
[@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
|
||||
118
README.md
118
README.md
@@ -1,19 +1,27 @@
|
||||
# winit - Cross-platform window creation and management in Rust
|
||||
|
||||
[](https://crates.io/crates/winit)
|
||||
|
||||
[](https://crates.io/crates/winit)
|
||||
[](https://docs.rs/winit)
|
||||
|
||||
[](https://travis-ci.org/tomaka/winit)
|
||||
[](https://ci.appveyor.com/project/tomaka/winit/branch/master)
|
||||
[](https://github.com/rust-windowing/winit/actions)
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.14"
|
||||
winit = "0.26.1"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
|
||||
For features _within_ the scope of winit, see [FEATURES.md](FEATURES.md).
|
||||
|
||||
For features _outside_ the scope of winit, see [Missing features provided by other crates](https://github.com/rust-windowing/winit/wiki/Missing-features-provided-by-other-crates) in the wiki.
|
||||
|
||||
## Contact Us
|
||||
|
||||
Join us in any of these:
|
||||
|
||||
[](https://matrix.to/#/#rust-windowing:matrix.org)
|
||||
[](https://web.libera.chat/#winit)
|
||||
|
||||
## Usage
|
||||
|
||||
Winit is a window creation and management library. It can create windows and lets you handle
|
||||
@@ -25,32 +33,98 @@ show something on the window you need to use the platform-specific getters provi
|
||||
another library.
|
||||
|
||||
```rust
|
||||
extern crate winit;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
let window = winit::Window::new(&events_loop).unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
match event {
|
||||
winit::Event::WindowEvent {
|
||||
event: winit::WindowEvent::CloseRequested,
|
||||
..
|
||||
} => winit::ControlFlow::Break,
|
||||
_ => winit::ControlFlow::Continue,
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => *control_flow = ControlFlow::Exit,
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Winit is only officially supported on the latest stable version of the Rust compiler.
|
||||
|
||||
### Cargo Features
|
||||
|
||||
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
|
||||
|
||||
#### Emscripten and WebAssembly
|
||||
#### WebAssembly
|
||||
|
||||
Building a binary will yield a `.js` file. In order to use it in an HTML file, you need to:
|
||||
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
|
||||
|
||||
This library makes use of the [ndk-rs](https://github.com/rust-windowing/android-ndk-rs) crates, refer to that repo for more documentation.
|
||||
|
||||
The `ndk_glue` version needs to match the version used by `winit`. Otherwise, the application will not start correctly as `ndk_glue`'s internal NativeActivity static is not the same due to version mismatch.
|
||||
|
||||
`ndk_glue` <-> `winit` version comparison compatibility:
|
||||
|
||||
| winit | ndk_glue |
|
||||
| :---: | :------------------: |
|
||||
| 0.24 | `ndk_glue = "0.2.0"` |
|
||||
| 0.25 | `ndk_glue = "0.3.0"` |
|
||||
| 0.26 | `ndk_glue = "0.5.0"` |
|
||||
|
||||
Running on an Android device needs a dynamic system library, add this to Cargo.toml:
|
||||
|
||||
```toml
|
||||
[[example]]
|
||||
name = "request_redraw_threaded"
|
||||
crate-type = ["cdylib"]
|
||||
```
|
||||
|
||||
And add this to the example file to add the native activity glue:
|
||||
```rust
|
||||
#[cfg_attr(target_os = "android", ndk_glue::main(backtrace = "on"))]
|
||||
fn main() {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
And run the application with `cargo apk run --example request_redraw_threaded`
|
||||
|
||||
#### MacOS
|
||||
|
||||
To ensure compatibility with older MacOS systems, winit links to
|
||||
CGDisplayCreateUUIDFromDisplayID through the CoreGraphics framework.
|
||||
However, under certain setups this function is only available to be linked
|
||||
through the newer ColorSync framework. So, winit provides the
|
||||
`WINIT_LINK_COLORSYNC` environment variable which can be set to `1` or `true`
|
||||
while compiling to enable linking via ColorSync.
|
||||
|
||||
- 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.
|
||||
|
||||
17
appveyor.yml
17
appveyor.yml
@@ -1,17 +0,0 @@
|
||||
environment:
|
||||
matrix:
|
||||
- TARGET: x86_64-pc-windows-msvc
|
||||
- TARGET: i686-pc-windows-msvc
|
||||
- TARGET: i686-pc-windows-gnu
|
||||
install:
|
||||
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:TARGET}.exe"
|
||||
- rust-nightly-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
|
||||
- SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin
|
||||
- SET PATH=%PATH%;C:\MinGW\bin
|
||||
- rustc -V
|
||||
- cargo -V
|
||||
|
||||
build: false
|
||||
|
||||
test_script:
|
||||
- cargo test --verbose
|
||||
10
build.rs
Normal file
10
build.rs
Normal file
@@ -0,0 +1,10 @@
|
||||
fn main() {
|
||||
// If building for macos and WINIT_LINK_COLORSYNC is set to true
|
||||
// use CGDisplayCreateUUIDFromDisplayID from ColorSync instead of CoreGraphics
|
||||
if std::env::var("CARGO_CFG_TARGET_OS").map_or(false, |os| os == "macos")
|
||||
&& std::env::var("WINIT_LINK_COLORSYNC")
|
||||
.map_or(false, |v| v == "1" || v.eq_ignore_ascii_case("true"))
|
||||
{
|
||||
println!("cargo:rustc-cfg=use_colorsync_cgdisplaycreateuuidfromdisplayid");
|
||||
}
|
||||
}
|
||||
114
examples/control_flow.rs
Normal file
114
examples/control_flow.rs
Normal file
@@ -0,0 +1,114 @@
|
||||
use std::{thread, time};
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, KeyboardInput, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Mode {
|
||||
Wait,
|
||||
WaitUntil,
|
||||
Poll,
|
||||
}
|
||||
|
||||
const WAIT_TIME: time::Duration = time::Duration::from_millis(100);
|
||||
const POLL_SLEEP_TIME: time::Duration = time::Duration::from_millis(100);
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
|
||||
println!("Press '1' to switch to Wait mode.");
|
||||
println!("Press '2' to switch to WaitUntil mode.");
|
||||
println!("Press '3' to switch to Poll mode.");
|
||||
println!("Press 'R' to toggle request_redraw() calls.");
|
||||
println!("Press 'Esc' to close the window.");
|
||||
|
||||
let event_loop = EventLoop::new();
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("Press 1, 2, 3 to change control flow mode. Press R to toggle redraw requests.")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut mode = Mode::Wait;
|
||||
let mut request_redraw = false;
|
||||
let mut wait_cancelled = false;
|
||||
let mut close_requested = false;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
use winit::event::{ElementState, StartCause, VirtualKeyCode};
|
||||
println!("{:?}", event);
|
||||
match event {
|
||||
Event::NewEvents(start_cause) => {
|
||||
wait_cancelled = match start_cause {
|
||||
StartCause::WaitCancelled { .. } => mode == Mode::WaitUntil,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
close_requested = true;
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
virtual_keycode: Some(virtual_code),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match virtual_code {
|
||||
VirtualKeyCode::Key1 => {
|
||||
mode = Mode::Wait;
|
||||
println!("\nmode: {:?}\n", mode);
|
||||
}
|
||||
VirtualKeyCode::Key2 => {
|
||||
mode = Mode::WaitUntil;
|
||||
println!("\nmode: {:?}\n", mode);
|
||||
}
|
||||
VirtualKeyCode::Key3 => {
|
||||
mode = Mode::Poll;
|
||||
println!("\nmode: {:?}\n", mode);
|
||||
}
|
||||
VirtualKeyCode::R => {
|
||||
request_redraw = !request_redraw;
|
||||
println!("\nrequest_redraw: {}\n", request_redraw);
|
||||
}
|
||||
VirtualKeyCode::Escape => {
|
||||
close_requested = true;
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
Event::MainEventsCleared => {
|
||||
if request_redraw && !wait_cancelled && !close_requested {
|
||||
window.request_redraw();
|
||||
}
|
||||
if close_requested {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
}
|
||||
Event::RedrawRequested(_window_id) => {}
|
||||
Event::RedrawEventsCleared => {
|
||||
*control_flow = match mode {
|
||||
Mode::Wait => ControlFlow::Wait,
|
||||
Mode::WaitUntil => {
|
||||
if wait_cancelled {
|
||||
*control_flow
|
||||
} else {
|
||||
ControlFlow::WaitUntil(instant::Instant::now() + WAIT_TIME)
|
||||
}
|
||||
}
|
||||
Mode::Poll => {
|
||||
thread::sleep(POLL_SLEEP_TIME);
|
||||
ControlFlow::Poll
|
||||
}
|
||||
};
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,32 +1,88 @@
|
||||
extern crate winit;
|
||||
|
||||
use winit::{Event, ElementState, MouseCursor, WindowEvent, KeyboardInput, ControlFlow};
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyboardInput, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::{CursorIcon, WindowBuilder},
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = winit::WindowBuilder::new().build(&events_loop).unwrap();
|
||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
window.set_title("A fantastic window!");
|
||||
|
||||
let cursors = [MouseCursor::Default, MouseCursor::Crosshair, MouseCursor::Hand, MouseCursor::Arrow, MouseCursor::Move, MouseCursor::Text, MouseCursor::Wait, MouseCursor::Help, MouseCursor::Progress, MouseCursor::NotAllowed, MouseCursor::ContextMenu, MouseCursor::NoneCursor, MouseCursor::Cell, MouseCursor::VerticalText, MouseCursor::Alias, MouseCursor::Copy, MouseCursor::NoDrop, MouseCursor::Grab, MouseCursor::Grabbing, MouseCursor::AllScroll, MouseCursor::ZoomIn, MouseCursor::ZoomOut, MouseCursor::EResize, MouseCursor::NResize, MouseCursor::NeResize, MouseCursor::NwResize, MouseCursor::SResize, MouseCursor::SeResize, MouseCursor::SwResize, MouseCursor::WResize, MouseCursor::EwResize, MouseCursor::NsResize, MouseCursor::NeswResize, MouseCursor::NwseResize, MouseCursor::ColResize, MouseCursor::RowResize];
|
||||
let mut cursor_idx = 0;
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event: WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. }, .. } => {
|
||||
println!("Setting cursor to \"{:?}\"", cursors[cursor_idx]);
|
||||
window.set_cursor(cursors[cursor_idx]);
|
||||
if cursor_idx < cursors.len() - 1 {
|
||||
Event::WindowEvent {
|
||||
event:
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
println!("Setting cursor to \"{:?}\"", CURSORS[cursor_idx]);
|
||||
window.set_cursor_icon(CURSORS[cursor_idx]);
|
||||
if cursor_idx < CURSORS.len() - 1 {
|
||||
cursor_idx += 1;
|
||||
} else {
|
||||
cursor_idx = 0;
|
||||
}
|
||||
},
|
||||
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
|
||||
return ControlFlow::Break;
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
ControlFlow::Continue
|
||||
});
|
||||
}
|
||||
|
||||
const CURSORS: &[CursorIcon] = &[
|
||||
CursorIcon::Default,
|
||||
CursorIcon::Crosshair,
|
||||
CursorIcon::Hand,
|
||||
CursorIcon::Arrow,
|
||||
CursorIcon::Move,
|
||||
CursorIcon::Text,
|
||||
CursorIcon::Wait,
|
||||
CursorIcon::Help,
|
||||
CursorIcon::Progress,
|
||||
CursorIcon::NotAllowed,
|
||||
CursorIcon::ContextMenu,
|
||||
CursorIcon::Cell,
|
||||
CursorIcon::VerticalText,
|
||||
CursorIcon::Alias,
|
||||
CursorIcon::Copy,
|
||||
CursorIcon::NoDrop,
|
||||
CursorIcon::Grab,
|
||||
CursorIcon::Grabbing,
|
||||
CursorIcon::AllScroll,
|
||||
CursorIcon::ZoomIn,
|
||||
CursorIcon::ZoomOut,
|
||||
CursorIcon::EResize,
|
||||
CursorIcon::NResize,
|
||||
CursorIcon::NeResize,
|
||||
CursorIcon::NwResize,
|
||||
CursorIcon::SResize,
|
||||
CursorIcon::SeResize,
|
||||
CursorIcon::SwResize,
|
||||
CursorIcon::WResize,
|
||||
CursorIcon::EwResize,
|
||||
CursorIcon::NsResize,
|
||||
CursorIcon::NeswResize,
|
||||
CursorIcon::NwseResize,
|
||||
CursorIcon::ColResize,
|
||||
CursorIcon::RowResize,
|
||||
];
|
||||
|
||||
56
examples/cursor_grab.rs
Normal file
56
examples/cursor_grab.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("Super Cursor Grab'n'Hide Simulator 9000")
|
||||
.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::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
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()),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
WindowEvent::ModifiersChanged(m) => modifiers = m,
|
||||
_ => (),
|
||||
},
|
||||
Event::DeviceEvent { event, .. } => match event {
|
||||
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),
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
53
examples/custom_events.rs
Normal file
53
examples/custom_events.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn main() {
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum CustomEvent {
|
||||
Timer,
|
||||
}
|
||||
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::<CustomEvent>::with_user_event();
|
||||
|
||||
let _window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
// `EventLoopProxy` allows you to dispatch custom events to the main Winit event
|
||||
// loop from any thread.
|
||||
let event_loop_proxy = event_loop.create_proxy();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
// Wake up the `event_loop` once every second and dispatch a custom event
|
||||
// from a different thread.
|
||||
loop {
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
event_loop_proxy.send_event(CustomEvent::Timer).ok();
|
||||
}
|
||||
});
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::UserEvent(event) => println!("user event: {:?}", event),
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => *control_flow = ControlFlow::Exit,
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn main() {
|
||||
panic!("This example is not supported on web.");
|
||||
}
|
||||
73
examples/drag_window.rs
Normal file
73
examples/drag_window.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{
|
||||
ElementState, Event, KeyboardInput, MouseButton, StartCause, VirtualKeyCode, WindowEvent,
|
||||
},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::{Window, WindowBuilder, WindowId},
|
||||
};
|
||||
|
||||
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 = ControlFlow::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 {
|
||||
input:
|
||||
KeyboardInput {
|
||||
state: ElementState::Released,
|
||||
virtual_keycode: Some(VirtualKeyCode::X),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
switched = !switched;
|
||||
name_windows(entered_id, switched, &window_1, &window_2);
|
||||
println!("Switched!")
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
});
|
||||
}
|
||||
|
||||
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,69 +1,67 @@
|
||||
extern crate winit;
|
||||
use std::io::{stdin, stdout, Write};
|
||||
|
||||
use std::io::{self, Write};
|
||||
use winit::{ControlFlow, Event, WindowEvent};
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent};
|
||||
use winit::event_loop::{ControlFlow, EventLoop};
|
||||
use winit::monitor::{MonitorHandle, VideoMode};
|
||||
use winit::window::{Fullscreen, WindowBuilder};
|
||||
|
||||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
// enumerating monitors
|
||||
let monitor = {
|
||||
for (num, monitor) in events_loop.get_available_monitors().enumerate() {
|
||||
println!("Monitor #{}: {:?}", num, monitor.get_name());
|
||||
}
|
||||
print!("Please choose the fullscreen mode: (1) exclusive, (2) borderless: ");
|
||||
stdout().flush().unwrap();
|
||||
|
||||
print!("Please write the number of the monitor to use: ");
|
||||
io::stdout().flush().unwrap();
|
||||
let mut num = String::new();
|
||||
stdin().read_line(&mut num).unwrap();
|
||||
let num = num.trim().parse().expect("Please enter a number");
|
||||
|
||||
let mut num = String::new();
|
||||
io::stdin().read_line(&mut num).unwrap();
|
||||
let num = num.trim().parse().ok().expect("Please enter a number");
|
||||
let monitor = events_loop.get_available_monitors().nth(num).expect("Please enter a valid ID");
|
||||
let fullscreen = Some(match num {
|
||||
1 => Fullscreen::Exclusive(prompt_for_video_mode(&prompt_for_monitor(&event_loop))),
|
||||
2 => Fullscreen::Borderless(Some(prompt_for_monitor(&event_loop))),
|
||||
_ => panic!("Please enter a valid number"),
|
||||
});
|
||||
|
||||
println!("Using {:?}", monitor.get_name());
|
||||
|
||||
monitor
|
||||
};
|
||||
|
||||
let window = winit::WindowBuilder::new()
|
||||
.with_title("Hello world!")
|
||||
.with_fullscreen(Some(monitor))
|
||||
.build(&events_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut is_fullscreen = true;
|
||||
let mut is_maximized = false;
|
||||
let mut decorations = true;
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
println!("{:?}", event);
|
||||
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;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => return ControlFlow::Break,
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
winit::KeyboardInput {
|
||||
KeyboardInput {
|
||||
virtual_keycode: Some(virtual_code),
|
||||
state,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match (virtual_code, state) {
|
||||
(winit::VirtualKeyCode::Escape, _) => return ControlFlow::Break,
|
||||
(winit::VirtualKeyCode::F, winit::ElementState::Pressed) => {
|
||||
is_fullscreen = !is_fullscreen;
|
||||
if !is_fullscreen {
|
||||
(VirtualKeyCode::Escape, _) => *control_flow = ControlFlow::Exit,
|
||||
(VirtualKeyCode::F, ElementState::Pressed) => {
|
||||
if window.fullscreen().is_some() {
|
||||
window.set_fullscreen(None);
|
||||
} else {
|
||||
window.set_fullscreen(Some(window.get_current_monitor()));
|
||||
window.set_fullscreen(fullscreen.clone());
|
||||
}
|
||||
}
|
||||
(winit::VirtualKeyCode::M, winit::ElementState::Pressed) => {
|
||||
is_maximized = !is_maximized;
|
||||
window.set_maximized(is_maximized);
|
||||
(VirtualKeyCode::S, ElementState::Pressed) => {
|
||||
println!("window.fullscreen {:?}", window.fullscreen());
|
||||
}
|
||||
(winit::VirtualKeyCode::D, winit::ElementState::Pressed) => {
|
||||
(VirtualKeyCode::M, ElementState::Pressed) => {
|
||||
let is_maximized = window.is_maximized();
|
||||
window.set_maximized(!is_maximized);
|
||||
}
|
||||
(VirtualKeyCode::D, ElementState::Pressed) => {
|
||||
decorations = !decorations;
|
||||
window.set_decorations(decorations);
|
||||
}
|
||||
@@ -73,7 +71,48 @@ fn main() {
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
ControlFlow::Continue
|
||||
});
|
||||
}
|
||||
|
||||
// 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().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().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,45 +0,0 @@
|
||||
extern crate winit;
|
||||
|
||||
use winit::{ControlFlow, WindowEvent, ElementState, KeyboardInput};
|
||||
|
||||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
|
||||
let window = winit::WindowBuilder::new().build(&events_loop).unwrap();
|
||||
window.set_title("winit - Cursor grabbing test");
|
||||
|
||||
let mut grabbed = false;
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
println!("{:?}", event);
|
||||
|
||||
match event {
|
||||
winit::Event::WindowEvent { event, .. } => {
|
||||
match event {
|
||||
WindowEvent::KeyboardInput { input: KeyboardInput { state: ElementState::Pressed, .. }, .. } => {
|
||||
if grabbed {
|
||||
grabbed = false;
|
||||
window.set_cursor_state(winit::CursorState::Normal)
|
||||
.ok().expect("could not ungrab mouse cursor");
|
||||
} else {
|
||||
grabbed = true;
|
||||
window.set_cursor_state(winit::CursorState::Grab)
|
||||
.ok().expect("could not grab mouse cursor");
|
||||
}
|
||||
},
|
||||
|
||||
WindowEvent::CloseRequested => return ControlFlow::Break,
|
||||
|
||||
a @ WindowEvent::CursorMoved { .. } => {
|
||||
println!("{:?}", a);
|
||||
},
|
||||
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
ControlFlow::Continue
|
||||
});
|
||||
}
|
||||
@@ -1,74 +1,84 @@
|
||||
extern crate winit;
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, KeyboardInput, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let _window = winit::WindowBuilder::new()
|
||||
let _window = WindowBuilder::new()
|
||||
.with_title("Your faithful window")
|
||||
.build(&events_loop)
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut close_requested = false;
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
use winit::WindowEvent::*;
|
||||
use winit::ElementState::Released;
|
||||
use winit::VirtualKeyCode::{N, Y};
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
use winit::event::{
|
||||
ElementState::Released,
|
||||
VirtualKeyCode::{N, Y},
|
||||
};
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
winit::Event::WindowEvent { event, .. } => match event {
|
||||
CloseRequested => {
|
||||
// `CloseRequested` is sent when the close button on the window is pressed (or
|
||||
// through whatever other mechanisms the window manager provides for closing a
|
||||
// window). If you don't handle this event, the close button won't actually do
|
||||
// anything.
|
||||
Event::WindowEvent { event, .. } => {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
// `CloseRequested` is sent when the close button on the window is pressed (or
|
||||
// through whatever other mechanisms the window manager provides for closing a
|
||||
// window). If you don't handle this event, the close button won't actually do
|
||||
// anything.
|
||||
|
||||
// A common thing to do here is prompt the user if they have unsaved work.
|
||||
// Creating a proper dialog box for that is far beyond the scope of this
|
||||
// example, so here we'll just respond to the Y and N keys.
|
||||
println!("Are you ready to bid your window farewell? [Y/N]");
|
||||
close_requested = true;
|
||||
// A common thing to do here is prompt the user if they have unsaved work.
|
||||
// Creating a proper dialog box for that is far beyond the scope of this
|
||||
// example, so here we'll just respond to the Y and N keys.
|
||||
println!("Are you ready to bid your window farewell? [Y/N]");
|
||||
close_requested = true;
|
||||
|
||||
// In applications where you can safely close the window without further
|
||||
// action from the user, this is generally where you'd handle cleanup before
|
||||
// closing the window. How to close the window is detailed in the handler for
|
||||
// the Y key.
|
||||
}
|
||||
KeyboardInput {
|
||||
input:
|
||||
winit::KeyboardInput {
|
||||
virtual_keycode: Some(virtual_code),
|
||||
state: Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match virtual_code {
|
||||
Y => {
|
||||
if close_requested {
|
||||
// This is where you'll want to do any cleanup you need.
|
||||
println!("Buh-bye!");
|
||||
|
||||
// For a single-window application like this, you'd normally just
|
||||
// break out of the event loop here. If you wanted to keep running the
|
||||
// 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.
|
||||
return winit::ControlFlow::Break;
|
||||
}
|
||||
// In applications where you can safely close the window without further
|
||||
// action from the user, this is generally where you'd handle cleanup before
|
||||
// closing the window. How to close the window is detailed in the handler for
|
||||
// the Y key.
|
||||
}
|
||||
N => {
|
||||
if close_requested {
|
||||
println!("Your window will continue to stay by your side.");
|
||||
close_requested = false;
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
virtual_keycode: Some(virtual_code),
|
||||
state: Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
match virtual_code {
|
||||
Y => {
|
||||
if close_requested {
|
||||
// This is where you'll want to do any cleanup you need.
|
||||
println!("Buh-bye!");
|
||||
|
||||
// For a single-window application like this, you'd normally just
|
||||
// break out of the event loop here. If you wanted to keep running the
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
N => {
|
||||
if close_requested {
|
||||
println!("Your window will continue to stay by your side.");
|
||||
close_requested = false;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
winit::ControlFlow::Continue
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
extern crate winit;
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
dpi::LogicalSize,
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = winit::WindowBuilder::new()
|
||||
.build(&events_loop)
|
||||
.unwrap();
|
||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
|
||||
window.set_min_dimensions(Some((400, 200)));
|
||||
window.set_max_dimensions(Some((800, 400)));
|
||||
window.set_min_inner_size(Some(LogicalSize::new(400.0, 200.0)));
|
||||
window.set_max_inner_size(Some(LogicalSize::new(800.0, 400.0)));
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
println!("{:?}", event);
|
||||
|
||||
match event {
|
||||
winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => winit::ControlFlow::Break,
|
||||
_ => winit::ControlFlow::Continue,
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => *control_flow = ControlFlow::Exit,
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
41
examples/minimize.rs
Normal file
41
examples/minimize.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
extern crate winit;
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::event::{Event, VirtualKeyCode, WindowEvent};
|
||||
use winit::event_loop::{ControlFlow, EventLoop};
|
||||
use winit::window::WindowBuilder;
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().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);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
11
examples/monitor_list.rs
Normal file
11
examples/monitor_list.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{event_loop::EventLoop, window::WindowBuilder};
|
||||
|
||||
fn main() {
|
||||
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());
|
||||
}
|
||||
48
examples/mouse_wheel.rs
Normal file
48
examples/mouse_wheel.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{DeviceEvent, Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("Mouse Wheel events")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
_ => (),
|
||||
},
|
||||
Event::DeviceEvent { event, .. } => match event {
|
||||
DeviceEvent::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)
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
181
examples/multithreaded.rs
Normal file
181
examples/multithreaded.rs
Normal file
@@ -0,0 +1,181 @@
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
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},
|
||||
};
|
||||
|
||||
const WINDOW_COUNT: usize = 3;
|
||||
const WINDOW_SIZE: PhysicalSize<u32> = PhysicalSize::new(600, 400);
|
||||
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
let mut window_senders = HashMap::with_capacity(WINDOW_COUNT);
|
||||
for _ in 0..WINDOW_COUNT {
|
||||
let window = WindowBuilder::new()
|
||||
.with_inner_size(WINDOW_SIZE)
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
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);
|
||||
thread::spawn(move || {
|
||||
while let Ok(event) = rx.recv() {
|
||||
match event {
|
||||
WindowEvent::Moved { .. } => {
|
||||
// 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.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.get(video_mode_id);
|
||||
|
||||
// Different monitors may support different video modes,
|
||||
// and the index we chose previously may now point to a
|
||||
// completely different video mode, so notify the user
|
||||
if video_mode != previous_video_mode.as_ref() {
|
||||
println!(
|
||||
"Window moved to another monitor, picked video mode: {}",
|
||||
video_modes.get(video_mode_id).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
#[allow(deprecated)]
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
state: ElementState::Released,
|
||||
virtual_keycode: Some(key),
|
||||
modifiers,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
window.set_title(&format!("{:?}", key));
|
||||
let state = !modifiers.shift();
|
||||
use VirtualKeyCode::*;
|
||||
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 => {
|
||||
video_mode_id = match key {
|
||||
Left => video_mode_id.saturating_sub(1),
|
||||
Right => (video_modes.len() - 1).min(video_mode_id + 1),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
println!("Picking video mode: {}", video_modes[video_mode_id]);
|
||||
}
|
||||
F => window.set_fullscreen(match (state, modifiers.alt()) {
|
||||
(true, false) => Some(Fullscreen::Borderless(None)),
|
||||
(true, true) => {
|
||||
Some(Fullscreen::Exclusive(video_modes[video_mode_id].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()
|
||||
}
|
||||
}
|
||||
Z => {
|
||||
window.set_visible(false);
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
window.set_visible(true);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
event_loop.run(move |event, _event_loop, control_flow| {
|
||||
*control_flow = match !window_senders.is_empty() {
|
||||
true => ControlFlow::Wait,
|
||||
false => ControlFlow::Exit,
|
||||
};
|
||||
match event {
|
||||
Event::WindowEvent { event, window_id } => match event {
|
||||
WindowEvent::CloseRequested
|
||||
| WindowEvent::Destroyed
|
||||
| WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
state: ElementState::Released,
|
||||
virtual_keycode: Some(VirtualKeyCode::Escape),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
window_senders.remove(&window_id);
|
||||
}
|
||||
_ => {
|
||||
if let Some(tx) = window_senders.get(&window_id) {
|
||||
if let Some(event) = event.to_static() {
|
||||
tx.send(event).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn main() {
|
||||
panic!("Example not supported on Wasm");
|
||||
}
|
||||
@@ -1,33 +1,53 @@
|
||||
extern crate winit;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyboardInput, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::Window,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let mut windows = HashMap::new();
|
||||
for _ in 0..3 {
|
||||
let window = winit::Window::new(&events_loop).unwrap();
|
||||
let window = Window::new(&event_loop).unwrap();
|
||||
windows.insert(window.id(), window);
|
||||
}
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
event_loop.run(move |event, event_loop, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
winit::Event::WindowEvent {
|
||||
event: winit::WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} => {
|
||||
println!("Window {:?} has received the signal to close", window_id);
|
||||
Event::WindowEvent { event, window_id } => {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
println!("Window {:?} has received the signal to close", window_id);
|
||||
|
||||
// This drops the window, causing it to close.
|
||||
windows.remove(&window_id);
|
||||
// This drops the window, causing it to close.
|
||||
windows.remove(&window_id);
|
||||
|
||||
if windows.is_empty() {
|
||||
return winit::ControlFlow::Break;
|
||||
if windows.is_empty() {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
let window = Window::new(event_loop).unwrap();
|
||||
windows.insert(window.id(), window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
winit::ControlFlow::Continue
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
extern crate winit;
|
||||
|
||||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
|
||||
let _window = winit::WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&events_loop)
|
||||
.unwrap();
|
||||
|
||||
let proxy = events_loop.create_proxy();
|
||||
|
||||
std::thread::spawn(move || {
|
||||
// Wake up the `events_loop` once every second.
|
||||
loop {
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
proxy.wakeup().unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
println!("{:?}", event);
|
||||
match event {
|
||||
winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } =>
|
||||
winit::ControlFlow::Break,
|
||||
_ => winit::ControlFlow::Continue,
|
||||
}
|
||||
});
|
||||
}
|
||||
39
examples/request_redraw.rs
Normal file
39
examples/request_redraw.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().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| {
|
||||
println!("{:?}", event);
|
||||
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
WindowEvent::MouseInput {
|
||||
state: ElementState::Released,
|
||||
..
|
||||
} => {
|
||||
window.request_redraw();
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::RedrawRequested(_) => {
|
||||
println!("\nredrawing!\n");
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
46
examples/request_redraw_threaded.rs
Normal file
46
examples/request_redraw_threaded.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
fn main() {
|
||||
use std::{thread, time};
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
thread::spawn(move || loop {
|
||||
thread::sleep(time::Duration::from_secs(1));
|
||||
window.request_redraw();
|
||||
});
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
println!("{:?}", event);
|
||||
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
_ => (),
|
||||
},
|
||||
Event::RedrawRequested(_) => {
|
||||
println!("\nredrawing!\n");
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn main() {
|
||||
unimplemented!() // `Window` can't be sent between threads
|
||||
}
|
||||
46
examples/resizable.rs
Normal file
46
examples/resizable.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
dpi::LogicalSize,
|
||||
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
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_resizable(resizable)
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
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::KeyboardInput {
|
||||
input:
|
||||
KeyboardInput {
|
||||
virtual_keycode: Some(VirtualKeyCode::Space),
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
resizable = !resizable;
|
||||
println!("Resizable: {}", resizable);
|
||||
window.set_resizable(resizable);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
};
|
||||
});
|
||||
}
|
||||
53
examples/set_ime_position.rs
Normal file
53
examples/set_ime_position.rs
Normal file
@@ -0,0 +1,53 @@
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
dpi::PhysicalPosition,
|
||||
event::{ElementState, Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
window.set_title("A fantastic window!");
|
||||
|
||||
println!("Ime position will system default");
|
||||
println!("Click to set ime position to cursor's");
|
||||
|
||||
let mut cursor_position = PhysicalPosition::new(0.0, 0.0);
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
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
|
||||
);
|
||||
window.set_ime_position(cursor_position);
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
40
examples/timer.rs
Normal file
40
examples/timer.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use instant::Instant;
|
||||
use std::time::Duration;
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, StartCause, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let _window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let timer_length = Duration::new(1, 0);
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
println!("{:?}", event);
|
||||
|
||||
match event {
|
||||
Event::NewEvents(StartCause::Init) => {
|
||||
*control_flow = ControlFlow::WaitUntil(Instant::now() + timer_length)
|
||||
}
|
||||
Event::NewEvents(StartCause::ResumeTimeReached { .. }) => {
|
||||
*control_flow = ControlFlow::WaitUntil(Instant::now() + timer_length);
|
||||
println!("\nTimer\n");
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => *control_flow = ControlFlow::Exit,
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,20 +1,32 @@
|
||||
extern crate winit;
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = winit::WindowBuilder::new().with_decorations(false)
|
||||
.with_transparency(true)
|
||||
.build(&events_loop).unwrap();
|
||||
let window = WindowBuilder::new()
|
||||
.with_decorations(false)
|
||||
.with_transparent(true)
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
window.set_title("A fantastic window!");
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
println!("{:?}", event);
|
||||
|
||||
match event {
|
||||
winit::Event::WindowEvent { event: winit::WindowEvent::CloseRequested, .. } => winit::ControlFlow::Break,
|
||||
_ => winit::ControlFlow::Continue,
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => *control_flow = ControlFlow::Exit,
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
20
examples/video_modes.rs
Normal file
20
examples/video_modes.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::event_loop::EventLoop;
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
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);
|
||||
}
|
||||
}
|
||||
58
examples/web.rs
Normal file
58
examples/web.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
pub fn main() {
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
log::debug!("{:?}", event);
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => *control_flow = ControlFlow::Exit,
|
||||
Event::MainEventsCleared => {
|
||||
window.request_redraw();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
mod wasm {
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn run() {
|
||||
console_log::init_with_level(log::Level::Debug).expect("error initializing logger");
|
||||
|
||||
super::main();
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,33 @@
|
||||
extern crate winit;
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let _window = winit::WindowBuilder::new()
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&events_loop)
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
println!("{:?}", event);
|
||||
|
||||
match event {
|
||||
winit::Event::WindowEvent {
|
||||
event: winit::WindowEvent::CloseRequested,
|
||||
..
|
||||
} => winit::ControlFlow::Break,
|
||||
_ => winit::ControlFlow::Continue,
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => *control_flow = ControlFlow::Exit,
|
||||
Event::MainEventsCleared => {
|
||||
window.request_redraw();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
125
examples/window_debug.rs
Normal file
125
examples/window_debug.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
// 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},
|
||||
window::{Fullscreen, WindowBuilder},
|
||||
};
|
||||
|
||||
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(100.0, 100.0))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
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 visible = true;
|
||||
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::DeviceEvent {
|
||||
event:
|
||||
DeviceEvent::Key(KeyboardInput {
|
||||
virtual_keycode: Some(key),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
}),
|
||||
..
|
||||
} => match key {
|
||||
VirtualKeyCode::M => {
|
||||
if minimized {
|
||||
minimized = !minimized;
|
||||
window.set_minimized(minimized);
|
||||
}
|
||||
}
|
||||
VirtualKeyCode::V => {
|
||||
if !visible {
|
||||
visible = !visible;
|
||||
window.set_visible(visible);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::KeyboardInput { input, .. },
|
||||
..
|
||||
} => match input {
|
||||
KeyboardInput {
|
||||
virtual_keycode: Some(key),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
} => match key {
|
||||
VirtualKeyCode::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");
|
||||
}
|
||||
}
|
||||
VirtualKeyCode::F => {
|
||||
if window.fullscreen().is_some() {
|
||||
window.set_fullscreen(None);
|
||||
} else {
|
||||
let monitor = window.current_monitor();
|
||||
window.set_fullscreen(Some(Fullscreen::Borderless(monitor)));
|
||||
}
|
||||
}
|
||||
VirtualKeyCode::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 => {
|
||||
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,
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,95 +1,58 @@
|
||||
// Heads up: you need to compile this example with `--features icon_loading`.
|
||||
// `Icon::from_path` won't be available otherwise, though for your own applications, you could use
|
||||
// `Icon::from_rgba` if you don't want to depend on the `image` crate.
|
||||
|
||||
extern crate winit;
|
||||
#[cfg(feature = "icon_loading")]
|
||||
extern crate image;
|
||||
use std::path::Path;
|
||||
|
||||
use winit::Icon;
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::Event,
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::{Icon, WindowBuilder},
|
||||
};
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
fn main() {
|
||||
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,
|
||||
// since it seems to work well enough in most cases. Be careful about going too high, or
|
||||
// you'll be bitten by the low-quality downscaling built into the WM.
|
||||
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/examples/icon.png");
|
||||
// While `Icon::from_path` is the most straightforward, you have a few other options. If you
|
||||
// want to use the `include_bytes` macro, then pass the result to `Icon::from_bytes`. See the
|
||||
// docs for the full list of options (you'll have to generate the docs with the `icon_loading`
|
||||
// feature enabled).
|
||||
let icon = Icon::from_path(path).expect("Failed to open icon");
|
||||
|
||||
let mut events_loop = winit::EventsLoop::new();
|
||||
let icon = load_icon(Path::new(path));
|
||||
|
||||
let window = winit::WindowBuilder::new()
|
||||
let event_loop = EventLoop::new();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("An iconic window!")
|
||||
// At present, this only does anything on Windows and X11, so if you want to save load
|
||||
// time, you can put icon loading behind a function that returns `None` on other platforms.
|
||||
.with_window_icon(Some(icon))
|
||||
.build(&events_loop)
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
if let winit::Event::WindowEvent { event, .. } = event {
|
||||
use winit::WindowEvent::*;
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
use winit::event::WindowEvent::*;
|
||||
match event {
|
||||
CloseRequested => return winit::ControlFlow::Break,
|
||||
CloseRequested => *control_flow = ControlFlow::Exit,
|
||||
DroppedFile(path) => {
|
||||
use image::GenericImage;
|
||||
|
||||
let icon_image = image::open(path).expect("Failed to open window icon");
|
||||
|
||||
let (width, height) = icon_image.dimensions();
|
||||
const DESIRED_SIZE: u32 = 32;
|
||||
let (new_width, new_height) = if width == height {
|
||||
(DESIRED_SIZE, DESIRED_SIZE)
|
||||
} else {
|
||||
// Note that this will never divide by zero, due to the previous condition.
|
||||
let aspect_adjustment = DESIRED_SIZE as f64
|
||||
/ std::cmp::max(width, height) as f64;
|
||||
(
|
||||
(width as f64 * aspect_adjustment) as u32,
|
||||
(height as f64 * aspect_adjustment) as u32,
|
||||
)
|
||||
};
|
||||
|
||||
// By scaling the icon ourselves, we get higher-quality filtering and save
|
||||
// some memory.
|
||||
let icon = image::imageops::resize(
|
||||
&icon_image,
|
||||
new_width,
|
||||
new_height,
|
||||
image::FilterType::Lanczos3,
|
||||
);
|
||||
|
||||
let (offset_x, offset_y) = (
|
||||
(DESIRED_SIZE - new_width) / 2,
|
||||
(DESIRED_SIZE - new_height) / 2,
|
||||
);
|
||||
|
||||
let mut canvas = image::ImageBuffer::new(DESIRED_SIZE, DESIRED_SIZE);
|
||||
image::imageops::replace(
|
||||
&mut canvas,
|
||||
&icon,
|
||||
offset_x,
|
||||
offset_y,
|
||||
);
|
||||
|
||||
window.set_window_icon(Some(canvas.into()));
|
||||
},
|
||||
window.set_window_icon(Some(load_icon(&path)));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
winit::ControlFlow::Continue
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "icon_loading"))]
|
||||
fn main() {
|
||||
print!(
|
||||
r#"This example requires the `icon_loading` feature:
|
||||
cargo run --example window_icon --features icon_loading
|
||||
"#);
|
||||
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_rgba8();
|
||||
let (width, height) = image.dimensions();
|
||||
let rgba = image.into_raw();
|
||||
(rgba, width, height)
|
||||
};
|
||||
Icon::from_rgba(icon_rgba, icon_width, icon_height).expect("Failed to open icon")
|
||||
}
|
||||
|
||||
63
examples/window_run_return.rs
Normal file
63
examples/window_run_return.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
// 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"
|
||||
))]
|
||||
fn main() {
|
||||
use std::{thread::sleep, time::Duration};
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
platform::run_return::EventLoopExtRunReturn,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
let mut event_loop = EventLoop::new();
|
||||
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let _window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut quit = false;
|
||||
|
||||
while !quit {
|
||||
event_loop.run_return(|event, _, control_flow| {
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
if let Event::WindowEvent { event, .. } = &event {
|
||||
// Print only Window events to reduce noise
|
||||
println!("{:?}", event);
|
||||
}
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => {
|
||||
quit = true;
|
||||
}
|
||||
Event::MainEventsCleared => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
|
||||
// Sleep for 1/60 second to simulate rendering
|
||||
println!("rendering");
|
||||
sleep(Duration::from_millis(16));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "ios", target_os = "android", target_arch = "wasm32"))]
|
||||
fn main() {
|
||||
println!("This platform doesn't support run_return.");
|
||||
}
|
||||
3
rustfmt.toml
Normal file
3
rustfmt.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
force_explicit_abi=true
|
||||
use_field_init_shorthand=true
|
||||
# merge_imports=true
|
||||
568
src/dpi.rs
Normal file
568
src/dpi.rs
Normal file
@@ -0,0 +1,568 @@
|
||||
//! UI scaling is important, so read the docs for this module if you don't want to be confused.
|
||||
//!
|
||||
//! ## Why should I care about UI scaling?
|
||||
//!
|
||||
//! Modern computer screens don't have a consistent relationship between resolution and size.
|
||||
//! 1920x1080 is a common resolution for both desktop and mobile screens, despite mobile screens
|
||||
//! normally being less than a quarter the size of their desktop counterparts. What's more, neither
|
||||
//! desktop nor mobile screens are consistent resolutions within their own size classes - common
|
||||
//! mobile screens range from below 720p to above 1440p, and desktop screens range from 720p to 5K
|
||||
//! and beyond.
|
||||
//!
|
||||
//! Given that, it's a mistake to assume that 2D content will only be displayed on screens with
|
||||
//! a consistent pixel density. If you were to render a 96-pixel-square image on a 1080p screen,
|
||||
//! then render the same image on a similarly-sized 4K screen, the 4K rendition would only take up
|
||||
//! about a quarter of the physical space as it did on the 1080p screen. That issue is especially
|
||||
//! problematic with text rendering, where quarter-sized text becomes a significant legibility
|
||||
//! problem.
|
||||
//!
|
||||
//! Failure to account for the scale factor can create a significantly degraded user experience.
|
||||
//! Most notably, it can make users feel like they have bad eyesight, which will potentially cause
|
||||
//! them to think about growing elderly, resulting in them having an existential crisis. Once users
|
||||
//! enter that state, they will no longer be focused on your application.
|
||||
//!
|
||||
//! ## How should I handle it?
|
||||
//!
|
||||
//! The solution to this problem is to account for the device's *scale factor*. The scale factor is
|
||||
//! the factor UI elements should be scaled by to be consistent with the rest of the user's system -
|
||||
//! for example, a button that's normally 50 pixels across would be 100 pixels across on a device
|
||||
//! with a scale factor of `2.0`, or 75 pixels across with a scale factor of `1.5`.
|
||||
//!
|
||||
//! Many UI systems, such as CSS, expose DPI-dependent units like [points] or [picas]. That's
|
||||
//! usually a mistake, since there's no consistent mapping between the scale factor and the screen's
|
||||
//! actual DPI. Unless you're printing to a physical medium, you should work in scaled pixels rather
|
||||
//! than any DPI-dependent units.
|
||||
//!
|
||||
//! ### 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.
|
||||
//! 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.
|
||||
//!
|
||||
//! Winit's position and size types types are generic over their exact pixel type, `P`, to allow the
|
||||
//! API to have integer precision where appropriate (e.g. most window manipulation functions) and
|
||||
//! 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
|
||||
//! 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 can be found by calling [window.scale_factor()].
|
||||
//!
|
||||
//! ## How is the scale factor calculated?
|
||||
//!
|
||||
//! Scale factor is calculated differently on different platforms:
|
||||
//!
|
||||
//! - **Windows:** On Windows 8 and 10, per-monitor scaling is readily configured by users from the
|
||||
//! display settings. While users are free to select any option they want, they're only given a
|
||||
//! 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:** 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.
|
||||
//!
|
||||
//! 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
|
||||
//! variables to do what you want before resorting to `WINIT_X11_SCALE_FACTOR`.
|
||||
//! - **Wayland:** On Wayland, scale factors are set per-screen by the server, and are always
|
||||
//! integers (most often 1 or 2).
|
||||
//! - **iOS:** Scale factors are set by Apple to the value that best suits the device, and range
|
||||
//! from `1.0` to `3.0`. See [this article][apple_1] and [this article][apple_2] for more
|
||||
//! information.
|
||||
//! - **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)
|
||||
//! [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;
|
||||
fn cast<P: Pixel>(self) -> P {
|
||||
P::from_f64(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Pixel for u8 {
|
||||
fn from_f64(f: f64) -> Self {
|
||||
f.round() as u8
|
||||
}
|
||||
}
|
||||
impl Pixel for u16 {
|
||||
fn from_f64(f: f64) -> Self {
|
||||
f.round() as u16
|
||||
}
|
||||
}
|
||||
impl Pixel for u32 {
|
||||
fn from_f64(f: f64) -> Self {
|
||||
f.round() as u32
|
||||
}
|
||||
}
|
||||
impl Pixel for i8 {
|
||||
fn from_f64(f: f64) -> Self {
|
||||
f.round() as i8
|
||||
}
|
||||
}
|
||||
impl Pixel for i16 {
|
||||
fn from_f64(f: f64) -> Self {
|
||||
f.round() as i16
|
||||
}
|
||||
}
|
||||
impl Pixel for i32 {
|
||||
fn from_f64(f: f64) -> Self {
|
||||
f.round() as i32
|
||||
}
|
||||
}
|
||||
impl Pixel for f32 {
|
||||
fn from_f64(f: f64) -> Self {
|
||||
f as f32
|
||||
}
|
||||
}
|
||||
impl Pixel for f64 {
|
||||
fn from_f64(f: f64) -> Self {
|
||||
f
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks that the scale factor is a normal positive `f64`.
|
||||
///
|
||||
/// All functions that take a scale factor assert that this will return `true`. If you're sourcing scale factors from
|
||||
/// anywhere other than winit, it's recommended to validate them using this function before passing them to winit;
|
||||
/// otherwise, you risk panics.
|
||||
#[inline]
|
||||
pub fn validate_scale_factor(scale_factor: f64) -> bool {
|
||||
scale_factor.is_sign_positive() && scale_factor.is_normal()
|
||||
}
|
||||
|
||||
/// A position represented in logical pixels.
|
||||
///
|
||||
/// 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, Eq, PartialEq, Default, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct LogicalPosition<P> {
|
||||
pub x: P,
|
||||
pub y: P,
|
||||
}
|
||||
|
||||
impl<P> LogicalPosition<P> {
|
||||
#[inline]
|
||||
pub const fn new(x: P, y: P) -> Self {
|
||||
LogicalPosition { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> LogicalPosition<P> {
|
||||
#[inline]
|
||||
pub fn from_physical<T: Into<PhysicalPosition<X>>, X: Pixel>(
|
||||
physical: T,
|
||||
scale_factor: f64,
|
||||
) -> Self {
|
||||
physical.into().to_logical(scale_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalPosition<X> {
|
||||
assert!(validate_scale_factor(scale_factor));
|
||||
let x = self.x.into() * scale_factor;
|
||||
let y = self.y.into() * scale_factor;
|
||||
PhysicalPosition::new(x, y).cast()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cast<X: Pixel>(&self) -> LogicalPosition<X> {
|
||||
LogicalPosition {
|
||||
x: self.x.cast(),
|
||||
y: self.y.cast(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalPosition<P> {
|
||||
fn from((x, y): (X, X)) -> LogicalPosition<P> {
|
||||
LogicalPosition::new(x.cast(), 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())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalPosition<P> {
|
||||
fn from([x, y]: [X; 2]) -> LogicalPosition<P> {
|
||||
LogicalPosition::new(x.cast(), 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, Eq, PartialEq, Default, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct PhysicalPosition<P> {
|
||||
pub x: P,
|
||||
pub y: P,
|
||||
}
|
||||
|
||||
impl<P> PhysicalPosition<P> {
|
||||
#[inline]
|
||||
pub const fn new(x: P, y: P) -> Self {
|
||||
PhysicalPosition { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> PhysicalPosition<P> {
|
||||
#[inline]
|
||||
pub fn from_logical<T: Into<LogicalPosition<X>>, X: Pixel>(
|
||||
logical: T,
|
||||
scale_factor: f64,
|
||||
) -> Self {
|
||||
logical.into().to_physical(scale_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalPosition<X> {
|
||||
assert!(validate_scale_factor(scale_factor));
|
||||
let x = self.x.into() / scale_factor;
|
||||
let y = self.y.into() / scale_factor;
|
||||
LogicalPosition::new(x, y).cast()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cast<X: Pixel>(&self) -> PhysicalPosition<X> {
|
||||
PhysicalPosition {
|
||||
x: self.x.cast(),
|
||||
y: self.y.cast(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalPosition<P> {
|
||||
fn from((x, y): (X, X)) -> PhysicalPosition<P> {
|
||||
PhysicalPosition::new(x.cast(), 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())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalPosition<P> {
|
||||
fn from([x, y]: [X; 2]) -> PhysicalPosition<P> {
|
||||
PhysicalPosition::new(x.cast(), 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, Eq, PartialEq, Default, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct LogicalSize<P> {
|
||||
pub width: P,
|
||||
pub height: P,
|
||||
}
|
||||
|
||||
impl<P> LogicalSize<P> {
|
||||
#[inline]
|
||||
pub const fn new(width: P, height: P) -> Self {
|
||||
LogicalSize { width, height }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> LogicalSize<P> {
|
||||
#[inline]
|
||||
pub fn from_physical<T: Into<PhysicalSize<X>>, X: Pixel>(
|
||||
physical: T,
|
||||
scale_factor: f64,
|
||||
) -> Self {
|
||||
physical.into().to_logical(scale_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_physical<X: Pixel>(&self, scale_factor: f64) -> PhysicalSize<X> {
|
||||
assert!(validate_scale_factor(scale_factor));
|
||||
let width = self.width.into() * scale_factor;
|
||||
let height = self.height.into() * scale_factor;
|
||||
PhysicalSize::new(width, height).cast()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cast<X: Pixel>(&self) -> LogicalSize<X> {
|
||||
LogicalSize {
|
||||
width: self.width.cast(),
|
||||
height: self.height.cast(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> From<(X, X)> for LogicalSize<P> {
|
||||
fn from((x, y): (X, X)) -> LogicalSize<P> {
|
||||
LogicalSize::new(x.cast(), y.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())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> From<[X; 2]> for LogicalSize<P> {
|
||||
fn from([x, y]: [X; 2]) -> LogicalSize<P> {
|
||||
LogicalSize::new(x.cast(), y.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, Eq, PartialEq, Default, Hash)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct PhysicalSize<P> {
|
||||
pub width: P,
|
||||
pub height: P,
|
||||
}
|
||||
|
||||
impl<P> PhysicalSize<P> {
|
||||
#[inline]
|
||||
pub const fn new(width: P, height: P) -> Self {
|
||||
PhysicalSize { width, height }
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> PhysicalSize<P> {
|
||||
#[inline]
|
||||
pub fn from_logical<T: Into<LogicalSize<X>>, X: Pixel>(logical: T, scale_factor: f64) -> Self {
|
||||
logical.into().to_physical(scale_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_logical<X: Pixel>(&self, scale_factor: f64) -> LogicalSize<X> {
|
||||
assert!(validate_scale_factor(scale_factor));
|
||||
let width = self.width.into() / scale_factor;
|
||||
let height = self.height.into() / scale_factor;
|
||||
LogicalSize::new(width, height).cast()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cast<X: Pixel>(&self) -> PhysicalSize<X> {
|
||||
PhysicalSize {
|
||||
width: self.width.cast(),
|
||||
height: self.height.cast(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> From<(X, X)> for PhysicalSize<P> {
|
||||
fn from((x, y): (X, X)) -> PhysicalSize<P> {
|
||||
PhysicalSize::new(x.cast(), y.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())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel, X: Pixel> From<[X; 2]> for PhysicalSize<P> {
|
||||
fn from([x, y]: [X; 2]) -> PhysicalSize<P> {
|
||||
PhysicalSize::new(x.cast(), y.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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A size that's either physical or logical.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum Size {
|
||||
Physical(PhysicalSize<u32>),
|
||||
Logical(LogicalSize<f64>),
|
||||
}
|
||||
|
||||
impl Size {
|
||||
pub fn new<S: Into<Size>>(size: S) -> Size {
|
||||
size.into()
|
||||
}
|
||||
|
||||
pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalSize<P> {
|
||||
match *self {
|
||||
Size::Physical(size) => size.to_logical(scale_factor),
|
||||
Size::Logical(size) => size.cast(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalSize<P> {
|
||||
match *self {
|
||||
Size::Physical(size) => size.cast(),
|
||||
Size::Logical(size) => size.to_physical(scale_factor),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<PhysicalSize<P>> for Size {
|
||||
#[inline]
|
||||
fn from(size: PhysicalSize<P>) -> Size {
|
||||
Size::Physical(size.cast())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<LogicalSize<P>> for Size {
|
||||
#[inline]
|
||||
fn from(size: LogicalSize<P>) -> Size {
|
||||
Size::Logical(size.cast())
|
||||
}
|
||||
}
|
||||
|
||||
/// A position that's either physical or logical.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub enum Position {
|
||||
Physical(PhysicalPosition<i32>),
|
||||
Logical(LogicalPosition<f64>),
|
||||
}
|
||||
|
||||
impl Position {
|
||||
pub fn new<S: Into<Position>>(position: S) -> Position {
|
||||
position.into()
|
||||
}
|
||||
|
||||
pub fn to_logical<P: Pixel>(&self, scale_factor: f64) -> LogicalPosition<P> {
|
||||
match *self {
|
||||
Position::Physical(position) => position.to_logical(scale_factor),
|
||||
Position::Logical(position) => position.cast(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_physical<P: Pixel>(&self, scale_factor: f64) -> PhysicalPosition<P> {
|
||||
match *self {
|
||||
Position::Physical(position) => position.cast(),
|
||||
Position::Logical(position) => position.to_physical(scale_factor),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<PhysicalPosition<P>> for Position {
|
||||
#[inline]
|
||||
fn from(position: PhysicalPosition<P>) -> Position {
|
||||
Position::Physical(position.cast())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Pixel> From<LogicalPosition<P>> for Position {
|
||||
#[inline]
|
||||
fn from(position: LogicalPosition<P>) -> Position {
|
||||
Position::Logical(position.cast())
|
||||
}
|
||||
}
|
||||
82
src/error.rs
Normal file
82
src/error.rs
Normal file
@@ -0,0 +1,82 @@
|
||||
use std::{error, fmt};
|
||||
|
||||
use crate::platform_impl;
|
||||
|
||||
/// An error whose cause it outside Winit's control.
|
||||
#[derive(Debug)]
|
||||
pub enum ExternalError {
|
||||
/// The operation is not supported by the backend.
|
||||
NotSupported(NotSupportedError),
|
||||
/// The OS cannot perform the operation.
|
||||
Os(OsError),
|
||||
}
|
||||
|
||||
/// The error type for when the requested operation is not supported by the backend.
|
||||
#[derive(Clone)]
|
||||
pub struct NotSupportedError {
|
||||
_marker: (),
|
||||
}
|
||||
|
||||
/// The error type for when the OS cannot perform the requested operation.
|
||||
#[derive(Debug)]
|
||||
pub struct OsError {
|
||||
line: u32,
|
||||
file: &'static str,
|
||||
error: platform_impl::OsError,
|
||||
}
|
||||
|
||||
impl NotSupportedError {
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn new() -> NotSupportedError {
|
||||
NotSupportedError { _marker: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl OsError {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn new(line: u32, file: &'static str, error: platform_impl::OsError) -> OsError {
|
||||
OsError { line, file, error }
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! os_error {
|
||||
($error:expr) => {{
|
||||
crate::error::OsError::new(line!(), file!(), $error)
|
||||
}};
|
||||
}
|
||||
|
||||
impl fmt::Display for OsError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
f.pad(&format!(
|
||||
"os error at {}:{}: {}",
|
||||
self.file, self.line, self.error
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ExternalError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
match self {
|
||||
ExternalError::NotSupported(e) => e.fmt(f),
|
||||
ExternalError::Os(e) => e.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NotSupportedError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
f.debug_struct("NotSupportedError").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NotSupportedError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
f.pad("the requested operation is not supported by Winit")
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for OsError {}
|
||||
impl error::Error for ExternalError {}
|
||||
impl error::Error for NotSupportedError {}
|
||||
1072
src/event.rs
Normal file
1072
src/event.rs
Normal file
File diff suppressed because it is too large
Load Diff
236
src/event_loop.rs
Normal file
236
src/event_loop.rs
Normal file
@@ -0,0 +1,236 @@
|
||||
//! 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.
|
||||
//!
|
||||
//! 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::ops::Deref;
|
||||
use std::{error, fmt};
|
||||
|
||||
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()`
|
||||
/// 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.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
pub struct EventLoop<T: 'static> {
|
||||
pub(crate) event_loop: platform_impl::EventLoop<T>,
|
||||
pub(crate) _marker: ::std::marker::PhantomData<*mut ()>, // Not Send nor Sync
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// 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
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for EventLoop<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.pad("EventLoop { .. }")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for EventLoopWindowTarget<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.pad("EventLoopWindowTarget { .. }")
|
||||
}
|
||||
}
|
||||
|
||||
/// 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`.
|
||||
///
|
||||
/// ## 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
|
||||
#[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.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
/// - **Web:** Events are queued and usually sent when `requestAnimationFrame` fires but sometimes
|
||||
/// the events in the queue may be sent before the next `requestAnimationFrame` callback, for
|
||||
/// example when the scaling of the page has changed. This should be treated as an implementation
|
||||
/// detail which should not be relied on.
|
||||
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.
|
||||
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,
|
||||
}
|
||||
|
||||
impl Default for ControlFlow {
|
||||
#[inline(always)]
|
||||
fn default() -> ControlFlow {
|
||||
ControlFlow::Poll
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoop<()> {
|
||||
/// Builds a new event loop with a `()` as the user event type.
|
||||
///
|
||||
/// ***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.
|
||||
pub fn new() -> EventLoop<()> {
|
||||
EventLoop::<()>::with_user_event()
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
pub fn with_user_event() -> EventLoop<T> {
|
||||
EventLoop {
|
||||
event_loop: platform_impl::EventLoop::new(),
|
||||
_marker: ::std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Hijacks the calling thread and initializes the winit event loop with the provided
|
||||
/// closure. Since the closure is `'static`, it must be a `move` closure if it needs to
|
||||
/// access any data from the calling context.
|
||||
///
|
||||
/// See the [`ControlFlow`] docs for information on how changes to `&mut ControlFlow` impact the
|
||||
/// event loop's behavior.
|
||||
///
|
||||
/// Any values not passed to this function will *not* be dropped.
|
||||
///
|
||||
/// [`ControlFlow`]: crate::event_loop::ControlFlow
|
||||
#[inline]
|
||||
pub fn run<F>(self, event_handler: F) -> !
|
||||
where
|
||||
F: 'static + FnMut(Event<'_, T>, &EventLoopWindowTarget<T>, &mut ControlFlow),
|
||||
{
|
||||
self.event_loop.run(event_handler)
|
||||
}
|
||||
|
||||
/// 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for EventLoop<T> {
|
||||
type Target = EventLoopWindowTarget<T>;
|
||||
fn deref(&self) -> &EventLoopWindowTarget<T> {
|
||||
self.event_loop.window_target()
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to send custom events to `EventLoop`.
|
||||
pub struct EventLoopProxy<T: 'static> {
|
||||
event_loop_proxy: platform_impl::EventLoopProxy<T>,
|
||||
}
|
||||
|
||||
impl<T: 'static> Clone for EventLoopProxy<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
event_loop_proxy: self.event_loop_proxy.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopProxy<T> {
|
||||
/// 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.
|
||||
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed<T>> {
|
||||
self.event_loop_proxy.send_event(event)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> fmt::Debug for EventLoopProxy<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.pad("EventLoopProxy { .. }")
|
||||
}
|
||||
}
|
||||
|
||||
/// 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`.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct EventLoopClosed<T>(pub T);
|
||||
|
||||
impl<T> fmt::Display for EventLoopClosed<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("Tried to wake up a closed `EventLoop`")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> error::Error for EventLoopClosed<T> {}
|
||||
454
src/events.rs
454
src/events.rs
@@ -1,454 +0,0 @@
|
||||
use std::path::PathBuf;
|
||||
use {WindowId, DeviceId};
|
||||
|
||||
/// Describes a generic event.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Event {
|
||||
WindowEvent {
|
||||
window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
},
|
||||
DeviceEvent {
|
||||
device_id: DeviceId,
|
||||
event: DeviceEvent,
|
||||
},
|
||||
Awakened,
|
||||
|
||||
/// The application has been suspended or resumed.
|
||||
///
|
||||
/// The parameter is true if app was suspended, and false if it has been resumed.
|
||||
Suspended(bool),
|
||||
}
|
||||
|
||||
/// Describes an event from a `Window`.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum WindowEvent {
|
||||
|
||||
/// The size of the window has changed. Contains the client area's new dimensions.
|
||||
Resized(u32, u32),
|
||||
|
||||
/// The position of the window has changed. Contains the window's new position.
|
||||
Moved(i32, i32),
|
||||
|
||||
/// The window has been requested to close.
|
||||
CloseRequested,
|
||||
|
||||
/// The window has been destroyed.
|
||||
Destroyed,
|
||||
|
||||
/// A file has been dropped into the window.
|
||||
DroppedFile(PathBuf),
|
||||
|
||||
/// A file is being hovered over the window.
|
||||
HoveredFile(PathBuf),
|
||||
|
||||
/// A file was hovered, but has exited the window.
|
||||
HoveredFileCancelled,
|
||||
|
||||
/// The window received a unicode character.
|
||||
ReceivedCharacter(char),
|
||||
|
||||
/// The window gained or lost focus.
|
||||
///
|
||||
/// The parameter is true if the window has gained focus, and false if it has lost focus.
|
||||
Focused(bool),
|
||||
|
||||
/// An event from the keyboard has been received.
|
||||
KeyboardInput { device_id: DeviceId, input: KeyboardInput },
|
||||
|
||||
/// The cursor has moved on the window.
|
||||
CursorMoved {
|
||||
device_id: DeviceId,
|
||||
|
||||
/// (x,y) coords in pixels relative to the top-left corner of the window. Because the range of this data is
|
||||
/// limited by the display area and it may have been transformed by the OS to implement effects such as cursor
|
||||
/// acceleration, it should not be used to implement non-cursor-like interactions such as 3D camera control.
|
||||
position: (f64, f64),
|
||||
modifiers: ModifiersState
|
||||
},
|
||||
|
||||
/// The cursor has entered the window.
|
||||
CursorEntered { device_id: DeviceId },
|
||||
|
||||
/// The cursor has left the window.
|
||||
CursorLeft { device_id: DeviceId },
|
||||
|
||||
/// A mouse wheel movement or touchpad scroll occurred.
|
||||
MouseWheel { device_id: DeviceId, delta: MouseScrollDelta, phase: TouchPhase, modifiers: ModifiersState },
|
||||
|
||||
/// An mouse button press has been received.
|
||||
MouseInput { device_id: DeviceId, state: ElementState, button: MouseButton, modifiers: ModifiersState },
|
||||
|
||||
|
||||
/// Touchpad pressure event.
|
||||
///
|
||||
/// At the moment, only supported on Apple forcetouch-capable macbooks.
|
||||
/// The parameters are: pressure level (value between 0 and 1 representing how hard the touchpad
|
||||
/// is being pressed) and stage (integer representing the click level).
|
||||
TouchpadPressure { device_id: DeviceId, pressure: f32, stage: i64 },
|
||||
|
||||
/// Motion on some analog axis. May report data redundant to other, more specific events.
|
||||
AxisMotion { device_id: DeviceId, axis: AxisId, value: f64 },
|
||||
|
||||
/// The window needs to be redrawn.
|
||||
Refresh,
|
||||
|
||||
/// Touch event has been received
|
||||
Touch(Touch),
|
||||
|
||||
/// DPI scaling factor of the window has changed.
|
||||
///
|
||||
/// The following actions cause DPI changes:
|
||||
///
|
||||
/// * A user changes the resolution.
|
||||
/// * A user changes the desktop scaling value (e.g. in Control Panel on Windows).
|
||||
/// * A user moves the application window to a display with a different DPI.
|
||||
HiDPIFactorChanged(f32),
|
||||
}
|
||||
|
||||
/// Represents raw hardware events that are not associated with any particular window.
|
||||
///
|
||||
/// Useful for interactions that diverge significantly from a conventional 2D GUI, such as 3D camera or first-person
|
||||
/// game controls. Many physical actions, such as mouse movement, can produce both device and window events. Because
|
||||
/// window events typically arise from virtual devices (corresponding to GUI cursors and keyboard focus) the device IDs
|
||||
/// may not match.
|
||||
///
|
||||
/// Note that these events are delivered regardless of input focus.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum DeviceEvent {
|
||||
Added,
|
||||
Removed,
|
||||
|
||||
/// Change in physical position of a pointing device.
|
||||
///
|
||||
/// This represents raw, unfiltered physical motion. Not to be confused with `WindowEvent::CursorMoved`.
|
||||
MouseMotion {
|
||||
/// (x, y) change in position in unspecified units.
|
||||
///
|
||||
/// Different devices may use different units.
|
||||
delta: (f64, f64),
|
||||
},
|
||||
|
||||
/// Physical scroll event
|
||||
MouseWheel {
|
||||
delta: MouseScrollDelta,
|
||||
},
|
||||
|
||||
/// Motion on some analog axis. This event will be reported for all arbitrary input devices
|
||||
/// that winit supports on this platform, including mouse devices. If the device is a mouse
|
||||
/// device then this will be reported alongside the MouseMotion event.
|
||||
Motion { axis: AxisId, value: f64 },
|
||||
|
||||
Button { button: ButtonId, state: ElementState },
|
||||
Key(KeyboardInput),
|
||||
Text { codepoint: char },
|
||||
}
|
||||
|
||||
/// Describes a keyboard input event.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct KeyboardInput {
|
||||
/// Identifies the physical key pressed
|
||||
///
|
||||
/// This should not change if the user adjusts the host's keyboard map. Use when the physical location of the
|
||||
/// key is more important than the key's host GUI semantics, such as for movement controls in a first-person
|
||||
/// game.
|
||||
pub scancode: ScanCode,
|
||||
|
||||
pub state: ElementState,
|
||||
|
||||
/// Identifies the semantic meaning of the key
|
||||
///
|
||||
/// Use when the semantics of the key are more important than the physical location of the key, such as when
|
||||
/// implementing appropriate behavior for "page up."
|
||||
pub virtual_keycode: Option<VirtualKeyCode>,
|
||||
|
||||
/// Modifier keys active at the time of this input.
|
||||
///
|
||||
/// This is tracked internally to avoid tracking errors arising from modifier key state changes when events from
|
||||
/// this device are not being delivered to the application, e.g. due to keyboard focus being elsewhere.
|
||||
pub modifiers: ModifiersState
|
||||
}
|
||||
|
||||
/// Describes touch-screen input state.
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum TouchPhase {
|
||||
Started,
|
||||
Moved,
|
||||
Ended,
|
||||
Cancelled
|
||||
}
|
||||
|
||||
/// Represents touch event
|
||||
///
|
||||
/// Every time user touches screen new Start event with some finger id is generated.
|
||||
/// When the finger is removed from the screen End event with same id is generated.
|
||||
///
|
||||
/// For every id there will be at least 2 events with phases Start and End (or Cancelled).
|
||||
/// There may be 0 or more Move events.
|
||||
///
|
||||
///
|
||||
/// Depending on platform implementation id may or may not be reused by system after End event.
|
||||
///
|
||||
/// Gesture regonizer using this event should assume that Start event received with same id
|
||||
/// as previously received End event is a new finger and has nothing to do with an old one.
|
||||
///
|
||||
/// Touch may be cancelled if for example window lost focus.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Touch {
|
||||
pub device_id: DeviceId,
|
||||
pub phase: TouchPhase,
|
||||
pub location: (f64,f64),
|
||||
/// unique identifier of a finger.
|
||||
pub id: u64
|
||||
}
|
||||
|
||||
/// Hardware-dependent keyboard scan code.
|
||||
pub type ScanCode = u32;
|
||||
|
||||
/// Identifier for a specific analog axis on some device.
|
||||
pub type AxisId = u32;
|
||||
|
||||
/// Identifier for a specific button on some device.
|
||||
pub type ButtonId = u32;
|
||||
|
||||
/// Describes the input state of a key.
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum ElementState {
|
||||
Pressed,
|
||||
Released,
|
||||
}
|
||||
|
||||
/// Describes a button of a mouse controller.
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum MouseButton {
|
||||
Left,
|
||||
Right,
|
||||
Middle,
|
||||
Other(u8),
|
||||
}
|
||||
|
||||
/// Describes a difference in the mouse scroll wheel state.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum MouseScrollDelta {
|
||||
/// Amount in lines or rows to scroll in the horizontal
|
||||
/// and vertical directions.
|
||||
///
|
||||
/// Positive values indicate movement forward
|
||||
/// (away from the user) or rightwards.
|
||||
LineDelta(f32, f32),
|
||||
/// Amount in pixels to scroll in the horizontal and
|
||||
/// vertical direction.
|
||||
///
|
||||
/// Scroll events are expressed as a PixelDelta if
|
||||
/// supported by the device (eg. a touchpad) and
|
||||
/// platform.
|
||||
PixelDelta(f32, f32)
|
||||
}
|
||||
|
||||
/// Symbolic name for a keyboard key.
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
#[repr(u32)]
|
||||
pub enum VirtualKeyCode {
|
||||
/// The '1' key over the letters.
|
||||
Key1,
|
||||
/// The '2' key over the letters.
|
||||
Key2,
|
||||
/// The '3' key over the letters.
|
||||
Key3,
|
||||
/// The '4' key over the letters.
|
||||
Key4,
|
||||
/// The '5' key over the letters.
|
||||
Key5,
|
||||
/// The '6' key over the letters.
|
||||
Key6,
|
||||
/// The '7' key over the letters.
|
||||
Key7,
|
||||
/// The '8' key over the letters.
|
||||
Key8,
|
||||
/// The '9' key over the letters.
|
||||
Key9,
|
||||
/// The '0' key over the 'O' and 'P' keys.
|
||||
Key0,
|
||||
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
E,
|
||||
F,
|
||||
G,
|
||||
H,
|
||||
I,
|
||||
J,
|
||||
K,
|
||||
L,
|
||||
M,
|
||||
N,
|
||||
O,
|
||||
P,
|
||||
Q,
|
||||
R,
|
||||
S,
|
||||
T,
|
||||
U,
|
||||
V,
|
||||
W,
|
||||
X,
|
||||
Y,
|
||||
Z,
|
||||
|
||||
/// The Escape key, next to F1.
|
||||
Escape,
|
||||
|
||||
F1,
|
||||
F2,
|
||||
F3,
|
||||
F4,
|
||||
F5,
|
||||
F6,
|
||||
F7,
|
||||
F8,
|
||||
F9,
|
||||
F10,
|
||||
F11,
|
||||
F12,
|
||||
F13,
|
||||
F14,
|
||||
F15,
|
||||
|
||||
/// Print Screen/SysRq.
|
||||
Snapshot,
|
||||
/// Scroll Lock.
|
||||
Scroll,
|
||||
/// Pause/Break key, next to Scroll lock.
|
||||
Pause,
|
||||
|
||||
/// `Insert`, next to Backspace.
|
||||
Insert,
|
||||
Home,
|
||||
Delete,
|
||||
End,
|
||||
PageDown,
|
||||
PageUp,
|
||||
|
||||
Left,
|
||||
Up,
|
||||
Right,
|
||||
Down,
|
||||
|
||||
/// The Backspace key, right over Enter.
|
||||
// TODO: rename
|
||||
Back,
|
||||
/// The Enter key.
|
||||
Return,
|
||||
/// The space bar.
|
||||
Space,
|
||||
|
||||
/// The "Compose" key on Linux.
|
||||
Compose,
|
||||
|
||||
Caret,
|
||||
|
||||
Numlock,
|
||||
Numpad0,
|
||||
Numpad1,
|
||||
Numpad2,
|
||||
Numpad3,
|
||||
Numpad4,
|
||||
Numpad5,
|
||||
Numpad6,
|
||||
Numpad7,
|
||||
Numpad8,
|
||||
Numpad9,
|
||||
|
||||
AbntC1,
|
||||
AbntC2,
|
||||
Add,
|
||||
Apostrophe,
|
||||
Apps,
|
||||
At,
|
||||
Ax,
|
||||
Backslash,
|
||||
Calculator,
|
||||
Capital,
|
||||
Colon,
|
||||
Comma,
|
||||
Convert,
|
||||
Decimal,
|
||||
Divide,
|
||||
Equals,
|
||||
Grave,
|
||||
Kana,
|
||||
Kanji,
|
||||
LAlt,
|
||||
LBracket,
|
||||
LControl,
|
||||
LMenu,
|
||||
LShift,
|
||||
LWin,
|
||||
Mail,
|
||||
MediaSelect,
|
||||
MediaStop,
|
||||
Minus,
|
||||
Multiply,
|
||||
Mute,
|
||||
MyComputer,
|
||||
NavigateForward, // also called "Prior"
|
||||
NavigateBackward, // also called "Next"
|
||||
NextTrack,
|
||||
NoConvert,
|
||||
NumpadComma,
|
||||
NumpadEnter,
|
||||
NumpadEquals,
|
||||
OEM102,
|
||||
Period,
|
||||
PlayPause,
|
||||
Power,
|
||||
PrevTrack,
|
||||
RAlt,
|
||||
RBracket,
|
||||
RControl,
|
||||
RMenu,
|
||||
RShift,
|
||||
RWin,
|
||||
Semicolon,
|
||||
Slash,
|
||||
Sleep,
|
||||
Stop,
|
||||
Subtract,
|
||||
Sysrq,
|
||||
Tab,
|
||||
Underline,
|
||||
Unlabeled,
|
||||
VolumeDown,
|
||||
VolumeUp,
|
||||
Wake,
|
||||
WebBack,
|
||||
WebFavorites,
|
||||
WebForward,
|
||||
WebHome,
|
||||
WebRefresh,
|
||||
WebSearch,
|
||||
WebStop,
|
||||
Yen,
|
||||
Copy,
|
||||
Paste,
|
||||
Cut,
|
||||
}
|
||||
|
||||
/// Represents the current state of the keyboard modifiers
|
||||
///
|
||||
/// Each field of this struct represents a modifier and is `true` if this modifier is active.
|
||||
#[derive(Default, Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
pub struct ModifiersState {
|
||||
/// The "shift" key
|
||||
pub shift: bool,
|
||||
/// The "control" key
|
||||
pub ctrl: bool,
|
||||
/// The "alt" key
|
||||
pub alt: bool,
|
||||
/// The "logo" key
|
||||
///
|
||||
/// This is the "windows" key on PC and "command" key on Mac.
|
||||
pub logo: bool
|
||||
}
|
||||
177
src/icon.rs
177
src/icon.rs
@@ -1,12 +1,5 @@
|
||||
use std::{fmt, mem};
|
||||
use std::error::Error;
|
||||
#[cfg(feature = "icon_loading")]
|
||||
use std::io::{BufRead, Seek};
|
||||
#[cfg(feature = "icon_loading")]
|
||||
use std::path::Path;
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
use image;
|
||||
use crate::platform_impl::PlatformIcon;
|
||||
use std::{error::Error, fmt, io, mem};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
@@ -19,14 +12,12 @@ pub(crate) struct Pixel {
|
||||
|
||||
pub(crate) const PIXEL_SIZE: usize = mem::size_of::<Pixel>();
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug)]
|
||||
/// 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.
|
||||
ByteCountNotDivisibleBy4 {
|
||||
byte_count: usize,
|
||||
},
|
||||
ByteCountNotDivisibleBy4 { byte_count: usize },
|
||||
/// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`.
|
||||
/// At least one of your arguments is incorrect.
|
||||
DimensionsVsPixelCount {
|
||||
@@ -35,126 +26,110 @@ pub enum BadIcon {
|
||||
width_x_height: usize,
|
||||
pixel_count: usize,
|
||||
},
|
||||
/// Produced when underlying OS functionality failed to create the icon
|
||||
OsError(io::Error),
|
||||
}
|
||||
|
||||
impl fmt::Display for BadIcon {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
let msg = match self {
|
||||
&BadIcon::ByteCountNotDivisibleBy4 { byte_count } => format!(
|
||||
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,
|
||||
),
|
||||
&BadIcon::DimensionsVsPixelCount {
|
||||
BadIcon::DimensionsVsPixelCount {
|
||||
width,
|
||||
height,
|
||||
width_x_height,
|
||||
pixel_count,
|
||||
} => format!(
|
||||
} => 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,
|
||||
),
|
||||
};
|
||||
write!(formatter, "{}", msg)
|
||||
BadIcon::OsError(e) => write!(f, "OS error when instantiating the icon: {:?}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for BadIcon {
|
||||
fn description(&self) -> &str {
|
||||
"A valid icon cannot be created from these arguments"
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&Error> {
|
||||
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
/// An icon used for the window titlebar, taskbar, etc.
|
||||
///
|
||||
/// Enabling the `icon_loading` feature provides you with several convenience methods for creating
|
||||
/// an `Icon` from any format supported by the [image](https://github.com/PistonDevelopers/image)
|
||||
/// crate.
|
||||
pub struct Icon {
|
||||
pub(crate) struct RgbaIcon {
|
||||
pub(crate) rgba: Vec<u8>,
|
||||
pub(crate) width: u32,
|
||||
pub(crate) height: u32,
|
||||
}
|
||||
|
||||
/// For platforms which don't have window icons (e.g. web)
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) struct NoIcon;
|
||||
|
||||
#[allow(dead_code)] // These are not used on every platform
|
||||
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 {
|
||||
byte_count: rgba.len(),
|
||||
});
|
||||
}
|
||||
let pixel_count = rgba.len() / PIXEL_SIZE;
|
||||
if pixel_count != (width * height) as usize {
|
||||
Err(BadIcon::DimensionsVsPixelCount {
|
||||
width,
|
||||
height,
|
||||
width_x_height: (width * height) as usize,
|
||||
pixel_count,
|
||||
})
|
||||
} else {
|
||||
Ok(RgbaIcon {
|
||||
rgba,
|
||||
width,
|
||||
height,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NoIcon {
|
||||
pub fn from_rgba(rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, BadIcon> {
|
||||
// Create the rgba icon anyway to validate the input
|
||||
let _ = RgbaIcon::from_rgba(rgba, width, height)?;
|
||||
Ok(NoIcon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An icon used for the window titlebar, taskbar, etc.
|
||||
#[derive(Clone)]
|
||||
pub struct Icon {
|
||||
pub(crate) inner: PlatformIcon,
|
||||
}
|
||||
|
||||
impl fmt::Debug for Icon {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
fmt::Debug::fmt(&self.inner, formatter)
|
||||
}
|
||||
}
|
||||
|
||||
impl Icon {
|
||||
/// 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 { byte_count: rgba.len() });
|
||||
}
|
||||
let pixel_count = rgba.len() / PIXEL_SIZE;
|
||||
if pixel_count != (width * height) as usize {
|
||||
Err(BadIcon::DimensionsVsPixelCount {
|
||||
width,
|
||||
height,
|
||||
width_x_height: (width * height) as usize,
|
||||
pixel_count,
|
||||
})
|
||||
} else {
|
||||
Ok(Icon { rgba, width, height })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
/// Loads an `Icon` from the path of an image on the filesystem.
|
||||
pub fn from_path<P: AsRef<Path>>(path: P) -> image::ImageResult<Self> {
|
||||
image::open(path).map(Into::into)
|
||||
}
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
/// Loads an `Icon` from anything implementing `BufRead` and `Seek`.
|
||||
pub fn from_reader<R: BufRead + Seek>(
|
||||
reader: R,
|
||||
format: image::ImageFormat,
|
||||
) -> image::ImageResult<Self> {
|
||||
image::load(reader, format).map(Into::into)
|
||||
}
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
/// Loads an `Icon` from the unprocessed bytes of an image file.
|
||||
/// Uses heuristics to determine format.
|
||||
pub fn from_bytes(bytes: &[u8]) -> image::ImageResult<Self> {
|
||||
image::load_from_memory(bytes).map(Into::into)
|
||||
}
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
/// Loads an `Icon` from the unprocessed bytes of an image.
|
||||
pub fn from_bytes_with_format(
|
||||
bytes: &[u8],
|
||||
format: image::ImageFormat,
|
||||
) -> image::ImageResult<Self> {
|
||||
image::load_from_memory_with_format(bytes, format).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
impl From<image::DynamicImage> for Icon {
|
||||
fn from(image: image::DynamicImage) -> Self {
|
||||
use image::{GenericImage, Pixel};
|
||||
let (width, height) = image.dimensions();
|
||||
let mut rgba = Vec::with_capacity((width * height) as usize * PIXEL_SIZE);
|
||||
for (_, _, pixel) in image.pixels() {
|
||||
rgba.extend_from_slice(&pixel.to_rgba().data);
|
||||
}
|
||||
Icon { rgba, width, height }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "icon_loading")]
|
||||
impl From<image::RgbaImage> for Icon {
|
||||
fn from(buf: image::RgbaImage) -> Self {
|
||||
let (width, height) = buf.dimensions();
|
||||
let mut rgba = Vec::with_capacity((width * height) as usize * PIXEL_SIZE);
|
||||
for (_, _, pixel) in buf.enumerate_pixels() {
|
||||
rgba.extend_from_slice(&pixel.data);
|
||||
}
|
||||
Icon { rgba, width, height }
|
||||
Ok(Icon {
|
||||
inner: PlatformIcon::from_rgba(rgba, width, height)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
552
src/lib.rs
552
src/lib.rs
@@ -1,472 +1,160 @@
|
||||
//! Winit allows you to build a window on as many platforms as possible.
|
||||
//! Winit is a cross-platform window creation and event loop management library.
|
||||
//!
|
||||
//! # Building a window
|
||||
//! # Building windows
|
||||
//!
|
||||
//! Before you can build a window, you first need to build an `EventsLoop`. This is done with the
|
||||
//! `EventsLoop::new()` function. Example:
|
||||
//! Before you can build a [`Window`], you first need to build an [`EventLoop`]. This is done with the
|
||||
//! [`EventLoop::new()`] function.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use winit::EventsLoop;
|
||||
//! let events_loop = EventsLoop::new();
|
||||
//! use winit::event_loop::EventLoop;
|
||||
//! let event_loop = EventLoop::new();
|
||||
//! ```
|
||||
//!
|
||||
//! Once this is done there are two ways to create a window:
|
||||
//! Once this is done there are two ways to create a [`Window`]:
|
||||
//!
|
||||
//! - Calling `Window::new(&events_loop)`.
|
||||
//! - Calling `let builder = WindowBuilder::new()` then `builder.build(&events_loop)`.
|
||||
//! - Calling [`Window::new(&event_loop)`][window_new].
|
||||
//! - Calling [`let builder = WindowBuilder::new()`][window_builder_new] then [`builder.build(&event_loop)`][window_builder_build].
|
||||
//!
|
||||
//! The first way is the simpliest way and will give you default values for everything.
|
||||
//! The first method is the simplest, and will give you default values for everything. The second
|
||||
//! method allows you to customize the way your [`Window`] will look and behave by modifying the
|
||||
//! fields of the [`WindowBuilder`] object before you create the [`Window`].
|
||||
//!
|
||||
//! The second way allows you to customize the way your window will look and behave by modifying
|
||||
//! the fields of the `WindowBuilder` object before you create the window.
|
||||
//! # Event handling
|
||||
//!
|
||||
//! # Events handling
|
||||
//! Once a [`Window`] has been created, it will generate different *events*. A [`Window`] object can
|
||||
//! generate [`WindowEvent`]s when certain input events occur, such as a cursor moving over the
|
||||
//! window or a key getting pressed while the window is focused. Devices can generate
|
||||
//! [`DeviceEvent`]s, which contain unfiltered event data that isn't specific to a certain window.
|
||||
//! 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.
|
||||
//!
|
||||
//! Once a window has been created, it will *generate events*. For example whenever the user moves
|
||||
//! the window, resizes the window, moves the mouse, etc. an event is generated.
|
||||
//! 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.
|
||||
//!
|
||||
//! The events generated by a window can be retreived from the `EventsLoop` the window was created
|
||||
//! with.
|
||||
//! Winit no longer uses a `EventLoop::poll_events() -> impl Iterator<Event>`-based event loop
|
||||
//! 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.
|
||||
//!
|
||||
//! There are two ways to do so. The first is to call `events_loop.poll_events(...)`, which will
|
||||
//! retreive all the events pending on the windows and immediately return after no new event is
|
||||
//! available. You usually want to use this method in application that render continuously on the
|
||||
//! screen, such as video games.
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use winit::{Event, WindowEvent};
|
||||
//! # use winit::EventsLoop;
|
||||
//! # let mut events_loop = EventsLoop::new();
|
||||
//! use winit::{
|
||||
//! event::{Event, WindowEvent},
|
||||
//! event_loop::{ControlFlow, EventLoop},
|
||||
//! window::WindowBuilder,
|
||||
//! };
|
||||
//!
|
||||
//! loop {
|
||||
//! events_loop.poll_events(|event| {
|
||||
//! match event {
|
||||
//! Event::WindowEvent { event: WindowEvent::Resized(w, h), .. } => {
|
||||
//! println!("The window was resized to {}x{}", w, h);
|
||||
//! },
|
||||
//! _ => ()
|
||||
//! }
|
||||
//! });
|
||||
//! }
|
||||
//! ```
|
||||
//! let event_loop = EventLoop::new();
|
||||
//! let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
//!
|
||||
//! The second way is to call `events_loop.run_forever(...)`. As its name tells, it will run
|
||||
//! forever unless it is stopped by returning `ControlFlow::Break`.
|
||||
//! 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;
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use winit::{ControlFlow, Event, WindowEvent};
|
||||
//! # use winit::EventsLoop;
|
||||
//! # let mut events_loop = EventsLoop::new();
|
||||
//! // 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;
|
||||
//!
|
||||
//! events_loop.run_forever(|event| {
|
||||
//! match event {
|
||||
//! Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
|
||||
//! Event::WindowEvent {
|
||||
//! event: WindowEvent::CloseRequested,
|
||||
//! ..
|
||||
//! } => {
|
||||
//! println!("The close button was pressed; stopping");
|
||||
//! ControlFlow::Break
|
||||
//! *control_flow = ControlFlow::Exit
|
||||
//! },
|
||||
//! _ => ControlFlow::Continue,
|
||||
//! 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 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.
|
||||
//! },
|
||||
//! _ => ()
|
||||
//! }
|
||||
//! });
|
||||
//! ```
|
||||
//!
|
||||
//! If you use multiple windows, the `WindowEvent` event has a member named `window_id`. You can
|
||||
//! compare it with the value returned by the `id()` method of `Window` in order to know which
|
||||
//! window has received the event.
|
||||
//! [`Event`]`::`[`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be
|
||||
//! compared to the value returned by [`Window::id()`][window_id_fn] to determine which [`Window`]
|
||||
//! dispatched the event.
|
||||
//!
|
||||
//! # Drawing on the window
|
||||
//!
|
||||
//! Winit doesn't provide any function that allows drawing on a window. However it allows you to
|
||||
//! retreive the raw handle of the window (see the `os` module for that), which in turn allows you
|
||||
//! to create an OpenGL/Vulkan/DirectX/Metal/etc. context that will draw 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 and/or the
|
||||
//! [`raw_window_handle`] method), 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
|
||||
//! [`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
|
||||
//! [`Window`]: window::Window
|
||||
//! [`WindowId`]: window::WindowId
|
||||
//! [`WindowBuilder`]: window::WindowBuilder
|
||||
//! [window_new]: window::Window::new
|
||||
//! [window_builder_new]: window::WindowBuilder::new
|
||||
//! [window_builder_build]: window::WindowBuilder::build
|
||||
//! [window_id_fn]: window::Window::id
|
||||
//! [`Event`]: event::Event
|
||||
//! [`WindowEvent`]: event::WindowEvent
|
||||
//! [`DeviceEvent`]: event::DeviceEvent
|
||||
//! [`UserEvent`]: event::Event::UserEvent
|
||||
//! [`LoopDestroyed`]: event::Event::LoopDestroyed
|
||||
//! [`platform`]: platform
|
||||
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "windows"))]
|
||||
#![deny(rust_2018_idioms)]
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate libc;
|
||||
#[cfg(feature = "icon_loading")]
|
||||
extern crate image;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
extern crate winapi;
|
||||
extern crate log;
|
||||
#[cfg(feature = "serde")]
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
#[macro_use]
|
||||
extern crate objc;
|
||||
#[cfg(target_os = "macos")]
|
||||
extern crate cocoa;
|
||||
#[cfg(target_os = "macos")]
|
||||
extern crate core_foundation;
|
||||
#[cfg(target_os = "macos")]
|
||||
extern crate core_graphics;
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
extern crate x11_dl;
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
extern crate parking_lot;
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
extern crate percent_encoding;
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))]
|
||||
extern crate smithay_client_toolkit as sctk;
|
||||
|
||||
pub use events::*;
|
||||
pub use window::{AvailableMonitorsIter, MonitorId};
|
||||
pub use icon::*;
|
||||
|
||||
mod platform;
|
||||
mod events;
|
||||
mod window;
|
||||
pub mod dpi;
|
||||
#[macro_use]
|
||||
pub mod error;
|
||||
pub mod event;
|
||||
pub mod event_loop;
|
||||
mod icon;
|
||||
pub mod monitor;
|
||||
mod platform_impl;
|
||||
pub mod window;
|
||||
|
||||
pub mod os;
|
||||
|
||||
/// Represents a window.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use winit::{Event, EventsLoop, Window, WindowEvent, ControlFlow};
|
||||
///
|
||||
/// let mut events_loop = EventsLoop::new();
|
||||
/// let window = Window::new(&events_loop).unwrap();
|
||||
///
|
||||
/// events_loop.run_forever(|event| {
|
||||
/// match event {
|
||||
/// Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => {
|
||||
/// ControlFlow::Break
|
||||
/// },
|
||||
/// _ => ControlFlow::Continue,
|
||||
/// }
|
||||
/// });
|
||||
/// ```
|
||||
pub struct Window {
|
||||
window: platform::Window,
|
||||
}
|
||||
|
||||
/// Identifier of a window. Unique for each window.
|
||||
///
|
||||
/// Can be obtained with `window.id()`.
|
||||
///
|
||||
/// Whenever you receive an event specific to a window, this event contains a `WindowId` which you
|
||||
/// can then compare to the ids of your windows.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(platform::WindowId);
|
||||
|
||||
/// Identifier of an input device.
|
||||
///
|
||||
/// Whenever you receive an event arising from a particular input device, this event contains a `DeviceId` which
|
||||
/// identifies its origin. Note that devices may be virtual (representing an on-screen cursor and keyboard focus) or
|
||||
/// physical. Virtual devices typically aggregate inputs from multiple physical devices.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId(platform::DeviceId);
|
||||
|
||||
/// Provides a way to retreive events from the system and from the windows that were registered to
|
||||
/// the events loop.
|
||||
///
|
||||
/// An `EventsLoop` can be seen more or less as a "context". Calling `EventsLoop::new()`
|
||||
/// initializes everything that will be required to create windows. For example on Linux creating
|
||||
/// an events loop opens a connection to the X or Wayland server.
|
||||
///
|
||||
/// To wake up an `EventsLoop` from a another thread, see the `EventsLoopProxy` docs.
|
||||
///
|
||||
/// Note that the `EventsLoop` cannot be shared accross threads (due to platform-dependant logic
|
||||
/// forbiding it), as such it is neither `Send` nor `Sync`. If you need cross-thread access, the
|
||||
/// `Window` created from this `EventsLoop` _can_ be sent to an other thread, and the
|
||||
/// `EventsLoopProxy` allows you to wakeup an `EventsLoop` from an other thread.
|
||||
pub struct EventsLoop {
|
||||
events_loop: platform::EventsLoop,
|
||||
_marker: ::std::marker::PhantomData<*mut ()> // Not Send nor Sync
|
||||
}
|
||||
|
||||
/// Returned by the user callback given to the `EventsLoop::run_forever` method.
|
||||
///
|
||||
/// Indicates whether the `run_forever` method should continue or complete.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ControlFlow {
|
||||
/// Continue looping and waiting for events.
|
||||
Continue,
|
||||
/// Break from the event loop.
|
||||
Break,
|
||||
}
|
||||
|
||||
impl EventsLoop {
|
||||
/// Builds a new events loop.
|
||||
///
|
||||
/// 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.
|
||||
pub fn new() -> EventsLoop {
|
||||
EventsLoop {
|
||||
events_loop: platform::EventsLoop::new(),
|
||||
_marker: ::std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the list of all the monitors available on the system.
|
||||
///
|
||||
// Note: should be replaced with `-> impl Iterator` once stable.
|
||||
#[inline]
|
||||
pub fn get_available_monitors(&self) -> AvailableMonitorsIter {
|
||||
let data = self.events_loop.get_available_monitors();
|
||||
AvailableMonitorsIter{ data: data.into_iter() }
|
||||
}
|
||||
|
||||
/// Returns the primary monitor of the system.
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
MonitorId { inner: self.events_loop.get_primary_monitor() }
|
||||
}
|
||||
|
||||
/// Fetches all the events that are pending, calls the callback function for each of them,
|
||||
/// and returns.
|
||||
#[inline]
|
||||
pub fn poll_events<F>(&mut self, callback: F)
|
||||
where F: FnMut(Event)
|
||||
{
|
||||
self.events_loop.poll_events(callback)
|
||||
}
|
||||
|
||||
/// Calls `callback` every time an event is received. If no event is available, sleeps the
|
||||
/// current thread and waits for an event. If the callback returns `ControlFlow::Break` then
|
||||
/// `run_forever` will immediately return.
|
||||
#[inline]
|
||||
pub fn run_forever<F>(&mut self, callback: F)
|
||||
where F: FnMut(Event) -> ControlFlow
|
||||
{
|
||||
self.events_loop.run_forever(callback)
|
||||
}
|
||||
|
||||
/// Creates an `EventsLoopProxy` that can be used to wake up the `EventsLoop` from another
|
||||
/// thread.
|
||||
pub fn create_proxy(&self) -> EventsLoopProxy {
|
||||
EventsLoopProxy {
|
||||
events_loop_proxy: self.events_loop.create_proxy(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to wake up the `EventsLoop` from another thread.
|
||||
#[derive(Clone)]
|
||||
pub struct EventsLoopProxy {
|
||||
events_loop_proxy: platform::EventsLoopProxy,
|
||||
}
|
||||
|
||||
impl EventsLoopProxy {
|
||||
/// Wake up the `EventsLoop` from which this proxy was created.
|
||||
///
|
||||
/// This causes the `EventsLoop` to emit an `Awakened` event.
|
||||
///
|
||||
/// Returns an `Err` if the associated `EventsLoop` no longer exists.
|
||||
pub fn wakeup(&self) -> Result<(), EventsLoopClosed> {
|
||||
self.events_loop_proxy.wakeup()
|
||||
}
|
||||
}
|
||||
|
||||
/// The error that is returned when an `EventsLoopProxy` attempts to wake up an `EventsLoop` that
|
||||
/// no longer exists.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct EventsLoopClosed;
|
||||
|
||||
impl std::fmt::Display for EventsLoopClosed {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{}", std::error::Error::description(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for EventsLoopClosed {
|
||||
fn description(&self) -> &str {
|
||||
"Tried to wake up a closed `EventsLoop`"
|
||||
}
|
||||
}
|
||||
|
||||
/// Object that allows you to build windows.
|
||||
#[derive(Clone)]
|
||||
pub struct WindowBuilder {
|
||||
/// The attributes to use to create the window.
|
||||
pub window: WindowAttributes,
|
||||
|
||||
// Platform-specific configuration. Private.
|
||||
platform_specific: platform::PlatformSpecificWindowBuilderAttributes,
|
||||
}
|
||||
|
||||
/// Error that can happen while creating a window or a headless renderer.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum CreationError {
|
||||
OsError(String),
|
||||
/// TODO: remove this error
|
||||
NotSupported,
|
||||
}
|
||||
|
||||
impl CreationError {
|
||||
fn to_string(&self) -> &str {
|
||||
match *self {
|
||||
CreationError::OsError(ref text) => &text,
|
||||
CreationError::NotSupported => "Some of the requested attributes are not supported",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for CreationError {
|
||||
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
|
||||
formatter.write_str(self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for CreationError {
|
||||
fn description(&self) -> &str {
|
||||
self.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes the appearance of the mouse cursor.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum MouseCursor {
|
||||
/// The platform-dependent default cursor.
|
||||
Default,
|
||||
/// A simple crosshair.
|
||||
Crosshair,
|
||||
/// A hand (often used to indicate links in web browsers).
|
||||
Hand,
|
||||
/// Self explanatory.
|
||||
Arrow,
|
||||
/// Indicates something is to be moved.
|
||||
Move,
|
||||
/// Indicates text that may be selected or edited.
|
||||
Text,
|
||||
/// Program busy indicator.
|
||||
Wait,
|
||||
/// Help indicator (often rendered as a "?")
|
||||
Help,
|
||||
/// Progress indicator. Shows that processing is being done. But in contrast
|
||||
/// with "Wait" the user may still interact with the program. Often rendered
|
||||
/// as a spinning beach ball, or an arrow with a watch or hourglass.
|
||||
Progress,
|
||||
|
||||
/// Cursor showing that something cannot be done.
|
||||
NotAllowed,
|
||||
ContextMenu,
|
||||
NoneCursor,
|
||||
Cell,
|
||||
VerticalText,
|
||||
Alias,
|
||||
Copy,
|
||||
NoDrop,
|
||||
Grab,
|
||||
Grabbing,
|
||||
AllScroll,
|
||||
ZoomIn,
|
||||
ZoomOut,
|
||||
|
||||
/// Indicate that some edge is to be moved. For example, the 'SeResize' cursor
|
||||
/// is used when the movement starts from the south-east corner of the box.
|
||||
EResize,
|
||||
NResize,
|
||||
NeResize,
|
||||
NwResize,
|
||||
SResize,
|
||||
SeResize,
|
||||
SwResize,
|
||||
WResize,
|
||||
EwResize,
|
||||
NsResize,
|
||||
NeswResize,
|
||||
NwseResize,
|
||||
ColResize,
|
||||
RowResize,
|
||||
}
|
||||
|
||||
/// Describes how winit handles the cursor.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum CursorState {
|
||||
/// Normal cursor behavior.
|
||||
Normal,
|
||||
|
||||
/// The cursor will be invisible when over the window.
|
||||
Hide,
|
||||
|
||||
/// Grabs the mouse cursor. The cursor's motion will be confined to this
|
||||
/// window and the window has exclusive access to further events regarding
|
||||
/// the cursor.
|
||||
///
|
||||
/// This is useful for first-person cameras for example.
|
||||
Grab,
|
||||
}
|
||||
|
||||
/// Attributes to use when creating a window.
|
||||
#[derive(Clone)]
|
||||
pub struct WindowAttributes {
|
||||
/// The dimensions of the window. If this is `None`, some platform-specific dimensions will be
|
||||
/// used.
|
||||
///
|
||||
/// The default is `None`.
|
||||
pub dimensions: Option<(u32, u32)>,
|
||||
|
||||
/// The minimum dimensions a window can be, If this is `None`, the window will have no minimum dimensions (aside from reserved).
|
||||
///
|
||||
/// The default is `None`.
|
||||
pub min_dimensions: Option<(u32, u32)>,
|
||||
|
||||
/// The maximum dimensions a window can be, If this is `None`, the maximum will have no maximum or will be set to the primary monitor's dimensions by the platform.
|
||||
///
|
||||
/// The default is `None`.
|
||||
pub max_dimensions: Option<(u32, u32)>,
|
||||
|
||||
/// Whether the window should be set as fullscreen upon creation.
|
||||
///
|
||||
/// The default is `None`.
|
||||
pub fullscreen: Option<MonitorId>,
|
||||
|
||||
/// The title of the window in the title bar.
|
||||
///
|
||||
/// The default is `"winit window"`.
|
||||
pub title: String,
|
||||
|
||||
/// Whether the window should be maximized upon creation.
|
||||
///
|
||||
/// The default is `false`.
|
||||
pub maximized: bool,
|
||||
|
||||
/// Whether the window should be immediately visible upon creation.
|
||||
///
|
||||
/// The default is `true`.
|
||||
pub visible: bool,
|
||||
|
||||
/// Whether the the window should be transparent. If this is true, writing colors
|
||||
/// with alpha values different than `1.0` will produce a transparent window.
|
||||
///
|
||||
/// The default is `false`.
|
||||
pub transparent: bool,
|
||||
|
||||
/// Whether the window should have borders and bars.
|
||||
///
|
||||
/// The default is `true`.
|
||||
pub decorations: bool,
|
||||
|
||||
/// The window icon.
|
||||
///
|
||||
/// The default is `None`.
|
||||
pub window_icon: Option<Icon>,
|
||||
|
||||
/// [iOS only] Enable multitouch,
|
||||
/// see [multipleTouchEnabled](https://developer.apple.com/documentation/uikit/uiview/1622519-multipletouchenabled)
|
||||
pub multitouch: bool,
|
||||
}
|
||||
|
||||
impl Default for WindowAttributes {
|
||||
#[inline]
|
||||
fn default() -> WindowAttributes {
|
||||
WindowAttributes {
|
||||
dimensions: None,
|
||||
min_dimensions: None,
|
||||
max_dimensions: None,
|
||||
title: "winit window".to_owned(),
|
||||
maximized: false,
|
||||
fullscreen: None,
|
||||
visible: true,
|
||||
transparent: false,
|
||||
decorations: true,
|
||||
window_icon: None,
|
||||
multitouch: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub mod platform;
|
||||
|
||||
174
src/monitor.rs
Normal file
174
src/monitor.rs
Normal file
@@ -0,0 +1,174 @@
|
||||
//! 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 retrieved from one of the following methods, which return an iterator of
|
||||
//! [`MonitorHandle`][monitor_handle]:
|
||||
//! - [`EventLoopWindowTarget::available_monitors`][loop_get]
|
||||
//! - [`Window::available_monitors`][window_get].
|
||||
//!
|
||||
//! [monitor_handle]: crate::monitor::MonitorHandle
|
||||
//! [loop_get]: crate::event_loop::EventLoopWindowTarget::available_monitors
|
||||
//! [window_get]: crate::window::Window::available_monitors
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
platform_impl,
|
||||
};
|
||||
|
||||
/// Describes a fullscreen video mode of a monitor.
|
||||
///
|
||||
/// Can be acquired with:
|
||||
/// - [`MonitorHandle::video_modes`][monitor_get].
|
||||
///
|
||||
/// [monitor_get]: crate::monitor::MonitorHandle::video_modes
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct VideoMode {
|
||||
pub(crate) video_mode: platform_impl::VideoMode,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for VideoMode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.video_mode.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for VideoMode {
|
||||
fn partial_cmp(&self, other: &VideoMode) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
.then(
|
||||
self.refresh_rate()
|
||||
.cmp(&other.refresh_rate())
|
||||
.then(self.bit_depth().cmp(&other.bit_depth())),
|
||||
)
|
||||
.reverse(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl VideoMode {
|
||||
/// Returns the resolution of this video mode.
|
||||
#[inline]
|
||||
pub fn size(&self) -> PhysicalSize<u32> {
|
||||
self.video_mode.size()
|
||||
}
|
||||
|
||||
/// Returns the bit depth of this video mode, as in how many bits you have
|
||||
/// available per color. This is generally 24 bits or 32 bits on modern
|
||||
/// systems, depending on whether the alpha channel is counted or not.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Wayland:** 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.
|
||||
#[inline]
|
||||
pub fn refresh_rate(&self) -> u16 {
|
||||
self.video_mode.refresh_rate()
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for VideoMode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}x{} @ {} Hz ({} bpp)",
|
||||
self.size().width,
|
||||
self.size().height,
|
||||
self.refresh_rate(),
|
||||
self.bit_depth()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to a monitor.
|
||||
///
|
||||
/// Allows you to retrieve information about a given monitor and can be used in [`Window`] creation.
|
||||
///
|
||||
/// [`Window`]: crate::window::Window
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct MonitorHandle {
|
||||
pub(crate) inner: platform_impl::MonitorHandle,
|
||||
}
|
||||
|
||||
impl MonitorHandle {
|
||||
/// Returns a human-readable name of the monitor.
|
||||
///
|
||||
/// Returns `None` if the monitor doesn't exist anymore.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Web:** Always returns None
|
||||
#[inline]
|
||||
pub fn name(&self) -> Option<String> {
|
||||
self.inner.name()
|
||||
}
|
||||
|
||||
/// Returns the monitor's resolution.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Web:** Always returns (0,0)
|
||||
#[inline]
|
||||
pub fn size(&self) -> PhysicalSize<u32> {
|
||||
self.inner.size()
|
||||
}
|
||||
|
||||
/// Returns the top-left corner position of the monitor relative to the larger full
|
||||
/// screen area.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Web:** Always returns (0,0)
|
||||
#[inline]
|
||||
pub fn position(&self) -> PhysicalPosition<i32> {
|
||||
self.inner.position()
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **X11:** Can be overridden using the `WINIT_X11_SCALE_FACTOR` environment variable.
|
||||
/// - **Android:** Always returns 1.0.
|
||||
/// - **Web:** Always returns 1.0
|
||||
#[inline]
|
||||
pub fn scale_factor(&self) -> f64 {
|
||||
self.inner.scale_factor()
|
||||
}
|
||||
|
||||
/// Returns all fullscreen video modes supported by this monitor.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Web:** Always returns an empty iterator
|
||||
#[inline]
|
||||
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
|
||||
self.inner.video_modes()
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
#![cfg(any(target_os = "android"))]
|
||||
|
||||
use std::os::raw::c_void;
|
||||
use EventsLoop;
|
||||
use Window;
|
||||
use WindowBuilder;
|
||||
|
||||
/// Additional methods on `EventsLoop` that are specific to Android.
|
||||
pub trait EventsLoopExt {
|
||||
/// Makes it possible for glutin to register a callback when a suspend event happens on Android
|
||||
fn set_suspend_callback(&self, cb: Option<Box<Fn(bool) -> ()>>);
|
||||
}
|
||||
|
||||
impl EventsLoopExt for EventsLoop {
|
||||
fn set_suspend_callback(&self, cb: Option<Box<Fn(bool) -> ()>>) {
|
||||
self.events_loop.set_suspend_callback(cb);
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `Window` that are specific to Android.
|
||||
pub trait WindowExt {
|
||||
fn get_native_window(&self) -> *const c_void;
|
||||
}
|
||||
|
||||
impl WindowExt for Window {
|
||||
#[inline]
|
||||
fn get_native_window(&self) -> *const c_void {
|
||||
self.window.get_native_window()
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `WindowBuilder` that are specific to Android.
|
||||
pub trait WindowBuilderExt {
|
||||
|
||||
}
|
||||
|
||||
impl WindowBuilderExt for WindowBuilder {
|
||||
}
|
||||
151
src/os/macos.rs
151
src/os/macos.rs
@@ -1,151 +0,0 @@
|
||||
#![cfg(target_os = "macos")]
|
||||
|
||||
use std::convert::From;
|
||||
use std::os::raw::c_void;
|
||||
use cocoa::appkit::NSApplicationActivationPolicy;
|
||||
use {MonitorId, Window, WindowBuilder};
|
||||
|
||||
/// Additional methods on `Window` that are specific to MacOS.
|
||||
pub trait WindowExt {
|
||||
/// Returns a pointer to the cocoa `NSWindow` that is used by this window.
|
||||
///
|
||||
/// The pointer will become invalid when the `Window` is destroyed.
|
||||
fn get_nswindow(&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.
|
||||
fn get_nsview(&self) -> *mut c_void;
|
||||
}
|
||||
|
||||
impl WindowExt for Window {
|
||||
#[inline]
|
||||
fn get_nswindow(&self) -> *mut c_void {
|
||||
self.window.get_nswindow()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_nsview(&self) -> *mut c_void {
|
||||
self.window.get_nsview()
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to `NSApplicationActivationPolicy`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum ActivationPolicy {
|
||||
/// Corresponds to `NSApplicationActivationPolicyRegular`.
|
||||
Regular,
|
||||
/// Corresponds to `NSApplicationActivationPolicyAccessory`.
|
||||
Accessory,
|
||||
/// Corresponds to `NSApplicationActivationPolicyProhibited`.
|
||||
Prohibited,
|
||||
}
|
||||
|
||||
impl Default for ActivationPolicy {
|
||||
fn default() -> Self {
|
||||
ActivationPolicy::Regular
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ActivationPolicy> for NSApplicationActivationPolicy {
|
||||
fn from(activation_policy: ActivationPolicy) -> Self {
|
||||
match activation_policy {
|
||||
ActivationPolicy::Regular =>
|
||||
NSApplicationActivationPolicy::NSApplicationActivationPolicyRegular,
|
||||
ActivationPolicy::Accessory =>
|
||||
NSApplicationActivationPolicy::NSApplicationActivationPolicyAccessory,
|
||||
ActivationPolicy::Prohibited =>
|
||||
NSApplicationActivationPolicy::NSApplicationActivationPolicyProhibited,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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`
|
||||
pub trait WindowBuilderExt {
|
||||
fn with_activation_policy(self, activation_policy: ActivationPolicy) -> WindowBuilder;
|
||||
fn with_movable_by_window_background(self, movable_by_window_background: bool) -> WindowBuilder;
|
||||
fn with_titlebar_transparent(self, titlebar_transparent: bool) -> WindowBuilder;
|
||||
fn with_title_hidden(self, title_hidden: bool) -> WindowBuilder;
|
||||
fn with_titlebar_hidden(self, titlebar_hidden: bool) -> WindowBuilder;
|
||||
fn with_titlebar_buttons_hidden(self, titlebar_buttons_hidden: bool) -> WindowBuilder;
|
||||
fn with_fullsize_content_view(self, fullsize_content_view: bool) -> WindowBuilder;
|
||||
}
|
||||
|
||||
impl WindowBuilderExt for WindowBuilder {
|
||||
/// Sets the activation policy for the window being built
|
||||
#[inline]
|
||||
fn with_activation_policy(mut self, activation_policy: ActivationPolicy) -> WindowBuilder {
|
||||
self.platform_specific.activation_policy = activation_policy;
|
||||
self
|
||||
}
|
||||
|
||||
/// Enables click-and-drag behavior for the entire window, not just the titlebar
|
||||
#[inline]
|
||||
fn with_movable_by_window_background(mut self, movable_by_window_background: bool) -> WindowBuilder {
|
||||
self.platform_specific.movable_by_window_background = movable_by_window_background;
|
||||
self
|
||||
}
|
||||
|
||||
/// Makes the titlebar transparent and allows the content to appear behind it
|
||||
#[inline]
|
||||
fn with_titlebar_transparent(mut self, titlebar_transparent: bool) -> WindowBuilder {
|
||||
self.platform_specific.titlebar_transparent = titlebar_transparent;
|
||||
self
|
||||
}
|
||||
|
||||
/// Hides the window titlebar
|
||||
#[inline]
|
||||
fn with_titlebar_hidden(mut self, titlebar_hidden: bool) -> WindowBuilder {
|
||||
self.platform_specific.titlebar_hidden = titlebar_hidden;
|
||||
self
|
||||
}
|
||||
|
||||
/// Hides the window titlebar buttons
|
||||
#[inline]
|
||||
fn with_titlebar_buttons_hidden(mut self, titlebar_buttons_hidden: bool) -> WindowBuilder {
|
||||
self.platform_specific.titlebar_buttons_hidden = titlebar_buttons_hidden;
|
||||
self
|
||||
}
|
||||
|
||||
/// Hides the window title
|
||||
#[inline]
|
||||
fn with_title_hidden(mut self, title_hidden: bool) -> WindowBuilder {
|
||||
self.platform_specific.title_hidden = title_hidden;
|
||||
self
|
||||
}
|
||||
|
||||
/// Makes the window content appear behind the titlebar
|
||||
#[inline]
|
||||
fn with_fullsize_content_view(mut self, fullsize_content_view: bool) -> WindowBuilder {
|
||||
self.platform_specific.fullsize_content_view = fullsize_content_view;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `MonitorId` that are specific to MacOS.
|
||||
pub trait MonitorIdExt {
|
||||
/// Returns the identifier of the monitor for Cocoa.
|
||||
fn native_id(&self) -> u32;
|
||||
/// Returns a pointer to the NSScreen representing this monitor.
|
||||
fn get_nsscreen(&self) -> Option<*mut c_void>;
|
||||
}
|
||||
|
||||
impl MonitorIdExt for MonitorId {
|
||||
#[inline]
|
||||
fn native_id(&self) -> u32 {
|
||||
self.inner.get_native_identifier()
|
||||
}
|
||||
|
||||
fn get_nsscreen(&self) -> Option<*mut c_void> {
|
||||
self.inner.get_nsscreen().map(|s| s as *mut c_void)
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
//! Contains traits with platform-specific methods in them.
|
||||
//!
|
||||
//! Contains the follow modules:
|
||||
//!
|
||||
//! - `android`
|
||||
//! - `macos`
|
||||
//! - `unix`
|
||||
//! - `windows`
|
||||
//!
|
||||
//! However only the module corresponding to the platform you're compiling to will be available.
|
||||
//!
|
||||
pub mod android;
|
||||
pub mod macos;
|
||||
pub mod unix;
|
||||
pub mod windows;
|
||||
246
src/os/unix.rs
246
src/os/unix.rs
@@ -1,246 +0,0 @@
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
|
||||
use std::os::raw;
|
||||
use std::sync::Arc;
|
||||
use std::ptr;
|
||||
use EventsLoop;
|
||||
use MonitorId;
|
||||
use Window;
|
||||
use platform::EventsLoop as LinuxEventsLoop;
|
||||
use platform::Window as LinuxWindow;
|
||||
use WindowBuilder;
|
||||
use platform::x11::XConnection;
|
||||
use platform::x11::ffi::XVisualInfo;
|
||||
|
||||
// TODO: stupid hack so that glutin can do its work
|
||||
#[doc(hidden)]
|
||||
pub use platform::x11;
|
||||
|
||||
pub use platform::XNotSupported;
|
||||
|
||||
/// Additional methods on `EventsLoop` that are specific to Linux.
|
||||
pub trait EventsLoopExt {
|
||||
/// Builds a new `EventsLoop` that is forced to use X11.
|
||||
fn new_x11() -> Result<Self, XNotSupported>
|
||||
where Self: Sized;
|
||||
|
||||
/// Builds a new `EventsLoop` that is forced to use Wayland.
|
||||
fn new_wayland() -> Self
|
||||
where Self: Sized;
|
||||
|
||||
/// True if the `EventsLoop` uses Wayland.
|
||||
fn is_wayland(&self) -> bool;
|
||||
|
||||
/// True if the `EventsLoop` uses X11.
|
||||
fn is_x11(&self) -> bool;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>>;
|
||||
}
|
||||
|
||||
impl EventsLoopExt for EventsLoop {
|
||||
#[inline]
|
||||
fn new_x11() -> Result<Self, XNotSupported> {
|
||||
LinuxEventsLoop::new_x11().map(|ev|
|
||||
EventsLoop {
|
||||
events_loop: ev,
|
||||
_marker: ::std::marker::PhantomData,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_wayland() -> Self {
|
||||
EventsLoop {
|
||||
events_loop: match LinuxEventsLoop::new_wayland() {
|
||||
Ok(e) => e,
|
||||
Err(_) => panic!() // TODO: propagate
|
||||
},
|
||||
_marker: ::std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_wayland(&self) -> bool {
|
||||
self.events_loop.is_wayland()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_x11(&self) -> bool {
|
||||
!self.events_loop.is_wayland()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>> {
|
||||
self.events_loop.x_connection().cloned()
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `Window` that are specific to Unix.
|
||||
pub trait WindowExt {
|
||||
/// 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 get_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 get_xlib_display(&self) -> Option<*mut raw::c_void>;
|
||||
|
||||
fn get_xlib_screen_id(&self) -> Option<raw::c_int>;
|
||||
|
||||
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>>;
|
||||
|
||||
fn send_xim_spot(&self, x: i16, y: i16);
|
||||
|
||||
/// 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 get_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 get_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 get_wayland_display(&self) -> Option<*mut raw::c_void>;
|
||||
|
||||
/// 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 WindowExt for Window {
|
||||
#[inline]
|
||||
fn get_xlib_window(&self) -> Option<raw::c_ulong> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.get_xlib_window()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_xlib_display(&self) -> Option<*mut raw::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.get_xlib_display()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_xlib_screen_id(&self) -> Option<raw::c_int> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.get_xlib_screen_id()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.get_xlib_xconnection()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_xcb_connection(&self) -> Option<*mut raw::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.get_xcb_connection()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn send_xim_spot(&self, x: i16, y: i16) {
|
||||
if let LinuxWindow::X(ref w) = self.window {
|
||||
w.send_xim_spot(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_wayland_surface(&self) -> Option<*mut raw::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::Wayland(ref w) => Some(w.get_surface().c_ptr() as *mut _),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_wayland_display(&self) -> Option<*mut raw::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::Wayland(ref w) => Some(w.get_display().c_ptr() as *mut _),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_ready(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `WindowBuilder` that are specific to Unix.
|
||||
pub trait WindowBuilderExt {
|
||||
fn with_x11_visual<T>(self, visual_infos: *const T) -> WindowBuilder;
|
||||
fn with_x11_screen(self, screen_id: i32) -> WindowBuilder;
|
||||
|
||||
/// Build window with resize increment hint. Only implemented on X11.
|
||||
fn with_resize_increments(self, width_inc: i32, height_inc: i32) -> WindowBuilder;
|
||||
/// Build window with base size hint. Only implemented on X11.
|
||||
fn with_base_size(self, base_width: i32, base_height: i32) -> WindowBuilder;
|
||||
}
|
||||
|
||||
impl WindowBuilderExt for WindowBuilder {
|
||||
#[inline]
|
||||
fn with_x11_visual<T>(mut self, visual_infos: *const T) -> WindowBuilder {
|
||||
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) -> WindowBuilder {
|
||||
self.platform_specific.screen_id = Some(screen_id);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_resize_increments(mut self, width_inc: i32, height_inc: i32) -> WindowBuilder {
|
||||
self.platform_specific.resize_increments = Some((width_inc, height_inc));
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_base_size(mut self, base_width: i32, base_height: i32) -> WindowBuilder {
|
||||
self.platform_specific.base_size = Some((base_width, base_height));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `MonitorId` that are specific to Linux.
|
||||
pub trait MonitorIdExt {
|
||||
/// Returns the inner identifier of the monitor.
|
||||
fn native_id(&self) -> u32;
|
||||
}
|
||||
|
||||
impl MonitorIdExt for MonitorId {
|
||||
#[inline]
|
||||
fn native_id(&self) -> u32 {
|
||||
self.inner.get_native_identifier()
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
#![cfg(target_os = "windows")]
|
||||
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use libc;
|
||||
use winapi::shared::windef::HWND;
|
||||
|
||||
use {DeviceId, Icon, MonitorId, Window, WindowBuilder};
|
||||
|
||||
/// Additional methods on `Window` that are specific to Windows.
|
||||
pub trait WindowExt {
|
||||
/// Returns the native handle that is used by this window.
|
||||
///
|
||||
/// The pointer will become invalid when the native window was destroyed.
|
||||
fn get_hwnd(&self) -> *mut libc::c_void;
|
||||
|
||||
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
|
||||
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>);
|
||||
}
|
||||
|
||||
impl WindowExt for Window {
|
||||
#[inline]
|
||||
fn get_hwnd(&self) -> *mut libc::c_void {
|
||||
self.window.hwnd() as *mut _
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>) {
|
||||
self.window.set_taskbar_icon(taskbar_icon)
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `WindowBuilder` that are specific to Windows.
|
||||
pub trait WindowBuilderExt {
|
||||
/// Sets a parent to the window to be created.
|
||||
fn with_parent_window(self, parent: HWND) -> WindowBuilder;
|
||||
|
||||
/// This sets `ICON_BIG`. A good ceiling here is 256x256.
|
||||
fn with_taskbar_icon(self, taskbar_icon: Option<Icon>) -> WindowBuilder;
|
||||
}
|
||||
|
||||
impl WindowBuilderExt for WindowBuilder {
|
||||
#[inline]
|
||||
fn with_parent_window(mut self, parent: HWND) -> WindowBuilder {
|
||||
self.platform_specific.parent = Some(parent);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_taskbar_icon(mut self, taskbar_icon: Option<Icon>) -> WindowBuilder {
|
||||
self.platform_specific.taskbar_icon = taskbar_icon;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `MonitorId` that are specific to Windows.
|
||||
pub trait MonitorIdExt {
|
||||
/// Returns the name of the monitor adapter specific to the Win32 API.
|
||||
fn native_id(&self) -> String;
|
||||
|
||||
/// Returns the handle of the monitor - `HMONITOR`.
|
||||
fn hmonitor(&self) -> *mut c_void;
|
||||
}
|
||||
|
||||
impl MonitorIdExt for MonitorId {
|
||||
#[inline]
|
||||
fn native_id(&self) -> String {
|
||||
self.inner.get_native_identifier()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hmonitor(&self) -> *mut c_void {
|
||||
self.inner.get_hmonitor() as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `DeviceId` that are specific to Windows.
|
||||
pub trait DeviceIdExt {
|
||||
/// Returns an identifier that persistently refers to this specific device.
|
||||
///
|
||||
/// Will return `None` if the device is no longer available.
|
||||
fn get_persistent_identifier(&self) -> Option<String>;
|
||||
}
|
||||
|
||||
impl DeviceIdExt for DeviceId {
|
||||
#[inline]
|
||||
fn get_persistent_identifier(&self) -> Option<String> {
|
||||
self.0.get_persistent_identifier()
|
||||
}
|
||||
}
|
||||
40
src/platform/android.rs
Normal file
40
src/platform/android.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
#![cfg(any(target_os = "android"))]
|
||||
|
||||
use crate::{
|
||||
event_loop::{EventLoop, EventLoopWindowTarget},
|
||||
window::{Window, WindowBuilder},
|
||||
};
|
||||
use ndk::configuration::Configuration;
|
||||
use ndk_glue::Rect;
|
||||
|
||||
/// Additional methods on `EventLoop` that are specific to Android.
|
||||
pub trait EventLoopExtAndroid {}
|
||||
|
||||
impl<T> EventLoopExtAndroid for EventLoop<T> {}
|
||||
|
||||
/// 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 content_rect(&self) -> Rect;
|
||||
|
||||
fn config(&self) -> Configuration;
|
||||
}
|
||||
|
||||
impl WindowExtAndroid for Window {
|
||||
fn content_rect(&self) -> Rect {
|
||||
self.window.content_rect()
|
||||
}
|
||||
|
||||
fn config(&self) -> Configuration {
|
||||
self.window.config()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EventLoopWindowTargetExtAndroid for EventLoopWindowTarget<T> {}
|
||||
|
||||
/// Additional methods on `WindowBuilder` that are specific to Android.
|
||||
pub trait WindowBuilderExtAndroid {}
|
||||
|
||||
impl WindowBuilderExtAndroid for WindowBuilder {}
|
||||
@@ -1,108 +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 {}
|
||||
|
||||
/**
|
||||
* asset_manager.h
|
||||
*/
|
||||
pub type AAssetManager = raw::c_void;
|
||||
|
||||
/**
|
||||
* native_window.h
|
||||
*/
|
||||
pub type ANativeWindow = raw::c_void;
|
||||
|
||||
extern {
|
||||
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 fn(*mut ANativeActivity),
|
||||
pub onResume: extern fn(*mut ANativeActivity),
|
||||
pub onSaveInstanceState: extern fn(*mut ANativeActivity, *mut libc::size_t),
|
||||
pub onPause: extern fn(*mut ANativeActivity),
|
||||
pub onStop: extern fn(*mut ANativeActivity),
|
||||
pub onDestroy: extern fn(*mut ANativeActivity),
|
||||
pub onWindowFocusChanged: extern fn(*mut ANativeActivity, libc::c_int),
|
||||
pub onNativeWindowCreated: extern fn(*mut ANativeActivity, *const ANativeWindow),
|
||||
pub onNativeWindowResized: extern fn(*mut ANativeActivity, *const ANativeWindow),
|
||||
pub onNativeWindowRedrawNeeded: extern fn(*mut ANativeActivity, *const ANativeWindow),
|
||||
pub onNativeWindowDestroyed: extern fn(*mut ANativeActivity, *const ANativeWindow),
|
||||
pub onInputQueueCreated: extern fn(*mut ANativeActivity, *mut AInputQueue),
|
||||
pub onInputQueueDestroyed: extern fn(*mut ANativeActivity, *mut AInputQueue),
|
||||
pub onContentRectChanged: extern fn(*mut ANativeActivity, *const ARect),
|
||||
pub onConfigurationChanged: extern fn(*mut ANativeActivity),
|
||||
pub onLowMemory: extern fn(*mut ANativeActivity),
|
||||
}
|
||||
|
||||
/**
|
||||
* looper.h
|
||||
*/
|
||||
pub type ALooper = ();
|
||||
|
||||
#[link(name = "android")]
|
||||
extern {
|
||||
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 fn(libc::c_int, libc::c_int, *mut libc::c_void) -> libc::c_int;
|
||||
@@ -1,345 +0,0 @@
|
||||
#![cfg(target_os = "android")]
|
||||
|
||||
extern crate android_glue;
|
||||
|
||||
use libc;
|
||||
use std::sync::mpsc::{Receiver, channel};
|
||||
use std::os::raw::c_void;
|
||||
use {CreationError, Event, WindowEvent, MouseCursor};
|
||||
use CreationError::OsError;
|
||||
use WindowId as RootWindowId;
|
||||
use events::{Touch, TouchPhase};
|
||||
use window::MonitorId as RootMonitorId;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use CursorState;
|
||||
use WindowAttributes;
|
||||
|
||||
pub struct EventsLoop {
|
||||
event_rx: Receiver<android_glue::Event>,
|
||||
suspend_callback: RefCell<Option<Box<Fn(bool) -> ()>>>
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EventsLoopProxy;
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn new() -> EventsLoop {
|
||||
let (tx, rx) = channel();
|
||||
android_glue::add_sender(tx);
|
||||
EventsLoop {
|
||||
event_rx: rx,
|
||||
suspend_callback: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
let mut rb = VecDeque::new();
|
||||
rb.push_back(MonitorId);
|
||||
rb
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
MonitorId
|
||||
}
|
||||
|
||||
|
||||
pub fn poll_events<F>(&mut self, mut callback: F)
|
||||
where F: FnMut(::Event)
|
||||
{
|
||||
while let Ok(event) = self.event_rx.try_recv() {
|
||||
|
||||
let e = match event{
|
||||
android_glue::Event::EventMotion(motion) => {
|
||||
Some(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId),
|
||||
event: WindowEvent::Touch(Touch {
|
||||
phase: match motion.action {
|
||||
android_glue::MotionAction::Down => TouchPhase::Started,
|
||||
android_glue::MotionAction::Move => TouchPhase::Moved,
|
||||
android_glue::MotionAction::Up => TouchPhase::Ended,
|
||||
android_glue::MotionAction::Cancel => TouchPhase::Cancelled,
|
||||
},
|
||||
location: (motion.x as f64, motion.y as f64),
|
||||
id: motion.pointer_id as u64,
|
||||
device_id: DEVICE_ID,
|
||||
}),
|
||||
})
|
||||
},
|
||||
android_glue::Event::InitWindow => {
|
||||
// The activity went to foreground.
|
||||
if let Some(cb) = self.suspend_callback.borrow().as_ref() {
|
||||
(*cb)(false);
|
||||
}
|
||||
Some(Event::Suspended(false))
|
||||
},
|
||||
android_glue::Event::TermWindow => {
|
||||
// The activity went to background.
|
||||
if let Some(cb) = self.suspend_callback.borrow().as_ref() {
|
||||
(*cb)(true);
|
||||
}
|
||||
Some(Event::Suspended(true))
|
||||
},
|
||||
android_glue::Event::WindowResized |
|
||||
android_glue::Event::ConfigChanged => {
|
||||
// Activity Orientation changed or resized.
|
||||
let native_window = unsafe { android_glue::get_native_window() };
|
||||
if native_window.is_null() {
|
||||
None
|
||||
} else {
|
||||
let w = unsafe { ffi::ANativeWindow_getWidth(native_window as *const _) } as u32;
|
||||
let h = unsafe { ffi::ANativeWindow_getHeight(native_window as *const _) } as u32;
|
||||
Some(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId),
|
||||
event: WindowEvent::Resized(w, h),
|
||||
})
|
||||
}
|
||||
},
|
||||
android_glue::Event::WindowRedrawNeeded => {
|
||||
// The activity needs to be redrawn.
|
||||
Some(Event::WindowEvent {
|
||||
window_id: RootWindowId(WindowId),
|
||||
event: WindowEvent::Refresh,
|
||||
})
|
||||
}
|
||||
android_glue::Event::Wake => {
|
||||
Some(Event::Awakened)
|
||||
}
|
||||
_ => {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(event) = e {
|
||||
callback(event);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn set_suspend_callback(&self, cb: Option<Box<Fn(bool) -> ()>>) {
|
||||
*self.suspend_callback.borrow_mut() = cb;
|
||||
}
|
||||
|
||||
pub fn run_forever<F>(&mut self, mut callback: F)
|
||||
where F: FnMut(::Event) -> ::ControlFlow,
|
||||
{
|
||||
// Yeah that's a very bad implementation.
|
||||
loop {
|
||||
let mut control_flow = ::ControlFlow::Continue;
|
||||
self.poll_events(|e| {
|
||||
if let ::ControlFlow::Break = callback(e) {
|
||||
control_flow = ::ControlFlow::Break;
|
||||
}
|
||||
});
|
||||
if let ::ControlFlow::Break = control_flow {
|
||||
break;
|
||||
}
|
||||
::std::thread::sleep(::std::time::Duration::from_millis(5));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventsLoopProxy {
|
||||
EventsLoopProxy
|
||||
}
|
||||
}
|
||||
|
||||
impl EventsLoopProxy {
|
||||
pub fn wakeup(&self) -> Result<(), ::EventsLoopClosed> {
|
||||
android_glue::wake_event_loop();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId;
|
||||
|
||||
pub struct Window {
|
||||
native_window: *const c_void,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MonitorId;
|
||||
|
||||
mod ffi;
|
||||
|
||||
impl MonitorId {
|
||||
#[inline]
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
Some("Primary".to_string())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
unsafe {
|
||||
let window = android_glue::get_native_window();
|
||||
(ffi::ANativeWindow_getWidth(window) as u32, ffi::ANativeWindow_getHeight(window) as u32)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
// Android assumes single screen
|
||||
(0, 0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
1.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes;
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificHeadlessBuilderAttributes;
|
||||
|
||||
impl Window {
|
||||
pub fn new(_: &EventsLoop, win_attribs: WindowAttributes,
|
||||
_: PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Window, CreationError>
|
||||
{
|
||||
// not implemented
|
||||
assert!(win_attribs.min_dimensions.is_none());
|
||||
assert!(win_attribs.max_dimensions.is_none());
|
||||
|
||||
let native_window = unsafe { android_glue::get_native_window() };
|
||||
if native_window.is_null() {
|
||||
return Err(OsError(format!("Android's native window is null")));
|
||||
}
|
||||
|
||||
android_glue::set_multitouch(win_attribs.multitouch);
|
||||
|
||||
Ok(Window {
|
||||
native_window: native_window as *const _,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_native_window(&self) -> *const c_void {
|
||||
self.native_window
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_title(&self, _: &str) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show(&self) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hide(&self) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_position(&self, _x: i32, _y: i32) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
if self.native_window.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some((
|
||||
unsafe { ffi::ANativeWindow_getWidth(self.native_window as *const _) } as u32,
|
||||
unsafe { ffi::ANativeWindow_getHeight(self.native_window as *const _) } as u32
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
self.get_inner_size()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_inner_size(&self, _x: u32, _y: u32) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, _: MouseCursor) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, _state: CursorState) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
1.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, _maximized: bool) {
|
||||
// Android has single screen maximized apps so nothing to do
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_fullscreen(&self, _monitor: Option<RootMonitorId>) {
|
||||
// Android has single screen maximized apps so nothing to do
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_decorations(&self, _decorations: bool) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, _icon: Option<::Icon>) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||
RootMonitorId{inner: MonitorId}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> WindowId {
|
||||
WindowId
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Window {}
|
||||
unsafe impl Sync for Window {}
|
||||
|
||||
// Constant device ID, to be removed when this backend is updated to report real device IDs.
|
||||
const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId);
|
||||
@@ -1,316 +0,0 @@
|
||||
#![allow(dead_code)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
use std::os::raw::{c_int, c_char, c_void, c_ulong, c_double, c_long, c_ushort};
|
||||
#[cfg(test)]
|
||||
use std::mem;
|
||||
|
||||
pub type EM_BOOL = c_int;
|
||||
pub type EM_UTF8 = c_char;
|
||||
pub type EMSCRIPTEN_RESULT = c_int;
|
||||
|
||||
pub const EM_TRUE: EM_BOOL = 1;
|
||||
pub const EM_FALSE: EM_BOOL = 0;
|
||||
|
||||
// values for EMSCRIPTEN_RESULT
|
||||
pub const EMSCRIPTEN_RESULT_SUCCESS: c_int = 0;
|
||||
pub const EMSCRIPTEN_RESULT_DEFERRED: c_int = 1;
|
||||
pub const EMSCRIPTEN_RESULT_NOT_SUPPORTED: c_int = -1;
|
||||
pub const EMSCRIPTEN_RESULT_FAILED_NOT_DEFERRED: c_int = -2;
|
||||
pub const EMSCRIPTEN_RESULT_INVALID_TARGET: c_int = -3;
|
||||
pub const EMSCRIPTEN_RESULT_UNKNOWN_TARGET: c_int = -4;
|
||||
pub const EMSCRIPTEN_RESULT_INVALID_PARAM: c_int = -5;
|
||||
pub const EMSCRIPTEN_RESULT_FAILED: c_int = -6;
|
||||
pub const EMSCRIPTEN_RESULT_NO_DATA: c_int = -7;
|
||||
|
||||
// values for EMSCRIPTEN EVENT
|
||||
pub const EMSCRIPTEN_EVENT_KEYPRESS: c_int = 1;
|
||||
pub const EMSCRIPTEN_EVENT_KEYDOWN: c_int = 2;
|
||||
pub const EMSCRIPTEN_EVENT_KEYUP: c_int = 3;
|
||||
pub const EMSCRIPTEN_EVENT_CLICK: c_int = 4;
|
||||
pub const EMSCRIPTEN_EVENT_MOUSEDOWN: c_int = 5;
|
||||
pub const EMSCRIPTEN_EVENT_MOUSEUP: c_int = 6;
|
||||
pub const EMSCRIPTEN_EVENT_DBLCLICK: c_int = 7;
|
||||
pub const EMSCRIPTEN_EVENT_MOUSEMOVE: c_int = 8;
|
||||
pub const EMSCRIPTEN_EVENT_WHEEL: c_int = 9;
|
||||
pub const EMSCRIPTEN_EVENT_RESIZE: c_int = 10;
|
||||
pub const EMSCRIPTEN_EVENT_SCROLL: c_int = 11;
|
||||
pub const EMSCRIPTEN_EVENT_BLUR: c_int = 12;
|
||||
pub const EMSCRIPTEN_EVENT_FOCUS: c_int = 13;
|
||||
pub const EMSCRIPTEN_EVENT_FOCUSIN: c_int = 14;
|
||||
pub const EMSCRIPTEN_EVENT_FOCUSOUT: c_int = 15;
|
||||
pub const EMSCRIPTEN_EVENT_DEVICEORIENTATION: c_int = 16;
|
||||
pub const EMSCRIPTEN_EVENT_DEVICEMOTION: c_int = 17;
|
||||
pub const EMSCRIPTEN_EVENT_ORIENTATIONCHANGE: c_int = 18;
|
||||
pub const EMSCRIPTEN_EVENT_FULLSCREENCHANGE: c_int = 19;
|
||||
pub const EMSCRIPTEN_EVENT_POINTERLOCKCHANGE: c_int = 20;
|
||||
pub const EMSCRIPTEN_EVENT_VISIBILITYCHANGE: c_int = 21;
|
||||
pub const EMSCRIPTEN_EVENT_TOUCHSTART: c_int = 22;
|
||||
pub const EMSCRIPTEN_EVENT_TOUCHEND: c_int = 23;
|
||||
pub const EMSCRIPTEN_EVENT_TOUCHMOVE: c_int = 24;
|
||||
pub const EMSCRIPTEN_EVENT_TOUCHCANCEL: c_int = 25;
|
||||
pub const EMSCRIPTEN_EVENT_GAMEPADCONNECTED: c_int = 26;
|
||||
pub const EMSCRIPTEN_EVENT_GAMEPADDISCONNECTED: c_int = 27;
|
||||
pub const EMSCRIPTEN_EVENT_BEFOREUNLOAD: c_int = 28;
|
||||
pub const EMSCRIPTEN_EVENT_BATTERYCHARGINGCHANGE: c_int = 29;
|
||||
pub const EMSCRIPTEN_EVENT_BATTERYLEVELCHANGE: c_int = 30;
|
||||
pub const EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST: c_int = 31;
|
||||
pub const EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED: c_int = 32;
|
||||
pub const EMSCRIPTEN_EVENT_MOUSEENTER: c_int = 33;
|
||||
pub const EMSCRIPTEN_EVENT_MOUSELEAVE: c_int = 34;
|
||||
pub const EMSCRIPTEN_EVENT_MOUSEOVER: c_int = 35;
|
||||
pub const EMSCRIPTEN_EVENT_MOUSEOUT: c_int = 36;
|
||||
pub const EMSCRIPTEN_EVENT_CANVASRESIZED: c_int = 37;
|
||||
pub const EMSCRIPTEN_EVENT_POINTERLOCKERROR: c_int = 38;
|
||||
|
||||
pub const EM_HTML5_SHORT_STRING_LEN_BYTES: usize = 32;
|
||||
|
||||
pub const DOM_KEY_LOCATION_STANDARD: c_ulong = 0x00;
|
||||
pub const DOM_KEY_LOCATION_LEFT: c_ulong = 0x01;
|
||||
pub const DOM_KEY_LOCATION_RIGHT: c_ulong = 0x02;
|
||||
pub const DOM_KEY_LOCATION_NUMPAD: c_ulong = 0x03;
|
||||
|
||||
pub type em_callback_func = Option<unsafe extern "C" fn()>;
|
||||
|
||||
pub type em_key_callback_func = Option<unsafe extern "C" fn(
|
||||
eventType: c_int,
|
||||
keyEvent: *const EmscriptenKeyboardEvent,
|
||||
userData: *mut c_void) -> EM_BOOL>;
|
||||
|
||||
pub type em_mouse_callback_func = Option<unsafe extern "C" fn(
|
||||
eventType: c_int,
|
||||
mouseEvent: *const EmscriptenMouseEvent,
|
||||
userData: *mut c_void) -> EM_BOOL>;
|
||||
|
||||
pub type em_pointerlockchange_callback_func = Option<unsafe extern "C" fn(
|
||||
eventType: c_int,
|
||||
pointerlockChangeEvent: *const EmscriptenPointerlockChangeEvent,
|
||||
userData: *mut c_void) -> EM_BOOL>;
|
||||
|
||||
pub type em_fullscreenchange_callback_func = Option<unsafe extern "C" fn(
|
||||
eventType: c_int,
|
||||
fullscreenChangeEvent: *const EmscriptenFullscreenChangeEvent,
|
||||
userData: *mut c_void) -> EM_BOOL>;
|
||||
|
||||
pub type em_touch_callback_func = Option<unsafe extern "C" fn(
|
||||
eventType: c_int,
|
||||
touchEvent: *const EmscriptenTouchEvent,
|
||||
userData: *mut c_void) -> EM_BOOL>;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct EmscriptenFullscreenChangeEvent {
|
||||
pub isFullscreen: c_int,
|
||||
pub fullscreenEnabled: c_int,
|
||||
pub nodeName: [c_char; 128usize],
|
||||
pub id: [c_char; 128usize],
|
||||
pub elementWidth: c_int,
|
||||
pub elementHeight: c_int,
|
||||
pub screenWidth: c_int,
|
||||
pub screenHeight: c_int,
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_EmscriptenFullscreenChangeEvent() {
|
||||
assert_eq!(mem::size_of::<EmscriptenFullscreenChangeEvent>(), 280usize);
|
||||
assert_eq!(mem::align_of::<EmscriptenFullscreenChangeEvent>(), 4usize);
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy)]
|
||||
pub struct EmscriptenKeyboardEvent {
|
||||
pub key: [c_char; 32usize],
|
||||
pub code: [c_char; 32usize],
|
||||
pub location: c_ulong,
|
||||
pub ctrlKey: c_int,
|
||||
pub shiftKey: c_int,
|
||||
pub altKey: c_int,
|
||||
pub metaKey: c_int,
|
||||
pub repeat: c_int,
|
||||
pub locale: [c_char; 32usize],
|
||||
pub charValue: [c_char; 32usize],
|
||||
pub charCode: c_ulong,
|
||||
pub keyCode: c_ulong,
|
||||
pub which: c_ulong,
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_EmscriptenKeyboardEvent() {
|
||||
assert_eq!(mem::size_of::<EmscriptenKeyboardEvent>(), 184usize);
|
||||
assert_eq!(mem::align_of::<EmscriptenKeyboardEvent>(), 8usize);
|
||||
}
|
||||
impl Clone for EmscriptenKeyboardEvent {
|
||||
fn clone(&self) -> Self { *self }
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct EmscriptenMouseEvent {
|
||||
pub timestamp: f64,
|
||||
pub screenX: c_long,
|
||||
pub screenY: c_long,
|
||||
pub clientX: c_long,
|
||||
pub clientY: c_long,
|
||||
pub ctrlKey: c_int,
|
||||
pub shiftKey: c_int,
|
||||
pub altKey: c_int,
|
||||
pub metaKey: c_int,
|
||||
pub button: c_ushort,
|
||||
pub buttons: c_ushort,
|
||||
pub movementX: c_long,
|
||||
pub movementY: c_long,
|
||||
pub targetX: c_long,
|
||||
pub targetY: c_long,
|
||||
pub canvasX: c_long,
|
||||
pub canvasY: c_long,
|
||||
pub padding: c_long,
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_EmscriptenMouseEvent() {
|
||||
assert_eq!(mem::size_of::<EmscriptenMouseEvent>(), 120usize);
|
||||
assert_eq!(mem::align_of::<EmscriptenMouseEvent>(), 8usize);
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct EmscriptenTouchPoint {
|
||||
pub identifier: c_long,
|
||||
pub screenX: c_long,
|
||||
pub screenY: c_long,
|
||||
pub clientX: c_long,
|
||||
pub clientY: c_long,
|
||||
pub pageX: c_long,
|
||||
pub pageY: c_long,
|
||||
pub isChanged: c_int,
|
||||
pub onTarget: c_int,
|
||||
pub targetX: c_long,
|
||||
pub targetY: c_long,
|
||||
pub canvasX: c_long,
|
||||
pub canvasY: c_long,
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_EmscriptenTouchPoint() {
|
||||
assert_eq!(mem::size_of::<EmscriptenTouchPoint>(), 96usize);
|
||||
assert_eq!(mem::align_of::<EmscriptenTouchPoint>(), 8usize);
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct EmscriptenTouchEvent {
|
||||
pub numTouches: c_int,
|
||||
pub ctrlKey: c_int,
|
||||
pub shiftKey: c_int,
|
||||
pub altKey: c_int,
|
||||
pub metaKey: c_int,
|
||||
pub touches: [EmscriptenTouchPoint; 32usize],
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_EmscriptenTouchEvent() {
|
||||
assert_eq!(mem::size_of::<EmscriptenTouchEvent>(), 3096usize);
|
||||
assert_eq!(mem::align_of::<EmscriptenTouchEvent>(), 8usize);
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct EmscriptenPointerlockChangeEvent {
|
||||
pub isActive: c_int,
|
||||
pub nodeName: [c_char; 128usize],
|
||||
pub id: [c_char; 128usize],
|
||||
}
|
||||
#[test]
|
||||
fn bindgen_test_layout_EmscriptenPointerlockChangeEvent() {
|
||||
assert_eq!(mem::size_of::<EmscriptenPointerlockChangeEvent>(), 260usize);
|
||||
assert_eq!(mem::align_of::<EmscriptenPointerlockChangeEvent>(), 4usize);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
pub fn emscripten_set_canvas_size(
|
||||
width: c_int, height: c_int)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_get_canvas_size(
|
||||
width: *mut c_int, height: *mut c_int,
|
||||
is_fullscreen: *mut c_int)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_set_element_css_size(
|
||||
target: *const c_char, width: c_double,
|
||||
height: c_double) -> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_get_element_css_size(
|
||||
target: *const c_char, width: *mut c_double,
|
||||
height: *mut c_double) -> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_request_pointerlock(
|
||||
target: *const c_char, deferUntilInEventHandler: EM_BOOL)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_exit_pointerlock() -> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_request_fullscreen(
|
||||
target: *const c_char, deferUntilInEventHandler: EM_BOOL)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_exit_fullscreen() -> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_set_keydown_callback(
|
||||
target: *const c_char, userData: *mut c_void,
|
||||
useCapture: EM_BOOL, callback: em_key_callback_func)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_set_keyup_callback(
|
||||
target: *const c_char, userData: *mut c_void,
|
||||
useCapture: EM_BOOL, callback: em_key_callback_func)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_set_mousemove_callback(
|
||||
target: *const c_char, user_data: *mut c_void,
|
||||
use_capture: EM_BOOL, callback: em_mouse_callback_func)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_set_mousedown_callback(
|
||||
target: *const c_char, user_data: *mut c_void,
|
||||
use_capture: EM_BOOL, callback: em_mouse_callback_func)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_set_mouseup_callback(
|
||||
target: *const c_char, user_data: *mut c_void,
|
||||
use_capture: EM_BOOL, callback: em_mouse_callback_func)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_hide_mouse();
|
||||
|
||||
pub fn emscripten_get_device_pixel_ratio() -> f64;
|
||||
|
||||
pub fn emscripten_set_pointerlockchange_callback(
|
||||
target: *const c_char, userData: *mut c_void, useCapture: EM_BOOL,
|
||||
callback: em_pointerlockchange_callback_func) -> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_set_fullscreenchange_callback(
|
||||
target: *const c_char, userData: *mut c_void, useCapture: EM_BOOL,
|
||||
callback: em_fullscreenchange_callback_func) -> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_asm_const(code: *const c_char);
|
||||
|
||||
pub fn emscripten_set_main_loop(
|
||||
func: em_callback_func, fps: c_int, simulate_infinite_loop: EM_BOOL);
|
||||
|
||||
pub fn emscripten_cancel_main_loop();
|
||||
|
||||
pub fn emscripten_set_touchstart_callback(
|
||||
target: *const c_char, userData: *mut c_void,
|
||||
useCapture: c_int, callback: em_touch_callback_func)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_set_touchend_callback(
|
||||
target: *const c_char, userData: *mut c_void,
|
||||
useCapture: c_int, callback: em_touch_callback_func)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_set_touchmove_callback(
|
||||
target: *const c_char, userData: *mut c_void,
|
||||
useCapture: c_int, callback: em_touch_callback_func)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
|
||||
pub fn emscripten_set_touchcancel_callback(
|
||||
target: *const c_char, userData: *mut c_void,
|
||||
useCapture: c_int, callback: em_touch_callback_func)
|
||||
-> EMSCRIPTEN_RESULT;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
317
src/platform/ios.rs
Normal file
317
src/platform/ios.rs
Normal file
@@ -0,0 +1,317 @@
|
||||
#![cfg(target_os = "ios")]
|
||||
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use crate::{
|
||||
event_loop::EventLoop,
|
||||
monitor::{MonitorHandle, VideoMode},
|
||||
window::{Window, WindowBuilder},
|
||||
};
|
||||
|
||||
/// Additional methods on [`EventLoop`] that are specific to iOS.
|
||||
pub trait EventLoopExtIOS {
|
||||
/// Returns the [`Idiom`] (phone/tablet/tv/etc) for the current device.
|
||||
fn idiom(&self) -> Idiom;
|
||||
}
|
||||
|
||||
impl<T: 'static> EventLoopExtIOS for EventLoop<T> {
|
||||
fn idiom(&self) -> Idiom {
|
||||
self.event_loop.idiom()
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`Window`] that are specific to iOS.
|
||||
pub trait WindowExtIOS {
|
||||
/// Returns a pointer to the [`UIWindow`] that is used by this window.
|
||||
///
|
||||
/// The pointer will become invalid when the [`Window`] is destroyed.
|
||||
///
|
||||
/// [`UIWindow`]: https://developer.apple.com/documentation/uikit/uiwindow?language=objc
|
||||
fn ui_window(&self) -> *mut c_void;
|
||||
|
||||
/// Returns a pointer to the [`UIViewController`] that is used by this window.
|
||||
///
|
||||
/// The pointer will become invalid when the [`Window`] is destroyed.
|
||||
///
|
||||
/// [`UIViewController`]: https://developer.apple.com/documentation/uikit/uiviewcontroller?language=objc
|
||||
fn ui_view_controller(&self) -> *mut c_void;
|
||||
|
||||
/// Returns a pointer to the [`UIView`] that is used by this window.
|
||||
///
|
||||
/// The pointer will become invalid when the [`Window`] is destroyed.
|
||||
///
|
||||
/// [`UIView`]: https://developer.apple.com/documentation/uikit/uiview?language=objc
|
||||
fn ui_view(&self) -> *mut c_void;
|
||||
|
||||
/// 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
|
||||
/// this to [`MonitorHandle::scale_factor()`].
|
||||
///
|
||||
/// [`UIWindow`]: https://developer.apple.com/documentation/uikit/uiwindow?language=objc
|
||||
/// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc
|
||||
fn set_scale_factor(&self, scale_factor: f64);
|
||||
|
||||
/// Sets the valid orientations for the [`Window`].
|
||||
///
|
||||
/// The default value is [`ValidOrientations::LandscapeAndPortrait`].
|
||||
///
|
||||
/// This changes the value returned by
|
||||
/// [`-[UIViewController supportedInterfaceOrientations]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621435-supportedinterfaceorientations?language=objc),
|
||||
/// and then calls
|
||||
/// [`-[UIViewController attemptRotationToDeviceOrientation]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621400-attemptrotationtodeviceorientati?language=objc).
|
||||
fn set_valid_orientations(&self, valid_orientations: ValidOrientations);
|
||||
|
||||
/// Sets whether the [`Window`] prefers the home indicator hidden.
|
||||
///
|
||||
/// The default is to prefer showing the home indicator.
|
||||
///
|
||||
/// This changes the value returned by
|
||||
/// [`-[UIViewController prefersHomeIndicatorAutoHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887510-prefershomeindicatorautohidden?language=objc),
|
||||
/// and then calls
|
||||
/// [`-[UIViewController setNeedsUpdateOfHomeIndicatorAutoHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887509-setneedsupdateofhomeindicatoraut?language=objc).
|
||||
///
|
||||
/// This only has an effect on iOS 11.0+.
|
||||
fn set_prefers_home_indicator_hidden(&self, hidden: bool);
|
||||
|
||||
/// Sets the screen edges for which the system gestures will take a lower priority than the
|
||||
/// application's touch handling.
|
||||
///
|
||||
/// This changes the value returned by
|
||||
/// [`-[UIViewController preferredScreenEdgesDeferringSystemGestures]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887512-preferredscreenedgesdeferringsys?language=objc),
|
||||
/// and then calls
|
||||
/// [`-[UIViewController setNeedsUpdateOfScreenEdgesDeferringSystemGestures]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887507-setneedsupdateofscreenedgesdefer?language=objc).
|
||||
///
|
||||
/// This only has an effect on iOS 11.0+.
|
||||
fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge);
|
||||
|
||||
/// Sets whether the [`Window`] prefers the status bar hidden.
|
||||
///
|
||||
/// The default is to prefer showing the status bar.
|
||||
///
|
||||
/// This changes the value returned by
|
||||
/// [`-[UIViewController prefersStatusBarHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621440-prefersstatusbarhidden?language=objc),
|
||||
/// and then calls
|
||||
/// [`-[UIViewController setNeedsStatusBarAppearanceUpdate]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621354-setneedsstatusbarappearanceupdat?language=objc).
|
||||
fn set_prefers_status_bar_hidden(&self, hidden: bool);
|
||||
}
|
||||
|
||||
impl WindowExtIOS for Window {
|
||||
#[inline]
|
||||
fn ui_window(&self) -> *mut c_void {
|
||||
self.window.ui_window() as _
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ui_view_controller(&self) -> *mut c_void {
|
||||
self.window.ui_view_controller() as _
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ui_view(&self) -> *mut c_void {
|
||||
self.window.ui_view() as _
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_scale_factor(&self, scale_factor: f64) {
|
||||
self.window.set_scale_factor(scale_factor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_valid_orientations(&self, valid_orientations: ValidOrientations) {
|
||||
self.window.set_valid_orientations(valid_orientations)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_prefers_home_indicator_hidden(&self, hidden: bool) {
|
||||
self.window.set_prefers_home_indicator_hidden(hidden)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge) {
|
||||
self.window
|
||||
.set_preferred_screen_edges_deferring_system_gestures(edges)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_prefers_status_bar_hidden(&self, hidden: bool) {
|
||||
self.window.set_prefers_status_bar_hidden(hidden)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// this to [`MonitorHandle::scale_factor()`].
|
||||
///
|
||||
/// [`UIWindow`]: https://developer.apple.com/documentation/uikit/uiwindow?language=objc
|
||||
/// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc
|
||||
fn with_scale_factor(self, scale_factor: f64) -> WindowBuilder;
|
||||
|
||||
/// Sets the valid orientations for the [`Window`].
|
||||
///
|
||||
/// The default value is [`ValidOrientations::LandscapeAndPortrait`].
|
||||
///
|
||||
/// This sets the initial value returned by
|
||||
/// [`-[UIViewController supportedInterfaceOrientations]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621435-supportedinterfaceorientations?language=objc).
|
||||
fn with_valid_orientations(self, valid_orientations: ValidOrientations) -> WindowBuilder;
|
||||
|
||||
/// Sets whether the [`Window`] prefers the home indicator hidden.
|
||||
///
|
||||
/// The default is to prefer showing the home indicator.
|
||||
///
|
||||
/// This sets the initial value returned by
|
||||
/// [`-[UIViewController prefersHomeIndicatorAutoHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887510-prefershomeindicatorautohidden?language=objc).
|
||||
///
|
||||
/// This only has an effect on iOS 11.0+.
|
||||
fn with_prefers_home_indicator_hidden(self, hidden: bool) -> WindowBuilder;
|
||||
|
||||
/// Sets the screen edges for which the system gestures will take a lower priority than the
|
||||
/// application's touch handling.
|
||||
///
|
||||
/// This sets the initial value returned by
|
||||
/// [`-[UIViewController preferredScreenEdgesDeferringSystemGestures]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887512-preferredscreenedgesdeferringsys?language=objc).
|
||||
///
|
||||
/// This only has an effect on iOS 11.0+.
|
||||
fn with_preferred_screen_edges_deferring_system_gestures(
|
||||
self,
|
||||
edges: ScreenEdge,
|
||||
) -> WindowBuilder;
|
||||
|
||||
/// Sets whether the [`Window`] prefers the status bar hidden.
|
||||
///
|
||||
/// The default is to prefer showing the status bar.
|
||||
///
|
||||
/// This sets the initial value returned by
|
||||
/// [`-[UIViewController prefersStatusBarHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621440-prefersstatusbarhidden?language=objc).
|
||||
fn with_prefers_status_bar_hidden(self, hidden: bool) -> WindowBuilder;
|
||||
}
|
||||
|
||||
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);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_valid_orientations(mut self, valid_orientations: ValidOrientations) -> WindowBuilder {
|
||||
self.platform_specific.valid_orientations = valid_orientations;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_prefers_home_indicator_hidden(mut self, hidden: bool) -> WindowBuilder {
|
||||
self.platform_specific.prefers_home_indicator_hidden = hidden;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_preferred_screen_edges_deferring_system_gestures(
|
||||
mut self,
|
||||
edges: ScreenEdge,
|
||||
) -> WindowBuilder {
|
||||
self.platform_specific
|
||||
.preferred_screen_edges_deferring_system_gestures = edges;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_prefers_status_bar_hidden(mut self, hidden: bool) -> WindowBuilder {
|
||||
self.platform_specific.prefers_status_bar_hidden = hidden;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`MonitorHandle`] that are specific to iOS.
|
||||
pub trait MonitorHandleExtIOS {
|
||||
/// Returns a pointer to the [`UIScreen`] that is used by this monitor.
|
||||
///
|
||||
/// [`UIScreen`]: https://developer.apple.com/documentation/uikit/uiscreen?language=objc
|
||||
fn ui_screen(&self) -> *mut c_void;
|
||||
|
||||
/// Returns the preferred [`VideoMode`] for this monitor.
|
||||
///
|
||||
/// This translates to a call to [`-[UIScreen preferredMode]`](https://developer.apple.com/documentation/uikit/uiscreen/1617823-preferredmode?language=objc).
|
||||
fn preferred_video_mode(&self) -> VideoMode;
|
||||
}
|
||||
|
||||
impl MonitorHandleExtIOS for MonitorHandle {
|
||||
#[inline]
|
||||
fn ui_screen(&self) -> *mut c_void {
|
||||
self.inner.ui_screen() as _
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn preferred_video_mode(&self) -> VideoMode {
|
||||
self.inner.preferred_video_mode()
|
||||
}
|
||||
}
|
||||
|
||||
/// Valid orientations for a particular [`Window`].
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ValidOrientations {
|
||||
/// Excludes `PortraitUpsideDown` on iphone
|
||||
LandscapeAndPortrait,
|
||||
|
||||
Landscape,
|
||||
|
||||
/// Excludes `PortraitUpsideDown` on iphone
|
||||
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)]
|
||||
pub enum Idiom {
|
||||
Unspecified,
|
||||
|
||||
/// iPhone and iPod touch.
|
||||
Phone,
|
||||
|
||||
/// iPad.
|
||||
Pad,
|
||||
|
||||
/// tvOS and Apple TV.
|
||||
TV,
|
||||
CarPlay,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// The [edges] of a screen.
|
||||
///
|
||||
/// [edges]: https://developer.apple.com/documentation/uikit/uirectedge?language=objc
|
||||
#[derive(Default)]
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
use std::ffi::CString;
|
||||
|
||||
use libc;
|
||||
use objc::runtime::{ Object, Class };
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type id = *mut Object;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const nil: id = 0 as id;
|
||||
|
||||
pub type CFStringRef = *const libc::c_void;
|
||||
pub type CFTimeInterval = f64;
|
||||
pub type Boolean = u32;
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const kCFRunLoopRunHandledSource: i32 = 4;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
pub type CGFloat = f32;
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
pub type CGFloat = f64;
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
pub type NSUInteger = u32;
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
pub type NSUInteger = u64;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CGPoint {
|
||||
pub x: CGFloat,
|
||||
pub y: CGFloat,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CGRect {
|
||||
pub origin: CGPoint,
|
||||
pub size: CGSize
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CGSize {
|
||||
pub width: CGFloat,
|
||||
pub height: CGFloat
|
||||
}
|
||||
|
||||
#[link(name = "UIKit", kind = "framework")]
|
||||
#[link(name = "CoreFoundation", kind = "framework")]
|
||||
#[link(name = "GlKit", kind = "framework")]
|
||||
extern {
|
||||
pub static kCFRunLoopDefaultMode: CFStringRef;
|
||||
|
||||
// int UIApplicationMain ( int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName );
|
||||
pub fn UIApplicationMain(argc: libc::c_int, argv: *const libc::c_char, principalClassName: id, delegateClassName: id) -> libc::c_int;
|
||||
|
||||
// SInt32 CFRunLoopRunInMode ( CFStringRef mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled );
|
||||
pub fn CFRunLoopRunInMode(mode: CFStringRef, seconds: CFTimeInterval, returnAfterSourceHandled: Boolean) -> i32;
|
||||
}
|
||||
|
||||
extern {
|
||||
pub fn setjmp(env: *mut libc::c_void) -> libc::c_int;
|
||||
pub fn longjmp(env: *mut libc::c_void, val: libc::c_int);
|
||||
}
|
||||
|
||||
pub trait NSString: Sized {
|
||||
unsafe fn alloc(_: Self) -> id {
|
||||
msg_send![class("NSString"), alloc]
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn initWithUTF8String_(self, c_string: *const i8) -> id;
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn stringByAppendingString_(self, other: id) -> id;
|
||||
unsafe fn init_str(self, string: &str) -> Self;
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn UTF8String(self) -> *const libc::c_char;
|
||||
}
|
||||
|
||||
impl NSString for id {
|
||||
unsafe fn initWithUTF8String_(self, c_string: *const i8) -> 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 libc::c_char {
|
||||
msg_send![self, UTF8String]
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn class(name: &str) -> *mut Class {
|
||||
unsafe {
|
||||
::std::mem::transmute(Class::get(name))
|
||||
}
|
||||
}
|
||||
@@ -1,569 +0,0 @@
|
||||
//! iOS support
|
||||
//!
|
||||
//! # Building app
|
||||
//! To build ios app you will need rustc built for this targets:
|
||||
//!
|
||||
//! - armv7-apple-ios
|
||||
//! - armv7s-apple-ios
|
||||
//! - i386-apple-ios
|
||||
//! - aarch64-apple-ios
|
||||
//! - x86_64-apple-ios
|
||||
//!
|
||||
//! Then
|
||||
//!
|
||||
//! ```
|
||||
//! cargo build --target=...
|
||||
//! ```
|
||||
//! The simplest way to integrate your app into xcode environment is to build it
|
||||
//! as a static library. Wrap your main function and export it.
|
||||
//!
|
||||
//! ```rust, ignore
|
||||
//! #[no_mangle]
|
||||
//! pub extern fn start_glutin_app() {
|
||||
//! start_inner()
|
||||
//! }
|
||||
//!
|
||||
//! fn start_inner() {
|
||||
//! ...
|
||||
//! }
|
||||
//!
|
||||
//! ```
|
||||
//!
|
||||
//! Compile project and then drag resulting .a into Xcode project. Add glutin.h to xcode.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! void start_glutin_app();
|
||||
//! ```
|
||||
//!
|
||||
//! Use start_glutin_app inside your xcode's main function.
|
||||
//!
|
||||
//!
|
||||
//! # App lifecycle and events
|
||||
//!
|
||||
//! iOS environment is very different from other platforms and you must be very
|
||||
//! careful with it's events. Familiarize yourself with
|
||||
//! [app lifecycle](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIApplicationDelegate_Protocol/).
|
||||
//!
|
||||
//!
|
||||
//! This is how those event are represented in glutin:
|
||||
//!
|
||||
//! - applicationDidBecomeActive is Focused(true)
|
||||
//! - applicationWillResignActive is Focused(false)
|
||||
//! - applicationDidEnterBackground is Suspended(true)
|
||||
//! - applicationWillEnterForeground is Suspended(false)
|
||||
//! - applicationWillTerminate is Destroyed
|
||||
//!
|
||||
//! Keep in mind that after Destroyed event is received every attempt to draw with
|
||||
//! opengl will result in segfault.
|
||||
//!
|
||||
//! Also note that app will not receive Destroyed event if suspended, it will be SIGKILL'ed
|
||||
|
||||
#![cfg(target_os = "ios")]
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::ptr;
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use libc;
|
||||
use libc::c_int;
|
||||
use objc::runtime::{Class, Object, Sel, BOOL, YES };
|
||||
use objc::declare::{ ClassDecl };
|
||||
|
||||
use { CreationError, CursorState, MouseCursor, WindowAttributes };
|
||||
use WindowId as RootEventId;
|
||||
use WindowEvent;
|
||||
use Event;
|
||||
use events::{ Touch, TouchPhase };
|
||||
use window::MonitorId as RootMonitorId;
|
||||
|
||||
mod ffi;
|
||||
use self::ffi::{
|
||||
setjmp,
|
||||
UIApplicationMain,
|
||||
CFTimeInterval,
|
||||
CFRunLoopRunInMode,
|
||||
kCFRunLoopDefaultMode,
|
||||
kCFRunLoopRunHandledSource,
|
||||
id,
|
||||
nil,
|
||||
NSString,
|
||||
CGFloat,
|
||||
longjmp,
|
||||
CGRect,
|
||||
CGPoint
|
||||
};
|
||||
|
||||
static mut jmpbuf: [c_int;27] = [0;27];
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MonitorId;
|
||||
|
||||
pub struct Window {
|
||||
delegate_state: *mut DelegateState
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WindowProxy;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DelegateState {
|
||||
events_queue: VecDeque<Event>,
|
||||
window: id,
|
||||
controller: id,
|
||||
size: (u32,u32),
|
||||
scale: f32
|
||||
}
|
||||
|
||||
|
||||
impl DelegateState {
|
||||
#[inline]
|
||||
fn new(window: id, controller:id, size: (u32,u32), scale: f32) -> DelegateState {
|
||||
DelegateState {
|
||||
events_queue: VecDeque::new(),
|
||||
window: window,
|
||||
controller: controller,
|
||||
size: size,
|
||||
scale: scale
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
#[inline]
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
Some("Primary".to_string())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
// iOS assumes single screen
|
||||
(0, 0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
1.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventsLoop {
|
||||
delegate_state: *mut DelegateState
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct EventsLoopProxy;
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn new() -> EventsLoop {
|
||||
unsafe {
|
||||
if setjmp(mem::transmute(&mut jmpbuf)) != 0 {
|
||||
let app: id = msg_send![Class::get("UIApplication").unwrap(), sharedApplication];
|
||||
let delegate: id = msg_send![app, delegate];
|
||||
let state: *mut c_void = *(&*delegate).get_ivar("glutinState");
|
||||
let state = state as *mut DelegateState;
|
||||
|
||||
let events_loop = EventsLoop {
|
||||
delegate_state: state
|
||||
};
|
||||
|
||||
return events_loop;
|
||||
}
|
||||
}
|
||||
|
||||
create_delegate_class();
|
||||
create_view_class();
|
||||
start_app();
|
||||
|
||||
panic!("Couldn't create UIApplication")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
let mut rb = VecDeque::new();
|
||||
rb.push_back(MonitorId);
|
||||
rb
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
MonitorId
|
||||
}
|
||||
|
||||
pub fn poll_events<F>(&mut self, mut callback: F)
|
||||
where F: FnMut(::Event)
|
||||
{
|
||||
unsafe {
|
||||
let state = &mut *self.delegate_state;
|
||||
|
||||
if let Some(event) = state.events_queue.pop_front() {
|
||||
callback(event);
|
||||
return;
|
||||
}
|
||||
|
||||
// jump hack, so we won't quit on willTerminate event before processing it
|
||||
if setjmp(mem::transmute(&mut jmpbuf)) != 0 {
|
||||
if let Some(event) = state.events_queue.pop_front() {
|
||||
callback(event);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// run runloop
|
||||
let seconds: CFTimeInterval = 0.000002;
|
||||
while CFRunLoopRunInMode(kCFRunLoopDefaultMode, seconds, 1) == kCFRunLoopRunHandledSource {}
|
||||
|
||||
if let Some(event) = state.events_queue.pop_front() {
|
||||
callback(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_forever<F>(&mut self, mut callback: F)
|
||||
where F: FnMut(::Event) -> ::ControlFlow,
|
||||
{
|
||||
// Yeah that's a very bad implementation.
|
||||
loop {
|
||||
let mut control_flow = ::ControlFlow::Continue;
|
||||
self.poll_events(|e| {
|
||||
if let ::ControlFlow::Break = callback(e) {
|
||||
control_flow = ::ControlFlow::Break;
|
||||
}
|
||||
});
|
||||
if let ::ControlFlow::Break = control_flow {
|
||||
break;
|
||||
}
|
||||
::std::thread::sleep(::std::time::Duration::from_millis(5));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventsLoopProxy {
|
||||
EventsLoopProxy
|
||||
}
|
||||
}
|
||||
|
||||
impl EventsLoopProxy {
|
||||
pub fn wakeup(&self) -> Result<(), ::EventsLoopClosed> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes;
|
||||
|
||||
impl Window {
|
||||
pub fn new(ev: &EventsLoop, _: WindowAttributes, _: PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Window, CreationError>
|
||||
{
|
||||
Ok(Window {
|
||||
delegate_state: ev.delegate_state,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_title(&self, _: &str) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show(&self) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hide(&self) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_position(&self, _x: i32, _y: i32) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
unsafe { Some((&*self.delegate_state).size) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
self.get_inner_size()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_inner_size(&self, _x: u32, _y: u32) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, _dimensions: Option<(u32, u32)>) { }
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_resize_callback(&mut self, _: Option<fn(u32, u32)>) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, _: MouseCursor) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, _: CursorState) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
unsafe { (&*self.delegate_state) }.scale
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn create_window_proxy(&self) -> WindowProxy {
|
||||
WindowProxy
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, _maximized: bool) {
|
||||
// iOS has single screen maximized apps so nothing to do
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_fullscreen(&self, _monitor: Option<RootMonitorId>) {
|
||||
// iOS has single screen maximized apps so nothing to do
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_decorations(&self, _decorations: bool) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, _icon: Option<::Icon>) {
|
||||
// N/A
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||
RootMonitorId{inner: MonitorId}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
WindowId
|
||||
}
|
||||
}
|
||||
|
||||
fn create_delegate_class() {
|
||||
extern fn did_finish_launching(this: &mut Object, _: Sel, _: id, _: id) -> BOOL {
|
||||
unsafe {
|
||||
let main_screen: id = msg_send![Class::get("UIScreen").unwrap(), mainScreen];
|
||||
let bounds: CGRect = msg_send![main_screen, bounds];
|
||||
let scale: CGFloat = msg_send![main_screen, nativeScale];
|
||||
|
||||
let window: id = msg_send![Class::get("UIWindow").unwrap(), alloc];
|
||||
let window: id = msg_send![window, initWithFrame:bounds.clone()];
|
||||
|
||||
let size = (bounds.size.width as u32, bounds.size.height as u32);
|
||||
|
||||
let view_controller: id = msg_send![Class::get("MainViewController").unwrap(), alloc];
|
||||
let view_controller: id = msg_send![view_controller, init];
|
||||
|
||||
let _: () = msg_send![window, setRootViewController:view_controller];
|
||||
let _: () = msg_send![window, makeKeyAndVisible];
|
||||
|
||||
let state = Box::new(DelegateState::new(window, view_controller, size, scale as f32));
|
||||
let state_ptr: *mut DelegateState = mem::transmute(state);
|
||||
this.set_ivar("glutinState", state_ptr as *mut c_void);
|
||||
|
||||
|
||||
let _: () = msg_send![this, performSelector:sel!(postLaunch:) withObject:nil afterDelay:0.0];
|
||||
}
|
||||
YES
|
||||
}
|
||||
|
||||
extern fn post_launch(_: &Object, _: Sel, _: id) {
|
||||
unsafe { longjmp(mem::transmute(&mut jmpbuf),1); }
|
||||
}
|
||||
|
||||
extern fn did_become_active(this: &Object, _: Sel, _: id) {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("glutinState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
state.events_queue.push_back(Event::WindowEvent {
|
||||
window_id: RootEventId(WindowId),
|
||||
event: WindowEvent::Focused(true),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extern fn will_resign_active(this: &Object, _: Sel, _: id) {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("glutinState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
state.events_queue.push_back(Event::WindowEvent {
|
||||
window_id: RootEventId(WindowId),
|
||||
event: WindowEvent::Focused(false),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
extern fn will_enter_foreground(this: &Object, _: Sel, _: id) {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("glutinState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
state.events_queue.push_back(Event::Suspended(false));
|
||||
}
|
||||
}
|
||||
|
||||
extern fn did_enter_background(this: &Object, _: Sel, _: id) {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("glutinState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
state.events_queue.push_back(Event::Suspended(true));
|
||||
}
|
||||
}
|
||||
|
||||
extern fn will_terminate(this: &Object, _: Sel, _: id) {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("glutinState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
// push event to the front to garantee that we'll process it
|
||||
// immidiatly after jump
|
||||
state.events_queue.push_front(Event::WindowEvent {
|
||||
window_id: RootEventId(WindowId),
|
||||
event: WindowEvent::Destroyed,
|
||||
});
|
||||
longjmp(mem::transmute(&mut jmpbuf),1);
|
||||
}
|
||||
}
|
||||
|
||||
extern fn handle_touches(this: &Object, _: Sel, touches: id, _:id) {
|
||||
unsafe {
|
||||
let state: *mut c_void = *this.get_ivar("glutinState");
|
||||
let state = &mut *(state as *mut DelegateState);
|
||||
|
||||
let touches_enum: id = msg_send![touches, objectEnumerator];
|
||||
|
||||
loop {
|
||||
let touch: id = msg_send![touches_enum, nextObject];
|
||||
if touch == nil {
|
||||
break
|
||||
}
|
||||
let location: CGPoint = msg_send![touch, locationInView:nil];
|
||||
let touch_id = touch as u64;
|
||||
let phase: i32 = msg_send![touch, phase];
|
||||
|
||||
state.events_queue.push_back(Event::WindowEvent {
|
||||
window_id: RootEventId(WindowId),
|
||||
event: WindowEvent::Touch(Touch {
|
||||
device_id: DEVICE_ID,
|
||||
id: touch_id,
|
||||
location: (location.x as f64, location.y as f64),
|
||||
phase: match phase {
|
||||
0 => TouchPhase::Started,
|
||||
1 => TouchPhase::Moved,
|
||||
// 2 is UITouchPhaseStationary and is not expected here
|
||||
3 => TouchPhase::Ended,
|
||||
4 => TouchPhase::Cancelled,
|
||||
_ => panic!("unexpected touch phase: {:?}", phase)
|
||||
}
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let ui_responder = Class::get("UIResponder").unwrap();
|
||||
let mut decl = ClassDecl::new("AppDelegate", ui_responder).unwrap();
|
||||
|
||||
unsafe {
|
||||
decl.add_method(sel!(application:didFinishLaunchingWithOptions:),
|
||||
did_finish_launching as extern fn(&mut Object, Sel, id, id) -> BOOL);
|
||||
|
||||
decl.add_method(sel!(applicationDidBecomeActive:),
|
||||
did_become_active as extern fn(&Object, Sel, id));
|
||||
|
||||
decl.add_method(sel!(applicationWillResignActive:),
|
||||
will_resign_active as extern fn(&Object, Sel, id));
|
||||
|
||||
decl.add_method(sel!(applicationWillEnterForeground:),
|
||||
will_enter_foreground as extern fn(&Object, Sel, id));
|
||||
|
||||
decl.add_method(sel!(applicationDidEnterBackground:),
|
||||
did_enter_background as extern fn(&Object, Sel, id));
|
||||
|
||||
decl.add_method(sel!(applicationWillTerminate:),
|
||||
will_terminate as extern fn(&Object, Sel, id));
|
||||
|
||||
|
||||
decl.add_method(sel!(touchesBegan:withEvent:),
|
||||
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
|
||||
|
||||
decl.add_method(sel!(touchesMoved:withEvent:),
|
||||
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
|
||||
|
||||
decl.add_method(sel!(touchesEnded:withEvent:),
|
||||
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
|
||||
|
||||
decl.add_method(sel!(touchesCancelled:withEvent:),
|
||||
handle_touches as extern fn(this: &Object, _: Sel, _: id, _:id));
|
||||
|
||||
|
||||
decl.add_method(sel!(postLaunch:),
|
||||
post_launch as extern fn(&Object, Sel, id));
|
||||
|
||||
decl.add_ivar::<*mut c_void>("glutinState");
|
||||
|
||||
decl.register();
|
||||
}
|
||||
}
|
||||
|
||||
fn create_view_class() {
|
||||
let ui_view_controller = Class::get("UIViewController").unwrap();
|
||||
let decl = ClassDecl::new("MainViewController", ui_view_controller).unwrap();
|
||||
|
||||
decl.register();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn start_app() {
|
||||
unsafe {
|
||||
UIApplicationMain(0, ptr::null(), nil, NSString::alloc(nil).init_str("AppDelegate"));
|
||||
}
|
||||
}
|
||||
|
||||
// Constant device ID, to be removed when this backend is updated to report real device IDs.
|
||||
const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId);
|
||||
@@ -1,15 +0,0 @@
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::os::raw::{c_void, c_char, c_int};
|
||||
|
||||
pub const RTLD_LAZY: c_int = 0x001;
|
||||
pub const RTLD_NOW: c_int = 0x002;
|
||||
|
||||
#[link="dl"]
|
||||
extern {
|
||||
pub fn dlopen(filename: *const c_char, flag: c_int) -> *mut c_void;
|
||||
pub fn dlerror() -> *mut c_char;
|
||||
pub fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
|
||||
pub fn dlclose(handle: *mut c_void) -> c_int;
|
||||
}
|
||||
@@ -1,482 +0,0 @@
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::{env, mem};
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
// `std::os::raw::c_void` and `libc::c_void` are NOT interchangeable!
|
||||
use libc;
|
||||
|
||||
use {
|
||||
CreationError,
|
||||
CursorState,
|
||||
EventsLoopClosed,
|
||||
Icon,
|
||||
MouseCursor,
|
||||
ControlFlow,
|
||||
WindowAttributes,
|
||||
};
|
||||
use window::MonitorId as RootMonitorId;
|
||||
use self::x11::{XConnection, XError};
|
||||
use self::x11::ffi::XVisualInfo;
|
||||
pub use self::x11::XNotSupported;
|
||||
|
||||
mod dlopen;
|
||||
pub mod wayland;
|
||||
pub mod x11;
|
||||
|
||||
/// Environment variable specifying which backend should be used on unix platform.
|
||||
///
|
||||
/// Legal values are x11 and wayland. If this variable is set only the named backend
|
||||
/// will be tried by winit. 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.
|
||||
const BACKEND_PREFERENCE_ENV_VAR: &str = "WINIT_UNIX_BACKEND";
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
pub visual_infos: Option<XVisualInfo>,
|
||||
pub screen_id: Option<i32>,
|
||||
pub resize_increments: Option<(i32, i32)>,
|
||||
pub base_size: Option<(i32, i32)>,
|
||||
}
|
||||
|
||||
lazy_static!(
|
||||
pub static ref X11_BACKEND: Result<Arc<XConnection>, XNotSupported> = {
|
||||
XConnection::new(Some(x_error_callback)).map(Arc::new)
|
||||
};
|
||||
);
|
||||
|
||||
pub enum Window {
|
||||
X(x11::Window),
|
||||
Wayland(wayland::Window)
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum WindowId {
|
||||
X(x11::WindowId),
|
||||
Wayland(wayland::WindowId)
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum DeviceId {
|
||||
X(x11::DeviceId),
|
||||
Wayland(wayland::DeviceId)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum MonitorId {
|
||||
X(x11::MonitorId),
|
||||
Wayland(wayland::MonitorId),
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
#[inline]
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
match self {
|
||||
&MonitorId::X(ref m) => m.get_name(),
|
||||
&MonitorId::Wayland(ref m) => m.get_name(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> u32 {
|
||||
match self {
|
||||
&MonitorId::X(ref m) => m.get_native_identifier(),
|
||||
&MonitorId::Wayland(ref m) => m.get_native_identifier(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
match self {
|
||||
&MonitorId::X(ref m) => m.get_dimensions(),
|
||||
&MonitorId::Wayland(ref m) => m.get_dimensions(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
match self {
|
||||
&MonitorId::X(ref m) => m.get_position(),
|
||||
&MonitorId::Wayland(ref m) => m.get_position(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
match self {
|
||||
&MonitorId::X(ref m) => m.get_hidpi_factor(),
|
||||
&MonitorId::Wayland(ref m) => m.get_hidpi_factor(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
#[inline]
|
||||
pub fn new(
|
||||
events_loop: &EventsLoop,
|
||||
attribs: WindowAttributes,
|
||||
pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
||||
) -> Result<Self, CreationError> {
|
||||
match *events_loop {
|
||||
EventsLoop::Wayland(ref events_loop) => {
|
||||
wayland::Window::new(events_loop, attribs).map(Window::Wayland)
|
||||
},
|
||||
EventsLoop::X(ref events_loop) => {
|
||||
x11::Window::new(events_loop, attribs, pl_attribs).map(Window::X)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
match self {
|
||||
&Window::X(ref w) => WindowId::X(w.id()),
|
||||
&Window::Wayland(ref w) => WindowId::Wayland(w.id())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_title(&self, title: &str) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_title(title),
|
||||
&Window::Wayland(ref w) => w.set_title(title)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show(&self) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.show(),
|
||||
&Window::Wayland(ref w) => w.show()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hide(&self) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.hide(),
|
||||
&Window::Wayland(ref w) => w.hide()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
match self {
|
||||
&Window::X(ref w) => w.get_position(),
|
||||
&Window::Wayland(ref w) => w.get_position()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||
match self {
|
||||
&Window::X(ref m) => m.get_inner_position(),
|
||||
&Window::Wayland(ref m) => m.get_inner_position(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_position(&self, x: i32, y: i32) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_position(x, y),
|
||||
&Window::Wayland(ref w) => w.set_position(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
match self {
|
||||
&Window::X(ref w) => w.get_inner_size(),
|
||||
&Window::Wayland(ref w) => w.get_inner_size()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
match self {
|
||||
&Window::X(ref w) => w.get_outer_size(),
|
||||
&Window::Wayland(ref w) => w.get_outer_size()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_inner_size(&self, x: u32, y: u32) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_inner_size(x, y),
|
||||
&Window::Wayland(ref w) => w.set_inner_size(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_min_dimensions(dimensions),
|
||||
&Window::Wayland(ref w) => w.set_min_dimensions(dimensions)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_max_dimensions(dimensions),
|
||||
&Window::Wayland(ref w) => w.set_max_dimensions(dimensions)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, cursor: MouseCursor) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_cursor(cursor),
|
||||
&Window::Wayland(ref w) => w.set_cursor(cursor)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_cursor_state(state),
|
||||
&Window::Wayland(ref w) => w.set_cursor_state(state)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
match self {
|
||||
&Window::X(ref w) => w.hidpi_factor(),
|
||||
&Window::Wayland(ref w) => w.hidpi_factor()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_cursor_position(x, y),
|
||||
&Window::Wayland(ref w) => w.set_cursor_position(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
match self {
|
||||
&Window::X(ref w) => w.platform_display(),
|
||||
&Window::Wayland(ref w) => w.get_display().c_ptr() as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
||||
match self {
|
||||
&Window::X(ref w) => w.platform_window(),
|
||||
&Window::Wayland(ref w) => w.get_surface().c_ptr() as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_maximized(maximized),
|
||||
&Window::Wayland(ref w) => w.set_maximized(maximized),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_fullscreen(monitor),
|
||||
&Window::Wayland(ref w) => w.set_fullscreen(monitor)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_decorations(&self, decorations: bool) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_decorations(decorations),
|
||||
&Window::Wayland(ref w) => w.set_decorations(decorations)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_window_icon(&self, window_icon: Option<Icon>) {
|
||||
match self {
|
||||
&Window::X(ref w) => w.set_window_icon(window_icon),
|
||||
&Window::Wayland(_) => (),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_current_monitor(&self) -> RootMonitorId {
|
||||
match self {
|
||||
&Window::X(ref w) => RootMonitorId{inner: MonitorId::X(w.get_current_monitor())},
|
||||
&Window::Wayland(ref w) => RootMonitorId{inner: MonitorId::Wayland(w.get_current_monitor())},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn x_error_callback(
|
||||
display: *mut x11::ffi::Display,
|
||||
event: *mut x11::ffi::XErrorEvent,
|
||||
) -> c_int {
|
||||
if let Ok(ref xconn) = *X11_BACKEND {
|
||||
let mut buf: [c_char; 1024] = mem::uninitialized();
|
||||
(xconn.xlib.XGetErrorText)(
|
||||
display,
|
||||
(*event).error_code as c_int,
|
||||
buf.as_mut_ptr(),
|
||||
buf.len() as c_int,
|
||||
);
|
||||
let description = CStr::from_ptr(buf.as_ptr()).to_string_lossy();
|
||||
|
||||
let error = XError {
|
||||
description: description.into_owned(),
|
||||
error_code: (*event).error_code,
|
||||
request_code: (*event).request_code,
|
||||
minor_code: (*event).minor_code,
|
||||
};
|
||||
|
||||
*xconn.latest_error.lock() = Some(error);
|
||||
}
|
||||
// Fun fact: this return value is completely ignored.
|
||||
0
|
||||
}
|
||||
|
||||
pub enum EventsLoop {
|
||||
Wayland(wayland::EventsLoop),
|
||||
X(x11::EventsLoop)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum EventsLoopProxy {
|
||||
X(x11::EventsLoopProxy),
|
||||
Wayland(wayland::EventsLoopProxy),
|
||||
}
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn new() -> EventsLoop {
|
||||
if let Ok(env_var) = env::var(BACKEND_PREFERENCE_ENV_VAR) {
|
||||
match env_var.as_str() {
|
||||
"x11" => {
|
||||
// TODO: propagate
|
||||
return EventsLoop::new_x11().expect("Failed to initialize X11 backend");
|
||||
},
|
||||
"wayland" => {
|
||||
return EventsLoop::new_wayland()
|
||||
.expect("Failed to initialize Wayland backend");
|
||||
},
|
||||
_ => panic!(
|
||||
"Unknown environment variable value for {}, try one of `x11`,`wayland`",
|
||||
BACKEND_PREFERENCE_ENV_VAR,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
let wayland_err = match EventsLoop::new_wayland() {
|
||||
Ok(event_loop) => return event_loop,
|
||||
Err(err) => err,
|
||||
};
|
||||
|
||||
let x11_err = match EventsLoop::new_x11() {
|
||||
Ok(event_loop) => return event_loop,
|
||||
Err(err) => err,
|
||||
};
|
||||
|
||||
let err_string = format!(
|
||||
r#"Failed to initialize any backend!
|
||||
Wayland status: {:#?}
|
||||
X11 status: {:#?}
|
||||
"#,
|
||||
wayland_err,
|
||||
x11_err,
|
||||
);
|
||||
panic!(err_string);
|
||||
}
|
||||
|
||||
pub fn new_wayland() -> Result<EventsLoop, ()> {
|
||||
wayland::EventsLoop::new()
|
||||
.map(EventsLoop::Wayland)
|
||||
.ok_or(())
|
||||
}
|
||||
|
||||
pub fn new_x11() -> Result<EventsLoop, XNotSupported> {
|
||||
match *X11_BACKEND {
|
||||
Ok(ref x) => Ok(EventsLoop::X(x11::EventsLoop::new(x.clone()))),
|
||||
Err(ref err) => Err(err.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
match *self {
|
||||
EventsLoop::Wayland(ref evlp) => evlp.get_available_monitors()
|
||||
.into_iter()
|
||||
.map(MonitorId::Wayland)
|
||||
.collect(),
|
||||
EventsLoop::X(ref evlp) => x11::get_available_monitors(evlp.x_connection())
|
||||
.into_iter()
|
||||
.map(MonitorId::X)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
match *self {
|
||||
EventsLoop::Wayland(ref evlp) => MonitorId::Wayland(evlp.get_primary_monitor()),
|
||||
EventsLoop::X(ref evlp) => MonitorId::X(x11::get_primary_monitor(evlp.x_connection())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventsLoopProxy {
|
||||
match *self {
|
||||
EventsLoop::Wayland(ref evlp) => EventsLoopProxy::Wayland(evlp.create_proxy()),
|
||||
EventsLoop::X(ref evlp) => EventsLoopProxy::X(evlp.create_proxy()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll_events<F>(&mut self, callback: F)
|
||||
where F: FnMut(::Event)
|
||||
{
|
||||
match *self {
|
||||
EventsLoop::Wayland(ref mut evlp) => evlp.poll_events(callback),
|
||||
EventsLoop::X(ref mut evlp) => evlp.poll_events(callback)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_forever<F>(&mut self, callback: F)
|
||||
where F: FnMut(::Event) -> ControlFlow
|
||||
{
|
||||
match *self {
|
||||
EventsLoop::Wayland(ref mut evlp) => evlp.run_forever(callback),
|
||||
EventsLoop::X(ref mut evlp) => evlp.run_forever(callback)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_wayland(&self) -> bool {
|
||||
match *self {
|
||||
EventsLoop::Wayland(_) => true,
|
||||
EventsLoop::X(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn x_connection(&self) -> Option<&Arc<XConnection>> {
|
||||
match *self {
|
||||
EventsLoop::Wayland(_) => None,
|
||||
EventsLoop::X(ref ev) => Some(ev.x_connection()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventsLoopProxy {
|
||||
pub fn wakeup(&self) -> Result<(), EventsLoopClosed> {
|
||||
match *self {
|
||||
EventsLoopProxy::Wayland(ref proxy) => proxy.wakeup(),
|
||||
EventsLoopProxy::X(ref proxy) => proxy.wakeup(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,469 +0,0 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use {ControlFlow, EventsLoopClosed};
|
||||
|
||||
use super::WindowId;
|
||||
use super::window::WindowStore;
|
||||
|
||||
use sctk::Environment;
|
||||
use sctk::output::OutputMgr;
|
||||
use sctk::reexports::client::{Display, EventQueue, GlobalEvent, Proxy};
|
||||
use sctk::reexports::client::commons::Implementation;
|
||||
use sctk::reexports::client::protocol::{wl_keyboard, wl_output, wl_pointer, wl_registry, wl_seat,
|
||||
wl_touch};
|
||||
|
||||
use sctk::reexports::client::protocol::wl_display::RequestsTrait as DisplayRequests;
|
||||
|
||||
pub struct EventsLoopSink {
|
||||
buffer: VecDeque<::Event>,
|
||||
}
|
||||
|
||||
impl EventsLoopSink {
|
||||
pub fn new() -> EventsLoopSink {
|
||||
EventsLoopSink {
|
||||
buffer: VecDeque::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_event(&mut self, evt: ::WindowEvent, wid: WindowId) {
|
||||
let evt = ::Event::WindowEvent {
|
||||
event: evt,
|
||||
window_id: ::WindowId(::platform::WindowId::Wayland(wid)),
|
||||
};
|
||||
self.buffer.push_back(evt);
|
||||
}
|
||||
|
||||
pub fn send_raw_event(&mut self, evt: ::Event) {
|
||||
self.buffer.push_back(evt);
|
||||
}
|
||||
|
||||
fn empty_with<F>(&mut self, callback: &mut F)
|
||||
where
|
||||
F: FnMut(::Event),
|
||||
{
|
||||
for evt in self.buffer.drain(..) {
|
||||
callback(evt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventsLoop {
|
||||
// The Event Queue
|
||||
pub evq: RefCell<EventQueue>,
|
||||
// our sink, shared with some handlers, buffering the events
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
// Whether or not there is a pending `Awakened` event to be emitted.
|
||||
pending_wakeup: Arc<AtomicBool>,
|
||||
// The window store
|
||||
pub store: Arc<Mutex<WindowStore>>,
|
||||
// the env
|
||||
pub env: Environment,
|
||||
// a cleanup switch to prune dead windows
|
||||
pub cleanup_needed: Arc<Mutex<bool>>,
|
||||
// The wayland display
|
||||
pub display: Arc<Display>,
|
||||
// The list of seats
|
||||
pub seats: Arc<Mutex<Vec<(u32, Proxy<wl_seat::WlSeat>)>>>,
|
||||
}
|
||||
|
||||
// A handle that can be sent across threads and used to wake up the `EventsLoop`.
|
||||
//
|
||||
// We should only try and wake up the `EventsLoop` if it still exists, so we hold Weak ptrs.
|
||||
#[derive(Clone)]
|
||||
pub struct EventsLoopProxy {
|
||||
display: Weak<Display>,
|
||||
pending_wakeup: Weak<AtomicBool>,
|
||||
}
|
||||
|
||||
impl EventsLoopProxy {
|
||||
// Causes the `EventsLoop` to stop blocking on `run_forever` and emit an `Awakened` event.
|
||||
//
|
||||
// Returns `Err` if the associated `EventsLoop` no longer exists.
|
||||
pub fn wakeup(&self) -> Result<(), EventsLoopClosed> {
|
||||
let display = self.display.upgrade();
|
||||
let wakeup = self.pending_wakeup.upgrade();
|
||||
match (display, wakeup) {
|
||||
(Some(display), Some(wakeup)) => {
|
||||
// Update the `EventsLoop`'s `pending_wakeup` flag.
|
||||
wakeup.store(true, Ordering::Relaxed);
|
||||
// Cause the `EventsLoop` to break from `dispatch` if it is currently blocked.
|
||||
let _ = display.sync();
|
||||
display.flush().map_err(|_| EventsLoopClosed)?;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(EventsLoopClosed),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn new() -> Option<EventsLoop> {
|
||||
let (display, mut event_queue) = match Display::connect_to_env() {
|
||||
Ok(ret) => ret,
|
||||
Err(_) => return None,
|
||||
};
|
||||
|
||||
let sink = Arc::new(Mutex::new(EventsLoopSink::new()));
|
||||
let store = Arc::new(Mutex::new(WindowStore::new()));
|
||||
let seats = Arc::new(Mutex::new(Vec::new()));
|
||||
|
||||
let env = Environment::from_registry_with_cb(
|
||||
display.get_registry().unwrap(),
|
||||
&mut event_queue,
|
||||
SeatManager {
|
||||
sink: sink.clone(),
|
||||
store: store.clone(),
|
||||
seats: seats.clone(),
|
||||
},
|
||||
).unwrap();
|
||||
|
||||
Some(EventsLoop {
|
||||
display: Arc::new(display),
|
||||
evq: RefCell::new(event_queue),
|
||||
sink: sink,
|
||||
pending_wakeup: Arc::new(AtomicBool::new(false)),
|
||||
store: store,
|
||||
env: env,
|
||||
cleanup_needed: Arc::new(Mutex::new(false)),
|
||||
seats: seats,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> EventsLoopProxy {
|
||||
EventsLoopProxy {
|
||||
display: Arc::downgrade(&self.display),
|
||||
pending_wakeup: Arc::downgrade(&self.pending_wakeup),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll_events<F>(&mut self, mut callback: F)
|
||||
where
|
||||
F: FnMut(::Event),
|
||||
{
|
||||
// send pending events to the server
|
||||
self.display.flush().expect("Wayland connection lost.");
|
||||
|
||||
// dispatch any pre-buffered events
|
||||
self.sink.lock().unwrap().empty_with(&mut callback);
|
||||
|
||||
// try to read pending events
|
||||
if let Some(h) = self.evq.get_mut().prepare_read() {
|
||||
h.read_events().expect("Wayland connection lost.");
|
||||
}
|
||||
// dispatch wayland events
|
||||
self.evq
|
||||
.get_mut()
|
||||
.dispatch_pending()
|
||||
.expect("Wayland connection lost.");
|
||||
self.post_dispatch_triggers();
|
||||
|
||||
// dispatch buffered events to client
|
||||
self.sink.lock().unwrap().empty_with(&mut callback);
|
||||
}
|
||||
|
||||
pub fn run_forever<F>(&mut self, mut callback: F)
|
||||
where
|
||||
F: FnMut(::Event) -> ControlFlow,
|
||||
{
|
||||
// send pending events to the server
|
||||
self.display.flush().expect("Wayland connection lost.");
|
||||
|
||||
// Check for control flow by wrapping the callback.
|
||||
let control_flow = ::std::cell::Cell::new(ControlFlow::Continue);
|
||||
let mut callback = |event| {
|
||||
if let ControlFlow::Break = callback(event) {
|
||||
control_flow.set(ControlFlow::Break);
|
||||
}
|
||||
};
|
||||
|
||||
// dispatch any pre-buffered events
|
||||
self.post_dispatch_triggers();
|
||||
self.sink.lock().unwrap().empty_with(&mut callback);
|
||||
|
||||
loop {
|
||||
// dispatch events blocking if needed
|
||||
self.evq
|
||||
.get_mut()
|
||||
.dispatch()
|
||||
.expect("Wayland connection lost.");
|
||||
self.post_dispatch_triggers();
|
||||
|
||||
// empty buffer of events
|
||||
self.sink.lock().unwrap().empty_with(&mut callback);
|
||||
|
||||
if let ControlFlow::Break = control_flow.get() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
self.env.outputs.with_all(|list| {
|
||||
if let Some(&(_, ref proxy, _)) = list.first() {
|
||||
MonitorId {
|
||||
proxy: proxy.clone(),
|
||||
mgr: self.env.outputs.clone(),
|
||||
}
|
||||
} else {
|
||||
panic!("No monitor is available.")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
self.env.outputs.with_all(|list| {
|
||||
list.iter()
|
||||
.map(|&(_, ref proxy, _)| MonitorId {
|
||||
proxy: proxy.clone(),
|
||||
mgr: self.env.outputs.clone(),
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Private EventsLoop Internals
|
||||
*/
|
||||
|
||||
impl EventsLoop {
|
||||
fn post_dispatch_triggers(&mut self) {
|
||||
let mut sink = self.sink.lock().unwrap();
|
||||
// process a possible pending wakeup call
|
||||
if self.pending_wakeup.load(Ordering::Relaxed) {
|
||||
sink.send_raw_event(::Event::Awakened);
|
||||
self.pending_wakeup.store(false, Ordering::Relaxed);
|
||||
}
|
||||
// prune possible dead windows
|
||||
{
|
||||
let mut cleanup_needed = self.cleanup_needed.lock().unwrap();
|
||||
if *cleanup_needed {
|
||||
let pruned = self.store.lock().unwrap().cleanup();
|
||||
*cleanup_needed = false;
|
||||
for wid in pruned {
|
||||
sink.send_event(::WindowEvent::Destroyed, wid);
|
||||
}
|
||||
}
|
||||
}
|
||||
// process pending resize/refresh
|
||||
self.store.lock().unwrap().for_each(
|
||||
|newsize, refresh, frame_refresh, closed, wid, frame| {
|
||||
if let Some(frame) = frame {
|
||||
if let Some((w, h)) = newsize {
|
||||
frame.resize(w as u32, h as u32);
|
||||
frame.refresh();
|
||||
sink.send_event(::WindowEvent::Resized(w as u32, h as u32), wid);
|
||||
} else if frame_refresh {
|
||||
frame.refresh();
|
||||
}
|
||||
}
|
||||
if refresh {
|
||||
sink.send_event(::WindowEvent::Refresh, wid);
|
||||
}
|
||||
if closed {
|
||||
sink.send_event(::WindowEvent::CloseRequested, wid);
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Wayland protocol implementations
|
||||
*/
|
||||
|
||||
struct SeatManager {
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
store: Arc<Mutex<WindowStore>>,
|
||||
seats: Arc<Mutex<Vec<(u32, Proxy<wl_seat::WlSeat>)>>>,
|
||||
}
|
||||
|
||||
impl Implementation<Proxy<wl_registry::WlRegistry>, GlobalEvent> for SeatManager {
|
||||
fn receive(&mut self, evt: GlobalEvent, registry: Proxy<wl_registry::WlRegistry>) {
|
||||
use self::wl_registry::RequestsTrait as RegistryRequests;
|
||||
use self::wl_seat::RequestsTrait as SeatRequests;
|
||||
match evt {
|
||||
GlobalEvent::New {
|
||||
id,
|
||||
ref interface,
|
||||
version,
|
||||
} if interface == "wl_seat" =>
|
||||
{
|
||||
use std::cmp::min;
|
||||
let seat = registry
|
||||
.bind::<wl_seat::WlSeat>(min(version, 5), id)
|
||||
.unwrap()
|
||||
.implement(SeatData {
|
||||
sink: self.sink.clone(),
|
||||
store: self.store.clone(),
|
||||
pointer: None,
|
||||
keyboard: None,
|
||||
touch: None,
|
||||
});
|
||||
self.store.lock().unwrap().new_seat(&seat);
|
||||
self.seats.lock().unwrap().push((id, seat));
|
||||
}
|
||||
GlobalEvent::Removed { id, ref interface } if interface == "wl_seat" => {
|
||||
let mut seats = self.seats.lock().unwrap();
|
||||
if let Some(idx) = seats.iter().position(|&(i, _)| i == id) {
|
||||
let (_, seat) = seats.swap_remove(idx);
|
||||
if seat.version() >= 5 {
|
||||
seat.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SeatData {
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
store: Arc<Mutex<WindowStore>>,
|
||||
pointer: Option<Proxy<wl_pointer::WlPointer>>,
|
||||
keyboard: Option<Proxy<wl_keyboard::WlKeyboard>>,
|
||||
touch: Option<Proxy<wl_touch::WlTouch>>,
|
||||
}
|
||||
|
||||
impl Implementation<Proxy<wl_seat::WlSeat>, wl_seat::Event> for SeatData {
|
||||
fn receive(&mut self, evt: wl_seat::Event, seat: Proxy<wl_seat::WlSeat>) {
|
||||
use self::wl_seat::RequestsTrait as SeatRequests;
|
||||
match evt {
|
||||
wl_seat::Event::Name { .. } => (),
|
||||
wl_seat::Event::Capabilities { capabilities } => {
|
||||
// create pointer if applicable
|
||||
if capabilities.contains(wl_seat::Capability::Pointer) && self.pointer.is_none() {
|
||||
self.pointer = Some(super::pointer::implement_pointer(
|
||||
seat.get_pointer().unwrap(),
|
||||
self.sink.clone(),
|
||||
self.store.clone(),
|
||||
))
|
||||
}
|
||||
// destroy pointer if applicable
|
||||
if !capabilities.contains(wl_seat::Capability::Pointer) {
|
||||
if let Some(pointer) = self.pointer.take() {
|
||||
if pointer.version() >= 3 {
|
||||
use self::wl_pointer::RequestsTrait;
|
||||
pointer.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
// create keyboard if applicable
|
||||
if capabilities.contains(wl_seat::Capability::Keyboard) && self.keyboard.is_none() {
|
||||
self.keyboard = Some(super::keyboard::init_keyboard(
|
||||
seat.get_keyboard().unwrap(),
|
||||
self.sink.clone(),
|
||||
))
|
||||
}
|
||||
// destroy keyboard if applicable
|
||||
if !capabilities.contains(wl_seat::Capability::Keyboard) {
|
||||
if let Some(kbd) = self.keyboard.take() {
|
||||
if kbd.version() >= 3 {
|
||||
use self::wl_keyboard::RequestsTrait;
|
||||
kbd.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
// create touch if applicable
|
||||
if capabilities.contains(wl_seat::Capability::Touch) && self.touch.is_none() {
|
||||
self.touch = Some(super::touch::implement_touch(
|
||||
seat.get_touch().unwrap(),
|
||||
self.sink.clone(),
|
||||
self.store.clone(),
|
||||
))
|
||||
}
|
||||
// destroy touch if applicable
|
||||
if !capabilities.contains(wl_seat::Capability::Touch) {
|
||||
if let Some(touch) = self.touch.take() {
|
||||
if touch.version() >= 3 {
|
||||
use self::wl_touch::RequestsTrait;
|
||||
touch.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SeatData {
|
||||
fn drop(&mut self) {
|
||||
if let Some(pointer) = self.pointer.take() {
|
||||
if pointer.version() >= 3 {
|
||||
use self::wl_pointer::RequestsTrait;
|
||||
pointer.release();
|
||||
}
|
||||
}
|
||||
if let Some(kbd) = self.keyboard.take() {
|
||||
if kbd.version() >= 3 {
|
||||
use self::wl_keyboard::RequestsTrait;
|
||||
kbd.release();
|
||||
}
|
||||
}
|
||||
if let Some(touch) = self.touch.take() {
|
||||
if touch.version() >= 3 {
|
||||
use self::wl_touch::RequestsTrait;
|
||||
touch.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Monitor stuff
|
||||
*/
|
||||
|
||||
pub struct MonitorId {
|
||||
pub(crate) proxy: Proxy<wl_output::WlOutput>,
|
||||
pub(crate) mgr: OutputMgr,
|
||||
}
|
||||
|
||||
impl Clone for MonitorId {
|
||||
fn clone(&self) -> MonitorId {
|
||||
MonitorId {
|
||||
proxy: self.proxy.clone(),
|
||||
mgr: self.mgr.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
self.mgr.with_info(&self.proxy, |_, info| {
|
||||
format!("{} ({})", info.model, info.make)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> u32 {
|
||||
self.mgr.with_info(&self.proxy, |id, _| id).unwrap_or(0)
|
||||
}
|
||||
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
match self.mgr.with_info(&self.proxy, |_, info| {
|
||||
info.modes
|
||||
.iter()
|
||||
.find(|m| m.is_current)
|
||||
.map(|m| m.dimensions)
|
||||
}) {
|
||||
Some(Some((w, h))) => (w as u32, h as u32),
|
||||
_ => (0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
self.mgr
|
||||
.with_info(&self.proxy, |_, info| info.location)
|
||||
.unwrap_or((0, 0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
self.mgr
|
||||
.with_info(&self.proxy, |_, info| info.scale_factor as f32)
|
||||
.unwrap_or(1.0)
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd",
|
||||
target_os = "openbsd"))]
|
||||
|
||||
pub use self::window::Window;
|
||||
pub use self::event_loop::{EventsLoop, EventsLoopProxy, EventsLoopSink, MonitorId};
|
||||
|
||||
use sctk::reexports::client::protocol::wl_surface;
|
||||
use sctk::reexports::client::Proxy;
|
||||
|
||||
mod event_loop;
|
||||
mod pointer;
|
||||
mod touch;
|
||||
mod keyboard;
|
||||
mod window;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(usize);
|
||||
|
||||
#[inline]
|
||||
fn make_wid(s: &Proxy<wl_surface::WlSurface>) -> WindowId {
|
||||
WindowId(s.c_ptr() as usize)
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use {ElementState, MouseButton, MouseScrollDelta, TouchPhase, WindowEvent};
|
||||
use events::ModifiersState;
|
||||
|
||||
use super::DeviceId;
|
||||
use super::event_loop::EventsLoopSink;
|
||||
use super::window::WindowStore;
|
||||
|
||||
use sctk::reexports::client::{NewProxy, Proxy};
|
||||
use sctk::reexports::client::protocol::wl_pointer::{self, Event as PtrEvent, WlPointer};
|
||||
|
||||
pub fn implement_pointer(
|
||||
pointer: NewProxy<WlPointer>,
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
store: Arc<Mutex<WindowStore>>,
|
||||
) -> Proxy<WlPointer> {
|
||||
let mut mouse_focus = None;
|
||||
let mut axis_buffer = None;
|
||||
let mut axis_discrete_buffer = None;
|
||||
let mut axis_state = TouchPhase::Ended;
|
||||
|
||||
pointer.implement(move |evt, pointer: Proxy<_>| {
|
||||
let mut sink = sink.lock().unwrap();
|
||||
let store = store.lock().unwrap();
|
||||
match evt {
|
||||
PtrEvent::Enter {
|
||||
surface,
|
||||
surface_x,
|
||||
surface_y,
|
||||
..
|
||||
} => {
|
||||
let wid = store.find_wid(&surface);
|
||||
if let Some(wid) = wid {
|
||||
mouse_focus = Some(wid);
|
||||
sink.send_event(
|
||||
WindowEvent::CursorEntered {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
sink.send_event(
|
||||
WindowEvent::CursorMoved {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
position: (surface_x, surface_y),
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
PtrEvent::Leave { surface, .. } => {
|
||||
mouse_focus = None;
|
||||
let wid = store.find_wid(&surface);
|
||||
if let Some(wid) = wid {
|
||||
sink.send_event(
|
||||
WindowEvent::CursorLeft {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
PtrEvent::Motion {
|
||||
surface_x,
|
||||
surface_y,
|
||||
..
|
||||
} => {
|
||||
if let Some(wid) = mouse_focus {
|
||||
sink.send_event(
|
||||
WindowEvent::CursorMoved {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
position: (surface_x, surface_y),
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
PtrEvent::Button { button, state, .. } => {
|
||||
if let Some(wid) = mouse_focus {
|
||||
let state = match state {
|
||||
wl_pointer::ButtonState::Pressed => ElementState::Pressed,
|
||||
wl_pointer::ButtonState::Released => ElementState::Released,
|
||||
};
|
||||
let button = match button {
|
||||
0x110 => MouseButton::Left,
|
||||
0x111 => MouseButton::Right,
|
||||
0x112 => MouseButton::Middle,
|
||||
// TODO figure out the translation ?
|
||||
_ => return,
|
||||
};
|
||||
sink.send_event(
|
||||
WindowEvent::MouseInput {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
state: state,
|
||||
button: button,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
PtrEvent::Axis { axis, value, .. } => {
|
||||
if let Some(wid) = mouse_focus {
|
||||
if pointer.version() < 5 {
|
||||
let (mut x, mut y) = (0.0, 0.0);
|
||||
// old seat compatibility
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= value as f32,
|
||||
wl_pointer::Axis::HorizontalScroll => x += value as f32,
|
||||
}
|
||||
sink.send_event(
|
||||
WindowEvent::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
|
||||
phase: TouchPhase::Moved,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
} else {
|
||||
let (mut x, mut y) = axis_buffer.unwrap_or((0.0, 0.0));
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= value as f32,
|
||||
wl_pointer::Axis::HorizontalScroll => x += value as f32,
|
||||
}
|
||||
axis_buffer = Some((x, y));
|
||||
axis_state = match axis_state {
|
||||
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
||||
_ => TouchPhase::Started,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PtrEvent::Frame => {
|
||||
let axis_buffer = axis_buffer.take();
|
||||
let axis_discrete_buffer = axis_discrete_buffer.take();
|
||||
if let Some(wid) = mouse_focus {
|
||||
if let Some((x, y)) = axis_discrete_buffer {
|
||||
sink.send_event(
|
||||
WindowEvent::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::LineDelta(x as f32, y as f32),
|
||||
phase: axis_state,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
} else if let Some((x, y)) = axis_buffer {
|
||||
sink.send_event(
|
||||
WindowEvent::MouseWheel {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
delta: MouseScrollDelta::PixelDelta(x as f32, y as f32),
|
||||
phase: axis_state,
|
||||
// TODO: replace dummy value with actual modifier state
|
||||
modifiers: ModifiersState::default(),
|
||||
},
|
||||
wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
PtrEvent::AxisSource { .. } => (),
|
||||
PtrEvent::AxisStop { .. } => {
|
||||
axis_state = TouchPhase::Ended;
|
||||
}
|
||||
PtrEvent::AxisDiscrete { axis, discrete } => {
|
||||
let (mut x, mut y) = axis_discrete_buffer.unwrap_or((0, 0));
|
||||
match axis {
|
||||
// wayland vertical sign convention is the inverse of winit
|
||||
wl_pointer::Axis::VerticalScroll => y -= discrete,
|
||||
wl_pointer::Axis::HorizontalScroll => x += discrete,
|
||||
}
|
||||
axis_discrete_buffer = Some((x, y));
|
||||
axis_state = match axis_state {
|
||||
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
||||
_ => TouchPhase::Started,
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use {TouchPhase, WindowEvent};
|
||||
|
||||
use super::{DeviceId, WindowId};
|
||||
use super::event_loop::EventsLoopSink;
|
||||
use super::window::WindowStore;
|
||||
|
||||
use sctk::reexports::client::{NewProxy, Proxy};
|
||||
use sctk::reexports::client::protocol::wl_touch::{Event as TouchEvent, WlTouch};
|
||||
|
||||
struct TouchPoint {
|
||||
wid: WindowId,
|
||||
location: (f64, f64),
|
||||
id: i32,
|
||||
}
|
||||
|
||||
pub(crate) fn implement_touch(
|
||||
touch: NewProxy<WlTouch>,
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
store: Arc<Mutex<WindowStore>>,
|
||||
) -> Proxy<WlTouch> {
|
||||
let mut pending_ids = Vec::new();
|
||||
touch.implement(move |evt, _| {
|
||||
let mut sink = sink.lock().unwrap();
|
||||
let store = store.lock().unwrap();
|
||||
match evt {
|
||||
TouchEvent::Down {
|
||||
surface, id, x, y, ..
|
||||
} => {
|
||||
let wid = store.find_wid(&surface);
|
||||
if let Some(wid) = wid {
|
||||
sink.send_event(
|
||||
WindowEvent::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Started,
|
||||
location: (x, y),
|
||||
id: id as u64,
|
||||
}),
|
||||
wid,
|
||||
);
|
||||
pending_ids.push(TouchPoint {
|
||||
wid: wid,
|
||||
location: (x, y),
|
||||
id: id,
|
||||
});
|
||||
}
|
||||
}
|
||||
TouchEvent::Up { id, .. } => {
|
||||
let idx = pending_ids.iter().position(|p| p.id == id);
|
||||
if let Some(idx) = idx {
|
||||
let pt = pending_ids.remove(idx);
|
||||
sink.send_event(
|
||||
WindowEvent::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Ended,
|
||||
location: pt.location,
|
||||
id: id as u64,
|
||||
}),
|
||||
pt.wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
TouchEvent::Motion { id, x, y, .. } => {
|
||||
let pt = pending_ids.iter_mut().find(|p| p.id == id);
|
||||
if let Some(pt) = pt {
|
||||
pt.location = (x, y);
|
||||
sink.send_event(
|
||||
WindowEvent::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Moved,
|
||||
location: (x, y),
|
||||
id: id as u64,
|
||||
}),
|
||||
pt.wid,
|
||||
);
|
||||
}
|
||||
}
|
||||
TouchEvent::Frame => (),
|
||||
TouchEvent::Cancel => for pt in pending_ids.drain(..) {
|
||||
sink.send_event(
|
||||
WindowEvent::Touch(::Touch {
|
||||
device_id: ::DeviceId(::platform::DeviceId::Wayland(DeviceId)),
|
||||
phase: TouchPhase::Cancelled,
|
||||
location: pt.location,
|
||||
id: pt.id as u64,
|
||||
}),
|
||||
pt.wid,
|
||||
);
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,361 +0,0 @@
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
|
||||
use {CreationError, CursorState, MouseCursor, WindowAttributes};
|
||||
use platform::MonitorId as PlatformMonitorId;
|
||||
use window::MonitorId as RootMonitorId;
|
||||
|
||||
use sctk::window::{BasicFrame, Event as WEvent, Window as SWindow};
|
||||
use sctk::reexports::client::{Display, Proxy};
|
||||
use sctk::reexports::client::protocol::{wl_seat, wl_surface};
|
||||
use sctk::reexports::client::protocol::wl_compositor::RequestsTrait as CompositorRequests;
|
||||
use sctk::reexports::client::protocol::wl_surface::RequestsTrait as SurfaceRequests;
|
||||
|
||||
use super::{make_wid, EventsLoop, MonitorId, WindowId};
|
||||
|
||||
pub struct Window {
|
||||
surface: Proxy<wl_surface::WlSurface>,
|
||||
frame: Arc<Mutex<SWindow<BasicFrame>>>,
|
||||
monitors: Arc<Mutex<Vec<MonitorId>>>,
|
||||
size: Arc<Mutex<(u32, u32)>>,
|
||||
kill_switch: (Arc<Mutex<bool>>, Arc<Mutex<bool>>),
|
||||
display: Arc<Display>,
|
||||
need_frame_refresh: Arc<Mutex<bool>>,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(evlp: &EventsLoop, attributes: WindowAttributes) -> Result<Window, CreationError> {
|
||||
let (width, height) = attributes.dimensions.unwrap_or((800, 600));
|
||||
// Create the window
|
||||
let size = Arc::new(Mutex::new((width, height)));
|
||||
|
||||
// monitor tracking
|
||||
let monitor_list = Arc::new(Mutex::new(Vec::new()));
|
||||
|
||||
let surface = evlp.env.compositor.create_surface().unwrap().implement({
|
||||
let list = monitor_list.clone();
|
||||
let omgr = evlp.env.outputs.clone();
|
||||
move |event, _| match event {
|
||||
wl_surface::Event::Enter { output } => list.lock().unwrap().push(MonitorId {
|
||||
proxy: output,
|
||||
mgr: omgr.clone(),
|
||||
}),
|
||||
wl_surface::Event::Leave { output } => {
|
||||
list.lock().unwrap().retain(|m| !m.proxy.equals(&output));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let window_store = evlp.store.clone();
|
||||
let my_surface = surface.clone();
|
||||
let mut frame = SWindow::<BasicFrame>::init(
|
||||
surface.clone(),
|
||||
(width, height),
|
||||
&evlp.env.compositor,
|
||||
&evlp.env.subcompositor,
|
||||
&evlp.env.shm,
|
||||
&evlp.env.shell,
|
||||
move |event, ()| match event {
|
||||
WEvent::Configure { new_size, .. } => {
|
||||
let mut store = window_store.lock().unwrap();
|
||||
for window in &mut store.windows {
|
||||
if window.surface.equals(&my_surface) {
|
||||
window.newsize = new_size.map(|(w, h)| (w as i32, h as i32));
|
||||
window.need_refresh = true;
|
||||
*(window.need_frame_refresh.lock().unwrap()) = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
WEvent::Refresh => {
|
||||
let store = window_store.lock().unwrap();
|
||||
for window in &store.windows {
|
||||
if window.surface.equals(&my_surface) {
|
||||
*(window.need_frame_refresh.lock().unwrap()) = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
WEvent::Close => {
|
||||
let mut store = window_store.lock().unwrap();
|
||||
for window in &mut store.windows {
|
||||
if window.surface.equals(&my_surface) {
|
||||
window.closed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
).unwrap();
|
||||
|
||||
for &(_, ref seat) in evlp.seats.lock().unwrap().iter() {
|
||||
frame.new_seat(seat);
|
||||
}
|
||||
|
||||
// Check for fullscreen requirements
|
||||
if let Some(RootMonitorId {
|
||||
inner: PlatformMonitorId::Wayland(ref monitor_id),
|
||||
}) = attributes.fullscreen
|
||||
{
|
||||
frame.set_fullscreen(Some(&monitor_id.proxy));
|
||||
} else if attributes.maximized {
|
||||
frame.set_maximized();
|
||||
}
|
||||
|
||||
// set decorations
|
||||
frame.set_decorate(attributes.decorations);
|
||||
|
||||
// min-max dimensions
|
||||
frame.set_min_size(attributes.min_dimensions);
|
||||
frame.set_max_size(attributes.max_dimensions);
|
||||
|
||||
let kill_switch = Arc::new(Mutex::new(false));
|
||||
let need_frame_refresh = Arc::new(Mutex::new(true));
|
||||
let frame = Arc::new(Mutex::new(frame));
|
||||
|
||||
evlp.store.lock().unwrap().windows.push(InternalWindow {
|
||||
closed: false,
|
||||
newsize: None,
|
||||
need_refresh: false,
|
||||
need_frame_refresh: need_frame_refresh.clone(),
|
||||
surface: surface.clone(),
|
||||
kill_switch: kill_switch.clone(),
|
||||
frame: Arc::downgrade(&frame),
|
||||
});
|
||||
evlp.evq.borrow_mut().sync_roundtrip().unwrap();
|
||||
|
||||
Ok(Window {
|
||||
display: evlp.display.clone(),
|
||||
surface: surface,
|
||||
frame: frame,
|
||||
monitors: monitor_list,
|
||||
size: size,
|
||||
kill_switch: (kill_switch, evlp.cleanup_needed.clone()),
|
||||
need_frame_refresh: need_frame_refresh,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
make_wid(&self.surface)
|
||||
}
|
||||
|
||||
pub fn set_title(&self, title: &str) {
|
||||
self.frame.lock().unwrap().set_title(title.into());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show(&self) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hide(&self) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
// Not possible with wayland
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_position(&self) -> Option<(i32, i32)> {
|
||||
// Not possible with wayland
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_position(&self, _x: i32, _y: i32) {
|
||||
// Not possible with wayland
|
||||
}
|
||||
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
Some(self.size.lock().unwrap().clone())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
let (w, h) = self.size.lock().unwrap().clone();
|
||||
// let (w, h) = super::wayland_window::add_borders(w as i32, h as i32);
|
||||
Some((w as u32, h as u32))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
// NOTE: This will only resize the borders, the contents must be updated by the user
|
||||
pub fn set_inner_size(&self, x: u32, y: u32) {
|
||||
self.frame.lock().unwrap().resize(x, y);
|
||||
*(self.size.lock().unwrap()) = (x, y);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_min_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
self.frame.lock().unwrap().set_min_size(dimensions);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_max_dimensions(&self, dimensions: Option<(u32, u32)>) {
|
||||
self.frame.lock().unwrap().set_max_size(dimensions);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, _cursor: MouseCursor) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
use CursorState::{Grab, Hide, Normal};
|
||||
// TODO : not yet possible on wayland to grab cursor
|
||||
match state {
|
||||
Grab => Err("Cursor cannot be grabbed on wayland yet.".to_string()),
|
||||
Hide => Err("Cursor cannot be hidden on wayland yet.".to_string()),
|
||||
Normal => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
let mut factor: f32 = 1.0;
|
||||
let guard = self.monitors.lock().unwrap();
|
||||
for monitor_id in guard.iter() {
|
||||
let hidpif = monitor_id.get_hidpi_factor();
|
||||
factor = factor.max(hidpif);
|
||||
}
|
||||
factor
|
||||
}
|
||||
|
||||
pub fn set_decorations(&self, decorate: bool) {
|
||||
self.frame.lock().unwrap().set_decorate(decorate);
|
||||
*(self.need_frame_refresh.lock().unwrap()) = true;
|
||||
}
|
||||
|
||||
pub fn set_maximized(&self, maximized: bool) {
|
||||
if maximized {
|
||||
self.frame.lock().unwrap().set_maximized();
|
||||
} else {
|
||||
self.frame.lock().unwrap().unset_maximized();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_fullscreen(&self, monitor: Option<RootMonitorId>) {
|
||||
if let Some(RootMonitorId {
|
||||
inner: PlatformMonitorId::Wayland(ref monitor_id),
|
||||
}) = monitor
|
||||
{
|
||||
self.frame
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_fullscreen(Some(&monitor_id.proxy));
|
||||
} else {
|
||||
self.frame.lock().unwrap().unset_fullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
|
||||
// TODO: not yet possible on wayland
|
||||
Err(())
|
||||
}
|
||||
|
||||
pub fn get_display(&self) -> &Display {
|
||||
&*self.display
|
||||
}
|
||||
|
||||
pub fn get_surface(&self) -> &Proxy<wl_surface::WlSurface> {
|
||||
&self.surface
|
||||
}
|
||||
|
||||
pub fn get_current_monitor(&self) -> MonitorId {
|
||||
// we don't know how much each monitor sees us so...
|
||||
// just return the most recent one ?
|
||||
let guard = self.monitors.lock().unwrap();
|
||||
guard.last().unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
*(self.kill_switch.0.lock().unwrap()) = true;
|
||||
*(self.kill_switch.1.lock().unwrap()) = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal store for windows
|
||||
*/
|
||||
|
||||
struct InternalWindow {
|
||||
surface: Proxy<wl_surface::WlSurface>,
|
||||
newsize: Option<(i32, i32)>,
|
||||
need_refresh: bool,
|
||||
need_frame_refresh: Arc<Mutex<bool>>,
|
||||
closed: bool,
|
||||
kill_switch: Arc<Mutex<bool>>,
|
||||
frame: Weak<Mutex<SWindow<BasicFrame>>>,
|
||||
}
|
||||
|
||||
pub struct WindowStore {
|
||||
windows: Vec<InternalWindow>,
|
||||
}
|
||||
|
||||
impl WindowStore {
|
||||
pub fn new() -> WindowStore {
|
||||
WindowStore {
|
||||
windows: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_wid(&self, surface: &Proxy<wl_surface::WlSurface>) -> Option<WindowId> {
|
||||
for window in &self.windows {
|
||||
if surface.equals(&window.surface) {
|
||||
return Some(make_wid(surface));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn cleanup(&mut self) -> Vec<WindowId> {
|
||||
let mut pruned = Vec::new();
|
||||
self.windows.retain(|w| {
|
||||
if *w.kill_switch.lock().unwrap() {
|
||||
// window is dead, cleanup
|
||||
pruned.push(make_wid(&w.surface));
|
||||
w.surface.destroy();
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
pruned
|
||||
}
|
||||
|
||||
pub fn new_seat(&self, seat: &Proxy<wl_seat::WlSeat>) {
|
||||
for window in &self.windows {
|
||||
if let Some(w) = window.frame.upgrade() {
|
||||
w.lock().unwrap().new_seat(seat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_each<F>(&mut self, mut f: F)
|
||||
where
|
||||
F: FnMut(Option<(i32, i32)>, bool, bool, bool, WindowId, Option<&mut SWindow<BasicFrame>>),
|
||||
{
|
||||
for window in &mut self.windows {
|
||||
let opt_arc = window.frame.upgrade();
|
||||
let mut opt_mutex_lock = opt_arc.as_ref().map(|m| m.lock().unwrap());
|
||||
f(
|
||||
window.newsize.take(),
|
||||
window.need_refresh,
|
||||
::std::mem::replace(&mut *window.need_frame_refresh.lock().unwrap(), false),
|
||||
window.closed,
|
||||
make_wid(&window.surface),
|
||||
opt_mutex_lock.as_mut().map(|m| &mut **m),
|
||||
);
|
||||
window.need_refresh = false;
|
||||
// avoid re-spamming the event
|
||||
window.closed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +0,0 @@
|
||||
pub use x11_dl::keysym::*;
|
||||
pub use x11_dl::xcursor::*;
|
||||
pub use x11_dl::xlib::*;
|
||||
pub use x11_dl::xinput::*;
|
||||
pub use x11_dl::xinput2::*;
|
||||
pub use x11_dl::xlib_xcb::*;
|
||||
pub use x11_dl::error::OpenError;
|
||||
pub use x11_dl::xrandr::*;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,129 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
use std::slice;
|
||||
|
||||
use super::XConnection;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MonitorId {
|
||||
/// The actual id
|
||||
id: u32,
|
||||
/// The name of the monitor
|
||||
name: String,
|
||||
/// The size of the monitor
|
||||
dimensions: (u32, u32),
|
||||
/// The position of the monitor in the X screen
|
||||
position: (i32, i32),
|
||||
/// If the monitor is the primary one
|
||||
primary: bool,
|
||||
/// The DPI scaling factor
|
||||
hidpi_factor: f32,
|
||||
}
|
||||
|
||||
pub fn get_available_monitors(x: &Arc<XConnection>) -> Vec<MonitorId> {
|
||||
let mut available = Vec::new();
|
||||
unsafe {
|
||||
let root = (x.xlib.XDefaultRootWindow)(x.display);
|
||||
let resources = (x.xrandr.XRRGetScreenResources)(x.display, root);
|
||||
|
||||
if let Some(ref xrandr_1_5) = x.xrandr_1_5 {
|
||||
// We're in XRandR >= 1.5, enumerate Monitors to handle things like MST and videowalls
|
||||
let mut nmonitors = 0;
|
||||
let monitors = (xrandr_1_5.XRRGetMonitors)(x.display, root, 1, &mut nmonitors);
|
||||
for i in 0..nmonitors {
|
||||
let monitor = *(monitors.offset(i as isize));
|
||||
let output = (xrandr_1_5.XRRGetOutputInfo)(x.display, resources, *(monitor.outputs.offset(0)));
|
||||
let nameslice = slice::from_raw_parts((*output).name as *mut u8, (*output).nameLen as usize);
|
||||
let name = String::from_utf8_lossy(nameslice).into_owned();
|
||||
let hidpi_factor = {
|
||||
let x_mm = (*output).mm_width as f32;
|
||||
let y_mm = (*output).mm_height as f32;
|
||||
let x_px = monitor.width as f32;
|
||||
let y_px = monitor.height as f32;
|
||||
let ppmm = ((x_px * y_px) / (x_mm * y_mm)).sqrt();
|
||||
|
||||
// Quantize 1/12 step size
|
||||
((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0)
|
||||
};
|
||||
(xrandr_1_5.XRRFreeOutputInfo)(output);
|
||||
available.push(MonitorId{
|
||||
id: i as u32,
|
||||
name,
|
||||
hidpi_factor,
|
||||
dimensions: (monitor.width as u32, monitor.height as u32),
|
||||
position: (monitor.x as i32, monitor.y as i32),
|
||||
primary: (monitor.primary != 0),
|
||||
});
|
||||
}
|
||||
(xrandr_1_5.XRRFreeMonitors)(monitors);
|
||||
} else {
|
||||
// We're in XRandR < 1.5, enumerate CRTCs. Everything will work but MST and
|
||||
// videowall setups will show more monitors than the logical groups the user
|
||||
// cares about
|
||||
for i in 0..(*resources).ncrtc {
|
||||
let crtcid = *((*resources).crtcs.offset(i as isize));
|
||||
let crtc = (x.xrandr.XRRGetCrtcInfo)(x.display, resources, crtcid);
|
||||
if (*crtc).width > 0 && (*crtc).height > 0 && (*crtc).noutput > 0 {
|
||||
let output = (x.xrandr.XRRGetOutputInfo)(x.display, resources, *((*crtc).outputs.offset(0)));
|
||||
let nameslice = slice::from_raw_parts((*output).name as *mut u8, (*output).nameLen as usize);
|
||||
let name = String::from_utf8_lossy(nameslice).into_owned();
|
||||
|
||||
let hidpi_factor = {
|
||||
let x_mm = (*output).mm_width as f32;
|
||||
let y_mm = (*output).mm_height as f32;
|
||||
let x_px = (*crtc).width as f32;
|
||||
let y_px = (*crtc).height as f32;
|
||||
let ppmm = ((x_px * y_px) / (x_mm * y_mm)).sqrt();
|
||||
|
||||
// Quantize 1/12 step size
|
||||
((ppmm * (12.0 * 25.4 / 96.0)).round() / 12.0).max(1.0)
|
||||
};
|
||||
|
||||
(x.xrandr.XRRFreeOutputInfo)(output);
|
||||
available.push(MonitorId{
|
||||
id: crtcid as u32,
|
||||
name,
|
||||
hidpi_factor,
|
||||
dimensions: ((*crtc).width as u32, (*crtc).height as u32),
|
||||
position: ((*crtc).x as i32, (*crtc).y as i32),
|
||||
primary: true,
|
||||
});
|
||||
}
|
||||
(x.xrandr.XRRFreeCrtcInfo)(crtc);
|
||||
}
|
||||
}
|
||||
(x.xrandr.XRRFreeScreenResources)(resources);
|
||||
}
|
||||
available
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(x: &Arc<XConnection>) -> MonitorId {
|
||||
get_available_monitors(x).into_iter().find(|m| m.primary)
|
||||
// 'no primary' case is better handled picking some existing monitor
|
||||
.or_else(|| get_available_monitors(x).into_iter().next())
|
||||
.expect("[winit] Failed to find any x11 monitor")
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
Some(self.name.clone())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> u32 {
|
||||
self.id as u32
|
||||
}
|
||||
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
self.dimensions
|
||||
}
|
||||
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
self.position
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
self.hidpi_factor
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::*;
|
||||
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use super::*;
|
||||
|
||||
type AtomCache = HashMap<CString, ffi::Atom>;
|
||||
|
||||
lazy_static! {
|
||||
static ref ATOM_CACHE: Mutex<AtomCache> = Mutex::new(HashMap::with_capacity(2048));
|
||||
}
|
||||
|
||||
pub unsafe fn get_atom(xconn: &Arc<XConnection>, name: &[u8]) -> Result<ffi::Atom, XError> {
|
||||
let name = CStr::from_bytes_with_nul_unchecked(name); // I trust you. Don't let me down.
|
||||
let mut atom_cache_lock = ATOM_CACHE.lock();
|
||||
let cached_atom = (*atom_cache_lock).get(name).cloned();
|
||||
if let Some(atom) = cached_atom {
|
||||
Ok(atom)
|
||||
} else {
|
||||
let atom = (xconn.xlib.XInternAtom)(
|
||||
xconn.display,
|
||||
name.as_ptr() as *const c_char,
|
||||
ffi::False,
|
||||
);
|
||||
/*println!(
|
||||
"XInternAtom name:{:?} atom:{:?}",
|
||||
name,
|
||||
atom,
|
||||
);*/
|
||||
xconn.check_errors()?;
|
||||
(*atom_cache_lock).insert(name.to_owned(), atom);
|
||||
Ok(atom)
|
||||
}
|
||||
}
|
||||
|
||||
// Note: this doesn't use caching, for the sake of simplicity.
|
||||
// If you're dealing with this many atoms, you'll usually want to cache them locally anyway.
|
||||
pub unsafe fn get_atoms(
|
||||
xconn: &Arc<XConnection>,
|
||||
names: &[*mut c_char],
|
||||
) -> Result<Vec<ffi::Atom>, XError> {
|
||||
let mut atoms = Vec::with_capacity(names.len());
|
||||
(xconn.xlib.XInternAtoms)(
|
||||
xconn.display,
|
||||
names.as_ptr() as *mut _,
|
||||
names.len() as c_int,
|
||||
ffi::False,
|
||||
atoms.as_mut_ptr(),
|
||||
);
|
||||
xconn.check_errors()?;
|
||||
atoms.set_len(names.len());
|
||||
/*println!(
|
||||
"XInternAtoms atoms:{:?}",
|
||||
atoms,
|
||||
);*/
|
||||
Ok(atoms)
|
||||
}
|
||||
@@ -1,350 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TranslatedCoords {
|
||||
pub x_rel_root: c_int,
|
||||
pub y_rel_root: c_int,
|
||||
pub child: ffi::Window,
|
||||
}
|
||||
|
||||
// This is adequate for get_inner_position
|
||||
pub unsafe fn translate_coords(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
root: ffi::Window,
|
||||
) -> Result<TranslatedCoords, XError> {
|
||||
let mut translated_coords: TranslatedCoords = mem::uninitialized();
|
||||
|
||||
(xconn.xlib.XTranslateCoordinates)(
|
||||
xconn.display,
|
||||
window,
|
||||
root,
|
||||
0,
|
||||
0,
|
||||
&mut translated_coords.x_rel_root,
|
||||
&mut translated_coords.y_rel_root,
|
||||
&mut translated_coords.child,
|
||||
);
|
||||
|
||||
//println!("XTranslateCoordinates coords:{:?}", translated_coords);
|
||||
|
||||
xconn.check_errors().map(|_| translated_coords)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Geometry {
|
||||
pub root: ffi::Window,
|
||||
// If you want positions relative to the root window, use translate_coords.
|
||||
// Note that the overwhelming majority of window managers are reparenting WMs, thus the window
|
||||
// ID we get from window creation is for a nested window used as the window's client area. If
|
||||
// you call get_geometry with that window ID, then you'll get the position of that client area
|
||||
// window relative to the parent it's nested in (the frame), which isn't helpful if you want
|
||||
// to know the frame position.
|
||||
pub x_rel_parent: c_int,
|
||||
pub y_rel_parent: c_int,
|
||||
// In that same case, this will give you client area size.
|
||||
pub width: c_uint,
|
||||
pub height: c_uint,
|
||||
// xmonad and dwm were the only WMs tested that use the border return at all.
|
||||
// The majority of WMs seem to simply fill it with 0 unconditionally.
|
||||
pub border: c_uint,
|
||||
pub depth: c_uint,
|
||||
}
|
||||
|
||||
// This is adequate for get_inner_size
|
||||
pub unsafe fn get_geometry(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
) -> Result<Geometry, XError> {
|
||||
let mut geometry: Geometry = mem::uninitialized();
|
||||
|
||||
let _status = (xconn.xlib.XGetGeometry)(
|
||||
xconn.display,
|
||||
window,
|
||||
&mut geometry.root,
|
||||
&mut geometry.x_rel_parent,
|
||||
&mut geometry.y_rel_parent,
|
||||
&mut geometry.width,
|
||||
&mut geometry.height,
|
||||
&mut geometry.border,
|
||||
&mut geometry.depth,
|
||||
);
|
||||
|
||||
//println!("XGetGeometry geo:{:?}", geometry);
|
||||
|
||||
xconn.check_errors().map(|_| geometry)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FrameExtents {
|
||||
pub left: c_ulong,
|
||||
pub right: c_ulong,
|
||||
pub top: c_ulong,
|
||||
pub bottom: c_ulong,
|
||||
}
|
||||
|
||||
impl FrameExtents {
|
||||
pub fn new(left: c_ulong, right: c_ulong, top: c_ulong, bottom: c_ulong) -> Self {
|
||||
FrameExtents { left, right, top, bottom }
|
||||
}
|
||||
|
||||
pub fn from_border(border: c_ulong) -> Self {
|
||||
Self::new(border, border, border, border)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_frame_extents(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
) -> Option<self::FrameExtents> {
|
||||
let extents_atom = unsafe { self::get_atom(xconn, b"_NET_FRAME_EXTENTS\0") }
|
||||
.expect("Failed to call XInternAtom (_NET_FRAME_EXTENTS)");
|
||||
|
||||
if !self::hint_is_supported(extents_atom) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Of the WMs tested, xmonad, i3, dwm, IceWM (1.3.x and earlier), and blackbox don't
|
||||
// support this. As this is part of EWMH (Extended Window Manager Hints), it's likely to
|
||||
// be unsupported by many smaller WMs.
|
||||
let extents: Option<Vec<c_ulong>> = unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
window,
|
||||
extents_atom,
|
||||
ffi::XA_CARDINAL,
|
||||
)
|
||||
}.ok();
|
||||
|
||||
extents.and_then(|extents| {
|
||||
if extents.len() >= 4 {
|
||||
Some(self::FrameExtents {
|
||||
left: extents[0],
|
||||
right: extents[1],
|
||||
top: extents[2],
|
||||
bottom: extents[3],
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_top_level(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
root: ffi::Window,
|
||||
) -> Option<bool> {
|
||||
let client_list_atom = unsafe { self::get_atom(xconn, b"_NET_CLIENT_LIST\0") }
|
||||
.expect("Failed to call XInternAtom (_NET_CLIENT_LIST)");
|
||||
|
||||
if !self::hint_is_supported(client_list_atom) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let client_list: Option<Vec<ffi::Window>> = unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
root,
|
||||
client_list_atom,
|
||||
ffi::XA_WINDOW,
|
||||
)
|
||||
}.ok();
|
||||
|
||||
client_list.map(|client_list| client_list.contains(&window))
|
||||
}
|
||||
|
||||
unsafe fn get_parent_window(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
) -> Result<ffi::Window, XError> {
|
||||
let mut root: ffi::Window = mem::uninitialized();
|
||||
let mut parent: ffi::Window = mem::uninitialized();
|
||||
let mut children: *mut ffi::Window = ptr::null_mut();
|
||||
let mut nchildren: c_uint = mem::uninitialized();
|
||||
|
||||
let _status = (xconn.xlib.XQueryTree)(
|
||||
xconn.display,
|
||||
window,
|
||||
&mut root,
|
||||
&mut parent,
|
||||
&mut children,
|
||||
&mut nchildren,
|
||||
);
|
||||
|
||||
// The list of children isn't used
|
||||
if children != ptr::null_mut() {
|
||||
(xconn.xlib.XFree)(children as *mut _);
|
||||
}
|
||||
|
||||
xconn.check_errors().map(|_| parent)
|
||||
}
|
||||
|
||||
fn climb_hierarchy(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
root: ffi::Window,
|
||||
) -> Result<ffi::Window, XError> {
|
||||
let mut outer_window = window;
|
||||
loop {
|
||||
let candidate = unsafe { get_parent_window(xconn, outer_window) }?;
|
||||
if candidate == root {
|
||||
break;
|
||||
}
|
||||
outer_window = candidate;
|
||||
}
|
||||
Ok(outer_window)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum FrameExtentsHeuristicPath {
|
||||
Supported,
|
||||
UnsupportedNested,
|
||||
UnsupportedBordered,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FrameExtentsHeuristic {
|
||||
pub frame_extents: FrameExtents,
|
||||
pub heuristic_path: FrameExtentsHeuristicPath,
|
||||
}
|
||||
|
||||
impl FrameExtentsHeuristic {
|
||||
pub fn inner_pos_to_outer(&self, x: i32, y: i32) -> (i32, i32) {
|
||||
use self::FrameExtentsHeuristicPath::*;
|
||||
if self.heuristic_path != UnsupportedBordered {
|
||||
(x - self.frame_extents.left as i32, y - self.frame_extents.top as i32)
|
||||
} else {
|
||||
(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inner_size_to_outer(&self, width: u32, height: u32) -> (u32, u32) {
|
||||
(
|
||||
width.saturating_add(
|
||||
self.frame_extents.left.saturating_add(self.frame_extents.right) as u32
|
||||
),
|
||||
height.saturating_add(
|
||||
self.frame_extents.top.saturating_add(self.frame_extents.bottom) as u32
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_frame_extents_heuristic(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
root: ffi::Window,
|
||||
) -> FrameExtentsHeuristic {
|
||||
use self::FrameExtentsHeuristicPath::*;
|
||||
|
||||
// Position relative to root window.
|
||||
// With rare exceptions, this is the position of a nested window. Cases where the window
|
||||
// isn't nested are outlined in the comments throghout this function, but in addition to
|
||||
// that, fullscreen windows often aren't nested.
|
||||
let (inner_y_rel_root, child) = {
|
||||
let coords = unsafe { translate_coords(xconn, window, root) }
|
||||
.expect("Failed to translate window coordinates");
|
||||
(
|
||||
coords.y_rel_root,
|
||||
coords.child,
|
||||
)
|
||||
};
|
||||
|
||||
let (width, height, border) = {
|
||||
let inner_geometry = unsafe { get_geometry(xconn, window) }
|
||||
.expect("Failed to get inner window geometry");
|
||||
(
|
||||
inner_geometry.width,
|
||||
inner_geometry.height,
|
||||
inner_geometry.border,
|
||||
)
|
||||
};
|
||||
|
||||
// The first condition is only false for un-nested windows, but isn't always false for
|
||||
// un-nested windows. Mutter/Muffin/Budgie and Marco present a mysterious discrepancy:
|
||||
// when y is on the range [0, 2] and if the window has been unfocused since being
|
||||
// undecorated (or was undecorated upon construction), the first condition is true,
|
||||
// requiring us to rely on the second condition.
|
||||
let nested = !(window == child || is_top_level(xconn, child, root) == Some(true));
|
||||
|
||||
// Hopefully the WM supports EWMH, allowing us to get exact info on the window frames.
|
||||
if let Some(mut frame_extents) = get_frame_extents(xconn, window) {
|
||||
// Mutter/Muffin/Budgie and Marco preserve their decorated frame extents when
|
||||
// decorations are disabled, but since the window becomes un-nested, it's easy to
|
||||
// catch.
|
||||
if !nested {
|
||||
frame_extents = FrameExtents::new(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
// The difference between the nested window's position and the outermost window's
|
||||
// position is equivalent to the frame size. In most scenarios, this is equivalent to
|
||||
// manually climbing the hierarchy as is done in the case below. Here's a list of
|
||||
// known discrepancies:
|
||||
// * Mutter/Muffin/Budgie gives decorated windows a margin of 9px (only 7px on top) in
|
||||
// addition to a 1px semi-transparent border. The margin can be easily observed by
|
||||
// using a screenshot tool to get a screenshot of a selected window, and is
|
||||
// presumably used for drawing drop shadows. Getting window geometry information
|
||||
// via hierarchy-climbing results in this margin being included in both the
|
||||
// position and outer size, so a window positioned at (0, 0) would be reported as
|
||||
// having a position (-10, -8).
|
||||
// * Compiz has a drop shadow margin just like Mutter/Muffin/Budgie, though it's 10px
|
||||
// on all sides, and there's no additional border.
|
||||
// * Enlightenment otherwise gets a y position equivalent to inner_y_rel_root.
|
||||
// Without decorations, there's no difference. This is presumably related to
|
||||
// Enlightenment's fairly unique concept of window position; it interprets
|
||||
// positions given to XMoveWindow as a client area position rather than a position
|
||||
// of the overall window.
|
||||
|
||||
FrameExtentsHeuristic {
|
||||
frame_extents,
|
||||
heuristic_path: Supported,
|
||||
}
|
||||
} else if nested {
|
||||
// If the position value we have is for a nested window used as the client area, we'll
|
||||
// just climb up the hierarchy and get the geometry of the outermost window we're
|
||||
// nested in.
|
||||
let outer_window = climb_hierarchy(xconn, window, root)
|
||||
.expect("Failed to climb window hierarchy");
|
||||
|
||||
let (outer_y, outer_width, outer_height) = {
|
||||
let outer_geometry = unsafe { get_geometry(xconn, outer_window) }
|
||||
.expect("Failed to get outer window geometry");
|
||||
(
|
||||
outer_geometry.y_rel_parent,
|
||||
outer_geometry.width,
|
||||
outer_geometry.height,
|
||||
)
|
||||
};
|
||||
|
||||
// Since we have the geometry of the outermost window and the geometry of the client
|
||||
// area, we can figure out what's in between.
|
||||
let diff_x = outer_width.saturating_sub(width);
|
||||
let diff_y = outer_height.saturating_sub(height);
|
||||
let offset_y = inner_y_rel_root.saturating_sub(outer_y) as c_uint;
|
||||
|
||||
let left = diff_x / 2;
|
||||
let right = left;
|
||||
let top = offset_y;
|
||||
let bottom = diff_y.saturating_sub(offset_y);
|
||||
|
||||
let frame_extents = FrameExtents::new(
|
||||
left.into(),
|
||||
right.into(),
|
||||
top.into(),
|
||||
bottom.into(),
|
||||
);
|
||||
FrameExtentsHeuristic {
|
||||
frame_extents,
|
||||
heuristic_path: UnsupportedNested,
|
||||
}
|
||||
} else {
|
||||
// This is the case for xmonad and dwm, AKA the only WMs tested that supplied a
|
||||
// border value. This is convenient, since we can use it to get an accurate frame.
|
||||
let frame_extents = FrameExtents::from_border(border.into());
|
||||
FrameExtentsHeuristic {
|
||||
frame_extents,
|
||||
heuristic_path: UnsupportedBordered,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
use super::*;
|
||||
|
||||
pub const MWM_HINTS_DECORATIONS: c_ulong = 2;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum StateOperation {
|
||||
Remove = 0, // _NET_WM_STATE_REMOVE
|
||||
Add = 1, // _NET_WM_STATE_ADD
|
||||
_Toggle = 2, // _NET_WM_STATE_TOGGLE
|
||||
}
|
||||
|
||||
impl From<bool> for StateOperation {
|
||||
fn from(b: bool) -> Self {
|
||||
if b {
|
||||
StateOperation::Add
|
||||
} else {
|
||||
StateOperation::Remove
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
use super::*;
|
||||
use events::ModifiersState;
|
||||
|
||||
pub unsafe fn select_xinput_events(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: c_ulong,
|
||||
device_id: c_int,
|
||||
mask: i32,
|
||||
) -> Flusher {
|
||||
let mut event_mask = ffi::XIEventMask {
|
||||
deviceid: device_id,
|
||||
mask: &mask as *const _ as *mut c_uchar,
|
||||
mask_len: mem::size_of_val(&mask) as c_int,
|
||||
};
|
||||
(xconn.xinput2.XISelectEvents)(
|
||||
xconn.display,
|
||||
window,
|
||||
&mut event_mask as *mut ffi::XIEventMask,
|
||||
1, // number of masks to read from pointer above
|
||||
);
|
||||
Flusher::new(xconn)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub unsafe fn select_xkb_events(
|
||||
xconn: &Arc<XConnection>,
|
||||
device_id: c_uint,
|
||||
mask: c_ulong,
|
||||
) -> Option<Flusher> {
|
||||
let status = (xconn.xlib.XkbSelectEvents)(
|
||||
xconn.display,
|
||||
device_id,
|
||||
mask,
|
||||
mask,
|
||||
);
|
||||
if status == ffi::True {
|
||||
Some(Flusher::new(xconn))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ffi::XIModifierState> for ModifiersState {
|
||||
fn from(mods: ffi::XIModifierState) -> Self {
|
||||
let state = mods.effective as c_uint;
|
||||
ModifiersState {
|
||||
alt: state & ffi::Mod1Mask != 0,
|
||||
shift: state & ffi::ShiftMask != 0,
|
||||
ctrl: state & ffi::ControlMask != 0,
|
||||
logo: state & ffi::Mod4Mask != 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PointerState<'a> {
|
||||
xconn: &'a Arc<XConnection>,
|
||||
_root: ffi::Window,
|
||||
_child: ffi::Window,
|
||||
_root_x: c_double,
|
||||
_root_y: c_double,
|
||||
_win_x: c_double,
|
||||
_win_y: c_double,
|
||||
_buttons: ffi::XIButtonState,
|
||||
modifiers: ffi::XIModifierState,
|
||||
_group: ffi::XIGroupState,
|
||||
_relative_to_window: bool,
|
||||
}
|
||||
|
||||
impl<'a> PointerState<'a> {
|
||||
pub fn get_modifier_state(&self) -> ModifiersState {
|
||||
self.modifiers.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for PointerState<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
// This is why you need to read the docs carefully...
|
||||
(self.xconn.xlib.XFree)(self._buttons.mask as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn query_pointer(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
device_id: c_int,
|
||||
) -> Result<PointerState, XError> {
|
||||
let mut root_return = mem::uninitialized();
|
||||
let mut child_return = mem::uninitialized();
|
||||
let mut root_x_return = mem::uninitialized();
|
||||
let mut root_y_return = mem::uninitialized();
|
||||
let mut win_x_return = mem::uninitialized();
|
||||
let mut win_y_return = mem::uninitialized();
|
||||
let mut buttons_return = mem::uninitialized();
|
||||
let mut modifiers_return = mem::uninitialized();
|
||||
let mut group_return = mem::uninitialized();
|
||||
|
||||
let relative_to_window = (xconn.xinput2.XIQueryPointer)(
|
||||
xconn.display,
|
||||
device_id,
|
||||
window,
|
||||
&mut root_return,
|
||||
&mut child_return,
|
||||
&mut root_x_return,
|
||||
&mut root_y_return,
|
||||
&mut win_x_return,
|
||||
&mut win_y_return,
|
||||
&mut buttons_return,
|
||||
&mut modifiers_return,
|
||||
&mut group_return,
|
||||
) == ffi::True;
|
||||
|
||||
xconn.check_errors()?;
|
||||
|
||||
Ok(PointerState {
|
||||
xconn,
|
||||
_root: root_return,
|
||||
_child: child_return,
|
||||
_root_x: root_x_return,
|
||||
_root_y: root_y_return,
|
||||
_win_x: win_x_return,
|
||||
_win_y: win_y_return,
|
||||
_buttons: buttons_return,
|
||||
modifiers: modifiers_return,
|
||||
_group: group_return,
|
||||
_relative_to_window: relative_to_window,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn lookup_utf8_inner(
|
||||
xconn: &Arc<XConnection>,
|
||||
ic: ffi::XIC,
|
||||
key_event: &mut ffi::XKeyEvent,
|
||||
buffer: &mut [u8],
|
||||
) -> (ffi::KeySym, ffi::Status, c_int) {
|
||||
let mut keysym: ffi::KeySym = 0;
|
||||
let mut status: ffi::Status = 0;
|
||||
let count = (xconn.xlib.Xutf8LookupString)(
|
||||
ic,
|
||||
key_event,
|
||||
buffer.as_mut_ptr() as *mut c_char,
|
||||
buffer.len() as c_int,
|
||||
&mut keysym,
|
||||
&mut status,
|
||||
);
|
||||
(keysym, status, count)
|
||||
}
|
||||
|
||||
// A base buffer size of 1kB uses a negligible amount of RAM while preventing us from having to
|
||||
// re-allocate (and make another round-trip) in the *vast* majority of cases.
|
||||
// To test if lookup_utf8 works correctly, set this to 1.
|
||||
const TEXT_BUFFER_SIZE: usize = 1024;
|
||||
|
||||
pub unsafe fn lookup_utf8(
|
||||
xconn: &Arc<XConnection>,
|
||||
ic: ffi::XIC,
|
||||
key_event: &mut ffi::XKeyEvent,
|
||||
) -> String {
|
||||
let mut buffer: [u8; TEXT_BUFFER_SIZE] = mem::uninitialized();
|
||||
let (_, status, count) = lookup_utf8_inner(
|
||||
xconn,
|
||||
ic,
|
||||
key_event,
|
||||
&mut buffer,
|
||||
);
|
||||
|
||||
// The buffer overflowed, so we'll make a new one on the heap.
|
||||
if status == ffi::XBufferOverflow {
|
||||
let mut buffer = Vec::with_capacity(count as usize);
|
||||
buffer.set_len(count as usize);
|
||||
let (_, _, new_count) = lookup_utf8_inner(
|
||||
xconn,
|
||||
ic,
|
||||
key_event,
|
||||
&mut buffer,
|
||||
);
|
||||
debug_assert_eq!(count, new_count);
|
||||
str::from_utf8(&buffer[..count as usize])
|
||||
.unwrap_or("")
|
||||
.to_string()
|
||||
} else {
|
||||
str::from_utf8(&buffer[..count as usize])
|
||||
.unwrap_or("")
|
||||
.to_string()
|
||||
}
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
// Welcome to the util module, where we try to keep you from shooting yourself in the foot.
|
||||
// *results may vary
|
||||
|
||||
mod atom;
|
||||
mod geometry;
|
||||
mod hint;
|
||||
mod icon;
|
||||
mod input;
|
||||
mod window_property;
|
||||
mod wm;
|
||||
|
||||
pub use self::atom::*;
|
||||
pub use self::geometry::*;
|
||||
pub use self::hint::*;
|
||||
pub use self::icon::*;
|
||||
pub use self::input::*;
|
||||
pub use self::window_property::*;
|
||||
pub use self::wm::*;
|
||||
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
use std::str;
|
||||
use std::sync::Arc;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::raw::*;
|
||||
|
||||
use super::{ffi, XConnection, XError};
|
||||
|
||||
// This isn't actually the number of the bits in the format.
|
||||
// X11 does a match on this value to determine which type to call sizeof on.
|
||||
// Thus, we use 32 for c_long, since 32 maps to c_long which maps to 64.
|
||||
// ...if that sounds confusing, then you know why this enum is here.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Format {
|
||||
Char = 8,
|
||||
Short = 16,
|
||||
Long = 32,
|
||||
}
|
||||
|
||||
impl Format {
|
||||
pub fn from_format(format: usize) -> Option<Self> {
|
||||
match format {
|
||||
8 => Some(Format::Char),
|
||||
16 => Some(Format::Short),
|
||||
32 => Some(Format::Long),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_same_size_as<T>(&self) -> bool {
|
||||
mem::size_of::<T>() == self.get_actual_size()
|
||||
}
|
||||
|
||||
pub fn get_actual_size(&self) -> usize {
|
||||
match self {
|
||||
&Format::Char => mem::size_of::<c_char>(),
|
||||
&Format::Short => mem::size_of::<c_short>(),
|
||||
&Format::Long => mem::size_of::<c_long>(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct XSmartPointer<'a, T> {
|
||||
xconn: &'a Arc<XConnection>,
|
||||
pub ptr: *mut T,
|
||||
}
|
||||
|
||||
impl<'a, T> XSmartPointer<'a, T> {
|
||||
// You're responsible for only passing things to this that should be XFree'd.
|
||||
// Returns None if ptr is null.
|
||||
pub fn new(xconn: &'a Arc<XConnection>, ptr: *mut T) -> Option<Self> {
|
||||
if !ptr.is_null() {
|
||||
Some(XSmartPointer {
|
||||
xconn,
|
||||
ptr,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for XSmartPointer<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for XSmartPointer<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Drop for XSmartPointer<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
(self.xconn.xlib.XFree)(self.ptr as *mut _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is impoartant, so pay attention!
|
||||
// Xlib has an output buffer, and tries to hide the async nature of X from you.
|
||||
// This buffer contains the requests you make, and is flushed under various circumstances:
|
||||
// 1. XPending, XNextEvent, and XWindowEvent flush "as needed"
|
||||
// 2. XFlush explicitly flushes
|
||||
// 3. XSync flushes and blocks until all requests are responded to
|
||||
// 4. Calls that have a return dependent on a response (i.e. XGetWindowProperty) sync internally.
|
||||
// When in doubt, check the X11 source; if a function calls _XReply, it flushes and waits.
|
||||
// All util functions that abstract an async function will return a Flusher.
|
||||
pub unsafe fn flush_requests(xconn: &Arc<XConnection>) -> Result<(), XError> {
|
||||
(xconn.xlib.XFlush)(xconn.display);
|
||||
//println!("XFlush");
|
||||
// This isn't necessarily a useful time to check for errors (since our request hasn't
|
||||
// necessarily been processed yet)
|
||||
xconn.check_errors()
|
||||
}
|
||||
|
||||
pub unsafe fn sync_with_server(xconn: &Arc<XConnection>) -> Result<(), XError> {
|
||||
(xconn.xlib.XSync)(xconn.display, ffi::False);
|
||||
//println!("XSync");
|
||||
xconn.check_errors()
|
||||
}
|
||||
|
||||
#[must_use = "This request was made asynchronously, and is still in the output buffer. You must explicitly choose to either `.flush()` (empty the output buffer, sending the request now) or `.queue()` (wait to send the request, allowing you to continue to add more requests without additional round-trips). For more information, see the documentation for `util::flush_requests`."]
|
||||
pub struct Flusher<'a> {
|
||||
xconn: &'a Arc<XConnection>,
|
||||
}
|
||||
|
||||
impl<'a> Flusher<'a> {
|
||||
pub fn new(xconn: &'a Arc<XConnection>) -> Self {
|
||||
Flusher { xconn }
|
||||
}
|
||||
|
||||
// "I want this request sent now!"
|
||||
pub fn flush(self) -> Result<(), XError> {
|
||||
unsafe { flush_requests(self.xconn) }
|
||||
}
|
||||
|
||||
// "I'm aware that this request hasn't been sent, and I'm okay with waiting."
|
||||
pub fn queue(self) {}
|
||||
}
|
||||
|
||||
pub unsafe fn send_client_msg(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: c_ulong, // The window this is "about"; not necessarily this window
|
||||
target_window: c_ulong, // The window we're sending to
|
||||
message_type: ffi::Atom,
|
||||
event_mask: Option<c_long>,
|
||||
data: (c_long, c_long, c_long, c_long, c_long),
|
||||
) -> Flusher {
|
||||
let mut event: ffi::XClientMessageEvent = mem::uninitialized();
|
||||
event.type_ = ffi::ClientMessage;
|
||||
event.display = xconn.display;
|
||||
event.window = window;
|
||||
event.message_type = message_type;
|
||||
event.format = Format::Long as c_int;
|
||||
event.data = ffi::ClientMessageData::new();
|
||||
event.data.set_long(0, data.0);
|
||||
event.data.set_long(1, data.1);
|
||||
event.data.set_long(2, data.2);
|
||||
event.data.set_long(3, data.3);
|
||||
event.data.set_long(4, data.4);
|
||||
|
||||
let event_mask = event_mask.unwrap_or(ffi::NoEventMask);
|
||||
|
||||
(xconn.xlib.XSendEvent)(
|
||||
xconn.display,
|
||||
target_window,
|
||||
ffi::False,
|
||||
event_mask,
|
||||
&mut event.into(),
|
||||
);
|
||||
|
||||
Flusher::new(xconn)
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
use std;
|
||||
use std::fmt::Debug;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub type Cardinal = c_long;
|
||||
pub const CARDINAL_SIZE: usize = mem::size_of::<c_long>();
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum GetPropertyError {
|
||||
XError(XError),
|
||||
TypeMismatch(ffi::Atom),
|
||||
FormatMismatch(c_int),
|
||||
NothingAllocated,
|
||||
}
|
||||
|
||||
impl GetPropertyError {
|
||||
pub fn is_actual_property_type(&self, t: ffi::Atom) -> bool {
|
||||
if let GetPropertyError::TypeMismatch(actual_type) = *self {
|
||||
actual_type == t
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Number of 32-bit chunks to retrieve per iteration of get_property's inner loop.
|
||||
// To test if get_property works correctly, set this to 1.
|
||||
const PROPERTY_BUFFER_SIZE: c_long = 1024; // 4k of RAM ought to be enough for anyone!
|
||||
|
||||
pub unsafe fn get_property<T: Debug + Clone>(
|
||||
xconn: &Arc<XConnection>,
|
||||
window: c_ulong,
|
||||
property: ffi::Atom,
|
||||
property_type: ffi::Atom,
|
||||
) -> Result<Vec<T>, GetPropertyError> {
|
||||
let mut data = Vec::new();
|
||||
let mut offset = 0;
|
||||
|
||||
let mut done = false;
|
||||
while !done {
|
||||
let mut actual_type: ffi::Atom = mem::uninitialized();
|
||||
let mut actual_format: c_int = mem::uninitialized();
|
||||
let mut quantity_returned: c_ulong = mem::uninitialized();
|
||||
let mut bytes_after: c_ulong = mem::uninitialized();
|
||||
let mut buf: *mut c_uchar = ptr::null_mut();
|
||||
(xconn.xlib.XGetWindowProperty)(
|
||||
xconn.display,
|
||||
window,
|
||||
property,
|
||||
// This offset is in terms of 32-bit chunks.
|
||||
offset,
|
||||
// This is the quanity of 32-bit chunks to receive at once.
|
||||
PROPERTY_BUFFER_SIZE,
|
||||
ffi::False,
|
||||
property_type,
|
||||
&mut actual_type,
|
||||
&mut actual_format,
|
||||
// This is the quantity of items we retrieved in our format, NOT of 32-bit chunks!
|
||||
&mut quantity_returned,
|
||||
// ...and this is a quantity of bytes. So, this function deals in 3 different units.
|
||||
&mut bytes_after,
|
||||
&mut buf,
|
||||
);
|
||||
|
||||
if let Err(e) = xconn.check_errors() {
|
||||
return Err(GetPropertyError::XError(e));
|
||||
}
|
||||
|
||||
if actual_type != property_type {
|
||||
return Err(GetPropertyError::TypeMismatch(actual_type));
|
||||
}
|
||||
|
||||
let format_mismatch = Format::from_format(actual_format as _)
|
||||
.map(|actual_format| !actual_format.is_same_size_as::<T>())
|
||||
// This won't actually be reached; the XError condition above is triggered first.
|
||||
.unwrap_or(true);
|
||||
|
||||
if format_mismatch {
|
||||
return Err(GetPropertyError::FormatMismatch(actual_format));
|
||||
}
|
||||
|
||||
if !buf.is_null() {
|
||||
offset += PROPERTY_BUFFER_SIZE;
|
||||
let new_data = std::slice::from_raw_parts(
|
||||
buf as *mut T,
|
||||
quantity_returned as usize,
|
||||
);
|
||||
/*println!(
|
||||
"XGetWindowProperty prop:{:?} fmt:{:02} len:{:02} off:{:02} out:{:02}, buf:{:?}",
|
||||
property,
|
||||
mem::size_of::<T>() * 8,
|
||||
data.len(),
|
||||
offset,
|
||||
quantity_returned,
|
||||
new_data,
|
||||
);*/
|
||||
data.extend_from_slice(&new_data);
|
||||
// Fun fact: XGetWindowProperty allocates one extra byte at the end.
|
||||
(xconn.xlib.XFree)(buf as _); // Don't try to access new_data after this.
|
||||
} else {
|
||||
return Err(GetPropertyError::NothingAllocated);
|
||||
}
|
||||
|
||||
done = bytes_after == 0;
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PropMode {
|
||||
Replace = ffi::PropModeReplace as isize,
|
||||
_Prepend = ffi::PropModePrepend as isize,
|
||||
_Append = ffi::PropModeAppend as isize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InvalidFormat {
|
||||
format_used: Format,
|
||||
size_passed: usize,
|
||||
size_expected: usize,
|
||||
}
|
||||
|
||||
pub unsafe fn change_property<'a, T: Debug>(
|
||||
xconn: &'a Arc<XConnection>,
|
||||
window: c_ulong,
|
||||
property: ffi::Atom,
|
||||
property_type: ffi::Atom,
|
||||
format: Format,
|
||||
mode: PropMode,
|
||||
new_value: &[T],
|
||||
) -> Flusher<'a> {
|
||||
if !format.is_same_size_as::<T>() {
|
||||
panic!(format!(
|
||||
"[winit developer error] Incorrect usage of `util::change_property`: {:#?}",
|
||||
InvalidFormat {
|
||||
format_used: format,
|
||||
size_passed: mem::size_of::<T>() * 8,
|
||||
size_expected: format.get_actual_size() * 8,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
(xconn.xlib.XChangeProperty)(
|
||||
xconn.display,
|
||||
window,
|
||||
property,
|
||||
property_type,
|
||||
format as c_int,
|
||||
mode as c_int,
|
||||
new_value.as_ptr() as *const c_uchar,
|
||||
new_value.len() as c_int,
|
||||
);
|
||||
|
||||
/*println!(
|
||||
"XChangeProperty prop:{:?} val:{:?}",
|
||||
property,
|
||||
new_value,
|
||||
);*/
|
||||
|
||||
Flusher::new(xconn)
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use super::*;
|
||||
|
||||
// This info is global to the window manager.
|
||||
lazy_static! {
|
||||
static ref SUPPORTED_HINTS: Mutex<Vec<ffi::Atom>> = Mutex::new(Vec::with_capacity(0));
|
||||
static ref WM_NAME: Mutex<Option<String>> = Mutex::new(None);
|
||||
}
|
||||
|
||||
pub fn hint_is_supported(hint: ffi::Atom) -> bool {
|
||||
(*SUPPORTED_HINTS.lock()).contains(&hint)
|
||||
}
|
||||
|
||||
pub fn wm_name_is_one_of(names: &[&str]) -> bool {
|
||||
if let Some(ref name) = *WM_NAME.lock() {
|
||||
names.contains(&name.as_str())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_cached_wm_info(xconn: &Arc<XConnection>, root: ffi::Window) {
|
||||
*SUPPORTED_HINTS.lock() = self::get_supported_hints(xconn, root);
|
||||
*WM_NAME.lock() = self::get_wm_name(xconn, root);
|
||||
}
|
||||
|
||||
fn get_supported_hints(xconn: &Arc<XConnection>, root: ffi::Window) -> Vec<ffi::Atom> {
|
||||
let supported_atom = unsafe { self::get_atom(xconn, b"_NET_SUPPORTED\0") }
|
||||
.expect("Failed to call XInternAtom (_NET_SUPPORTED)");
|
||||
unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
root,
|
||||
supported_atom,
|
||||
ffi::XA_ATOM,
|
||||
)
|
||||
}.unwrap_or_else(|_| Vec::with_capacity(0))
|
||||
}
|
||||
|
||||
fn get_wm_name(xconn: &Arc<XConnection>, root: ffi::Window) -> Option<String> {
|
||||
let check_atom = unsafe { self::get_atom(xconn, b"_NET_SUPPORTING_WM_CHECK\0") }
|
||||
.expect("Failed to call XInternAtom (_NET_SUPPORTING_WM_CHECK)");
|
||||
let wm_name_atom = unsafe { self::get_atom(xconn, b"_NET_WM_NAME\0") }
|
||||
.expect("Failed to call XInternAtom (_NET_WM_NAME)");
|
||||
|
||||
// Mutter/Muffin/Budgie doesn't have _NET_SUPPORTING_WM_CHECK in its _NET_SUPPORTED, despite
|
||||
// it working and being supported. This has been reported upstream, but due to the
|
||||
// inavailability of time machines, we'll just try to get _NET_SUPPORTING_WM_CHECK
|
||||
// regardless of whether or not the WM claims to support it.
|
||||
//
|
||||
// Blackbox 0.70 also incorrectly reports not supporting this, though that appears to be fixed
|
||||
// in 0.72.
|
||||
/*if !supported_hints.contains(&check_atom) {
|
||||
return None;
|
||||
}*/
|
||||
|
||||
// IceWM (1.3.x and earlier) doesn't report supporting _NET_WM_NAME, but will nonetheless
|
||||
// provide us with a value for it. Note that the unofficial 1.4 fork of IceWM works fine.
|
||||
/*if !supported_hints.contains(&wm_name_atom) {
|
||||
return None;
|
||||
}*/
|
||||
|
||||
// Of the WMs tested, only xmonad and dwm fail to provide a WM name.
|
||||
|
||||
// Querying this property on the root window will give us the ID of a child window created by
|
||||
// the WM.
|
||||
let root_window_wm_check = {
|
||||
let result = unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
root,
|
||||
check_atom,
|
||||
ffi::XA_WINDOW,
|
||||
)
|
||||
};
|
||||
|
||||
let wm_check = result
|
||||
.ok()
|
||||
.and_then(|wm_check| wm_check.get(0).cloned());
|
||||
|
||||
if let Some(wm_check) = wm_check {
|
||||
wm_check
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// Querying the same property on the child window we were given, we should get this child
|
||||
// window's ID again.
|
||||
let child_window_wm_check = {
|
||||
let result = unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
root_window_wm_check,
|
||||
check_atom,
|
||||
ffi::XA_WINDOW,
|
||||
)
|
||||
};
|
||||
|
||||
let wm_check = result
|
||||
.ok()
|
||||
.and_then(|wm_check| wm_check.get(0).cloned());
|
||||
|
||||
if let Some(wm_check) = wm_check {
|
||||
wm_check
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
// These values should be the same.
|
||||
if root_window_wm_check != child_window_wm_check {
|
||||
return None;
|
||||
}
|
||||
|
||||
// All of that work gives us a window ID that we can get the WM name from.
|
||||
let wm_name = {
|
||||
let utf8_string_atom = unsafe { self::get_atom(xconn, b"UTF8_STRING\0") }
|
||||
.expect("Failed to call XInternAtom (UTF8_STRING)");
|
||||
|
||||
let result = unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
root_window_wm_check,
|
||||
wm_name_atom,
|
||||
utf8_string_atom,
|
||||
)
|
||||
};
|
||||
|
||||
// IceWM requires this. IceWM was also the only WM tested that returns a null-terminated
|
||||
// string. For more fun trivia, IceWM is also unique in including version and uname
|
||||
// information in this string (this means you'll have to be careful if you want to match
|
||||
// against it, though).
|
||||
// The unofficial 1.4 fork of IceWM still includes the extra details, but properly
|
||||
// returns a UTF8 string that isn't null-terminated.
|
||||
let no_utf8 = if let Err(ref err) = result {
|
||||
err.is_actual_property_type(ffi::XA_STRING)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if no_utf8 {
|
||||
unsafe {
|
||||
self::get_property(
|
||||
xconn,
|
||||
root_window_wm_check,
|
||||
wm_name_atom,
|
||||
ffi::XA_STRING,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}.ok();
|
||||
|
||||
wm_name.and_then(|wm_name| String::from_utf8(wm_name).ok())
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
250
src/platform/macos.rs
Normal file
250
src/platform/macos.rs
Normal file
@@ -0,0 +1,250 @@
|
||||
#![cfg(target_os = "macos")]
|
||||
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use crate::{
|
||||
dpi::LogicalSize,
|
||||
event_loop::{EventLoop, EventLoopWindowTarget},
|
||||
monitor::MonitorHandle,
|
||||
platform_impl::get_aux_state_mut,
|
||||
window::{Window, WindowBuilder},
|
||||
};
|
||||
|
||||
/// 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.
|
||||
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.
|
||||
fn ns_view(&self) -> *mut c_void;
|
||||
|
||||
/// Returns whether or not the window is in simple fullscreen mode.
|
||||
fn simple_fullscreen(&self) -> bool;
|
||||
|
||||
/// Toggles a fullscreen mode that doesn't require a new macOS space.
|
||||
/// Returns a boolean indicating whether the transition was successful (this
|
||||
/// won't work if the window was already in the native fullscreen).
|
||||
///
|
||||
/// This is how fullscreen used to work on macOS in versions before Lion.
|
||||
/// 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);
|
||||
}
|
||||
|
||||
impl WindowExtMacOS for Window {
|
||||
#[inline]
|
||||
fn ns_window(&self) -> *mut c_void {
|
||||
self.window.ns_window()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ns_view(&self) -> *mut c_void {
|
||||
self.window.ns_view()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn simple_fullscreen(&self) -> bool {
|
||||
self.window.simple_fullscreen()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to `NSApplicationActivationPolicy`.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum ActivationPolicy {
|
||||
/// Corresponds to `NSApplicationActivationPolicyRegular`.
|
||||
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.
|
||||
///
|
||||
/// **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`
|
||||
pub trait WindowBuilderExtMacOS {
|
||||
/// 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;
|
||||
/// Makes the titlebar transparent and allows the content to appear behind it.
|
||||
fn with_titlebar_transparent(self, titlebar_transparent: bool) -> WindowBuilder;
|
||||
/// Hides the window title.
|
||||
fn with_title_hidden(self, title_hidden: bool) -> WindowBuilder;
|
||||
/// Hides the window titlebar.
|
||||
fn with_titlebar_hidden(self, titlebar_hidden: bool) -> WindowBuilder;
|
||||
/// Hides the window titlebar buttons.
|
||||
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;
|
||||
}
|
||||
|
||||
impl WindowBuilderExtMacOS for WindowBuilder {
|
||||
#[inline]
|
||||
fn with_movable_by_window_background(
|
||||
mut self,
|
||||
movable_by_window_background: bool,
|
||||
) -> WindowBuilder {
|
||||
self.platform_specific.movable_by_window_background = movable_by_window_background;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_titlebar_transparent(mut self, titlebar_transparent: bool) -> WindowBuilder {
|
||||
self.platform_specific.titlebar_transparent = titlebar_transparent;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_titlebar_hidden(mut self, titlebar_hidden: bool) -> WindowBuilder {
|
||||
self.platform_specific.titlebar_hidden = titlebar_hidden;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_titlebar_buttons_hidden(mut self, titlebar_buttons_hidden: bool) -> WindowBuilder {
|
||||
self.platform_specific.titlebar_buttons_hidden = titlebar_buttons_hidden;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_title_hidden(mut self, title_hidden: bool) -> WindowBuilder {
|
||||
self.platform_specific.title_hidden = title_hidden;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_fullsize_content_view(mut self, fullsize_content_view: bool) -> WindowBuilder {
|
||||
self.platform_specific.fullsize_content_view = fullsize_content_view;
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EventLoopExtMacOS {
|
||||
/// Sets the activation policy for the application. It is set to
|
||||
/// `NSApplicationActivationPolicyRegular` by default.
|
||||
///
|
||||
/// This function only takes effect if it's called before calling [`run`](crate::event_loop::EventLoop::run) or
|
||||
/// [`run_return`](crate::platform::run_return::EventLoopExtRunReturn::run_return)
|
||||
fn set_activation_policy(&mut self, activation_policy: ActivationPolicy);
|
||||
|
||||
/// Used to prevent a default menubar menu from getting created
|
||||
///
|
||||
/// The default menu creation is enabled by default.
|
||||
///
|
||||
/// This function only takes effect if it's called before calling
|
||||
/// [`run`](crate::event_loop::EventLoop::run) or
|
||||
/// [`run_return`](crate::platform::run_return::EventLoopExtRunReturn::run_return)
|
||||
fn enable_default_menu_creation(&mut self, enable: bool);
|
||||
}
|
||||
impl<T> EventLoopExtMacOS for EventLoop<T> {
|
||||
#[inline]
|
||||
fn set_activation_policy(&mut self, activation_policy: ActivationPolicy) {
|
||||
unsafe {
|
||||
get_aux_state_mut(&**self.event_loop.delegate).activation_policy = activation_policy;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn enable_default_menu_creation(&mut self, enable: bool) {
|
||||
unsafe {
|
||||
get_aux_state_mut(&**self.event_loop.delegate).create_default_menu = enable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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;
|
||||
/// Returns a pointer to the NSScreen representing this monitor.
|
||||
fn ns_screen(&self) -> Option<*mut c_void>;
|
||||
}
|
||||
|
||||
impl MonitorHandleExtMacOS for MonitorHandle {
|
||||
#[inline]
|
||||
fn native_id(&self) -> u32 {
|
||||
self.inner.native_identifier()
|
||||
}
|
||||
|
||||
fn ns_screen(&self) -> Option<*mut c_void> {
|
||||
self.inner.ns_screen().map(|s| s as *mut c_void)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
self.p.hide_application()
|
||||
}
|
||||
|
||||
fn hide_other_applications(&self) {
|
||||
self.p.hide_other_applications()
|
||||
}
|
||||
}
|
||||
@@ -1,774 +0,0 @@
|
||||
use {ControlFlow, EventsLoopClosed};
|
||||
use cocoa::{self, appkit, foundation};
|
||||
use cocoa::appkit::{NSApplication, NSEvent, NSEventMask, NSEventModifierFlags, NSEventPhase, NSView, NSWindow};
|
||||
use events::{self, ElementState, Event, MouseButton, TouchPhase, WindowEvent, DeviceEvent, ModifiersState, KeyboardInput};
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::{Arc, Mutex, Weak};
|
||||
use super::window::Window2;
|
||||
use std;
|
||||
use super::DeviceId;
|
||||
|
||||
|
||||
pub struct EventsLoop {
|
||||
modifiers: Modifiers,
|
||||
pub shared: Arc<Shared>,
|
||||
}
|
||||
|
||||
// State shared between the `EventsLoop` and its registered windows.
|
||||
pub struct Shared {
|
||||
pub windows: Mutex<Vec<Weak<Window2>>>,
|
||||
pub pending_events: Mutex<VecDeque<Event>>,
|
||||
// The user event callback given via either of the `poll_events` or `run_forever` methods.
|
||||
//
|
||||
// We store the user's callback here so that it may be accessed by each of the window delegate
|
||||
// callbacks (e.g. resize, close, etc) for the duration of a call to either of the
|
||||
// `poll_events` or `run_forever` methods.
|
||||
//
|
||||
// This is *only* `Some` for the duration of a call to either of these methods and will be
|
||||
// `None` otherwise.
|
||||
user_callback: UserCallback,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Proxy {}
|
||||
|
||||
struct Modifiers {
|
||||
shift_pressed: bool,
|
||||
ctrl_pressed: bool,
|
||||
win_pressed: bool,
|
||||
alt_pressed: bool,
|
||||
}
|
||||
|
||||
// Wrapping the user callback in a type allows us to:
|
||||
//
|
||||
// - ensure the callback pointer is never accidentally cloned
|
||||
// - ensure that only the `EventsLoop` can `store` and `drop` the callback pointer
|
||||
// - Share access to the user callback with the NSWindow callbacks.
|
||||
pub struct UserCallback {
|
||||
mutex: Mutex<Option<*mut FnMut(Event)>>,
|
||||
}
|
||||
|
||||
|
||||
impl Shared {
|
||||
|
||||
pub fn new() -> Self {
|
||||
Shared {
|
||||
windows: Mutex::new(Vec::new()),
|
||||
pending_events: Mutex::new(VecDeque::new()),
|
||||
user_callback: UserCallback { mutex: Mutex::new(None) },
|
||||
}
|
||||
}
|
||||
|
||||
fn call_user_callback_with_pending_events(&self) {
|
||||
loop {
|
||||
let event = match self.pending_events.lock().unwrap().pop_front() {
|
||||
Some(event) => event,
|
||||
None => return,
|
||||
};
|
||||
unsafe {
|
||||
self.user_callback.call_with_event(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calls the user callback if one exists.
|
||||
//
|
||||
// Otherwise, stores the event in the `pending_events` queue.
|
||||
//
|
||||
// This is necessary for the case when `WindowDelegate` callbacks are triggered during a call
|
||||
// to the user's callback.
|
||||
pub fn call_user_callback_with_event_or_store_in_pending(&self, event: Event) {
|
||||
if self.user_callback.mutex.lock().unwrap().is_some() {
|
||||
unsafe {
|
||||
self.user_callback.call_with_event(event);
|
||||
}
|
||||
} else {
|
||||
self.pending_events.lock().unwrap().push_back(event);
|
||||
}
|
||||
}
|
||||
|
||||
// Removes the window with the given `Id` from the `windows` list.
|
||||
//
|
||||
// This is called in response to `windowWillClose`.
|
||||
pub fn find_and_remove_window(&self, id: super::window::Id) {
|
||||
if let Ok(mut windows) = self.windows.lock() {
|
||||
windows.retain(|w| match w.upgrade() {
|
||||
Some(w) => w.id() != id,
|
||||
None => false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
impl Modifiers {
|
||||
pub fn new() -> Self {
|
||||
Modifiers {
|
||||
shift_pressed: false,
|
||||
ctrl_pressed: false,
|
||||
win_pressed: false,
|
||||
alt_pressed: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl UserCallback {
|
||||
|
||||
// Here we store user's `callback` behind the mutex so that they may be safely shared between
|
||||
// each of the window delegates.
|
||||
//
|
||||
// In order to make sure that the pointer is always valid, we must manually guarantee that it
|
||||
// is dropped before the callback itself is dropped. Thus, this should *only* be called at the
|
||||
// beginning of a call to `poll_events` and `run_forever`, both of which *must* drop the
|
||||
// callback at the end of their scope using the `drop` method.
|
||||
fn store<F>(&self, callback: &mut F)
|
||||
where F: FnMut(Event)
|
||||
{
|
||||
let trait_object = callback as &mut FnMut(Event);
|
||||
let trait_object_ptr = trait_object as *const FnMut(Event) as *mut FnMut(Event);
|
||||
*self.mutex.lock().unwrap() = Some(trait_object_ptr);
|
||||
}
|
||||
|
||||
// Emits the given event via the user-given callback.
|
||||
//
|
||||
// This is unsafe as it requires dereferencing the pointer to the user-given callback. We
|
||||
// guarantee this is safe by ensuring the `UserCallback` never lives longer than the user-given
|
||||
// callback.
|
||||
//
|
||||
// Note that the callback may not always be `Some`. This is because some `NSWindowDelegate`
|
||||
// callbacks can be triggered by means other than `NSApp().sendEvent`. For example, if a window
|
||||
// is destroyed or created during a call to the user's callback, the `WindowDelegate` methods
|
||||
// may be called with `windowShouldClose` or `windowDidResignKey`.
|
||||
unsafe fn call_with_event(&self, event: Event) {
|
||||
let callback = match self.mutex.lock().unwrap().take() {
|
||||
Some(callback) => callback,
|
||||
None => return,
|
||||
};
|
||||
(*callback)(event);
|
||||
*self.mutex.lock().unwrap() = Some(callback);
|
||||
}
|
||||
|
||||
// Used to drop the user callback pointer at the end of the `poll_events` and `run_forever`
|
||||
// methods. This is done to enforce our guarantee that the top callback will never live longer
|
||||
// than the call to either `poll_events` or `run_forever` to which it was given.
|
||||
fn drop(&self) {
|
||||
self.mutex.lock().unwrap().take();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
impl EventsLoop {
|
||||
|
||||
pub fn new() -> Self {
|
||||
// Mark this thread as the main thread of the Cocoa event system.
|
||||
//
|
||||
// This must be done before any worker threads get a chance to call it
|
||||
// (e.g., via `EventsLoopProxy::wakeup()`), causing a wrong thread to be
|
||||
// marked as the main thread.
|
||||
unsafe { appkit::NSApp(); }
|
||||
|
||||
EventsLoop {
|
||||
shared: Arc::new(Shared::new()),
|
||||
modifiers: Modifiers::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll_events<F>(&mut self, mut callback: F)
|
||||
where F: FnMut(Event),
|
||||
{
|
||||
unsafe {
|
||||
if !msg_send![cocoa::base::class("NSThread"), isMainThread] {
|
||||
panic!("Events can only be polled from the main thread on macOS");
|
||||
}
|
||||
}
|
||||
|
||||
self.shared.user_callback.store(&mut callback);
|
||||
|
||||
// Loop as long as we have pending events to return.
|
||||
loop {
|
||||
unsafe {
|
||||
// First, yield all pending events.
|
||||
self.shared.call_user_callback_with_pending_events();
|
||||
|
||||
let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil);
|
||||
|
||||
// Poll for the next event, returning `nil` if there are none.
|
||||
let ns_event = appkit::NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_(
|
||||
NSEventMask::NSAnyEventMask.bits() | NSEventMask::NSEventMaskPressure.bits(),
|
||||
foundation::NSDate::distantPast(cocoa::base::nil),
|
||||
foundation::NSDefaultRunLoopMode,
|
||||
cocoa::base::YES);
|
||||
|
||||
let event = self.ns_event_to_event(ns_event);
|
||||
|
||||
let _: () = msg_send![pool, release];
|
||||
|
||||
match event {
|
||||
// Call the user's callback.
|
||||
Some(event) => self.shared.user_callback.call_with_event(event),
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.shared.user_callback.drop();
|
||||
}
|
||||
|
||||
pub fn run_forever<F>(&mut self, mut callback: F)
|
||||
where F: FnMut(Event) -> ControlFlow
|
||||
{
|
||||
unsafe {
|
||||
if !msg_send![cocoa::base::class("NSThread"), isMainThread] {
|
||||
panic!("Events can only be polled from the main thread on macOS");
|
||||
}
|
||||
}
|
||||
|
||||
// Track whether or not control flow has changed.
|
||||
let control_flow = std::cell::Cell::new(ControlFlow::Continue);
|
||||
|
||||
let mut callback = |event| {
|
||||
if let ControlFlow::Break = callback(event) {
|
||||
control_flow.set(ControlFlow::Break);
|
||||
}
|
||||
};
|
||||
|
||||
self.shared.user_callback.store(&mut callback);
|
||||
|
||||
loop {
|
||||
unsafe {
|
||||
// First, yield all pending events.
|
||||
self.shared.call_user_callback_with_pending_events();
|
||||
if let ControlFlow::Break = control_flow.get() {
|
||||
break;
|
||||
}
|
||||
|
||||
let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil);
|
||||
|
||||
// Wait for the next event. Note that this function blocks during resize.
|
||||
let ns_event = appkit::NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_(
|
||||
NSEventMask::NSAnyEventMask.bits() | NSEventMask::NSEventMaskPressure.bits(),
|
||||
foundation::NSDate::distantFuture(cocoa::base::nil),
|
||||
foundation::NSDefaultRunLoopMode,
|
||||
cocoa::base::YES);
|
||||
|
||||
let maybe_event = self.ns_event_to_event(ns_event);
|
||||
|
||||
// Release the pool before calling the top callback in case the user calls either
|
||||
// `run_forever` or `poll_events` within the callback.
|
||||
let _: () = msg_send![pool, release];
|
||||
|
||||
if let Some(event) = maybe_event {
|
||||
self.shared.user_callback.call_with_event(event);
|
||||
if let ControlFlow::Break = control_flow.get() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.shared.user_callback.drop();
|
||||
}
|
||||
|
||||
// Convert some given `NSEvent` into a winit `Event`.
|
||||
unsafe fn ns_event_to_event(&mut self, ns_event: cocoa::base::id) -> Option<Event> {
|
||||
if ns_event == cocoa::base::nil {
|
||||
return None;
|
||||
}
|
||||
|
||||
// FIXME: Despite not being documented anywhere, an `NSEvent` is produced when a user opens
|
||||
// Spotlight while the NSApplication is in focus. This `NSEvent` produces a `NSEventType`
|
||||
// with value `21`. This causes a SEGFAULT as soon as we try to match on the `NSEventType`
|
||||
// enum as there is no variant associated with the value. Thus, we return early if this
|
||||
// sneaky event occurs. If someone does find some documentation on this, please fix this by
|
||||
// adding an appropriate variant to the `NSEventType` enum in the cocoa-rs crate.
|
||||
if ns_event.eventType() as u64 == 21 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let event_type = ns_event.eventType();
|
||||
let ns_window = ns_event.window();
|
||||
let window_id = super::window::get_window_id(ns_window);
|
||||
|
||||
// FIXME: Document this. Why do we do this? Seems like it passes on events to window/app.
|
||||
// If we don't do this, window does not become main for some reason.
|
||||
match event_type {
|
||||
appkit::NSKeyDown => (),
|
||||
_ => appkit::NSApp().sendEvent_(ns_event),
|
||||
}
|
||||
|
||||
let windows = self.shared.windows.lock().unwrap();
|
||||
let maybe_window = windows.iter()
|
||||
.filter_map(Weak::upgrade)
|
||||
.find(|window| window_id == window.id());
|
||||
|
||||
let into_event = |window_event| Event::WindowEvent {
|
||||
window_id: ::WindowId(window_id),
|
||||
event: window_event,
|
||||
};
|
||||
|
||||
// Returns `Some` window if one of our windows is the key window.
|
||||
let maybe_key_window = || windows.iter()
|
||||
.filter_map(Weak::upgrade)
|
||||
.find(|window| {
|
||||
let is_key_window: cocoa::base::BOOL = msg_send![*window.window, isKeyWindow];
|
||||
is_key_window == cocoa::base::YES
|
||||
});
|
||||
|
||||
match event_type {
|
||||
|
||||
appkit::NSKeyDown => {
|
||||
let mut events = std::collections::VecDeque::new();
|
||||
let received_c_str = foundation::NSString::UTF8String(ns_event.characters());
|
||||
let received_str = std::ffi::CStr::from_ptr(received_c_str);
|
||||
|
||||
let vkey = to_virtual_key_code(NSEvent::keyCode(ns_event));
|
||||
let state = ElementState::Pressed;
|
||||
let code = NSEvent::keyCode(ns_event) as u32;
|
||||
let window_event = WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: code,
|
||||
virtual_keycode: vkey,
|
||||
modifiers: event_mods(ns_event),
|
||||
},
|
||||
};
|
||||
for received_char in std::str::from_utf8(received_str.to_bytes()).unwrap().chars() {
|
||||
let window_event = WindowEvent::ReceivedCharacter(received_char);
|
||||
events.push_back(into_event(window_event));
|
||||
}
|
||||
self.shared.pending_events.lock().unwrap().extend(events.into_iter());
|
||||
Some(into_event(window_event))
|
||||
},
|
||||
|
||||
appkit::NSKeyUp => {
|
||||
let vkey = to_virtual_key_code(NSEvent::keyCode(ns_event));
|
||||
|
||||
let state = ElementState::Released;
|
||||
let code = NSEvent::keyCode(ns_event) as u32;
|
||||
let window_event = WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: code,
|
||||
virtual_keycode: vkey,
|
||||
modifiers: event_mods(ns_event),
|
||||
},
|
||||
};
|
||||
Some(into_event(window_event))
|
||||
},
|
||||
|
||||
appkit::NSFlagsChanged => {
|
||||
unsafe fn modifier_event(event: cocoa::base::id,
|
||||
keymask: NSEventModifierFlags,
|
||||
key: events::VirtualKeyCode,
|
||||
key_pressed: bool) -> Option<WindowEvent>
|
||||
{
|
||||
if !key_pressed && NSEvent::modifierFlags(event).contains(keymask) {
|
||||
let state = ElementState::Pressed;
|
||||
let code = NSEvent::keyCode(event) as u32;
|
||||
let window_event = WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: code,
|
||||
virtual_keycode: Some(key),
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
};
|
||||
Some(window_event)
|
||||
|
||||
} else if key_pressed && !NSEvent::modifierFlags(event).contains(keymask) {
|
||||
let state = ElementState::Released;
|
||||
let code = NSEvent::keyCode(event) as u32;
|
||||
let window_event = WindowEvent::KeyboardInput {
|
||||
device_id: DEVICE_ID,
|
||||
input: KeyboardInput {
|
||||
state: state,
|
||||
scancode: code,
|
||||
virtual_keycode: Some(key),
|
||||
modifiers: event_mods(event),
|
||||
},
|
||||
};
|
||||
Some(window_event)
|
||||
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
let mut events = std::collections::VecDeque::new();
|
||||
if let Some(window_event) = modifier_event(ns_event,
|
||||
NSEventModifierFlags::NSShiftKeyMask,
|
||||
events::VirtualKeyCode::LShift,
|
||||
self.modifiers.shift_pressed)
|
||||
{
|
||||
self.modifiers.shift_pressed = !self.modifiers.shift_pressed;
|
||||
events.push_back(into_event(window_event));
|
||||
}
|
||||
|
||||
if let Some(window_event) = modifier_event(ns_event,
|
||||
NSEventModifierFlags::NSControlKeyMask,
|
||||
events::VirtualKeyCode::LControl,
|
||||
self.modifiers.ctrl_pressed)
|
||||
{
|
||||
self.modifiers.ctrl_pressed = !self.modifiers.ctrl_pressed;
|
||||
events.push_back(into_event(window_event));
|
||||
}
|
||||
|
||||
if let Some(window_event) = modifier_event(ns_event,
|
||||
NSEventModifierFlags::NSCommandKeyMask,
|
||||
events::VirtualKeyCode::LWin,
|
||||
self.modifiers.win_pressed)
|
||||
{
|
||||
self.modifiers.win_pressed = !self.modifiers.win_pressed;
|
||||
events.push_back(into_event(window_event));
|
||||
}
|
||||
|
||||
if let Some(window_event) = modifier_event(ns_event,
|
||||
NSEventModifierFlags::NSAlternateKeyMask,
|
||||
events::VirtualKeyCode::LAlt,
|
||||
self.modifiers.alt_pressed)
|
||||
{
|
||||
self.modifiers.alt_pressed = !self.modifiers.alt_pressed;
|
||||
events.push_back(into_event(window_event));
|
||||
}
|
||||
|
||||
let event = events.pop_front();
|
||||
self.shared.pending_events.lock().unwrap().extend(events.into_iter());
|
||||
event
|
||||
},
|
||||
|
||||
appkit::NSLeftMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Left, modifiers: event_mods(ns_event) })) },
|
||||
appkit::NSLeftMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Left, modifiers: event_mods(ns_event) })) },
|
||||
appkit::NSRightMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Right, modifiers: event_mods(ns_event) })) },
|
||||
appkit::NSRightMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Right, modifiers: event_mods(ns_event) })) },
|
||||
appkit::NSOtherMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Middle, modifiers: event_mods(ns_event) })) },
|
||||
appkit::NSOtherMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Middle, modifiers: event_mods(ns_event) })) },
|
||||
|
||||
appkit::NSMouseEntered => {
|
||||
let window = match maybe_window.or_else(maybe_key_window) {
|
||||
Some(window) => window,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let window_point = ns_event.locationInWindow();
|
||||
let view_point = if ns_window == cocoa::base::nil {
|
||||
let ns_size = foundation::NSSize::new(0.0, 0.0);
|
||||
let ns_rect = foundation::NSRect::new(window_point, ns_size);
|
||||
let window_rect = window.window.convertRectFromScreen_(ns_rect);
|
||||
window.view.convertPoint_fromView_(window_rect.origin, cocoa::base::nil)
|
||||
} else {
|
||||
window.view.convertPoint_fromView_(window_point, cocoa::base::nil)
|
||||
};
|
||||
let view_rect = NSView::frame(*window.view);
|
||||
let scale_factor = window.hidpi_factor();
|
||||
|
||||
let x = (scale_factor * view_point.x as f32) as f64;
|
||||
let y = (scale_factor * (view_rect.size.height - view_point.y) as f32) as f64;
|
||||
let window_event = WindowEvent::CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event_mods(ns_event) };
|
||||
let event = Event::WindowEvent { window_id: ::WindowId(window.id()), event: window_event };
|
||||
|
||||
self.shared.pending_events.lock().unwrap().push_back(event);
|
||||
Some(into_event(WindowEvent::CursorEntered { device_id: DEVICE_ID }))
|
||||
},
|
||||
appkit::NSMouseExited => { Some(into_event(WindowEvent::CursorLeft { device_id: DEVICE_ID })) },
|
||||
|
||||
appkit::NSMouseMoved |
|
||||
appkit::NSLeftMouseDragged |
|
||||
appkit::NSOtherMouseDragged |
|
||||
appkit::NSRightMouseDragged => {
|
||||
// If the mouse movement was on one of our windows, use it.
|
||||
// Otherwise, if one of our windows is the key window (receiving input), use it.
|
||||
// Otherwise, return `None`.
|
||||
let window = match maybe_window.or_else(maybe_key_window) {
|
||||
Some(window) => window,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let window_point = ns_event.locationInWindow();
|
||||
let view_point = if ns_window == cocoa::base::nil {
|
||||
let ns_size = foundation::NSSize::new(0.0, 0.0);
|
||||
let ns_rect = foundation::NSRect::new(window_point, ns_size);
|
||||
let window_rect = window.window.convertRectFromScreen_(ns_rect);
|
||||
window.view.convertPoint_fromView_(window_rect.origin, cocoa::base::nil)
|
||||
} else {
|
||||
window.view.convertPoint_fromView_(window_point, cocoa::base::nil)
|
||||
};
|
||||
let view_rect = NSView::frame(*window.view);
|
||||
let scale_factor = window.hidpi_factor();
|
||||
|
||||
let mut events = std::collections::VecDeque::new();
|
||||
|
||||
{
|
||||
let x = (scale_factor * view_point.x as f32) as f64;
|
||||
let y = (scale_factor * (view_rect.size.height - view_point.y) as f32) as f64;
|
||||
let window_event = WindowEvent::CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event_mods(ns_event) };
|
||||
let event = Event::WindowEvent { window_id: ::WindowId(window.id()), event: window_event };
|
||||
events.push_back(event);
|
||||
}
|
||||
|
||||
let delta_x = (scale_factor * ns_event.deltaX() as f32) as f64;
|
||||
if delta_x != 0.0 {
|
||||
let motion_event = DeviceEvent::Motion { axis: 0, value: delta_x };
|
||||
let event = Event::DeviceEvent{ device_id: DEVICE_ID, event: motion_event };
|
||||
events.push_back(event);
|
||||
}
|
||||
|
||||
let delta_y = (scale_factor * ns_event.deltaY() as f32) as f64;
|
||||
if delta_y != 0.0 {
|
||||
let motion_event = DeviceEvent::Motion { axis: 1, value: delta_y };
|
||||
let event = Event::DeviceEvent{ device_id: DEVICE_ID, event: motion_event };
|
||||
events.push_back(event);
|
||||
}
|
||||
|
||||
if delta_x != 0.0 || delta_y != 0.0 {
|
||||
let motion_event = DeviceEvent::MouseMotion { delta: (delta_x, delta_y) };
|
||||
let event = Event::DeviceEvent{ device_id: DEVICE_ID, event: motion_event };
|
||||
events.push_back(event);
|
||||
}
|
||||
|
||||
let event = events.pop_front();
|
||||
self.shared.pending_events.lock().unwrap().extend(events.into_iter());
|
||||
event
|
||||
},
|
||||
|
||||
appkit::NSScrollWheel => {
|
||||
// If none of the windows received the scroll, return `None`.
|
||||
let window = match maybe_window {
|
||||
Some(window) => window,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
use events::MouseScrollDelta::{LineDelta, PixelDelta};
|
||||
let scale_factor = window.hidpi_factor();
|
||||
let delta = if ns_event.hasPreciseScrollingDeltas() == cocoa::base::YES {
|
||||
PixelDelta(scale_factor * ns_event.scrollingDeltaX() as f32,
|
||||
scale_factor * ns_event.scrollingDeltaY() as f32)
|
||||
} else {
|
||||
LineDelta(scale_factor * ns_event.scrollingDeltaX() as f32,
|
||||
scale_factor * ns_event.scrollingDeltaY() as f32)
|
||||
};
|
||||
let phase = match ns_event.phase() {
|
||||
NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => TouchPhase::Started,
|
||||
NSEventPhase::NSEventPhaseEnded => TouchPhase::Ended,
|
||||
_ => TouchPhase::Moved,
|
||||
};
|
||||
self.shared.pending_events.lock().unwrap().push_back(Event::DeviceEvent {
|
||||
device_id: DEVICE_ID,
|
||||
event: DeviceEvent::MouseWheel {
|
||||
delta: if ns_event.hasPreciseScrollingDeltas() == cocoa::base::YES {
|
||||
PixelDelta(ns_event.scrollingDeltaX() as f32,
|
||||
ns_event.scrollingDeltaY() as f32)
|
||||
} else {
|
||||
LineDelta(ns_event.scrollingDeltaX() as f32,
|
||||
ns_event.scrollingDeltaY() as f32)
|
||||
},
|
||||
}
|
||||
});
|
||||
let window_event = WindowEvent::MouseWheel { device_id: DEVICE_ID, delta: delta, phase: phase, modifiers: event_mods(ns_event) };
|
||||
Some(into_event(window_event))
|
||||
},
|
||||
|
||||
appkit::NSEventTypePressure => {
|
||||
let pressure = ns_event.pressure();
|
||||
let stage = ns_event.stage();
|
||||
let window_event = WindowEvent::TouchpadPressure { device_id: DEVICE_ID, pressure: pressure, stage: stage };
|
||||
Some(into_event(window_event))
|
||||
},
|
||||
|
||||
appkit::NSApplicationDefined => match ns_event.subtype() {
|
||||
appkit::NSEventSubtype::NSApplicationActivatedEventType => {
|
||||
Some(Event::Awakened)
|
||||
},
|
||||
_ => None,
|
||||
},
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_proxy(&self) -> Proxy {
|
||||
Proxy {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Proxy {
|
||||
pub fn wakeup(&self) -> Result<(), EventsLoopClosed> {
|
||||
// Awaken the event loop by triggering `NSApplicationActivatedEventType`.
|
||||
unsafe {
|
||||
let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil);
|
||||
let event =
|
||||
NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(
|
||||
cocoa::base::nil,
|
||||
appkit::NSApplicationDefined,
|
||||
foundation::NSPoint::new(0.0, 0.0),
|
||||
appkit::NSEventModifierFlags::empty(),
|
||||
0.0,
|
||||
0,
|
||||
cocoa::base::nil,
|
||||
appkit::NSEventSubtype::NSApplicationActivatedEventType,
|
||||
0,
|
||||
0);
|
||||
appkit::NSApp().postEvent_atStart_(event, cocoa::base::NO);
|
||||
foundation::NSAutoreleasePool::drain(pool);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn to_virtual_key_code(code: u16) -> Option<events::VirtualKeyCode> {
|
||||
Some(match code {
|
||||
0x00 => events::VirtualKeyCode::A,
|
||||
0x01 => events::VirtualKeyCode::S,
|
||||
0x02 => events::VirtualKeyCode::D,
|
||||
0x03 => events::VirtualKeyCode::F,
|
||||
0x04 => events::VirtualKeyCode::H,
|
||||
0x05 => events::VirtualKeyCode::G,
|
||||
0x06 => events::VirtualKeyCode::Z,
|
||||
0x07 => events::VirtualKeyCode::X,
|
||||
0x08 => events::VirtualKeyCode::C,
|
||||
0x09 => events::VirtualKeyCode::V,
|
||||
//0x0a => World 1,
|
||||
0x0b => events::VirtualKeyCode::B,
|
||||
0x0c => events::VirtualKeyCode::Q,
|
||||
0x0d => events::VirtualKeyCode::W,
|
||||
0x0e => events::VirtualKeyCode::E,
|
||||
0x0f => events::VirtualKeyCode::R,
|
||||
0x10 => events::VirtualKeyCode::Y,
|
||||
0x11 => events::VirtualKeyCode::T,
|
||||
0x12 => events::VirtualKeyCode::Key1,
|
||||
0x13 => events::VirtualKeyCode::Key2,
|
||||
0x14 => events::VirtualKeyCode::Key3,
|
||||
0x15 => events::VirtualKeyCode::Key4,
|
||||
0x16 => events::VirtualKeyCode::Key6,
|
||||
0x17 => events::VirtualKeyCode::Key5,
|
||||
0x18 => events::VirtualKeyCode::Equals,
|
||||
0x19 => events::VirtualKeyCode::Key9,
|
||||
0x1a => events::VirtualKeyCode::Key7,
|
||||
0x1b => events::VirtualKeyCode::Minus,
|
||||
0x1c => events::VirtualKeyCode::Key8,
|
||||
0x1d => events::VirtualKeyCode::Key0,
|
||||
0x1e => events::VirtualKeyCode::RBracket,
|
||||
0x1f => events::VirtualKeyCode::O,
|
||||
0x20 => events::VirtualKeyCode::U,
|
||||
0x21 => events::VirtualKeyCode::LBracket,
|
||||
0x22 => events::VirtualKeyCode::I,
|
||||
0x23 => events::VirtualKeyCode::P,
|
||||
0x24 => events::VirtualKeyCode::Return,
|
||||
0x25 => events::VirtualKeyCode::L,
|
||||
0x26 => events::VirtualKeyCode::J,
|
||||
0x27 => events::VirtualKeyCode::Apostrophe,
|
||||
0x28 => events::VirtualKeyCode::K,
|
||||
0x29 => events::VirtualKeyCode::Semicolon,
|
||||
0x2a => events::VirtualKeyCode::Backslash,
|
||||
0x2b => events::VirtualKeyCode::Comma,
|
||||
0x2c => events::VirtualKeyCode::Slash,
|
||||
0x2d => events::VirtualKeyCode::N,
|
||||
0x2e => events::VirtualKeyCode::M,
|
||||
0x2f => events::VirtualKeyCode::Period,
|
||||
0x30 => events::VirtualKeyCode::Tab,
|
||||
0x31 => events::VirtualKeyCode::Space,
|
||||
0x32 => events::VirtualKeyCode::Grave,
|
||||
0x33 => events::VirtualKeyCode::Back,
|
||||
//0x34 => unkown,
|
||||
0x35 => events::VirtualKeyCode::Escape,
|
||||
0x36 => events::VirtualKeyCode::RWin,
|
||||
0x37 => events::VirtualKeyCode::LWin,
|
||||
0x38 => events::VirtualKeyCode::LShift,
|
||||
//0x39 => Caps lock,
|
||||
//0x3a => Left alt,
|
||||
0x3b => events::VirtualKeyCode::LControl,
|
||||
0x3c => events::VirtualKeyCode::RShift,
|
||||
//0x3d => Right alt,
|
||||
0x3e => events::VirtualKeyCode::RControl,
|
||||
//0x3f => Fn key,
|
||||
//0x40 => F17 Key,
|
||||
0x41 => events::VirtualKeyCode::Decimal,
|
||||
//0x42 -> unkown,
|
||||
0x43 => events::VirtualKeyCode::Multiply,
|
||||
//0x44 => unkown,
|
||||
0x45 => events::VirtualKeyCode::Add,
|
||||
//0x46 => unkown,
|
||||
0x47 => events::VirtualKeyCode::Numlock,
|
||||
//0x48 => KeypadClear,
|
||||
0x49 => events::VirtualKeyCode::VolumeUp,
|
||||
0x4a => events::VirtualKeyCode::VolumeDown,
|
||||
0x4b => events::VirtualKeyCode::Divide,
|
||||
0x4c => events::VirtualKeyCode::NumpadEnter,
|
||||
//0x4d => unkown,
|
||||
0x4e => events::VirtualKeyCode::Subtract,
|
||||
//0x4f => F18 key,
|
||||
//0x50 => F19 Key,
|
||||
0x51 => events::VirtualKeyCode::NumpadEquals,
|
||||
0x52 => events::VirtualKeyCode::Numpad0,
|
||||
0x53 => events::VirtualKeyCode::Numpad1,
|
||||
0x54 => events::VirtualKeyCode::Numpad2,
|
||||
0x55 => events::VirtualKeyCode::Numpad3,
|
||||
0x56 => events::VirtualKeyCode::Numpad4,
|
||||
0x57 => events::VirtualKeyCode::Numpad5,
|
||||
0x58 => events::VirtualKeyCode::Numpad6,
|
||||
0x59 => events::VirtualKeyCode::Numpad7,
|
||||
//0x5a => F20 Key,
|
||||
0x5b => events::VirtualKeyCode::Numpad8,
|
||||
0x5c => events::VirtualKeyCode::Numpad9,
|
||||
//0x5d => unkown,
|
||||
//0x5e => unkown,
|
||||
//0x5f => unkown,
|
||||
0x60 => events::VirtualKeyCode::F5,
|
||||
0x61 => events::VirtualKeyCode::F6,
|
||||
0x62 => events::VirtualKeyCode::F7,
|
||||
0x63 => events::VirtualKeyCode::F3,
|
||||
0x64 => events::VirtualKeyCode::F8,
|
||||
0x65 => events::VirtualKeyCode::F9,
|
||||
//0x66 => unkown,
|
||||
0x67 => events::VirtualKeyCode::F11,
|
||||
//0x68 => unkown,
|
||||
0x69 => events::VirtualKeyCode::F13,
|
||||
//0x6a => F16 Key,
|
||||
0x6b => events::VirtualKeyCode::F14,
|
||||
//0x6c => unkown,
|
||||
0x6d => events::VirtualKeyCode::F10,
|
||||
//0x6e => unkown,
|
||||
0x6f => events::VirtualKeyCode::F12,
|
||||
//0x70 => unkown,
|
||||
0x71 => events::VirtualKeyCode::F15,
|
||||
0x72 => events::VirtualKeyCode::Insert,
|
||||
0x73 => events::VirtualKeyCode::Home,
|
||||
0x74 => events::VirtualKeyCode::PageUp,
|
||||
0x75 => events::VirtualKeyCode::Delete,
|
||||
0x76 => events::VirtualKeyCode::F4,
|
||||
0x77 => events::VirtualKeyCode::End,
|
||||
0x78 => events::VirtualKeyCode::F2,
|
||||
0x79 => events::VirtualKeyCode::PageDown,
|
||||
0x7a => events::VirtualKeyCode::F1,
|
||||
0x7b => events::VirtualKeyCode::Left,
|
||||
0x7c => events::VirtualKeyCode::Right,
|
||||
0x7d => events::VirtualKeyCode::Down,
|
||||
0x7e => events::VirtualKeyCode::Up,
|
||||
//0x7f => unkown,
|
||||
|
||||
0xa => events::VirtualKeyCode::Caret,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
fn event_mods(event: cocoa::base::id) -> ModifiersState {
|
||||
let flags = unsafe {
|
||||
NSEvent::modifierFlags(event)
|
||||
};
|
||||
ModifiersState {
|
||||
shift: flags.contains(NSEventModifierFlags::NSShiftKeyMask),
|
||||
ctrl: flags.contains(NSEventModifierFlags::NSControlKeyMask),
|
||||
alt: flags.contains(NSEventModifierFlags::NSAlternateKeyMask),
|
||||
logo: flags.contains(NSEventModifierFlags::NSCommandKeyMask),
|
||||
}
|
||||
}
|
||||
|
||||
// Constant device ID, to be removed when this backend is updated to report real device IDs.
|
||||
const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId);
|
||||
@@ -1,42 +0,0 @@
|
||||
#![cfg(target_os = "macos")]
|
||||
|
||||
pub use self::events_loop::{EventsLoop, Proxy as EventsLoopProxy};
|
||||
pub use self::monitor::MonitorId;
|
||||
pub use self::window::{Id as WindowId, PlatformSpecificWindowBuilderAttributes, Window2};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId;
|
||||
|
||||
use {CreationError};
|
||||
|
||||
pub struct Window {
|
||||
pub window: Arc<Window2>,
|
||||
}
|
||||
|
||||
impl ::std::ops::Deref for Window {
|
||||
type Target = Window2;
|
||||
#[inline]
|
||||
fn deref(&self) -> &Window2 {
|
||||
&*self.window
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
|
||||
pub fn new(events_loop: &EventsLoop,
|
||||
attributes: ::WindowAttributes,
|
||||
pl_attribs: PlatformSpecificWindowBuilderAttributes) -> Result<Self, CreationError>
|
||||
{
|
||||
let weak_shared = Arc::downgrade(&events_loop.shared);
|
||||
let window = Arc::new(try!(Window2::new(weak_shared, attributes, pl_attribs)));
|
||||
let weak_window = Arc::downgrade(&window);
|
||||
events_loop.shared.windows.lock().unwrap().push(weak_window);
|
||||
Ok(Window { window: window })
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
mod events_loop;
|
||||
mod monitor;
|
||||
mod window;
|
||||
@@ -1,94 +0,0 @@
|
||||
use cocoa::appkit::NSScreen;
|
||||
use cocoa::base::{id, nil};
|
||||
use cocoa::foundation::{NSString, NSUInteger};
|
||||
use core_graphics::display::{CGDirectDisplayID, CGDisplay};
|
||||
use std::collections::VecDeque;
|
||||
use super::EventsLoop;
|
||||
use super::window::IdRef;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct MonitorId(CGDirectDisplayID);
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
let mut monitors = VecDeque::new();
|
||||
if let Ok(displays) = CGDisplay::active_displays() {
|
||||
for d in displays {
|
||||
monitors.push_back(MonitorId(d));
|
||||
}
|
||||
}
|
||||
monitors
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
let id = MonitorId(CGDisplay::main().id);
|
||||
id
|
||||
}
|
||||
|
||||
pub fn make_monitor_from_display(id: CGDirectDisplayID) -> MonitorId {
|
||||
let id = MonitorId(id);
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
let MonitorId(display_id) = *self;
|
||||
let screen_num = CGDisplay::new(display_id).model_number();
|
||||
Some(format!("Monitor #{}", screen_num))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
let MonitorId(display_id) = *self;
|
||||
let display = CGDisplay::new(display_id);
|
||||
let dimension = {
|
||||
let height = display.pixels_high();
|
||||
let width = display.pixels_wide();
|
||||
(width as u32, height as u32)
|
||||
};
|
||||
dimension
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
let screen = match self.get_nsscreen() {
|
||||
Some(screen) => screen,
|
||||
None => return 1.0, // default to 1.0 when we can't find the screen
|
||||
};
|
||||
|
||||
unsafe { NSScreen::backingScaleFactor(screen) as f32 }
|
||||
}
|
||||
|
||||
pub(crate) fn get_nsscreen(&self) -> Option<id> {
|
||||
unsafe {
|
||||
let native_id = self.get_native_identifier();
|
||||
let screens = NSScreen::screens(nil);
|
||||
let count: NSUInteger = msg_send![screens, count];
|
||||
let key = IdRef::new(NSString::alloc(nil).init_str("NSScreenNumber"));
|
||||
let mut matching_screen: Option<id> = None;
|
||||
for i in 0..count {
|
||||
let screen = msg_send![screens, objectAtIndex: i as NSUInteger];
|
||||
let device_description = NSScreen::deviceDescription(screen);
|
||||
let value: id = msg_send![device_description, objectForKey:*key];
|
||||
if value != nil {
|
||||
let screen_number: NSUInteger = msg_send![value, unsignedIntegerValue];
|
||||
if screen_number as u32 == native_id {
|
||||
matching_screen = Some(screen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
matching_screen
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,25 +1,25 @@
|
||||
pub use self::platform::*;
|
||||
//! Contains traits with platform-specific methods in them.
|
||||
//!
|
||||
//! Contains the follow OS-specific modules:
|
||||
//!
|
||||
//! - `android`
|
||||
//! - `ios`
|
||||
//! - `macos`
|
||||
//! - `unix`
|
||||
//! - `windows`
|
||||
//! - `web`
|
||||
//!
|
||||
//! And the following platform-specific module:
|
||||
//!
|
||||
//! - `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(target_os = "windows")]
|
||||
#[path="windows/mod.rs"]
|
||||
mod platform;
|
||||
#[cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
#[path="linux/mod.rs"]
|
||||
mod platform;
|
||||
#[cfg(target_os = "macos")]
|
||||
#[path="macos/mod.rs"]
|
||||
mod platform;
|
||||
#[cfg(target_os = "android")]
|
||||
#[path="android/mod.rs"]
|
||||
mod platform;
|
||||
#[cfg(target_os = "ios")]
|
||||
#[path="ios/mod.rs"]
|
||||
mod platform;
|
||||
#[cfg(target_os = "emscripten")]
|
||||
#[path="emscripten/mod.rs"]
|
||||
mod platform;
|
||||
pub mod android;
|
||||
pub mod ios;
|
||||
pub mod macos;
|
||||
pub mod unix;
|
||||
pub mod windows;
|
||||
|
||||
#[cfg(all(not(target_os = "ios"), not(target_os = "windows"), not(target_os = "linux"),
|
||||
not(target_os = "macos"), not(target_os = "android"), not(target_os = "dragonfly"),
|
||||
not(target_os = "freebsd"), not(target_os = "openbsd"), not(target_os = "emscripten")))]
|
||||
compile_error!("The platform you're compiling for is not supported by winit");
|
||||
pub mod run_return;
|
||||
pub mod web;
|
||||
|
||||
58
src/platform/run_return.rs
Normal file
58
src/platform/run_return.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
#![cfg(any(
|
||||
target_os = "windows",
|
||||
target_os = "macos",
|
||||
target_os = "android",
|
||||
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` 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`.
|
||||
///
|
||||
/// # Caveats
|
||||
/// 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
|
||||
/// 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)
|
||||
where
|
||||
F: FnMut(
|
||||
Event<'_, Self::UserEvent>,
|
||||
&EventLoopWindowTarget<Self::UserEvent>,
|
||||
&mut ControlFlow,
|
||||
);
|
||||
}
|
||||
|
||||
impl<T> EventLoopExtRunReturn for EventLoop<T> {
|
||||
type UserEvent = T;
|
||||
|
||||
fn run_return<F>(&mut self, event_handler: F)
|
||||
where
|
||||
F: FnMut(
|
||||
Event<'_, Self::UserEvent>,
|
||||
&EventLoopWindowTarget<Self::UserEvent>,
|
||||
&mut ControlFlow,
|
||||
),
|
||||
{
|
||||
self.event_loop.run_return(event_handler)
|
||||
}
|
||||
}
|
||||
464
src/platform/unix.rs
Normal file
464
src/platform/unix.rs
Normal file
@@ -0,0 +1,464 @@
|
||||
#![cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
|
||||
use std::os::raw;
|
||||
#[cfg(feature = "x11")]
|
||||
use std::{ptr, sync::Arc};
|
||||
|
||||
use crate::{
|
||||
event_loop::{EventLoop, EventLoopWindowTarget},
|
||||
monitor::MonitorHandle,
|
||||
window::{Window, WindowBuilder},
|
||||
};
|
||||
|
||||
#[cfg(feature = "x11")]
|
||||
use crate::dpi::Size;
|
||||
#[cfg(feature = "x11")]
|
||||
use crate::platform_impl::x11::{ffi::XVisualInfo, XConnection};
|
||||
use crate::platform_impl::{
|
||||
EventLoop as LinuxEventLoop, EventLoopWindowTarget as LinuxEventLoopWindowTarget,
|
||||
Window as LinuxWindow,
|
||||
};
|
||||
|
||||
// TODO: stupid hack so that glutin can do its work
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "x11")]
|
||||
pub use crate::platform_impl::x11;
|
||||
#[cfg(feature = "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.
|
||||
#[cfg(feature = "wayland")]
|
||||
fn is_wayland(&self) -> bool;
|
||||
|
||||
/// True if the `EventLoopWindowTarget` uses X11.
|
||||
#[cfg(feature = "x11")]
|
||||
fn is_x11(&self) -> bool;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "x11")]
|
||||
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.
|
||||
#[cfg(feature = "wayland")]
|
||||
fn wayland_display(&self) -> Option<*mut raw::c_void>;
|
||||
}
|
||||
|
||||
impl<T> EventLoopWindowTargetExtUnix for EventLoopWindowTarget<T> {
|
||||
#[inline]
|
||||
#[cfg(feature = "wayland")]
|
||||
fn is_wayland(&self) -> bool {
|
||||
self.p.is_wayland()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "x11")]
|
||||
fn is_x11(&self) -> bool {
|
||||
!self.p.is_wayland()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "x11")]
|
||||
fn xlib_xconnection(&self) -> Option<Arc<XConnection>> {
|
||||
match self.p {
|
||||
LinuxEventLoopWindowTarget::X(ref e) => Some(e.x_connection().clone()),
|
||||
#[cfg(feature = "wayland")]
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "wayland")]
|
||||
fn wayland_display(&self) -> Option<*mut raw::c_void> {
|
||||
match self.p {
|
||||
LinuxEventLoopWindowTarget::Wayland(ref p) => {
|
||||
Some(p.display().get_display_ptr() as *mut _)
|
||||
}
|
||||
#[cfg(feature = "x11")]
|
||||
_ => 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).
|
||||
#[cfg(feature = "x11")]
|
||||
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).
|
||||
#[cfg(feature = "wayland")]
|
||||
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.
|
||||
#[cfg(feature = "x11")]
|
||||
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.
|
||||
#[cfg(feature = "wayland")]
|
||||
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]
|
||||
#[cfg(feature = "x11")]
|
||||
fn new_x11_any_thread() -> Result<Self, XNotSupported> {
|
||||
LinuxEventLoop::new_x11_any_thread().map(wrap_ev)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "wayland")]
|
||||
fn new_wayland_any_thread() -> Self {
|
||||
wrap_ev(
|
||||
LinuxEventLoop::new_wayland_any_thread()
|
||||
// TODO: propagate
|
||||
.expect("failed to open Wayland connection"),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "x11")]
|
||||
fn new_x11() -> Result<Self, XNotSupported> {
|
||||
LinuxEventLoop::new_x11().map(wrap_ev)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "wayland")]
|
||||
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).
|
||||
#[cfg(feature = "x11")]
|
||||
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.
|
||||
#[cfg(feature = "x11")]
|
||||
fn xlib_display(&self) -> Option<*mut raw::c_void>;
|
||||
|
||||
#[cfg(feature = "x11")]
|
||||
fn xlib_screen_id(&self) -> Option<raw::c_int>;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "x11")]
|
||||
fn xlib_xconnection(&self) -> Option<Arc<XConnection>>;
|
||||
|
||||
/// 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.
|
||||
#[cfg(feature = "x11")]
|
||||
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.
|
||||
#[cfg(feature = "wayland")]
|
||||
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.
|
||||
#[cfg(feature = "wayland")]
|
||||
fn wayland_display(&self) -> Option<*mut raw::c_void>;
|
||||
|
||||
/// 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]
|
||||
#[cfg(feature = "x11")]
|
||||
fn xlib_window(&self) -> Option<raw::c_ulong> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.xlib_window()),
|
||||
#[cfg(feature = "wayland")]
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "x11")]
|
||||
fn xlib_display(&self) -> Option<*mut raw::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.xlib_display()),
|
||||
#[cfg(feature = "wayland")]
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "x11")]
|
||||
fn xlib_screen_id(&self) -> Option<raw::c_int> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.xlib_screen_id()),
|
||||
#[cfg(feature = "wayland")]
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "x11")]
|
||||
fn xlib_xconnection(&self) -> Option<Arc<XConnection>> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.xlib_xconnection()),
|
||||
#[cfg(feature = "wayland")]
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "x11")]
|
||||
fn xcb_connection(&self) -> Option<*mut raw::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.xcb_connection()),
|
||||
#[cfg(feature = "wayland")]
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "wayland")]
|
||||
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 _),
|
||||
#[cfg(feature = "x11")]
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "wayland")]
|
||||
fn wayland_display(&self) -> Option<*mut raw::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::Wayland(ref w) => Some(w.display().get_display_ptr() as *mut _),
|
||||
#[cfg(feature = "x11")]
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_ready(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `WindowBuilder` that are specific to Unix.
|
||||
pub trait WindowBuilderExtUnix {
|
||||
#[cfg(feature = "x11")]
|
||||
fn with_x11_visual<T>(self, visual_infos: *const T) -> Self;
|
||||
#[cfg(feature = "x11")]
|
||||
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.
|
||||
#[cfg(feature = "x11")]
|
||||
fn with_class(self, class: String, instance: String) -> Self;
|
||||
/// Build window with override-redirect flag; defaults to false. Only relevant on X11.
|
||||
#[cfg(feature = "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.
|
||||
#[cfg(feature = "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.
|
||||
#[cfg(feature = "x11")]
|
||||
fn with_gtk_theme_variant(self, variant: String) -> Self;
|
||||
/// Build window with resize increment hint. Only implemented on X11.
|
||||
///
|
||||
/// ```
|
||||
/// # use winit::dpi::{LogicalSize, PhysicalSize};
|
||||
/// # use winit::window::WindowBuilder;
|
||||
/// # use winit::platform::unix::WindowBuilderExtUnix;
|
||||
/// // Specify the size in logical dimensions like this:
|
||||
/// WindowBuilder::new().with_resize_increments(LogicalSize::new(400.0, 200.0));
|
||||
///
|
||||
/// // Or specify the size in physical dimensions like this:
|
||||
/// WindowBuilder::new().with_resize_increments(PhysicalSize::new(400, 200));
|
||||
/// ```
|
||||
#[cfg(feature = "x11")]
|
||||
fn with_resize_increments<S: Into<Size>>(self, increments: S) -> Self;
|
||||
/// Build window with base size hint. Only implemented on X11.
|
||||
///
|
||||
/// ```
|
||||
/// # use winit::dpi::{LogicalSize, PhysicalSize};
|
||||
/// # use winit::window::WindowBuilder;
|
||||
/// # use winit::platform::unix::WindowBuilderExtUnix;
|
||||
/// // 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));
|
||||
/// ```
|
||||
#[cfg(feature = "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)
|
||||
#[cfg(feature = "wayland")]
|
||||
fn with_app_id(self, app_id: String) -> Self;
|
||||
}
|
||||
|
||||
impl WindowBuilderExtUnix for WindowBuilder {
|
||||
#[inline]
|
||||
#[cfg(feature = "x11")]
|
||||
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]
|
||||
#[cfg(feature = "x11")]
|
||||
fn with_x11_screen(mut self, screen_id: i32) -> Self {
|
||||
self.platform_specific.screen_id = Some(screen_id);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "x11")]
|
||||
fn with_class(mut self, instance: String, class: String) -> Self {
|
||||
self.platform_specific.class = Some((instance, class));
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "x11")]
|
||||
fn with_override_redirect(mut self, override_redirect: bool) -> Self {
|
||||
self.platform_specific.override_redirect = override_redirect;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "x11")]
|
||||
fn with_x11_window_type(mut self, x11_window_types: Vec<XWindowType>) -> Self {
|
||||
self.platform_specific.x11_window_types = x11_window_types;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "x11")]
|
||||
fn with_gtk_theme_variant(mut self, variant: String) -> Self {
|
||||
self.platform_specific.gtk_theme_variant = Some(variant);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "x11")]
|
||||
fn with_resize_increments<S: Into<Size>>(mut self, increments: S) -> Self {
|
||||
self.platform_specific.resize_increments = Some(increments.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "x11")]
|
||||
fn with_base_size<S: Into<Size>>(mut self, base_size: S) -> Self {
|
||||
self.platform_specific.base_size = Some(base_size.into());
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(feature = "wayland")]
|
||||
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()
|
||||
}
|
||||
}
|
||||
29
src/platform/web.rs
Normal file
29
src/platform/web.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
#![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 `WindowExtWebSys` trait
|
||||
//! to retrieve the canvas from the Window. Alternatively, use the `WindowBuilderExtWebSys` trait
|
||||
//! to provide your own canvas.
|
||||
|
||||
use crate::window::WindowBuilder;
|
||||
|
||||
use web_sys::HtmlCanvasElement;
|
||||
|
||||
pub trait WindowExtWebSys {
|
||||
fn canvas(&self) -> HtmlCanvasElement;
|
||||
|
||||
/// Whether the browser reports the preferred color scheme to be "dark".
|
||||
fn is_dark_mode(&self) -> bool;
|
||||
}
|
||||
|
||||
pub trait WindowBuilderExtWebSys {
|
||||
fn with_canvas(self, canvas: Option<HtmlCanvasElement>) -> Self;
|
||||
}
|
||||
|
||||
impl WindowBuilderExtWebSys for WindowBuilder {
|
||||
fn with_canvas(mut self, canvas: Option<HtmlCanvasElement>) -> Self {
|
||||
self.platform_specific.canvas = canvas;
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
293
src/platform/windows.rs
Normal file
293
src/platform/windows.rs
Normal file
@@ -0,0 +1,293 @@
|
||||
#![cfg(target_os = "windows")]
|
||||
|
||||
use std::os::raw::c_void;
|
||||
use std::path::Path;
|
||||
|
||||
use winapi::shared::minwindef::WORD;
|
||||
use winapi::shared::windef::{HMENU, HWND};
|
||||
|
||||
use crate::{
|
||||
dpi::PhysicalSize,
|
||||
event::DeviceId,
|
||||
event_loop::EventLoop,
|
||||
monitor::MonitorHandle,
|
||||
platform_impl::{EventLoop as WindowsEventLoop, Parent, WinIcon},
|
||||
window::{BadIcon, Icon, Theme, Window, WindowBuilder},
|
||||
};
|
||||
|
||||
/// Additional methods on `EventLoop` that are specific to Windows.
|
||||
pub trait EventLoopExtWindows {
|
||||
/// Creates an event loop off of the main thread.
|
||||
///
|
||||
/// # `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;
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// The `Window` caveats in [`new_any_thread`](EventLoopExtWindows::new_any_thread) also apply here.
|
||||
fn new_dpi_unaware_any_thread() -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
impl<T> EventLoopExtWindows for EventLoop<T> {
|
||||
#[inline]
|
||||
fn new_any_thread() -> Self {
|
||||
EventLoop {
|
||||
event_loop: WindowsEventLoop::new_any_thread(),
|
||||
_marker: ::std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_dpi_unaware() -> Self {
|
||||
EventLoop {
|
||||
event_loop: WindowsEventLoop::new_dpi_unaware(),
|
||||
_marker: ::std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_dpi_unaware_any_thread() -> Self {
|
||||
EventLoop {
|
||||
event_loop: WindowsEventLoop::new_dpi_unaware_any_thread(),
|
||||
_marker: ::std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `Window` that are specific to Windows.
|
||||
pub trait WindowExtWindows {
|
||||
/// Returns the HINSTANCE of the window
|
||||
fn hinstance(&self) -> *mut c_void;
|
||||
/// 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 c_void;
|
||||
|
||||
/// 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>);
|
||||
|
||||
/// Returns the current window theme.
|
||||
fn theme(&self) -> Theme;
|
||||
}
|
||||
|
||||
impl WindowExtWindows for Window {
|
||||
#[inline]
|
||||
fn hinstance(&self) -> *mut c_void {
|
||||
self.window.hinstance() as *mut _
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hwnd(&self) -> *mut c_void {
|
||||
self.window.hwnd() as *mut _
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_enable(&self, enabled: bool) {
|
||||
self.window.set_enable(enabled)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_taskbar_icon(&self, taskbar_icon: Option<Icon>) {
|
||||
self.window.set_taskbar_icon(taskbar_icon)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn theme(&self) -> Theme {
|
||||
self.window.theme()
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `WindowBuilder` that are specific to Windows.
|
||||
pub trait WindowBuilderExtWindows {
|
||||
/// Sets a parent to the window to be created.
|
||||
///
|
||||
/// A child window has the WS_CHILD style and is confined to the client area of its parent window.
|
||||
///
|
||||
/// For more information, see <https://docs.microsoft.com/en-us/windows/win32/winmsg/window-features#child-windows>
|
||||
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.
|
||||
/// 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 [`winapi::um::winuser::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.
|
||||
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;
|
||||
|
||||
/// Forces a theme or uses the system settings if `None` was provided.
|
||||
fn with_theme(self, theme: Option<Theme>) -> WindowBuilder;
|
||||
}
|
||||
|
||||
impl WindowBuilderExtWindows for WindowBuilder {
|
||||
#[inline]
|
||||
fn with_parent_window(mut self, parent: HWND) -> WindowBuilder {
|
||||
self.platform_specific.parent = Parent::ChildOf(parent);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_owner_window(mut self, parent: HWND) -> WindowBuilder {
|
||||
self.platform_specific.parent = Parent::OwnedBy(parent);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_menu(mut self, menu: HMENU) -> WindowBuilder {
|
||||
self.platform_specific.menu = Some(menu);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_taskbar_icon(mut self, taskbar_icon: Option<Icon>) -> WindowBuilder {
|
||||
self.platform_specific.taskbar_icon = taskbar_icon;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_no_redirection_bitmap(mut self, flag: bool) -> 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_theme(mut self, theme: Option<Theme>) -> WindowBuilder {
|
||||
self.platform_specific.preferred_theme = theme;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `MonitorHandle` that are specific to Windows.
|
||||
pub trait MonitorHandleExtWindows {
|
||||
/// Returns the name of the monitor adapter specific to the Win32 API.
|
||||
fn native_id(&self) -> String;
|
||||
|
||||
/// Returns the handle of the monitor - `HMONITOR`.
|
||||
fn hmonitor(&self) -> *mut c_void;
|
||||
}
|
||||
|
||||
impl MonitorHandleExtWindows for MonitorHandle {
|
||||
#[inline]
|
||||
fn native_id(&self) -> String {
|
||||
self.inner.native_identifier()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hmonitor(&self) -> *mut c_void {
|
||||
self.inner.hmonitor() as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `DeviceId` that are specific to Windows.
|
||||
pub trait DeviceIdExtWindows {
|
||||
/// Returns an identifier that persistently refers to this specific device.
|
||||
///
|
||||
/// Will return `None` if the device is no longer available.
|
||||
fn persistent_identifier(&self) -> Option<String>;
|
||||
}
|
||||
|
||||
impl DeviceIdExtWindows for DeviceId {
|
||||
#[inline]
|
||||
fn persistent_identifier(&self) -> Option<String> {
|
||||
self.0.persistent_identifier()
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `Icon` that are specific to Windows.
|
||||
pub trait IconExtWindows: Sized {
|
||||
/// Create an icon from a file path.
|
||||
///
|
||||
/// Specify `size` to load a specific icon size from the file, or `None` to load the default
|
||||
/// icon size from the file.
|
||||
///
|
||||
/// 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_path<P: AsRef<Path>>(path: P, size: Option<PhysicalSize<u32>>)
|
||||
-> Result<Self, BadIcon>;
|
||||
|
||||
/// Create an icon from a resource embedded in this executable or library.
|
||||
///
|
||||
/// Specify `size` to load a specific icon size from the file, or `None` to load the default
|
||||
/// icon size from the file.
|
||||
///
|
||||
/// 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>;
|
||||
}
|
||||
|
||||
impl IconExtWindows for Icon {
|
||||
fn from_path<P: AsRef<Path>>(
|
||||
path: P,
|
||||
size: Option<PhysicalSize<u32>>,
|
||||
) -> Result<Self, BadIcon> {
|
||||
let win_icon = WinIcon::from_path(path, size)?;
|
||||
Ok(Icon { inner: win_icon })
|
||||
}
|
||||
|
||||
fn from_resource(ordinal: WORD, size: Option<PhysicalSize<u32>>) -> Result<Self, BadIcon> {
|
||||
let win_icon = WinIcon::from_resource(ordinal, size)?;
|
||||
Ok(Icon { inner: win_icon })
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,112 +0,0 @@
|
||||
use std::{self, mem, ptr};
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::path::Path;
|
||||
|
||||
use winapi::ctypes::{c_int, wchar_t};
|
||||
use winapi::shared::minwindef::{BYTE, LPARAM, WPARAM};
|
||||
use winapi::shared::windef::{HICON, HWND};
|
||||
use winapi::um::winuser;
|
||||
|
||||
use {Pixel, PIXEL_SIZE, Icon};
|
||||
use platform::platform::util;
|
||||
|
||||
impl Pixel {
|
||||
fn to_bgra(&mut self) {
|
||||
mem::swap(&mut self.r, &mut self.b);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum IconType {
|
||||
Small = winuser::ICON_SMALL as isize,
|
||||
Big = winuser::ICON_BIG as isize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WinIcon {
|
||||
pub handle: HICON,
|
||||
}
|
||||
|
||||
impl WinIcon {
|
||||
#[allow(dead_code)]
|
||||
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, util::WinError> {
|
||||
let wide_path: Vec<u16> = path.as_ref().as_os_str().encode_wide().collect();
|
||||
let handle = unsafe {
|
||||
winuser::LoadImageW(
|
||||
ptr::null_mut(),
|
||||
wide_path.as_ptr() as *const wchar_t,
|
||||
winuser::IMAGE_ICON,
|
||||
0, // 0 indicates that we want to use the actual width
|
||||
0, // and height
|
||||
winuser::LR_LOADFROMFILE,
|
||||
) as HICON
|
||||
};
|
||||
if !handle.is_null() {
|
||||
Ok(WinIcon { handle })
|
||||
} else {
|
||||
Err(util::WinError::from_last_error())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_icon(icon: Icon) -> Result<Self, util::WinError> {
|
||||
Self::from_rgba(icon.rgba, icon.width, icon.height)
|
||||
}
|
||||
|
||||
pub fn from_rgba(mut rgba: Vec<u8>, width: u32, height: u32) -> Result<Self, util::WinError> {
|
||||
assert_eq!(rgba.len() % PIXEL_SIZE, 0);
|
||||
let pixel_count = rgba.len() / PIXEL_SIZE;
|
||||
assert_eq!(pixel_count, (width * height) as usize);
|
||||
let mut and_mask = Vec::with_capacity(pixel_count);
|
||||
let pixels = rgba.as_mut_ptr() as *mut Pixel; // how not to write idiomatic Rust
|
||||
for pixel_index in 0..pixel_count {
|
||||
let pixel = unsafe { &mut *pixels.offset(pixel_index as isize) };
|
||||
and_mask.push(pixel.a.wrapping_sub(std::u8::MAX)); // invert alpha channel
|
||||
pixel.to_bgra();
|
||||
}
|
||||
assert_eq!(and_mask.len(), pixel_count);
|
||||
let handle = unsafe {
|
||||
winuser::CreateIcon(
|
||||
ptr::null_mut(),
|
||||
width as c_int,
|
||||
height as c_int,
|
||||
1,
|
||||
(PIXEL_SIZE * 8) as BYTE,
|
||||
and_mask.as_ptr() as *const BYTE,
|
||||
rgba.as_ptr() as *const BYTE,
|
||||
) as HICON
|
||||
};
|
||||
if !handle.is_null() {
|
||||
Ok(WinIcon { handle })
|
||||
} else {
|
||||
Err(util::WinError::from_last_error())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_for_window(&self, hwnd: HWND, icon_type: IconType) {
|
||||
unsafe {
|
||||
winuser::SendMessageW(
|
||||
hwnd,
|
||||
winuser::WM_SETICON,
|
||||
icon_type as WPARAM,
|
||||
self.handle as LPARAM,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WinIcon {
|
||||
fn drop(&mut self) {
|
||||
unsafe { winuser::DestroyIcon(self.handle) };
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unset_for_window(hwnd: HWND, icon_type: IconType) {
|
||||
unsafe {
|
||||
winuser::SendMessageW(
|
||||
hwnd,
|
||||
winuser::WM_SETICON,
|
||||
icon_type as WPARAM,
|
||||
0 as LPARAM,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
#![cfg(target_os = "windows")]
|
||||
|
||||
use winapi;
|
||||
use winapi::shared::windef::HWND;
|
||||
|
||||
pub use self::events_loop::{EventsLoop, EventsLoopProxy};
|
||||
pub use self::monitor::MonitorId;
|
||||
pub use self::window::Window;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
pub parent: Option<HWND>,
|
||||
pub taskbar_icon: Option<::Icon>,
|
||||
}
|
||||
|
||||
unsafe impl Send for PlatformSpecificWindowBuilderAttributes {}
|
||||
unsafe impl Sync for PlatformSpecificWindowBuilderAttributes {}
|
||||
|
||||
// TODO: document what this means
|
||||
pub type Cursor = *const winapi::ctypes::wchar_t;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct DeviceId(u32);
|
||||
|
||||
impl DeviceId {
|
||||
pub fn get_persistent_identifier(&self) -> Option<String> {
|
||||
if self.0 != 0 {
|
||||
raw_input::get_raw_input_device_name(self.0 as _)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Constant device ID, to be removed when this backend is updated to report real device IDs.
|
||||
const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId(0));
|
||||
|
||||
fn wrap_device_id(id: u32) -> ::DeviceId {
|
||||
::DeviceId(DeviceId(id))
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(HWND);
|
||||
unsafe impl Send for WindowId {}
|
||||
unsafe impl Sync for WindowId {}
|
||||
|
||||
mod event;
|
||||
mod events_loop;
|
||||
mod icon;
|
||||
mod monitor;
|
||||
mod raw_input;
|
||||
mod util;
|
||||
mod window;
|
||||
@@ -1,172 +0,0 @@
|
||||
use winapi::ctypes::wchar_t;
|
||||
use winapi::shared::minwindef::{DWORD, LPARAM, BOOL, TRUE};
|
||||
use winapi::shared::windef::{HMONITOR, HDC, LPRECT, HWND};
|
||||
use winapi::um::winuser;
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::{mem, ptr};
|
||||
|
||||
use super::{EventsLoop, util};
|
||||
|
||||
/// Win32 implementation of the main `MonitorId` object.
|
||||
#[derive(Clone)]
|
||||
pub struct MonitorId {
|
||||
/// The system name of the adapter.
|
||||
adapter_name: [wchar_t; 32],
|
||||
|
||||
/// Monitor handle.
|
||||
hmonitor: HMonitor,
|
||||
|
||||
/// The system name of the monitor.
|
||||
monitor_name: String,
|
||||
|
||||
/// True if this is the primary monitor.
|
||||
primary: bool,
|
||||
|
||||
/// The position of the monitor in pixels on the desktop.
|
||||
///
|
||||
/// A window that is positioned at these coordinates will overlap the monitor.
|
||||
position: (i32, i32),
|
||||
|
||||
/// The current resolution in pixels on the monitor.
|
||||
dimensions: (u32, u32),
|
||||
|
||||
/// DPI scaling factor.
|
||||
hidpi_factor: f32,
|
||||
}
|
||||
|
||||
// Send is not implemented for HMONITOR, we have to wrap it and implement it manually.
|
||||
// For more info see:
|
||||
// https://github.com/retep998/winapi-rs/issues/360
|
||||
// https://github.com/retep998/winapi-rs/issues/396
|
||||
#[derive(Clone)]
|
||||
struct HMonitor(HMONITOR);
|
||||
|
||||
unsafe impl Send for HMonitor {}
|
||||
|
||||
unsafe extern "system" fn monitor_enum_proc(hmonitor: HMONITOR, _: HDC, place: LPRECT, data: LPARAM) -> BOOL {
|
||||
let monitors = data as *mut VecDeque<MonitorId>;
|
||||
|
||||
let place = *place;
|
||||
let position = (place.left as i32, place.top as i32);
|
||||
let dimensions = ((place.right - place.left) as u32, (place.bottom - place.top) as u32);
|
||||
|
||||
let mut monitor_info: winuser::MONITORINFOEXW = mem::zeroed();
|
||||
monitor_info.cbSize = mem::size_of::<winuser::MONITORINFOEXW>() as DWORD;
|
||||
if winuser::GetMonitorInfoW(hmonitor, &mut monitor_info as *mut winuser::MONITORINFOEXW as *mut winuser::MONITORINFO) == 0 {
|
||||
// Some error occurred, just skip this monitor and go on.
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
(*monitors).push_back(MonitorId {
|
||||
adapter_name: monitor_info.szDevice,
|
||||
hmonitor: HMonitor(hmonitor),
|
||||
monitor_name: util::wchar_to_string(&monitor_info.szDevice),
|
||||
primary: monitor_info.dwFlags & winuser::MONITORINFOF_PRIMARY != 0,
|
||||
position,
|
||||
dimensions,
|
||||
hidpi_factor: 1.0,
|
||||
});
|
||||
|
||||
// TRUE means continue enumeration.
|
||||
TRUE
|
||||
}
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorId> {
|
||||
unsafe {
|
||||
let mut result: VecDeque<MonitorId> = VecDeque::new();
|
||||
winuser::EnumDisplayMonitors(ptr::null_mut(), ptr::null_mut(), Some(monitor_enum_proc), &mut result as *mut _ as LPARAM);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_current_monitor(handle: HWND) -> MonitorId {
|
||||
unsafe {
|
||||
let mut monitor_info: winuser::MONITORINFOEXW = mem::zeroed();
|
||||
monitor_info.cbSize = mem::size_of::<winuser::MONITORINFOEXW>() as DWORD;
|
||||
|
||||
let hmonitor = winuser::MonitorFromWindow(handle, winuser::MONITOR_DEFAULTTONEAREST);
|
||||
|
||||
winuser::GetMonitorInfoW(
|
||||
hmonitor,
|
||||
&mut monitor_info as *mut winuser::MONITORINFOEXW as *mut winuser::MONITORINFO,
|
||||
);
|
||||
|
||||
let place = monitor_info.rcMonitor;
|
||||
let position = (place.left as i32, place.top as i32);
|
||||
let dimensions = (
|
||||
(place.right - place.left) as u32,
|
||||
(place.bottom - place.top) as u32,
|
||||
);
|
||||
|
||||
MonitorId {
|
||||
adapter_name: monitor_info.szDevice,
|
||||
hmonitor: super::monitor::HMonitor(hmonitor),
|
||||
monitor_name: util::wchar_to_string(&monitor_info.szDevice),
|
||||
primary: monitor_info.dwFlags & winuser::MONITORINFOF_PRIMARY != 0,
|
||||
position,
|
||||
dimensions,
|
||||
hidpi_factor: 1.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_primary_monitor(&self) -> MonitorId {
|
||||
// we simply get all available monitors and return the one with the `MONITORINFOF_PRIMARY` flag
|
||||
// TODO: it is possible to query the win32 API for the primary monitor, this should be done
|
||||
// instead
|
||||
for monitor in self.get_available_monitors().into_iter() {
|
||||
if monitor.primary {
|
||||
return monitor;
|
||||
}
|
||||
}
|
||||
|
||||
panic!("Failed to find the primary monitor")
|
||||
}
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
/// See the docs if the crate root file.
|
||||
#[inline]
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
Some(self.monitor_name.clone())
|
||||
}
|
||||
|
||||
/// See the docs of the crate root file.
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> String {
|
||||
self.monitor_name.clone()
|
||||
}
|
||||
|
||||
/// See the docs of the crate root file.
|
||||
#[inline]
|
||||
pub fn get_hmonitor(&self) -> HMONITOR {
|
||||
self.hmonitor.0
|
||||
}
|
||||
|
||||
/// See the docs of the crate root file.
|
||||
#[inline]
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
// TODO: retrieve the dimensions every time this is called
|
||||
self.dimensions
|
||||
}
|
||||
|
||||
/// This is a Win32-only function for `MonitorId` that returns the system name of the adapter
|
||||
/// device.
|
||||
#[inline]
|
||||
pub fn get_adapter_name(&self) -> &[wchar_t] {
|
||||
&self.adapter_name
|
||||
}
|
||||
|
||||
/// A window that is positioned at these coordinates will overlap the monitor.
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> (i32, i32) {
|
||||
self.position
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_hidpi_factor(&self) -> f32 {
|
||||
self.hidpi_factor
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
use std::{self, mem, ptr};
|
||||
use std::ops::BitAnd;
|
||||
|
||||
use winapi::ctypes::wchar_t;
|
||||
use winapi::shared::minwindef::DWORD;
|
||||
use winapi::um::errhandlingapi::GetLastError;
|
||||
use winapi::um::winbase::{
|
||||
FormatMessageW,
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
||||
FORMAT_MESSAGE_FROM_SYSTEM,
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
lstrlenW,
|
||||
LocalFree,
|
||||
};
|
||||
use winapi::um::winnt::{
|
||||
LPCWSTR,
|
||||
MAKELANGID,
|
||||
LANG_NEUTRAL,
|
||||
SUBLANG_DEFAULT,
|
||||
};
|
||||
|
||||
pub fn has_flag<T>(bitset: T, flag: T) -> bool
|
||||
where T:
|
||||
Copy + PartialEq + BitAnd<T, Output = T>
|
||||
{
|
||||
bitset & flag == flag
|
||||
}
|
||||
|
||||
pub fn wchar_to_string(wchar: &[wchar_t]) -> String {
|
||||
String::from_utf16_lossy(wchar)
|
||||
.trim_right_matches(0 as char)
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq)]
|
||||
pub struct WinError(Option<String>);
|
||||
|
||||
impl WinError {
|
||||
pub fn from_last_error() -> Self {
|
||||
WinError(unsafe { get_last_error() })
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn get_last_error() -> Option<String> {
|
||||
let err = GetLastError();
|
||||
if err != 0 {
|
||||
let buf_addr: LPCWSTR = {
|
||||
let mut buf_addr: LPCWSTR = mem::uninitialized();
|
||||
FormatMessageW(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER
|
||||
| FORMAT_MESSAGE_FROM_SYSTEM
|
||||
| FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
ptr::null(),
|
||||
err,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) as DWORD,
|
||||
// This is a pointer to a pointer
|
||||
&mut buf_addr as *mut LPCWSTR as *mut _,
|
||||
0,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
buf_addr
|
||||
};
|
||||
if !buf_addr.is_null() {
|
||||
let buf_len = lstrlenW(buf_addr) as usize;
|
||||
let buf_slice = std::slice::from_raw_parts(buf_addr, buf_len);
|
||||
let string = wchar_to_string(buf_slice);
|
||||
LocalFree(buf_addr as *mut _);
|
||||
return Some(string);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
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