mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
1632 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1dad450ee | ||
|
|
8d66df7f6f | ||
|
|
1c5fcf3309 | ||
|
|
58c89c1ffc | ||
|
|
2dae807c4f | ||
|
|
8b6c8ef323 | ||
|
|
2e63493776 | ||
|
|
d2acea95cc | ||
|
|
454dc56a6e | ||
|
|
016fd47d0d | ||
|
|
8ce5f6ea41 | ||
|
|
64084c5cf0 | ||
|
|
96d29ab26c | ||
|
|
3e42fa364c | ||
|
|
cb855b87cc | ||
|
|
9c033ce101 | ||
|
|
54ad02e4b9 | ||
|
|
00fe65630e | ||
|
|
961b675d34 | ||
|
|
bcd2fba4a0 | ||
|
|
0135fe51ae | ||
|
|
ed600e415a | ||
|
|
ad892c6949 | ||
|
|
a6b25643ad | ||
|
|
bc5b6ff8ce | ||
|
|
ba6254bc25 | ||
|
|
bd2d2760f0 | ||
|
|
3de08204d3 | ||
|
|
c9030f06c0 | ||
|
|
3035546b17 | ||
|
|
57cb3126d6 | ||
|
|
221b2e71cd | ||
|
|
0dc376a9ef | ||
|
|
85052c09bb | ||
|
|
be63581654 | ||
|
|
45b5f3b031 | ||
|
|
0270516067 | ||
|
|
a90cd1c9ad | ||
|
|
dbeeaeffd9 | ||
|
|
c4bfbbe417 | ||
|
|
978ec7dfec | ||
|
|
87f44ecffb | ||
|
|
da82971f52 | ||
|
|
324dd5fa86 | ||
|
|
fdedda38d2 | ||
|
|
cf0a533461 | ||
|
|
017ff26e7d | ||
|
|
6eb79f04c8 | ||
|
|
2bf12c74dc | ||
|
|
2998bbf7db | ||
|
|
3f82a6a90d | ||
|
|
2e610111b0 | ||
|
|
63d52aae32 | ||
|
|
5b4f97edac | ||
|
|
9135eb4024 | ||
|
|
23b3c127fd | ||
|
|
b343f45500 | ||
|
|
572d61f9ba | ||
|
|
87fc19826b | ||
|
|
11d1b7a980 | ||
|
|
5ca810ba8f | ||
|
|
2d1607b3f7 | ||
|
|
a32e232020 | ||
|
|
9b03bb7276 | ||
|
|
e39596151c | ||
|
|
5289b4f206 | ||
|
|
380dc4c451 | ||
|
|
6fbdbce6dd | ||
|
|
cafcaa2cdc | ||
|
|
e00204e626 | ||
|
|
a5b89bfe5a | ||
|
|
44052a093e | ||
|
|
40cee238e2 | ||
|
|
3dc5c42387 | ||
|
|
8c4a6ddcb4 | ||
|
|
5011a67f6d | ||
|
|
d621ab5018 | ||
|
|
966c033a6c | ||
|
|
1681410ca8 | ||
|
|
a82327c73f | ||
|
|
e71f765dea | ||
|
|
0738528931 | ||
|
|
8119c72d64 | ||
|
|
43f29f0481 | ||
|
|
266219f27f | ||
|
|
7449534ba2 | ||
|
|
7aa202b872 | ||
|
|
06cec065d4 | ||
|
|
f968e64ac8 | ||
|
|
a97309690e | ||
|
|
dec45ce0ff | ||
|
|
f709ac667f | ||
|
|
ecbe04caa7 | ||
|
|
7103514ae8 | ||
|
|
8b5aa33a88 | ||
|
|
7a3b486965 | ||
|
|
fc9c78cb56 | ||
|
|
525219716c | ||
|
|
7f851fe433 | ||
|
|
5dea2a4734 | ||
|
|
821fc63a9c | ||
|
|
8e9a3d2dd3 | ||
|
|
70a77b8534 | ||
|
|
a5bb6d67f7 | ||
|
|
33a2e4cebd | ||
|
|
0ee26986d8 | ||
|
|
ec41dddd0d | ||
|
|
7a872903a4 | ||
|
|
d82886bddc | ||
|
|
08edda1b0b | ||
|
|
7de33bca40 | ||
|
|
40ba9a7ce7 | ||
|
|
0656c54c3b | ||
|
|
74fcf7f9c0 | ||
|
|
0bc8f5e33a | ||
|
|
f6cc6c1472 | ||
|
|
20384d2f02 | ||
|
|
cdee616812 | ||
|
|
f58fb69446 | ||
|
|
6b445219c1 | ||
|
|
18b8569161 | ||
|
|
08b0464ac3 | ||
|
|
df2f5adfba | ||
|
|
99f86d729f | ||
|
|
d06deeecf6 | ||
|
|
e6d2fd7287 | ||
|
|
f2edd23542 | ||
|
|
70e6ddd210 | ||
|
|
f3fb27c17b | ||
|
|
75b463a368 | ||
|
|
ea8604e175 | ||
|
|
b1bd0f77fb | ||
|
|
1fded249d0 | ||
|
|
349a3e7b8c | ||
|
|
f2d277e599 | ||
|
|
8d5d612456 | ||
|
|
5788319632 | ||
|
|
976023bfc0 | ||
|
|
0f9b95814e | ||
|
|
112dcc808a | ||
|
|
4a381fb1db | ||
|
|
8339ddf368 | ||
|
|
b41f01c990 | ||
|
|
570f3101e5 | ||
|
|
3923c59fd8 | ||
|
|
75ae402a24 | ||
|
|
4385c17cbb | ||
|
|
3af256260e | ||
|
|
d9363219e1 | ||
|
|
a52a6d47ca | ||
|
|
ec83de3938 | ||
|
|
43d6eac871 | ||
|
|
1f101b2654 | ||
|
|
709929fcab | ||
|
|
220a2d32d5 | ||
|
|
c5cef46060 | ||
|
|
367a2ae057 | ||
|
|
e038597e81 | ||
|
|
27cd20739d | ||
|
|
8d9fd3d3d6 | ||
|
|
56427e47a7 | ||
|
|
c744b9aea5 | ||
|
|
ef9ed71f1b | ||
|
|
dda8053bd3 | ||
|
|
779212da33 | ||
|
|
28552c9cc1 | ||
|
|
48647b506f | ||
|
|
42243ce288 | ||
|
|
84d9bfd59e | ||
|
|
cd5c1fb724 | ||
|
|
8455f3415e | ||
|
|
c59d6bc809 | ||
|
|
4681133eca | ||
|
|
b278aa859f | ||
|
|
ee4ec43cf3 | ||
|
|
25b629f117 | ||
|
|
4b30f9ce22 | ||
|
|
2428224c09 | ||
|
|
2d9b852a95 | ||
|
|
246d53d5a1 | ||
|
|
865afd22be | ||
|
|
05130cb329 | ||
|
|
a1a6f7baf9 | ||
|
|
f160a6003c | ||
|
|
a24d092fa1 | ||
|
|
a8a0462c0d | ||
|
|
647c320ca7 | ||
|
|
5144337253 | ||
|
|
7f1aaa652d | ||
|
|
00b5de0a68 | ||
|
|
80d1e49354 | ||
|
|
07dd45f8e3 | ||
|
|
4e6ce00ec5 | ||
|
|
65c2482d74 | ||
|
|
ba2bfd064f | ||
|
|
08ad3f19e2 | ||
|
|
e3fbfd6792 | ||
|
|
c40af0062b | ||
|
|
511bf53889 | ||
|
|
7451c4b88c | ||
|
|
42ecef7b31 | ||
|
|
5d9ce7f5f4 | ||
|
|
ef5b71d658 | ||
|
|
4ab36f336c | ||
|
|
2791cbd65e | ||
|
|
03bf83f45e | ||
|
|
02870202cb | ||
|
|
c268922def | ||
|
|
61b921c466 | ||
|
|
794d0c1f73 | ||
|
|
8ce58c7053 | ||
|
|
cff9b01052 | ||
|
|
7e9dc147d8 | ||
|
|
d7827b36d3 | ||
|
|
5b90a4e194 | ||
|
|
281077a0d8 | ||
|
|
d21395bb3f | ||
|
|
f69616ac2c | ||
|
|
645b1ff00f | ||
|
|
3925281652 | ||
|
|
3bf0fa9ec8 | ||
|
|
7de2bc7ae6 | ||
|
|
3f44eb1fd9 | ||
|
|
456c735bfe | ||
|
|
973e6ad400 | ||
|
|
07652c76fb | ||
|
|
7d93c34e42 | ||
|
|
a2e1a0ac19 | ||
|
|
f1a64b3155 | ||
|
|
f8ffa314d0 | ||
|
|
e28974bc04 | ||
|
|
93f5f1ac3c | ||
|
|
a02c680a87 | ||
|
|
6bb62d0b13 | ||
|
|
fae4cbd2aa | ||
|
|
7a954c7e08 | ||
|
|
164dce2b8a | ||
|
|
0ba4283c29 | ||
|
|
62b4ba8b50 | ||
|
|
afebe2e7d1 | ||
|
|
0efcfaf5a9 | ||
|
|
d86ce9de9f | ||
|
|
7fa7cea700 | ||
|
|
36ebad3246 | ||
|
|
912c45e9f7 | ||
|
|
e2c71a4422 | ||
|
|
b50d9a0228 | ||
|
|
692f15c49f | ||
|
|
5366694db2 | ||
|
|
7962271faa | ||
|
|
78b5f2feb8 | ||
|
|
4baab2d93e | ||
|
|
79385ecd1f | ||
|
|
8d18043a3c | ||
|
|
297c3f80eb | ||
|
|
1d80005b91 | ||
|
|
fab0f62c5a | ||
|
|
d83188befd | ||
|
|
b9d89e97ed | ||
|
|
5fa4b8f003 | ||
|
|
7a4ce631bd | ||
|
|
8d5f82f0c0 | ||
|
|
08fe32eac3 | ||
|
|
1cddc96a0b | ||
|
|
84ef89eb1c | ||
|
|
a320702a71 | ||
|
|
b0106898f7 | ||
|
|
924f3323b5 | ||
|
|
b97df599c5 | ||
|
|
0defd747c8 | ||
|
|
e23186db8e | ||
|
|
059abb06fc | ||
|
|
bc216b8f67 | ||
|
|
864a1d5924 | ||
|
|
05444628e6 | ||
|
|
66ff52b012 | ||
|
|
7929999c1c | ||
|
|
7094a223af | ||
|
|
b2a46d0439 | ||
|
|
4748890935 | ||
|
|
6300cf915e | ||
|
|
9a9c9b15ba | ||
|
|
7ce86c3d2a | ||
|
|
a444637b18 | ||
|
|
f0d88c52a3 | ||
|
|
e17977d7c7 | ||
|
|
a7a8ff0bbb | ||
|
|
fc046add78 | ||
|
|
ab4a4a89e6 | ||
|
|
f7a400ddf6 | ||
|
|
07d39abddd | ||
|
|
0c8bf25ae4 | ||
|
|
b5785ba785 | ||
|
|
9797ed86f0 | ||
|
|
e220a75556 | ||
|
|
29d3729ac8 | ||
|
|
4a36741f9c | ||
|
|
ab46aa5b79 | ||
|
|
12fb37d827 | ||
|
|
c88a4ab221 | ||
|
|
8f7f3efc0d | ||
|
|
eb2d3894ef | ||
|
|
3f4f580181 | ||
|
|
d3aeff8838 | ||
|
|
0786d534f4 | ||
|
|
b4b2389d0a | ||
|
|
964e342f69 | ||
|
|
a134a59917 | ||
|
|
fbba203c4a | ||
|
|
61bd8b8254 | ||
|
|
587fa67571 | ||
|
|
7500a88230 | ||
|
|
82d0380ea6 | ||
|
|
642ce2bfa7 | ||
|
|
5bbe87960e | ||
|
|
cf77f82ae3 | ||
|
|
72cf4e577f | ||
|
|
4f3eacf01e | ||
|
|
31ebc5caf4 | ||
|
|
d273518ce9 | ||
|
|
2ade772ab0 | ||
|
|
4ac2006cbc | ||
|
|
ba5ad3be13 | ||
|
|
8092fa2440 | ||
|
|
8bb004a1d9 | ||
|
|
de5327477a | ||
|
|
8f959714cc | ||
|
|
035eebb19a | ||
|
|
b5af6bb266 | ||
|
|
1805124c54 | ||
|
|
0f64589dba | ||
|
|
5438a2a524 | ||
|
|
918430979f | ||
|
|
f3f46cb3f6 | ||
|
|
3c3be71a77 | ||
|
|
a7986b077f | ||
|
|
8a0edde5c8 | ||
|
|
a9e168e10d | ||
|
|
bd9cc2a9da | ||
|
|
596c0edf0f | ||
|
|
92592ec605 | ||
|
|
25c4e2e451 | ||
|
|
ad52c72e41 | ||
|
|
d15feb5cfa | ||
|
|
9938327066 | ||
|
|
f980ed7b83 | ||
|
|
2496098890 | ||
|
|
60e91b187a | ||
|
|
2486f0f1a1 | ||
|
|
fbea75d31f | ||
|
|
d4c9535af9 | ||
|
|
f0fcb346b0 | ||
|
|
77f8e511e9 | ||
|
|
3217eaa416 | ||
|
|
b18295a1ce | ||
|
|
fb9695d56d | ||
|
|
08bdca19b1 | ||
|
|
79ac236721 | ||
|
|
b870a11a99 | ||
|
|
2af1550bbb | ||
|
|
ed796dcd15 | ||
|
|
a006cd7dc8 | ||
|
|
a31f71ee07 | ||
|
|
0f89aac9f6 | ||
|
|
82df9531f4 | ||
|
|
265152355e | ||
|
|
37c0f615cf | ||
|
|
5ba6bdef49 | ||
|
|
69d6076310 | ||
|
|
7029ce6ecd | ||
|
|
1eb1a13a77 | ||
|
|
3fd73848dd | ||
|
|
4e1c46fe9e | ||
|
|
180a4c7a16 | ||
|
|
13613931cf | ||
|
|
483c1d40ae | ||
|
|
1b4045dcb2 | ||
|
|
8f8da0f8bb | ||
|
|
23b821285c | ||
|
|
c984476687 | ||
|
|
42c395e49d | ||
|
|
422c6b1987 | ||
|
|
b457329003 | ||
|
|
930df0ec45 | ||
|
|
e1b7fda409 | ||
|
|
e423802ed3 | ||
|
|
a82f66826b | ||
|
|
0f2fbe373b | ||
|
|
7341ee80ea | ||
|
|
d448d3e14f | ||
|
|
a867032e1e | ||
|
|
b711a11549 | ||
|
|
809162fbd0 | ||
|
|
de782504ab | ||
|
|
1886949efe | ||
|
|
b1a5fae1f5 | ||
|
|
a88d2e079d | ||
|
|
067535eb38 | ||
|
|
7d626d9dfd | ||
|
|
62ce14a013 | ||
|
|
6f60c7a6cc | ||
|
|
6cf0bf76da | ||
|
|
9225b2812e | ||
|
|
08ce3af3e1 | ||
|
|
490abcad14 | ||
|
|
4b22ca8daf | ||
|
|
66ca445caa | ||
|
|
2f52c23fa9 | ||
|
|
ee88e38f13 | ||
|
|
5e77d70245 | ||
|
|
58ec458877 | ||
|
|
94e4c394e7 | ||
|
|
f43ce2a131 | ||
|
|
402cbd55f9 | ||
|
|
da7422c6e1 | ||
|
|
8934d2765d | ||
|
|
89eea64a4a | ||
|
|
9f781bc422 | ||
|
|
4ed4e918f3 | ||
|
|
2e4d79f57a | ||
|
|
bf92f3e97b | ||
|
|
2a58b785fe | ||
|
|
32784af3c4 | ||
|
|
94688a62f0 | ||
|
|
28e34c2e1b | ||
|
|
9ae7498a8a | ||
|
|
1786c877ec | ||
|
|
101ac8908c | ||
|
|
ba4bf03675 | ||
|
|
a63b066ed5 | ||
|
|
f77f858e9b | ||
|
|
6d0cf6a275 | ||
|
|
12df8b6c0c | ||
|
|
65baae75c4 | ||
|
|
418cc44e93 | ||
|
|
ce6c6e8c95 | ||
|
|
bdcbd7d1f9 | ||
|
|
05484c5888 | ||
|
|
50bbc85dc3 | ||
|
|
8669c2e8df | ||
|
|
97d4c7b303 | ||
|
|
08f9e374e0 | ||
|
|
a7a7cc64cd | ||
|
|
04d9e081b8 | ||
|
|
8fc24c959a | ||
|
|
2fb15dbe8a | ||
|
|
92fdf5ba85 | ||
|
|
4f06cfcf5b | ||
|
|
462bb4d324 | ||
|
|
f6ca8515ab | ||
|
|
71094e5703 | ||
|
|
bb0f965c57 | ||
|
|
4d48c76da9 | ||
|
|
fafdedfb7d | ||
|
|
25b129362f | ||
|
|
48b843e42d | ||
|
|
58f2455aa9 | ||
|
|
155f1f9720 | ||
|
|
3b56b0e76f | ||
|
|
5d2aca90bd | ||
|
|
ba49db2cb9 | ||
|
|
a4695c5397 | ||
|
|
92ddb3483e | ||
|
|
fec52b028e | ||
|
|
d8c0ee733b | ||
|
|
fb248eaadc | ||
|
|
da7bf8e29b | ||
|
|
a6a8b12537 | ||
|
|
340f951d10 | ||
|
|
05dd31b8ea | ||
|
|
0fca8b088d | ||
|
|
ab56e9f57d | ||
|
|
97d2aaa953 | ||
|
|
29419d6c38 | ||
|
|
e517e468f8 | ||
|
|
d67c928120 | ||
|
|
112965b4ff | ||
|
|
e0018d0710 | ||
|
|
1ca8b65e85 | ||
|
|
a43a15b4a0 | ||
|
|
66aa6c945d | ||
|
|
dfecdc5762 | ||
|
|
8729119536 | ||
|
|
ec7e935248 | ||
|
|
fd72000a9a | ||
|
|
e91ee811cb | ||
|
|
b3b80166ce | ||
|
|
f9b41fd819 | ||
|
|
0d9c39029c | ||
|
|
da2cef97a3 | ||
|
|
76f158d310 | ||
|
|
2e4338bb8d | ||
|
|
ec2888b8b7 | ||
|
|
fa83bace12 | ||
|
|
ee7dc48e3b | ||
|
|
11d4a301e4 | ||
|
|
ad41eaf151 | ||
|
|
9b71df9f97 | ||
|
|
b1c9e4a6fa | ||
|
|
cdbaf4816a | ||
|
|
6b7ceedc91 | ||
|
|
c53a574bff | ||
|
|
95246d81c1 | ||
|
|
bf537009d9 | ||
|
|
50035643f7 | ||
|
|
64c22f9075 | ||
|
|
4895a29e92 | ||
|
|
6cdb3179c8 | ||
|
|
4fd52af682 | ||
|
|
5a0bad130d | ||
|
|
08d025968e | ||
|
|
1cd0e94c26 | ||
|
|
1ec976f95e | ||
|
|
f10ef5f331 | ||
|
|
653bc59813 | ||
|
|
3e991e13dc | ||
|
|
5397b53e04 | ||
|
|
f09259f6de | ||
|
|
430a49ebc2 | ||
|
|
1091a8ba1a | ||
|
|
9116b6c8cd | ||
|
|
990e34a129 | ||
|
|
472d7b9376 | ||
|
|
50dd7881b1 | ||
|
|
aa8f8db305 | ||
|
|
cdd9b1e1eb | ||
|
|
2d2ce70edc | ||
|
|
78f1d1df38 | ||
|
|
a06bb3f992 | ||
|
|
e289f30e5d | ||
|
|
4b10993970 | ||
|
|
d78a870e66 | ||
|
|
cb41c58f21 | ||
|
|
c55d97183d | ||
|
|
8646cbc9f5 | ||
|
|
64c1d4c5bb | ||
|
|
76b949c196 | ||
|
|
c93ef47b9b | ||
|
|
2b414cd825 | ||
|
|
ac42447459 | ||
|
|
401d20fa1f | ||
|
|
6b5b570b45 | ||
|
|
9e6f666616 | ||
|
|
8ef9fe44c7 | ||
|
|
3e0a544eb8 | ||
|
|
6474891f1e | ||
|
|
40abb526cc | ||
|
|
c532d910c0 | ||
|
|
44288f6280 | ||
|
|
eec84ade86 | ||
|
|
10419ff441 | ||
|
|
57981b533d | ||
|
|
c5eaa0ab69 | ||
|
|
2c01e9e747 | ||
|
|
4c39b3188c | ||
|
|
224872ce03 | ||
|
|
f10a984ba3 | ||
|
|
c7f7181388 | ||
|
|
92530299eb | ||
|
|
58cd23d1ac | ||
|
|
5d85c10a2c | ||
|
|
f11270dac0 | ||
|
|
bcd76d4718 | ||
|
|
4dd2b66aaa | ||
|
|
829a140d9b | ||
|
|
f04fa5d54f | ||
|
|
b4175c1454 | ||
|
|
0728105b2b | ||
|
|
ea09d1d10e | ||
|
|
e7f88588bf | ||
|
|
ce890c3455 | ||
|
|
cbba00d360 | ||
|
|
bf366cb99d | ||
|
|
142d55ff24 | ||
|
|
a58400a82c | ||
|
|
aac28d24ac | ||
|
|
d624a3e648 | ||
|
|
c57294b41a | ||
|
|
485e82dcb1 | ||
|
|
e8d910ffd3 | ||
|
|
ab1f636960 | ||
|
|
52c4670237 | ||
|
|
2ae12fb0a0 | ||
|
|
6c1d3c4fd8 | ||
|
|
08de2b3fc4 | ||
|
|
945a9e3122 | ||
|
|
6e28ba8927 | ||
|
|
7369551c02 | ||
|
|
e22c76b3ac | ||
|
|
a438091266 | ||
|
|
85baf79d17 | ||
|
|
1c68be0631 | ||
|
|
b222dde835 | ||
|
|
78e5a395da | ||
|
|
7846e6a31e | ||
|
|
b7e7755edd | ||
|
|
40f48cbeb4 | ||
|
|
fb8313aa97 | ||
|
|
f9643917d3 | ||
|
|
ac1c9b1218 | ||
|
|
daf0d6b9a7 | ||
|
|
fa14863284 | ||
|
|
cd9ec0afc7 | ||
|
|
f3f6f1008a | ||
|
|
0e52672f4a | ||
|
|
bc1dc1fd63 | ||
|
|
f93f2c158b | ||
|
|
9229e2d88b | ||
|
|
51bb6b751e | ||
|
|
2cc87cab65 | ||
|
|
7cd273ae58 | ||
|
|
001fb7ef60 | ||
|
|
cf4660841a | ||
|
|
a52f755ce8 | ||
|
|
2a2abc4843 | ||
|
|
8af222c1e3 | ||
|
|
d3e6949007 | ||
|
|
a033b25ecb | ||
|
|
39dd30c239 | ||
|
|
c5c99d2357 | ||
|
|
25ff30ee8c | ||
|
|
6b250a74f8 | ||
|
|
5331397c6c | ||
|
|
0b39024133 | ||
|
|
438d286fd5 | ||
|
|
18a61f1058 | ||
|
|
20d012ae3f | ||
|
|
efc54ab8ba | ||
|
|
ea1c031b54 | ||
|
|
11a44081df | ||
|
|
5eb9c9504b | ||
|
|
29a078f65c | ||
|
|
be61ca13fe | ||
|
|
e9d5b2007a | ||
|
|
f2de8475fc | ||
|
|
3ecbea3c39 | ||
|
|
c4df7ad7a5 | ||
|
|
387567a917 | ||
|
|
cfbe8462cc | ||
|
|
5f4df54895 | ||
|
|
ed698f2462 | ||
|
|
b4774861db | ||
|
|
9768f73bb7 | ||
|
|
805249e27e | ||
|
|
5f24c40d05 | ||
|
|
1b3b82a3c1 | ||
|
|
9e72396709 | ||
|
|
125ee0b446 | ||
|
|
3bfb580d7a | ||
|
|
b54d47796d | ||
|
|
b5d0d6ff3e | ||
|
|
c9520deef8 | ||
|
|
ceab0f8c40 | ||
|
|
b87757c552 | ||
|
|
1972eb952d | ||
|
|
f92803d80e | ||
|
|
8afeb910bd | ||
|
|
f16ed98af4 | ||
|
|
2a9916103b | ||
|
|
63ad47a7bf | ||
|
|
27e6548343 | ||
|
|
8c91986dd3 | ||
|
|
5a65347c4e | ||
|
|
635180c8be | ||
|
|
019ce9862f | ||
|
|
c7f46876a7 | ||
|
|
c916eb6137 | ||
|
|
67cca71524 | ||
|
|
1eff7ae004 | ||
|
|
982ad46c83 | ||
|
|
657b4fd59e | ||
|
|
b371b406d5 | ||
|
|
91591c4e94 | ||
|
|
078b9719cc | ||
|
|
41d9826ee9 | ||
|
|
0152508a39 | ||
|
|
cdeb1c3828 | ||
|
|
0986fae066 | ||
|
|
277515636d | ||
|
|
45aacd8407 | ||
|
|
e8cdf8b092 | ||
|
|
1c4d6e7613 | ||
|
|
04b4e48265 | ||
|
|
dabcb1834d | ||
|
|
629cd86c7c | ||
|
|
ba704c4eb4 | ||
|
|
0487876826 | ||
|
|
ca9c05368e | ||
|
|
0d634a0061 | ||
|
|
86748fbc68 | ||
|
|
599477d754 | ||
|
|
889258f538 | ||
|
|
ffe2143d14 | ||
|
|
98470393d1 | ||
|
|
4192d04a53 | ||
|
|
3571dcd68c | ||
|
|
952edcb804 | ||
|
|
10a94c0794 | ||
|
|
dd32ace9ab | ||
|
|
7e0c6ee097 | ||
|
|
b1be34c6a0 | ||
|
|
b9307a9967 | ||
|
|
b1d353180b | ||
|
|
bd99eb1347 | ||
|
|
f79c01b0cf | ||
|
|
3f1e09ec0e | ||
|
|
05125029c6 | ||
|
|
05fe983757 | ||
|
|
d1a7749df5 | ||
|
|
9d63fc7ca0 | ||
|
|
38fccebe1f | ||
|
|
c05952b813 | ||
|
|
932cbe40bf | ||
|
|
39573d65d0 | ||
|
|
6db308f1e9 | ||
|
|
6f70fd90b9 | ||
|
|
db038d943c | ||
|
|
c5620efc9c | ||
|
|
8fb7aa5cef | ||
|
|
6ddee9a8ac | ||
|
|
5700359a61 | ||
|
|
0861a353d6 | ||
|
|
f79efec7ef | ||
|
|
77d5d20391 | ||
|
|
165e51d850 | ||
|
|
1c38f113b3 | ||
|
|
66859607a3 | ||
|
|
edf396b1a4 | ||
|
|
cbeb51b436 | ||
|
|
45e4fd6ec1 | ||
|
|
3a077ff211 | ||
|
|
be850e483a | ||
|
|
33fb62bb25 | ||
|
|
66c117e599 | ||
|
|
8aa1be8336 | ||
|
|
037d4121a1 | ||
|
|
fbd3918d3a | ||
|
|
7c543a43a9 | ||
|
|
ee3996cac6 | ||
|
|
96809ac659 | ||
|
|
6343059bc0 | ||
|
|
5a78fe33e8 | ||
|
|
676fb947f2 | ||
|
|
d18afb4a50 | ||
|
|
fc336a76bf | ||
|
|
b9f3d333e4 | ||
|
|
3d85af04be | ||
|
|
471b1e003a | ||
|
|
be2e17d605 | ||
|
|
9d6b9797c0 | ||
|
|
3cd6a18048 | ||
|
|
c9558c5f0e | ||
|
|
71e3d25422 | ||
|
|
644dc13e00 | ||
|
|
47e7aa4209 | ||
|
|
1c97a310b1 | ||
|
|
d612a1b5a1 | ||
|
|
386ead15a3 | ||
|
|
83c95e774d | ||
|
|
e4754999b7 | ||
|
|
c66489dbb1 | ||
|
|
21f9aefc7e | ||
|
|
d103dc2631 | ||
|
|
cac627ed05 | ||
|
|
e2cf2a5754 | ||
|
|
658a9a4ea8 | ||
|
|
a2db4c0a32 | ||
|
|
02a34a167a | ||
|
|
bea60930b6 | ||
|
|
0f7c82d38f | ||
|
|
6ba583d198 | ||
|
|
89d4c06dec | ||
|
|
9c72cc2a98 | ||
|
|
412bd94ea4 | ||
|
|
514ab043f2 | ||
|
|
68100102be | ||
|
|
05fdcb5b27 | ||
|
|
7a49c88200 | ||
|
|
40232d48ba | ||
|
|
55dff53a98 | ||
|
|
6919c2fb2d | ||
|
|
3d5d05eac7 | ||
|
|
dd866a74a6 | ||
|
|
b1e22aa559 | ||
|
|
2191e9ecd5 | ||
|
|
bf62103417 | ||
|
|
4b1b314ce2 | ||
|
|
c1ea0dde92 | ||
|
|
5a6cfc314e | ||
|
|
a4121a2c2e | ||
|
|
03335cef85 | ||
|
|
ff66bdda7c | ||
|
|
6cfddfea21 | ||
|
|
49bcec1d27 | ||
|
|
878c179761 | ||
|
|
bc19c04339 | ||
|
|
c7a33f926b | ||
|
|
3c38afdb47 | ||
|
|
b8828105cf | ||
|
|
007b195a5e | ||
|
|
b4c6cdf9a3 | ||
|
|
26775fa0b6 | ||
|
|
114fe9d502 | ||
|
|
54bc41f68b | ||
|
|
47ff8d61d1 | ||
|
|
849b8f5dce | ||
|
|
aabe42d252 | ||
|
|
78a62ec547 | ||
|
|
6dae994bb4 | ||
|
|
4c4d0916fd | ||
|
|
d5609729cc | ||
|
|
1f24a09570 | ||
|
|
a8e777a5df | ||
|
|
0bc58f695b | ||
|
|
28023d9f5b | ||
|
|
c2aed1979d | ||
|
|
7e04273719 | ||
|
|
0683bdcd42 | ||
|
|
29ab0bb629 | ||
|
|
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 | ||
|
|
e48f1fc5f1 | ||
|
|
374f131f1e | ||
|
|
363261077f | ||
|
|
102dd07456 | ||
|
|
1e97103094 | ||
|
|
cc8907b956 | ||
|
|
c4b92ebd45 | ||
|
|
fee874b5b7 | ||
|
|
eba888207e | ||
|
|
ea28791da6 | ||
|
|
fe2d37fcdc | ||
|
|
3407a8dd78 | ||
|
|
5761fb6b30 | ||
|
|
7aeb2c083b | ||
|
|
8f47fdbe67 | ||
|
|
2ea42b3947 | ||
|
|
7510b95d8c | ||
|
|
4641433c6a | ||
|
|
eadd9a19b2 | ||
|
|
42f0671531 | ||
|
|
eae7cb247c | ||
|
|
0474dc9861 | ||
|
|
8fd49a4dbe | ||
|
|
10688915eb | ||
|
|
19cd53193b | ||
|
|
2477d8ce46 | ||
|
|
8b0b4ab248 | ||
|
|
24333b806a | ||
|
|
c327960f3e | ||
|
|
bdc01fee1a | ||
|
|
457c0b7208 | ||
|
|
09c809003b | ||
|
|
f08bf44670 | ||
|
|
e36fd1788d | ||
|
|
4005bf11e4 | ||
|
|
9d036a6faa | ||
|
|
591e0d9b8e | ||
|
|
580321b56f | ||
|
|
1c4973d5b7 | ||
|
|
7cd440135a | ||
|
|
f3ab8af813 | ||
|
|
be6d2ed3b9 | ||
|
|
0b922ad9c0 | ||
|
|
b40b14f37f | ||
|
|
7a1946589c | ||
|
|
bbcd3019e8 | ||
|
|
d667a395b6 | ||
|
|
76118af341 | ||
|
|
ce7a426bb5 | ||
|
|
5dcde83b4c | ||
|
|
559681b0ed | ||
|
|
51181b4347 | ||
|
|
e6fefd5e93 | ||
|
|
f3d43016ad | ||
|
|
4c62d71950 | ||
|
|
f279b2f229 | ||
|
|
c62296dc2b | ||
|
|
7daf27f389 | ||
|
|
4abcc164cd | ||
|
|
4cce65274f | ||
|
|
ff17eff00f | ||
|
|
9b5254adeb | ||
|
|
b49abbbf17 | ||
|
|
107a1e7332 | ||
|
|
150d2706f9 | ||
|
|
7d38ed2fab | ||
|
|
1609808e27 | ||
|
|
7e1c70964d | ||
|
|
0e81251f3a | ||
|
|
0ea4f93f05 | ||
|
|
dddd9de151 | ||
|
|
124b16fcde | ||
|
|
198d9ff230 | ||
|
|
23881459bc | ||
|
|
a8d5a9e1ab | ||
|
|
f89dc9e903 | ||
|
|
011720848a | ||
|
|
d92666c188 | ||
|
|
463f316cb8 | ||
|
|
b36a8e010f | ||
|
|
23e4293179 | ||
|
|
8f18dab061 | ||
|
|
9698d0a8d8 | ||
|
|
d18db208ff | ||
|
|
d2dd82c146 | ||
|
|
8348e8225b | ||
|
|
663d615379 | ||
|
|
0f14e63b34 | ||
|
|
fae10c6072 | ||
|
|
cfd087d9a5 | ||
|
|
c61f9b75f8 | ||
|
|
bbf13561b5 | ||
|
|
61d25be3e0 | ||
|
|
37a10e6741 | ||
|
|
62e45fa75d | ||
|
|
01d1178d7b | ||
|
|
b3c5ee6219 | ||
|
|
36058ab8e6 | ||
|
|
159364bec3 | ||
|
|
58181dbff9 | ||
|
|
760e588627 | ||
|
|
d10312c6b1 | ||
|
|
4e4db1749d | ||
|
|
229029f2da | ||
|
|
48902297b7 | ||
|
|
eff3440482 | ||
|
|
80d9a6371d | ||
|
|
3d8c94bae5 | ||
|
|
3f33cd1929 | ||
|
|
1db92063d9 | ||
|
|
5af88d97e8 | ||
|
|
b337d8f99b | ||
|
|
515595153d | ||
|
|
df7e349c70 | ||
|
|
9c116a1bae | ||
|
|
15fbc0dff4 | ||
|
|
6a2a7036d4 | ||
|
|
3edc8e0cd9 | ||
|
|
52a78d61bf | ||
|
|
f81a0741f5 | ||
|
|
192bd798e3 | ||
|
|
52a7b07c79 | ||
|
|
1d0b5bcfbd | ||
|
|
769d4fe897 | ||
|
|
ae7802c8c2 | ||
|
|
c82fcd203f | ||
|
|
59c33d2c6a | ||
|
|
342d5d8587 | ||
|
|
0ada6c15ea | ||
|
|
2a4b381384 | ||
|
|
fe1deb83ae | ||
|
|
5b57b73fe8 | ||
|
|
3d1c18ded9 | ||
|
|
e65cacbc86 | ||
|
|
1b22e39fb2 | ||
|
|
7dc6fcdedc | ||
|
|
f7a8bcddb8 | ||
|
|
60b575a7c1 | ||
|
|
b3ef9c8b22 | ||
|
|
9693f7caa9 | ||
|
|
1382adbf11 | ||
|
|
b35c4a5ee5 | ||
|
|
eff04394c9 | ||
|
|
1d97a2a506 | ||
|
|
a4052b8693 | ||
|
|
17de3f1d15 | ||
|
|
be9d4e7e03 | ||
|
|
3b8de1af34 | ||
|
|
f81d6ddf93 | ||
|
|
200ef9c307 | ||
|
|
63fdc3f903 | ||
|
|
8e3e60b1c3 | ||
|
|
24f5b0b591 | ||
|
|
4202c35786 | ||
|
|
786666aca8 | ||
|
|
657860a233 | ||
|
|
d2034b1700 | ||
|
|
13bd116891 | ||
|
|
a582df443b | ||
|
|
718e0f8551 | ||
|
|
d868510cc8 | ||
|
|
8f03fb7a34 | ||
|
|
a14bae8742 | ||
|
|
c508d68d1d | ||
|
|
15c4641758 | ||
|
|
6820e2a826 | ||
|
|
ed761bef7d | ||
|
|
491bc891e8 | ||
|
|
c57ec33c00 | ||
|
|
28cdce99c0 | ||
|
|
45c5568c89 | ||
|
|
d65d9950f2 | ||
|
|
06e01e4cb3 | ||
|
|
5f00028f6b | ||
|
|
4ef7c71c66 | ||
|
|
3b7dbd01d0 | ||
|
|
8f04d6d4d0 | ||
|
|
3ccee6c2f1 | ||
|
|
772acc742e | ||
|
|
a15b818827 | ||
|
|
32eea41b1b | ||
|
|
cc89d56e43 | ||
|
|
c86cf416d5 | ||
|
|
8d5b23d56c | ||
|
|
28eddb64a9 | ||
|
|
2066909845 | ||
|
|
8add21b04f | ||
|
|
00197b3d04 | ||
|
|
58b800c344 | ||
|
|
506e830cb0 | ||
|
|
2171918023 | ||
|
|
b09e3b2568 | ||
|
|
2e079fe9a2 | ||
|
|
e1e21ded28 | ||
|
|
a1d2ee6ecf | ||
|
|
7a1a2667b1 | ||
|
|
9cd370fa4c | ||
|
|
d6b9faacc9 | ||
|
|
9462a51f32 | ||
|
|
e1fba7d92f | ||
|
|
8e13f85fac | ||
|
|
f2ee78bcf5 | ||
|
|
aea61a74fb | ||
|
|
117beed0b5 | ||
|
|
aad82eb987 | ||
|
|
5ebeb8ab5f | ||
|
|
90a81cca29 | ||
|
|
601599eb0a | ||
|
|
0371b6573f | ||
|
|
ac0d6c890a | ||
|
|
75856e0e39 | ||
|
|
3d9e8da9ec | ||
|
|
e196f80e98 | ||
|
|
ee0dbb8546 | ||
|
|
886eab5c7e | ||
|
|
51036e6ba5 | ||
|
|
61ba6dce7f | ||
|
|
05cd9f2114 | ||
|
|
09d086749d | ||
|
|
9d2a32b415 | ||
|
|
db0f012574 | ||
|
|
3c710de65d | ||
|
|
9a17c2eb86 | ||
|
|
5e5debc48f | ||
|
|
e915454a9d | ||
|
|
66a12d5332 | ||
|
|
3a89843767 | ||
|
|
eaa92a4282 | ||
|
|
789598fa84 | ||
|
|
7a19ef1907 | ||
|
|
9804cad7dd | ||
|
|
a08347eef0 | ||
|
|
fe61d81d41 | ||
|
|
04ccad1dbc | ||
|
|
df1276d72a | ||
|
|
24d6f8da49 | ||
|
|
cd71271f0d | ||
|
|
4b42af910b | ||
|
|
0af3c04900 | ||
|
|
0237526999 | ||
|
|
c5b9bd3612 | ||
|
|
db9e80bdb6 | ||
|
|
f2dd2f0752 | ||
|
|
38856b1c60 | ||
|
|
2b55b2e0ef | ||
|
|
8f0ef514b1 | ||
|
|
647a1727d0 | ||
|
|
339318f295 | ||
|
|
9ca2f83784 | ||
|
|
f6587aed39 | ||
|
|
c8e791b402 | ||
|
|
06a5ec35b3 | ||
|
|
f9f1000d8c | ||
|
|
fdb1c56366 | ||
|
|
1523548d3e | ||
|
|
7298df74bc | ||
|
|
148c751f32 | ||
|
|
96a90d0ec6 | ||
|
|
a17f7328c9 | ||
|
|
b8c6afb96a | ||
|
|
622b3b248e | ||
|
|
4a50fa6c44 | ||
|
|
c2c27c1b37 | ||
|
|
c33bedce5e | ||
|
|
ce8682be2e | ||
|
|
55b5054d50 | ||
|
|
4ec5078bdb | ||
|
|
ad8d2b0470 | ||
|
|
f034017ab2 | ||
|
|
5645941a14 | ||
|
|
4f2515f91e | ||
|
|
46057d7122 | ||
|
|
6c5bc52a5f | ||
|
|
f3db0ba641 | ||
|
|
8288d419fd | ||
|
|
c92ac695af | ||
|
|
8f9f2352cf | ||
|
|
0b75a6b60c | ||
|
|
15aafc2908 | ||
|
|
689d0521f5 | ||
|
|
4087627b12 | ||
|
|
6c99546035 | ||
|
|
c4cdb9aa5c | ||
|
|
22bc119cd7 |
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[alias]
|
||||
run-wasm = ["run", "--release", "--package", "run-wasm", "--"]
|
||||
40
.github/CODEOWNERS
vendored
Normal file
40
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
# Core maintainers:
|
||||
# - @msiglreith
|
||||
# - @kchibisov
|
||||
# - @madsmtm
|
||||
# - @maroider
|
||||
|
||||
# Android
|
||||
/src/platform/android.rs @msiglreith
|
||||
/src/platform_impl/android @msiglreith
|
||||
|
||||
# iOS
|
||||
/src/platform/ios.rs @madsmtm
|
||||
/src/platform_impl/ios @madsmtm
|
||||
|
||||
# Unix
|
||||
/src/platform_impl/linux/mod.rs @kchibisov
|
||||
|
||||
# Wayland
|
||||
/src/platform/wayland.rs @kchibisov
|
||||
/src/platform_impl/linux/wayland @kchibisov
|
||||
|
||||
# X11
|
||||
/src/platform/x11.rs @kchibisov
|
||||
/src/platform_impl/linux/x11 @kchibisov
|
||||
|
||||
# macOS
|
||||
/src/platform/macos.rs @madsmtm
|
||||
/src/platform_impl/macos @madsmtm
|
||||
|
||||
# Web (no maintainer)
|
||||
/src/platform/web.rs @daxpedda
|
||||
/src/platform_impl/web @daxpedda
|
||||
|
||||
# Windows
|
||||
/src/platform/windows.rs @msiglreith
|
||||
/src/platform_impl/windows @msiglreith
|
||||
|
||||
# Orbital (Redox OS)
|
||||
/src/platform/orbital.rs @jackpot51
|
||||
/src/platform_impl/orbital @jackpot51
|
||||
5
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
5
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
- [ ] Tested on all platforms changed
|
||||
- [ ] 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
|
||||
187
.github/workflows/ci.yml
vendored
Normal file
187
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
fmt:
|
||||
name: Check formatting
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
- name: Check Formatting
|
||||
run: cargo fmt -- --check
|
||||
|
||||
tests:
|
||||
name: Test ${{ matrix.toolchain }} ${{ matrix.platform.name }}
|
||||
runs-on: ${{ matrix.platform.os }}
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
toolchain: [stable, nightly, '1.65.0']
|
||||
platform:
|
||||
# Note: Make sure that we test all the `docs.rs` targets defined in Cargo.toml!
|
||||
- { name: 'Windows 64bit MSVC', target: x86_64-pc-windows-msvc, os: windows-latest, }
|
||||
- { name: 'Windows 32bit MSVC', target: i686-pc-windows-msvc, os: windows-latest, }
|
||||
- { name: 'Windows 64bit GNU', target: x86_64-pc-windows-gnu, os: windows-latest, host: -x86_64-pc-windows-gnu }
|
||||
- { name: 'Windows 32bit GNU', target: i686-pc-windows-gnu, os: windows-latest, host: -i686-pc-windows-gnu }
|
||||
- { name: 'Linux 32bit', target: i686-unknown-linux-gnu, os: ubuntu-latest, }
|
||||
- { name: 'Linux 64bit', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, }
|
||||
- { name: 'X11', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=x11' }
|
||||
- { name: 'Wayland', target: x86_64-unknown-linux-gnu, os: ubuntu-latest, options: '--no-default-features --features=wayland,wayland-dlopen' }
|
||||
- { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
|
||||
- { name: 'Redox OS', target: x86_64-unknown-redox, os: ubuntu-latest, }
|
||||
- { name: 'macOS', target: x86_64-apple-darwin, os: macos-latest, }
|
||||
- { name: 'iOS x86_64', target: x86_64-apple-ios, os: macos-latest, }
|
||||
- { name: 'iOS Aarch64', target: aarch64-apple-ios, os: macos-latest, }
|
||||
- { name: 'web', target: wasm32-unknown-unknown, os: ubuntu-latest, }
|
||||
exclude:
|
||||
# Android is tested on stable-3
|
||||
- toolchain: '1.65.0'
|
||||
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
|
||||
include:
|
||||
- toolchain: '1.69.0'
|
||||
platform: { name: 'Android', target: aarch64-linux-android, os: ubuntu-latest, options: '--package=winit --features=android-native-activity', cmd: 'apk --' }
|
||||
|
||||
env:
|
||||
# Set more verbose terminal output
|
||||
CARGO_TERM_VERBOSE: true
|
||||
RUST_BACKTRACE: 1
|
||||
|
||||
# Faster compilation and error on warnings
|
||||
RUSTFLAGS: '--codegen=debuginfo=0 --deny=warnings'
|
||||
RUSTDOCFLAGS: '--deny=warnings'
|
||||
|
||||
OPTIONS: --target=${{ matrix.platform.target }} ${{ matrix.platform.options }}
|
||||
CMD: ${{ matrix.platform.cmd }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Restore cache of cargo folder
|
||||
# We use `restore` and later `save`, so that we can create the key after
|
||||
# the cache has been downloaded.
|
||||
#
|
||||
# This could be avoided if we added Cargo.lock to the repository.
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
# https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci
|
||||
path: |
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
key: cargo-${{ matrix.toolchain }}-${{ matrix.platform.name }}-never-intended-to-be-found
|
||||
restore-keys: cargo-${{ matrix.toolchain }}-${{ matrix.platform.name }}
|
||||
|
||||
- name: Generate lockfile
|
||||
# Also updates the crates.io index
|
||||
run: cargo generate-lockfile && cargo update -p ahash --precise 0.8.7 && cargo update -p bumpalo --precise 3.14.0
|
||||
|
||||
- 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: Cache cargo-apk
|
||||
if: contains(matrix.platform.target, 'android')
|
||||
id: cargo-apk-cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.cargo/bin/cargo-apk
|
||||
# Change this key if we update the required cargo-apk version
|
||||
key: cargo-apk-v0-9-7
|
||||
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
if: contains(matrix.platform.target, 'android') && (steps.cargo-apk-cache.outputs.cache-hit != 'true')
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: Install cargo-apk
|
||||
if: contains(matrix.platform.target, 'android') && (steps.cargo-apk-cache.outputs.cache-hit != 'true')
|
||||
run: cargo install cargo-apk --version=^0.9.7 --locked
|
||||
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ matrix.toolchain }}${{ matrix.platform.host }}
|
||||
targets: ${{ matrix.platform.target }}
|
||||
components: clippy
|
||||
|
||||
- name: Check documentation
|
||||
run: cargo doc --no-deps $OPTIONS --document-private-items
|
||||
|
||||
- name: Build crate
|
||||
run: cargo $CMD build $OPTIONS
|
||||
|
||||
- name: Build tests
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.toolchain != '1.65.0'
|
||||
run: cargo $CMD test --no-run $OPTIONS
|
||||
|
||||
- name: Run tests
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'android') &&
|
||||
!contains(matrix.platform.target, 'ios') &&
|
||||
!contains(matrix.platform.target, 'wasm32') &&
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.toolchain != '1.65.0'
|
||||
run: cargo $CMD test $OPTIONS
|
||||
|
||||
- name: Lint with clippy
|
||||
if: (matrix.toolchain == 'stable') && !contains(matrix.platform.options, '--no-default-features')
|
||||
run: cargo clippy --all-targets $OPTIONS -- -Dwarnings
|
||||
|
||||
- name: Build tests with serde enabled
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.toolchain != '1.65.0'
|
||||
run: cargo $CMD test --no-run $OPTIONS --features serde
|
||||
|
||||
- name: Run tests with serde enabled
|
||||
if: >
|
||||
!contains(matrix.platform.target, 'android') &&
|
||||
!contains(matrix.platform.target, 'ios') &&
|
||||
!contains(matrix.platform.target, 'wasm32') &&
|
||||
!contains(matrix.platform.target, 'redox') &&
|
||||
matrix.toolchain != '1.65.0'
|
||||
run: cargo $CMD test $OPTIONS --features serde
|
||||
|
||||
# See restore step above
|
||||
- name: Save cache of cargo folder
|
||||
uses: actions/cache/save@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry/index/
|
||||
~/.cargo/registry/cache/
|
||||
~/.cargo/git/db/
|
||||
key: cargo-${{ matrix.toolchain }}-${{ matrix.platform.name }}-${{ hashFiles('Cargo.lock') }}
|
||||
|
||||
cargo-deny:
|
||||
name: Run cargo-deny on ${{ matrix.platform.name }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# TODO: remove this matrix when https://github.com/EmbarkStudios/cargo-deny/issues/324 is resolved
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- { name: 'Android', target: aarch64-linux-android }
|
||||
- { name: 'iOS', target: aarch64-apple-ios }
|
||||
- { name: 'Linux', target: x86_64-unknown-linux-gnu }
|
||||
- { name: 'macOS', target: x86_64-apple-darwin }
|
||||
- { name: 'Redox OS', target: x86_64-unknown-redox }
|
||||
- { name: 'web', target: wasm32-unknown-unknown }
|
||||
- { name: 'Windows', target: x86_64-pc-windows-gnu }
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: EmbarkStudios/cargo-deny-action@v1
|
||||
with:
|
||||
command: check
|
||||
log-level: error
|
||||
arguments: --all-features --target ${{ matrix.platform.target }}
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,4 +1,10 @@
|
||||
Cargo.lock
|
||||
target/
|
||||
rls/
|
||||
.vscode/
|
||||
*~
|
||||
*.wasm
|
||||
*.ts
|
||||
*.js
|
||||
#*#
|
||||
.DS_Store
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "deps/apk-builder"]
|
||||
path = deps/apk-builder
|
||||
url = https://github.com/tomaka/android-rs-glue
|
||||
33
.travis.yml
33
.travis.yml
@@ -1,33 +0,0 @@
|
||||
language: rust
|
||||
|
||||
rust:
|
||||
- nightly
|
||||
- stable
|
||||
|
||||
cache: cargo
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libxxf86vm-dev
|
||||
|
||||
install:
|
||||
- |
|
||||
if [ $TRAVIS_OS_NAME = osx ]; then
|
||||
rustup target add x86_64-apple-ios
|
||||
fi
|
||||
|
||||
script:
|
||||
- cargo build --verbose
|
||||
- if [ $TRAVIS_OS_NAME = osx ]; then cargo build --target x86_64-apple-ios --verbose; fi
|
||||
- cargo test --verbose
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
after_success:
|
||||
- |
|
||||
[ $TRAVIS_BRANCH = master ] &&
|
||||
[ $TRAVIS_PULL_REQUEST = false ] &&
|
||||
cargo publish --token ${CRATESIO_TOKEN}
|
||||
1252
CHANGELOG.md
Normal file
1252
CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
71
CONTRIBUTING.md
Normal file
71
CONTRIBUTING.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# 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 a 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:
|
||||
|
||||
- your patch builds with Winit's minimal supported rust version - Rust 1.65.
|
||||
- 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.
|
||||
|
||||
You needn't worry about the added entry causing conflicts, the maintainer that merges the PR will
|
||||
handle those for you when merging (see below).
|
||||
- if your PR affects the platform compatibility of one or more features or adds another feature, the
|
||||
relevant sections in [`FEATURES.md`](https://github.com/rust-windowing/winit/blob/master/FEATURES.md#features)
|
||||
should be updated.
|
||||
|
||||
Once your PR is open, you can ask for a 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).
|
||||
|
||||
Once your PR is deemed ready, the merging maintainer will take care of resolving conflicts in
|
||||
`CHANGELOG.md` (but you must resolve other conflicts yourself). Doing this requires that you check the
|
||||
"give contributors write access to the branch" checkbox when creating the PR.
|
||||
|
||||
## Maintainers & Testers
|
||||
|
||||
The current maintainers are listed in the [CODEOWNERS](.github/CODEOWNERS) file.
|
||||
|
||||
If you are interested in being pinged when testing is needed for a specific platform, please add yourself to the [Testers and Contributors](https://github.com/rust-windowing/winit/wiki/Testers-and-Contributors) table!
|
||||
|
||||
## Release process
|
||||
|
||||
Given that winit is a widely used library, we should be able to make a patch
|
||||
releases at any time we want without blocking the development of new features.
|
||||
|
||||
To achieve these goals, a new branch is created for every new release. Releases and later patch releases are committed and tagged in this branch.
|
||||
|
||||
The exact steps for an exemplary `0.2.0` release might look like this:
|
||||
1. Initially, the version on the latest master is `0.1.0`
|
||||
2. A new `v0.2.x` branch is created for the release
|
||||
3. In the branch, the version is bumped to `v0.2.0`
|
||||
4. The new commit in the branch is tagged `v0.2.0`
|
||||
5. The version is pushed to crates.io
|
||||
6. A GitHub release is created for the `v0.2.0` tag
|
||||
7. On master, the version is bumped to `0.2.0`, and the CHANGELOG is updated
|
||||
|
||||
When doing a patch release, the process is similar:
|
||||
1. Initially, the version of the latest release is `0.2.0`
|
||||
2. Checkout the `v0.2.x` branch
|
||||
3. Cherry-pick the required non-breaking changes into the `v0.2.x`
|
||||
4. Follow steps 3-7 of the regular release example
|
||||
235
Cargo.toml
235
Cargo.toml
@@ -1,43 +1,226 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.6.4"
|
||||
authors = ["The winit contributors, Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
version = "0.29.13"
|
||||
authors = ["The winit contributors", "Pierre Krieger <pierre.krieger1708@gmail.com>"]
|
||||
description = "Cross-platform window creation library."
|
||||
edition = "2021"
|
||||
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"]
|
||||
rust-version = "1.65.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = [
|
||||
"rwh_04",
|
||||
"rwh_05",
|
||||
"rwh_06",
|
||||
"serde",
|
||||
# Enabled to get docs to compile
|
||||
"android-native-activity",
|
||||
]
|
||||
default-target = "x86_64-unknown-linux-gnu"
|
||||
# These are all tested in CI
|
||||
targets = [
|
||||
# Windows
|
||||
"i686-pc-windows-msvc",
|
||||
"x86_64-pc-windows-msvc",
|
||||
# macOS
|
||||
"x86_64-apple-darwin",
|
||||
# Unix (X11 & Wayland)
|
||||
"i686-unknown-linux-gnu",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
# iOS
|
||||
"x86_64-apple-ios",
|
||||
# Android
|
||||
"aarch64-linux-android",
|
||||
# WebAssembly
|
||||
"wasm32-unknown-unknown",
|
||||
]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[features]
|
||||
default = ["rwh_06", "x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"]
|
||||
x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb"]
|
||||
wayland = ["wayland-client", "wayland-backend", "wayland-protocols", "wayland-protocols-plasma", "sctk", "ahash", "memmap2"]
|
||||
wayland-dlopen = ["wayland-backend/dlopen"]
|
||||
wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"]
|
||||
wayland-csd-adwaita-crossfont = ["sctk-adwaita", "sctk-adwaita/crossfont"]
|
||||
wayland-csd-adwaita-notitle = ["sctk-adwaita"]
|
||||
android-native-activity = ["android-activity/native-activity"]
|
||||
android-game-activity = ["android-activity/game-activity"]
|
||||
serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde"]
|
||||
rwh_04 = ["dep:rwh_04", "ndk/rwh_04"]
|
||||
rwh_05 = ["dep:rwh_05", "ndk/rwh_05"]
|
||||
rwh_06 = ["dep:rwh_06", "ndk/rwh_06"]
|
||||
|
||||
[build-dependencies]
|
||||
cfg_aliases = "0.1.1"
|
||||
|
||||
[dependencies]
|
||||
lazy_static = "0.2.2"
|
||||
libc = "0.2"
|
||||
shared_library = "0.1.5"
|
||||
bitflags = "2"
|
||||
cursor-icon = "1.1.0"
|
||||
log = "0.4"
|
||||
mint = { version = "0.5.6", optional = true }
|
||||
once_cell = "1.12"
|
||||
rwh_04 = { package = "raw-window-handle", version = "0.4", optional = true }
|
||||
rwh_05 = { package = "raw-window-handle", version = "0.5.2", features = ["std"], optional = true }
|
||||
rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"], optional = true }
|
||||
serde = { version = "1", optional = true, features = ["serde_derive"] }
|
||||
smol_str = "0.2.0"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies.android_glue]
|
||||
version = "0.2"
|
||||
[dev-dependencies]
|
||||
image = { version = "0.24.0", default-features = false, features = ["png"] }
|
||||
simple_logger = { version = "4.2.0", default_features = false }
|
||||
winit = { path = ".", features = ["rwh_05"] }
|
||||
|
||||
[target.'cfg(target_os = "ios")'.dependencies]
|
||||
objc = "0.2"
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dev-dependencies]
|
||||
softbuffer = "0.3.0"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
android-activity = "0.5.0"
|
||||
ndk = { version = "0.8.0", default-features = false }
|
||||
ndk-sys = "0.5.0"
|
||||
|
||||
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
|
||||
core-foundation = "0.9.3"
|
||||
objc2 = "0.4.1"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
objc = "0.2"
|
||||
cgl = "0.2"
|
||||
cocoa = "=0.5.2"
|
||||
core-foundation = "0.2"
|
||||
core-graphics = "0.4"
|
||||
core-graphics = "0.23.1"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies.icrate]
|
||||
version = "0.0.4"
|
||||
features = [
|
||||
"dispatch",
|
||||
"Foundation",
|
||||
"Foundation_NSArray",
|
||||
"Foundation_NSAttributedString",
|
||||
"Foundation_NSMutableAttributedString",
|
||||
"Foundation_NSData",
|
||||
"Foundation_NSDictionary",
|
||||
"Foundation_NSString",
|
||||
"Foundation_NSProcessInfo",
|
||||
"Foundation_NSThread",
|
||||
"Foundation_NSNumber",
|
||||
]
|
||||
|
||||
[target.'cfg(target_os = "ios")'.dependencies.icrate]
|
||||
version = "0.0.4"
|
||||
features = [
|
||||
"dispatch",
|
||||
"Foundation",
|
||||
"Foundation_NSArray",
|
||||
"Foundation_NSString",
|
||||
"Foundation_NSProcessInfo",
|
||||
"Foundation_NSThread",
|
||||
"Foundation_NSSet",
|
||||
]
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = "0.2"
|
||||
shell32-sys = "0.1"
|
||||
gdi32-sys = "0.1"
|
||||
user32-sys = "~0.1.2"
|
||||
kernel32-sys = "0.2"
|
||||
dwmapi-sys = "0.1"
|
||||
unicode-segmentation = "1.7.1"
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))'.dependencies]
|
||||
wayland-client = { version = "0.8.6", features = ["dlopen"] }
|
||||
wayland-kbd = "0.8.0"
|
||||
wayland-window = "0.5.0"
|
||||
x11-dl = "2.8"
|
||||
[target.'cfg(target_os = "windows")'.dependencies.windows-sys]
|
||||
version = "0.48"
|
||||
features = [
|
||||
"Win32_Devices_HumanInterfaceDevice",
|
||||
"Win32_Foundation",
|
||||
"Win32_Globalization",
|
||||
"Win32_Graphics_Dwm",
|
||||
"Win32_Graphics_Gdi",
|
||||
"Win32_Media",
|
||||
"Win32_System_Com_StructuredStorage",
|
||||
"Win32_System_Com",
|
||||
"Win32_System_LibraryLoader",
|
||||
"Win32_System_Ole",
|
||||
"Win32_System_SystemInformation",
|
||||
"Win32_System_SystemServices",
|
||||
"Win32_System_Threading",
|
||||
"Win32_System_WindowsProgramming",
|
||||
"Win32_UI_Accessibility",
|
||||
"Win32_UI_Controls",
|
||||
"Win32_UI_HiDpi",
|
||||
"Win32_UI_Input_Ime",
|
||||
"Win32_UI_Input_KeyboardAndMouse",
|
||||
"Win32_UI_Input_Pointer",
|
||||
"Win32_UI_Input_Touch",
|
||||
"Win32_UI_Shell",
|
||||
"Win32_UI_TextServices",
|
||||
"Win32_UI_WindowsAndMessaging",
|
||||
]
|
||||
|
||||
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_os = "ios", target_os = "macos"))))'.dependencies]
|
||||
ahash = { version = "0.8.3", features = ["no-rng"], optional = true }
|
||||
bytemuck = { version = "1.13.1", default-features = false, optional = true }
|
||||
calloop = "0.12.3"
|
||||
libc = "0.2.64"
|
||||
memmap2 = { version = "0.9.0", optional = true }
|
||||
percent-encoding = { version = "2.0", optional = true }
|
||||
rustix = { version = "0.38.4", default-features = false, features = ["std", "system", "thread", "process"] }
|
||||
sctk = { package = "smithay-client-toolkit", version = "0.18.0", default-features = false, features = ["calloop"], optional = true }
|
||||
sctk-adwaita = { version = "0.8.0", default_features = false, optional = true }
|
||||
wayland-backend = { version = "0.3.0", default_features = false, features = ["client_system"], optional = true }
|
||||
wayland-client = { version = "0.31.1", optional = true }
|
||||
wayland-protocols = { version = "0.31.0", features = [ "staging"], optional = true }
|
||||
wayland-protocols-plasma = { version = "0.2.0", features = [ "client" ], optional = true }
|
||||
x11-dl = { version = "2.18.5", optional = true }
|
||||
x11rb = { version = "0.13.0", default-features = false, features = ["allow-unsafe-code", "dl-libxcb", "randr", "resource_manager", "xinput", "xkb"], optional = true }
|
||||
xkbcommon-dl = "0.4.2"
|
||||
|
||||
[target.'cfg(target_os = "redox")'.dependencies]
|
||||
orbclient = { version = "0.3.42", default-features = false }
|
||||
redox_syscall = "0.3"
|
||||
|
||||
[target.'cfg(target_family = "wasm")'.dependencies.web_sys]
|
||||
package = "web-sys"
|
||||
version = "0.3.64"
|
||||
features = [
|
||||
'AbortController',
|
||||
'AbortSignal',
|
||||
'console',
|
||||
'CssStyleDeclaration',
|
||||
'Document',
|
||||
'DomRect',
|
||||
'DomRectReadOnly',
|
||||
'Element',
|
||||
'Event',
|
||||
'EventTarget',
|
||||
'FocusEvent',
|
||||
'HtmlCanvasElement',
|
||||
'HtmlElement',
|
||||
'IntersectionObserver',
|
||||
'IntersectionObserverEntry',
|
||||
'KeyboardEvent',
|
||||
'MediaQueryList',
|
||||
'MessageChannel',
|
||||
'MessagePort',
|
||||
'Node',
|
||||
'PageTransitionEvent',
|
||||
'PointerEvent',
|
||||
'ResizeObserver',
|
||||
'ResizeObserverBoxOptions',
|
||||
'ResizeObserverEntry',
|
||||
'ResizeObserverOptions',
|
||||
'ResizeObserverSize',
|
||||
'VisibilityState',
|
||||
'Window',
|
||||
'WheelEvent'
|
||||
]
|
||||
|
||||
[target.'cfg(target_family = "wasm")'.dependencies]
|
||||
atomic-waker = "1"
|
||||
js-sys = "0.3.64"
|
||||
wasm-bindgen = "0.2"
|
||||
wasm-bindgen-futures = "0.4"
|
||||
web-time = "0.2"
|
||||
|
||||
[target.'cfg(target_family = "wasm")'.dev-dependencies]
|
||||
console_log = "1"
|
||||
web-sys = { version = "0.3.22", features = ['CanvasRenderingContext2d'] }
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"run-wasm",
|
||||
]
|
||||
|
||||
248
FEATURES.md
Normal file
248
FEATURES.md
Normal file
@@ -0,0 +1,248 @@
|
||||
# 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 following main graphical platforms:
|
||||
- Desktop
|
||||
- Windows 7+ (10+ is tested regularly)
|
||||
- macOS 10.7+ (10.14+ is tested regularly)
|
||||
- Unix
|
||||
- via X11
|
||||
- via Wayland
|
||||
- Redox OS, via Orbital
|
||||
- Mobile
|
||||
- iOS
|
||||
- Android
|
||||
- Web
|
||||
- Chrome
|
||||
- Firefox
|
||||
- Safari 13.1+
|
||||
|
||||
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 get 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 locking**: Locking the cursor inside the window so it cannot move.
|
||||
- **Cursor confining**: Confining the cursor to the window bounds so it cannot leave them.
|
||||
- **Cursor icon**: Changing the cursor icon or hiding the cursor.
|
||||
- **Cursor hittest**: Handle or ignore mouse events for a window.
|
||||
- **Touch events**: Single-touch events.
|
||||
- **Touch pressure**: Touch events contain information about the amount of force being applied.
|
||||
- **Multitouch**: Multi-touch events, including cancellation of a gesture.
|
||||
- **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 name of the internal window class
|
||||
* 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
|
||||
* Accepts first mouse
|
||||
* Set a preferred theme and get current theme.
|
||||
|
||||
### Unix
|
||||
* Window urgency
|
||||
* X11 Window Class
|
||||
* X11 Override Redirect Flag
|
||||
* GTK Theme Variant
|
||||
* Base window size
|
||||
* Setting the X11 parent window
|
||||
|
||||
### iOS
|
||||
* `winit` has a minimum OS requirement of iOS 8
|
||||
* Get the `UIScreen` object pointer
|
||||
* Setting the `UIView` hidpi factor
|
||||
* Valid orientations
|
||||
* Home indicator visibility
|
||||
* Status bar visibility and style
|
||||
* Deferring system gestures
|
||||
* Getting the device idiom
|
||||
* Getting the preferred video mode
|
||||
|
||||
### Web
|
||||
* Get if the 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 |Web |Redox OS|
|
||||
|-------------------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|
||||
|Window initialization |✔️ |✔️ |▢[#5] |✔️ |▢[#33]|▢[#33] |✔️ |✔️ |
|
||||
|Providing pointer to init OpenGL |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |
|
||||
|Providing pointer to init Vulkan |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |**N/A**|**N/A** |
|
||||
|Window decorations |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|✔️ |
|
||||
|Window decorations toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||
|Window resizing |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |✔️ |
|
||||
|Window resize increments |❌ |✔️ |✔️ |❌ |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||
|Window transparency |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|N/A |✔️ |
|
||||
|Window blur |❌ |❌ |❌ |✔️ |**N/A**|**N/A**|N/A |❌ |
|
||||
|Window maximization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||
|Window maximization toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||
|Window minimization |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||
|Fullscreen |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |✔️ |**N/A** |
|
||||
|Fullscreen toggle |✔️ |✔️ |✔️ |✔️ |**N/A**|✔️ |✔️ |**N/A** |
|
||||
|Exclusive fullscreen |✔️ |✔️ |✔️ |**N/A** |❌ |✔️ |**N/A**|**N/A** |
|
||||
|HiDPI support |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❌ |
|
||||
|Popup windows |❌ |❌ |❌ |❌ |❌ |❌ |**N/A**|**N/A** |
|
||||
|
||||
### System information
|
||||
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |Web |Redox OS|
|
||||
|---------------- | ----- | ---- | ------- | ----------- | ----- | ------- | -------- | ------ |
|
||||
|Monitor list |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**|❌ |
|
||||
|Video mode query |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A**|❌ |
|
||||
|
||||
### Input handling
|
||||
|Feature |Windows |MacOS |Linux x11|Linux Wayland|Android|iOS |Web |Redox OS|
|
||||
|----------------------- | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|
||||
|Mouse events |✔️ |▢[#63] |✔️ |✔️ |**N/A**|**N/A**|✔️ |✔️ |
|
||||
|Mouse set location |✔️ |✔️ |✔️ |✔️(when locked) |**N/A**|**N/A**|**N/A**|**N/A** |
|
||||
|Cursor locking |❌ |✔️ |❌ |✔️ |**N/A**|**N/A**|✔️ |❌ |
|
||||
|Cursor confining |✔️ |❌ |✔️ |✔️ |**N/A**|**N/A**|❌ |❌ |
|
||||
|Cursor icon |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|✔️ |**N/A** |
|
||||
|Cursor hittest |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|❌ |❌ |
|
||||
|Touch events |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |✔️ |**N/A** |
|
||||
|Touch pressure |✔️ |❌ |❌ |❌ |❌ |✔️ |✔️ |**N/A** |
|
||||
|Multitouch |✔️ |❌ |✔️ |✔️ |✔️ |✔️ |❌ |**N/A** |
|
||||
|Keyboard events |✔️ |✔️ |✔️ |✔️ |✔️ |❌ |✔️ |✔️ |
|
||||
|Drag & Drop |▢[#720] |▢[#720] |▢[#720] |▢[#720] |**N/A**|**N/A**|❓ |**N/A** |
|
||||
|Raw Device Events |▢[#750] |▢[#750] |▢[#750] |❌ |❌ |❌ |❓ |**N/A** |
|
||||
|Gamepad/Joystick events |❌[#804] |❌ |❌ |❌ |❌ |❌ |❓ |**N/A** |
|
||||
|Device movement events |❓ |❓ |❓ |❓ |❌ |❌ |❓ |**N/A** |
|
||||
|Drag window with cursor |✔️ |✔️ |✔️ |✔️ |**N/A**|**N/A**|**N/A** |**N/A** |
|
||||
|Resize with cursor |✔️ |❌ |✔️ |✔️ |**N/A**|**N/A**|**N/A** |**N/A** |
|
||||
|
||||
### Pending API Reworks
|
||||
Changes in the API that have been agreed upon but aren't implemented across all platforms.
|
||||
|
||||
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |Web |Redox OS|
|
||||
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|
||||
|New API for HiDPI ([#315] [#319]) |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |❓ |
|
||||
|Event Loop 2.0 ([#459]) |✔️ |✔️ |✔️ |✔️ |✔️ |✔️ |❓ |✔️ |
|
||||
|Keyboard Input 2.0 ([#753]) |✔️ |✔️ |✔️ |✔️ |✔️ |❌ |✔️ |✔️ |
|
||||
|
||||
### Completed API Reworks
|
||||
|Feature |Windows|MacOS |Linux x11|Linux Wayland|Android|iOS |Web |Redox OS|
|
||||
|------------------------------ | ----- | ---- | ------- | ----------- | ----- | ----- | -------- | ------ |
|
||||
|
||||
[#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
|
||||
[#753]: https://github.com/rust-windowing/winit/issues/753
|
||||
[#804]: https://github.com/rust-windowing/winit/issues/804
|
||||
25
HALL_OF_CHAMPIONS.md
Normal file
25
HALL_OF_CHAMPIONS.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# 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 the 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.
|
||||
* [@ArturKovacs]: For consistently maintaining the macOS backend and for his immense involvement in designing and implementing the new keyboard API.
|
||||
|
||||
[@tomaka]: https://github.com/tomaka
|
||||
[@vberger]: https://github.com/vberger
|
||||
[@francesca64]: https://github.com/francesca64
|
||||
[@Osspial]: https://github.com/Osspial
|
||||
[@goddessfreya]: https://github.com/goddessfreya
|
||||
[@ArturKovacs]: https://github.com/ArturKovacs
|
||||
185
README.md
185
README.md
@@ -1,40 +1,187 @@
|
||||
# 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.5"
|
||||
winit = "0.29.13"
|
||||
```
|
||||
|
||||
## [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 [Are we GUI Yet?](https://areweguiyet.com/) and [Are we game yet?](https://arewegameyet.rs/), depending on what kind of project you're looking to do.
|
||||
|
||||
## 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
|
||||
events (for example: the window being resized, a key being pressed, a mouse mouvement, etc.)
|
||||
produced by window.
|
||||
events (for example: the window being resized, a key being pressed, a mouse movement, etc.)
|
||||
produced by the window.
|
||||
|
||||
Winit is designed to be a low-level brick in a hierarchy of libraries. Consequently, in order to
|
||||
show something on the window you need to use the platform-specific getters provided by winit, or
|
||||
another library.
|
||||
|
||||
```rust
|
||||
extern crate winit;
|
||||
### Cargo Features
|
||||
|
||||
fn main() {
|
||||
let window = winit::Window::new().unwrap();
|
||||
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.
|
||||
|
||||
## MSRV Policy
|
||||
|
||||
This crate's Minimum Supported Rust Version (MSRV) is **1.65**. Changes to
|
||||
the MSRV will be accompanied by a minor version bump.
|
||||
|
||||
As a **tentative** policy, the upper bound of the MSRV is given by the following
|
||||
formula:
|
||||
|
||||
for event in window.wait_events() {
|
||||
match event {
|
||||
winit::Event::Closed => break,
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
min(sid, stable - 3)
|
||||
```
|
||||
|
||||
Where `sid` is the current version of `rustc` provided by [Debian Sid], and
|
||||
`stable` is the latest stable version of Rust. This bound may be broken in case of a major ecosystem shift or a security vulnerability.
|
||||
|
||||
[Debian Sid]: https://packages.debian.org/sid/rustc
|
||||
|
||||
The exception is for the Android platform, where a higher Rust version
|
||||
must be used for certain Android features. In this case, the MSRV will be
|
||||
capped at the latest stable version of Rust minus three. This inconsistency is
|
||||
not reflected in Cargo metadata, as it is not powerful enough to expose this
|
||||
restriction.
|
||||
|
||||
All crates in the [`rust-windowing`] organizations have the
|
||||
same MSRV policy.
|
||||
|
||||
[`rust-windowing`]: https://github.com/rust-windowing
|
||||
|
||||
### Platform-specific usage
|
||||
|
||||
#### Wayland
|
||||
|
||||
Note that windows don't appear on Wayland until you draw/present to them.
|
||||
|
||||
#### WebAssembly
|
||||
|
||||
To run the web example: `cargo run-wasm --example web`
|
||||
|
||||
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 the example code using Winit with WebAssembly, check out the [web example]. For
|
||||
information on using Rust on WebAssembly, check out the [Rust and WebAssembly
|
||||
book].
|
||||
|
||||
[web with_canvas]: https://docs.rs/winit/latest/wasm32-unknown-unknown/winit/platform/web/trait.WindowBuilderExtWebSys.html#tymethod.with_canvas
|
||||
[web canvas getter]: https://docs.rs/winit/latest/wasm32-unknown-unknown/winit/platform/web/trait.WindowExtWebSys.html#tymethod.canvas
|
||||
[web example]: ./examples/web.rs
|
||||
[Rust and WebAssembly book]: https://rustwasm.github.io/book/
|
||||
|
||||
#### Android
|
||||
|
||||
The Android backend builds on (and exposes types from) the [`ndk`](https://docs.rs/ndk/latest/ndk/) crate.
|
||||
|
||||
Native Android applications need some form of "glue" crate that is responsible
|
||||
for defining the main entry point for your Rust application as well as tracking
|
||||
various life-cycle events and synchronizing with the main JVM thread.
|
||||
|
||||
Winit uses the [android-activity](https://github.com/rib/android-activity) as a
|
||||
glue crate (prior to `0.28` it used
|
||||
[ndk-glue](https://github.com/rust-windowing/android-ndk-rs/tree/master/ndk-glue)).
|
||||
|
||||
The version of the glue crate that your application depends on _must_ match the
|
||||
version that Winit depends on because the glue crate is responsible for your
|
||||
application's main entry point. If Cargo resolves multiple versions, they will
|
||||
clash.
|
||||
|
||||
`winit` glue compatibility table:
|
||||
|
||||
| winit | ndk-glue |
|
||||
| :---: | :--------------------------: |
|
||||
| 0.29 | `android-activity = "0.5"` |
|
||||
| 0.28 | `android-activity = "0.4"` |
|
||||
| 0.27 | `ndk-glue = "0.7"` |
|
||||
| 0.26 | `ndk-glue = "0.5"` |
|
||||
| 0.25 | `ndk-glue = "0.3"` |
|
||||
| 0.24 | `ndk-glue = "0.2"` |
|
||||
|
||||
The recommended way to avoid a conflict with the glue version is to avoid explicitly
|
||||
depending on the `android-activity` crate, and instead consume the API that
|
||||
is re-exported by Winit under `winit::platform::android::activity::*`
|
||||
|
||||
Running on an Android device needs a dynamic system library. Add this to Cargo.toml:
|
||||
|
||||
```toml
|
||||
[lib]
|
||||
name = "main"
|
||||
crate-type = ["cdylib"]
|
||||
```
|
||||
|
||||
All Android applications are based on an `Activity` subclass, and the
|
||||
`android-activity` crate is designed to support different choices for this base
|
||||
class. Your application _must_ specify the base class it needs via a feature flag:
|
||||
|
||||
| Base Class | Feature Flag | Notes |
|
||||
| :--------------: | :---------------: | :-----: |
|
||||
| `NativeActivity` | `android-native-activity` | Built-in to Android - it is possible to use without compiling any Java or Kotlin code. Java or Kotlin code may be needed to subclass `NativeActivity` to access some platform features. It does not derive from the [`AndroidAppCompat`] base class.|
|
||||
| [`GameActivity`] | `android-game-activity` | Derives from [`AndroidAppCompat`], a defacto standard `Activity` base class that helps support a wider range of Android versions. Requires a build system that can compile Java or Kotlin and fetch Android dependencies from a [Maven repository][agdk_jetpack] (or link with an embedded [release][agdk_releases] of [`GameActivity`]) |
|
||||
|
||||
[`GameActivity`]: https://developer.android.com/games/agdk/game-activity
|
||||
[`GameTextInput`]: https://developer.android.com/games/agdk/add-support-for-text-input
|
||||
[`AndroidAppCompat`]: https://developer.android.com/reference/androidx/appcompat/app/AppCompatActivity
|
||||
[agdk_jetpack]: https://developer.android.com/jetpack/androidx/releases/games
|
||||
[agdk_releases]: https://developer.android.com/games/agdk/download#agdk-libraries
|
||||
[Gradle]: https://developer.android.com/studio/build
|
||||
|
||||
For more details, refer to these `android-activity` [example applications](https://github.com/rust-mobile/android-activity/tree/main/examples).
|
||||
|
||||
##### Converting from `ndk-glue` to `android-activity`
|
||||
|
||||
If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building with `cargo apk`, then the minimal changes would be:
|
||||
1. Remove `ndk-glue` from your `Cargo.toml`
|
||||
2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.29.13", features = [ "android-native-activity" ] }`
|
||||
3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize logging as above).
|
||||
4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your event loop (as shown above).
|
||||
|
||||
#### MacOS
|
||||
|
||||
A lot of functionality expects the application to be ready before you start
|
||||
doing anything; this includes creating windows, fetching monitors, drawing,
|
||||
and so on, see issues [#2238], [#2051] and [#2087].
|
||||
|
||||
If you encounter problems, you should try doing your initialization inside
|
||||
`Event::Resumed`.
|
||||
|
||||
#### iOS
|
||||
|
||||
Similar to macOS, iOS's main `UIApplicationMain` does some init work that's required
|
||||
by all UI-related code (see issue [#1705]). It would be best to consider creating your windows
|
||||
inside `Event::Resumed`.
|
||||
|
||||
|
||||
[#2238]: https://github.com/rust-windowing/winit/issues/2238
|
||||
[#2051]: https://github.com/rust-windowing/winit/issues/2051
|
||||
[#2087]: https://github.com/rust-windowing/winit/issues/2087
|
||||
[#1705]: https://github.com/rust-windowing/winit/issues/1705
|
||||
|
||||
#### Redox OS
|
||||
|
||||
Redox OS has some functionality not yet present that will be implemented when
|
||||
its orbital display server provides it.
|
||||
|
||||
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
|
||||
24
build.rs
Normal file
24
build.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
use cfg_aliases::cfg_aliases;
|
||||
|
||||
fn main() {
|
||||
// The script doesn't depend on our code
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
|
||||
// Setup cfg aliases
|
||||
cfg_aliases! {
|
||||
// Systems.
|
||||
android_platform: { target_os = "android" },
|
||||
wasm_platform: { all(target_family = "wasm", not(target_os = "emscripten")) },
|
||||
macos_platform: { target_os = "macos" },
|
||||
ios_platform: { target_os = "ios" },
|
||||
windows_platform: { target_os = "windows" },
|
||||
apple: { any(target_os = "ios", target_os = "macos") },
|
||||
free_unix: { all(unix, not(apple), not(android_platform), not(target_os = "emscripten")) },
|
||||
redox: { target_os = "redox" },
|
||||
|
||||
// Native displays.
|
||||
x11_platform: { all(feature = "x11", free_unix, not(wasm), not(redox)) },
|
||||
wayland_platform: { all(feature = "wayland", free_unix, not(wasm), not(redox)) },
|
||||
orbital_platform: { redox },
|
||||
}
|
||||
}
|
||||
13
clippy.toml
Normal file
13
clippy.toml
Normal file
@@ -0,0 +1,13 @@
|
||||
disallowed-methods = [
|
||||
{ path = "web_sys::window", reason = "is not available in every context" },
|
||||
{ path = "web_sys::HtmlCanvasElement::width", reason = "Winit shouldn't touch the internal canvas size" },
|
||||
{ path = "web_sys::HtmlCanvasElement::height", reason = "Winit shouldn't touch the internal canvas size" },
|
||||
{ path = "web_sys::HtmlCanvasElement::set_width", reason = "Winit shouldn't touch the internal canvas size" },
|
||||
{ path = "web_sys::HtmlCanvasElement::set_height", reason = "Winit shouldn't touch the internal canvas size" },
|
||||
{ path = "web_sys::Window::document", reason = "cache this to reduce calls to JS" },
|
||||
{ path = "web_sys::Window::get_computed_style", reason = "cache this to reduce calls to JS" },
|
||||
{ path = "web_sys::Element::request_fullscreen", reason = "Doesn't account for compatibility with Safari" },
|
||||
{ path = "web_sys::Document::exit_fullscreen", reason = "Doesn't account for compatibility with Safari" },
|
||||
{ path = "web_sys::Document::fullscreen_element", reason = "Doesn't account for compatibility with Safari" },
|
||||
{ path = "icrate::AppKit::NSView::visibleRect", reason = "We expose a render target to the user, and visibility is not really relevant to that (and can break if you don't use the rectangle position as well). Use `frame` instead." },
|
||||
]
|
||||
64
deny.toml
Normal file
64
deny.toml
Normal file
@@ -0,0 +1,64 @@
|
||||
# https://embarkstudios.github.io/cargo-deny/
|
||||
# cargo install cargo-deny
|
||||
# cargo update && cargo deny --all-features --log-level error --target aarch64-apple-ios check
|
||||
# Note: running just `cargo deny check` without a `--target` will result in
|
||||
# false positives due to https://github.com/EmbarkStudios/cargo-deny/issues/324
|
||||
targets = [
|
||||
{ triple = "aarch64-apple-ios" },
|
||||
{ triple = "aarch64-linux-android" },
|
||||
{ triple = "i686-pc-windows-gnu" },
|
||||
{ triple = "i686-pc-windows-msvc" },
|
||||
{ triple = "i686-unknown-linux-gnu" },
|
||||
{ triple = "wasm32-unknown-unknown" },
|
||||
{ triple = "x86_64-apple-darwin" },
|
||||
{ triple = "x86_64-apple-ios" },
|
||||
{ triple = "x86_64-pc-windows-gnu" },
|
||||
{ triple = "x86_64-pc-windows-msvc" },
|
||||
{ triple = "x86_64-unknown-linux-gnu" },
|
||||
{ triple = "x86_64-unknown-redox" },
|
||||
]
|
||||
|
||||
|
||||
[advisories]
|
||||
vulnerability = "deny"
|
||||
unmaintained = "warn"
|
||||
yanked = "deny"
|
||||
ignore = []
|
||||
|
||||
|
||||
[bans]
|
||||
multiple-versions = "deny"
|
||||
wildcards = "allow" # at least until https://github.com/EmbarkStudios/cargo-deny/issues/241 is fixed
|
||||
deny = []
|
||||
skip = [
|
||||
{ name = "raw-window-handle" }, # we intentionally have multiple versions of this
|
||||
{ name = "bitflags" }, # the ecosystem is in the process of migrating.
|
||||
{ name = "libloading" }, # x11rb uses a different version until the next update
|
||||
{ name = "redox_syscall" }, # https://gitlab.redox-os.org/redox-os/orbclient/-/issues/46
|
||||
]
|
||||
skip-tree = []
|
||||
|
||||
|
||||
[licenses]
|
||||
private = { ignore = true }
|
||||
unlicensed = "deny"
|
||||
allow-osi-fsf-free = "neither"
|
||||
confidence-threshold = 0.92 # We want really high confidence when inferring licenses from text
|
||||
copyleft = "deny"
|
||||
allow = [
|
||||
"Apache-2.0 WITH LLVM-exception", # https://spdx.org/licenses/LLVM-exception.html
|
||||
"Apache-2.0", # https://tldrlegal.com/license/apache-license-2.0-(apache-2.0)
|
||||
"BSD-2-Clause", # https://tldrlegal.com/license/bsd-2-clause-license-(freebsd)
|
||||
"BSD-3-Clause", # https://tldrlegal.com/license/bsd-3-clause-license-(revised)
|
||||
"BSL-1.0", # https://tldrlegal.com/license/boost-software-license-1.0-explained
|
||||
"CC0-1.0", # https://creativecommons.org/publicdomain/zero/1.0/
|
||||
"ISC", # https://tldrlegal.com/license/-isc-license
|
||||
"LicenseRef-UFL-1.0", # https://tldrlegal.com/license/ubuntu-font-license,-1.0 - no official SPDX, see https://github.com/emilk/egui/issues/2321
|
||||
"MIT-0", # https://choosealicense.com/licenses/mit-0/
|
||||
"MIT", # https://tldrlegal.com/license/mit-license
|
||||
"MPL-2.0", # https://www.mozilla.org/en-US/MPL/2.0/FAQ/ - see Q11. Used by webpki-roots on Linux.
|
||||
"OFL-1.1", # https://spdx.org/licenses/OFL-1.1.html
|
||||
"OpenSSL", # https://www.openssl.org/source/license.html - used on Linux
|
||||
"Unicode-DFS-2016", # https://spdx.org/licenses/Unicode-DFS-2016.html
|
||||
"Zlib", # https://tldrlegal.com/license/zlib-libpng-license-(zlib)
|
||||
]
|
||||
11
docs/res/ATTRIBUTION.md
Normal file
11
docs/res/ATTRIBUTION.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Image Attribution
|
||||
|
||||
These images are used in the documentation of `winit`.
|
||||
|
||||
## keyboard_*.svg
|
||||
|
||||
These files are a modified version of "[ANSI US QWERTY (Windows)](https://commons.wikimedia.org/wiki/File:ANSI_US_QWERTY_(Windows).svg)"
|
||||
by [Tomiĉo] (https://commons.wikimedia.org/wiki/User:Tomi%C4%89o). It was
|
||||
originally released under the [CC-BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/deed.en)
|
||||
License. Minor modifications have been made by [John Nunley](https://github.com/notgull),
|
||||
which have been released under the same license as a derivative work.
|
||||
1
docs/res/keyboard_left_shift_key.svg
Normal file
1
docs/res/keyboard_left_shift_key.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 73 KiB |
1
docs/res/keyboard_numpad_1_key.svg
Normal file
1
docs/res/keyboard_numpad_1_key.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 73 KiB |
1
docs/res/keyboard_right_shift_key.svg
Normal file
1
docs/res/keyboard_right_shift_key.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 73 KiB |
1
docs/res/keyboard_standard_1_key.svg
Normal file
1
docs/res/keyboard_standard_1_key.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 73 KiB |
97
examples/child_window.rs
Normal file
97
examples/child_window.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
#[cfg(all(
|
||||
feature = "rwh_06",
|
||||
any(x11_platform, macos_platform, windows_platform)
|
||||
))]
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
#[cfg(all(
|
||||
feature = "rwh_06",
|
||||
any(x11_platform, macos_platform, windows_platform)
|
||||
))]
|
||||
#[allow(deprecated)]
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use winit::{
|
||||
dpi::{LogicalPosition, LogicalSize, Position},
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::{EventLoop, EventLoopWindowTarget},
|
||||
raw_window_handle::HasRawWindowHandle,
|
||||
window::{Window, WindowBuilder, WindowId},
|
||||
};
|
||||
|
||||
fn spawn_child_window(
|
||||
parent: &Window,
|
||||
event_loop: &EventLoopWindowTarget<()>,
|
||||
windows: &mut HashMap<WindowId, Window>,
|
||||
) {
|
||||
let parent = parent.raw_window_handle().unwrap();
|
||||
let mut builder = WindowBuilder::new()
|
||||
.with_title("child window")
|
||||
.with_inner_size(LogicalSize::new(200.0f32, 200.0f32))
|
||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||
.with_visible(true);
|
||||
// `with_parent_window` is unsafe. Parent window must be a valid window.
|
||||
builder = unsafe { builder.with_parent_window(Some(parent)) };
|
||||
let child_window = builder.build(event_loop).unwrap();
|
||||
|
||||
let id = child_window.id();
|
||||
windows.insert(id, child_window);
|
||||
println!("child window created with id: {id:?}");
|
||||
}
|
||||
|
||||
let mut windows = HashMap::new();
|
||||
|
||||
let event_loop: EventLoop<()> = EventLoop::new().unwrap();
|
||||
let parent_window = WindowBuilder::new()
|
||||
.with_title("parent window")
|
||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||
.with_inner_size(LogicalSize::new(640.0f32, 480.0f32))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
println!("parent window: {parent_window:?})");
|
||||
|
||||
event_loop.run(move |event: Event<()>, elwt| {
|
||||
if let Event::WindowEvent { event, window_id } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
windows.clear();
|
||||
elwt.exit();
|
||||
}
|
||||
WindowEvent::CursorEntered { device_id: _ } => {
|
||||
// On x11, println when the cursor entered in a window even if the child window is created
|
||||
// by some key inputs.
|
||||
// the child windows are always placed at (0, 0) with size (200, 200) in the parent window,
|
||||
// so we also can see this log when we move the cursor arround (200, 200) in parent window.
|
||||
println!("cursor entered in the window {window_id:?}");
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
spawn_child_window(&parent_window, elwt, &mut windows);
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let Some(window) = windows.get(&window_id) {
|
||||
fill::fill_window(window);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(all(
|
||||
feature = "rwh_06",
|
||||
any(x11_platform, macos_platform, windows_platform)
|
||||
)))]
|
||||
fn main() {
|
||||
panic!("This example is supported only on x11, macOS, and Windows, with the `rwh_06` feature enabled.");
|
||||
}
|
||||
128
examples/control_flow.rs
Normal file
128
examples/control_flow.rs
Normal file
@@ -0,0 +1,128 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use std::thread;
|
||||
#[cfg(not(wasm_platform))]
|
||||
use std::time;
|
||||
#[cfg(wasm_platform)]
|
||||
use web_time as time;
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
keyboard::{Key, NamedKey},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
#[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() -> Result<(), impl std::error::Error> {
|
||||
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().unwrap();
|
||||
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, elwt| {
|
||||
use winit::event::StartCause;
|
||||
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 {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: key,
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match key.as_ref() {
|
||||
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||
// See the `key_binding` example
|
||||
Key::Character("1") => {
|
||||
mode = Mode::Wait;
|
||||
println!("\nmode: {mode:?}\n");
|
||||
}
|
||||
Key::Character("2") => {
|
||||
mode = Mode::WaitUntil;
|
||||
println!("\nmode: {mode:?}\n");
|
||||
}
|
||||
Key::Character("3") => {
|
||||
mode = Mode::Poll;
|
||||
println!("\nmode: {mode:?}\n");
|
||||
}
|
||||
Key::Character("r") => {
|
||||
request_redraw = !request_redraw;
|
||||
println!("\nrequest_redraw: {request_redraw}\n");
|
||||
}
|
||||
Key::Named(NamedKey::Escape) => {
|
||||
close_requested = true;
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::AboutToWait => {
|
||||
if request_redraw && !wait_cancelled && !close_requested {
|
||||
window.request_redraw();
|
||||
}
|
||||
|
||||
match mode {
|
||||
Mode::Wait => elwt.set_control_flow(ControlFlow::Wait),
|
||||
Mode::WaitUntil => {
|
||||
if !wait_cancelled {
|
||||
elwt.set_control_flow(ControlFlow::WaitUntil(
|
||||
time::Instant::now() + WAIT_TIME,
|
||||
));
|
||||
}
|
||||
}
|
||||
Mode::Poll => {
|
||||
thread::sleep(POLL_SLEEP_TIME);
|
||||
elwt.set_control_flow(ControlFlow::Poll);
|
||||
}
|
||||
};
|
||||
|
||||
if close_requested {
|
||||
elwt.exit();
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,31 +1,88 @@
|
||||
extern crate winit;
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use winit::{Event, ElementState, MouseCursor, WindowEvent};
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::{CursorIcon, WindowBuilder},
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let events_loop = winit::EventsLoop::new();
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
let window = winit::WindowBuilder::new().build(&events_loop).unwrap();
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().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| {
|
||||
match event {
|
||||
Event::WindowEvent { event: WindowEvent::KeyboardInput(ElementState::Pressed, _, _, _), .. } => {
|
||||
println!("Setting cursor to \"{:?}\"", cursors[cursor_idx]);
|
||||
window.set_cursor(cursors[cursor_idx]);
|
||||
if cursor_idx < cursors.len() - 1 {
|
||||
cursor_idx += 1;
|
||||
} else {
|
||||
cursor_idx = 0;
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
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::Closed, .. } => {
|
||||
events_loop.interrupt()
|
||||
},
|
||||
_ => ()
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
WindowEvent::CloseRequested => {
|
||||
elwt.exit();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
const CURSORS: &[CursorIcon] = &[
|
||||
CursorIcon::Default,
|
||||
CursorIcon::Crosshair,
|
||||
CursorIcon::Pointer,
|
||||
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,
|
||||
];
|
||||
|
||||
73
examples/cursor_grab.rs
Normal file
73
examples/cursor_grab.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{DeviceEvent, ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::{Key, ModifiersState, NamedKey},
|
||||
window::{CursorGrabMode, WindowBuilder},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
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, elwt| match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: key,
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
let result = match key {
|
||||
Key::Named(NamedKey::Escape) => {
|
||||
elwt.exit();
|
||||
Ok(())
|
||||
}
|
||||
Key::Character(ch) => match ch.to_lowercase().as_str() {
|
||||
"g" => window.set_cursor_grab(CursorGrabMode::Confined),
|
||||
"l" => window.set_cursor_grab(CursorGrabMode::Locked),
|
||||
"a" => window.set_cursor_grab(CursorGrabMode::None),
|
||||
"h" => {
|
||||
window.set_cursor_visible(modifiers.shift_key());
|
||||
Ok(())
|
||||
}
|
||||
_ => Ok(()),
|
||||
},
|
||||
_ => Ok(()),
|
||||
};
|
||||
|
||||
if let Err(err) = result {
|
||||
println!("error: {err}");
|
||||
}
|
||||
}
|
||||
WindowEvent::ModifiersChanged(new) => modifiers = new.state(),
|
||||
WindowEvent::RedrawRequested => fill::fill_window(&window),
|
||||
_ => (),
|
||||
},
|
||||
Event::DeviceEvent { event, .. } => match event {
|
||||
DeviceEvent::MouseMotion { delta } => println!("mouse moved: {delta:?}"),
|
||||
DeviceEvent::Button { button, state } => match state {
|
||||
ElementState::Pressed => println!("mouse button {button} pressed"),
|
||||
ElementState::Released => println!("mouse button {button} released"),
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
})
|
||||
}
|
||||
62
examples/custom_events.rs
Normal file
62
examples/custom_events.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
#[cfg(not(wasm_platform))]
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoopBuilder,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum CustomEvent {
|
||||
Timer,
|
||||
}
|
||||
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoopBuilder::<CustomEvent>::with_user_event()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
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, elwt| match event {
|
||||
Event::UserEvent(event) => println!("user event: {event:?}"),
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => elwt.exit(),
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::RedrawRequested,
|
||||
..
|
||||
} => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(wasm_platform)]
|
||||
fn main() {
|
||||
panic!("This example is not supported on web.");
|
||||
}
|
||||
105
examples/drag_window.rs
Normal file
105
examples/drag_window.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::Key,
|
||||
window::{Window, WindowBuilder, WindowId},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
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();
|
||||
let mut cursor_location = None;
|
||||
|
||||
event_loop.run(move |event, elwt| 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 => elwt.exit(),
|
||||
WindowEvent::CursorMoved { position, .. } => cursor_location = Some(position),
|
||||
WindowEvent::MouseInput { state, button, .. } => {
|
||||
let window = if (window_id == window_1.id() && switched)
|
||||
|| (window_id == window_2.id() && !switched)
|
||||
{
|
||||
&window_2
|
||||
} else {
|
||||
&window_1
|
||||
};
|
||||
|
||||
match (button, state) {
|
||||
(MouseButton::Left, ElementState::Pressed) => window.drag_window().unwrap(),
|
||||
(MouseButton::Right, ElementState::Released) => {
|
||||
if let Some(position) = cursor_location {
|
||||
window.show_window_menu(position);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
WindowEvent::CursorEntered { .. } => {
|
||||
entered_id = window_id;
|
||||
name_windows(entered_id, switched, &window_1, &window_2)
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Released,
|
||||
logical_key: Key::Character(c),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match c.as_str() {
|
||||
"x" => {
|
||||
switched = !switched;
|
||||
name_windows(entered_id, switched, &window_1, &window_2);
|
||||
println!("Switched!")
|
||||
}
|
||||
"d" => {
|
||||
let window = if (window_id == window_1.id() && switched)
|
||||
|| (window_id == window_2.id() && !switched)
|
||||
{
|
||||
&window_2
|
||||
} else {
|
||||
&window_1
|
||||
};
|
||||
|
||||
window.set_decorations(!window.is_decorated());
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
if window_id == window_1.id() {
|
||||
fill::fill_window(&window_1);
|
||||
} else if window_id == window_2.id() {
|
||||
fill::fill_window(&window_2);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
|
||||
_ => (),
|
||||
})
|
||||
}
|
||||
|
||||
fn name_windows(window_id: WindowId, switched: bool, window_1: &Window, window_2: &Window) {
|
||||
let (drag_target, other) =
|
||||
if (window_id == window_1.id() && switched) || (window_id == window_2.id() && !switched) {
|
||||
(&window_2, &window_1)
|
||||
} else {
|
||||
(&window_1, &window_2)
|
||||
};
|
||||
drag_target.set_title("drag target");
|
||||
other.set_title("winit window");
|
||||
}
|
||||
56
examples/focus.rs
Normal file
56
examples/focus.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
//! Example for focusing a window.
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
#[cfg(not(wasm_platform))]
|
||||
use std::time;
|
||||
#[cfg(wasm_platform)]
|
||||
use web_time as time;
|
||||
use winit::{
|
||||
event::{Event, StartCause, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut deadline = time::Instant::now() + time::Duration::from_secs(3);
|
||||
event_loop.run(move |event, elwt| {
|
||||
match event {
|
||||
Event::NewEvents(StartCause::ResumeTimeReached { .. }) => {
|
||||
// Timeout reached; focus the window.
|
||||
println!("Re-focusing the window.");
|
||||
deadline += time::Duration::from_secs(3);
|
||||
window.focus_window();
|
||||
}
|
||||
Event::WindowEvent { event, window_id } if window_id == window.id() => match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::RedrawRequested => {
|
||||
// Notify the windowing system that we'll be presenting to the window.
|
||||
window.pre_present_notify();
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::AboutToWait => {
|
||||
window.request_redraw();
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
|
||||
elwt.set_control_flow(winit::event_loop::ControlFlow::WaitUntil(deadline));
|
||||
})
|
||||
}
|
||||
@@ -1,46 +1,163 @@
|
||||
extern crate winit;
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use std::io::{self, Write};
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::dpi::PhysicalSize;
|
||||
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::keyboard::{Key, NamedKey};
|
||||
use winit::window::{Fullscreen, WindowBuilder};
|
||||
|
||||
fn main() {
|
||||
// enumerating monitors
|
||||
let monitor = {
|
||||
for (num, monitor) in winit::get_available_monitors().enumerate() {
|
||||
println!("Monitor #{}: {:?}", num, monitor.get_name());
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
use winit::platform::macos::WindowExtMacOS;
|
||||
|
||||
print!("Please write the number of the monitor to use: ");
|
||||
io::stdout().flush().unwrap();
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
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 = winit::get_available_monitors().nth(num).expect("Please enter a valid ID");
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
println!("Using {:?}", monitor.get_name());
|
||||
let mut decorations = true;
|
||||
let mut minimized = false;
|
||||
let mut with_min_size = false;
|
||||
let mut with_max_size = false;
|
||||
|
||||
monitor
|
||||
};
|
||||
|
||||
let events_loop = winit::EventsLoop::new();
|
||||
|
||||
let _window = winit::WindowBuilder::new()
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("Hello world!")
|
||||
.with_fullscreen(monitor)
|
||||
.build(&events_loop)
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
println!("{:?}", event);
|
||||
let mut monitor_index = 0;
|
||||
let mut monitor = event_loop
|
||||
.available_monitors()
|
||||
.next()
|
||||
.expect("no monitor found!");
|
||||
println!("Monitor: {:?}", monitor.name());
|
||||
|
||||
match event {
|
||||
winit::Event::WindowEvent { event, .. } => {
|
||||
match event {
|
||||
winit::WindowEvent::Closed => events_loop.interrupt(),
|
||||
winit::WindowEvent::KeyboardInput(_, _, Some(winit::VirtualKeyCode::Escape), _) => events_loop.interrupt(),
|
||||
_ => ()
|
||||
let mut mode_index = 0;
|
||||
let mut mode = monitor.video_modes().next().expect("no mode found");
|
||||
println!("Mode: {mode}");
|
||||
|
||||
println!("Keys:");
|
||||
println!("- Esc\tExit");
|
||||
println!("- F\tToggle exclusive fullscreen mode");
|
||||
println!("- B\tToggle borderless mode");
|
||||
#[cfg(target_os = "macos")]
|
||||
println!("- C\tToggle simple fullscreen mode");
|
||||
println!("- S\tNext screen");
|
||||
println!("- M\tNext mode for this screen");
|
||||
println!("- D\tToggle window decorations");
|
||||
println!("- X\tMaximize window");
|
||||
println!("- Z\tMinimize window");
|
||||
println!("- I\tToggle mIn size limit");
|
||||
println!("- A\tToggle mAx size limit");
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: key,
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match key {
|
||||
Key::Named(NamedKey::Escape) => elwt.exit(),
|
||||
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||
// See the `key_binding` example
|
||||
Key::Character(ch) => match ch.to_lowercase().as_str() {
|
||||
"f" | "b" if window.fullscreen().is_some() => {
|
||||
window.set_fullscreen(None);
|
||||
}
|
||||
"f" => {
|
||||
let fullscreen = Some(Fullscreen::Exclusive(mode.clone()));
|
||||
println!("Setting mode: {fullscreen:?}");
|
||||
window.set_fullscreen(fullscreen);
|
||||
}
|
||||
"b" => {
|
||||
let fullscreen = Some(Fullscreen::Borderless(Some(monitor.clone())));
|
||||
println!("Setting mode: {fullscreen:?}");
|
||||
window.set_fullscreen(fullscreen);
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
"c" => {
|
||||
window.set_simple_fullscreen(!window.simple_fullscreen());
|
||||
}
|
||||
"s" => {
|
||||
monitor_index += 1;
|
||||
if let Some(mon) = elwt.available_monitors().nth(monitor_index) {
|
||||
monitor = mon;
|
||||
} else {
|
||||
monitor_index = 0;
|
||||
monitor =
|
||||
elwt.available_monitors().next().expect("no monitor found!");
|
||||
}
|
||||
println!("Monitor: {:?}", monitor.name());
|
||||
|
||||
mode_index = 0;
|
||||
mode = monitor.video_modes().next().expect("no mode found");
|
||||
println!("Mode: {mode}");
|
||||
}
|
||||
"m" => {
|
||||
mode_index += 1;
|
||||
if let Some(m) = monitor.video_modes().nth(mode_index) {
|
||||
mode = m;
|
||||
} else {
|
||||
mode_index = 0;
|
||||
mode = monitor.video_modes().next().expect("no mode found");
|
||||
}
|
||||
println!("Mode: {mode}");
|
||||
}
|
||||
"d" => {
|
||||
decorations = !decorations;
|
||||
window.set_decorations(decorations);
|
||||
}
|
||||
"x" => {
|
||||
let is_maximized = window.is_maximized();
|
||||
window.set_maximized(!is_maximized);
|
||||
}
|
||||
"z" => {
|
||||
minimized = !minimized;
|
||||
window.set_minimized(minimized);
|
||||
}
|
||||
"i" => {
|
||||
with_min_size = !with_min_size;
|
||||
let min_size = if with_min_size {
|
||||
Some(PhysicalSize::new(100, 100))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
window.set_min_inner_size(min_size);
|
||||
eprintln!(
|
||||
"Min: {with_min_size}: {min_size:?} => {:?}",
|
||||
window.inner_size()
|
||||
);
|
||||
}
|
||||
"a" => {
|
||||
with_max_size = !with_max_size;
|
||||
let max_size = if with_max_size {
|
||||
Some(PhysicalSize::new(200, 200))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
window.set_max_inner_size(max_size);
|
||||
eprintln!(
|
||||
"Max: {with_max_size}: {max_size:?} => {:?}",
|
||||
window.inner_size()
|
||||
);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
extern crate winit;
|
||||
|
||||
use winit::{WindowEvent, ElementState};
|
||||
|
||||
fn main() {
|
||||
let 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(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::Closed => events_loop.interrupt(),
|
||||
|
||||
a @ WindowEvent::MouseMoved(_, _) => {
|
||||
println!("{:?}", a);
|
||||
},
|
||||
|
||||
_ => (),
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
86
examples/handling_close.rs
Normal file
86
examples/handling_close.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::Key,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("Your faithful window")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut close_requested = false;
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = 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;
|
||||
|
||||
// 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.
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: key,
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||
// See the `key_binding` example
|
||||
match key.as_ref() {
|
||||
Key::Character("y") => {
|
||||
if close_requested {
|
||||
// This is where you'll want to do any cleanup you need.
|
||||
println!("Buh-bye!");
|
||||
|
||||
// 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.
|
||||
elwt.exit();
|
||||
}
|
||||
}
|
||||
Key::Character("n") => {
|
||||
if close_requested {
|
||||
println!("Your window will continue to stay by your side.");
|
||||
close_requested = false;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
BIN
examples/icon.png
Normal file
BIN
examples/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
94
examples/ime.rs
Normal file
94
examples/ime.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use log::LevelFilter;
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
event::{ElementState, Event, Ime, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::NamedKey,
|
||||
window::{ImePurpose, WindowBuilder},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new()
|
||||
.with_level(LevelFilter::Trace)
|
||||
.init()
|
||||
.unwrap();
|
||||
|
||||
println!("IME position will system default");
|
||||
println!("Click to set IME position to cursor's");
|
||||
println!("Press F2 to toggle IME. See the documentation of `set_ime_allowed` for more info");
|
||||
println!("Press F3 to cycle through IME purposes.");
|
||||
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(256f64, 128f64))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut ime_purpose = ImePurpose::Normal;
|
||||
let mut ime_allowed = true;
|
||||
window.set_ime_allowed(ime_allowed);
|
||||
|
||||
let mut may_show_ime = false;
|
||||
let mut cursor_position = PhysicalPosition::new(0.0, 0.0);
|
||||
let mut ime_pos = PhysicalPosition::new(0.0, 0.0);
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
cursor_position = position;
|
||||
}
|
||||
WindowEvent::MouseInput {
|
||||
state: ElementState::Released,
|
||||
..
|
||||
} => {
|
||||
println!(
|
||||
"Setting ime position to {}, {}",
|
||||
cursor_position.x, cursor_position.y
|
||||
);
|
||||
ime_pos = cursor_position;
|
||||
if may_show_ime {
|
||||
window.set_ime_cursor_area(ime_pos, PhysicalSize::new(10, 10));
|
||||
}
|
||||
}
|
||||
WindowEvent::Ime(event) => {
|
||||
println!("{event:?}");
|
||||
may_show_ime = event != Ime::Disabled;
|
||||
if may_show_ime {
|
||||
window.set_ime_cursor_area(ime_pos, PhysicalSize::new(10, 10));
|
||||
}
|
||||
}
|
||||
WindowEvent::KeyboardInput { event, .. } => {
|
||||
println!("key: {event:?}");
|
||||
|
||||
if event.state == ElementState::Pressed && event.logical_key == NamedKey::F2 {
|
||||
ime_allowed = !ime_allowed;
|
||||
window.set_ime_allowed(ime_allowed);
|
||||
println!("\nIME allowed: {ime_allowed}\n");
|
||||
}
|
||||
if event.state == ElementState::Pressed && event.logical_key == NamedKey::F3 {
|
||||
ime_purpose = match ime_purpose {
|
||||
ImePurpose::Normal => ImePurpose::Password,
|
||||
ImePurpose::Password => ImePurpose::Terminal,
|
||||
_ => ImePurpose::Normal,
|
||||
};
|
||||
window.set_ime_purpose(ime_purpose);
|
||||
println!("\nIME purpose: {ime_purpose:?}\n");
|
||||
}
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
62
examples/key_binding.rs
Normal file
62
examples/key_binding.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))]
|
||||
use winit::{
|
||||
dpi::LogicalSize,
|
||||
event::{ElementState, Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::{Key, ModifiersState},
|
||||
// WARNING: This is not available on all platforms (for example on the web).
|
||||
platform::modifier_supplement::KeyEventExtModifierSupplement,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))]
|
||||
fn main() {
|
||||
println!("This example is not supported on this platform");
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))]
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
simple_logger::SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_inner_size(LogicalSize::new(400.0, 200.0))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut modifiers = ModifiersState::default();
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::ModifiersChanged(new) => {
|
||||
modifiers = new.state();
|
||||
}
|
||||
WindowEvent::KeyboardInput { event, .. } => {
|
||||
if event.state == ElementState::Pressed && !event.repeat {
|
||||
match event.key_without_modifiers().as_ref() {
|
||||
Key::Character("1") => {
|
||||
if modifiers.shift_key() {
|
||||
println!("Shift + 1 | logical_key: {:?}", event.logical_key);
|
||||
} else {
|
||||
println!("1");
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
};
|
||||
})
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
extern crate winit;
|
||||
|
||||
fn main() {
|
||||
let events_loop = winit::EventsLoop::new();
|
||||
|
||||
let _window = winit::WindowBuilder::new()
|
||||
.with_min_dimensions(400, 200)
|
||||
.with_max_dimensions(800, 400)
|
||||
.build(&events_loop)
|
||||
.unwrap();
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
println!("{:?}", event);
|
||||
|
||||
match event {
|
||||
winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => events_loop.interrupt(),
|
||||
_ => ()
|
||||
}
|
||||
});
|
||||
}
|
||||
58
examples/monitor_list.rs
Normal file
58
examples/monitor_list.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use winit::monitor::MonitorHandle;
|
||||
use winit::{event_loop::EventLoop, window::WindowBuilder};
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||
|
||||
if let Some(mon) = window.primary_monitor() {
|
||||
print_info("Primary output", mon);
|
||||
}
|
||||
|
||||
for mon in window.available_monitors() {
|
||||
if Some(&mon) == window.primary_monitor().as_ref() {
|
||||
continue;
|
||||
}
|
||||
|
||||
println!();
|
||||
print_info("Output", mon);
|
||||
}
|
||||
}
|
||||
|
||||
fn print_info(intro: &str, monitor: MonitorHandle) {
|
||||
if let Some(name) = monitor.name() {
|
||||
println!("{intro}: {name}");
|
||||
} else {
|
||||
println!("{intro}: [no name]");
|
||||
}
|
||||
|
||||
let PhysicalSize { width, height } = monitor.size();
|
||||
print!(" Current mode: {width}x{height}");
|
||||
if let Some(m_hz) = monitor.refresh_rate_millihertz() {
|
||||
println!(" @ {}.{} Hz", m_hz / 1000, m_hz % 1000);
|
||||
} else {
|
||||
println!();
|
||||
}
|
||||
|
||||
let PhysicalPosition { x, y } = monitor.position();
|
||||
println!(" Position: {x},{y}");
|
||||
|
||||
println!(" Scale factor: {}", monitor.scale_factor());
|
||||
|
||||
println!(" Available modes (width x height x bit-depth):");
|
||||
for mode in monitor.video_modes() {
|
||||
let PhysicalSize { width, height } = mode.size();
|
||||
let bits = mode.bit_depth();
|
||||
let m_hz = mode.refresh_rate_millihertz();
|
||||
println!(
|
||||
" {width}x{height}x{bits} @ {}.{} Hz",
|
||||
m_hz / 1000,
|
||||
m_hz % 1000
|
||||
);
|
||||
}
|
||||
}
|
||||
65
examples/mouse_wheel.rs
Normal file
65
examples/mouse_wheel.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("Mouse Wheel events")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
println!(
|
||||
r"
|
||||
When using so called 'natural scrolling' (scrolling that acts like on a touch screen), this is what to expect:
|
||||
|
||||
Moving your finger downwards on a scroll wheel should make the window move down, and you should see a positive Y scroll value.
|
||||
|
||||
When moving fingers on a trackpad down and to the right, you should see positive X and Y deltas, and the window should move down and to the right.
|
||||
|
||||
With reverse scrolling, you should see the inverse behavior.
|
||||
|
||||
In both cases the example window should move like the content of a scroll area in any other application.
|
||||
|
||||
In other words, the deltas indicate the direction in which to move the content (in this case the window)."
|
||||
);
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::MouseWheel { delta, .. } => match delta {
|
||||
winit::event::MouseScrollDelta::LineDelta(x, y) => {
|
||||
println!("mouse wheel Line Delta: ({x},{y})");
|
||||
let pixels_per_line = 120.0;
|
||||
let mut pos = window.outer_position().unwrap();
|
||||
pos.x += (x * pixels_per_line) as i32;
|
||||
pos.y += (y * pixels_per_line) as i32;
|
||||
window.set_outer_position(pos)
|
||||
}
|
||||
winit::event::MouseScrollDelta::PixelDelta(p) => {
|
||||
println!("mouse wheel Pixel Delta: ({},{})", p.x, p.y);
|
||||
let mut pos = window.outer_position().unwrap();
|
||||
pos.x += p.x as i32;
|
||||
pos.y += p.y as i32;
|
||||
window.set_outer_position(pos)
|
||||
}
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
209
examples/multithreaded.rs
Normal file
209
examples/multithreaded.rs
Normal file
@@ -0,0 +1,209 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
#[cfg(not(wasm_platform))]
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
use std::{collections::HashMap, sync::mpsc, thread, time::Duration};
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::{Key, ModifiersState, NamedKey},
|
||||
window::{CursorGrabMode, CursorIcon, Fullscreen, WindowBuilder, WindowLevel},
|
||||
};
|
||||
|
||||
const WINDOW_COUNT: usize = 3;
|
||||
const WINDOW_SIZE: PhysicalSize<u32> = PhysicalSize::new(600, 400);
|
||||
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
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);
|
||||
let mut modifiers = ModifiersState::default();
|
||||
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()
|
||||
);
|
||||
}
|
||||
}
|
||||
WindowEvent::ModifiersChanged(new) => {
|
||||
modifiers = new.state();
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Released,
|
||||
logical_key: key,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
use NamedKey::{ArrowLeft, ArrowRight};
|
||||
window.set_title(&format!("{key:?}"));
|
||||
let state = !modifiers.shift_key();
|
||||
match key {
|
||||
// Cycle through video modes
|
||||
Key::Named(ArrowRight) | Key::Named(ArrowLeft) => {
|
||||
if key == ArrowLeft {
|
||||
video_mode_id = video_mode_id.saturating_sub(1);
|
||||
} else if key == ArrowRight {
|
||||
video_mode_id = (video_modes.len() - 1).min(video_mode_id + 1);
|
||||
}
|
||||
println!("Picking video mode: {}", video_modes[video_mode_id]);
|
||||
}
|
||||
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||
// See the `key_binding` example
|
||||
Key::Character(ch) => match ch.to_lowercase().as_str() {
|
||||
"1" => window.set_window_level(WindowLevel::AlwaysOnTop),
|
||||
"2" => window.set_window_level(WindowLevel::AlwaysOnBottom),
|
||||
"3" => window.set_window_level(WindowLevel::Normal),
|
||||
"c" => window.set_cursor_icon(match state {
|
||||
true => CursorIcon::Progress,
|
||||
false => CursorIcon::Default,
|
||||
}),
|
||||
"d" => window.set_decorations(!state),
|
||||
"f" => window.set_fullscreen(match (state, modifiers.alt_key()) {
|
||||
(true, false) => Some(Fullscreen::Borderless(None)),
|
||||
(true, true) => Some(Fullscreen::Exclusive(
|
||||
video_modes[video_mode_id].clone(),
|
||||
)),
|
||||
(false, _) => None,
|
||||
}),
|
||||
"l" if state => {
|
||||
if let Err(err) = window.set_cursor_grab(CursorGrabMode::Locked)
|
||||
{
|
||||
println!("error: {err}");
|
||||
}
|
||||
}
|
||||
"g" if state => {
|
||||
if let Err(err) =
|
||||
window.set_cursor_grab(CursorGrabMode::Confined)
|
||||
{
|
||||
println!("error: {err}");
|
||||
}
|
||||
}
|
||||
"g" | "l" if !state => {
|
||||
if let Err(err) = window.set_cursor_grab(CursorGrabMode::None) {
|
||||
println!("error: {err}");
|
||||
}
|
||||
}
|
||||
"h" => window.set_cursor_visible(!state),
|
||||
"i" => {
|
||||
println!("Info:");
|
||||
println!("-> outer_position : {:?}", window.outer_position());
|
||||
println!("-> inner_position : {:?}", window.inner_position());
|
||||
println!("-> outer_size : {:?}", window.outer_size());
|
||||
println!("-> inner_size : {:?}", window.inner_size());
|
||||
println!("-> fullscreen : {:?}", window.fullscreen());
|
||||
}
|
||||
"l" => window.set_min_inner_size(match state {
|
||||
true => Some(WINDOW_SIZE),
|
||||
false => None,
|
||||
}),
|
||||
"m" => window.set_maximized(state),
|
||||
"p" => window.set_outer_position({
|
||||
let mut position = window.outer_position().unwrap();
|
||||
let sign = if state { 1 } else { -1 };
|
||||
position.x += 10 * sign;
|
||||
position.y += 10 * sign;
|
||||
position
|
||||
}),
|
||||
"q" => window.request_redraw(),
|
||||
"r" => window.set_resizable(state),
|
||||
"s" => {
|
||||
let _ = window.request_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, elwt| {
|
||||
if window_senders.is_empty() {
|
||||
elwt.exit()
|
||||
}
|
||||
match event {
|
||||
Event::WindowEvent { event, window_id } => match event {
|
||||
WindowEvent::CloseRequested
|
||||
| WindowEvent::Destroyed
|
||||
| WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Released,
|
||||
logical_key: Key::Named(NamedKey::Escape),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
window_senders.remove(&window_id);
|
||||
}
|
||||
_ => {
|
||||
if let Some(tx) = window_senders.get(&window_id) {
|
||||
tx.send(event).unwrap();
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(wasm_platform)]
|
||||
fn main() {
|
||||
panic!("Example not supported on Wasm");
|
||||
}
|
||||
@@ -1,33 +1,64 @@
|
||||
extern crate winit;
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
fn main() {
|
||||
let events_loop = winit::EventsLoop::new();
|
||||
use std::collections::HashMap;
|
||||
|
||||
let window1 = winit::Window::new(&events_loop).unwrap();
|
||||
let window2 = winit::Window::new(&events_loop).unwrap();
|
||||
let window3 = winit::Window::new(&events_loop).unwrap();
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::{Key, NamedKey},
|
||||
window::Window,
|
||||
};
|
||||
|
||||
let mut num_windows = 3;
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
events_loop.run_forever(|event| {
|
||||
match event {
|
||||
winit::Event::WindowEvent { event: winit::WindowEvent::Closed, window_id } => {
|
||||
if window_id == window1.id() {
|
||||
println!("Window 1 has been closed")
|
||||
} else if window_id == window2.id() {
|
||||
println!("Window 2 has been closed")
|
||||
} else if window_id == window3.id() {
|
||||
println!("Window 3 has been closed");
|
||||
} else {
|
||||
unreachable!()
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let mut windows = HashMap::new();
|
||||
for _ in 0..3 {
|
||||
let window = Window::new(&event_loop).unwrap();
|
||||
println!("Opened a new window: {:?}", window.id());
|
||||
windows.insert(window.id(), window);
|
||||
}
|
||||
|
||||
println!("Press N to open a new window.");
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, window_id } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
println!("Window {window_id:?} has received the signal to close");
|
||||
|
||||
// This drops the window, causing it to close.
|
||||
windows.remove(&window_id);
|
||||
|
||||
if windows.is_empty() {
|
||||
elwt.exit();
|
||||
}
|
||||
}
|
||||
|
||||
num_windows -= 1;
|
||||
if num_windows == 0 {
|
||||
events_loop.interrupt();
|
||||
WindowEvent::KeyboardInput {
|
||||
event,
|
||||
is_synthetic: false,
|
||||
..
|
||||
} if event.state == ElementState::Pressed => match event.logical_key {
|
||||
Key::Named(NamedKey::Escape) => elwt.exit(),
|
||||
Key::Character(c) if c == "n" || c == "N" => {
|
||||
let window = Window::new(elwt).unwrap();
|
||||
println!("Opened a new window: {:?}", window.id());
|
||||
windows.insert(window.id(), window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let Some(window) = windows.get(&window_id) {
|
||||
fill::fill_window(window);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
42
examples/request_redraw.rs
Normal file
42
examples/request_redraw.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
println!("{event:?}");
|
||||
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::MouseInput {
|
||||
state: ElementState::Released,
|
||||
..
|
||||
} => {
|
||||
window.request_redraw();
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
println!("\nredrawing!\n");
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
59
examples/request_redraw_threaded.rs
Normal file
59
examples/request_redraw_threaded.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
#[cfg(not(wasm_platform))]
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
use std::{sync::Arc, thread, time};
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = {
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
Arc::new(window)
|
||||
};
|
||||
|
||||
thread::spawn({
|
||||
let window = window.clone();
|
||||
move || loop {
|
||||
thread::sleep(time::Duration::from_secs(1));
|
||||
window.request_redraw();
|
||||
}
|
||||
});
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
println!("{event:?}");
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => elwt.exit(),
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::RedrawRequested,
|
||||
..
|
||||
} => {
|
||||
println!("\nredrawing!\n");
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(wasm_platform)]
|
||||
fn main() {
|
||||
unimplemented!() // `Window` can't be sent between threads
|
||||
}
|
||||
54
examples/resizable.rs
Normal file
54
examples/resizable.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
dpi::LogicalSize,
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::{KeyCode, PhysicalKey},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let mut resizable = false;
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("Hit space to toggle resizability.")
|
||||
.with_inner_size(LogicalSize::new(600.0, 300.0))
|
||||
.with_min_inner_size(LogicalSize::new(400.0, 200.0))
|
||||
.with_max_inner_size(LogicalSize::new(800.0, 400.0))
|
||||
.with_resizable(resizable)
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
physical_key: PhysicalKey::Code(KeyCode::Space),
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
resizable = !resizable;
|
||||
println!("Resizable: {resizable}");
|
||||
window.set_resizable(resizable);
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
};
|
||||
})
|
||||
}
|
||||
116
examples/startup_notification.rs
Normal file
116
examples/startup_notification.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
//! Demonstrates the use of startup notifications on Linux.
|
||||
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
#[path = "./util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
mod example {
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::platform::startup_notify::{
|
||||
EventLoopExtStartupNotify, WindowBuilderExtStartupNotify, WindowExtStartupNotify,
|
||||
};
|
||||
use winit::window::{Window, WindowBuilder, WindowId};
|
||||
|
||||
pub(super) fn main() -> Result<(), impl std::error::Error> {
|
||||
// Create the event loop and get the activation token.
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
let mut current_token = match event_loop.read_token_from_env() {
|
||||
Some(token) => Some(token),
|
||||
None => {
|
||||
println!("No startup notification token found in environment.");
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let mut windows: HashMap<WindowId, Rc<Window>> = HashMap::new();
|
||||
let mut counter = 0;
|
||||
let mut create_first_window = false;
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
match event {
|
||||
Event::Resumed => create_first_window = true,
|
||||
|
||||
Event::WindowEvent { window_id, event } => match event {
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key,
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
if logical_key == "n" {
|
||||
if let Some(window) = windows.get(&window_id) {
|
||||
// Request a new activation token on this window.
|
||||
// Once we get it we will use it to create a window.
|
||||
window
|
||||
.request_activation_token()
|
||||
.expect("Failed to request activation token.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WindowEvent::CloseRequested => {
|
||||
// Remove the window from the map.
|
||||
windows.remove(&window_id);
|
||||
if windows.is_empty() {
|
||||
elwt.exit();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
WindowEvent::ActivationTokenDone { token, .. } => {
|
||||
current_token = Some(token);
|
||||
}
|
||||
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let Some(window) = windows.get(&window_id) {
|
||||
super::fill::fill_window(window);
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
// See if we've passed the deadline.
|
||||
if current_token.is_some() || create_first_window {
|
||||
// Create the initial window.
|
||||
let window = {
|
||||
let mut builder =
|
||||
WindowBuilder::new().with_title(format!("Window {}", counter));
|
||||
|
||||
if let Some(token) = current_token.take() {
|
||||
println!("Creating a window with token {token:?}");
|
||||
builder = builder.with_activation_token(token);
|
||||
}
|
||||
|
||||
Rc::new(builder.build(elwt).unwrap())
|
||||
};
|
||||
|
||||
// Add the window to the map.
|
||||
windows.insert(window.id(), window.clone());
|
||||
|
||||
counter += 1;
|
||||
create_first_window = false;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
example::main()
|
||||
}
|
||||
|
||||
#[cfg(not(any(x11_platform, wayland_platform)))]
|
||||
fn main() {
|
||||
println!("This example is only supported on X11 and Wayland platforms.");
|
||||
}
|
||||
68
examples/theme.rs
Normal file
68
examples/theme.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::Key,
|
||||
window::{Theme, WindowBuilder},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.with_theme(Some(Theme::Dark))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
println!("Initial theme: {:?}", window.theme());
|
||||
println!("debugging keys:");
|
||||
println!(" (A) Automatic theme");
|
||||
println!(" (L) Light theme");
|
||||
println!(" (D) Dark theme");
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { window_id, event } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::ThemeChanged(theme) if window_id == window.id() => {
|
||||
println!("Theme is changed: {theme:?}")
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: key,
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match key.as_ref() {
|
||||
Key::Character("A" | "a") => {
|
||||
println!("Theme was: {:?}", window.theme());
|
||||
window.set_theme(None);
|
||||
}
|
||||
Key::Character("L" | "l") => {
|
||||
println!("Theme was: {:?}", window.theme());
|
||||
window.set_theme(Some(Theme::Light));
|
||||
}
|
||||
Key::Character("D" | "d") => {
|
||||
println!("Theme was: {:?}", window.theme());
|
||||
window.set_theme(Some(Theme::Dark));
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
println!("\nredrawing!\n");
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
54
examples/timer.rs
Normal file
54
examples/timer.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use std::time::Duration;
|
||||
#[cfg(not(wasm_platform))]
|
||||
use std::time::Instant;
|
||||
#[cfg(wasm_platform)]
|
||||
use web_time::Instant;
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, StartCause, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
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, elwt| {
|
||||
println!("{event:?}");
|
||||
|
||||
match event {
|
||||
Event::NewEvents(StartCause::Init) => {
|
||||
elwt.set_control_flow(ControlFlow::WaitUntil(Instant::now() + timer_length));
|
||||
}
|
||||
Event::NewEvents(StartCause::ResumeTimeReached { .. }) => {
|
||||
elwt.set_control_flow(ControlFlow::WaitUntil(Instant::now() + timer_length));
|
||||
println!("\nTimer\n");
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => elwt.exit(),
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::RedrawRequested,
|
||||
..
|
||||
} => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
}
|
||||
50
examples/touchpad_gestures.rs
Normal file
50
examples/touchpad_gestures.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("Touchpad gestures")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
println!("Only supported on macOS at the moment.");
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::TouchpadMagnify { delta, .. } => {
|
||||
if delta > 0.0 {
|
||||
println!("Zoomed in {delta}");
|
||||
} else {
|
||||
println!("Zoomed out {delta}");
|
||||
}
|
||||
}
|
||||
WindowEvent::SmartMagnify { .. } => {
|
||||
println!("Smart zoom");
|
||||
}
|
||||
WindowEvent::TouchpadRotate { delta, .. } => {
|
||||
if delta > 0.0 {
|
||||
println!("Rotated counterclockwise {delta}");
|
||||
} else {
|
||||
println!("Rotated clockwise {delta}");
|
||||
}
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,20 +1,38 @@
|
||||
extern crate winit;
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
fn main() {
|
||||
let events_loop = winit::EventsLoop::new();
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
let window = winit::WindowBuilder::new().with_decorations(false)
|
||||
.with_transparency(true)
|
||||
.build(&events_loop).unwrap();
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().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| {
|
||||
println!("{:?}", event);
|
||||
event_loop.run(move |event, elwt| {
|
||||
println!("{event:?}");
|
||||
|
||||
match event {
|
||||
winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => events_loop.interrupt(),
|
||||
_ => ()
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
116
examples/util/fill.rs
Normal file
116
examples/util/fill.rs
Normal file
@@ -0,0 +1,116 @@
|
||||
//! Fill the window buffer with a solid color.
|
||||
//!
|
||||
//! Launching a window without drawing to it has unpredictable results varying from platform to
|
||||
//! platform. In order to have well-defined examples, this module provides an easy way to
|
||||
//! fill the window buffer with a solid color.
|
||||
//!
|
||||
//! The `softbuffer` crate is used, largely because of its ease of use. `glutin` or `wgpu` could
|
||||
//! also be used to fill the window buffer, but they are more complicated to use.
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub use platform::cleanup_window;
|
||||
pub use platform::fill_window;
|
||||
|
||||
#[cfg(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios"))))]
|
||||
mod platform {
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
use softbuffer::{Context, Surface};
|
||||
use winit::window::Window;
|
||||
use winit::window::WindowId;
|
||||
|
||||
thread_local! {
|
||||
// NOTE: You should never do things like that, create context and drop it before
|
||||
// you drop the event loop. We do this for brevity to not blow up examples. We use
|
||||
// ManuallyDrop to prevent destructors from running.
|
||||
//
|
||||
// A static, thread-local map of graphics contexts to open windows.
|
||||
static GC: ManuallyDrop<RefCell<Option<GraphicsContext>>> = const { ManuallyDrop::new(RefCell::new(None)) };
|
||||
}
|
||||
|
||||
/// The graphics context used to draw to a window.
|
||||
struct GraphicsContext {
|
||||
/// The global softbuffer context.
|
||||
context: Context,
|
||||
|
||||
/// The hash map of window IDs to surfaces.
|
||||
surfaces: HashMap<WindowId, Surface>,
|
||||
}
|
||||
|
||||
impl GraphicsContext {
|
||||
fn new(w: &Window) -> Self {
|
||||
Self {
|
||||
context: unsafe { Context::new(w) }.expect("Failed to create a softbuffer context"),
|
||||
surfaces: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn create_surface(&mut self, window: &Window) -> &mut Surface {
|
||||
self.surfaces.entry(window.id()).or_insert_with(|| {
|
||||
unsafe { Surface::new(&self.context, window) }
|
||||
.expect("Failed to create a softbuffer surface")
|
||||
})
|
||||
}
|
||||
|
||||
fn destroy_surface(&mut self, window: &Window) {
|
||||
self.surfaces.remove(&window.id());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fill_window(window: &Window) {
|
||||
GC.with(|gc| {
|
||||
let size = window.inner_size();
|
||||
let (Some(width), Some(height)) =
|
||||
(NonZeroU32::new(size.width), NonZeroU32::new(size.height))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Either get the last context used or create a new one.
|
||||
let mut gc = gc.borrow_mut();
|
||||
let surface = gc
|
||||
.get_or_insert_with(|| GraphicsContext::new(window))
|
||||
.create_surface(window);
|
||||
|
||||
// Fill a buffer with a solid color.
|
||||
const DARK_GRAY: u32 = 0xFF181818;
|
||||
|
||||
surface
|
||||
.resize(width, height)
|
||||
.expect("Failed to resize the softbuffer surface");
|
||||
|
||||
let mut buffer = surface
|
||||
.buffer_mut()
|
||||
.expect("Failed to get the softbuffer buffer");
|
||||
buffer.fill(DARK_GRAY);
|
||||
buffer
|
||||
.present()
|
||||
.expect("Failed to present the softbuffer buffer");
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn cleanup_window(window: &Window) {
|
||||
GC.with(|gc| {
|
||||
let mut gc = gc.borrow_mut();
|
||||
if let Some(context) = gc.as_mut() {
|
||||
context.destroy_surface(window);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(all(feature = "rwh_05", not(any(target_os = "android", target_os = "ios")))))]
|
||||
mod platform {
|
||||
pub fn fill_window(_window: &winit::window::Window) {
|
||||
// No-op on mobile platforms.
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn cleanup_window(_window: &winit::window::Window) {
|
||||
// No-op on mobile platforms.
|
||||
}
|
||||
}
|
||||
22
examples/video_modes.rs
Normal file
22
examples/video_modes.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::event_loop::EventLoop;
|
||||
|
||||
fn main() {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
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}");
|
||||
}
|
||||
}
|
||||
147
examples/web.rs
Normal file
147
examples/web.rs
Normal file
@@ -0,0 +1,147 @@
|
||||
#![allow(clippy::disallowed_methods, clippy::single_match)]
|
||||
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::Key,
|
||||
window::{Fullscreen, WindowBuilder},
|
||||
};
|
||||
|
||||
pub fn main() -> Result<(), impl std::error::Error> {
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let builder = WindowBuilder::new().with_title("A fantastic window!");
|
||||
#[cfg(wasm_platform)]
|
||||
let builder = {
|
||||
use winit::platform::web::WindowBuilderExtWebSys;
|
||||
builder.with_append(true)
|
||||
};
|
||||
let window = builder.build(&event_loop).unwrap();
|
||||
|
||||
#[cfg(wasm_platform)]
|
||||
let log_list = wasm::insert_canvas_and_create_log_list(&window);
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
#[cfg(wasm_platform)]
|
||||
wasm::log_event(&log_list, &event);
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => elwt.exit(),
|
||||
Event::AboutToWait => {
|
||||
window.request_redraw();
|
||||
}
|
||||
Event::WindowEvent {
|
||||
window_id,
|
||||
event:
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: Key::Character(c),
|
||||
state: ElementState::Released,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
} if window_id == window.id() && c == "f" => {
|
||||
if window.fullscreen().is_some() {
|
||||
window.set_fullscreen(None);
|
||||
} else {
|
||||
window.set_fullscreen(Some(Fullscreen::Borderless(None)));
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(wasm_platform)]
|
||||
mod wasm {
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
use softbuffer::{Surface, SurfaceExtWeb};
|
||||
use wasm_bindgen::prelude::*;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
window::Window,
|
||||
};
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn run() {
|
||||
console_log::init_with_level(log::Level::Debug).expect("error initializing logger");
|
||||
|
||||
#[allow(clippy::main_recursion)]
|
||||
let _ = super::main();
|
||||
}
|
||||
|
||||
pub fn insert_canvas_and_create_log_list(window: &Window) -> web_sys::Element {
|
||||
use winit::platform::web::WindowExtWebSys;
|
||||
|
||||
let canvas = window.canvas().unwrap();
|
||||
let mut surface = Surface::from_canvas(canvas.clone()).unwrap();
|
||||
surface
|
||||
.resize(
|
||||
NonZeroU32::new(canvas.width()).unwrap(),
|
||||
NonZeroU32::new(canvas.height()).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
let mut buffer = surface.buffer_mut().unwrap();
|
||||
buffer.fill(0xFFF0000);
|
||||
buffer.present().unwrap();
|
||||
|
||||
let window = web_sys::window().unwrap();
|
||||
let document = window.document().unwrap();
|
||||
let body = document.body().unwrap();
|
||||
|
||||
let style = &canvas.style();
|
||||
style.set_property("margin", "50px").unwrap();
|
||||
// Use to test interactions with border and padding.
|
||||
//style.set_property("border", "50px solid black").unwrap();
|
||||
//style.set_property("padding", "50px").unwrap();
|
||||
|
||||
let log_header = document.create_element("h2").unwrap();
|
||||
log_header.set_text_content(Some("Event Log"));
|
||||
body.append_child(&log_header).unwrap();
|
||||
|
||||
let log_list = document.create_element("ul").unwrap();
|
||||
body.append_child(&log_list).unwrap();
|
||||
log_list
|
||||
}
|
||||
|
||||
pub fn log_event(log_list: &web_sys::Element, event: &Event<()>) {
|
||||
log::debug!("{:?}", event);
|
||||
|
||||
// Getting access to browser logs requires a lot of setup on mobile devices.
|
||||
// So we implement this basic logging system into the page to give developers an easy alternative.
|
||||
// As a bonus its also kind of handy on desktop.
|
||||
let event = match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::RedrawRequested,
|
||||
..
|
||||
} => None,
|
||||
Event::WindowEvent { event, .. } => Some(format!("{event:?}")),
|
||||
Event::Resumed | Event::Suspended => Some(format!("{event:?}")),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(event) = event {
|
||||
let window = web_sys::window().unwrap();
|
||||
let document = window.document().unwrap();
|
||||
let log = document.create_element("li").unwrap();
|
||||
|
||||
let date = js_sys::Date::new_0();
|
||||
log.set_text_content(Some(&format!(
|
||||
"{:02}:{:02}:{:02}.{:03}: {event}",
|
||||
date.get_hours(),
|
||||
date.get_minutes(),
|
||||
date.get_seconds(),
|
||||
date.get_milliseconds(),
|
||||
)));
|
||||
|
||||
log_list
|
||||
.insert_before(&log, log_list.first_child().as_ref())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
101
examples/web_aspect_ratio.rs
Normal file
101
examples/web_aspect_ratio.rs
Normal file
@@ -0,0 +1,101 @@
|
||||
#![allow(clippy::disallowed_methods)]
|
||||
|
||||
pub fn main() {
|
||||
println!("This example must be run with cargo run-wasm --example web_aspect_ratio")
|
||||
}
|
||||
|
||||
#[cfg(wasm_platform)]
|
||||
mod wasm {
|
||||
use wasm_bindgen::prelude::*;
|
||||
use web_sys::HtmlCanvasElement;
|
||||
use winit::{
|
||||
dpi::PhysicalSize,
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
platform::web::WindowBuilderExtWebSys,
|
||||
window::{Window, WindowBuilder},
|
||||
};
|
||||
|
||||
const EXPLANATION: &str = "
|
||||
This example draws a circle in the middle of a 4/1 aspect ratio canvas which acts as a useful demonstration of winit's resize handling on web.
|
||||
Even when the browser window is resized or aspect-ratio of the canvas changed the circle should always:
|
||||
* Fill the entire width or height of the canvas (whichever is smaller) without exceeding it.
|
||||
* Be perfectly round
|
||||
* Not be blurry or pixelated (there is no antialiasing so you may still see jagged edges depending on the DPI of your monitor)
|
||||
|
||||
Currently winit does not handle resizes on web so the circle is rendered incorrectly.
|
||||
This example demonstrates the desired future functionality which will possibly be provided by https://github.com/rust-windowing/winit/pull/2074
|
||||
";
|
||||
|
||||
#[wasm_bindgen(start)]
|
||||
pub fn run() {
|
||||
console_log::init_with_level(log::Level::Debug).expect("error initializing logger");
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
// When running in a non-wasm environment this would set the window size to 100x100.
|
||||
// However in this example it just sets a default initial size of 100x100 that is immediately overwritten due to the layout + styling of the page.
|
||||
.with_inner_size(PhysicalSize::new(100, 100))
|
||||
.with_append(true)
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let canvas = create_canvas(&window);
|
||||
|
||||
// Render once with the size info we currently have
|
||||
render_circle(&canvas, window.inner_size());
|
||||
|
||||
let _ = event_loop.run(move |event, _| match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::Resized(resize),
|
||||
window_id,
|
||||
} if window_id == window.id() => {
|
||||
render_circle(&canvas, resize);
|
||||
}
|
||||
_ => (),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn create_canvas(window: &Window) -> HtmlCanvasElement {
|
||||
use winit::platform::web::WindowExtWebSys;
|
||||
|
||||
let web_window = web_sys::window().unwrap();
|
||||
let document = web_window.document().unwrap();
|
||||
let body = document.body().unwrap();
|
||||
|
||||
// Set a background color for the canvas to make it easier to tell the where the canvas is for debugging purposes.
|
||||
let canvas = window.canvas().unwrap();
|
||||
canvas
|
||||
.style()
|
||||
.set_css_text("display: block; background-color: crimson; margin: auto; width: 50%; aspect-ratio: 4 / 1;");
|
||||
|
||||
let explanation = document.create_element("pre").unwrap();
|
||||
explanation.set_text_content(Some(EXPLANATION));
|
||||
body.append_child(&explanation).unwrap();
|
||||
|
||||
canvas
|
||||
}
|
||||
|
||||
pub fn render_circle(canvas: &HtmlCanvasElement, size: PhysicalSize<u32>) {
|
||||
log::info!("rendering circle with canvas size: {:?}", size);
|
||||
let context = canvas
|
||||
.get_context("2d")
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.dyn_into::<web_sys::CanvasRenderingContext2d>()
|
||||
.unwrap();
|
||||
|
||||
context.begin_path();
|
||||
context
|
||||
.arc(
|
||||
size.width as f64 / 2.0,
|
||||
size.height as f64 / 2.0,
|
||||
size.width.min(size.height) as f64 / 2.0,
|
||||
0.0,
|
||||
std::f64::consts::PI * 2.0,
|
||||
)
|
||||
.unwrap();
|
||||
context.fill();
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,43 @@
|
||||
extern crate winit;
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
fn main() {
|
||||
let events_loop = winit::EventsLoop::new();
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
let window = winit::WindowBuilder::new()
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
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| {
|
||||
println!("{:?}", event);
|
||||
event_loop.run(move |event, elwt| {
|
||||
println!("{event:?}");
|
||||
|
||||
match event {
|
||||
winit::Event::WindowEvent { event: winit::WindowEvent::Closed, .. } => events_loop.interrupt(),
|
||||
_ => ()
|
||||
Event::WindowEvent { event, window_id } if window_id == window.id() => match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::RedrawRequested => {
|
||||
// Notify the windowing system that we'll be presenting to the window.
|
||||
window.pre_present_notify();
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::AboutToWait => {
|
||||
window.request_redraw();
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
68
examples/window_buttons.rs
Normal file
68
examples/window_buttons.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
// This example is used by developers to test various window functions.
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
dpi::LogicalSize,
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::{DeviceEvents, EventLoop},
|
||||
keyboard::Key,
|
||||
window::{WindowBuilder, WindowButtons},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.with_inner_size(LogicalSize::new(300.0, 300.0))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
eprintln!("Window Button keys:");
|
||||
eprintln!(" (F) Toggle close button");
|
||||
eprintln!(" (G) Toggle maximize button");
|
||||
eprintln!(" (H) Toggle minimize button");
|
||||
|
||||
event_loop.listen_device_events(DeviceEvents::Always);
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { window_id, event } = event {
|
||||
match event {
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: key,
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match key.as_ref() {
|
||||
Key::Character("F" | "f") => {
|
||||
let buttons = window.enabled_buttons();
|
||||
window.set_enabled_buttons(buttons ^ WindowButtons::CLOSE);
|
||||
}
|
||||
Key::Character("G" | "g") => {
|
||||
let buttons = window.enabled_buttons();
|
||||
window.set_enabled_buttons(buttons ^ WindowButtons::MAXIMIZE);
|
||||
}
|
||||
Key::Character("H" | "h") => {
|
||||
let buttons = window.enabled_buttons();
|
||||
window.set_enabled_buttons(buttons ^ WindowButtons::MINIMIZE);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
WindowEvent::CloseRequested if window_id == window.id() => elwt.exit(),
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
137
examples/window_debug.rs
Normal file
137
examples/window_debug.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
// This example is used by developers to test various window functions.
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
dpi::{LogicalSize, PhysicalSize},
|
||||
event::{DeviceEvent, ElementState, Event, KeyEvent, RawKeyEvent, WindowEvent},
|
||||
event_loop::{DeviceEvents, EventLoop},
|
||||
keyboard::{Key, KeyCode, PhysicalKey},
|
||||
window::{Fullscreen, WindowBuilder},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
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.listen_device_events(DeviceEvents::Always);
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
match event {
|
||||
// This used to use the virtual key, but the new API
|
||||
// only provides the `physical_key` (`Code`).
|
||||
Event::DeviceEvent {
|
||||
event:
|
||||
DeviceEvent::Key(RawKeyEvent {
|
||||
physical_key,
|
||||
state: ElementState::Released,
|
||||
..
|
||||
}),
|
||||
..
|
||||
} => match physical_key {
|
||||
PhysicalKey::Code(KeyCode::KeyM) => {
|
||||
if minimized {
|
||||
minimized = !minimized;
|
||||
window.set_minimized(minimized);
|
||||
window.focus_window();
|
||||
}
|
||||
}
|
||||
PhysicalKey::Code(KeyCode::KeyV) => {
|
||||
if !visible {
|
||||
visible = !visible;
|
||||
window.set_visible(visible);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::WindowEvent { window_id, event } => match event {
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
logical_key: Key::Character(key_str),
|
||||
state: ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => match key_str.as_ref() {
|
||||
// WARNING: Consider using `key_without_modifers()` if available on your platform.
|
||||
// See the `key_binding` example
|
||||
"e" => {
|
||||
fn area(size: PhysicalSize<u32>) -> u32 {
|
||||
size.width * size.height
|
||||
}
|
||||
|
||||
let monitor = window.current_monitor().unwrap();
|
||||
if let Some(mode) = monitor
|
||||
.video_modes()
|
||||
.max_by(|a, b| area(a.size()).cmp(&area(b.size())))
|
||||
{
|
||||
window.set_fullscreen(Some(Fullscreen::Exclusive(mode)));
|
||||
} else {
|
||||
eprintln!("no video modes available");
|
||||
}
|
||||
}
|
||||
"f" => {
|
||||
if window.fullscreen().is_some() {
|
||||
window.set_fullscreen(None);
|
||||
} else {
|
||||
let monitor = window.current_monitor();
|
||||
window.set_fullscreen(Some(Fullscreen::Borderless(monitor)));
|
||||
}
|
||||
}
|
||||
"p" => {
|
||||
if window.fullscreen().is_some() {
|
||||
window.set_fullscreen(None);
|
||||
} else {
|
||||
window.set_fullscreen(Some(Fullscreen::Borderless(None)));
|
||||
}
|
||||
}
|
||||
"m" => {
|
||||
minimized = !minimized;
|
||||
window.set_minimized(minimized);
|
||||
}
|
||||
"q" => {
|
||||
elwt.exit();
|
||||
}
|
||||
"v" => {
|
||||
visible = !visible;
|
||||
window.set_visible(visible);
|
||||
}
|
||||
"x" => {
|
||||
let is_maximized = window.is_maximized();
|
||||
window.set_maximized(!is_maximized);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
WindowEvent::CloseRequested if window_id == window.id() => elwt.exit(),
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
}
|
||||
149
examples/window_drag_resize.rs
Normal file
149
examples/window_drag_resize.rs
Normal file
@@ -0,0 +1,149 @@
|
||||
//! Demonstrates capability to create in-app draggable regions for client-side decoration support.
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, MouseButton, StartCause, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::Key,
|
||||
window::{CursorIcon, ResizeDirection, WindowBuilder},
|
||||
};
|
||||
|
||||
const BORDER: f64 = 8.0;
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(600.0, 400.0))
|
||||
.with_min_inner_size(winit::dpi::LogicalSize::new(400.0, 200.0))
|
||||
.with_decorations(false)
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut border = false;
|
||||
let mut cursor_location = None;
|
||||
|
||||
event_loop.run(move |event, elwt| match event {
|
||||
Event::NewEvents(StartCause::Init) => {
|
||||
eprintln!("Press 'B' to toggle borderless")
|
||||
}
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
if !window.is_decorated() {
|
||||
let new_location =
|
||||
cursor_resize_direction(window.inner_size(), position, BORDER);
|
||||
|
||||
if new_location != cursor_location {
|
||||
cursor_location = new_location;
|
||||
window.set_cursor_icon(cursor_direction_icon(cursor_location))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WindowEvent::MouseInput {
|
||||
state: ElementState::Pressed,
|
||||
button: MouseButton::Left,
|
||||
..
|
||||
} => {
|
||||
if let Some(dir) = cursor_location {
|
||||
let _res = window.drag_resize_window(dir);
|
||||
} else if !window.is_decorated() {
|
||||
let _res = window.drag_window();
|
||||
}
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Released,
|
||||
logical_key: Key::Character(c),
|
||||
..
|
||||
},
|
||||
..
|
||||
} if matches!(c.as_ref(), "B" | "b") => {
|
||||
border = !border;
|
||||
window.set_decorations(border);
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
|
||||
_ => (),
|
||||
})
|
||||
}
|
||||
|
||||
fn cursor_direction_icon(resize_direction: Option<ResizeDirection>) -> CursorIcon {
|
||||
match resize_direction {
|
||||
Some(resize_direction) => match resize_direction {
|
||||
ResizeDirection::East => CursorIcon::EResize,
|
||||
ResizeDirection::North => CursorIcon::NResize,
|
||||
ResizeDirection::NorthEast => CursorIcon::NeResize,
|
||||
ResizeDirection::NorthWest => CursorIcon::NwResize,
|
||||
ResizeDirection::South => CursorIcon::SResize,
|
||||
ResizeDirection::SouthEast => CursorIcon::SeResize,
|
||||
ResizeDirection::SouthWest => CursorIcon::SwResize,
|
||||
ResizeDirection::West => CursorIcon::WResize,
|
||||
},
|
||||
None => CursorIcon::Default,
|
||||
}
|
||||
}
|
||||
|
||||
fn cursor_resize_direction(
|
||||
win_size: winit::dpi::PhysicalSize<u32>,
|
||||
position: winit::dpi::PhysicalPosition<f64>,
|
||||
border_size: f64,
|
||||
) -> Option<ResizeDirection> {
|
||||
enum XDirection {
|
||||
West,
|
||||
East,
|
||||
Default,
|
||||
}
|
||||
|
||||
enum YDirection {
|
||||
North,
|
||||
South,
|
||||
Default,
|
||||
}
|
||||
|
||||
let xdir = if position.x < border_size {
|
||||
XDirection::West
|
||||
} else if position.x > (win_size.width as f64 - border_size) {
|
||||
XDirection::East
|
||||
} else {
|
||||
XDirection::Default
|
||||
};
|
||||
|
||||
let ydir = if position.y < border_size {
|
||||
YDirection::North
|
||||
} else if position.y > (win_size.height as f64 - border_size) {
|
||||
YDirection::South
|
||||
} else {
|
||||
YDirection::Default
|
||||
};
|
||||
|
||||
Some(match xdir {
|
||||
XDirection::West => match ydir {
|
||||
YDirection::North => ResizeDirection::NorthWest,
|
||||
YDirection::South => ResizeDirection::SouthWest,
|
||||
YDirection::Default => ResizeDirection::West,
|
||||
},
|
||||
|
||||
XDirection::East => match ydir {
|
||||
YDirection::North => ResizeDirection::NorthEast,
|
||||
YDirection::South => ResizeDirection::SouthEast,
|
||||
YDirection::Default => ResizeDirection::East,
|
||||
},
|
||||
|
||||
XDirection::Default => match ydir {
|
||||
YDirection::North => ResizeDirection::North,
|
||||
YDirection::South => ResizeDirection::South,
|
||||
YDirection::Default => return None,
|
||||
},
|
||||
})
|
||||
}
|
||||
60
examples/window_icon.rs
Normal file
60
examples/window_icon.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::{Icon, WindowBuilder},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
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");
|
||||
|
||||
let icon = load_icon(Path::new(path));
|
||||
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
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(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::DroppedFile(path) => {
|
||||
window.set_window_icon(Some(load_icon(&path)));
|
||||
}
|
||||
WindowEvent::RedrawRequested => fill::fill_window(&window),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
93
examples/window_on_demand.rs
Normal file
93
examples/window_on_demand.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
// Limit this example to only compatible platforms.
|
||||
#[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform,))]
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
use std::time::Duration;
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
|
||||
use winit::{
|
||||
error::EventLoopError,
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
platform::run_on_demand::EventLoopExtRunOnDemand,
|
||||
window::{Window, WindowBuilder, WindowId},
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
#[derive(Default)]
|
||||
struct App {
|
||||
window_id: Option<WindowId>,
|
||||
window: Option<Window>,
|
||||
}
|
||||
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let mut event_loop = EventLoop::new().unwrap();
|
||||
|
||||
fn run_app(event_loop: &mut EventLoop<()>, idx: usize) -> Result<(), EventLoopError> {
|
||||
let mut app = App::default();
|
||||
|
||||
event_loop.run_on_demand(move |event, elwt| {
|
||||
println!("Run {idx}: {:?}", event);
|
||||
|
||||
if let Some(window) = &app.window {
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window.id() == window_id => {
|
||||
println!("--------------------------------------------------------- Window {idx} CloseRequested");
|
||||
fill::cleanup_window(window);
|
||||
app.window = None;
|
||||
}
|
||||
Event::AboutToWait => window.request_redraw(),
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::RedrawRequested,
|
||||
..
|
||||
} => {
|
||||
fill::fill_window(window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
} else if let Some(id) = app.window_id {
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::Destroyed,
|
||||
window_id,
|
||||
} if id == window_id => {
|
||||
println!("--------------------------------------------------------- Window {idx} Destroyed");
|
||||
app.window_id = None;
|
||||
elwt.exit();
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
} else if let Event::Resumed = event {
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("Fantastic window number one!")
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||
.build(elwt)
|
||||
.unwrap();
|
||||
app.window_id = Some(window.id());
|
||||
app.window = Some(window);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
run_app(&mut event_loop, 1)?;
|
||||
|
||||
println!("--------------------------------------------------------- Finished first loop");
|
||||
println!("--------------------------------------------------------- Waiting 5 seconds");
|
||||
std::thread::sleep(Duration::from_secs(5));
|
||||
|
||||
let ret = run_app(&mut event_loop, 2);
|
||||
println!("--------------------------------------------------------- Finished second loop");
|
||||
ret
|
||||
}
|
||||
|
||||
#[cfg(not(any(windows_platform, macos_platform, x11_platform, wayland_platform,)))]
|
||||
fn main() {
|
||||
println!("This example is not supported on this platform");
|
||||
}
|
||||
72
examples/window_option_as_alt.rs
Normal file
72
examples/window_option_as_alt.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use winit::platform::macos::{OptionAsAlt, WindowExtMacOS};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use winit::{
|
||||
event::ElementState,
|
||||
event::{Event, MouseButton, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
/// Prints the keyboard events characters received when option_is_alt is true versus false.
|
||||
/// A left mouse click will toggle option_is_alt.
|
||||
#[cfg(target_os = "macos")]
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
window.set_ime_allowed(true);
|
||||
|
||||
let mut option_as_alt = window.option_as_alt();
|
||||
|
||||
event_loop.run(move |event, elwt| match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => elwt.exit(),
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::MouseInput {
|
||||
state: ElementState::Pressed,
|
||||
button: MouseButton::Left,
|
||||
..
|
||||
} => {
|
||||
option_as_alt = match option_as_alt {
|
||||
OptionAsAlt::None => OptionAsAlt::OnlyLeft,
|
||||
OptionAsAlt::OnlyLeft => OptionAsAlt::OnlyRight,
|
||||
OptionAsAlt::OnlyRight => OptionAsAlt::Both,
|
||||
OptionAsAlt::Both => OptionAsAlt::None,
|
||||
};
|
||||
|
||||
println!("Received Mouse click, toggling option_as_alt to: {option_as_alt:?}");
|
||||
window.set_option_as_alt(option_as_alt);
|
||||
}
|
||||
WindowEvent::KeyboardInput { .. } => println!("KeyboardInput: {event:?}"),
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::AboutToWait => {
|
||||
window.request_redraw();
|
||||
}
|
||||
|
||||
_ => (),
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
fn main() {
|
||||
println!("This example is only supported on MacOS");
|
||||
}
|
||||
74
examples/window_pump_events.rs
Normal file
74
examples/window_pump_events.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
// Limit this example to only compatible platforms.
|
||||
#[cfg(any(
|
||||
windows_platform,
|
||||
macos_platform,
|
||||
x11_platform,
|
||||
wayland_platform,
|
||||
android_platform,
|
||||
))]
|
||||
fn main() -> std::process::ExitCode {
|
||||
use std::{process::ExitCode, thread::sleep, time::Duration};
|
||||
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
platform::pump_events::{EventLoopExtPumpEvents, PumpStatus},
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
let mut event_loop = EventLoop::new().unwrap();
|
||||
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
'main: loop {
|
||||
let timeout = Some(Duration::ZERO);
|
||||
let status = event_loop.pump_events(timeout, |event, elwt| {
|
||||
if let Event::WindowEvent { event, .. } = &event {
|
||||
// Print only Window events to reduce noise
|
||||
println!("{event:?}");
|
||||
}
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => elwt.exit(),
|
||||
Event::AboutToWait => {
|
||||
window.request_redraw();
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::RedrawRequested,
|
||||
..
|
||||
} => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
});
|
||||
if let PumpStatus::Exit(exit_code) = status {
|
||||
break 'main ExitCode::from(exit_code as u8);
|
||||
}
|
||||
|
||||
// Sleep for 1/60 second to simulate application work
|
||||
//
|
||||
// Since `pump_events` doesn't block it will be important to
|
||||
// throttle the loop in the app somehow.
|
||||
println!("Update()");
|
||||
sleep(Duration::from_millis(16));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(ios_platform, wasm_platform, orbital_platform))]
|
||||
fn main() {
|
||||
println!("This platform doesn't support pump_events.");
|
||||
}
|
||||
52
examples/window_resize_increments.rs
Normal file
52
examples/window_resize_increments.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
use log::debug;
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
dpi::LogicalSize,
|
||||
event::{ElementState, Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::NamedKey,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("A fantastic window!")
|
||||
.with_inner_size(LogicalSize::new(128.0, 128.0))
|
||||
.with_resize_increments(LogicalSize::new(25.0, 25.0))
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
let mut has_increments = true;
|
||||
|
||||
event_loop.run(move |event, elwt| match event {
|
||||
Event::WindowEvent { event, window_id } if window_id == window.id() => match event {
|
||||
WindowEvent::CloseRequested => elwt.exit(),
|
||||
WindowEvent::KeyboardInput { event, .. }
|
||||
if event.logical_key == NamedKey::Space
|
||||
&& event.state == ElementState::Released =>
|
||||
{
|
||||
has_increments = !has_increments;
|
||||
|
||||
let new_increments = match window.resize_increments() {
|
||||
Some(_) => None,
|
||||
None => Some(LogicalSize::new(25.0, 25.0)),
|
||||
};
|
||||
debug!("Had increments: {}", new_increments.is_none());
|
||||
window.set_resize_increments(new_increments);
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
Event::AboutToWait => window.request_redraw(),
|
||||
|
||||
_ => (),
|
||||
})
|
||||
}
|
||||
107
examples/window_tabbing.rs
Normal file
107
examples/window_tabbing.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use std::{collections::HashMap, num::NonZeroUsize};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use simple_logger::SimpleLogger;
|
||||
#[cfg(target_os = "macos")]
|
||||
use winit::{
|
||||
event::{ElementState, Event, KeyEvent, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
keyboard::{Key, NamedKey},
|
||||
platform::macos::{WindowBuilderExtMacOS, WindowExtMacOS},
|
||||
window::{Window, WindowBuilder},
|
||||
};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let mut windows = HashMap::new();
|
||||
let window = Window::new(&event_loop).unwrap();
|
||||
println!("Opened a new window: {:?}", window.id());
|
||||
windows.insert(window.id(), window);
|
||||
|
||||
println!("Press N to open a new window.");
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
if let Event::WindowEvent { event, window_id } = event {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
println!("Window {window_id:?} has received the signal to close");
|
||||
|
||||
// This drops the window, causing it to close.
|
||||
windows.remove(&window_id);
|
||||
|
||||
if windows.is_empty() {
|
||||
elwt.exit();
|
||||
}
|
||||
}
|
||||
WindowEvent::Resized(_) => {
|
||||
if let Some(window) = windows.get(&window_id) {
|
||||
window.request_redraw();
|
||||
}
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
event:
|
||||
KeyEvent {
|
||||
state: ElementState::Pressed,
|
||||
logical_key,
|
||||
..
|
||||
},
|
||||
is_synthetic: false,
|
||||
..
|
||||
} => match logical_key.as_ref() {
|
||||
Key::Character("t") => {
|
||||
let tabbing_id = windows.get(&window_id).unwrap().tabbing_identifier();
|
||||
let window = WindowBuilder::new()
|
||||
.with_tabbing_identifier(&tabbing_id)
|
||||
.build(elwt)
|
||||
.unwrap();
|
||||
println!("Added a new tab: {:?}", window.id());
|
||||
windows.insert(window.id(), window);
|
||||
}
|
||||
Key::Character("w") => {
|
||||
let _ = windows.remove(&window_id);
|
||||
}
|
||||
Key::Named(NamedKey::ArrowRight) => {
|
||||
windows.get(&window_id).unwrap().select_next_tab();
|
||||
}
|
||||
Key::Named(NamedKey::ArrowLeft) => {
|
||||
windows.get(&window_id).unwrap().select_previous_tab();
|
||||
}
|
||||
Key::Character(ch) => {
|
||||
if let Ok(index) = ch.parse::<NonZeroUsize>() {
|
||||
let index = index.get();
|
||||
// Select the last tab when pressing `9`.
|
||||
let window = windows.get(&window_id).unwrap();
|
||||
if index == 9 {
|
||||
window.select_tab_at_index(window.num_tabs() - 1)
|
||||
} else {
|
||||
window.select_tab_at_index(index - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let Some(window) = windows.get(&window_id) {
|
||||
fill::fill_window(window);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
fn main() {
|
||||
println!("This example is only supported on MacOS");
|
||||
}
|
||||
70
examples/x11_embed.rs
Normal file
70
examples/x11_embed.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
//! A demonstration of embedding a winit window in an existing X11 application.
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
#[cfg(x11_platform)]
|
||||
mod imple {
|
||||
use super::fill;
|
||||
use simple_logger::SimpleLogger;
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::EventLoop,
|
||||
platform::x11::WindowBuilderExtX11,
|
||||
window::WindowBuilder,
|
||||
};
|
||||
|
||||
pub(super) fn entry() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// First argument should be a 32-bit X11 window ID.
|
||||
let parent_window_id = std::env::args()
|
||||
.nth(1)
|
||||
.ok_or("Expected a 32-bit X11 window ID as the first argument.")?
|
||||
.parse::<u32>()?;
|
||||
|
||||
SimpleLogger::new().init().unwrap();
|
||||
let event_loop = EventLoop::new()?;
|
||||
|
||||
let window = WindowBuilder::new()
|
||||
.with_title("An embedded window!")
|
||||
.with_inner_size(winit::dpi::LogicalSize::new(128.0, 128.0))
|
||||
.with_embed_parent_window(parent_window_id)
|
||||
.build(&event_loop)
|
||||
.unwrap();
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
window_id,
|
||||
} if window_id == window.id() => elwt.exit(),
|
||||
Event::AboutToWait => {
|
||||
window.request_redraw();
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::RedrawRequested,
|
||||
..
|
||||
} => {
|
||||
// Notify the windowing system that we'll be presenting to the window.
|
||||
window.pre_present_notify();
|
||||
fill::fill_window(&window);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(x11_platform))]
|
||||
mod imple {
|
||||
pub(super) fn entry() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("This example is only supported on X11 platforms.");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
imple::entry()
|
||||
}
|
||||
9
run-wasm/Cargo.toml
Normal file
9
run-wasm/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "run-wasm"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
cargo-run-wasm = "0.2.0"
|
||||
3
run-wasm/src/main.rs
Normal file
3
run-wasm/src/main.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
cargo_run_wasm::run_wasm_with_css("body { margin: 0px; }");
|
||||
}
|
||||
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
|
||||
@@ -1,101 +0,0 @@
|
||||
|
||||
//! This temporary module generates types that wrap around the old API (winit v5 and below) and
|
||||
//! expose the new API (winit v6 and above).
|
||||
//!
|
||||
//! This is temporary so that existing backends can smoothly transition. After all implementations
|
||||
//! have finished transitionning, this module should disappear.
|
||||
|
||||
macro_rules! gen_api_transition {
|
||||
() => {
|
||||
pub struct EventsLoop {
|
||||
windows: ::std::sync::Mutex<Vec<::std::sync::Arc<Window>>>,
|
||||
interrupted: ::std::sync::atomic::AtomicBool,
|
||||
}
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn new() -> EventsLoop {
|
||||
EventsLoop {
|
||||
windows: ::std::sync::Mutex::new(vec![]),
|
||||
interrupted: ::std::sync::atomic::AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interrupt(&self) {
|
||||
self.interrupted.store(true, ::std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn poll_events<F>(&self, mut callback: F)
|
||||
where F: FnMut(::Event)
|
||||
{
|
||||
let mut windows = self.windows.lock().unwrap();
|
||||
for window in windows.iter() {
|
||||
for event in window.poll_events() {
|
||||
callback(::Event::WindowEvent {
|
||||
window_id: ::WindowId(WindowId(&**window as *const Window as usize)),
|
||||
event: event,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_forever<F>(&self, mut callback: F)
|
||||
where F: FnMut(::Event)
|
||||
{
|
||||
self.interrupted.store(false, ::std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
// Yeah that's a very bad implementation.
|
||||
loop {
|
||||
self.poll_events(|e| callback(e));
|
||||
::std::thread::sleep_ms(5);
|
||||
if self.interrupted.load(::std::sync::atomic::Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(usize);
|
||||
|
||||
pub struct Window2 {
|
||||
pub window: ::std::sync::Arc<Window>,
|
||||
events_loop: ::std::sync::Weak<EventsLoop>,
|
||||
}
|
||||
|
||||
impl ::std::ops::Deref for Window2 {
|
||||
type Target = Window;
|
||||
#[inline]
|
||||
fn deref(&self) -> &Window {
|
||||
&*self.window
|
||||
}
|
||||
}
|
||||
|
||||
impl Window2 {
|
||||
pub fn new(events_loop: ::std::sync::Arc<EventsLoop>, window: &::WindowAttributes,
|
||||
pl_attribs: &PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Window2, CreationError>
|
||||
{
|
||||
let win = ::std::sync::Arc::new(try!(Window::new(window, pl_attribs)));
|
||||
events_loop.windows.lock().unwrap().push(win.clone());
|
||||
Ok(Window2 {
|
||||
window: win,
|
||||
events_loop: ::std::sync::Arc::downgrade(&events_loop),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
WindowId(&*self.window as *const Window as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window2 {
|
||||
fn drop(&mut self) {
|
||||
if let Some(ev) = self.events_loop.upgrade() {
|
||||
let mut windows = ev.windows.lock().unwrap();
|
||||
windows.retain(|w| &**w as *const Window != &*self.window as *const _);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
1001
src/dpi.rs
Normal file
1001
src/dpi.rs
Normal file
File diff suppressed because it is too large
Load Diff
142
src/error.rs
Normal file
142
src/error.rs
Normal file
@@ -0,0 +1,142 @@
|
||||
use std::{error, fmt};
|
||||
|
||||
use crate::platform_impl;
|
||||
|
||||
// TODO: Rename
|
||||
/// An error that may be generated when requesting Winit state
|
||||
#[derive(Debug)]
|
||||
pub enum ExternalError {
|
||||
/// The operation is not supported by the backend.
|
||||
NotSupported(NotSupportedError),
|
||||
/// The operation was ignored.
|
||||
Ignored,
|
||||
/// 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,
|
||||
}
|
||||
|
||||
/// A general error that may occur while running the Winit event loop
|
||||
#[derive(Debug)]
|
||||
pub enum EventLoopError {
|
||||
/// The operation is not supported by the backend.
|
||||
NotSupported(NotSupportedError),
|
||||
/// The OS cannot perform the operation.
|
||||
Os(OsError),
|
||||
/// The event loop can't be re-run while it's already running
|
||||
AlreadyRunning,
|
||||
/// The event loop can't be re-created.
|
||||
RecreationAttempt,
|
||||
/// Application has exit with an error status.
|
||||
ExitFailure(i32),
|
||||
}
|
||||
|
||||
impl From<OsError> for EventLoopError {
|
||||
fn from(value: OsError) -> Self {
|
||||
Self::Os(value)
|
||||
}
|
||||
}
|
||||
|
||||
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::Ignored => write!(f, "Operation was ignored"),
|
||||
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 fmt::Display for EventLoopError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||
match self {
|
||||
EventLoopError::AlreadyRunning => write!(f, "EventLoop is already running"),
|
||||
EventLoopError::RecreationAttempt => write!(f, "EventLoop can't be recreated"),
|
||||
EventLoopError::NotSupported(e) => e.fmt(f),
|
||||
EventLoopError::Os(e) => e.fmt(f),
|
||||
EventLoopError::ExitFailure(status) => write!(f, "Exit Failure: {status}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for OsError {}
|
||||
impl error::Error for ExternalError {}
|
||||
impl error::Error for NotSupportedError {}
|
||||
impl error::Error for EventLoopError {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::redundant_clone)]
|
||||
|
||||
use super::*;
|
||||
|
||||
// Eat attributes for testing
|
||||
#[test]
|
||||
fn ensure_fmt_does_not_panic() {
|
||||
let _ = format!(
|
||||
"{:?}, {}",
|
||||
NotSupportedError::new(),
|
||||
NotSupportedError::new().clone()
|
||||
);
|
||||
let _ = format!(
|
||||
"{:?}, {}",
|
||||
ExternalError::NotSupported(NotSupportedError::new()),
|
||||
ExternalError::NotSupported(NotSupportedError::new())
|
||||
);
|
||||
}
|
||||
}
|
||||
1342
src/event.rs
Normal file
1342
src/event.rs
Normal file
File diff suppressed because it is too large
Load Diff
475
src/event_loop.rs
Normal file
475
src/event_loop.rs
Normal file
@@ -0,0 +1,475 @@
|
||||
//! The [`EventLoop`] struct and assorted supporting types, including
|
||||
//! [`ControlFlow`].
|
||||
//!
|
||||
//! If you want to send custom events to the event loop, use
|
||||
//! [`EventLoop::create_proxy`] to acquire an [`EventLoopProxy`] and call its
|
||||
//! [`send_event`](`EventLoopProxy::send_event`) method.
|
||||
//!
|
||||
//! See the root-level documentation for information on how to create and use an event loop to
|
||||
//! handle events.
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::{error, fmt};
|
||||
|
||||
#[cfg(not(wasm_platform))]
|
||||
use std::time::{Duration, Instant};
|
||||
#[cfg(wasm_platform)]
|
||||
use web_time::{Duration, Instant};
|
||||
|
||||
use crate::error::EventLoopError;
|
||||
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 this cannot be shared across threads (due to platform-dependant logic
|
||||
/// forbidding it), as such it is neither [`Send`] nor [`Sync`]. If you need cross-thread access, the
|
||||
/// [`Window`] created from this _can_ be sent to an other thread, and the
|
||||
/// [`EventLoopProxy`] allows you to wake up an `EventLoop` from another thread.
|
||||
///
|
||||
/// [`Window`]: crate::window::Window
|
||||
pub struct EventLoop<T: 'static> {
|
||||
pub(crate) event_loop: platform_impl::EventLoop<T>,
|
||||
pub(crate) _marker: 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: PhantomData<*mut ()>, // Not Send nor Sync
|
||||
}
|
||||
|
||||
/// Object that allows building the event loop.
|
||||
///
|
||||
/// This is used to make specifying options that affect the whole application
|
||||
/// easier. But note that constructing multiple event loops is not supported.
|
||||
#[derive(Default)]
|
||||
pub struct EventLoopBuilder<T: 'static> {
|
||||
pub(crate) platform_specific: platform_impl::PlatformSpecificEventLoopAttributes,
|
||||
_p: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl EventLoopBuilder<()> {
|
||||
/// Start building a new event loop.
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
Self::with_user_event()
|
||||
}
|
||||
}
|
||||
|
||||
static EVENT_LOOP_CREATED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
impl<T> EventLoopBuilder<T> {
|
||||
/// Start building a new event loop, with the given type as the user event
|
||||
/// type.
|
||||
#[inline]
|
||||
pub fn with_user_event() -> Self {
|
||||
Self {
|
||||
platform_specific: Default::default(),
|
||||
_p: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a new event loop.
|
||||
///
|
||||
/// ***For cross-platform compatibility, the [`EventLoop`] must be created on the main thread,
|
||||
/// and only once per application.***
|
||||
///
|
||||
/// Calling this function will result in display backend initialisation.
|
||||
///
|
||||
/// ## Panics
|
||||
///
|
||||
/// Attempting to create the event loop off the main 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.
|
||||
/// `EventLoopBuilderExt::any_thread` functions are exposed in the relevant
|
||||
/// [`platform`] module if the target platform supports creating an event
|
||||
/// loop on any thread.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Wayland/X11:** to prevent running under `Wayland` or `X11` unset `WAYLAND_DISPLAY`
|
||||
/// or `DISPLAY` respectively when building the event loop.
|
||||
/// - **Android:** must be configured with an `AndroidApp` from `android_main()` by calling
|
||||
/// [`.with_android_app(app)`] before calling `.build()`, otherwise it'll panic.
|
||||
///
|
||||
/// [`platform`]: crate::platform
|
||||
#[cfg_attr(
|
||||
android,
|
||||
doc = "[`.with_android_app(app)`]: crate::platform::android::EventLoopBuilderExtAndroid::with_android_app"
|
||||
)]
|
||||
#[cfg_attr(
|
||||
not(android),
|
||||
doc = "[`.with_android_app(app)`]: #only-available-on-android"
|
||||
)]
|
||||
#[inline]
|
||||
pub fn build(&mut self) -> Result<EventLoop<T>, EventLoopError> {
|
||||
if EVENT_LOOP_CREATED.swap(true, Ordering::Relaxed) {
|
||||
return Err(EventLoopError::RecreationAttempt);
|
||||
}
|
||||
|
||||
// Certain platforms accept a mutable reference in their API.
|
||||
#[allow(clippy::unnecessary_mut_passed)]
|
||||
Ok(EventLoop {
|
||||
event_loop: platform_impl::EventLoop::new(&mut self.platform_specific)?,
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(wasm_platform)]
|
||||
pub(crate) fn allow_event_loop_recreation() {
|
||||
EVENT_LOOP_CREATED.store(false, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for EventLoop<T> {
|
||||
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 through [`EventLoopWindowTarget::set_control_flow()`].
|
||||
///
|
||||
/// Indicates the desired behavior of the event loop after [`Event::AboutToWait`] is emitted.
|
||||
///
|
||||
/// Defaults to [`Wait`].
|
||||
///
|
||||
/// [`Wait`]: Self::Wait
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub enum ControlFlow {
|
||||
/// When the current loop iteration finishes, immediately begin a new iteration regardless of
|
||||
/// whether or not new events are available to process.
|
||||
Poll,
|
||||
|
||||
/// When the current loop iteration finishes, suspend the thread until another event arrives.
|
||||
#[default]
|
||||
Wait,
|
||||
|
||||
/// When the current loop iteration finishes, suspend the thread until either another event
|
||||
/// arrives or the given time is reached.
|
||||
///
|
||||
/// Useful for implementing efficient timers. Applications which want to render at the display's
|
||||
/// native refresh rate should instead use [`Poll`] and the VSync functionality of a graphics API
|
||||
/// to reduce odds of missed frames.
|
||||
///
|
||||
/// [`Poll`]: Self::Poll
|
||||
WaitUntil(Instant),
|
||||
}
|
||||
|
||||
impl ControlFlow {
|
||||
/// Creates a [`ControlFlow`] that waits until a timeout has expired.
|
||||
///
|
||||
/// In most cases, this is set to [`WaitUntil`]. However, if the timeout overflows, it is
|
||||
/// instead set to [`Wait`].
|
||||
///
|
||||
/// [`WaitUntil`]: Self::WaitUntil
|
||||
/// [`Wait`]: Self::Wait
|
||||
pub fn wait_duration(timeout: Duration) -> Self {
|
||||
match Instant::now().checked_add(timeout) {
|
||||
Some(instant) => Self::WaitUntil(instant),
|
||||
None => Self::Wait,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoop<()> {
|
||||
/// Alias for [`EventLoopBuilder::new().build()`].
|
||||
///
|
||||
/// [`EventLoopBuilder::new().build()`]: EventLoopBuilder::build
|
||||
#[inline]
|
||||
pub fn new() -> Result<EventLoop<()>, EventLoopError> {
|
||||
EventLoopBuilder::new().build()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EventLoop<T> {
|
||||
#[deprecated = "Use `EventLoopBuilder::<T>::with_user_event().build()` instead."]
|
||||
pub fn with_user_event() -> Result<EventLoop<T>, EventLoopError> {
|
||||
EventLoopBuilder::<T>::with_user_event().build()
|
||||
}
|
||||
|
||||
/// Runs the event loop in the calling thread and calls the given `event_handler` closure
|
||||
/// to dispatch any pending events.
|
||||
///
|
||||
/// Since the closure is `'static`, it must be a `move` closure if it needs to
|
||||
/// access any data from the calling context.
|
||||
///
|
||||
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **iOS:** Will never return to the caller and so values not passed to this function will
|
||||
/// *not* be dropped before the process exits.
|
||||
/// - **Web:** Will _act_ as if it never returns to the caller by throwing a Javascript exception
|
||||
/// (that Rust doesn't see) that will also mean that the rest of the function is never executed
|
||||
/// and any values not passed to this function will *not* be dropped.
|
||||
///
|
||||
/// Web applications are recommended to use
|
||||
#[cfg_attr(
|
||||
wasm_platform,
|
||||
doc = "[`EventLoopExtWebSys::spawn()`][crate::platform::web::EventLoopExtWebSys::spawn()]"
|
||||
)]
|
||||
#[cfg_attr(not(wasm_platform), doc = "`EventLoopExtWebSys::spawn()`")]
|
||||
/// [^1] instead of [`run()`] to avoid the need
|
||||
/// for the Javascript exception trick, and to make it clearer that the event loop runs
|
||||
/// asynchronously (via the browser's own, internal, event loop) and doesn't block the
|
||||
/// current thread of execution like it does on other platforms.
|
||||
///
|
||||
/// This function won't be available with `target_feature = "exception-handling"`.
|
||||
///
|
||||
/// [`set_control_flow()`]: EventLoopWindowTarget::set_control_flow()
|
||||
/// [`run()`]: Self::run()
|
||||
/// [^1]: `EventLoopExtWebSys::spawn()` is only available on WASM.
|
||||
#[inline]
|
||||
#[cfg(not(all(wasm_platform, target_feature = "exception-handling")))]
|
||||
pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &EventLoopWindowTarget<T>),
|
||||
{
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl<T> rwh_06::HasDisplayHandle for EventLoop<T> {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
rwh_06::HasDisplayHandle::display_handle(&**self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoop<T> {
|
||||
/// Returns a [`rwh_05::RawDisplayHandle`] for the event loop.
|
||||
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
|
||||
rwh_05::HasRawDisplayHandle::raw_display_handle(&**self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
impl<T> AsFd for EventLoop<T> {
|
||||
/// Get the underlying [EventLoop]'s `fd` which you can register
|
||||
/// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
|
||||
/// loop must be polled with the [`pump_events`] API.
|
||||
///
|
||||
/// [`calloop`]: https://crates.io/crates/calloop
|
||||
/// [`mio`]: https://crates.io/crates/mio
|
||||
/// [`pump_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_events
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
self.event_loop.as_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
impl<T> AsRawFd for EventLoop<T> {
|
||||
/// Get the underlying [EventLoop]'s raw `fd` which you can register
|
||||
/// into other event loop, like [`calloop`] or [`mio`]. When doing so, the
|
||||
/// loop must be polled with the [`pump_events`] API.
|
||||
///
|
||||
/// [`calloop`]: https://crates.io/crates/calloop
|
||||
/// [`mio`]: https://crates.io/crates/mio
|
||||
/// [`pump_events`]: crate::platform::pump_events::EventLoopExtPumpEvents::pump_events
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.event_loop.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
#[allow(clippy::useless_conversion)] // false positive on some platforms
|
||||
self.p
|
||||
.available_monitors()
|
||||
.into_iter()
|
||||
.map(|inner| MonitorHandle { inner })
|
||||
}
|
||||
|
||||
/// Returns the primary monitor of the system.
|
||||
///
|
||||
/// Returns `None` if it can't identify any monitor as a primary one.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// **Wayland / Web:** Always returns `None`.
|
||||
#[inline]
|
||||
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
|
||||
self.p
|
||||
.primary_monitor()
|
||||
.map(|inner| MonitorHandle { inner })
|
||||
}
|
||||
|
||||
/// Change if or when [`DeviceEvent`]s are captured.
|
||||
///
|
||||
/// Since the [`DeviceEvent`] capture can lead to high CPU usage for unfocused windows, winit
|
||||
/// will ignore them by default for unfocused windows on Linux/BSD. This method allows changing
|
||||
/// this at runtime to explicitly capture them again.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Wayland / macOS / iOS / Android / Orbital:** Unsupported.
|
||||
///
|
||||
/// [`DeviceEvent`]: crate::event::DeviceEvent
|
||||
pub fn listen_device_events(&self, allowed: DeviceEvents) {
|
||||
self.p.listen_device_events(allowed);
|
||||
}
|
||||
|
||||
/// Sets the [`ControlFlow`].
|
||||
pub fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||
self.p.set_control_flow(control_flow)
|
||||
}
|
||||
|
||||
/// Gets the current [`ControlFlow`].
|
||||
pub fn control_flow(&self) -> ControlFlow {
|
||||
self.p.control_flow()
|
||||
}
|
||||
|
||||
/// This exits the event loop.
|
||||
///
|
||||
/// See [`LoopExiting`](Event::LoopExiting).
|
||||
pub fn exit(&self) {
|
||||
self.p.exit()
|
||||
}
|
||||
|
||||
/// Returns if the [`EventLoop`] is about to stop.
|
||||
///
|
||||
/// See [`exit()`](Self::exit).
|
||||
pub fn exiting(&self) -> bool {
|
||||
self.p.exiting()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
impl<T> rwh_06::HasDisplayHandle for EventLoopWindowTarget<T> {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.p.raw_display_handle_rwh_06()?;
|
||||
// SAFETY: The display will never be deallocated while the event loop is alive.
|
||||
Ok(unsafe { rwh_06::DisplayHandle::borrow_raw(raw) })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rwh_05")]
|
||||
unsafe impl<T> rwh_05::HasRawDisplayHandle for EventLoopWindowTarget<T> {
|
||||
/// Returns a [`rwh_05::RawDisplayHandle`] for the event loop.
|
||||
fn raw_display_handle(&self) -> rwh_05::RawDisplayHandle {
|
||||
self.p.raw_display_handle_rwh_05()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// [`UserEvent(event)`]: Event::UserEvent
|
||||
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 [`EventLoopProxy::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> {}
|
||||
|
||||
/// Control when device events are captured.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
|
||||
pub enum DeviceEvents {
|
||||
/// Report device events regardless of window focus.
|
||||
Always,
|
||||
/// Only capture device events while the window is focused.
|
||||
#[default]
|
||||
WhenFocused,
|
||||
/// Never capture device events.
|
||||
Never,
|
||||
}
|
||||
|
||||
/// A unique identifier of the winit's async request.
|
||||
///
|
||||
/// This could be used to identify the async request once it's done
|
||||
/// and a specific action must be taken.
|
||||
///
|
||||
/// One of the handling scenarious could be to maintain a working list
|
||||
/// containing [`AsyncRequestSerial`] and some closure associated with it.
|
||||
/// Then once event is arriving the working list is being traversed and a job
|
||||
/// executed and removed from the list.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct AsyncRequestSerial {
|
||||
serial: usize,
|
||||
}
|
||||
|
||||
impl AsyncRequestSerial {
|
||||
// TODO(kchibisov): Remove `cfg` when the clipboard will be added.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn get() -> Self {
|
||||
static CURRENT_SERIAL: AtomicUsize = AtomicUsize::new(0);
|
||||
// NOTE: We rely on wrap around here, while the user may just request
|
||||
// in the loop usize::MAX times that's issue is considered on them.
|
||||
let serial = CURRENT_SERIAL.fetch_add(1, Ordering::Relaxed);
|
||||
Self { serial }
|
||||
}
|
||||
}
|
||||
339
src/events.rs
339
src/events.rs
@@ -1,339 +0,0 @@
|
||||
use std::path::PathBuf;
|
||||
use WindowId;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Event {
|
||||
WindowEvent {
|
||||
window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum WindowEvent {
|
||||
// TODO: remove ; can break the lib internally so be careful
|
||||
Awakened,
|
||||
|
||||
/// The size of the window has changed.
|
||||
Resized(u32, u32),
|
||||
|
||||
/// The position of the window has changed.
|
||||
Moved(i32, i32),
|
||||
|
||||
/// The window has been closed.
|
||||
Closed,
|
||||
|
||||
/// A file has been dropped into the window.
|
||||
DroppedFile(PathBuf),
|
||||
|
||||
/// 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(ElementState, ScanCode, Option<VirtualKeyCode>, ModifiersState),
|
||||
|
||||
/// The cursor has moved on the window.
|
||||
///
|
||||
/// The parameter are the (x,y) coords in pixels relative to the top-left corner of the window.
|
||||
MouseMoved(i32, i32),
|
||||
|
||||
/// The cursor has entered the window.
|
||||
MouseEntered,
|
||||
|
||||
/// The cursor has left the window.
|
||||
MouseLeft,
|
||||
|
||||
/// A mouse wheel movement or touchpad scroll occurred.
|
||||
MouseWheel(MouseScrollDelta, TouchPhase),
|
||||
|
||||
/// An event from the mouse has been received.
|
||||
MouseInput(ElementState, MouseButton),
|
||||
|
||||
/// 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(f32, i64),
|
||||
|
||||
/// The window needs to be redrawn.
|
||||
Refresh,
|
||||
|
||||
/// App has been suspended or resumed.
|
||||
///
|
||||
/// The parameter is true if app was suspended, and false if it has been resumed.
|
||||
Suspended(bool),
|
||||
|
||||
/// Touch event has been received
|
||||
Touch(Touch)
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum TouchPhase {
|
||||
Started,
|
||||
Moved,
|
||||
Ended,
|
||||
Cancelled
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
/// 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.
|
||||
pub struct Touch {
|
||||
pub phase: TouchPhase,
|
||||
pub location: (f64,f64),
|
||||
/// unique identifier of a finger.
|
||||
pub id: u64
|
||||
}
|
||||
|
||||
pub type ScanCode = u8;
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum ElementState {
|
||||
Pressed,
|
||||
Released,
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum MouseButton {
|
||||
Left,
|
||||
Right,
|
||||
Middle,
|
||||
Other(u8),
|
||||
}
|
||||
|
||||
#[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)
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
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,
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
/// 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, 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
|
||||
}
|
||||
125
src/icon.rs
Normal file
125
src/icon.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
use crate::platform_impl::PlatformIcon;
|
||||
use std::{error::Error, fmt, io, mem};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Pixel {
|
||||
pub(crate) r: u8,
|
||||
pub(crate) g: u8,
|
||||
pub(crate) b: u8,
|
||||
pub(crate) a: u8,
|
||||
}
|
||||
|
||||
pub(crate) const PIXEL_SIZE: usize = mem::size_of::<Pixel>();
|
||||
|
||||
#[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 },
|
||||
/// Produced when the number of pixels (`rgba.len() / 4`) isn't equal to `width * height`.
|
||||
/// At least one of your arguments is incorrect.
|
||||
DimensionsVsPixelCount {
|
||||
width: u32,
|
||||
height: u32,
|
||||
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, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
BadIcon::ByteCountNotDivisibleBy4 { byte_count } => write!(f,
|
||||
"The length of the `rgba` argument ({byte_count:?}) isn't divisible by 4, making it impossible to interpret as 32bpp RGBA pixels.",
|
||||
),
|
||||
BadIcon::DimensionsVsPixelCount {
|
||||
width,
|
||||
height,
|
||||
width_x_height,
|
||||
pixel_count,
|
||||
} => write!(f,
|
||||
"The specified dimensions ({width:?}x{height:?}) don't match the number of pixels supplied by the `rgba` argument ({pixel_count:?}). For those dimensions, the expected pixel count is {width_x_height:?}.",
|
||||
),
|
||||
BadIcon::OsError(e) => write!(f, "OS error when instantiating the icon: {e:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for BadIcon {}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
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 {
|
||||
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> {
|
||||
Ok(Icon {
|
||||
inner: PlatformIcon::from_rgba(rgba, width, height)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
1798
src/keyboard.rs
Normal file
1798
src/keyboard.rs
Normal file
File diff suppressed because it is too large
Load Diff
520
src/lib.rs
520
src/lib.rs
@@ -1,404 +1,198 @@
|
||||
//! 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().unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! 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 [`Event::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()`]. This function will
|
||||
//! dispatch events for every [`Window`] that was created with that particular [`EventLoop`], and
|
||||
//! will run until [`exit()`] is used, at which point [`Event::LoopExiting`].
|
||||
//!
|
||||
//! 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
|
||||
#![cfg_attr(
|
||||
any(
|
||||
windows_platform,
|
||||
macos_platform,
|
||||
android_platform,
|
||||
x11_platform,
|
||||
wayland_platform
|
||||
),
|
||||
doc = "[`EventLoopExtPumpEvents::pump_events()`][platform::pump_events::EventLoopExtPumpEvents::pump_events()]"
|
||||
)]
|
||||
#![cfg_attr(
|
||||
not(any(
|
||||
windows_platform,
|
||||
macos_platform,
|
||||
android_platform,
|
||||
x11_platform,
|
||||
wayland_platform
|
||||
)),
|
||||
doc = "`EventLoopExtPumpEvents::pump_events()`"
|
||||
)]
|
||||
//! [^1]. 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;
|
||||
//! use winit::WindowEvent;
|
||||
//! # use winit::EventsLoop;
|
||||
//! # let 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().unwrap();
|
||||
//! 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 calling `events_loop.interrupt()`.
|
||||
//! // ControlFlow::Poll continuously runs the event loop, even if the OS hasn't
|
||||
//! // dispatched any events. This is ideal for games and similar applications.
|
||||
//! event_loop.set_control_flow(ControlFlow::Poll);
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use winit::Event;
|
||||
//! use winit::WindowEvent;
|
||||
//! # use winit::EventsLoop;
|
||||
//! # let 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.
|
||||
//! event_loop.set_control_flow(ControlFlow::Wait);
|
||||
//!
|
||||
//! events_loop.run_forever(|event| {
|
||||
//! event_loop.run(move |event, elwt| {
|
||||
//! match event {
|
||||
//! Event::WindowEvent { event: WindowEvent::Closed, .. } => {
|
||||
//! println!("The window was closed ; stopping");
|
||||
//! events_loop.interrupt();
|
||||
//! Event::WindowEvent {
|
||||
//! event: WindowEvent::CloseRequested,
|
||||
//! ..
|
||||
//! } => {
|
||||
//! println!("The close button was pressed; stopping");
|
||||
//! elwt.exit();
|
||||
//! },
|
||||
//! Event::AboutToWait => {
|
||||
//! // 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 render here instead.
|
||||
//! window.request_redraw();
|
||||
//! },
|
||||
//! Event::WindowEvent {
|
||||
//! event: WindowEvent::RedrawRequested,
|
||||
//! ..
|
||||
//! } => {
|
||||
//! // Redraw the application.
|
||||
//! //
|
||||
//! // It's preferable for applications that do not render continuously to render in
|
||||
//! // this event rather than in AboutToWait, 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.
|
||||
//! [`WindowEvent`] has a [`WindowId`] member. In multi-window environments, it should be
|
||||
//! compared to the value returned by [`Window::id()`] 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 and display (see the [`platform`] module and/or the
|
||||
//! [`raw_window_handle`] and [`raw_display_handle`] methods), which in turn allows
|
||||
//! you to create an OpenGL/Vulkan/DirectX/Metal/etc. context that can be used to render graphics.
|
||||
//!
|
||||
//! Note that many platforms will display garbage data in the window's client area if the
|
||||
//! application doesn't render anything to the window by the time the desktop compositor is ready to
|
||||
//! display the window to the user. If you notice this happening, you should create the window with
|
||||
//! [`visible` set to `false`](crate::window::WindowBuilder::with_visible) and explicitly make the
|
||||
//! window visible only once you're ready to render into it.
|
||||
//!
|
||||
//! [`EventLoop`]: event_loop::EventLoop
|
||||
//! [`EventLoop::new()`]: event_loop::EventLoop::new
|
||||
//! [`EventLoop::run()`]: event_loop::EventLoop::run
|
||||
//! [`exit()`]: event_loop::EventLoopWindowTarget::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()`]: window::Window::id
|
||||
//! [`WindowEvent`]: event::WindowEvent
|
||||
//! [`DeviceEvent`]: event::DeviceEvent
|
||||
//! [`Event::UserEvent`]: event::Event::UserEvent
|
||||
//! [`Event::LoopExiting`]: event::Event::LoopExiting
|
||||
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
|
||||
//! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle
|
||||
//! [^1]: `EventLoopExtPumpEvents::pump_events()` is only available on Windows, macOS, Android, X11 and Wayland.
|
||||
|
||||
#![deny(rust_2018_idioms)]
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
#![deny(clippy::all)]
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
#![cfg_attr(feature = "cargo-clippy", deny(warnings))]
|
||||
// Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc
|
||||
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
|
||||
#[cfg(feature = "rwh_06")]
|
||||
pub use rwh_06 as raw_window_handle;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
extern crate log;
|
||||
#[cfg(feature = "serde")]
|
||||
#[macro_use]
|
||||
extern crate shared_library;
|
||||
|
||||
extern crate libc;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate winapi;
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate kernel32;
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate shell32;
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate gdi32;
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate user32;
|
||||
#[cfg(target_os = "windows")]
|
||||
extern crate dwmapi;
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate objc;
|
||||
#[cfg(target_os = "macos")]
|
||||
extern crate cgl;
|
||||
#[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 = "freebsd", target_os = "dragonfly", target_os = "openbsd"))]
|
||||
#[macro_use(wayland_env,declare_handler)]
|
||||
extern crate wayland_client;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
pub use events::*;
|
||||
pub use window::{AvailableMonitorsIter, MonitorId, get_available_monitors, get_primary_monitor};
|
||||
pub use native_monitor::NativeMonitorId;
|
||||
extern crate bitflags;
|
||||
|
||||
pub mod dpi;
|
||||
#[macro_use]
|
||||
mod api_transition;
|
||||
pub mod error;
|
||||
pub mod event;
|
||||
pub mod event_loop;
|
||||
mod icon;
|
||||
pub mod keyboard;
|
||||
pub mod monitor;
|
||||
mod platform_impl;
|
||||
pub mod window;
|
||||
|
||||
mod platform;
|
||||
mod events;
|
||||
mod window;
|
||||
pub mod platform;
|
||||
|
||||
pub mod os;
|
||||
|
||||
/// Represents a window.
|
||||
/// Wrapper for objects which winit will access on the main thread so they are effectively `Send`
|
||||
/// and `Sync`, since they always execute on a single thread.
|
||||
///
|
||||
/// # Example
|
||||
/// # Safety
|
||||
///
|
||||
/// ```no_run
|
||||
/// use winit::Event;
|
||||
/// use winit::EventsLoop;
|
||||
/// use winit::Window;
|
||||
/// use winit::WindowEvent;
|
||||
///
|
||||
/// let events_loop = EventsLoop::new();
|
||||
/// let window = Window::new(&events_loop).unwrap();
|
||||
///
|
||||
/// events_loop.run_forever(|event| {
|
||||
/// match event {
|
||||
/// Event::WindowEvent { event: WindowEvent::Closed, .. } => {
|
||||
/// events_loop.interrupt();
|
||||
/// },
|
||||
/// _ => ()
|
||||
/// }
|
||||
/// });
|
||||
/// ```
|
||||
pub struct Window {
|
||||
window: platform::Window2,
|
||||
}
|
||||
/// Winit can run only one event loop at a time, and the event loop itself is tied to some thread.
|
||||
/// The objects could be sent across the threads, but once passed to winit, they execute on the
|
||||
/// main thread if the platform demands it. Thus, marking such objects as `Send + Sync` is safe.
|
||||
#[doc(hidden)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct SendSyncWrapper<T>(pub(crate) T);
|
||||
|
||||
/// 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);
|
||||
|
||||
/// Provides a way to retreive events from the windows that were registered to it.
|
||||
// TODO: document usage in multiple threads
|
||||
pub struct EventsLoop {
|
||||
events_loop: Arc<platform::EventsLoop>,
|
||||
}
|
||||
|
||||
impl EventsLoop {
|
||||
/// Builds a new events loop.
|
||||
pub fn new() -> EventsLoop {
|
||||
EventsLoop {
|
||||
events_loop: Arc::new(platform::EventsLoop::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Fetches all the events that are pending, calls the callback function for each of them,
|
||||
/// and returns.
|
||||
#[inline]
|
||||
pub fn poll_events<F>(&self, callback: F)
|
||||
where F: FnMut(Event)
|
||||
{
|
||||
self.events_loop.poll_events(callback)
|
||||
}
|
||||
|
||||
/// Runs forever until `interrupt()` is called. Whenever an event happens, calls the callback.
|
||||
#[inline]
|
||||
pub fn run_forever<F>(&self, callback: F)
|
||||
where F: FnMut(Event)
|
||||
{
|
||||
self.events_loop.run_forever(callback)
|
||||
}
|
||||
|
||||
/// If we called `run_forever()`, stops the process of waiting for events.
|
||||
// TODO: what if we're waiting from multiple threads?
|
||||
#[inline]
|
||||
pub fn interrupt(&self) {
|
||||
self.events_loop.interrupt()
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
#[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 glutin 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)>,
|
||||
|
||||
/// If `Some`, the window will be in fullscreen mode with the given monitor.
|
||||
///
|
||||
/// The default is `None`.
|
||||
pub monitor: Option<platform::MonitorId>,
|
||||
|
||||
/// The title of the window in the title bar.
|
||||
///
|
||||
/// The default is `"glutin window"`.
|
||||
pub title: String,
|
||||
|
||||
/// 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,
|
||||
|
||||
/// [iOS only] Enable multitouch, see [UIView#multipleTouchEnabled]
|
||||
/// (https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/occ/instp/UIView/multipleTouchEnabled)
|
||||
pub multitouch: bool,
|
||||
}
|
||||
|
||||
impl Default for WindowAttributes {
|
||||
#[inline]
|
||||
fn default() -> WindowAttributes {
|
||||
WindowAttributes {
|
||||
dimensions: None,
|
||||
min_dimensions: None,
|
||||
max_dimensions: None,
|
||||
monitor: None,
|
||||
title: "glutin window".to_owned(),
|
||||
visible: true,
|
||||
transparent: false,
|
||||
decorations: true,
|
||||
multitouch: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod native_monitor {
|
||||
/// Native platform identifier for a monitor. Different platforms use fundamentally different types
|
||||
/// to represent a monitor ID.
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub enum NativeMonitorId {
|
||||
/// Cocoa and X11 use a numeric identifier to represent a monitor.
|
||||
Numeric(u32),
|
||||
|
||||
/// Win32 uses a Unicode string to represent a monitor.
|
||||
Name(String),
|
||||
|
||||
/// Other platforms (Android) don't support monitor identification.
|
||||
Unavailable
|
||||
}
|
||||
}
|
||||
unsafe impl<T> Send for SendSyncWrapper<T> {}
|
||||
unsafe impl<T> Sync for SendSyncWrapper<T> {}
|
||||
|
||||
169
src/monitor.rs
Normal file
169
src/monitor.rs
Normal file
@@ -0,0 +1,169 @@
|
||||
//! Types useful for interacting with a user's monitors.
|
||||
//!
|
||||
//! If you want to get basic information about a monitor, you can use the
|
||||
//! [`MonitorHandle`] type. This is retrieved from one of the following
|
||||
//! methods, which return an iterator of [`MonitorHandle`]:
|
||||
//! - [`EventLoopWindowTarget::available_monitors`](crate::event_loop::EventLoopWindowTarget::available_monitors).
|
||||
//! - [`Window::available_monitors`](crate::window::Window::available_monitors).
|
||||
use crate::{
|
||||
dpi::{PhysicalPosition, PhysicalSize},
|
||||
platform_impl,
|
||||
};
|
||||
|
||||
/// Describes a fullscreen video mode of a monitor.
|
||||
///
|
||||
/// Can be acquired with [`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 {
|
||||
self.monitor().cmp(&other.monitor()).then(
|
||||
self.size()
|
||||
.cmp(&other.size())
|
||||
.then(
|
||||
self.refresh_rate_millihertz()
|
||||
.cmp(&other.refresh_rate_millihertz())
|
||||
.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 / Orbital:** Always returns 32.
|
||||
/// - **iOS:** Always returns 32.
|
||||
#[inline]
|
||||
pub fn bit_depth(&self) -> u16 {
|
||||
self.video_mode.bit_depth()
|
||||
}
|
||||
|
||||
/// Returns the refresh rate of this video mode in mHz.
|
||||
#[inline]
|
||||
pub fn refresh_rate_millihertz(&self) -> u32 {
|
||||
self.video_mode.refresh_rate_millihertz()
|
||||
}
|
||||
|
||||
/// Returns the monitor that this video mode is valid for. Each monitor has
|
||||
/// a separate set of valid video modes.
|
||||
#[inline]
|
||||
pub fn monitor(&self) -> MonitorHandle {
|
||||
MonitorHandle {
|
||||
inner: self.video_mode.monitor(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for VideoMode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}x{} @ {} mHz ({} bpp)",
|
||||
self.size().width,
|
||||
self.size().height,
|
||||
self.refresh_rate_millihertz(),
|
||||
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.
|
||||
#[inline]
|
||||
pub fn name(&self) -> Option<String> {
|
||||
self.inner.name()
|
||||
}
|
||||
|
||||
/// Returns the monitor's resolution.
|
||||
#[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.
|
||||
#[inline]
|
||||
pub fn position(&self) -> PhysicalPosition<i32> {
|
||||
self.inner.position()
|
||||
}
|
||||
|
||||
/// The monitor refresh rate used by the system.
|
||||
///
|
||||
/// Return `Some` if succeed, or `None` if failed, which usually happens when the monitor
|
||||
/// the window is on is removed.
|
||||
///
|
||||
/// When using exclusive fullscreen, the refresh rate of the [`VideoMode`] that was used to
|
||||
/// enter fullscreen should be used instead.
|
||||
#[inline]
|
||||
pub fn refresh_rate_millihertz(&self) -> Option<u32> {
|
||||
self.inner.refresh_rate_millihertz()
|
||||
}
|
||||
|
||||
/// Returns the scale factor of the underlying monitor. To map logical pixels to physical
|
||||
/// pixels and vice versa, use [`Window::scale_factor`].
|
||||
///
|
||||
/// See the [`dpi`](crate::dpi) module for more information.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **X11:** Can be overridden using the `WINIT_X11_SCALE_FACTOR` environment variable.
|
||||
/// - **Wayland:** May differ from [`Window::scale_factor`].
|
||||
/// - **Android:** Always returns 1.0.
|
||||
///
|
||||
/// [`Window::scale_factor`]: crate::window::Window::scale_factor
|
||||
#[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()
|
||||
.map(|video_mode| VideoMode { video_mode })
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#![cfg(any(target_os = "android"))]
|
||||
|
||||
use std::os::raw::c_void;
|
||||
use Window;
|
||||
use WindowBuilder;
|
||||
|
||||
/// 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 {
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
#![cfg(target_os = "macos")]
|
||||
|
||||
use std::convert::From;
|
||||
use std::os::raw::c_void;
|
||||
use cocoa::appkit::NSApplicationActivationPolicy;
|
||||
use {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.
|
||||
pub trait WindowBuilderExt {
|
||||
fn with_activation_policy(self, activation_policy: ActivationPolicy) -> 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
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
174
src/os/unix.rs
174
src/os/unix.rs
@@ -1,174 +0,0 @@
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::ptr;
|
||||
use libc;
|
||||
use Window;
|
||||
use platform::Window2 as LinuxWindow;
|
||||
use platform::{UnixBackend, UNIX_BACKEND};
|
||||
use WindowBuilder;
|
||||
use platform::x11::XConnection;
|
||||
use platform::x11::ffi::XVisualInfo;
|
||||
|
||||
use wayland_client::protocol::wl_display::WlDisplay;
|
||||
use wayland_client::protocol::wl_surface::WlSurface;
|
||||
|
||||
pub use platform::x11;
|
||||
|
||||
// TODO: do not expose XConnection
|
||||
pub fn get_x11_xconnection() -> Option<Arc<XConnection>> {
|
||||
match *UNIX_BACKEND {
|
||||
UnixBackend::X(ref connec) => Some(connec.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `Window` that are specific to Unix.
|
||||
pub trait WindowExt {
|
||||
/// Returns a pointer to the `Window` 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_window(&self) -> Option<*mut libc::c_void>;
|
||||
|
||||
/// 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 libc::c_void>;
|
||||
|
||||
fn get_xlib_screen_id(&self) -> Option<*mut libc::c_void>;
|
||||
|
||||
fn get_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.
|
||||
fn get_xcb_connection(&self) -> Option<*mut libc::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 libc::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 libc::c_void>;
|
||||
|
||||
/// Returns a reference to the `WlSurface` object of wayland that is used by this window.
|
||||
///
|
||||
/// For use with the `wayland-client` crate.
|
||||
///
|
||||
/// **This function is not part of winit's public API.**
|
||||
///
|
||||
/// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
|
||||
fn get_wayland_client_surface(&self) -> Option<&WlSurface>;
|
||||
|
||||
/// Returns a pointer to the `WlDisplay` object of wayland that is used by this window.
|
||||
///
|
||||
/// For use with the `wayland-client` crate.
|
||||
///
|
||||
/// **This function is not part of winit's public API.**
|
||||
///
|
||||
/// Returns `None` if the window doesn't use wayland (if it uses xlib for example).
|
||||
fn get_wayland_client_display(&self) -> Option<&WlDisplay>;
|
||||
}
|
||||
|
||||
impl WindowExt for Window {
|
||||
#[inline]
|
||||
fn get_xlib_window(&self) -> Option<*mut libc::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.get_xlib_window()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_xlib_display(&self) -> Option<*mut libc::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.get_xlib_display()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_xlib_screen_id(&self) -> Option<*mut libc::c_void> {
|
||||
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 libc::c_void> {
|
||||
match self.window {
|
||||
LinuxWindow::X(ref w) => Some(w.get_xcb_connection()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_wayland_surface(&self) -> Option<*mut libc::c_void> {
|
||||
use wayland_client::Proxy;
|
||||
self.get_wayland_client_surface().map(|p| p.ptr() as *mut _)
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
fn get_wayland_display(&self) -> Option<*mut libc::c_void> {
|
||||
use wayland_client::Proxy;
|
||||
self.get_wayland_client_display().map(|p| p.ptr() as *mut _)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_wayland_client_surface(&self) -> Option<&WlSurface> {
|
||||
match self.window {
|
||||
LinuxWindow::Wayland(ref w) => Some(w.get_surface()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_wayland_client_display(&self) -> Option<&WlDisplay> {
|
||||
match self.window {
|
||||
LinuxWindow::Wayland(ref w) => Some(w.get_display()),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
#![cfg(target_os = "windows")]
|
||||
|
||||
use libc;
|
||||
use Window;
|
||||
use WindowBuilder;
|
||||
use winapi;
|
||||
|
||||
/// Additional methods on `Window` that are specific to Windows.
|
||||
pub trait WindowExt {
|
||||
/// Returns a pointer to the `Window` 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_hwnd(&self) -> *mut libc::c_void;
|
||||
}
|
||||
|
||||
impl WindowExt for Window {
|
||||
#[inline]
|
||||
fn get_hwnd(&self) -> *mut libc::c_void {
|
||||
self.window.platform_window()
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on `WindowBuilder` that are specific to Windows.
|
||||
pub trait WindowBuilderExt {
|
||||
fn with_parent_window(self, parent: winapi::HWND) -> WindowBuilder;
|
||||
}
|
||||
|
||||
impl WindowBuilderExt for WindowBuilder {
|
||||
/// Sets a parent to the window to be created
|
||||
#[inline]
|
||||
fn with_parent_window(mut self, parent: winapi::HWND) -> WindowBuilder {
|
||||
self.platform_specific.parent = Some(parent);
|
||||
self
|
||||
}
|
||||
}
|
||||
93
src/platform/android.rs
Normal file
93
src/platform/android.rs
Normal file
@@ -0,0 +1,93 @@
|
||||
use crate::{
|
||||
event_loop::{EventLoop, EventLoopBuilder, EventLoopWindowTarget},
|
||||
window::{Window, WindowBuilder},
|
||||
};
|
||||
|
||||
use android_activity::{AndroidApp, ConfigurationRef, 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) -> ConfigurationRef;
|
||||
}
|
||||
|
||||
impl WindowExtAndroid for Window {
|
||||
fn content_rect(&self) -> Rect {
|
||||
self.window.content_rect()
|
||||
}
|
||||
|
||||
fn config(&self) -> ConfigurationRef {
|
||||
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 {}
|
||||
|
||||
pub trait EventLoopBuilderExtAndroid {
|
||||
/// Associates the `AndroidApp` that was passed to `android_main()` with the event loop
|
||||
///
|
||||
/// This must be called on Android since the `AndroidApp` is not global state.
|
||||
fn with_android_app(&mut self, app: AndroidApp) -> &mut Self;
|
||||
|
||||
/// Calling this will mark the volume keys to be manually handled by the application
|
||||
///
|
||||
/// Default is to let the operating system handle the volume keys
|
||||
fn handle_volume_keys(&mut self) -> &mut Self;
|
||||
}
|
||||
|
||||
impl<T> EventLoopBuilderExtAndroid for EventLoopBuilder<T> {
|
||||
fn with_android_app(&mut self, app: AndroidApp) -> &mut Self {
|
||||
self.platform_specific.android_app = Some(app);
|
||||
self
|
||||
}
|
||||
|
||||
fn handle_volume_keys(&mut self) -> &mut Self {
|
||||
self.platform_specific.ignore_volume_keys = false;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Re-export of the `android_activity` API
|
||||
///
|
||||
/// Winit re-exports the `android_activity` API for convenience so that most
|
||||
/// applications can rely on the Winit crate to resolve the required version of
|
||||
/// `android_activity` and avoid any chance of a conflict between Winit and the
|
||||
/// application crate.
|
||||
///
|
||||
/// Unlike most libraries there can only be a single implementation
|
||||
/// of the `android_activity` glue crate linked with an application because
|
||||
/// it is responsible for the application's `android_main()` entry point.
|
||||
///
|
||||
/// Since Winit depends on a specific version of `android_activity` the simplest
|
||||
/// way to avoid creating a conflict is for applications to avoid explicitly
|
||||
/// depending on the `android_activity` crate, and instead consume the API that
|
||||
/// is re-exported by Winit.
|
||||
///
|
||||
/// For compatibility applications should then import the `AndroidApp` type for
|
||||
/// their `android_main(app: AndroidApp)` function like:
|
||||
/// ```rust
|
||||
/// #[cfg(target_os="android")]
|
||||
/// use winit::platform::android::activity::AndroidApp;
|
||||
/// ```
|
||||
pub mod activity {
|
||||
// We enable the `"native-activity"` feature just so that we can build the
|
||||
// docs, but it'll be very confusing for users to see the docs with that
|
||||
// feature enabled, so we avoid inlining it so that they're forced to view
|
||||
// it on the crate's own docs.rs page.
|
||||
#[doc(no_inline)]
|
||||
pub use android_activity::*;
|
||||
}
|
||||
@@ -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,275 +0,0 @@
|
||||
#![cfg(target_os = "android")]
|
||||
|
||||
extern crate android_glue;
|
||||
|
||||
use libc;
|
||||
use std::ffi::{CString};
|
||||
use std::sync::mpsc::{Receiver, channel};
|
||||
use std::os::raw::c_void;
|
||||
use {CreationError, WindowEvent as Event, MouseCursor};
|
||||
use CreationError::OsError;
|
||||
use events::ElementState::{Pressed, Released};
|
||||
use events::{Touch, TouchPhase};
|
||||
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use CursorState;
|
||||
use WindowAttributes;
|
||||
use native_monitor::NativeMonitorId;
|
||||
|
||||
gen_api_transition!();
|
||||
|
||||
pub struct Window {
|
||||
native_window: *const c_void,
|
||||
event_rx: Receiver<android_glue::Event>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MonitorId;
|
||||
|
||||
mod ffi;
|
||||
|
||||
#[inline]
|
||||
pub fn get_available_monitors() -> VecDeque<MonitorId> {
|
||||
let mut rb = VecDeque::new();
|
||||
rb.push_back(MonitorId);
|
||||
rb
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor() -> MonitorId {
|
||||
MonitorId
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
#[inline]
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
Some("Primary".to_string())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> NativeMonitorId {
|
||||
NativeMonitorId::Unavailable
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes;
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificHeadlessBuilderAttributes;
|
||||
|
||||
pub struct PollEventsIterator<'a> {
|
||||
window: &'a Window,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for PollEventsIterator<'a> {
|
||||
type Item = Event;
|
||||
|
||||
fn next(&mut self) -> Option<Event> {
|
||||
match self.window.event_rx.try_recv() {
|
||||
Ok(android_glue::Event::EventMotion(motion)) => {
|
||||
Some(Event::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,
|
||||
}))
|
||||
},
|
||||
Ok(android_glue::Event::InitWindow) => {
|
||||
// The activity went to foreground.
|
||||
Some(Event::Suspended(false))
|
||||
},
|
||||
Ok(android_glue::Event::TermWindow) => {
|
||||
// The activity went to background.
|
||||
Some(Event::Suspended(true))
|
||||
},
|
||||
Ok(android_glue::Event::WindowResized) |
|
||||
Ok(android_glue::Event::ConfigChanged) => {
|
||||
// Activity Orientation changed or resized.
|
||||
self.window.get_inner_size().map(|s| Event::Resized(s.0, s.1))
|
||||
},
|
||||
Ok(android_glue::Event::WindowRedrawNeeded) => {
|
||||
// The activity needs to be redrawn.
|
||||
Some(Event::Refresh)
|
||||
}
|
||||
_ => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WaitEventsIterator<'a> {
|
||||
window: &'a Window,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for WaitEventsIterator<'a> {
|
||||
type Item = Event;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Event> {
|
||||
loop {
|
||||
// calling poll_events()
|
||||
if let Some(ev) = self.window.poll_events().next() {
|
||||
return Some(ev);
|
||||
}
|
||||
|
||||
// TODO: Implement a proper way of sleeping on the event queue
|
||||
// timer::sleep(Duration::milliseconds(16));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(win_attribs: &WindowAttributes, _: &PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Window, CreationError>
|
||||
{
|
||||
use std::{mem, ptr};
|
||||
|
||||
// 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")));
|
||||
}
|
||||
|
||||
let (tx, rx) = channel();
|
||||
android_glue::add_sender(tx);
|
||||
android_glue::set_multitouch(win_attribs.multitouch);
|
||||
|
||||
Ok(Window {
|
||||
native_window: native_window as *const _,
|
||||
event_rx: rx,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_native_window(&self) -> *const c_void {
|
||||
self.native_window
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_closed(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[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 set_position(&self, _x: i32, _y: i32) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
let native_window = unsafe { android_glue::get_native_window() };
|
||||
|
||||
if native_window.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some((
|
||||
unsafe { ffi::ANativeWindow_getWidth(native_window as *const _) } as u32,
|
||||
unsafe { ffi::ANativeWindow_getHeight(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 create_window_proxy(&self) -> WindowProxy {
|
||||
WindowProxy
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn poll_events(&self) -> PollEventsIterator {
|
||||
PollEventsIterator {
|
||||
window: self
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn wait_events(&self) -> WaitEventsIterator {
|
||||
WaitEventsIterator {
|
||||
window: self
|
||||
}
|
||||
}
|
||||
|
||||
#[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, 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<(), ()> {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Window {}
|
||||
unsafe impl Sync for Window {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WindowProxy;
|
||||
|
||||
impl WindowProxy {
|
||||
#[inline]
|
||||
pub fn wakeup_event_loop(&self) {
|
||||
android_glue::wake_event_loop();
|
||||
}
|
||||
}
|
||||
308
src/platform/ios.rs
Normal file
308
src/platform/ios.rs
Normal file
@@ -0,0 +1,308 @@
|
||||
use std::os::raw::c_void;
|
||||
|
||||
use icrate::Foundation::MainThreadMarker;
|
||||
use objc2::rc::Id;
|
||||
|
||||
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 {
|
||||
/// 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 sets the value of the
|
||||
/// [`prefersStatusBarHidden`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621440-prefersstatusbarhidden?language=objc)
|
||||
/// property.
|
||||
///
|
||||
/// [`setNeedsStatusBarAppearanceUpdate()`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621354-setneedsstatusbarappearanceupdat?language=objc)
|
||||
/// is also called for you.
|
||||
fn set_prefers_status_bar_hidden(&self, hidden: bool);
|
||||
|
||||
/// Sets the preferred status bar style for the [`Window`].
|
||||
///
|
||||
/// The default is system-defined.
|
||||
///
|
||||
/// This sets the value of the
|
||||
/// [`preferredStatusBarStyle`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621416-preferredstatusbarstyle?language=objc)
|
||||
/// property.
|
||||
///
|
||||
/// [`setNeedsStatusBarAppearanceUpdate()`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621354-setneedsstatusbarappearanceupdat?language=objc)
|
||||
/// is also called for you.
|
||||
fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle);
|
||||
}
|
||||
|
||||
impl WindowExtIOS for Window {
|
||||
#[inline]
|
||||
fn set_scale_factor(&self, scale_factor: f64) {
|
||||
self.window
|
||||
.maybe_queue_on_main(move |w| w.set_scale_factor(scale_factor))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_valid_orientations(&self, valid_orientations: ValidOrientations) {
|
||||
self.window
|
||||
.maybe_queue_on_main(move |w| w.set_valid_orientations(valid_orientations))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_prefers_home_indicator_hidden(&self, hidden: bool) {
|
||||
self.window
|
||||
.maybe_queue_on_main(move |w| w.set_prefers_home_indicator_hidden(hidden))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_preferred_screen_edges_deferring_system_gestures(&self, edges: ScreenEdge) {
|
||||
self.window.maybe_queue_on_main(move |w| {
|
||||
w.set_preferred_screen_edges_deferring_system_gestures(edges)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_prefers_status_bar_hidden(&self, hidden: bool) {
|
||||
self.window
|
||||
.maybe_queue_on_main(move |w| w.set_prefers_status_bar_hidden(hidden))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_preferred_status_bar_style(&self, status_bar_style: StatusBarStyle) {
|
||||
self.window
|
||||
.maybe_queue_on_main(move |w| w.set_preferred_status_bar_style(status_bar_style))
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional methods on [`WindowBuilder`] that are specific to iOS.
|
||||
pub trait WindowBuilderExtIOS {
|
||||
/// 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) -> Self;
|
||||
|
||||
/// 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) -> Self;
|
||||
|
||||
/// 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) -> Self;
|
||||
|
||||
/// 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) -> Self;
|
||||
|
||||
/// 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) -> Self;
|
||||
|
||||
/// Sets the style of the [`Window`]'s status bar.
|
||||
///
|
||||
/// The default is system-defined.
|
||||
///
|
||||
/// This sets the initial value returned by
|
||||
/// [`-[UIViewController preferredStatusBarStyle]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621416-preferredstatusbarstyle?language=objc),
|
||||
fn with_preferred_status_bar_style(self, status_bar_style: StatusBarStyle) -> Self;
|
||||
}
|
||||
|
||||
impl WindowBuilderExtIOS for WindowBuilder {
|
||||
#[inline]
|
||||
fn with_scale_factor(mut self, scale_factor: f64) -> Self {
|
||||
self.platform_specific.scale_factor = Some(scale_factor);
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_valid_orientations(mut self, valid_orientations: ValidOrientations) -> Self {
|
||||
self.platform_specific.valid_orientations = valid_orientations;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_prefers_home_indicator_hidden(mut self, hidden: bool) -> Self {
|
||||
self.platform_specific.prefers_home_indicator_hidden = hidden;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_preferred_screen_edges_deferring_system_gestures(mut self, edges: ScreenEdge) -> Self {
|
||||
self.platform_specific
|
||||
.preferred_screen_edges_deferring_system_gestures = edges;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_prefers_status_bar_hidden(mut self, hidden: bool) -> Self {
|
||||
self.platform_specific.prefers_status_bar_hidden = hidden;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn with_preferred_status_bar_style(mut self, status_bar_style: StatusBarStyle) -> Self {
|
||||
self.platform_specific.preferred_status_bar_style = status_bar_style;
|
||||
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 {
|
||||
// SAFETY: The marker is only used to get the pointer of the screen
|
||||
let mtm = unsafe { MainThreadMarker::new_unchecked() };
|
||||
Id::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn preferred_video_mode(&self) -> VideoMode {
|
||||
VideoMode {
|
||||
video_mode: self.inner.preferred_video_mode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Valid orientations for a particular [`Window`].
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum ValidOrientations {
|
||||
/// Excludes `PortraitUpsideDown` on iphone
|
||||
#[default]
|
||||
LandscapeAndPortrait,
|
||||
|
||||
Landscape,
|
||||
|
||||
/// Excludes `PortraitUpsideDown` on iphone
|
||||
Portrait,
|
||||
}
|
||||
|
||||
/// The device [idiom].
|
||||
///
|
||||
/// [idiom]: https://developer.apple.com/documentation/uikit/uidevice/1620037-userinterfaceidiom?language=objc
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
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, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ScreenEdge: u8 {
|
||||
const NONE = 0;
|
||||
const TOP = 1 << 0;
|
||||
const LEFT = 1 << 1;
|
||||
const BOTTOM = 1 << 2;
|
||||
const RIGHT = 1 << 3;
|
||||
const ALL = ScreenEdge::TOP.bits() | ScreenEdge::LEFT.bits()
|
||||
| ScreenEdge::BOTTOM.bits() | ScreenEdge::RIGHT.bits();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub enum StatusBarStyle {
|
||||
#[default]
|
||||
Default,
|
||||
LightContent,
|
||||
DarkContent,
|
||||
}
|
||||
@@ -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 {
|
||||
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,497 +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 Closed
|
||||
//!
|
||||
//! Keep in mind that after Closed event is received every attempt to draw with
|
||||
//! opengl will result in segfault.
|
||||
//!
|
||||
//! Also note that app will not receive Closed 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 native_monitor::NativeMonitorId;
|
||||
use { CreationError, CursorState, MouseCursor, WindowAttributes };
|
||||
use WindowEvent as Event;
|
||||
use events::{ Touch, TouchPhase };
|
||||
|
||||
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;
|
||||
|
||||
pub struct PollEventsIterator<'a> {
|
||||
window: &'a Window,
|
||||
}
|
||||
|
||||
pub struct WaitEventsIterator<'a> {
|
||||
window: &'a Window,
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_available_monitors() -> VecDeque<MonitorId> {
|
||||
let mut rb = VecDeque::new();
|
||||
rb.push_back(MonitorId);
|
||||
rb
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor() -> MonitorId {
|
||||
MonitorId
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
#[inline]
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
Some("Primary".to_string())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> NativeMonitorId {
|
||||
NativeMonitorId::Unavailable
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
gen_api_transition!();
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes;
|
||||
|
||||
impl Window {
|
||||
|
||||
pub fn new(_: &WindowAttributes, _: &PlatformSpecificWindowBuilderAttributes) -> Result<Window, CreationError>
|
||||
{
|
||||
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 window = Window {
|
||||
delegate_state: state
|
||||
};
|
||||
|
||||
return Ok(window)
|
||||
}
|
||||
}
|
||||
|
||||
Window::create_delegate_class();
|
||||
Window::create_view_class();
|
||||
Window::start_app();
|
||||
|
||||
Err(CreationError::OsError(format!("Couldn't create UIApplication")))
|
||||
}
|
||||
|
||||
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::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::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::Closed);
|
||||
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::Touch(Touch {
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
#[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 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 poll_events(&self) -> PollEventsIterator {
|
||||
PollEventsIterator {
|
||||
window: self
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn wait_events(&self) -> WaitEventsIterator {
|
||||
WaitEventsIterator {
|
||||
window: self
|
||||
}
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl WindowProxy {
|
||||
#[inline]
|
||||
pub fn wakeup_event_loop(&self) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'a> Iterator for WaitEventsIterator<'a> {
|
||||
type Item = Event;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Event> {
|
||||
loop {
|
||||
if let Some(ev) = self.window.poll_events().next() {
|
||||
return Some(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for PollEventsIterator<'a> {
|
||||
type Item = Event;
|
||||
|
||||
fn next(&mut self) -> Option<Event> {
|
||||
unsafe {
|
||||
let state = &mut *self.window.delegate_state;
|
||||
|
||||
if let Some(event) = state.events_queue.pop_front() {
|
||||
return Some(event)
|
||||
}
|
||||
|
||||
// jump hack, so we won't quit on willTerminate event before processing it
|
||||
if setjmp(mem::transmute(&mut jmpbuf)) != 0 {
|
||||
return state.events_queue.pop_front()
|
||||
}
|
||||
|
||||
// run runloop
|
||||
let seconds: CFTimeInterval = 0.000002;
|
||||
while CFRunLoopRunInMode(kCFRunLoopDefaultMode, seconds, 1) == kCFRunLoopRunHandledSource {}
|
||||
|
||||
state.events_queue.pop_front()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,345 +0,0 @@
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::Arc;
|
||||
|
||||
use CreationError;
|
||||
use CursorState;
|
||||
use MouseCursor;
|
||||
use libc;
|
||||
|
||||
use self::x11::XConnection;
|
||||
use self::x11::XError;
|
||||
use self::x11::XNotSupported;
|
||||
use self::x11::ffi::XVisualInfo;
|
||||
|
||||
mod dlopen;
|
||||
pub mod wayland;
|
||||
pub mod x11;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct PlatformSpecificWindowBuilderAttributes {
|
||||
pub visual_infos: Option<XVisualInfo>,
|
||||
pub screen_id: Option<i32>,
|
||||
}
|
||||
|
||||
pub enum UnixBackend {
|
||||
X(Arc<XConnection>),
|
||||
Wayland(Arc<wayland::WaylandContext>),
|
||||
Error(XNotSupported),
|
||||
}
|
||||
|
||||
lazy_static!(
|
||||
pub static ref UNIX_BACKEND: UnixBackend = {
|
||||
if let Some(ctxt) = wayland::WaylandContext::init() {
|
||||
UnixBackend::Wayland(Arc::new(ctxt))
|
||||
} else {
|
||||
match XConnection::new(Some(x_error_callback)) {
|
||||
Ok(x) => UnixBackend::X(Arc::new(x)),
|
||||
Err(e) => UnixBackend::Error(e),
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
|
||||
pub enum Window2 {
|
||||
#[doc(hidden)]
|
||||
X(x11::Window2),
|
||||
#[doc(hidden)]
|
||||
Wayland(wayland::Window)
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum WindowId {
|
||||
#[doc(hidden)]
|
||||
X(x11::WindowId),
|
||||
#[doc(hidden)]
|
||||
Wayland(wayland::WindowId)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum MonitorId {
|
||||
#[doc(hidden)]
|
||||
X(x11::MonitorId),
|
||||
#[doc(hidden)]
|
||||
Wayland(wayland::MonitorId),
|
||||
#[doc(hidden)]
|
||||
None,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_available_monitors() -> VecDeque<MonitorId> {
|
||||
match *UNIX_BACKEND {
|
||||
UnixBackend::Wayland(ref ctxt) => wayland::get_available_monitors(ctxt)
|
||||
.into_iter()
|
||||
.map(MonitorId::Wayland)
|
||||
.collect(),
|
||||
UnixBackend::X(ref connec) => x11::get_available_monitors(connec)
|
||||
.into_iter()
|
||||
.map(MonitorId::X)
|
||||
.collect(),
|
||||
UnixBackend::Error(_) => { let mut d = VecDeque::new(); d.push_back(MonitorId::None); d},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor() -> MonitorId {
|
||||
match *UNIX_BACKEND {
|
||||
UnixBackend::Wayland(ref ctxt) => MonitorId::Wayland(wayland::get_primary_monitor(ctxt)),
|
||||
UnixBackend::X(ref connec) => MonitorId::X(x11::get_primary_monitor(connec)),
|
||||
UnixBackend::Error(_) => MonitorId::None,
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
&MonitorId::None => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> ::native_monitor::NativeMonitorId {
|
||||
match self {
|
||||
&MonitorId::X(ref m) => m.get_native_identifier(),
|
||||
&MonitorId::Wayland(ref m) => m.get_native_identifier(),
|
||||
&MonitorId::None => unimplemented!() // FIXME:
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
match self {
|
||||
&MonitorId::X(ref m) => m.get_dimensions(),
|
||||
&MonitorId::Wayland(ref m) => m.get_dimensions(),
|
||||
&MonitorId::None => (800, 600), // FIXME:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Window2 {
|
||||
#[inline]
|
||||
pub fn new(events_loop: ::std::sync::Arc<EventsLoop>, window: &::WindowAttributes,
|
||||
pl_attribs: &PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Window2, CreationError>
|
||||
{
|
||||
match *UNIX_BACKEND {
|
||||
UnixBackend::Wayland(ref ctxt) => {
|
||||
if let EventsLoop::Wayland(ref evlp) = *events_loop {
|
||||
wayland::Window::new(evlp, ctxt.clone(), window).map(Window2::Wayland)
|
||||
} else {
|
||||
// It is not possible to instanciate an EventsLoop not matching its backend
|
||||
unreachable!()
|
||||
}
|
||||
},
|
||||
|
||||
UnixBackend::X(ref connec) => {
|
||||
x11::Window2::new(events_loop, connec, window, pl_attribs).map(Window2::X)
|
||||
},
|
||||
UnixBackend::Error(_) => {
|
||||
// If the Backend is Error(), it is not possible to instanciate an EventsLoop at all,
|
||||
// thus this function cannot be called!
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
match self {
|
||||
&Window2::X(ref w) => WindowId::X(w.id()),
|
||||
&Window2::Wayland(ref w) => WindowId::Wayland(w.id())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_title(&self, title: &str) {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.set_title(title),
|
||||
&Window2::Wayland(ref w) => w.set_title(title)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn show(&self) {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.show(),
|
||||
&Window2::Wayland(ref w) => w.show()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hide(&self) {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.hide(),
|
||||
&Window2::Wayland(ref w) => w.hide()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_position(&self) -> Option<(i32, i32)> {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.get_position(),
|
||||
&Window2::Wayland(ref w) => w.get_position()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_position(&self, x: i32, y: i32) {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.set_position(x, y),
|
||||
&Window2::Wayland(ref w) => w.set_position(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_inner_size(&self) -> Option<(u32, u32)> {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.get_inner_size(),
|
||||
&Window2::Wayland(ref w) => w.get_inner_size()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_outer_size(&self) -> Option<(u32, u32)> {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.get_outer_size(),
|
||||
&Window2::Wayland(ref w) => w.get_outer_size()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_inner_size(&self, x: u32, y: u32) {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.set_inner_size(x, y),
|
||||
&Window2::Wayland(ref w) => w.set_inner_size(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, cursor: MouseCursor) {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.set_cursor(cursor),
|
||||
&Window2::Wayland(ref w) => w.set_cursor(cursor)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.set_cursor_state(state),
|
||||
&Window2::Wayland(ref w) => w.set_cursor_state(state)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn hidpi_factor(&self) -> f32 {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.hidpi_factor(),
|
||||
&Window2::Wayland(ref w) => w.hidpi_factor()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, x: i32, y: i32) -> Result<(), ()> {
|
||||
match self {
|
||||
&Window2::X(ref w) => w.set_cursor_position(x, y),
|
||||
&Window2::Wayland(ref w) => w.set_cursor_position(x, y)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_display(&self) -> *mut libc::c_void {
|
||||
use wayland_client::Proxy;
|
||||
match self {
|
||||
&Window2::X(ref w) => w.platform_display(),
|
||||
&Window2::Wayland(ref w) => w.get_display().ptr() as *mut _
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn platform_window(&self) -> *mut libc::c_void {
|
||||
use wayland_client::Proxy;
|
||||
match self {
|
||||
&Window2::X(ref w) => w.platform_window(),
|
||||
&Window2::Wayland(ref w) => w.get_surface().ptr() as *mut _
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn x_error_callback(dpy: *mut x11::ffi::Display, event: *mut x11::ffi::XErrorEvent)
|
||||
-> libc::c_int
|
||||
{
|
||||
use std::ffi::CStr;
|
||||
|
||||
if let UnixBackend::X(ref x) = *UNIX_BACKEND {
|
||||
let mut buff: Vec<u8> = Vec::with_capacity(1024);
|
||||
(x.xlib.XGetErrorText)(dpy, (*event).error_code as i32, buff.as_mut_ptr() as *mut libc::c_char, buff.capacity() as i32);
|
||||
let description = CStr::from_ptr(buff.as_mut_ptr() as *const libc::c_char).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,
|
||||
};
|
||||
|
||||
*x.latest_error.lock().unwrap() = Some(error);
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
pub enum EventsLoop {
|
||||
#[doc(hidden)]
|
||||
Wayland(wayland::EventsLoop),
|
||||
#[doc(hidden)]
|
||||
X(x11::EventsLoop)
|
||||
}
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn new() -> EventsLoop {
|
||||
match *UNIX_BACKEND {
|
||||
UnixBackend::Wayland(ref ctxt) => {
|
||||
EventsLoop::Wayland(wayland::EventsLoop::new(ctxt.clone()))
|
||||
},
|
||||
|
||||
UnixBackend::X(_) => {
|
||||
EventsLoop::X(x11::EventsLoop::new())
|
||||
},
|
||||
|
||||
UnixBackend::Error(_) => {
|
||||
panic!("Attempted to create an EventsLoop while no backend was available.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interrupt(&self) {
|
||||
match *self {
|
||||
EventsLoop::Wayland(ref evlp) => evlp.interrupt(),
|
||||
EventsLoop::X(ref evlp) => evlp.interrupt()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll_events<F>(&self, callback: F)
|
||||
where F: FnMut(::Event)
|
||||
{
|
||||
match *self {
|
||||
EventsLoop::Wayland(ref evlp) => evlp.poll_events(callback),
|
||||
EventsLoop::X(ref evlp) => evlp.poll_events(callback)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_forever<F>(&self, callback: F)
|
||||
where F: FnMut(::Event)
|
||||
{
|
||||
match *self {
|
||||
EventsLoop::Wayland(ref evlp) => evlp.run_forever(callback),
|
||||
EventsLoop::X(ref evlp) => evlp.run_forever(callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,297 +0,0 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use wayland_client::{EnvHandler, default_connect, EventQueue, EventQueueHandle, Init, Proxy};
|
||||
use wayland_client::protocol::{wl_compositor, wl_seat, wl_shell, wl_shm, wl_subcompositor,
|
||||
wl_display, wl_registry, wl_output, wl_surface};
|
||||
|
||||
use super::wayland_window;
|
||||
|
||||
/*
|
||||
* Registry and globals handling
|
||||
*/
|
||||
|
||||
wayland_env!(InnerEnv,
|
||||
compositor: wl_compositor::WlCompositor,
|
||||
shell: wl_shell::WlShell,
|
||||
shm: wl_shm::WlShm,
|
||||
subcompositor: wl_subcompositor::WlSubcompositor
|
||||
);
|
||||
|
||||
struct WaylandEnv {
|
||||
registry: wl_registry::WlRegistry,
|
||||
inner: EnvHandler<InnerEnv>,
|
||||
monitors: Vec<OutputInfo>,
|
||||
my_id: usize,
|
||||
}
|
||||
|
||||
struct OutputInfo {
|
||||
output: wl_output::WlOutput,
|
||||
id: u32,
|
||||
scale: f32,
|
||||
pix_size: (u32, u32),
|
||||
name: String
|
||||
}
|
||||
|
||||
impl OutputInfo {
|
||||
fn new(output: wl_output::WlOutput, id: u32) -> OutputInfo {
|
||||
OutputInfo {
|
||||
output: output,
|
||||
id: id,
|
||||
scale: 1.0,
|
||||
pix_size: (0, 0),
|
||||
name: "".into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WaylandEnv {
|
||||
fn new(registry: wl_registry::WlRegistry) -> WaylandEnv {
|
||||
WaylandEnv {
|
||||
registry: registry,
|
||||
inner: EnvHandler::new(),
|
||||
monitors: Vec::new(),
|
||||
my_id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_seat(&self) -> Option<wl_seat::WlSeat> {
|
||||
for &(name, ref interface, version) in self.inner.globals() {
|
||||
if interface == "wl_seat" {
|
||||
if version < 5 {
|
||||
panic!("Winit requires at least version 5 of the wl_seat global.");
|
||||
}
|
||||
let seat = self.registry.bind::<wl_seat::WlSeat>(5, name);
|
||||
return Some(seat)
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Init for WaylandEnv {
|
||||
fn init(&mut self, evqh: &mut EventQueueHandle, index: usize) {
|
||||
evqh.register::<_, WaylandEnv>(&self.registry, index);
|
||||
self.my_id = index
|
||||
}
|
||||
}
|
||||
|
||||
impl wl_registry::Handler for WaylandEnv {
|
||||
fn global(&mut self,
|
||||
evqh: &mut EventQueueHandle,
|
||||
registry: &wl_registry::WlRegistry,
|
||||
name: u32,
|
||||
interface: String,
|
||||
version: u32)
|
||||
{
|
||||
if interface == "wl_output" {
|
||||
// intercept outputs
|
||||
// this "expect" cannot trigger (see https://github.com/vberger/wayland-client-rs/issues/69)
|
||||
let output = self.registry.bind::<wl_output::WlOutput>(1, name);
|
||||
evqh.register::<_, WaylandEnv>(&output, self.my_id);
|
||||
self.monitors.push(OutputInfo::new(output, name));
|
||||
}
|
||||
self.inner.global(evqh, registry, name, interface, version);
|
||||
}
|
||||
|
||||
fn global_remove(&mut self,
|
||||
evqh: &mut EventQueueHandle,
|
||||
registry: &wl_registry::WlRegistry,
|
||||
name: u32)
|
||||
{
|
||||
// prune old monitors
|
||||
self.monitors.retain(|m| m.id != name);
|
||||
self.inner.global_remove(evqh, registry, name);
|
||||
}
|
||||
}
|
||||
|
||||
declare_handler!(WaylandEnv, wl_registry::Handler, wl_registry::WlRegistry);
|
||||
|
||||
impl wl_output::Handler for WaylandEnv {
|
||||
fn geometry(&mut self,
|
||||
_: &mut EventQueueHandle,
|
||||
proxy: &wl_output::WlOutput,
|
||||
_x: i32, _y: i32,
|
||||
_physical_width: i32, _physical_height: i32,
|
||||
_subpixel: wl_output::Subpixel,
|
||||
make: String, model: String,
|
||||
_transform: wl_output::Transform)
|
||||
{
|
||||
for m in self.monitors.iter_mut().filter(|m| m.output.equals(proxy)) {
|
||||
m.name = format!("{} ({})", model, make);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fn mode(&mut self,
|
||||
_: &mut EventQueueHandle,
|
||||
proxy: &wl_output::WlOutput,
|
||||
flags: wl_output::Mode,
|
||||
width: i32, height: i32,
|
||||
_refresh: i32)
|
||||
{
|
||||
if flags.contains(wl_output::Current) {
|
||||
for m in self.monitors.iter_mut().filter(|m| m.output.equals(proxy)) {
|
||||
m.pix_size = (width as u32, height as u32);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fn scale(&mut self,
|
||||
_: &mut EventQueueHandle,
|
||||
proxy: &wl_output::WlOutput,
|
||||
factor: i32)
|
||||
{
|
||||
for m in self.monitors.iter_mut().filter(|m| m.output.equals(proxy)) {
|
||||
m.scale = factor as f32;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare_handler!(WaylandEnv, wl_output::Handler, wl_output::WlOutput);
|
||||
|
||||
/*
|
||||
* Main context struct
|
||||
*/
|
||||
|
||||
pub struct WaylandContext {
|
||||
pub display: wl_display::WlDisplay,
|
||||
evq: Mutex<EventQueue>,
|
||||
env_id: usize,
|
||||
}
|
||||
|
||||
impl WaylandContext {
|
||||
pub fn init() -> Option<WaylandContext> {
|
||||
// attempt to connect to the wayland server
|
||||
// this handles both "no libwayland" and "no compositor" cases
|
||||
let (display, mut event_queue) = match default_connect() {
|
||||
Ok(ret) => ret,
|
||||
Err(_) => return None
|
||||
};
|
||||
|
||||
// this "expect" cannot trigger (see https://github.com/vberger/wayland-client-rs/issues/69)
|
||||
let registry = display.get_registry();
|
||||
let env_id = event_queue.add_handler_with_init(WaylandEnv::new(registry));
|
||||
// two syncs fully initialize
|
||||
event_queue.sync_roundtrip().expect("Wayland connection unexpectedly lost");
|
||||
event_queue.sync_roundtrip().expect("Wayland connection unexpectedly lost");
|
||||
|
||||
Some(WaylandContext {
|
||||
evq: Mutex::new(event_queue),
|
||||
display: display,
|
||||
env_id: env_id
|
||||
})
|
||||
}
|
||||
|
||||
pub fn dispatch_pending(&self) {
|
||||
let mut guard = self.evq.lock().unwrap();
|
||||
guard.dispatch_pending().expect("Wayland connection unexpectedly lost");
|
||||
}
|
||||
|
||||
pub fn dispatch(&self) {
|
||||
let mut guard = self.evq.lock().unwrap();
|
||||
guard.dispatch().expect("Wayland connection unexpectedly lost");
|
||||
}
|
||||
|
||||
pub fn flush(&self) {
|
||||
let _ = self.display.flush();
|
||||
}
|
||||
|
||||
pub fn get_seat(&self) -> Option<wl_seat::WlSeat> {
|
||||
let mut guard = self.evq.lock().unwrap();
|
||||
let state = guard.state();
|
||||
state.get_handler::<WaylandEnv>(self.env_id).get_seat()
|
||||
}
|
||||
|
||||
pub fn with_output<F>(&self, id: MonitorId, f: F) where F: FnOnce(&wl_output::WlOutput) {
|
||||
let mut guard = self.evq.lock().unwrap();
|
||||
let state = guard.state();
|
||||
let env = state.get_handler::<WaylandEnv>(self.env_id);
|
||||
for m in env.monitors.iter().filter(|m| m.id == id.id) {
|
||||
f(&m.output);
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_window<H: wayland_window::Handler>(&self)
|
||||
-> (Arc<wl_surface::WlSurface>, wayland_window::DecoratedSurface<H>)
|
||||
{
|
||||
let mut guard = self.evq.lock().unwrap();
|
||||
let mut state = guard.state();
|
||||
let env = state.get_mut_handler::<WaylandEnv>(self.env_id);
|
||||
// this "expect" cannot trigger (see https://github.com/vberger/wayland-client-rs/issues/69)
|
||||
let surface = Arc::new(env.inner.compositor.create_surface());
|
||||
let decorated = wayland_window::DecoratedSurface::new(
|
||||
&*surface, 800, 600,
|
||||
&env.inner.compositor,
|
||||
&env.inner.subcompositor,
|
||||
&env.inner.shm,
|
||||
&env.inner.shell,
|
||||
env.get_seat(),
|
||||
false
|
||||
).expect("Failed to create a tmpfile buffer.");
|
||||
(surface, decorated)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Monitors API
|
||||
*/
|
||||
|
||||
pub fn get_primary_monitor(ctxt: &Arc<WaylandContext>) -> MonitorId {
|
||||
let mut guard = ctxt.evq.lock().unwrap();
|
||||
let state = guard.state();
|
||||
let env = state.get_handler::<WaylandEnv>(ctxt.env_id);
|
||||
if let Some(ref monitor) = env.monitors.iter().next() {
|
||||
MonitorId {
|
||||
id: monitor.id,
|
||||
ctxt: ctxt.clone()
|
||||
}
|
||||
} else {
|
||||
panic!("No monitor is available.")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_available_monitors(ctxt: &Arc<WaylandContext>) -> VecDeque<MonitorId> {
|
||||
let mut guard = ctxt.evq.lock().unwrap();
|
||||
let state = guard.state();
|
||||
let env = state.get_handler::<WaylandEnv>(ctxt.env_id);
|
||||
env.monitors.iter()
|
||||
.map(|m| MonitorId { id: m.id, ctxt: ctxt.clone() })
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MonitorId {
|
||||
id: u32,
|
||||
ctxt: Arc<WaylandContext>
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
let mut guard = self.ctxt.evq.lock().unwrap();
|
||||
let state = guard.state();
|
||||
let env = state.get_handler::<WaylandEnv>(self.ctxt.env_id);
|
||||
for m in env.monitors.iter().filter(|m| m.id == self.id) {
|
||||
return Some(m.name.clone())
|
||||
}
|
||||
// if we reach here, this monitor does not exist any more
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> ::native_monitor::NativeMonitorId {
|
||||
::native_monitor::NativeMonitorId::Unavailable
|
||||
}
|
||||
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
let mut guard = self.ctxt.evq.lock().unwrap();
|
||||
let state = guard.state();
|
||||
let env = state.get_handler::<WaylandEnv>(self.ctxt.env_id);
|
||||
for m in env.monitors.iter().filter(|m| m.id == self.id) {
|
||||
return m.pix_size
|
||||
}
|
||||
// if we reach here, this monitor does not exist any more
|
||||
(0,0)
|
||||
}
|
||||
}
|
||||
@@ -1,592 +0,0 @@
|
||||
use {WindowEvent as Event, ElementState, MouseButton, MouseScrollDelta, TouchPhase, ModifiersState};
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
use super::{DecoratedHandler, WindowId, WaylandContext};
|
||||
|
||||
|
||||
use wayland_client::{EventQueue, EventQueueHandle, Init, Proxy};
|
||||
use wayland_client::protocol::{wl_seat, wl_surface, wl_pointer, wl_keyboard};
|
||||
|
||||
use super::make_wid;
|
||||
use super::wayland_window::DecoratedSurface;
|
||||
use super::wayland_kbd::MappedKeyboard;
|
||||
use super::keyboard::KbdHandler;
|
||||
|
||||
/// This struct is used as a holder for the callback
|
||||
/// during the dispatching of events.
|
||||
///
|
||||
/// The proper ay to use it is:
|
||||
/// - set a callback in it (and retrieve the noop one it contains)
|
||||
/// - dispatch the EventQueue
|
||||
/// - put back the noop callback in it
|
||||
///
|
||||
/// Failure to do so is unsafe™
|
||||
pub struct EventsLoopSink {
|
||||
callback: Box<FnMut(::Event)>
|
||||
}
|
||||
|
||||
unsafe impl Send for EventsLoopSink { }
|
||||
|
||||
impl EventsLoopSink {
|
||||
pub fn new() -> EventsLoopSink {
|
||||
EventsLoopSink {
|
||||
callback: Box::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.callback)(evt)
|
||||
}
|
||||
|
||||
// This function is only safe of the set callback is unset before exclusive
|
||||
// access to the wayland EventQueue is finished.
|
||||
//
|
||||
// The callback also cannot be used any longer as long as it has not been
|
||||
// cleared from the Sink.
|
||||
unsafe fn set_callback(&mut self, cb: Box<FnMut(::Event)>) -> Box<FnMut(::Event)> {
|
||||
::std::mem::replace(&mut self.callback, cb)
|
||||
}
|
||||
|
||||
fn with_callback<F: FnOnce(&mut FnMut(::Event))>(&mut self, f: F) {
|
||||
f(&mut *self.callback)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventsLoop {
|
||||
// the global wayland context
|
||||
ctxt: Arc<WaylandContext>,
|
||||
// our EventQueue
|
||||
evq: Arc<Mutex<EventQueue>>,
|
||||
// ids of the DecoratedHandlers of the surfaces we know
|
||||
decorated_ids: Mutex<Vec<(usize, Arc<wl_surface::WlSurface>)>>,
|
||||
// our sink, receiver of callbacks, shared with some handlers
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
// trigger interruption of the run
|
||||
interrupted: AtomicBool,
|
||||
// trigger cleanup of the dead surfaces
|
||||
cleanup_needed: Arc<AtomicBool>,
|
||||
hid: usize
|
||||
}
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn new(ctxt: Arc<WaylandContext>) -> EventsLoop {
|
||||
let mut evq = ctxt.display.create_event_queue();
|
||||
let sink = Arc::new(Mutex::new(EventsLoopSink::new()));
|
||||
let hid = evq.add_handler_with_init(InputHandler::new(&ctxt, sink.clone()));
|
||||
EventsLoop {
|
||||
ctxt: ctxt,
|
||||
evq: Arc::new(Mutex::new(evq)),
|
||||
decorated_ids: Mutex::new(Vec::new()),
|
||||
sink: sink,
|
||||
interrupted: AtomicBool::new(false),
|
||||
cleanup_needed: Arc::new(AtomicBool::new(false)),
|
||||
hid: hid
|
||||
}
|
||||
}
|
||||
|
||||
// some internals that Window needs access to
|
||||
pub fn get_window_init(&self) -> (Arc<Mutex<EventQueue>>, Arc<AtomicBool>) {
|
||||
(self.evq.clone(), self.cleanup_needed.clone())
|
||||
}
|
||||
|
||||
pub fn register_window(&self, decorated_id: usize, surface: Arc<wl_surface::WlSurface>) {
|
||||
self.decorated_ids.lock().unwrap().push((decorated_id, surface.clone()));
|
||||
let mut guard = self.evq.lock().unwrap();
|
||||
let mut state = guard.state();
|
||||
state.get_mut_handler::<InputHandler>(self.hid).windows.push(surface);
|
||||
}
|
||||
|
||||
fn process_resize(evq: &mut EventQueue, ids: &[(usize, Arc<wl_surface::WlSurface>)], callback: &mut FnMut(::Event))
|
||||
{
|
||||
let mut state = evq.state();
|
||||
for &(decorated_id, ref window) in ids {
|
||||
let decorated = state.get_mut_handler::<DecoratedSurface<DecoratedHandler>>(decorated_id);
|
||||
if let Some((w, h)) = decorated.handler().as_mut().and_then(|h| h.take_newsize()) {
|
||||
decorated.resize(w as i32, h as i32);
|
||||
callback(
|
||||
::Event::WindowEvent {
|
||||
window_id: ::WindowId(::platform::WindowId::Wayland(make_wid(&window))),
|
||||
event: ::WindowEvent::Resized(w,h)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interrupt(&self) {
|
||||
self.interrupted.store(true, ::std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn prune_dead_windows(&self) {
|
||||
self.decorated_ids.lock().unwrap().retain(|&(_, ref w)| w.is_alive());
|
||||
let mut evq_guard = self.evq.lock().unwrap();
|
||||
let mut state = evq_guard.state();
|
||||
let handler = state.get_mut_handler::<InputHandler>(self.hid);
|
||||
handler.windows.retain(|w| w.is_alive());
|
||||
if let Some(w) = handler.mouse_focus.take() {
|
||||
if w.is_alive() {
|
||||
handler.mouse_focus = Some(w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll_events<F>(&self, callback: F)
|
||||
where F: FnMut(::Event)
|
||||
{
|
||||
// send pending requests to the server...
|
||||
self.ctxt.flush();
|
||||
|
||||
// first of all, get exclusive access to this event queue
|
||||
let mut evq_guard = self.evq.lock().unwrap();
|
||||
|
||||
// read some events from the socket if some are waiting & queue is empty
|
||||
if let Some(guard) = evq_guard.prepare_read() {
|
||||
guard.read_events().expect("Wayland connection unexpectedly lost");
|
||||
}
|
||||
|
||||
// set the callback into the sink
|
||||
// we extend the lifetime of the closure to 'static to be able to put it in
|
||||
// the sink, but we'll explicitly drop it at the end of this function, so it's fine
|
||||
let static_cb = unsafe { ::std::mem::transmute(Box::new(callback) as Box<FnMut(_)>) };
|
||||
let old_cb = unsafe { self.sink.lock().unwrap().set_callback(static_cb) };
|
||||
|
||||
// then do the actual dispatching
|
||||
self.ctxt.dispatch_pending();
|
||||
evq_guard.dispatch_pending().expect("Wayland connection unexpectedly lost");
|
||||
|
||||
let mut sink_guard = self.sink.lock().unwrap();
|
||||
|
||||
// events where probably dispatched, process resize
|
||||
let ids_guard = self.decorated_ids.lock().unwrap();
|
||||
sink_guard.with_callback(
|
||||
|cb| Self::process_resize(&mut evq_guard, &ids_guard, cb)
|
||||
);
|
||||
|
||||
// replace the old noop callback
|
||||
unsafe { self.sink.lock().unwrap().set_callback(old_cb) };
|
||||
|
||||
if self.cleanup_needed.swap(false, ::std::sync::atomic::Ordering::Relaxed) {
|
||||
self.prune_dead_windows()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_forever<F>(&self, callback: F)
|
||||
where F: FnMut(::Event)
|
||||
{
|
||||
// send pending requests to the server...
|
||||
self.ctxt.flush();
|
||||
|
||||
// first of all, get exclusive access to this event queue
|
||||
let mut evq_guard = self.evq.lock().unwrap();
|
||||
|
||||
// set the callback into the sink
|
||||
// we extend the lifetime of the closure to 'static to be able to put it in
|
||||
// the sink, but we'll explicitly drop it at the end of this function, so it's fine
|
||||
let static_cb = unsafe { ::std::mem::transmute(Box::new(callback) as Box<FnMut(_)>) };
|
||||
let old_cb = unsafe { self.sink.lock().unwrap().set_callback(static_cb) };
|
||||
|
||||
while !self.interrupted.load(::std::sync::atomic::Ordering::Relaxed) {
|
||||
self.ctxt.dispatch();
|
||||
evq_guard.dispatch_pending().expect("Wayland connection unexpectedly lost");
|
||||
let ids_guard = self.decorated_ids.lock().unwrap();
|
||||
self.sink.lock().unwrap().with_callback(
|
||||
|cb| Self::process_resize(&mut evq_guard, &ids_guard, cb)
|
||||
);
|
||||
self.ctxt.flush();
|
||||
|
||||
if self.cleanup_needed.swap(false, ::std::sync::atomic::Ordering::Relaxed) {
|
||||
self.prune_dead_windows()
|
||||
}
|
||||
}
|
||||
|
||||
// replace the old noop callback
|
||||
unsafe { self.sink.lock().unwrap().set_callback(old_cb) };
|
||||
}
|
||||
}
|
||||
|
||||
enum KbdType {
|
||||
Mapped(MappedKeyboard<KbdHandler>),
|
||||
Plain(Option<WindowId>)
|
||||
}
|
||||
|
||||
struct InputHandler {
|
||||
my_id: usize,
|
||||
windows: Vec<Arc<wl_surface::WlSurface>>,
|
||||
seat: Option<wl_seat::WlSeat>,
|
||||
mouse: Option<wl_pointer::WlPointer>,
|
||||
mouse_focus: Option<Arc<wl_surface::WlSurface>>,
|
||||
mouse_location: (i32, i32),
|
||||
axis_buffer: Option<(f32, f32)>,
|
||||
axis_discrete_buffer: Option<(i32, i32)>,
|
||||
axis_state: TouchPhase,
|
||||
kbd: Option<wl_keyboard::WlKeyboard>,
|
||||
kbd_handler: KbdType,
|
||||
callback: Arc<Mutex<EventsLoopSink>>
|
||||
}
|
||||
|
||||
impl InputHandler {
|
||||
fn new(ctxt: &WaylandContext, sink: Arc<Mutex<EventsLoopSink>>) -> InputHandler {
|
||||
let kbd_handler = match MappedKeyboard::new(KbdHandler::new(sink.clone())) {
|
||||
Ok(h) => KbdType::Mapped(h),
|
||||
Err(_) => KbdType::Plain(None)
|
||||
};
|
||||
InputHandler {
|
||||
my_id: 0,
|
||||
windows: Vec::new(),
|
||||
seat: ctxt.get_seat(),
|
||||
mouse: None,
|
||||
mouse_focus: None,
|
||||
mouse_location: (0,0),
|
||||
axis_buffer: None,
|
||||
axis_discrete_buffer: None,
|
||||
axis_state: TouchPhase::Started,
|
||||
kbd: None,
|
||||
kbd_handler: kbd_handler,
|
||||
callback: sink
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Init for InputHandler {
|
||||
fn init(&mut self, evqh: &mut EventQueueHandle, index: usize) {
|
||||
if let Some(ref seat) = self.seat {
|
||||
evqh.register::<_, InputHandler>(seat, index);
|
||||
}
|
||||
self.my_id = index;
|
||||
}
|
||||
}
|
||||
|
||||
impl wl_seat::Handler for InputHandler {
|
||||
fn capabilities(&mut self,
|
||||
evqh: &mut EventQueueHandle,
|
||||
seat: &wl_seat::WlSeat,
|
||||
capabilities: wl_seat::Capability)
|
||||
{
|
||||
// create pointer if applicable
|
||||
if capabilities.contains(wl_seat::Pointer) && self.mouse.is_none() {
|
||||
let pointer = seat.get_pointer().expect("Seat is not dead");
|
||||
evqh.register::<_, InputHandler>(&pointer, self.my_id);
|
||||
self.mouse = Some(pointer);
|
||||
}
|
||||
// destroy pointer if applicable
|
||||
if !capabilities.contains(wl_seat::Pointer) {
|
||||
if let Some(pointer) = self.mouse.take() {
|
||||
pointer.release();
|
||||
}
|
||||
}
|
||||
// create keyboard if applicable
|
||||
if capabilities.contains(wl_seat::Keyboard) && self.kbd.is_none() {
|
||||
let kbd = seat.get_keyboard().expect("Seat is not dead");
|
||||
evqh.register::<_, InputHandler>(&kbd, self.my_id);
|
||||
self.kbd = Some(kbd);
|
||||
}
|
||||
// destroy keyboard if applicable
|
||||
if !capabilities.contains(wl_seat::Keyboard) {
|
||||
if let Some(kbd) = self.kbd.take() {
|
||||
kbd.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare_handler!(InputHandler, wl_seat::Handler, wl_seat::WlSeat);
|
||||
|
||||
/*
|
||||
* Pointer Handling
|
||||
*/
|
||||
|
||||
impl wl_pointer::Handler for InputHandler {
|
||||
fn enter(&mut self,
|
||||
_evqh: &mut EventQueueHandle,
|
||||
_proxy: &wl_pointer::WlPointer,
|
||||
_serial: u32,
|
||||
surface: &wl_surface::WlSurface,
|
||||
surface_x: f64,
|
||||
surface_y: f64)
|
||||
{
|
||||
self.mouse_location = (surface_x as i32, surface_y as i32);
|
||||
for window in &self.windows {
|
||||
if window.equals(surface) {
|
||||
self.mouse_focus = Some(window.clone());
|
||||
let (w, h) = self.mouse_location;
|
||||
let mut guard = self.callback.lock().unwrap();
|
||||
guard.send_event(Event::MouseEntered, make_wid(window));
|
||||
guard.send_event(Event::MouseMoved(w, h), make_wid(window));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn leave(&mut self,
|
||||
_evqh: &mut EventQueueHandle,
|
||||
_proxy: &wl_pointer::WlPointer,
|
||||
_serial: u32,
|
||||
surface: &wl_surface::WlSurface)
|
||||
{
|
||||
self.mouse_focus = None;
|
||||
for window in &self.windows {
|
||||
if window.equals(surface) {
|
||||
self.callback.lock().unwrap().send_event(Event::MouseLeft, make_wid(window));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn motion(&mut self,
|
||||
_evqh: &mut EventQueueHandle,
|
||||
_proxy: &wl_pointer::WlPointer,
|
||||
_time: u32,
|
||||
surface_x: f64,
|
||||
surface_y: f64)
|
||||
{
|
||||
self.mouse_location = (surface_x as i32, surface_y as i32);
|
||||
if let Some(ref window) = self.mouse_focus {
|
||||
let (w,h) = self.mouse_location;
|
||||
self.callback.lock().unwrap().send_event(Event::MouseMoved(w, h), make_wid(window));
|
||||
}
|
||||
}
|
||||
|
||||
fn button(&mut self,
|
||||
_evqh: &mut EventQueueHandle,
|
||||
_proxy: &wl_pointer::WlPointer,
|
||||
_serial: u32,
|
||||
_time: u32,
|
||||
button: u32,
|
||||
state: wl_pointer::ButtonState)
|
||||
{
|
||||
if let Some(ref window) = self.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
|
||||
};
|
||||
self.callback.lock().unwrap().send_event(Event::MouseInput(state, button), make_wid(window));
|
||||
}
|
||||
}
|
||||
|
||||
fn axis(&mut self,
|
||||
_evqh: &mut EventQueueHandle,
|
||||
_proxy: &wl_pointer::WlPointer,
|
||||
_time: u32,
|
||||
axis: wl_pointer::Axis,
|
||||
value: f64)
|
||||
{
|
||||
let (mut x, mut y) = self.axis_buffer.unwrap_or((0.0, 0.0));
|
||||
match axis {
|
||||
wl_pointer::Axis::VerticalScroll => y += value as f32,
|
||||
wl_pointer::Axis::HorizontalScroll => x += value as f32
|
||||
}
|
||||
self.axis_buffer = Some((x,y));
|
||||
self.axis_state = match self.axis_state {
|
||||
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
||||
_ => TouchPhase::Started
|
||||
}
|
||||
}
|
||||
|
||||
fn frame(&mut self,
|
||||
_evqh: &mut EventQueueHandle,
|
||||
_proxy: &wl_pointer::WlPointer)
|
||||
{
|
||||
let axis_buffer = self.axis_buffer.take();
|
||||
let axis_discrete_buffer = self.axis_discrete_buffer.take();
|
||||
if let Some(ref window) = self.mouse_focus {
|
||||
if let Some((x, y)) = axis_discrete_buffer {
|
||||
self.callback.lock().unwrap().send_event(
|
||||
Event::MouseWheel(
|
||||
MouseScrollDelta::LineDelta(x as f32, y as f32),
|
||||
self.axis_state
|
||||
),
|
||||
make_wid(window)
|
||||
);
|
||||
} else if let Some((x, y)) = axis_buffer {
|
||||
self.callback.lock().unwrap().send_event(
|
||||
Event::MouseWheel(
|
||||
MouseScrollDelta::PixelDelta(x as f32, y as f32),
|
||||
self.axis_state
|
||||
),
|
||||
make_wid(window)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn axis_source(&mut self,
|
||||
_evqh: &mut EventQueueHandle,
|
||||
_proxy: &wl_pointer::WlPointer,
|
||||
_axis_source: wl_pointer::AxisSource)
|
||||
{
|
||||
}
|
||||
|
||||
fn axis_stop(&mut self,
|
||||
_evqh: &mut EventQueueHandle,
|
||||
_proxy: &wl_pointer::WlPointer,
|
||||
_time: u32,
|
||||
_axis: wl_pointer::Axis)
|
||||
{
|
||||
self.axis_state = TouchPhase::Ended;
|
||||
}
|
||||
|
||||
fn axis_discrete(&mut self,
|
||||
_evqh: &mut EventQueueHandle,
|
||||
_proxy: &wl_pointer::WlPointer,
|
||||
axis: wl_pointer::Axis,
|
||||
discrete: i32)
|
||||
{
|
||||
let (mut x, mut y) = self.axis_discrete_buffer.unwrap_or((0,0));
|
||||
match axis {
|
||||
wl_pointer::Axis::VerticalScroll => y += discrete,
|
||||
wl_pointer::Axis::HorizontalScroll => x += discrete
|
||||
}
|
||||
self.axis_discrete_buffer = Some((x,y));
|
||||
self.axis_state = match self.axis_state {
|
||||
TouchPhase::Started | TouchPhase::Moved => TouchPhase::Moved,
|
||||
_ => TouchPhase::Started
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare_handler!(InputHandler, wl_pointer::Handler, wl_pointer::WlPointer);
|
||||
|
||||
/*
|
||||
* Keyboard Handling
|
||||
*/
|
||||
|
||||
impl wl_keyboard::Handler for InputHandler {
|
||||
// mostly pass-through
|
||||
fn keymap(&mut self,
|
||||
evqh: &mut EventQueueHandle,
|
||||
proxy: &wl_keyboard::WlKeyboard,
|
||||
format: wl_keyboard::KeymapFormat,
|
||||
fd: ::std::os::unix::io::RawFd,
|
||||
size: u32)
|
||||
{
|
||||
match self.kbd_handler {
|
||||
KbdType::Mapped(ref mut h) => h.keymap(evqh, proxy, format, fd, size),
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
fn enter(&mut self,
|
||||
evqh: &mut EventQueueHandle,
|
||||
proxy: &wl_keyboard::WlKeyboard,
|
||||
serial: u32,
|
||||
surface: &wl_surface::WlSurface,
|
||||
keys: Vec<u8>)
|
||||
{
|
||||
for window in &self.windows {
|
||||
if window.equals(surface) {
|
||||
self.callback.lock().unwrap().send_event(Event::Focused(true), make_wid(window));
|
||||
match self.kbd_handler {
|
||||
KbdType::Mapped(ref mut h) => {
|
||||
h.handler().target = Some(make_wid(window));
|
||||
h.enter(evqh, proxy, serial, surface, keys);
|
||||
},
|
||||
KbdType::Plain(ref mut target) => {
|
||||
*target = Some(make_wid(window))
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn leave(&mut self,
|
||||
evqh: &mut EventQueueHandle,
|
||||
proxy: &wl_keyboard::WlKeyboard,
|
||||
serial: u32,
|
||||
surface: &wl_surface::WlSurface)
|
||||
{
|
||||
for window in &self.windows {
|
||||
if window.equals(surface) {
|
||||
self.callback.lock().unwrap().send_event(Event::Focused(false), make_wid(window));
|
||||
match self.kbd_handler {
|
||||
KbdType::Mapped(ref mut h) => {
|
||||
h.handler().target = None;
|
||||
h.leave(evqh, proxy, serial, surface);
|
||||
},
|
||||
KbdType::Plain(ref mut target) => {
|
||||
*target = None
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn key(&mut self,
|
||||
evqh: &mut EventQueueHandle,
|
||||
proxy: &wl_keyboard::WlKeyboard,
|
||||
serial: u32,
|
||||
time: u32,
|
||||
key: u32,
|
||||
state: wl_keyboard::KeyState)
|
||||
{
|
||||
match self.kbd_handler {
|
||||
KbdType::Mapped(ref mut h) => h.key(evqh, proxy, serial, time, key, state),
|
||||
KbdType::Plain(Some(wid)) => {
|
||||
let state = match state {
|
||||
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
|
||||
wl_keyboard::KeyState::Released => ElementState::Released,
|
||||
};
|
||||
// This is fallback impl if libxkbcommon was not available
|
||||
// This case should probably never happen, as most wayland
|
||||
// compositors _need_ libxkbcommon anyway...
|
||||
//
|
||||
// In this case, we don't have the modifiers state information
|
||||
// anyway, as we need libxkbcommon to interpret it (it is
|
||||
// supposed to be serialized by the compositor using libxkbcommon)
|
||||
self.callback.lock().unwrap().send_event(
|
||||
Event::KeyboardInput(
|
||||
state,
|
||||
key as u8,
|
||||
None,
|
||||
ModifiersState::default()
|
||||
),
|
||||
wid
|
||||
);
|
||||
},
|
||||
KbdType::Plain(None) => ()
|
||||
}
|
||||
}
|
||||
|
||||
fn modifiers(&mut self,
|
||||
evqh: &mut EventQueueHandle,
|
||||
proxy: &wl_keyboard::WlKeyboard,
|
||||
serial: u32,
|
||||
mods_depressed: u32,
|
||||
mods_latched: u32,
|
||||
mods_locked: u32,
|
||||
group: u32)
|
||||
{
|
||||
match self.kbd_handler {
|
||||
KbdType::Mapped(ref mut h) => h.modifiers(evqh, proxy, serial, mods_depressed,
|
||||
mods_latched, mods_locked, group),
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
|
||||
fn repeat_info(&mut self,
|
||||
evqh: &mut EventQueueHandle,
|
||||
proxy: &wl_keyboard::WlKeyboard,
|
||||
rate: i32,
|
||||
delay: i32)
|
||||
{
|
||||
match self.kbd_handler {
|
||||
KbdType::Mapped(ref mut h) => h.repeat_info(evqh, proxy, rate, delay),
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare_handler!(InputHandler, wl_keyboard::Handler, wl_keyboard::WlKeyboard);
|
||||
@@ -1,232 +0,0 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use {VirtualKeyCode, ElementState, WindowEvent as Event};
|
||||
|
||||
use events::ModifiersState;
|
||||
|
||||
use super::{wayland_kbd, EventsLoopSink, WindowId};
|
||||
use wayland_client::EventQueueHandle;
|
||||
use wayland_client::protocol::wl_keyboard;
|
||||
|
||||
pub struct KbdHandler {
|
||||
sink: Arc<Mutex<EventsLoopSink>>,
|
||||
pub target: Option<WindowId>
|
||||
}
|
||||
|
||||
impl KbdHandler {
|
||||
pub fn new(sink: Arc<Mutex<EventsLoopSink>>) -> KbdHandler {
|
||||
KbdHandler { sink: sink, target: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl wayland_kbd::Handler for KbdHandler {
|
||||
fn key(&mut self,
|
||||
_evqh: &mut EventQueueHandle,
|
||||
_proxy: &wl_keyboard::WlKeyboard,
|
||||
_serial: u32,
|
||||
_time: u32,
|
||||
mods: &wayland_kbd::ModifiersState,
|
||||
rawkey: u32,
|
||||
keysym: u32,
|
||||
state: wl_keyboard::KeyState,
|
||||
utf8: Option<String>)
|
||||
{
|
||||
if let Some(wid) = self.target {
|
||||
let state = match state {
|
||||
wl_keyboard::KeyState::Pressed => ElementState::Pressed,
|
||||
wl_keyboard::KeyState::Released => ElementState::Released,
|
||||
};
|
||||
let vkcode = key_to_vkey(rawkey, keysym);
|
||||
let mut guard = self.sink.lock().unwrap();
|
||||
guard.send_event(
|
||||
Event::KeyboardInput(
|
||||
state,
|
||||
rawkey as u8,
|
||||
vkcode,
|
||||
ModifiersState {
|
||||
shift: mods.shift,
|
||||
ctrl: mods.ctrl,
|
||||
alt: mods.alt,
|
||||
logo: mods.logo
|
||||
}
|
||||
),
|
||||
wid
|
||||
);
|
||||
// send char event only on key press, not release
|
||||
if let ElementState::Released = state { return }
|
||||
if let Some(txt) = utf8 {
|
||||
for chr in txt.chars() {
|
||||
guard.send_event(Event::ReceivedCharacter(chr), wid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn key_to_vkey(rawkey: u32, keysym: u32) -> Option<VirtualKeyCode> {
|
||||
match rawkey {
|
||||
1 => Some(VirtualKeyCode::Escape),
|
||||
2 => Some(VirtualKeyCode::Key1),
|
||||
3 => Some(VirtualKeyCode::Key2),
|
||||
4 => Some(VirtualKeyCode::Key3),
|
||||
5 => Some(VirtualKeyCode::Key4),
|
||||
6 => Some(VirtualKeyCode::Key5),
|
||||
7 => Some(VirtualKeyCode::Key6),
|
||||
8 => Some(VirtualKeyCode::Key7),
|
||||
9 => Some(VirtualKeyCode::Key8),
|
||||
10 => Some(VirtualKeyCode::Key9),
|
||||
11 => Some(VirtualKeyCode::Key0),
|
||||
_ => keysym_to_vkey(keysym)
|
||||
}
|
||||
}
|
||||
|
||||
fn keysym_to_vkey(keysym: u32) -> Option<VirtualKeyCode> {
|
||||
use super::wayland_kbd::keysyms;
|
||||
match keysym {
|
||||
// letters
|
||||
keysyms::XKB_KEY_A | keysyms::XKB_KEY_a => Some(VirtualKeyCode::A),
|
||||
keysyms::XKB_KEY_B | keysyms::XKB_KEY_b => Some(VirtualKeyCode::B),
|
||||
keysyms::XKB_KEY_C | keysyms::XKB_KEY_c => Some(VirtualKeyCode::C),
|
||||
keysyms::XKB_KEY_D | keysyms::XKB_KEY_d => Some(VirtualKeyCode::D),
|
||||
keysyms::XKB_KEY_E | keysyms::XKB_KEY_e => Some(VirtualKeyCode::E),
|
||||
keysyms::XKB_KEY_F | keysyms::XKB_KEY_f => Some(VirtualKeyCode::F),
|
||||
keysyms::XKB_KEY_G | keysyms::XKB_KEY_g => Some(VirtualKeyCode::G),
|
||||
keysyms::XKB_KEY_H | keysyms::XKB_KEY_h => Some(VirtualKeyCode::H),
|
||||
keysyms::XKB_KEY_I | keysyms::XKB_KEY_i => Some(VirtualKeyCode::I),
|
||||
keysyms::XKB_KEY_J | keysyms::XKB_KEY_j => Some(VirtualKeyCode::J),
|
||||
keysyms::XKB_KEY_K | keysyms::XKB_KEY_k => Some(VirtualKeyCode::K),
|
||||
keysyms::XKB_KEY_L | keysyms::XKB_KEY_l => Some(VirtualKeyCode::L),
|
||||
keysyms::XKB_KEY_M | keysyms::XKB_KEY_m => Some(VirtualKeyCode::M),
|
||||
keysyms::XKB_KEY_N | keysyms::XKB_KEY_n => Some(VirtualKeyCode::N),
|
||||
keysyms::XKB_KEY_O | keysyms::XKB_KEY_o => Some(VirtualKeyCode::O),
|
||||
keysyms::XKB_KEY_P | keysyms::XKB_KEY_p => Some(VirtualKeyCode::P),
|
||||
keysyms::XKB_KEY_Q | keysyms::XKB_KEY_q => Some(VirtualKeyCode::Q),
|
||||
keysyms::XKB_KEY_R | keysyms::XKB_KEY_r => Some(VirtualKeyCode::R),
|
||||
keysyms::XKB_KEY_S | keysyms::XKB_KEY_s => Some(VirtualKeyCode::S),
|
||||
keysyms::XKB_KEY_T | keysyms::XKB_KEY_t => Some(VirtualKeyCode::T),
|
||||
keysyms::XKB_KEY_U | keysyms::XKB_KEY_u => Some(VirtualKeyCode::U),
|
||||
keysyms::XKB_KEY_V | keysyms::XKB_KEY_v => Some(VirtualKeyCode::V),
|
||||
keysyms::XKB_KEY_W | keysyms::XKB_KEY_w => Some(VirtualKeyCode::W),
|
||||
keysyms::XKB_KEY_X | keysyms::XKB_KEY_x => Some(VirtualKeyCode::X),
|
||||
keysyms::XKB_KEY_Y | keysyms::XKB_KEY_y => Some(VirtualKeyCode::Y),
|
||||
keysyms::XKB_KEY_Z | keysyms::XKB_KEY_z => Some(VirtualKeyCode::Z),
|
||||
// F--
|
||||
keysyms::XKB_KEY_F1 => Some(VirtualKeyCode::F1),
|
||||
keysyms::XKB_KEY_F2 => Some(VirtualKeyCode::F2),
|
||||
keysyms::XKB_KEY_F3 => Some(VirtualKeyCode::F3),
|
||||
keysyms::XKB_KEY_F4 => Some(VirtualKeyCode::F4),
|
||||
keysyms::XKB_KEY_F5 => Some(VirtualKeyCode::F5),
|
||||
keysyms::XKB_KEY_F6 => Some(VirtualKeyCode::F6),
|
||||
keysyms::XKB_KEY_F7 => Some(VirtualKeyCode::F7),
|
||||
keysyms::XKB_KEY_F8 => Some(VirtualKeyCode::F8),
|
||||
keysyms::XKB_KEY_F9 => Some(VirtualKeyCode::F9),
|
||||
keysyms::XKB_KEY_F10 => Some(VirtualKeyCode::F10),
|
||||
keysyms::XKB_KEY_F11 => Some(VirtualKeyCode::F11),
|
||||
keysyms::XKB_KEY_F12 => Some(VirtualKeyCode::F12),
|
||||
keysyms::XKB_KEY_F13 => Some(VirtualKeyCode::F13),
|
||||
keysyms::XKB_KEY_F14 => Some(VirtualKeyCode::F14),
|
||||
keysyms::XKB_KEY_F15 => Some(VirtualKeyCode::F15),
|
||||
// flow control
|
||||
keysyms::XKB_KEY_Print => Some(VirtualKeyCode::Snapshot),
|
||||
keysyms::XKB_KEY_Scroll_Lock => Some(VirtualKeyCode::Scroll),
|
||||
keysyms::XKB_KEY_Pause => Some(VirtualKeyCode::Pause),
|
||||
keysyms::XKB_KEY_Insert => Some(VirtualKeyCode::Insert),
|
||||
keysyms::XKB_KEY_Home => Some(VirtualKeyCode::Home),
|
||||
keysyms::XKB_KEY_Delete => Some(VirtualKeyCode::Delete),
|
||||
keysyms::XKB_KEY_End => Some(VirtualKeyCode::End),
|
||||
keysyms::XKB_KEY_Page_Down => Some(VirtualKeyCode::PageDown),
|
||||
keysyms::XKB_KEY_Page_Up => Some(VirtualKeyCode::PageUp),
|
||||
// arrows
|
||||
keysyms::XKB_KEY_Left => Some(VirtualKeyCode::Left),
|
||||
keysyms::XKB_KEY_Up => Some(VirtualKeyCode::Up),
|
||||
keysyms::XKB_KEY_Right => Some(VirtualKeyCode::Right),
|
||||
keysyms::XKB_KEY_Down => Some(VirtualKeyCode::Down),
|
||||
//
|
||||
keysyms::XKB_KEY_BackSpace => Some(VirtualKeyCode::Back),
|
||||
keysyms::XKB_KEY_Return => Some(VirtualKeyCode::Return),
|
||||
keysyms::XKB_KEY_space => Some(VirtualKeyCode::Space),
|
||||
// keypad
|
||||
keysyms::XKB_KEY_Num_Lock => Some(VirtualKeyCode::Numlock),
|
||||
keysyms::XKB_KEY_KP_0 => Some(VirtualKeyCode::Numpad0),
|
||||
keysyms::XKB_KEY_KP_1 => Some(VirtualKeyCode::Numpad1),
|
||||
keysyms::XKB_KEY_KP_2 => Some(VirtualKeyCode::Numpad2),
|
||||
keysyms::XKB_KEY_KP_3 => Some(VirtualKeyCode::Numpad3),
|
||||
keysyms::XKB_KEY_KP_4 => Some(VirtualKeyCode::Numpad4),
|
||||
keysyms::XKB_KEY_KP_5 => Some(VirtualKeyCode::Numpad5),
|
||||
keysyms::XKB_KEY_KP_6 => Some(VirtualKeyCode::Numpad6),
|
||||
keysyms::XKB_KEY_KP_7 => Some(VirtualKeyCode::Numpad7),
|
||||
keysyms::XKB_KEY_KP_8 => Some(VirtualKeyCode::Numpad8),
|
||||
keysyms::XKB_KEY_KP_9 => Some(VirtualKeyCode::Numpad9),
|
||||
// misc
|
||||
// => Some(VirtualKeyCode::AbntC1),
|
||||
// => Some(VirtualKeyCode::AbntC2),
|
||||
keysyms::XKB_KEY_plus => Some(VirtualKeyCode::Add),
|
||||
keysyms::XKB_KEY_apostrophe => Some(VirtualKeyCode::Apostrophe),
|
||||
// => Some(VirtualKeyCode::Apps),
|
||||
// => Some(VirtualKeyCode::At),
|
||||
// => Some(VirtualKeyCode::Ax),
|
||||
keysyms::XKB_KEY_backslash => Some(VirtualKeyCode::Backslash),
|
||||
// => Some(VirtualKeyCode::Calculator),
|
||||
// => Some(VirtualKeyCode::Capital),
|
||||
keysyms::XKB_KEY_colon => Some(VirtualKeyCode::Colon),
|
||||
keysyms::XKB_KEY_comma => Some(VirtualKeyCode::Comma),
|
||||
// => Some(VirtualKeyCode::Convert),
|
||||
// => Some(VirtualKeyCode::Decimal),
|
||||
// => Some(VirtualKeyCode::Divide),
|
||||
keysyms::XKB_KEY_equal => Some(VirtualKeyCode::Equals),
|
||||
// => Some(VirtualKeyCode::Grave),
|
||||
// => Some(VirtualKeyCode::Kana),
|
||||
// => Some(VirtualKeyCode::Kanji),
|
||||
keysyms::XKB_KEY_Alt_L => Some(VirtualKeyCode::LAlt),
|
||||
// => Some(VirtualKeyCode::LBracket),
|
||||
keysyms::XKB_KEY_Control_L => Some(VirtualKeyCode::LControl),
|
||||
// => Some(VirtualKeyCode::LMenu),
|
||||
keysyms::XKB_KEY_Shift_L => Some(VirtualKeyCode::LShift),
|
||||
// => Some(VirtualKeyCode::LWin),
|
||||
// => Some(VirtualKeyCode::Mail),
|
||||
// => Some(VirtualKeyCode::MediaSelect),
|
||||
// => Some(VirtualKeyCode::MediaStop),
|
||||
keysyms::XKB_KEY_minus => Some(VirtualKeyCode::Minus),
|
||||
keysyms::XKB_KEY_asterisk => Some(VirtualKeyCode::Multiply),
|
||||
// => Some(VirtualKeyCode::Mute),
|
||||
// => Some(VirtualKeyCode::MyComputer),
|
||||
// => Some(VirtualKeyCode::NextTrack),
|
||||
// => Some(VirtualKeyCode::NoConvert),
|
||||
keysyms::XKB_KEY_KP_Separator => Some(VirtualKeyCode::NumpadComma),
|
||||
keysyms::XKB_KEY_KP_Enter => Some(VirtualKeyCode::NumpadEnter),
|
||||
keysyms::XKB_KEY_KP_Equal => Some(VirtualKeyCode::NumpadEquals),
|
||||
// => Some(VirtualKeyCode::OEM102),
|
||||
// => Some(VirtualKeyCode::Period),
|
||||
// => Some(VirtualKeyCode::Playpause),
|
||||
// => Some(VirtualKeyCode::Power),
|
||||
// => Some(VirtualKeyCode::Prevtrack),
|
||||
keysyms::XKB_KEY_Alt_R => Some(VirtualKeyCode::RAlt),
|
||||
// => Some(VirtualKeyCode::RBracket),
|
||||
keysyms::XKB_KEY_Control_R => Some(VirtualKeyCode::RControl),
|
||||
// => Some(VirtualKeyCode::RMenu),
|
||||
keysyms::XKB_KEY_Shift_R => Some(VirtualKeyCode::RShift),
|
||||
// => Some(VirtualKeyCode::RWin),
|
||||
keysyms::XKB_KEY_semicolon => Some(VirtualKeyCode::Semicolon),
|
||||
keysyms::XKB_KEY_slash => Some(VirtualKeyCode::Slash),
|
||||
// => Some(VirtualKeyCode::Sleep),
|
||||
// => Some(VirtualKeyCode::Stop),
|
||||
// => Some(VirtualKeyCode::Subtract),
|
||||
// => Some(VirtualKeyCode::Sysrq),
|
||||
keysyms::XKB_KEY_Tab => Some(VirtualKeyCode::Tab),
|
||||
// => Some(VirtualKeyCode::Underline),
|
||||
// => Some(VirtualKeyCode::Unlabeled),
|
||||
keysyms::XKB_KEY_XF86AudioLowerVolume => Some(VirtualKeyCode::VolumeDown),
|
||||
keysyms::XKB_KEY_XF86AudioRaiseVolume => Some(VirtualKeyCode::VolumeUp),
|
||||
// => Some(VirtualKeyCode::Wake),
|
||||
// => Some(VirtualKeyCode::Webback),
|
||||
// => Some(VirtualKeyCode::WebFavorites),
|
||||
// => Some(VirtualKeyCode::WebForward),
|
||||
// => Some(VirtualKeyCode::WebHome),
|
||||
// => Some(VirtualKeyCode::WebRefresh),
|
||||
// => Some(VirtualKeyCode::WebSearch),
|
||||
// => Some(VirtualKeyCode::WebStop),
|
||||
// => Some(VirtualKeyCode::Yen),
|
||||
// fallback
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
|
||||
pub use self::window::{Window, WindowId};
|
||||
pub use self::event_loop::EventsLoop;
|
||||
pub use self::context::{WaylandContext, MonitorId, get_available_monitors,
|
||||
get_primary_monitor};
|
||||
|
||||
use self::window::{make_wid, DecoratedHandler};
|
||||
use self::event_loop::EventsLoopSink;
|
||||
|
||||
extern crate wayland_kbd;
|
||||
extern crate wayland_window;
|
||||
|
||||
mod context;
|
||||
mod event_loop;
|
||||
mod keyboard;
|
||||
mod window;
|
||||
@@ -1,205 +0,0 @@
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
use wayland_client::{EventQueue, EventQueueHandle, Proxy};
|
||||
use wayland_client::protocol::{wl_display,wl_surface,wl_shell_surface};
|
||||
|
||||
use {CreationError, MouseCursor, CursorState, WindowAttributes};
|
||||
use platform::MonitorId as PlatformMonitorId;
|
||||
|
||||
use super::{WaylandContext, EventsLoop};
|
||||
use super::wayland_window;
|
||||
use super::wayland_window::DecoratedSurface;
|
||||
|
||||
pub struct Window {
|
||||
// the global wayland context
|
||||
ctxt: Arc<WaylandContext>,
|
||||
// the EventQueue of our EventsLoop
|
||||
evq: Arc<Mutex<EventQueue>>,
|
||||
// signal to advertize the EventsLoop when we are destroyed
|
||||
cleanup_signal: Arc<AtomicBool>,
|
||||
// our wayland surface
|
||||
surface: Arc<wl_surface::WlSurface>,
|
||||
// our current inner dimensions
|
||||
size: Mutex<(u32, u32)>,
|
||||
// the id of our DecoratedHandler in the EventQueue
|
||||
decorated_id: usize
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(usize);
|
||||
|
||||
#[inline]
|
||||
pub fn make_wid(s: &wl_surface::WlSurface) -> WindowId {
|
||||
WindowId(s.ptr() as usize)
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new(evlp: &EventsLoop, ctxt: Arc<WaylandContext>, attributes: &WindowAttributes) -> Result<Window, CreationError>
|
||||
{
|
||||
let (width, height) = attributes.dimensions.unwrap_or((800,600));
|
||||
|
||||
let (surface, decorated) = ctxt.create_window::<DecoratedHandler>();
|
||||
|
||||
// init DecoratedSurface
|
||||
let (evq, cleanup_signal) = evlp.get_window_init();
|
||||
let decorated_id = {
|
||||
let mut evq_guard = evq.lock().unwrap();
|
||||
let decorated_id = evq_guard.add_handler_with_init(decorated);
|
||||
{
|
||||
// initialize the DecoratedHandler
|
||||
let mut state = evq_guard.state();
|
||||
let decorated = state.get_mut_handler::<DecoratedSurface<DecoratedHandler>>(decorated_id);
|
||||
*(decorated.handler()) = Some(DecoratedHandler::new());
|
||||
|
||||
// set fullscreen if necessary
|
||||
if let Some(PlatformMonitorId::Wayland(ref monitor_id)) = attributes.monitor {
|
||||
ctxt.with_output(monitor_id.clone(), |output| {
|
||||
decorated.set_fullscreen(
|
||||
wl_shell_surface::FullscreenMethod::Default,
|
||||
0,
|
||||
Some(output)
|
||||
)
|
||||
});
|
||||
} else if attributes.decorations {
|
||||
decorated.set_decorate(true);
|
||||
}
|
||||
// Finally, set the decorations size
|
||||
decorated.resize(width as i32, height as i32);
|
||||
}
|
||||
decorated_id
|
||||
};
|
||||
let me = Window {
|
||||
ctxt: ctxt,
|
||||
evq: evq,
|
||||
cleanup_signal: cleanup_signal,
|
||||
surface: surface,
|
||||
size: Mutex::new((width, height)),
|
||||
decorated_id: decorated_id
|
||||
};
|
||||
|
||||
// register ourselves to the EventsLoop
|
||||
evlp.register_window(me.decorated_id, me.surface.clone());
|
||||
|
||||
Ok(me)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
make_wid(&self.surface)
|
||||
}
|
||||
|
||||
pub fn set_title(&self, title: &str) {
|
||||
let mut guard = self.evq.lock().unwrap();
|
||||
let mut state = guard.state();
|
||||
let decorated = state.get_mut_handler::<DecoratedSurface<DecoratedHandler>>(self.decorated_id);
|
||||
decorated.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 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) {
|
||||
let mut guard = self.evq.lock().unwrap();
|
||||
let mut state = guard.state();
|
||||
let mut decorated = state.get_mut_handler::<DecoratedSurface<DecoratedHandler>>(self.decorated_id);
|
||||
decorated.resize(x as i32, y as i32);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor(&self, _cursor: MouseCursor) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_state(&self, state: CursorState) -> Result<(), String> {
|
||||
use CursorState::{Grab, Normal, Hide};
|
||||
// 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 {
|
||||
// TODO
|
||||
1.0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_cursor_position(&self, _x: i32, _y: i32) -> Result<(), ()> {
|
||||
// TODO: not yet possible on wayland
|
||||
Err(())
|
||||
}
|
||||
|
||||
pub fn get_display(&self) -> &wl_display::WlDisplay {
|
||||
&self.ctxt.display
|
||||
}
|
||||
|
||||
pub fn get_surface(&self) -> &wl_surface::WlSurface {
|
||||
&self.surface
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
self.surface.destroy();
|
||||
self.cleanup_signal.store(true, ::std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DecoratedHandler {
|
||||
newsize: Option<(u32, u32)>
|
||||
}
|
||||
|
||||
impl DecoratedHandler {
|
||||
fn new() -> DecoratedHandler { DecoratedHandler { newsize: None }}
|
||||
|
||||
pub fn take_newsize(&mut self) -> Option<(u32, u32)> {
|
||||
self.newsize.take()
|
||||
}
|
||||
}
|
||||
|
||||
impl wayland_window::Handler for DecoratedHandler {
|
||||
fn configure(&mut self,
|
||||
_: &mut EventQueueHandle,
|
||||
_: wl_shell_surface::Resize,
|
||||
width: i32, height: i32)
|
||||
{
|
||||
use std::cmp::max;
|
||||
self.newsize = Some((max(width,1) as u32, max(height,1) as u32));
|
||||
}
|
||||
}
|
||||
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::xf86vmode::*;
|
||||
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;
|
||||
@@ -1,391 +0,0 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use libc;
|
||||
use std::{mem, ptr};
|
||||
use std::ffi::CString;
|
||||
use std::slice::from_raw_parts;
|
||||
|
||||
use WindowAttributes;
|
||||
|
||||
use events::WindowEvent as Event;
|
||||
use events::ModifiersState;
|
||||
|
||||
use super::{events, ffi};
|
||||
use super::XConnection;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum AxisType {
|
||||
HorizontalScroll,
|
||||
VerticalScroll
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Axis {
|
||||
id: i32,
|
||||
device_id: i32,
|
||||
axis_number: i32,
|
||||
axis_type: AxisType,
|
||||
scroll_increment: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct AxisValue {
|
||||
device_id: i32,
|
||||
axis_number: i32,
|
||||
value: f64
|
||||
}
|
||||
|
||||
struct InputState {
|
||||
/// Last-seen cursor position within a window in (x, y)
|
||||
/// coordinates
|
||||
cursor_pos: (f64, f64),
|
||||
/// Last-seen positions of axes, used to report delta
|
||||
/// movements when a new absolute axis value is received
|
||||
axis_values: Vec<AxisValue>
|
||||
}
|
||||
|
||||
pub struct XInputEventHandler {
|
||||
display: Arc<XConnection>,
|
||||
window: ffi::Window,
|
||||
ic: ffi::XIC,
|
||||
axis_list: Vec<Axis>,
|
||||
current_state: InputState,
|
||||
multitouch: bool,
|
||||
}
|
||||
|
||||
impl XInputEventHandler {
|
||||
pub fn new(display: &Arc<XConnection>, window: ffi::Window, ic: ffi::XIC,
|
||||
window_attrs: &WindowAttributes) -> XInputEventHandler {
|
||||
// query XInput support
|
||||
let mut opcode: libc::c_int = 0;
|
||||
let mut event: libc::c_int = 0;
|
||||
let mut error: libc::c_int = 0;
|
||||
let xinput_str = CString::new("XInputExtension").unwrap();
|
||||
|
||||
unsafe {
|
||||
if (display.xlib.XQueryExtension)(display.display, xinput_str.as_ptr(), &mut opcode, &mut event, &mut error) == ffi::False {
|
||||
panic!("XInput not available")
|
||||
}
|
||||
}
|
||||
|
||||
let mut xinput_major_ver = ffi::XI_2_Major;
|
||||
let mut xinput_minor_ver = ffi::XI_2_Minor;
|
||||
|
||||
unsafe {
|
||||
if (display.xinput2.XIQueryVersion)(display.display, &mut xinput_major_ver, &mut xinput_minor_ver) != ffi::Success as libc::c_int {
|
||||
panic!("Unable to determine XInput version");
|
||||
}
|
||||
}
|
||||
|
||||
// specify the XInput events we want to receive.
|
||||
// Button clicks and mouse events are handled via XInput
|
||||
// events. Key presses are still handled via plain core
|
||||
// X11 events.
|
||||
let mut mask: [libc::c_uchar; 3] = [0; 3];
|
||||
let mut input_event_mask = ffi::XIEventMask {
|
||||
deviceid: ffi::XIAllMasterDevices,
|
||||
mask_len: mask.len() as i32,
|
||||
mask: mask.as_mut_ptr()
|
||||
};
|
||||
let events = &[
|
||||
ffi::XI_ButtonPress,
|
||||
ffi::XI_ButtonRelease,
|
||||
ffi::XI_Motion,
|
||||
ffi::XI_Enter,
|
||||
ffi::XI_Leave,
|
||||
ffi::XI_FocusIn,
|
||||
ffi::XI_FocusOut,
|
||||
ffi::XI_TouchBegin,
|
||||
ffi::XI_TouchUpdate,
|
||||
ffi::XI_TouchEnd,
|
||||
];
|
||||
for event in events {
|
||||
ffi::XISetMask(&mut mask, *event);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
match (display.xinput2.XISelectEvents)(display.display, window, &mut input_event_mask, 1) {
|
||||
status if status as u8 == ffi::Success => (),
|
||||
err => panic!("Failed to select events {:?}", err)
|
||||
}
|
||||
}
|
||||
|
||||
XInputEventHandler {
|
||||
display: display.clone(),
|
||||
window: window,
|
||||
ic: ic,
|
||||
axis_list: read_input_axis_info(display),
|
||||
current_state: InputState {
|
||||
cursor_pos: (0.0, 0.0),
|
||||
axis_values: Vec::new()
|
||||
},
|
||||
multitouch: window_attrs.multitouch,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn translate_key_event(&self, event: &mut ffi::XKeyEvent) -> Vec<Event> {
|
||||
use events::WindowEvent::{KeyboardInput, ReceivedCharacter};
|
||||
use events::ElementState::{Pressed, Released};
|
||||
|
||||
let mut translated_events = Vec::new();
|
||||
|
||||
let state;
|
||||
if event.type_ == ffi::KeyPress {
|
||||
let raw_ev: *mut ffi::XKeyEvent = event;
|
||||
unsafe { (self.display.xlib.XFilterEvent)(mem::transmute(raw_ev), self.window) };
|
||||
state = Pressed;
|
||||
} else {
|
||||
state = Released;
|
||||
}
|
||||
|
||||
let mut kp_keysym = 0;
|
||||
|
||||
let mut ev_mods = ModifiersState::default();
|
||||
|
||||
let written = unsafe {
|
||||
use std::str;
|
||||
|
||||
let mut buffer: [u8; 16] = [mem::uninitialized(); 16];
|
||||
let raw_ev: *mut ffi::XKeyEvent = event;
|
||||
let count = (self.display.xlib.Xutf8LookupString)(self.ic, mem::transmute(raw_ev),
|
||||
mem::transmute(buffer.as_mut_ptr()),
|
||||
buffer.len() as libc::c_int, &mut kp_keysym, ptr::null_mut());
|
||||
|
||||
{
|
||||
// Translate x event state to mods
|
||||
let state = event.state;
|
||||
if (state & ffi::Mod1Mask) != 0 {
|
||||
ev_mods.alt = true;
|
||||
}
|
||||
|
||||
if (state & ffi::ShiftMask) != 0 {
|
||||
ev_mods.shift = true;
|
||||
}
|
||||
|
||||
if (state & ffi::ControlMask) != 0 {
|
||||
ev_mods.ctrl = true;
|
||||
}
|
||||
|
||||
if (state & ffi::Mod4Mask) != 0 {
|
||||
ev_mods.logo = true;
|
||||
}
|
||||
}
|
||||
|
||||
str::from_utf8(&buffer[..count as usize]).unwrap_or("").to_string()
|
||||
};
|
||||
|
||||
for chr in written.chars() {
|
||||
translated_events.push(ReceivedCharacter(chr));
|
||||
}
|
||||
|
||||
let mut keysym = unsafe {
|
||||
(self.display.xlib.XKeycodeToKeysym)(self.display.display, event.keycode as ffi::KeyCode, 0)
|
||||
};
|
||||
|
||||
if (ffi::XK_KP_Space as libc::c_ulong <= keysym) && (keysym <= ffi::XK_KP_9 as libc::c_ulong) {
|
||||
keysym = kp_keysym
|
||||
};
|
||||
|
||||
let vkey = events::keycode_to_element(keysym as libc::c_uint);
|
||||
|
||||
translated_events.push(KeyboardInput(state, event.keycode as u8, vkey, ev_mods));
|
||||
translated_events
|
||||
}
|
||||
|
||||
pub fn translate_event(&mut self, cookie: &ffi::XGenericEventCookie) -> Option<Event> {
|
||||
use events::WindowEvent::{Focused, MouseEntered, MouseInput, MouseLeft, MouseMoved, MouseWheel};
|
||||
use events::ElementState::{Pressed, Released};
|
||||
use events::MouseButton::{Left, Right, Middle};
|
||||
use events::MouseScrollDelta::LineDelta;
|
||||
use events::{Touch, TouchPhase};
|
||||
|
||||
match cookie.evtype {
|
||||
ffi::XI_ButtonPress | ffi::XI_ButtonRelease => {
|
||||
let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
|
||||
if self.multitouch && (event_data.flags & ffi::XIPointerEmulated) != 0 {
|
||||
// Deliver multi-touch events instead of emulated mouse events.
|
||||
return None
|
||||
}
|
||||
let state = if cookie.evtype == ffi::XI_ButtonPress {
|
||||
Pressed
|
||||
} else {
|
||||
Released
|
||||
};
|
||||
match event_data.detail as u32 {
|
||||
ffi::Button1 => Some(MouseInput(state, Left)),
|
||||
ffi::Button2 => Some(MouseInput(state, Middle)),
|
||||
ffi::Button3 => Some(MouseInput(state, Right)),
|
||||
ffi::Button4 | ffi::Button5 => {
|
||||
if event_data.flags & ffi::XIPointerEmulated == 0 {
|
||||
// scroll event from a traditional wheel with
|
||||
// distinct 'clicks'
|
||||
let delta = if event_data.detail as u32 == ffi::Button4 {
|
||||
1.0
|
||||
} else {
|
||||
-1.0
|
||||
};
|
||||
Some(MouseWheel(LineDelta(0.0, delta), TouchPhase::Moved))
|
||||
} else {
|
||||
// emulated button event from a touch/smooth-scroll
|
||||
// event. Ignore these events and handle scrolling
|
||||
// via XI_Motion event handler instead
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
},
|
||||
ffi::XI_Motion => {
|
||||
let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
|
||||
if self.multitouch && (event_data.flags & ffi::XIPointerEmulated) != 0 {
|
||||
// Deliver multi-touch events instead of emulated mouse events.
|
||||
return None
|
||||
}
|
||||
let axis_state = event_data.valuators;
|
||||
let mask = unsafe{ from_raw_parts(axis_state.mask, axis_state.mask_len as usize) };
|
||||
let mut axis_count = 0;
|
||||
|
||||
let mut scroll_delta = (0.0, 0.0);
|
||||
for axis_id in 0..axis_state.mask_len {
|
||||
if ffi::XIMaskIsSet(&mask, axis_id) {
|
||||
let axis_value = unsafe{*axis_state.values.offset(axis_count)};
|
||||
let delta = calc_scroll_deltas(event_data, axis_id, axis_value, &self.axis_list,
|
||||
&mut self.current_state.axis_values);
|
||||
scroll_delta.0 += delta.0;
|
||||
scroll_delta.1 += delta.1;
|
||||
axis_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if scroll_delta.0.abs() > 0.0 || scroll_delta.1.abs() > 0.0 {
|
||||
Some(MouseWheel(LineDelta(scroll_delta.0 as f32, scroll_delta.1 as f32),
|
||||
TouchPhase::Moved))
|
||||
} else {
|
||||
let new_cursor_pos = (event_data.event_x, event_data.event_y);
|
||||
if new_cursor_pos != self.current_state.cursor_pos {
|
||||
self.current_state.cursor_pos = new_cursor_pos;
|
||||
Some(MouseMoved(new_cursor_pos.0 as i32, new_cursor_pos.1 as i32))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
ffi::XI_Enter => {
|
||||
// axis movements whilst the cursor is outside the window
|
||||
// will alter the absolute value of the axes. We only want to
|
||||
// report changes in the axis value whilst the cursor is above
|
||||
// our window however, so clear the previous axis state whenever
|
||||
// the cursor re-enters the window
|
||||
self.current_state.axis_values.clear();
|
||||
Some(MouseEntered)
|
||||
},
|
||||
ffi::XI_Leave => Some(MouseLeft),
|
||||
ffi::XI_FocusIn => Some(Focused(true)),
|
||||
ffi::XI_FocusOut => Some(Focused(false)),
|
||||
ffi::XI_TouchBegin | ffi::XI_TouchUpdate | ffi::XI_TouchEnd => {
|
||||
if !self.multitouch {
|
||||
return None
|
||||
}
|
||||
let event_data: &ffi::XIDeviceEvent = unsafe{mem::transmute(cookie.data)};
|
||||
let phase = match cookie.evtype {
|
||||
ffi::XI_TouchBegin => TouchPhase::Started,
|
||||
ffi::XI_TouchUpdate => TouchPhase::Moved,
|
||||
ffi::XI_TouchEnd => TouchPhase::Ended,
|
||||
_ => unreachable!()
|
||||
};
|
||||
Some(Event::Touch(Touch {
|
||||
phase: phase,
|
||||
location: (event_data.event_x, event_data.event_y),
|
||||
id: event_data.detail as u64,
|
||||
}))
|
||||
}
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_input_axis_info(display: &Arc<XConnection>) -> Vec<Axis> {
|
||||
let mut axis_list = Vec::new();
|
||||
let mut device_count = 0;
|
||||
|
||||
// Check all input devices for scroll axes.
|
||||
let devices = unsafe{
|
||||
(display.xinput2.XIQueryDevice)(display.display, ffi::XIAllDevices, &mut device_count)
|
||||
};
|
||||
for i in 0..device_count {
|
||||
let device = unsafe { *(devices.offset(i as isize)) };
|
||||
for k in 0..device.num_classes {
|
||||
let class = unsafe { *(device.classes.offset(k as isize)) };
|
||||
match unsafe { (*class)._type } {
|
||||
// Note that scroll axis
|
||||
// are reported both as 'XIScrollClass' and 'XIValuatorClass'
|
||||
// axes. For the moment we only care about scrolling axes.
|
||||
ffi::XIScrollClass => {
|
||||
let scroll_class: &ffi::XIScrollClassInfo = unsafe{mem::transmute(class)};
|
||||
axis_list.push(Axis{
|
||||
id: scroll_class.sourceid,
|
||||
device_id: device.deviceid,
|
||||
axis_number: scroll_class.number,
|
||||
axis_type: match scroll_class.scroll_type {
|
||||
ffi::XIScrollTypeHorizontal => AxisType::HorizontalScroll,
|
||||
ffi::XIScrollTypeVertical => AxisType::VerticalScroll,
|
||||
_ => { unreachable!() }
|
||||
},
|
||||
scroll_increment: scroll_class.increment,
|
||||
})
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
(display.xinput2.XIFreeDeviceInfo)(devices);
|
||||
}
|
||||
|
||||
axis_list
|
||||
}
|
||||
|
||||
/// Given an input motion event for an axis and the previous
|
||||
/// state of the axes, return the horizontal/vertical
|
||||
/// scroll deltas
|
||||
fn calc_scroll_deltas(event: &ffi::XIDeviceEvent,
|
||||
axis_id: i32,
|
||||
axis_value: f64,
|
||||
axis_list: &[Axis],
|
||||
prev_axis_values: &mut Vec<AxisValue>) -> (f64, f64) {
|
||||
let prev_value_pos = prev_axis_values.iter().position(|prev_axis| {
|
||||
prev_axis.device_id == event.sourceid &&
|
||||
prev_axis.axis_number == axis_id
|
||||
});
|
||||
let delta = match prev_value_pos {
|
||||
Some(idx) => prev_axis_values[idx].value - axis_value,
|
||||
None => 0.0
|
||||
};
|
||||
|
||||
let new_axis_value = AxisValue{
|
||||
device_id: event.sourceid,
|
||||
axis_number: axis_id,
|
||||
value: axis_value
|
||||
};
|
||||
|
||||
match prev_value_pos {
|
||||
Some(idx) => prev_axis_values[idx] = new_axis_value,
|
||||
None => prev_axis_values.push(new_axis_value)
|
||||
}
|
||||
|
||||
let mut scroll_delta = (0.0, 0.0);
|
||||
|
||||
for axis in axis_list.iter() {
|
||||
if axis.id == event.sourceid &&
|
||||
axis.axis_number == axis_id {
|
||||
match axis.axis_type {
|
||||
AxisType::HorizontalScroll => scroll_delta.0 = delta / axis.scroll_increment,
|
||||
AxisType::VerticalScroll => scroll_delta.1 = delta / axis.scroll_increment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scroll_delta
|
||||
}
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
#![cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd"))]
|
||||
|
||||
pub use self::monitor::{MonitorId, get_available_monitors, get_primary_monitor};
|
||||
pub use self::window::{Window, XWindow, PollEventsIterator, WaitEventsIterator, WindowProxy};
|
||||
pub use self::xdisplay::{XConnection, XNotSupported, XError};
|
||||
|
||||
pub mod ffi;
|
||||
|
||||
use platform::PlatformSpecificWindowBuilderAttributes;
|
||||
use CreationError;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
mod events;
|
||||
mod input;
|
||||
mod monitor;
|
||||
mod window;
|
||||
mod xdisplay;
|
||||
|
||||
// API TRANSITION
|
||||
//
|
||||
// We don't use the gen_api_transistion!() macro but rather do the expansion manually:
|
||||
//
|
||||
// As this module is nested into platform/linux, its code is not _exactly_ the same as
|
||||
// the one generated by the macro.
|
||||
|
||||
pub struct EventsLoop {
|
||||
windows: ::std::sync::Mutex<Vec<::std::sync::Arc<Window>>>,
|
||||
interrupted: ::std::sync::atomic::AtomicBool,
|
||||
}
|
||||
|
||||
impl EventsLoop {
|
||||
pub fn new() -> EventsLoop {
|
||||
EventsLoop {
|
||||
windows: ::std::sync::Mutex::new(vec![]),
|
||||
interrupted: ::std::sync::atomic::AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interrupt(&self) {
|
||||
self.interrupted.store(true, ::std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn poll_events<F>(&self, mut callback: F)
|
||||
where F: FnMut(::Event)
|
||||
{
|
||||
let windows = self.windows.lock().unwrap();
|
||||
for window in windows.iter() {
|
||||
for event in window.poll_events() {
|
||||
callback(::Event::WindowEvent {
|
||||
window_id: ::WindowId(::platform::WindowId::X(WindowId(&**window as *const Window as usize))),
|
||||
event: event,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_forever<F>(&self, mut callback: F)
|
||||
where F: FnMut(::Event)
|
||||
{
|
||||
self.interrupted.store(false, ::std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
// Yeah that's a very bad implementation.
|
||||
loop {
|
||||
self.poll_events(|e| callback(e));
|
||||
::std::thread::sleep(::std::time::Duration::from_millis(5));
|
||||
if self.interrupted.load(::std::sync::atomic::Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct WindowId(usize);
|
||||
|
||||
pub struct Window2 {
|
||||
pub window: ::std::sync::Arc<Window>,
|
||||
events_loop: ::std::sync::Weak<::platform::EventsLoop>,
|
||||
}
|
||||
|
||||
impl ::std::ops::Deref for Window2 {
|
||||
type Target = Window;
|
||||
#[inline]
|
||||
fn deref(&self) -> &Window {
|
||||
&*self.window
|
||||
}
|
||||
}
|
||||
|
||||
impl Window2 {
|
||||
pub fn new(events_loop: ::std::sync::Arc<::platform::EventsLoop>, display: &Arc<XConnection>,
|
||||
window: &::WindowAttributes, pl_attribs: &PlatformSpecificWindowBuilderAttributes)
|
||||
-> Result<Window2, CreationError>
|
||||
{
|
||||
let win = ::std::sync::Arc::new(try!(Window::new(display, window, pl_attribs)));
|
||||
if let ::platform::EventsLoop::X(ref ev) = *events_loop {
|
||||
ev.windows.lock().unwrap().push(win.clone());
|
||||
} else {
|
||||
// It should not be possible to create an eventloop not matching the backend
|
||||
// in use
|
||||
unreachable!()
|
||||
}
|
||||
Ok(Window2 {
|
||||
window: win,
|
||||
events_loop: ::std::sync::Arc::downgrade(&events_loop),
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn id(&self) -> WindowId {
|
||||
WindowId(&*self.window as *const Window as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window2 {
|
||||
fn drop(&mut self) {
|
||||
if let Some(ev) = self.events_loop.upgrade() {
|
||||
if let ::platform::EventsLoop::X(ref ev) = *ev {
|
||||
let mut windows = ev.windows.lock().unwrap();
|
||||
windows.retain(|w| &**w as *const Window != &*self.window as *const _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::XConnection;
|
||||
use native_monitor::NativeMonitorId;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MonitorId(pub Arc<XConnection>, pub u32);
|
||||
|
||||
pub fn get_available_monitors(x: &Arc<XConnection>) -> VecDeque<MonitorId> {
|
||||
let nb_monitors = unsafe { (x.xlib.XScreenCount)(x.display) };
|
||||
x.check_errors().expect("Failed to call XScreenCount");
|
||||
|
||||
let mut monitors = VecDeque::new();
|
||||
monitors.extend((0 .. nb_monitors).map(|i| MonitorId(x.clone(), i as u32)));
|
||||
monitors
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_primary_monitor(x: &Arc<XConnection>) -> MonitorId {
|
||||
let primary_monitor = unsafe { (x.xlib.XDefaultScreen)(x.display) };
|
||||
x.check_errors().expect("Failed to call XDefaultScreen");
|
||||
MonitorId(x.clone(), primary_monitor as u32)
|
||||
}
|
||||
|
||||
impl MonitorId {
|
||||
pub fn get_name(&self) -> Option<String> {
|
||||
let MonitorId(_, screen_num) = *self;
|
||||
Some(format!("Monitor #{}", screen_num))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_native_identifier(&self) -> NativeMonitorId {
|
||||
NativeMonitorId::Numeric(self.1)
|
||||
}
|
||||
|
||||
pub fn get_dimensions(&self) -> (u32, u32) {
|
||||
let screen = unsafe { (self.0.xlib.XScreenOfDisplay)(self.0.display, self.1 as i32) };
|
||||
let width = unsafe { (self.0.xlib.XWidthOfScreen)(screen) };
|
||||
let height = unsafe { (self.0.xlib.XHeightOfScreen)(screen) };
|
||||
self.0.check_errors().expect("Failed to get monitor dimensions");
|
||||
(width as u32, height as u32)
|
||||
}
|
||||
}
|
||||
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