mirror of
https://github.com/italicsjenga/winit-sonoma-fix.git
synced 2024-12-23 22:01:31 +11:00
Squashed commit of the following: (#853)
commit fa95f204d3c10ceca70e794870657a0f33349761 Author: Hal Gentz <zegentzy@protonmail.com> Date: Sun Apr 28 00:14:01 2019 -0600 xrender Signed-off-by: Hal Gentz <zegentzy@protonmail.com> commit b62cee51c7b22f6f150bfe04f9b28f024e641323 Merge: 3f021ea7 a6551f46 Author: Hal Gentz <zegentzy@protonmail.com> Date: Thu Apr 25 18:13:43 2019 -0600 Merge branch 'macos-gentz' of github.com:ZeGentzy/winit into macos-gentz commit 3f021ea7f7ac6bc2a697a5b6e4e6424e838a2139 Author: Hal Gentz <zegentzy@protonmail.com> Date: Thu Apr 25 18:04:02 2019 -0600 Get rid of warnings. Signed-off-by: Hal Gentz <zegentzy@protonmail.com> commit a6551f4607ea0bc26df8716dee8115371ef367db Author: Hal Gentz <zegentzy@protonmail.com> Date: Thu Apr 25 07:40:56 2019 -0600 Fix example Signed-off-by: Hal Gentz <zegentzy@protonmail.com> commit cbfda6c57e9740b49d2b496bda43197f611cb48c Author: Hal Gentz <zegentzy@protonmail.com> Date: Wed Apr 24 23:47:46 2019 -0600 Fixes Signed-off-by: Hal Gentz <zegentzy@protonmail.com> commit 86bc86f3d3add4a6125aa9b2eca79061c0dfcd91 Author: Hal Gentz <zegentzy@protonmail.com> Date: Wed Apr 24 23:39:19 2019 -0600 Backport9a23ec3c37 (diff-1d95fe39cdbaa708c975380a16c314cb)
Signed-off-by: Hal Gentz <zegentzy@protonmail.com> commit 742a688efe2f0eeacc2ffbf49b1157c4aaffccbd Author: Hal Gentz <zegentzy@protonmail.com> Date: Wed Apr 24 23:09:14 2019 -0600 Backports45a4281413 (diff-1d95fe39cdbaa708c975380a16c314cb)
Signed-off-by: Hal Gentz <zegentzy@protonmail.com> commit 6c81f2a517d4e2d5ba2ff3eddca030bce972cb2a Author: Hal Gentz <zegentzy@protonmail.com> Date: Wed Apr 24 23:05:57 2019 -0600 Francesca's macos changes Also backportsbfbcab3a01 (diff-1d95fe39cdbaa708c975380a16c314cb)
commit 7c2e1300c26a0634ad505ce72b90eb6dc2fdcac7 Author: Francesca Plebani <franplebani@gmail.com> Date: Wed Apr 24 20:58:26 2019 -0600 Squashed commit of the following: commit 5f4aa9f01a719eef98c6d894801c20ee8f96d30f Author: Francesca Plebani <franplebani@gmail.com> Date: Fri Dec 21 17:14:14 2018 -0500 Protect against reentrancy (messily) commit b75073a5b2a8d65ab8806a00ffee390752255c8c Author: Francesca Plebani <franplebani@gmail.com> Date: Fri Dec 21 15:15:27 2018 -0500 Send resize events immediately commit 8e9fc01bd6b404f59488b130413f48e4e5f89b0d Author: Francesca Plebani <franplebani@gmail.com> Date: Fri Dec 21 16:07:43 2018 -0500 Don't use struct for window delegate commit c6853b0c4a8fe357f463604bb879dc1be424860e Author: Francesca Plebani <franplebani@gmail.com> Date: Wed Dec 19 21:17:48 2018 -0500 Split up util commit 262c46b148413130fa239099f1151c1f1bd5c13c Author: Francesca Plebani <franplebani@gmail.com> Date: Wed Dec 19 20:55:00 2018 -0500 Use dispatch crate commit 63152c2f475794d1a36a5b3687c777664d7d5613 Author: Francesca Plebani <franplebani@gmail.com> Date: Wed Dec 19 20:29:13 2018 -0500 RedrawRequested commit 27e475c7c78b059fd9b5e8350cd26756eecdfc94 Author: Francesca Plebani <franplebani@gmail.com> Date: Wed Dec 19 19:24:44 2018 -0500 User events commit 157418d7dedace9c571e977d98ea92464c3188b2 Author: Francesca Plebani <franplebani@gmail.com> Date: Tue Dec 18 22:38:05 2018 -0500 Moved out cursor loading commit b4925641c973979a38743202b4269efe09ac43b4 Author: Francesca Plebani <franplebani@gmail.com> Date: Tue Dec 18 21:32:12 2018 -0500 Fixed a bunch of threading issues commit 4aef63dfb78dfaf38c83cb0e88d4ea9d8d0578a6 Author: Francesca Plebani <franplebani@gmail.com> Date: Mon Dec 17 13:54:59 2018 -0500 Wait works commit 72ed426c695df5dc410902263bd74188059b8ddd Author: Francesca Plebani <franplebani@gmail.com> Date: Fri Dec 14 20:49:10 2018 -0500 Fixed drag and dropg commit 658209f4a20acd536218f41a01fb8cbbebc705e0 Author: Francesca Plebani <franplebani@gmail.com> Date: Fri Dec 14 20:42:42 2018 -0500 Made mutexes finer for less deadlock risk commit 8e6b9866084690da900c4d058e412cab8ebb30c4 Author: Francesca Plebani <franplebani@gmail.com> Date: Fri Dec 14 16:45:06 2018 -0500 Dump (encapsulate) everything into AppState commit d2dc83df15939d89301e2cff0ffa2d98c48b406f Author: Francesca Plebani <franplebani@gmail.com> Date: Thu Dec 13 17:36:47 2018 -0500 All window events work! commit 7c7fcc98872b3c35bd7767b5c6235a74bc105e06 Author: Francesca Plebani <franplebani@gmail.com> Date: Wed Dec 12 17:11:09 2018 -0500 Very rough usage of CFRunLoop commit 3c7a52ff4df683b5b7e1751e4051ec445a818774 Author: Francesca Plebani <franplebani@gmail.com> Date: Tue Dec 11 15:45:23 2018 -0500 Fixed deadlocks commit b74c7fe1bcd173e9b0c0e004956c257e805bc2a2 Author: Francesca Plebani <franplebani@gmail.com> Date: Mon Dec 10 18:59:46 2018 -0500 Fix keyDown deadlock commit 3798f9c1a4bef2a3d1552f846b26efc31b1bbb6c Author: Francesca Plebani <franplebani@gmail.com> Date: Mon Dec 10 18:44:40 2018 -0500 It builds! commit 8c8620214357714c5cd0b3beefda6704512e3f64 Author: Francesca Plebani <franplebani@gmail.com> Date: Fri Dec 7 21:09:55 2018 -0500 Horribly broken so far commit 8269ed2a9270e5ec5b14f80fd21d1e0e6f51be29 Author: Osspial <osspial@gmail.com> Date: Mon Nov 19 23:51:20 2018 -0500 Fix crash with runner refcell not getting dropped commit 54ce6a21a0722e408ae49c74f5008005fc1e4cbf Author: Osspial <osspial@gmail.com> Date: Sun Nov 18 19:12:45 2018 -0500 Fix buffered events not getting dispatched commit 2c18b804df66f49f93cfe722a679d6c5e01d8cb1 Author: Osspial <osspial@gmail.com> Date: Sun Nov 18 18:51:24 2018 -0500 Fix thread executor not executing closure when called from non-loop thread commit 5a3a5e2293cec3e566c4aac344ae7eaa343608b5 Author: Osspial <osspial@gmail.com> Date: Thu Nov 15 22:43:59 2018 -0500 Fix some deadlocks that could occur when changing window state commit 2a3cefd8c5df1c06127b05651cbdf5e3d9e3a6d3 Author: Osspial <osspial@gmail.com> Date: Thu Nov 15 16:45:17 2018 -0500 Document and implement Debug for EventLoopWindowTarget commit fa46825a289ca0587dc97f9c00dea5516fb4925a Author: Osspial <osspial@gmail.com> Date: Thu Nov 15 16:40:48 2018 -0500 Replace &EventLoop in callback with &EventLoopWindowTarget commit 9f36a7a68e1dc379cf9091213dae2c3586d3e473 Author: Osspial <osspial@gmail.com> Date: Wed Nov 14 21:28:38 2018 -0500 Fix freeze when setting decorations commit d9c3daca9b459e02ef614568fe803a723965fe8d Author: Osspial <osspial@gmail.com> Date: Fri Nov 9 20:41:15 2018 -0500 Fix 1.24.1 build commit 5289d22372046bac403a279c3641737c0cfc46d2 Author: Osspial <osspial@gmail.com> Date: Fri Nov 9 00:00:27 2018 -0500 Remove serde implementations from ControlFlow commit 92ac3d6ac7915923c22c380cc3a74c5f3830708e Author: Osspial <osspial@gmail.com> Date: Thu Nov 8 23:46:41 2018 -0500 Remove crossbeam dependency and make drop events work again commit 8299eb2f03773a34079c61fc8adb51405aafc467 Author: Osspial <osspial@gmail.com> Date: Thu Sep 13 22:39:40 2018 -0400 Fix crash when running in release mode commit bb6ab1bb6e9595e90f1915fdde7e23904f2ba594 Author: Osspial <osspial@gmail.com> Date: Sun Sep 9 14:28:16 2018 -0400 Fix unreachable panic after setting ControlFlow to Poll during some RedrawRequested events. commit 5068ff4ee152bfe93c9190235f02d001202feb88 Author: Osspial <osspial@gmail.com> Date: Sun Sep 9 14:14:28 2018 -0400 Improve clarity/fix typos in docs commit 8ed575ff4a4f0961bb2e784bda1ae109c6bd37b7 Author: Osspial <osspial@gmail.com> Date: Sun Sep 9 00:19:53 2018 -0400 Update send test and errors that broke some examples/APIs commit bf7bfa82ebb5d6ae110ce0492c124ef462945f85 Author: Osspial <osspial@gmail.com> Date: Wed Sep 5 22:36:05 2018 -0400 Fix resize lag when waiting in some situations commit 70722cc4c322e3e599b3a03bce5058a5d433970b Author: Osspial <osspial@gmail.com> Date: Wed Sep 5 19:58:52 2018 -1100 When SendEvent is called during event closure, buffer events commit 53370924b25da15ddd172173150b228065324864 Author: Osspial <osspial@gmail.com> Date: Sun Aug 26 21:55:51 2018 -0400 Improve WaitUntil timer precision commit a654400e730400c2e3584be2f47153043b5b7efe Author: Osspial <osspial@gmail.com> Date: Thu Aug 23 21:06:19 2018 -0400 Add CHANGELOG entry commit deb7d379b7c04e61d6d50ff655eccac0ad692e44 Author: Osspial <osspial@gmail.com> Date: Thu Aug 23 20:19:56 2018 -0400 Rename MonitorId to MonitorHandle commit 8d8d9b7cd1386c99c40023d86e17d10c3fd6652f Author: Osspial <osspial@gmail.com> Date: Thu Aug 23 20:16:52 2018 -0400 Change instances of "events_loop" to "event_loop" commit 0f344087630ae252c9c8f453864e684a1a5405b1 Author: Osspial <osspial@gmail.com> Date: Thu Aug 23 20:13:53 2018 -0400 Improve docs for run and run_return commit fba41f7a7ed8585cbb658b6d0b2f34f75482cb3d Author: Osspial <osspial@gmail.com> Date: Thu Aug 23 19:09:53 2018 -0400 Small changes to examples commit 42e8a0d2cf77af79da082fff7cd29cc8f52d99df Author: Osspial <osspial@gmail.com> Date: Thu Aug 23 19:09:19 2018 -0400 Improve documentation commit 4377680a44ea86dad52954f90bc7d8ad7ed0b4bf Author: Osspial <osspial@gmail.com> Date: Wed Aug 22 23:01:36 2018 -0400 Re-organize into module structure commit f20fac99f6ac57c51603a92d792fd4f665feb7f6 Author: Osspial <osspial@gmail.com> Date: Wed Aug 22 22:07:39 2018 -0400 Add platform::desktop module with EventLoopExt::run_return commit dad24d086aaaff60e557efc4f41d1ae7e3c71738 Author: Osspial <osspial@gmail.com> Date: Wed Aug 22 18:03:41 2018 -0400 Rename os to platform, add Ext trait postfixes commit 7df59c60a06008226f6455619e7242ed0156ed8d Author: Osspial <osspial@gmail.com> Date: Wed Aug 22 17:59:36 2018 -0400 Rename platform to platform_impl commit 99c0f84a9fc771c9c96099232de3716ddf27ca80 Author: Osspial <osspial@gmail.com> Date: Wed Aug 22 17:55:27 2018 -0400 Add request_redraw commit a0fef1a5fad9b5d5da59fff191c7d9c398ea9e01 Author: Osspial <osspial@gmail.com> Date: Mon Aug 20 01:47:11 2018 -0400 Fully invert windows control flow so win32 calls into winit's callback commit 2c607ff87f8fbcad8aa9dc3783b3298c014dd177 Author: Osspial <osspial@gmail.com> Date: Sun Aug 19 13:44:22 2018 -0400 Add ability to send custom user events commit a0b2bb36953f018ff782cef8fc86c6db9343095d Author: Osspial <osspial@gmail.com> Date: Fri Aug 17 17:49:46 2018 -0400 Add StartCause::Init support, timer example commit 02f922f003f56215b92b8feeb9148ad2dd181fc2 Author: Osspial <osspial@gmail.com> Date: Fri Aug 17 17:31:04 2018 -0400 Implement new ControlFlow and associated events commit 8b8a7675ec67e15a0f8f69db0bdeb79bee0ac20d Author: Osspial <osspial@gmail.com> Date: Fri Jul 13 01:48:26 2018 -0400 Replace windows Mutex with parking_lot Mutex commit 9feada206f6b9fb1e9da118be6b77dfc217ace8d Author: Osspial <osspial@gmail.com> Date: Fri Jul 13 01:39:53 2018 -0400 Update run_forever to hijack thread commit 2e83bac99cc264cd2723cb182feea84a0a15e08d Author: Osspial <osspial@gmail.com> Date: Thu Jul 12 23:43:58 2018 -0400 Remove second thread from win32 backend commit 64b8a9c6a50362d10c074077a1e37b057f3e3c81 Author: Osspial <osspial@gmail.com> Date: Thu Jul 12 22:13:07 2018 -0400 Rename WindowEvent::Refresh to WindowEvent::Redraw commit 529c08555fd0b709a23d486211d28fbd0980fc94 Author: Osspial <osspial@gmail.com> Date: Thu Jul 12 22:04:38 2018 -0400 Rename EventsLoop and associated types to EventLoop Signed-off-by: Hal Gentz <zegentzy@protonmail.com> Co-authored-by: Hal Gentz <zegentzy@protonmail.com> commit cfb929ba0a9e787f8bb1a6dae4e05e4c7776bc97 Author: Hal Gentz <zegentzy@protonmail.com> Date: Thu Apr 25 07:40:56 2019 -0600 Fix example Signed-off-by: Hal Gentz <zegentzy@protonmail.com> commit 68d3317ff58381d55f5f9bd3db0860d66544fe12 Author: Hal Gentz <zegentzy@protonmail.com> Date: Wed Apr 24 23:47:46 2019 -0600 Fixes Signed-off-by: Hal Gentz <zegentzy@protonmail.com> commit 02d1aae4db27df054b703aa935ca118f31e17123 Author: Hal Gentz <zegentzy@protonmail.com> Date: Wed Apr 24 23:39:19 2019 -0600 Backport9a23ec3c37 (diff-1d95fe39cdbaa708c975380a16c314cb)
Signed-off-by: Hal Gentz <zegentzy@protonmail.com> commit dd9de5a6d444a9ab17afe470f4cf2a57e3ed76ae Author: Hal Gentz <zegentzy@protonmail.com> Date: Wed Apr 24 23:09:14 2019 -0600 Backports45a4281413 (diff-1d95fe39cdbaa708c975380a16c314cb)
Signed-off-by: Hal Gentz <zegentzy@protonmail.com> commit 533e2adc1d1e417742475786635848b1620e476c Author: Hal Gentz <zegentzy@protonmail.com> Date: Wed Apr 24 23:05:57 2019 -0600 Francesca's macos changes Also backportsbfbcab3a01 (diff-1d95fe39cdbaa708c975380a16c314cb)
commit 73b52221080bd3a881ae3a58c2dbb19bc8d954c6 Author: Hal Gentz <zegentzy@protonmail.com> Date: Wed Apr 24 20:58:26 2019 -0600 Squashed commit of the following: commit 5f4aa9f01a719eef98c6d894801c20ee8f96d30f Author: Francesca Plebani <franplebani@gmail.com> Date: Fri Dec 21 17:14:14 2018 -0500 Protect against reentrancy (messily) commit b75073a5b2a8d65ab8806a00ffee390752255c8c Author: Francesca Plebani <franplebani@gmail.com> Date: Fri Dec 21 15:15:27 2018 -0500 Send resize events immediately commit 8e9fc01bd6b404f59488b130413f48e4e5f89b0d Author: Francesca Plebani <franplebani@gmail.com> Date: Fri Dec 21 16:07:43 2018 -0500 Don't use struct for window delegate commit c6853b0c4a8fe357f463604bb879dc1be424860e Author: Francesca Plebani <franplebani@gmail.com> Date: Wed Dec 19 21:17:48 2018 -0500 Split up util commit 262c46b148413130fa239099f1151c1f1bd5c13c Author: Francesca Plebani <franplebani@gmail.com> Date: Wed Dec 19 20:55:00 2018 -0500 Use dispatch crate commit 63152c2f475794d1a36a5b3687c777664d7d5613 Author: Francesca Plebani <franplebani@gmail.com> Date: Wed Dec 19 20:29:13 2018 -0500 RedrawRequested commit 27e475c7c78b059fd9b5e8350cd26756eecdfc94 Author: Francesca Plebani <franplebani@gmail.com> Date: Wed Dec 19 19:24:44 2018 -0500 User events commit 157418d7dedace9c571e977d98ea92464c3188b2 Author: Francesca Plebani <franplebani@gmail.com> Date: Tue Dec 18 22:38:05 2018 -0500 Moved out cursor loading commit b4925641c973979a38743202b4269efe09ac43b4 Author: Francesca Plebani <franplebani@gmail.com> Date: Tue Dec 18 21:32:12 2018 -0500 Fixed a bunch of threading issues commit 4aef63dfb78dfaf38c83cb0e88d4ea9d8d0578a6 Author: Francesca Plebani <franplebani@gmail.com> Date: Mon Dec 17 13:54:59 2018 -0500 Wait works commit 72ed426c695df5dc410902263bd74188059b8ddd Author: Francesca Plebani <franplebani@gmail.com> Date: Fri Dec 14 20:49:10 2018 -0500 Fixed drag and dropg commit 658209f4a20acd536218f41a01fb8cbbebc705e0 Author: Francesca Plebani <franplebani@gmail.com> Date: Fri Dec 14 20:42:42 2018 -0500 Made mutexes finer for less deadlock risk commit 8e6b9866084690da900c4d058e412cab8ebb30c4 Author: Francesca Plebani <franplebani@gmail.com> Date: Fri Dec 14 16:45:06 2018 -0500 Dump (encapsulate) everything into AppState commit d2dc83df15939d89301e2cff0ffa2d98c48b406f Author: Francesca Plebani <franplebani@gmail.com> Date: Thu Dec 13 17:36:47 2018 -0500 All window events work! commit 7c7fcc98872b3c35bd7767b5c6235a74bc105e06 Author: Francesca Plebani <franplebani@gmail.com> Date: Wed Dec 12 17:11:09 2018 -0500 Very rough usage of CFRunLoop commit 3c7a52ff4df683b5b7e1751e4051ec445a818774 Author: Francesca Plebani <franplebani@gmail.com> Date: Tue Dec 11 15:45:23 2018 -0500 Fixed deadlocks commit b74c7fe1bcd173e9b0c0e004956c257e805bc2a2 Author: Francesca Plebani <franplebani@gmail.com> Date: Mon Dec 10 18:59:46 2018 -0500 Fix keyDown deadlock commit 3798f9c1a4bef2a3d1552f846b26efc31b1bbb6c Author: Francesca Plebani <franplebani@gmail.com> Date: Mon Dec 10 18:44:40 2018 -0500 It builds! commit 8c8620214357714c5cd0b3beefda6704512e3f64 Author: Francesca Plebani <franplebani@gmail.com> Date: Fri Dec 7 21:09:55 2018 -0500 Horribly broken so far commit 8269ed2a9270e5ec5b14f80fd21d1e0e6f51be29 Author: Osspial <osspial@gmail.com> Date: Mon Nov 19 23:51:20 2018 -0500 Fix crash with runner refcell not getting dropped commit 54ce6a21a0722e408ae49c74f5008005fc1e4cbf Author: Osspial <osspial@gmail.com> Date: Sun Nov 18 19:12:45 2018 -0500 Fix buffered events not getting dispatched commit 2c18b804df66f49f93cfe722a679d6c5e01d8cb1 Author: Osspial <osspial@gmail.com> Date: Sun Nov 18 18:51:24 2018 -0500 Fix thread executor not executing closure when called from non-loop thread commit 5a3a5e2293cec3e566c4aac344ae7eaa343608b5 Author: Osspial <osspial@gmail.com> Date: Thu Nov 15 22:43:59 2018 -0500 Fix some deadlocks that could occur when changing window state commit 2a3cefd8c5df1c06127b05651cbdf5e3d9e3a6d3 Author: Osspial <osspial@gmail.com> Date: Thu Nov 15 16:45:17 2018 -0500 Document and implement Debug for EventLoopWindowTarget commit fa46825a289ca0587dc97f9c00dea5516fb4925a Author: Osspial <osspial@gmail.com> Date: Thu Nov 15 16:40:48 2018 -0500 Replace &EventLoop in callback with &EventLoopWindowTarget commit 9f36a7a68e1dc379cf9091213dae2c3586d3e473 Author: Osspial <osspial@gmail.com> Date: Wed Nov 14 21:28:38 2018 -0500 Fix freeze when setting decorations commit d9c3daca9b459e02ef614568fe803a723965fe8d Author: Osspial <osspial@gmail.com> Date: Fri Nov 9 20:41:15 2018 -0500 Fix 1.24.1 build commit 5289d22372046bac403a279c3641737c0cfc46d2 Author: Osspial <osspial@gmail.com> Date: Fri Nov 9 00:00:27 2018 -0500 Remove serde implementations from ControlFlow commit 92ac3d6ac7915923c22c380cc3a74c5f3830708e Author: Osspial <osspial@gmail.com> Date: Thu Nov 8 23:46:41 2018 -0500 Remove crossbeam dependency and make drop events work again commit 8299eb2f03773a34079c61fc8adb51405aafc467 Author: Osspial <osspial@gmail.com> Date: Thu Sep 13 22:39:40 2018 -0400 Fix crash when running in release mode commit bb6ab1bb6e9595e90f1915fdde7e23904f2ba594 Author: Osspial <osspial@gmail.com> Date: Sun Sep 9 14:28:16 2018 -0400 Fix unreachable panic after setting ControlFlow to Poll during some RedrawRequested events. commit 5068ff4ee152bfe93c9190235f02d001202feb88 Author: Osspial <osspial@gmail.com> Date: Sun Sep 9 14:14:28 2018 -0400 Improve clarity/fix typos in docs commit 8ed575ff4a4f0961bb2e784bda1ae109c6bd37b7 Author: Osspial <osspial@gmail.com> Date: Sun Sep 9 00:19:53 2018 -0400 Update send test and errors that broke some examples/APIs commit bf7bfa82ebb5d6ae110ce0492c124ef462945f85 Author: Osspial <osspial@gmail.com> Date: Wed Sep 5 22:36:05 2018 -0400 Fix resize lag when waiting in some situations commit 70722cc4c322e3e599b3a03bce5058a5d433970b Author: Osspial <osspial@gmail.com> Date: Wed Sep 5 19:58:52 2018 -1100 When SendEvent is called during event closure, buffer events commit 53370924b25da15ddd172173150b228065324864 Author: Osspial <osspial@gmail.com> Date: Sun Aug 26 21:55:51 2018 -0400 Improve WaitUntil timer precision commit a654400e730400c2e3584be2f47153043b5b7efe Author: Osspial <osspial@gmail.com> Date: Thu Aug 23 21:06:19 2018 -0400 Add CHANGELOG entry commit deb7d379b7c04e61d6d50ff655eccac0ad692e44 Author: Osspial <osspial@gmail.com> Date: Thu Aug 23 20:19:56 2018 -0400 Rename MonitorId to MonitorHandle commit 8d8d9b7cd1386c99c40023d86e17d10c3fd6652f Author: Osspial <osspial@gmail.com> Date: Thu Aug 23 20:16:52 2018 -0400 Change instances of "events_loop" to "event_loop" commit 0f344087630ae252c9c8f453864e684a1a5405b1 Author: Osspial <osspial@gmail.com> Date: Thu Aug 23 20:13:53 2018 -0400 Improve docs for run and run_return commit fba41f7a7ed8585cbb658b6d0b2f34f75482cb3d Author: Osspial <osspial@gmail.com> Date: Thu Aug 23 19:09:53 2018 -0400 Small changes to examples commit 42e8a0d2cf77af79da082fff7cd29cc8f52d99df Author: Osspial <osspial@gmail.com> Date: Thu Aug 23 19:09:19 2018 -0400 Improve documentation commit 4377680a44ea86dad52954f90bc7d8ad7ed0b4bf Author: Osspial <osspial@gmail.com> Date: Wed Aug 22 23:01:36 2018 -0400 Re-organize into module structure commit f20fac99f6ac57c51603a92d792fd4f665feb7f6 Author: Osspial <osspial@gmail.com> Date: Wed Aug 22 22:07:39 2018 -0400 Add platform::desktop module with EventLoopExt::run_return commit dad24d086aaaff60e557efc4f41d1ae7e3c71738 Author: Osspial <osspial@gmail.com> Date: Wed Aug 22 18:03:41 2018 -0400 Rename os to platform, add Ext trait postfixes commit 7df59c60a06008226f6455619e7242ed0156ed8d Author: Osspial <osspial@gmail.com> Date: Wed Aug 22 17:59:36 2018 -0400 Rename platform to platform_impl commit 99c0f84a9fc771c9c96099232de3716ddf27ca80 Author: Osspial <osspial@gmail.com> Date: Wed Aug 22 17:55:27 2018 -0400 Add request_redraw commit a0fef1a5fad9b5d5da59fff191c7d9c398ea9e01 Author: Osspial <osspial@gmail.com> Date: Mon Aug 20 01:47:11 2018 -0400 Fully invert windows control flow so win32 calls into winit's callback commit 2c607ff87f8fbcad8aa9dc3783b3298c014dd177 Author: Osspial <osspial@gmail.com> Date: Sun Aug 19 13:44:22 2018 -0400 Add ability to send custom user events commit a0b2bb36953f018ff782cef8fc86c6db9343095d Author: Osspial <osspial@gmail.com> Date: Fri Aug 17 17:49:46 2018 -0400 Add StartCause::Init support, timer example commit 02f922f003f56215b92b8feeb9148ad2dd181fc2 Author: Osspial <osspial@gmail.com> Date: Fri Aug 17 17:31:04 2018 -0400 Implement new ControlFlow and associated events commit 8b8a7675ec67e15a0f8f69db0bdeb79bee0ac20d Author: Osspial <osspial@gmail.com> Date: Fri Jul 13 01:48:26 2018 -0400 Replace windows Mutex with parking_lot Mutex commit 9feada206f6b9fb1e9da118be6b77dfc217ace8d Author: Osspial <osspial@gmail.com> Date: Fri Jul 13 01:39:53 2018 -0400 Update run_forever to hijack thread commit 2e83bac99cc264cd2723cb182feea84a0a15e08d Author: Osspial <osspial@gmail.com> Date: Thu Jul 12 23:43:58 2018 -0400 Remove second thread from win32 backend commit 64b8a9c6a50362d10c074077a1e37b057f3e3c81 Author: Osspial <osspial@gmail.com> Date: Thu Jul 12 22:13:07 2018 -0400 Rename WindowEvent::Refresh to WindowEvent::Redraw commit 529c08555fd0b709a23d486211d28fbd0980fc94 Author: Osspial <osspial@gmail.com> Date: Thu Jul 12 22:04:38 2018 -0400 Rename EventsLoop and associated types to EventLoop Signed-off-by: Hal Gentz <zegentzy@protonmail.com> commit ab1dfaaaa53a3acd206bf494ac90e3fe130dc609 Author: Hal Gentz <zegentzy@protonmail.com> Date: Tue Apr 23 21:52:17 2019 -0600 Minor Signed-off-by: Hal Gentz <zegentzy@protonmail.com> commit 7933209d603e0794adb806d9cf53507f1c2f1d3c Author: Victor Berger <victor.berger@m4x.org> Date: Thu Apr 18 09:10:41 2019 +0200 wayland/x11: Make ControlFlow::Exit sticky commit 8355a7513e299ffba21062c8518bcf4bdb735ba9 Author: Victor Berger <victor.berger@m4x.org> Date: Tue Apr 16 12:21:33 2019 +0200 x11: Implement run_return using calloop commit f64edb60cc85fcd98a1cec955ba9980f617fdd73 Author: Victor Berger <victor.berger@m4x.org> Date: Tue Apr 16 10:42:04 2019 +0200 x11: port to evl2 with stubs commit be372898ddc60e47887c9a152c10ff498445f8cf Author: Victor Berger <victor.berger@m4x.org> Date: Mon Apr 15 17:35:59 2019 +0200 Fix compilation on Linux. Signed-off-by: Hal Gentz <zegentzy@protonmail.com> Co-authored-by: Francesca Plebani <franplebani@gmail.com>
This commit is contained in:
parent
94f998af0a
commit
d5391686ae
|
@ -21,6 +21,7 @@ serde = { version = "1", optional = true, features = ["serde_derive"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
image = "0.21"
|
image = "0.21"
|
||||||
|
env_logger = "0.5"
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies.android_glue]
|
[target.'cfg(target_os = "android")'.dependencies.android_glue]
|
||||||
version = "0.2"
|
version = "0.2"
|
||||||
|
@ -29,10 +30,11 @@ version = "0.2"
|
||||||
objc = "0.2.3"
|
objc = "0.2.3"
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
objc = "0.2.3"
|
|
||||||
cocoa = "0.18.4"
|
cocoa = "0.18.4"
|
||||||
core-foundation = "0.6"
|
core-foundation = "0.6"
|
||||||
core-graphics = "0.17.3"
|
core-graphics = "0.17.3"
|
||||||
|
dispatch = "0.1.4"
|
||||||
|
objc = "0.2.3"
|
||||||
|
|
||||||
[target.'cfg(target_os = "windows")'.dependencies]
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
backtrace = "0.3"
|
backtrace = "0.3"
|
||||||
|
|
|
@ -70,12 +70,11 @@ fn main() {
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
{
|
{
|
||||||
if macos_use_simple_fullscreen {
|
if macos_use_simple_fullscreen {
|
||||||
use winit::os::macos::WindowExt;
|
use winit::platform::macos::WindowExtMacOS;
|
||||||
if WindowExt::set_simple_fullscreen(&window, !is_fullscreen) {
|
if WindowExtMacOS::set_simple_fullscreen(&window, !is_fullscreen) {
|
||||||
is_fullscreen = !is_fullscreen;
|
is_fullscreen = !is_fullscreen;
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
return ControlFlow::Continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
115
examples/multithreaded.rs
Normal file
115
examples/multithreaded.rs
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
extern crate env_logger;
|
||||||
|
extern crate winit;
|
||||||
|
|
||||||
|
use std::{collections::HashMap, sync::mpsc, thread, time::Duration};
|
||||||
|
|
||||||
|
use winit::{
|
||||||
|
event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent},
|
||||||
|
event_loop::{ControlFlow, EventLoop}, window::{MouseCursor, WindowBuilder},
|
||||||
|
};
|
||||||
|
|
||||||
|
const WINDOW_COUNT: usize = 3;
|
||||||
|
const WINDOW_SIZE: (u32, u32) = (600, 400);
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
env_logger::init();
|
||||||
|
let event_loop = EventLoop::new();
|
||||||
|
let mut window_senders = HashMap::with_capacity(WINDOW_COUNT);
|
||||||
|
for _ in 0..WINDOW_COUNT {
|
||||||
|
let window = WindowBuilder::new()
|
||||||
|
.with_dimensions(WINDOW_SIZE.into())
|
||||||
|
.build(&event_loop)
|
||||||
|
.unwrap();
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
window_senders.insert(window.id(), tx);
|
||||||
|
thread::spawn(move || {
|
||||||
|
while let Ok(event) = rx.recv() {
|
||||||
|
match event {
|
||||||
|
WindowEvent::KeyboardInput { input: KeyboardInput {
|
||||||
|
state: ElementState::Released,
|
||||||
|
virtual_keycode: Some(key),
|
||||||
|
modifiers,
|
||||||
|
..
|
||||||
|
}, .. } => {
|
||||||
|
window.set_title(&format!("{:?}", key));
|
||||||
|
let state = !modifiers.shift;
|
||||||
|
use self::VirtualKeyCode::*;
|
||||||
|
match key {
|
||||||
|
A => window.set_always_on_top(state),
|
||||||
|
C => window.set_cursor(match state {
|
||||||
|
true => MouseCursor::Progress,
|
||||||
|
false => MouseCursor::Default,
|
||||||
|
}),
|
||||||
|
D => window.set_decorations(!state),
|
||||||
|
F => window.set_fullscreen(match state {
|
||||||
|
true => Some(window.get_current_monitor()),
|
||||||
|
false => None,
|
||||||
|
}),
|
||||||
|
G => window.grab_cursor(state).unwrap(),
|
||||||
|
H => window.hide_cursor(state),
|
||||||
|
I => {
|
||||||
|
println!("Info:");
|
||||||
|
println!("-> position : {:?}", window.get_position());
|
||||||
|
println!("-> inner_position : {:?}", window.get_inner_position());
|
||||||
|
println!("-> outer_size : {:?}", window.get_outer_size());
|
||||||
|
println!("-> inner_size : {:?}", window.get_inner_size());
|
||||||
|
},
|
||||||
|
L => window.set_min_dimensions(match state {
|
||||||
|
true => Some(WINDOW_SIZE.into()),
|
||||||
|
false => None,
|
||||||
|
}),
|
||||||
|
M => window.set_maximized(state),
|
||||||
|
P => window.set_position({
|
||||||
|
let mut position = window.get_position().unwrap();
|
||||||
|
let sign = if state { 1.0 } else { -1.0 };
|
||||||
|
position.x += 10.0 * sign;
|
||||||
|
position.y += 10.0 * sign;
|
||||||
|
position
|
||||||
|
}),
|
||||||
|
Q => window.request_redraw(),
|
||||||
|
R => window.set_resizable(state),
|
||||||
|
S => window.set_inner_size(match state {
|
||||||
|
true => (WINDOW_SIZE.0 + 100, WINDOW_SIZE.1 + 100),
|
||||||
|
false => WINDOW_SIZE,
|
||||||
|
}.into()),
|
||||||
|
W => window.set_cursor_position((
|
||||||
|
WINDOW_SIZE.0 as i32 / 2,
|
||||||
|
WINDOW_SIZE.1 as i32 / 2,
|
||||||
|
).into()).unwrap(),
|
||||||
|
Z => {
|
||||||
|
window.hide();
|
||||||
|
thread::sleep(Duration::from_secs(1));
|
||||||
|
window.show();
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
event_loop.run(move |event, _event_loop, control_flow| {
|
||||||
|
*control_flow = match !window_senders.is_empty() {
|
||||||
|
true => ControlFlow::Wait,
|
||||||
|
false => ControlFlow::Exit,
|
||||||
|
};
|
||||||
|
match event {
|
||||||
|
Event::WindowEvent { event, window_id } => {
|
||||||
|
match event {
|
||||||
|
WindowEvent::CloseRequested
|
||||||
|
| WindowEvent::Destroyed
|
||||||
|
| WindowEvent::KeyboardInput { input: KeyboardInput {
|
||||||
|
virtual_keycode: Some(VirtualKeyCode::Escape),
|
||||||
|
.. }, .. } => {
|
||||||
|
window_senders.remove(&window_id);
|
||||||
|
},
|
||||||
|
_ => if let Some(tx) = window_senders.get(&window_id) {
|
||||||
|
tx.send(event).unwrap();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -98,6 +98,8 @@ extern crate objc;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
extern crate cocoa;
|
extern crate cocoa;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
|
extern crate dispatch;
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
extern crate core_foundation;
|
extern crate core_foundation;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
extern crate core_graphics;
|
extern crate core_graphics;
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
#![cfg(target_os = "macos")]
|
#![cfg(target_os = "macos")]
|
||||||
|
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use {LogicalSize, MonitorHandle, Window, WindowBuilder};
|
|
||||||
|
use crate::dpi::LogicalSize;
|
||||||
|
use crate::monitor::MonitorHandle;
|
||||||
|
use crate::window::{Window, WindowBuilder};
|
||||||
|
|
||||||
/// Additional methods on `Window` that are specific to MacOS.
|
/// Additional methods on `Window` that are specific to MacOS.
|
||||||
pub trait WindowExtMacOS {
|
pub trait WindowExtMacOS {
|
||||||
|
|
|
@ -112,11 +112,11 @@ pub trait EventLoopExtUnix {
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>>;
|
fn get_xlib_xconnection(&self) -> Option<Arc<XConnection>>;
|
||||||
|
|
||||||
/// Returns a pointer to the `wl_display` object of wayland that is used by this `EventsLoop`.
|
/// Returns a pointer to the `wl_display` object of wayland that is used by this `EventLoop`.
|
||||||
///
|
///
|
||||||
/// Returns `None` if the `EventsLoop` doesn't use wayland (if it uses xlib for example).
|
/// Returns `None` if the `EventLoop` doesn't use wayland (if it uses xlib for example).
|
||||||
///
|
///
|
||||||
/// The pointer will become invalid when the glutin `EventsLoop` is destroyed.
|
/// The pointer will become invalid when the glutin `EventLoop` is destroyed.
|
||||||
fn get_wayland_display(&self) -> Option<*mut raw::c_void>;
|
fn get_wayland_display(&self) -> Option<*mut raw::c_void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,3 +6,4 @@ pub use x11_dl::xinput2::*;
|
||||||
pub use x11_dl::xlib_xcb::*;
|
pub use x11_dl::xlib_xcb::*;
|
||||||
pub use x11_dl::error::OpenError;
|
pub use x11_dl::error::OpenError;
|
||||||
pub use x11_dl::xrandr::*;
|
pub use x11_dl::xrandr::*;
|
||||||
|
pub use x11_dl::xrender::*;
|
||||||
|
|
|
@ -236,6 +236,7 @@ impl<T: 'static> EventLoop<T> {
|
||||||
sticky_exit_callback(evt, &self.target, &mut control_flow, &mut callback);
|
sticky_exit_callback(evt, &self.target, &mut control_flow, &mut callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty the user event buffer
|
// Empty the user event buffer
|
||||||
{
|
{
|
||||||
let mut guard = self.pending_user_events.borrow_mut();
|
let mut guard = self.pending_user_events.borrow_mut();
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use std;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub type Cardinal = c_long;
|
pub type Cardinal = c_long;
|
||||||
|
|
|
@ -185,6 +185,14 @@ impl UnownedWindow {
|
||||||
None => ffi::CopyFromParent,
|
None => ffi::CopyFromParent,
|
||||||
},
|
},
|
||||||
ffi::InputOutput as c_uint,
|
ffi::InputOutput as c_uint,
|
||||||
|
// TODO: If window wants transparency and `visual_infos` is None,
|
||||||
|
// we need to find our own visual which has an `alphaMask` which
|
||||||
|
// is > 0, like we do in glutin.
|
||||||
|
//
|
||||||
|
// It is non obvious which masks, if any, we should pass to
|
||||||
|
// `XGetVisualInfo`. winit doesn't recieve any info about what
|
||||||
|
// properties the user wants. Users should consider choosing the
|
||||||
|
// visual themselves as glutin does.
|
||||||
match pl_attribs.visual_infos {
|
match pl_attribs.visual_infos {
|
||||||
Some(vi) => vi.visual,
|
Some(vi) => vi.visual,
|
||||||
None => ffi::CopyFromParent as *mut ffi::Visual,
|
None => ffi::CopyFromParent as *mut ffi::Visual,
|
||||||
|
|
|
@ -18,6 +18,7 @@ pub struct XConnection {
|
||||||
pub xcursor: ffi::Xcursor,
|
pub xcursor: ffi::Xcursor,
|
||||||
pub xinput2: ffi::XInput2,
|
pub xinput2: ffi::XInput2,
|
||||||
pub xlib_xcb: ffi::Xlib_xcb,
|
pub xlib_xcb: ffi::Xlib_xcb,
|
||||||
|
pub xrender: ffi::Xrender,
|
||||||
pub display: *mut ffi::Display,
|
pub display: *mut ffi::Display,
|
||||||
pub x11_fd: c_int,
|
pub x11_fd: c_int,
|
||||||
pub latest_error: Mutex<Option<XError>>,
|
pub latest_error: Mutex<Option<XError>>,
|
||||||
|
@ -37,6 +38,7 @@ impl XConnection {
|
||||||
let xrandr_1_5 = ffi::Xrandr::open().ok();
|
let xrandr_1_5 = ffi::Xrandr::open().ok();
|
||||||
let xinput2 = ffi::XInput2::open()?;
|
let xinput2 = ffi::XInput2::open()?;
|
||||||
let xlib_xcb = ffi::Xlib_xcb::open()?;
|
let xlib_xcb = ffi::Xlib_xcb::open()?;
|
||||||
|
let xrender = ffi::Xrender::open()?;
|
||||||
|
|
||||||
unsafe { (xlib.XInitThreads)() };
|
unsafe { (xlib.XInitThreads)() };
|
||||||
unsafe { (xlib.XSetErrorHandler)(error_handler) };
|
unsafe { (xlib.XSetErrorHandler)(error_handler) };
|
||||||
|
@ -62,6 +64,7 @@ impl XConnection {
|
||||||
xcursor,
|
xcursor,
|
||||||
xinput2,
|
xinput2,
|
||||||
xlib_xcb,
|
xlib_xcb,
|
||||||
|
xrender,
|
||||||
display,
|
display,
|
||||||
x11_fd: fd,
|
x11_fd: fd,
|
||||||
latest_error: Mutex::new(None),
|
latest_error: Mutex::new(None),
|
||||||
|
|
88
src/platform_impl/macos/app.rs
Normal file
88
src/platform_impl/macos/app.rs
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
use cocoa::{appkit::{self, NSEvent}, base::id};
|
||||||
|
use objc::{declare::ClassDecl, runtime::{Class, Object, Sel}};
|
||||||
|
|
||||||
|
use event::{DeviceEvent, Event};
|
||||||
|
use platform_impl::platform::{app_state::AppState, DEVICE_ID, util};
|
||||||
|
|
||||||
|
pub struct AppClass(pub *const Class);
|
||||||
|
unsafe impl Send for AppClass {}
|
||||||
|
unsafe impl Sync for AppClass {}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref APP_CLASS: AppClass = unsafe {
|
||||||
|
let superclass = class!(NSApplication);
|
||||||
|
let mut decl = ClassDecl::new("WinitApp", superclass).unwrap();
|
||||||
|
|
||||||
|
decl.add_method(
|
||||||
|
sel!(sendEvent:),
|
||||||
|
send_event as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
|
||||||
|
AppClass(decl.register())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normally, holding Cmd + any key never sends us a `keyUp` event for that key.
|
||||||
|
// Overriding `sendEvent:` like this fixes that. (https://stackoverflow.com/a/15294196)
|
||||||
|
// Fun fact: Firefox still has this bug! (https://bugzilla.mozilla.org/show_bug.cgi?id=1299553)
|
||||||
|
extern fn send_event(this: &Object, _sel: Sel, event: id) {
|
||||||
|
unsafe {
|
||||||
|
// For posterity, there are some undocumented event types
|
||||||
|
// (https://github.com/servo/cocoa-rs/issues/155)
|
||||||
|
// but that doesn't really matter here.
|
||||||
|
let event_type = event.eventType();
|
||||||
|
let modifier_flags = event.modifierFlags();
|
||||||
|
if event_type == appkit::NSKeyUp && util::has_flag(
|
||||||
|
modifier_flags,
|
||||||
|
appkit::NSEventModifierFlags::NSCommandKeyMask,
|
||||||
|
) {
|
||||||
|
let key_window: id = msg_send![this, keyWindow];
|
||||||
|
let _: () = msg_send![key_window, sendEvent:event];
|
||||||
|
} else {
|
||||||
|
maybe_dispatch_device_event(event);
|
||||||
|
let superclass = util::superclass(this);
|
||||||
|
let _: () = msg_send![super(this, superclass), sendEvent:event];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn maybe_dispatch_device_event(event: id) {
|
||||||
|
let event_type = event.eventType();
|
||||||
|
match event_type {
|
||||||
|
appkit::NSMouseMoved |
|
||||||
|
appkit::NSLeftMouseDragged |
|
||||||
|
appkit::NSOtherMouseDragged |
|
||||||
|
appkit::NSRightMouseDragged => {
|
||||||
|
let mut events = VecDeque::with_capacity(3);
|
||||||
|
|
||||||
|
let delta_x = event.deltaX() as f64;
|
||||||
|
let delta_y = event.deltaY() as f64;
|
||||||
|
|
||||||
|
if delta_x != 0.0 {
|
||||||
|
events.push_back(Event::DeviceEvent {
|
||||||
|
device_id: DEVICE_ID,
|
||||||
|
event: DeviceEvent::Motion { axis: 0, value: delta_x },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if delta_y != 0.0 {
|
||||||
|
events.push_back(Event::DeviceEvent {
|
||||||
|
device_id: DEVICE_ID,
|
||||||
|
event: DeviceEvent::Motion { axis: 1, value: delta_y },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if delta_x != 0.0 || delta_y != 0.0 {
|
||||||
|
events.push_back(Event::DeviceEvent {
|
||||||
|
device_id: DEVICE_ID,
|
||||||
|
event: DeviceEvent::MouseMotion { delta: (delta_x, delta_y) },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
AppState::queue_events(events);
|
||||||
|
},
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
101
src/platform_impl/macos/app_delegate.rs
Normal file
101
src/platform_impl/macos/app_delegate.rs
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
use cocoa::base::id;
|
||||||
|
use objc::{runtime::{Class, Object, Sel, BOOL, YES}, declare::ClassDecl};
|
||||||
|
|
||||||
|
use platform_impl::platform::app_state::AppState;
|
||||||
|
|
||||||
|
pub struct AppDelegateClass(pub *const Class);
|
||||||
|
unsafe impl Send for AppDelegateClass {}
|
||||||
|
unsafe impl Sync for AppDelegateClass {}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref APP_DELEGATE_CLASS: AppDelegateClass = unsafe {
|
||||||
|
let superclass = class!(NSResponder);
|
||||||
|
let mut decl = ClassDecl::new("WinitAppDelegate", superclass).unwrap();
|
||||||
|
|
||||||
|
decl.add_method(
|
||||||
|
sel!(applicationDidFinishLaunching:),
|
||||||
|
did_finish_launching as extern fn(&Object, Sel, 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),
|
||||||
|
);
|
||||||
|
|
||||||
|
AppDelegateClass(decl.register())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn did_finish_launching(_: &Object, _: Sel, _: id) -> BOOL {
|
||||||
|
trace!("Triggered `didFinishLaunching`");
|
||||||
|
AppState::launched();
|
||||||
|
trace!("Completed `didFinishLaunching`");
|
||||||
|
YES
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn did_become_active(_: &Object, _: Sel, _: id) {
|
||||||
|
trace!("Triggered `didBecomeActive`");
|
||||||
|
/*unsafe {
|
||||||
|
HANDLER.lock().unwrap().handle_nonuser_event(Event::Suspended(false))
|
||||||
|
}*/
|
||||||
|
trace!("Completed `didBecomeActive`");
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn will_resign_active(_: &Object, _: Sel, _: id) {
|
||||||
|
trace!("Triggered `willResignActive`");
|
||||||
|
/*unsafe {
|
||||||
|
HANDLER.lock().unwrap().handle_nonuser_event(Event::Suspended(true))
|
||||||
|
}*/
|
||||||
|
trace!("Completed `willResignActive`");
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn will_enter_foreground(_: &Object, _: Sel, _: id) {
|
||||||
|
trace!("Triggered `willEnterForeground`");
|
||||||
|
trace!("Completed `willEnterForeground`");
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn did_enter_background(_: &Object, _: Sel, _: id) {
|
||||||
|
trace!("Triggered `didEnterBackground`");
|
||||||
|
trace!("Completed `didEnterBackground`");
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn will_terminate(_: &Object, _: Sel, _: id) {
|
||||||
|
trace!("Triggered `willTerminate`");
|
||||||
|
/*unsafe {
|
||||||
|
let app: id = msg_send![class!(UIApplication), sharedApplication];
|
||||||
|
let windows: id = msg_send![app, windows];
|
||||||
|
let windows_enum: id = msg_send![windows, objectEnumerator];
|
||||||
|
let mut events = Vec::new();
|
||||||
|
loop {
|
||||||
|
let window: id = msg_send![windows_enum, nextObject];
|
||||||
|
if window == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
let is_winit_window: BOOL = msg_send![window, isKindOfClass:class!(WinitUIWindow)];
|
||||||
|
if is_winit_window == YES {
|
||||||
|
events.push(Event::WindowEvent {
|
||||||
|
window_id: RootWindowId(window.into()),
|
||||||
|
event: WindowEvent::Destroyed,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HANDLER.lock().unwrap().handle_nonuser_events(events);
|
||||||
|
HANDLER.lock().unwrap().terminated();
|
||||||
|
}*/
|
||||||
|
trace!("Completed `willTerminate`");
|
||||||
|
}
|
310
src/platform_impl/macos/app_state.rs
Normal file
310
src/platform_impl/macos/app_state.rs
Normal file
|
@ -0,0 +1,310 @@
|
||||||
|
use std::{
|
||||||
|
collections::VecDeque, fmt::{self, Debug, Formatter},
|
||||||
|
hint::unreachable_unchecked, mem,
|
||||||
|
sync::{atomic::{AtomicBool, Ordering}, Mutex, MutexGuard}, time::Instant,
|
||||||
|
};
|
||||||
|
|
||||||
|
use cocoa::{appkit::NSApp, base::nil};
|
||||||
|
|
||||||
|
use {
|
||||||
|
event::{Event, StartCause, WindowEvent},
|
||||||
|
event_loop::{ControlFlow, EventLoopWindowTarget as RootWindowTarget},
|
||||||
|
window::WindowId,
|
||||||
|
};
|
||||||
|
use platform_impl::platform::{observer::EventLoopWaker, util::Never};
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref HANDLER: Handler = Default::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Event<Never> {
|
||||||
|
fn userify<T: 'static>(self) -> Event<T> {
|
||||||
|
self.map_nonuser_event()
|
||||||
|
// `Never` can't be constructed, so the `UserEvent` variant can't
|
||||||
|
// be present here.
|
||||||
|
.unwrap_or_else(|_| unsafe { unreachable_unchecked() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait EventHandler: Debug {
|
||||||
|
fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow);
|
||||||
|
fn handle_user_events(&mut self, control_flow: &mut ControlFlow);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EventLoopHandler<F, T: 'static> {
|
||||||
|
callback: F,
|
||||||
|
will_exit: bool,
|
||||||
|
window_target: RootWindowTarget<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F, T> Debug for EventLoopHandler<F, T> {
|
||||||
|
fn fmt(&self, formatter: &mut Formatter) -> fmt::Result {
|
||||||
|
formatter.debug_struct("EventLoopHandler")
|
||||||
|
.field("window_target", &self.window_target)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F, T> EventHandler for EventLoopHandler<F, T>
|
||||||
|
where
|
||||||
|
F: 'static + FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
|
||||||
|
T: 'static,
|
||||||
|
{
|
||||||
|
fn handle_nonuser_event(&mut self, event: Event<Never>, control_flow: &mut ControlFlow) {
|
||||||
|
(self.callback)(
|
||||||
|
event.userify(),
|
||||||
|
&self.window_target,
|
||||||
|
control_flow,
|
||||||
|
);
|
||||||
|
self.will_exit |= *control_flow == ControlFlow::Exit;
|
||||||
|
if self.will_exit {
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_user_events(&mut self, control_flow: &mut ControlFlow) {
|
||||||
|
let mut will_exit = self.will_exit;
|
||||||
|
for event in self.window_target.p.receiver.try_iter() {
|
||||||
|
(self.callback)(
|
||||||
|
Event::UserEvent(event),
|
||||||
|
&self.window_target,
|
||||||
|
control_flow,
|
||||||
|
);
|
||||||
|
will_exit |= *control_flow == ControlFlow::Exit;
|
||||||
|
if will_exit {
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.will_exit = will_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Handler {
|
||||||
|
ready: AtomicBool,
|
||||||
|
in_callback: AtomicBool,
|
||||||
|
control_flow: Mutex<ControlFlow>,
|
||||||
|
control_flow_prev: Mutex<ControlFlow>,
|
||||||
|
start_time: Mutex<Option<Instant>>,
|
||||||
|
callback: Mutex<Option<Box<dyn EventHandler>>>,
|
||||||
|
pending_events: Mutex<VecDeque<Event<Never>>>,
|
||||||
|
deferred_events: Mutex<VecDeque<Event<Never>>>,
|
||||||
|
pending_redraw: Mutex<Vec<WindowId>>,
|
||||||
|
waker: Mutex<EventLoopWaker>,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for Handler {}
|
||||||
|
unsafe impl Sync for Handler {}
|
||||||
|
|
||||||
|
impl Handler {
|
||||||
|
fn events<'a>(&'a self) -> MutexGuard<'a, VecDeque<Event<Never>>> {
|
||||||
|
self.pending_events.lock().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deferred<'a>(&'a self) -> MutexGuard<'a, VecDeque<Event<Never>>> {
|
||||||
|
self.deferred_events.lock().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn redraw<'a>(&'a self) -> MutexGuard<'a, Vec<WindowId>> {
|
||||||
|
self.pending_redraw.lock().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn waker<'a>(&'a self) -> MutexGuard<'a, EventLoopWaker> {
|
||||||
|
self.waker.lock().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_ready(&self) -> bool {
|
||||||
|
self.ready.load(Ordering::Acquire)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_ready(&self) {
|
||||||
|
self.ready.store(true, Ordering::Release);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_exit(&self) -> bool {
|
||||||
|
*self.control_flow.lock().unwrap() == ControlFlow::Exit
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_control_flow_and_update_prev(&self) -> ControlFlow {
|
||||||
|
let control_flow = self.control_flow.lock().unwrap();
|
||||||
|
*self.control_flow_prev.lock().unwrap() = *control_flow;
|
||||||
|
*control_flow
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_old_and_new_control_flow(&self) -> (ControlFlow, ControlFlow) {
|
||||||
|
let old = *self.control_flow_prev.lock().unwrap();
|
||||||
|
let new = *self.control_flow.lock().unwrap();
|
||||||
|
(old, new)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_start_time(&self) -> Option<Instant> {
|
||||||
|
*self.start_time.lock().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_start_time(&self) {
|
||||||
|
*self.start_time.lock().unwrap() = Some(Instant::now());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_events(&self) -> VecDeque<Event<Never>> {
|
||||||
|
mem::replace(&mut *self.events(), Default::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_deferred(&self) -> VecDeque<Event<Never>> {
|
||||||
|
mem::replace(&mut *self.deferred(), Default::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_redraw(&self) -> Vec<WindowId> {
|
||||||
|
mem::replace(&mut *self.redraw(), Default::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_in_callback(&self) -> bool {
|
||||||
|
self.in_callback.load(Ordering::Acquire)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_in_callback(&self, in_callback: bool) {
|
||||||
|
self.in_callback.store(in_callback, Ordering::Release);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_nonuser_event(&self, event: Event<Never>) {
|
||||||
|
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
|
||||||
|
callback.handle_nonuser_event(
|
||||||
|
event,
|
||||||
|
&mut *self.control_flow.lock().unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_user_events(&self) {
|
||||||
|
if let Some(ref mut callback) = *self.callback.lock().unwrap() {
|
||||||
|
callback.handle_user_events(
|
||||||
|
&mut *self.control_flow.lock().unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum AppState {}
|
||||||
|
|
||||||
|
impl AppState {
|
||||||
|
pub fn set_callback<F, T>(callback: F, window_target: RootWindowTarget<T>)
|
||||||
|
where
|
||||||
|
F: 'static + FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
|
||||||
|
T: 'static,
|
||||||
|
{
|
||||||
|
*HANDLER.callback.lock().unwrap() = Some(Box::new(EventLoopHandler {
|
||||||
|
callback,
|
||||||
|
will_exit: false,
|
||||||
|
window_target,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit() {
|
||||||
|
HANDLER.set_in_callback(true);
|
||||||
|
HANDLER.handle_nonuser_event(Event::LoopDestroyed);
|
||||||
|
HANDLER.set_in_callback(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn launched() {
|
||||||
|
HANDLER.set_ready();
|
||||||
|
HANDLER.waker().start();
|
||||||
|
HANDLER.set_in_callback(true);
|
||||||
|
HANDLER.handle_nonuser_event(Event::NewEvents(StartCause::Init));
|
||||||
|
HANDLER.set_in_callback(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wakeup() {
|
||||||
|
if !HANDLER.is_ready() { return }
|
||||||
|
let start = HANDLER.get_start_time().unwrap();
|
||||||
|
let cause = match HANDLER.get_control_flow_and_update_prev() {
|
||||||
|
ControlFlow::Poll => StartCause::Poll,
|
||||||
|
ControlFlow::Wait => StartCause::WaitCancelled {
|
||||||
|
start,
|
||||||
|
requested_resume: None,
|
||||||
|
},
|
||||||
|
ControlFlow::WaitUntil(requested_resume) => {
|
||||||
|
if Instant::now() >= requested_resume {
|
||||||
|
StartCause::ResumeTimeReached {
|
||||||
|
start,
|
||||||
|
requested_resume,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
StartCause::WaitCancelled {
|
||||||
|
start,
|
||||||
|
requested_resume: Some(requested_resume),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ControlFlow::Exit => StartCause::Poll,//panic!("unexpected `ControlFlow::Exit`"),
|
||||||
|
};
|
||||||
|
HANDLER.set_in_callback(true);
|
||||||
|
HANDLER.handle_nonuser_event(Event::NewEvents(cause));
|
||||||
|
HANDLER.set_in_callback(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called from multiple threads at present
|
||||||
|
pub fn queue_redraw(window_id: WindowId) {
|
||||||
|
let mut pending_redraw = HANDLER.redraw();
|
||||||
|
if !pending_redraw.contains(&window_id) {
|
||||||
|
pending_redraw.push(window_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn queue_event(event: Event<Never>) {
|
||||||
|
if !unsafe { msg_send![class!(NSThread), isMainThread] } {
|
||||||
|
panic!("Event queued from different thread: {:#?}", event);
|
||||||
|
}
|
||||||
|
HANDLER.events().push_back(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn queue_events(mut events: VecDeque<Event<Never>>) {
|
||||||
|
if !unsafe { msg_send![class!(NSThread), isMainThread] } {
|
||||||
|
panic!("Events queued from different thread: {:#?}", events);
|
||||||
|
}
|
||||||
|
HANDLER.events().append(&mut events);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_event_immediately(event: Event<Never>) {
|
||||||
|
if !unsafe { msg_send![class!(NSThread), isMainThread] } {
|
||||||
|
panic!("Event sent from different thread: {:#?}", event);
|
||||||
|
}
|
||||||
|
HANDLER.deferred().push_back(event);
|
||||||
|
if !HANDLER.get_in_callback() {
|
||||||
|
HANDLER.set_in_callback(true);
|
||||||
|
for event in HANDLER.take_deferred() {
|
||||||
|
HANDLER.handle_nonuser_event(event);
|
||||||
|
}
|
||||||
|
HANDLER.set_in_callback(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cleared() {
|
||||||
|
if !HANDLER.is_ready() { return }
|
||||||
|
if !HANDLER.get_in_callback() {
|
||||||
|
HANDLER.set_in_callback(true);
|
||||||
|
HANDLER.handle_user_events();
|
||||||
|
for event in HANDLER.take_events() {
|
||||||
|
HANDLER.handle_nonuser_event(event);
|
||||||
|
}
|
||||||
|
for window_id in HANDLER.should_redraw() {
|
||||||
|
HANDLER.handle_nonuser_event(Event::WindowEvent {
|
||||||
|
window_id,
|
||||||
|
event: WindowEvent::RedrawRequested,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
HANDLER.handle_nonuser_event(Event::EventsCleared);
|
||||||
|
HANDLER.set_in_callback(false);
|
||||||
|
}
|
||||||
|
if HANDLER.should_exit() {
|
||||||
|
let _: () = unsafe { msg_send![NSApp(), stop:nil] };
|
||||||
|
return
|
||||||
|
}
|
||||||
|
HANDLER.update_start_time();
|
||||||
|
match HANDLER.get_old_and_new_control_flow() {
|
||||||
|
(ControlFlow::Exit, _) | (_, ControlFlow::Exit) => unreachable!(),
|
||||||
|
(old, new) if old == new => (),
|
||||||
|
(_, ControlFlow::Wait) => HANDLER.waker().stop(),
|
||||||
|
(_, ControlFlow::WaitUntil(instant)) => HANDLER.waker().start_at(instant),
|
||||||
|
(_, ControlFlow::Poll) => HANDLER.waker().start(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
273
src/platform_impl/macos/event.rs
Normal file
273
src/platform_impl/macos/event.rs
Normal file
|
@ -0,0 +1,273 @@
|
||||||
|
use std::os::raw::c_ushort;
|
||||||
|
|
||||||
|
use cocoa::{appkit::{NSEvent, NSEventModifierFlags}, base::id};
|
||||||
|
|
||||||
|
use event::{
|
||||||
|
ElementState, KeyboardInput,
|
||||||
|
ModifiersState, VirtualKeyCode, WindowEvent,
|
||||||
|
};
|
||||||
|
use platform_impl::platform::DEVICE_ID;
|
||||||
|
|
||||||
|
pub fn char_to_keycode(c: char) -> Option<VirtualKeyCode> {
|
||||||
|
// We only translate keys that are affected by keyboard layout.
|
||||||
|
//
|
||||||
|
// Note that since keys are translated in a somewhat "dumb" way (reading character)
|
||||||
|
// there is a concern that some combination, i.e. Cmd+char, causes the wrong
|
||||||
|
// letter to be received, and so we receive the wrong key.
|
||||||
|
//
|
||||||
|
// Implementation reference: https://github.com/WebKit/webkit/blob/82bae82cf0f329dbe21059ef0986c4e92fea4ba6/Source/WebCore/platform/cocoa/KeyEventCocoa.mm#L626
|
||||||
|
Some(match c {
|
||||||
|
'a' | 'A' => VirtualKeyCode::A,
|
||||||
|
'b' | 'B' => VirtualKeyCode::B,
|
||||||
|
'c' | 'C' => VirtualKeyCode::C,
|
||||||
|
'd' | 'D' => VirtualKeyCode::D,
|
||||||
|
'e' | 'E' => VirtualKeyCode::E,
|
||||||
|
'f' | 'F' => VirtualKeyCode::F,
|
||||||
|
'g' | 'G' => VirtualKeyCode::G,
|
||||||
|
'h' | 'H' => VirtualKeyCode::H,
|
||||||
|
'i' | 'I' => VirtualKeyCode::I,
|
||||||
|
'j' | 'J' => VirtualKeyCode::J,
|
||||||
|
'k' | 'K' => VirtualKeyCode::K,
|
||||||
|
'l' | 'L' => VirtualKeyCode::L,
|
||||||
|
'm' | 'M' => VirtualKeyCode::M,
|
||||||
|
'n' | 'N' => VirtualKeyCode::N,
|
||||||
|
'o' | 'O' => VirtualKeyCode::O,
|
||||||
|
'p' | 'P' => VirtualKeyCode::P,
|
||||||
|
'q' | 'Q' => VirtualKeyCode::Q,
|
||||||
|
'r' | 'R' => VirtualKeyCode::R,
|
||||||
|
's' | 'S' => VirtualKeyCode::S,
|
||||||
|
't' | 'T' => VirtualKeyCode::T,
|
||||||
|
'u' | 'U' => VirtualKeyCode::U,
|
||||||
|
'v' | 'V' => VirtualKeyCode::V,
|
||||||
|
'w' | 'W' => VirtualKeyCode::W,
|
||||||
|
'x' | 'X' => VirtualKeyCode::X,
|
||||||
|
'y' | 'Y' => VirtualKeyCode::Y,
|
||||||
|
'z' | 'Z' => VirtualKeyCode::Z,
|
||||||
|
'1' | '!' => VirtualKeyCode::Key1,
|
||||||
|
'2' | '@' => VirtualKeyCode::Key2,
|
||||||
|
'3' | '#' => VirtualKeyCode::Key3,
|
||||||
|
'4' | '$' => VirtualKeyCode::Key4,
|
||||||
|
'5' | '%' => VirtualKeyCode::Key5,
|
||||||
|
'6' | '^' => VirtualKeyCode::Key6,
|
||||||
|
'7' | '&' => VirtualKeyCode::Key7,
|
||||||
|
'8' | '*' => VirtualKeyCode::Key8,
|
||||||
|
'9' | '(' => VirtualKeyCode::Key9,
|
||||||
|
'0' | ')' => VirtualKeyCode::Key0,
|
||||||
|
'=' | '+' => VirtualKeyCode::Equals,
|
||||||
|
'-' | '_' => VirtualKeyCode::Minus,
|
||||||
|
']' | '}' => VirtualKeyCode::RBracket,
|
||||||
|
'[' | '{' => VirtualKeyCode::LBracket,
|
||||||
|
'\''| '"' => VirtualKeyCode::Apostrophe,
|
||||||
|
';' | ':' => VirtualKeyCode::Semicolon,
|
||||||
|
'\\'| '|' => VirtualKeyCode::Backslash,
|
||||||
|
',' | '<' => VirtualKeyCode::Comma,
|
||||||
|
'/' | '?' => VirtualKeyCode::Slash,
|
||||||
|
'.' | '>' => VirtualKeyCode::Period,
|
||||||
|
'`' | '~' => VirtualKeyCode::Grave,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scancode_to_keycode(scancode: c_ushort) -> Option<VirtualKeyCode> {
|
||||||
|
Some(match scancode {
|
||||||
|
0x00 => VirtualKeyCode::A,
|
||||||
|
0x01 => VirtualKeyCode::S,
|
||||||
|
0x02 => VirtualKeyCode::D,
|
||||||
|
0x03 => VirtualKeyCode::F,
|
||||||
|
0x04 => VirtualKeyCode::H,
|
||||||
|
0x05 => VirtualKeyCode::G,
|
||||||
|
0x06 => VirtualKeyCode::Z,
|
||||||
|
0x07 => VirtualKeyCode::X,
|
||||||
|
0x08 => VirtualKeyCode::C,
|
||||||
|
0x09 => VirtualKeyCode::V,
|
||||||
|
//0x0a => World 1,
|
||||||
|
0x0b => VirtualKeyCode::B,
|
||||||
|
0x0c => VirtualKeyCode::Q,
|
||||||
|
0x0d => VirtualKeyCode::W,
|
||||||
|
0x0e => VirtualKeyCode::E,
|
||||||
|
0x0f => VirtualKeyCode::R,
|
||||||
|
0x10 => VirtualKeyCode::Y,
|
||||||
|
0x11 => VirtualKeyCode::T,
|
||||||
|
0x12 => VirtualKeyCode::Key1,
|
||||||
|
0x13 => VirtualKeyCode::Key2,
|
||||||
|
0x14 => VirtualKeyCode::Key3,
|
||||||
|
0x15 => VirtualKeyCode::Key4,
|
||||||
|
0x16 => VirtualKeyCode::Key6,
|
||||||
|
0x17 => VirtualKeyCode::Key5,
|
||||||
|
0x18 => VirtualKeyCode::Equals,
|
||||||
|
0x19 => VirtualKeyCode::Key9,
|
||||||
|
0x1a => VirtualKeyCode::Key7,
|
||||||
|
0x1b => VirtualKeyCode::Minus,
|
||||||
|
0x1c => VirtualKeyCode::Key8,
|
||||||
|
0x1d => VirtualKeyCode::Key0,
|
||||||
|
0x1e => VirtualKeyCode::RBracket,
|
||||||
|
0x1f => VirtualKeyCode::O,
|
||||||
|
0x20 => VirtualKeyCode::U,
|
||||||
|
0x21 => VirtualKeyCode::LBracket,
|
||||||
|
0x22 => VirtualKeyCode::I,
|
||||||
|
0x23 => VirtualKeyCode::P,
|
||||||
|
0x24 => VirtualKeyCode::Return,
|
||||||
|
0x25 => VirtualKeyCode::L,
|
||||||
|
0x26 => VirtualKeyCode::J,
|
||||||
|
0x27 => VirtualKeyCode::Apostrophe,
|
||||||
|
0x28 => VirtualKeyCode::K,
|
||||||
|
0x29 => VirtualKeyCode::Semicolon,
|
||||||
|
0x2a => VirtualKeyCode::Backslash,
|
||||||
|
0x2b => VirtualKeyCode::Comma,
|
||||||
|
0x2c => VirtualKeyCode::Slash,
|
||||||
|
0x2d => VirtualKeyCode::N,
|
||||||
|
0x2e => VirtualKeyCode::M,
|
||||||
|
0x2f => VirtualKeyCode::Period,
|
||||||
|
0x30 => VirtualKeyCode::Tab,
|
||||||
|
0x31 => VirtualKeyCode::Space,
|
||||||
|
0x32 => VirtualKeyCode::Grave,
|
||||||
|
0x33 => VirtualKeyCode::Back,
|
||||||
|
//0x34 => unkown,
|
||||||
|
0x35 => VirtualKeyCode::Escape,
|
||||||
|
0x36 => VirtualKeyCode::RWin,
|
||||||
|
0x37 => VirtualKeyCode::LWin,
|
||||||
|
0x38 => VirtualKeyCode::LShift,
|
||||||
|
//0x39 => Caps lock,
|
||||||
|
0x3a => VirtualKeyCode::LAlt,
|
||||||
|
0x3b => VirtualKeyCode::LControl,
|
||||||
|
0x3c => VirtualKeyCode::RShift,
|
||||||
|
0x3d => VirtualKeyCode::RAlt,
|
||||||
|
0x3e => VirtualKeyCode::RControl,
|
||||||
|
//0x3f => Fn key,
|
||||||
|
0x40 => VirtualKeyCode::F17,
|
||||||
|
0x41 => VirtualKeyCode::Decimal,
|
||||||
|
//0x42 -> unkown,
|
||||||
|
0x43 => VirtualKeyCode::Multiply,
|
||||||
|
//0x44 => unkown,
|
||||||
|
0x45 => VirtualKeyCode::Add,
|
||||||
|
//0x46 => unkown,
|
||||||
|
0x47 => VirtualKeyCode::Numlock,
|
||||||
|
//0x48 => KeypadClear,
|
||||||
|
0x49 => VirtualKeyCode::VolumeUp,
|
||||||
|
0x4a => VirtualKeyCode::VolumeDown,
|
||||||
|
0x4b => VirtualKeyCode::Divide,
|
||||||
|
0x4c => VirtualKeyCode::NumpadEnter,
|
||||||
|
//0x4d => unkown,
|
||||||
|
0x4e => VirtualKeyCode::Subtract,
|
||||||
|
0x4f => VirtualKeyCode::F18,
|
||||||
|
0x50 => VirtualKeyCode::F19,
|
||||||
|
0x51 => VirtualKeyCode::NumpadEquals,
|
||||||
|
0x52 => VirtualKeyCode::Numpad0,
|
||||||
|
0x53 => VirtualKeyCode::Numpad1,
|
||||||
|
0x54 => VirtualKeyCode::Numpad2,
|
||||||
|
0x55 => VirtualKeyCode::Numpad3,
|
||||||
|
0x56 => VirtualKeyCode::Numpad4,
|
||||||
|
0x57 => VirtualKeyCode::Numpad5,
|
||||||
|
0x58 => VirtualKeyCode::Numpad6,
|
||||||
|
0x59 => VirtualKeyCode::Numpad7,
|
||||||
|
0x5a => VirtualKeyCode::F20,
|
||||||
|
0x5b => VirtualKeyCode::Numpad8,
|
||||||
|
0x5c => VirtualKeyCode::Numpad9,
|
||||||
|
0x5d => VirtualKeyCode::Yen,
|
||||||
|
//0x5e => JIS Ro,
|
||||||
|
//0x5f => unkown,
|
||||||
|
0x60 => VirtualKeyCode::F5,
|
||||||
|
0x61 => VirtualKeyCode::F6,
|
||||||
|
0x62 => VirtualKeyCode::F7,
|
||||||
|
0x63 => VirtualKeyCode::F3,
|
||||||
|
0x64 => VirtualKeyCode::F8,
|
||||||
|
0x65 => VirtualKeyCode::F9,
|
||||||
|
//0x66 => JIS Eisuu (macOS),
|
||||||
|
0x67 => VirtualKeyCode::F11,
|
||||||
|
//0x68 => JIS Kanna (macOS),
|
||||||
|
0x69 => VirtualKeyCode::F13,
|
||||||
|
0x6a => VirtualKeyCode::F16,
|
||||||
|
0x6b => VirtualKeyCode::F14,
|
||||||
|
//0x6c => unkown,
|
||||||
|
0x6d => VirtualKeyCode::F10,
|
||||||
|
//0x6e => unkown,
|
||||||
|
0x6f => VirtualKeyCode::F12,
|
||||||
|
//0x70 => unkown,
|
||||||
|
0x71 => VirtualKeyCode::F15,
|
||||||
|
0x72 => VirtualKeyCode::Insert,
|
||||||
|
0x73 => VirtualKeyCode::Home,
|
||||||
|
0x74 => VirtualKeyCode::PageUp,
|
||||||
|
0x75 => VirtualKeyCode::Delete,
|
||||||
|
0x76 => VirtualKeyCode::F4,
|
||||||
|
0x77 => VirtualKeyCode::End,
|
||||||
|
0x78 => VirtualKeyCode::F2,
|
||||||
|
0x79 => VirtualKeyCode::PageDown,
|
||||||
|
0x7a => VirtualKeyCode::F1,
|
||||||
|
0x7b => VirtualKeyCode::Left,
|
||||||
|
0x7c => VirtualKeyCode::Right,
|
||||||
|
0x7d => VirtualKeyCode::Down,
|
||||||
|
0x7e => VirtualKeyCode::Up,
|
||||||
|
//0x7f => unkown,
|
||||||
|
|
||||||
|
0xa => VirtualKeyCode::Caret,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// While F1-F20 have scancodes we can match on, we have to check against UTF-16
|
||||||
|
// constants for the rest.
|
||||||
|
// https://developer.apple.com/documentation/appkit/1535851-function-key_unicodes?preferredLanguage=occ
|
||||||
|
pub fn check_function_keys(string: &String) -> Option<VirtualKeyCode> {
|
||||||
|
if let Some(ch) = string.encode_utf16().next() {
|
||||||
|
return Some(match ch {
|
||||||
|
0xf718 => VirtualKeyCode::F21,
|
||||||
|
0xf719 => VirtualKeyCode::F22,
|
||||||
|
0xf71a => VirtualKeyCode::F23,
|
||||||
|
0xf71b => VirtualKeyCode::F24,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn event_mods(event: id) -> ModifiersState {
|
||||||
|
let flags = unsafe {
|
||||||
|
NSEvent::modifierFlags(event)
|
||||||
|
};
|
||||||
|
ModifiersState {
|
||||||
|
shift: flags.contains(NSEventModifierFlags::NSShiftKeyMask),
|
||||||
|
ctrl: flags.contains(NSEventModifierFlags::NSControlKeyMask),
|
||||||
|
alt: flags.contains(NSEventModifierFlags::NSAlternateKeyMask),
|
||||||
|
logo: flags.contains(NSEventModifierFlags::NSCommandKeyMask),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_scancode(event: cocoa::base::id) -> c_ushort {
|
||||||
|
// In AppKit, `keyCode` refers to the position (scancode) of a key rather than its character,
|
||||||
|
// and there is no easy way to navtively retrieve the layout-dependent character.
|
||||||
|
// In winit, we use keycode to refer to the key's character, and so this function aligns
|
||||||
|
// AppKit's terminology with ours.
|
||||||
|
unsafe {
|
||||||
|
msg_send![event, keyCode]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn modifier_event(
|
||||||
|
ns_event: id,
|
||||||
|
keymask: NSEventModifierFlags,
|
||||||
|
was_key_pressed: bool,
|
||||||
|
) -> Option<WindowEvent> {
|
||||||
|
if !was_key_pressed && NSEvent::modifierFlags(ns_event).contains(keymask)
|
||||||
|
|| was_key_pressed && !NSEvent::modifierFlags(ns_event).contains(keymask) {
|
||||||
|
let state = if was_key_pressed {
|
||||||
|
ElementState::Released
|
||||||
|
} else {
|
||||||
|
ElementState::Pressed
|
||||||
|
};
|
||||||
|
|
||||||
|
let scancode = get_scancode(ns_event);
|
||||||
|
let virtual_keycode = scancode_to_keycode(scancode);
|
||||||
|
Some(WindowEvent::KeyboardInput {
|
||||||
|
device_id: DEVICE_ID,
|
||||||
|
input: KeyboardInput {
|
||||||
|
state,
|
||||||
|
scancode: scancode as _,
|
||||||
|
virtual_keycode,
|
||||||
|
modifiers: event_mods(ns_event),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,812 +1,144 @@
|
||||||
use {ControlFlow, EventLoopClosed};
|
use std::{
|
||||||
use cocoa::{self, appkit, foundation};
|
collections::VecDeque, mem, os::raw::c_void, process, ptr, sync::mpsc, marker::PhantomData
|
||||||
use cocoa::appkit::{NSApplication, NSEvent, NSEventMask, NSEventModifierFlags, NSEventPhase, NSView, NSWindow};
|
};
|
||||||
use events::{self, ElementState, Event, TouchPhase, WindowEvent, DeviceEvent, ModifiersState, KeyboardInput};
|
|
||||||
use std::collections::VecDeque;
|
|
||||||
use std::sync::{Arc, Mutex, Weak};
|
|
||||||
use super::window::Window2;
|
|
||||||
use std;
|
|
||||||
use std::os::raw::*;
|
|
||||||
use super::DeviceId;
|
|
||||||
|
|
||||||
pub struct EventLoop {
|
use cocoa::{appkit::NSApp, base::{id, nil}, foundation::NSAutoreleasePool};
|
||||||
modifiers: Modifiers,
|
|
||||||
pub shared: Arc<Shared>,
|
use {
|
||||||
|
event::Event,
|
||||||
|
event_loop::{ControlFlow, EventLoopClosed, EventLoopWindowTarget as RootWindowTarget},
|
||||||
|
};
|
||||||
|
use platform_impl::platform::{
|
||||||
|
app::APP_CLASS, app_delegate::APP_DELEGATE_CLASS,
|
||||||
|
app_state::AppState, monitor::{self, MonitorHandle},
|
||||||
|
observer::*, util::IdRef,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct EventLoopWindowTarget<T: 'static> {
|
||||||
|
pub sender: mpsc::Sender<T>, // this is only here to be cloned elsewhere
|
||||||
|
pub receiver: mpsc::Receiver<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// State shared between the `EventLoop` and its registered windows.
|
impl<T> Default for EventLoopWindowTarget<T> {
|
||||||
pub struct Shared {
|
fn default() -> Self {
|
||||||
pub windows: Mutex<Vec<Weak<Window2>>>,
|
let (sender, receiver) = mpsc::channel();
|
||||||
pub pending_events: Mutex<VecDeque<Event>>,
|
EventLoopWindowTarget { sender, receiver }
|
||||||
// The user event callback given via either of the `poll_events` or `run_forever` methods.
|
}
|
||||||
//
|
}
|
||||||
// We store the user's callback here so that it may be accessed by each of the window delegate
|
|
||||||
// callbacks (e.g. resize, close, etc) for the duration of a call to either of the
|
pub struct EventLoop<T: 'static> {
|
||||||
// `poll_events` or `run_forever` methods.
|
window_target: RootWindowTarget<T>,
|
||||||
//
|
_delegate: IdRef,
|
||||||
// This is *only* `Some` for the duration of a call to either of these methods and will be
|
}
|
||||||
// `None` otherwise.
|
|
||||||
user_callback: UserCallback,
|
impl<T> EventLoop<T> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let delegate = unsafe {
|
||||||
|
if !msg_send![class!(NSThread), isMainThread] {
|
||||||
|
panic!("On macOS, `EventLoop` must be created on the main thread!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This must be done before `NSApp()` (equivalent to sending
|
||||||
|
// `sharedApplication`) is called anywhere else, or we'll end up
|
||||||
|
// with the wrong `NSApplication` class and the wrong thread could
|
||||||
|
// be marked as main.
|
||||||
|
let app: id = msg_send![APP_CLASS.0, sharedApplication];
|
||||||
|
|
||||||
|
let delegate = IdRef::new(msg_send![APP_DELEGATE_CLASS.0, new]);
|
||||||
|
let pool = NSAutoreleasePool::new(nil);
|
||||||
|
let _: () = msg_send![app, setDelegate:*delegate];
|
||||||
|
let _: () = msg_send![pool, drain];
|
||||||
|
delegate
|
||||||
|
};
|
||||||
|
setup_control_flow_observers();
|
||||||
|
EventLoop {
|
||||||
|
window_target: RootWindowTarget {
|
||||||
|
p: Default::default(),
|
||||||
|
_marker: PhantomData,
|
||||||
|
},
|
||||||
|
_delegate: delegate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_available_monitors(&self) -> VecDeque<MonitorHandle> {
|
||||||
|
monitor::get_available_monitors()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get_primary_monitor(&self) -> MonitorHandle {
|
||||||
|
monitor::get_primary_monitor()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn window_target(&self) -> &RootWindowTarget<T> {
|
||||||
|
&self.window_target
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run<F>(self, callback: F) -> !
|
||||||
|
where F: 'static + FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
let _pool = NSAutoreleasePool::new(nil);
|
||||||
|
let app = NSApp();
|
||||||
|
assert_ne!(app, nil);
|
||||||
|
AppState::set_callback(callback, self.window_target);
|
||||||
|
let _: () = msg_send![app, run];
|
||||||
|
AppState::exit();
|
||||||
|
process::exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_return<F>(&mut self, _callback: F)
|
||||||
|
where F: FnMut(Event<T>, &RootWindowTarget<T>, &mut ControlFlow),
|
||||||
|
{
|
||||||
|
unimplemented!();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_proxy(&self) -> Proxy<T> {
|
||||||
|
Proxy::new(self.window_target.p.sender.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Proxy {}
|
pub struct Proxy<T> {
|
||||||
|
sender: mpsc::Sender<T>,
|
||||||
struct Modifiers {
|
source: CFRunLoopSourceRef,
|
||||||
shift_pressed: bool,
|
|
||||||
ctrl_pressed: bool,
|
|
||||||
win_pressed: bool,
|
|
||||||
alt_pressed: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapping the user callback in a type allows us to:
|
unsafe impl<T> Send for Proxy<T> {}
|
||||||
//
|
unsafe impl<T> Sync for Proxy<T> {}
|
||||||
// - ensure the callback pointer is never accidentally cloned
|
|
||||||
// - ensure that only the `EventLoop` can `store` and `drop` the callback pointer
|
|
||||||
// - Share access to the user callback with the NSWindow callbacks.
|
|
||||||
pub struct UserCallback {
|
|
||||||
mutex: Mutex<Option<*mut FnMut(Event)>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
impl<T> Proxy<T> {
|
||||||
impl Shared {
|
fn new(sender: mpsc::Sender<T>) -> Self {
|
||||||
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Shared {
|
|
||||||
windows: Mutex::new(Vec::new()),
|
|
||||||
pending_events: Mutex::new(VecDeque::new()),
|
|
||||||
user_callback: UserCallback { mutex: Mutex::new(None) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call_user_callback_with_pending_events(&self) {
|
|
||||||
loop {
|
|
||||||
let event = match self.pending_events.lock().unwrap().pop_front() {
|
|
||||||
Some(event) => event,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
self.user_callback.call_with_event(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calls the user callback if one exists.
|
|
||||||
//
|
|
||||||
// Otherwise, stores the event in the `pending_events` queue.
|
|
||||||
//
|
|
||||||
// This is necessary for the case when `WindowDelegate` callbacks are triggered during a call
|
|
||||||
// to the user's callback.
|
|
||||||
pub fn call_user_callback_with_event_or_store_in_pending(&self, event: Event) {
|
|
||||||
if self.user_callback.mutex.lock().unwrap().is_some() {
|
|
||||||
unsafe {
|
|
||||||
self.user_callback.call_with_event(event);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.pending_events.lock().unwrap().push_back(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removes the window with the given `Id` from the `windows` list.
|
|
||||||
//
|
|
||||||
// This is called in response to `windowWillClose`.
|
|
||||||
pub fn find_and_remove_window(&self, id: super::window::Id) {
|
|
||||||
if let Ok(mut windows) = self.windows.lock() {
|
|
||||||
windows.retain(|w| match w.upgrade() {
|
|
||||||
Some(w) => w.id() != id,
|
|
||||||
None => false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl Modifiers {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Modifiers {
|
|
||||||
shift_pressed: false,
|
|
||||||
ctrl_pressed: false,
|
|
||||||
win_pressed: false,
|
|
||||||
alt_pressed: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl UserCallback {
|
|
||||||
|
|
||||||
// Here we store user's `callback` behind the mutex so that they may be safely shared between
|
|
||||||
// each of the window delegates.
|
|
||||||
//
|
|
||||||
// In order to make sure that the pointer is always valid, we must manually guarantee that it
|
|
||||||
// is dropped before the callback itself is dropped. Thus, this should *only* be called at the
|
|
||||||
// beginning of a call to `poll_events` and `run_forever`, both of which *must* drop the
|
|
||||||
// callback at the end of their scope using the `drop` method.
|
|
||||||
fn store<F>(&self, callback: &mut F)
|
|
||||||
where F: FnMut(Event)
|
|
||||||
{
|
|
||||||
let trait_object = callback as &mut FnMut(Event);
|
|
||||||
let trait_object_ptr = trait_object as *const FnMut(Event) as *mut FnMut(Event);
|
|
||||||
*self.mutex.lock().unwrap() = Some(trait_object_ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emits the given event via the user-given callback.
|
|
||||||
//
|
|
||||||
// This is unsafe as it requires dereferencing the pointer to the user-given callback. We
|
|
||||||
// guarantee this is safe by ensuring the `UserCallback` never lives longer than the user-given
|
|
||||||
// callback.
|
|
||||||
//
|
|
||||||
// Note that the callback may not always be `Some`. This is because some `NSWindowDelegate`
|
|
||||||
// callbacks can be triggered by means other than `NSApp().sendEvent`. For example, if a window
|
|
||||||
// is destroyed or created during a call to the user's callback, the `WindowDelegate` methods
|
|
||||||
// may be called with `windowShouldClose` or `windowDidResignKey`.
|
|
||||||
unsafe fn call_with_event(&self, event: Event) {
|
|
||||||
let callback = match self.mutex.lock().unwrap().take() {
|
|
||||||
Some(callback) => callback,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
(*callback)(event);
|
|
||||||
*self.mutex.lock().unwrap() = Some(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used to drop the user callback pointer at the end of the `poll_events` and `run_forever`
|
|
||||||
// methods. This is done to enforce our guarantee that the top callback will never live longer
|
|
||||||
// than the call to either `poll_events` or `run_forever` to which it was given.
|
|
||||||
fn drop(&self) {
|
|
||||||
self.mutex.lock().unwrap().take();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl EventLoop {
|
|
||||||
|
|
||||||
pub fn new() -> Self {
|
|
||||||
// Mark this thread as the main thread of the Cocoa event system.
|
|
||||||
//
|
|
||||||
// This must be done before any worker threads get a chance to call it
|
|
||||||
// (e.g., via `EventLoopProxy::wakeup()`), causing a wrong thread to be
|
|
||||||
// marked as the main thread.
|
|
||||||
unsafe { appkit::NSApp(); }
|
|
||||||
|
|
||||||
EventLoop {
|
|
||||||
shared: Arc::new(Shared::new()),
|
|
||||||
modifiers: Modifiers::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn poll_events<F>(&mut self, mut callback: F)
|
|
||||||
where F: FnMut(Event),
|
|
||||||
{
|
|
||||||
unsafe {
|
unsafe {
|
||||||
if !msg_send![class!(NSThread), isMainThread] {
|
// just wakeup the eventloop
|
||||||
panic!("Events can only be polled from the main thread on macOS");
|
extern "C" fn event_loop_proxy_handler(_: *mut c_void) {}
|
||||||
}
|
|
||||||
|
// adding a Source to the main CFRunLoop lets us wake it up and
|
||||||
|
// process user events through the normal OS EventLoop mechanisms.
|
||||||
|
let rl = CFRunLoopGetMain();
|
||||||
|
let mut context: CFRunLoopSourceContext = mem::zeroed();
|
||||||
|
context.perform = event_loop_proxy_handler;
|
||||||
|
let source = CFRunLoopSourceCreate(
|
||||||
|
ptr::null_mut(),
|
||||||
|
CFIndex::max_value() - 1,
|
||||||
|
&mut context,
|
||||||
|
);
|
||||||
|
CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
|
||||||
|
CFRunLoopWakeUp(rl);
|
||||||
|
|
||||||
|
Proxy { sender, source }
|
||||||
}
|
}
|
||||||
|
|
||||||
self.shared.user_callback.store(&mut callback);
|
|
||||||
|
|
||||||
// Loop as long as we have pending events to return.
|
|
||||||
loop {
|
|
||||||
unsafe {
|
|
||||||
// First, yield all pending events.
|
|
||||||
self.shared.call_user_callback_with_pending_events();
|
|
||||||
|
|
||||||
let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil);
|
|
||||||
|
|
||||||
// Poll for the next event, returning `nil` if there are none.
|
|
||||||
let ns_event = appkit::NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_(
|
|
||||||
NSEventMask::NSAnyEventMask.bits() | NSEventMask::NSEventMaskPressure.bits(),
|
|
||||||
foundation::NSDate::distantPast(cocoa::base::nil),
|
|
||||||
foundation::NSDefaultRunLoopMode,
|
|
||||||
cocoa::base::YES);
|
|
||||||
|
|
||||||
let event = self.ns_event_to_event(ns_event);
|
|
||||||
|
|
||||||
let _: () = msg_send![pool, release];
|
|
||||||
|
|
||||||
match event {
|
|
||||||
// Call the user's callback.
|
|
||||||
Some(event) => self.shared.user_callback.call_with_event(event),
|
|
||||||
None => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.shared.user_callback.drop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_forever<F>(&mut self, mut callback: F)
|
pub fn send_event(&self, event: T) -> Result<(), EventLoopClosed> {
|
||||||
where F: FnMut(Event) -> ControlFlow
|
self.sender.send(event).map_err(|_| EventLoopClosed)?;
|
||||||
{
|
|
||||||
unsafe {
|
unsafe {
|
||||||
if !msg_send![class!(NSThread), isMainThread] {
|
// let the main thread know there's a new event
|
||||||
panic!("Events can only be polled from the main thread on macOS");
|
CFRunLoopSourceSignal(self.source);
|
||||||
}
|
let rl = CFRunLoopGetMain();
|
||||||
}
|
CFRunLoopWakeUp(rl);
|
||||||
|
|
||||||
// Track whether or not control flow has changed.
|
|
||||||
let control_flow = std::cell::Cell::new(ControlFlow::Continue);
|
|
||||||
|
|
||||||
let mut callback = |event| {
|
|
||||||
if let ControlFlow::Break = callback(event) {
|
|
||||||
control_flow.set(ControlFlow::Break);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.shared.user_callback.store(&mut callback);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
unsafe {
|
|
||||||
// First, yield all pending events.
|
|
||||||
self.shared.call_user_callback_with_pending_events();
|
|
||||||
if let ControlFlow::Break = control_flow.get() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil);
|
|
||||||
|
|
||||||
// Wait for the next event. Note that this function blocks during resize.
|
|
||||||
let ns_event = appkit::NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_(
|
|
||||||
NSEventMask::NSAnyEventMask.bits() | NSEventMask::NSEventMaskPressure.bits(),
|
|
||||||
foundation::NSDate::distantFuture(cocoa::base::nil),
|
|
||||||
foundation::NSDefaultRunLoopMode,
|
|
||||||
cocoa::base::YES);
|
|
||||||
|
|
||||||
let maybe_event = self.ns_event_to_event(ns_event);
|
|
||||||
|
|
||||||
// Release the pool before calling the top callback in case the user calls either
|
|
||||||
// `run_forever` or `poll_events` within the callback.
|
|
||||||
let _: () = msg_send![pool, release];
|
|
||||||
|
|
||||||
if let Some(event) = maybe_event {
|
|
||||||
self.shared.user_callback.call_with_event(event);
|
|
||||||
if let ControlFlow::Break = control_flow.get() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.shared.user_callback.drop();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert some given `NSEvent` into a winit `Event`.
|
|
||||||
unsafe fn ns_event_to_event(&mut self, ns_event: cocoa::base::id) -> Option<Event> {
|
|
||||||
if ns_event == cocoa::base::nil {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Despite not being documented anywhere, an `NSEvent` is produced when a user opens
|
|
||||||
// Spotlight while the NSApplication is in focus. This `NSEvent` produces a `NSEventType`
|
|
||||||
// with value `21`. This causes a SEGFAULT as soon as we try to match on the `NSEventType`
|
|
||||||
// enum as there is no variant associated with the value. Thus, we return early if this
|
|
||||||
// sneaky event occurs. If someone does find some documentation on this, please fix this by
|
|
||||||
// adding an appropriate variant to the `NSEventType` enum in the cocoa-rs crate.
|
|
||||||
if ns_event.eventType() as u64 == 21 {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let event_type = ns_event.eventType();
|
|
||||||
let ns_window = ns_event.window();
|
|
||||||
let window_id = super::window::get_window_id(ns_window);
|
|
||||||
|
|
||||||
// FIXME: Document this. Why do we do this? Seems like it passes on events to window/app.
|
|
||||||
// If we don't do this, window does not become main for some reason.
|
|
||||||
appkit::NSApp().sendEvent_(ns_event);
|
|
||||||
|
|
||||||
let windows = self.shared.windows.lock().unwrap();
|
|
||||||
let maybe_window = windows.iter()
|
|
||||||
.filter_map(Weak::upgrade)
|
|
||||||
.find(|window| window_id == window.id());
|
|
||||||
|
|
||||||
let into_event = |window_event| Event::WindowEvent {
|
|
||||||
window_id: ::WindowId(window_id),
|
|
||||||
event: window_event,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Returns `Some` window if one of our windows is the key window.
|
|
||||||
let maybe_key_window = || windows.iter()
|
|
||||||
.filter_map(Weak::upgrade)
|
|
||||||
.find(|window| {
|
|
||||||
let is_key_window: cocoa::base::BOOL = msg_send![*window.window, isKeyWindow];
|
|
||||||
is_key_window == cocoa::base::YES
|
|
||||||
});
|
|
||||||
|
|
||||||
match event_type {
|
|
||||||
// https://github.com/glfw/glfw/blob/50eccd298a2bbc272b4977bd162d3e4b55f15394/src/cocoa_window.m#L881
|
|
||||||
appkit::NSKeyUp => {
|
|
||||||
if let Some(key_window) = maybe_key_window() {
|
|
||||||
if event_mods(ns_event).logo {
|
|
||||||
let _: () = msg_send![*key_window.window, sendEvent:ns_event];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
},
|
|
||||||
// similar to above, but for `<Cmd-.>`, the keyDown is suppressed instead of the
|
|
||||||
// KeyUp, and the above trick does not appear to work.
|
|
||||||
appkit::NSKeyDown => {
|
|
||||||
let modifiers = event_mods(ns_event);
|
|
||||||
let keycode = NSEvent::keyCode(ns_event);
|
|
||||||
if modifiers.logo && keycode == 47 {
|
|
||||||
modifier_event(ns_event, NSEventModifierFlags::NSCommandKeyMask, false)
|
|
||||||
.map(into_event)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
appkit::NSFlagsChanged => {
|
|
||||||
let mut events = std::collections::VecDeque::new();
|
|
||||||
|
|
||||||
if let Some(window_event) = modifier_event(
|
|
||||||
ns_event,
|
|
||||||
NSEventModifierFlags::NSShiftKeyMask,
|
|
||||||
self.modifiers.shift_pressed,
|
|
||||||
) {
|
|
||||||
self.modifiers.shift_pressed = !self.modifiers.shift_pressed;
|
|
||||||
events.push_back(into_event(window_event));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(window_event) = modifier_event(
|
|
||||||
ns_event,
|
|
||||||
NSEventModifierFlags::NSControlKeyMask,
|
|
||||||
self.modifiers.ctrl_pressed,
|
|
||||||
) {
|
|
||||||
self.modifiers.ctrl_pressed = !self.modifiers.ctrl_pressed;
|
|
||||||
events.push_back(into_event(window_event));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(window_event) = modifier_event(
|
|
||||||
ns_event,
|
|
||||||
NSEventModifierFlags::NSCommandKeyMask,
|
|
||||||
self.modifiers.win_pressed,
|
|
||||||
) {
|
|
||||||
self.modifiers.win_pressed = !self.modifiers.win_pressed;
|
|
||||||
events.push_back(into_event(window_event));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(window_event) = modifier_event(
|
|
||||||
ns_event,
|
|
||||||
NSEventModifierFlags::NSAlternateKeyMask,
|
|
||||||
self.modifiers.alt_pressed,
|
|
||||||
) {
|
|
||||||
self.modifiers.alt_pressed = !self.modifiers.alt_pressed;
|
|
||||||
events.push_back(into_event(window_event));
|
|
||||||
}
|
|
||||||
|
|
||||||
let event = events.pop_front();
|
|
||||||
self.shared.pending_events
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.extend(events.into_iter());
|
|
||||||
event
|
|
||||||
},
|
|
||||||
|
|
||||||
appkit::NSMouseEntered => {
|
|
||||||
let window = match maybe_window.or_else(maybe_key_window) {
|
|
||||||
Some(window) => window,
|
|
||||||
None => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let window_point = ns_event.locationInWindow();
|
|
||||||
let view_point = if ns_window == cocoa::base::nil {
|
|
||||||
let ns_size = foundation::NSSize::new(0.0, 0.0);
|
|
||||||
let ns_rect = foundation::NSRect::new(window_point, ns_size);
|
|
||||||
let window_rect = window.window.convertRectFromScreen_(ns_rect);
|
|
||||||
window.view.convertPoint_fromView_(window_rect.origin, cocoa::base::nil)
|
|
||||||
} else {
|
|
||||||
window.view.convertPoint_fromView_(window_point, cocoa::base::nil)
|
|
||||||
};
|
|
||||||
|
|
||||||
let view_rect = NSView::frame(*window.view);
|
|
||||||
let x = view_point.x as f64;
|
|
||||||
let y = (view_rect.size.height - view_point.y) as f64;
|
|
||||||
let window_event = WindowEvent::CursorMoved {
|
|
||||||
device_id: DEVICE_ID,
|
|
||||||
position: (x, y).into(),
|
|
||||||
modifiers: event_mods(ns_event),
|
|
||||||
};
|
|
||||||
let event = Event::WindowEvent { window_id: ::WindowId(window.id()), event: window_event };
|
|
||||||
self.shared.pending_events.lock().unwrap().push_back(event);
|
|
||||||
Some(into_event(WindowEvent::CursorEntered { device_id: DEVICE_ID }))
|
|
||||||
},
|
|
||||||
appkit::NSMouseExited => { Some(into_event(WindowEvent::CursorLeft { device_id: DEVICE_ID })) },
|
|
||||||
|
|
||||||
appkit::NSMouseMoved |
|
|
||||||
appkit::NSLeftMouseDragged |
|
|
||||||
appkit::NSOtherMouseDragged |
|
|
||||||
appkit::NSRightMouseDragged => {
|
|
||||||
// If the mouse movement was on one of our windows, use it.
|
|
||||||
// Otherwise, if one of our windows is the key window (receiving input), use it.
|
|
||||||
// Otherwise, return `None`.
|
|
||||||
match maybe_window.or_else(maybe_key_window) {
|
|
||||||
Some(_window) => (),
|
|
||||||
None => return None,
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut events = std::collections::VecDeque::with_capacity(3);
|
|
||||||
|
|
||||||
let delta_x = ns_event.deltaX() as f64;
|
|
||||||
if delta_x != 0.0 {
|
|
||||||
let motion_event = DeviceEvent::Motion { axis: 0, value: delta_x };
|
|
||||||
let event = Event::DeviceEvent { device_id: DEVICE_ID, event: motion_event };
|
|
||||||
events.push_back(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
let delta_y = ns_event.deltaY() as f64;
|
|
||||||
if delta_y != 0.0 {
|
|
||||||
let motion_event = DeviceEvent::Motion { axis: 1, value: delta_y };
|
|
||||||
let event = Event::DeviceEvent { device_id: DEVICE_ID, event: motion_event };
|
|
||||||
events.push_back(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
if delta_x != 0.0 || delta_y != 0.0 {
|
|
||||||
let motion_event = DeviceEvent::MouseMotion { delta: (delta_x, delta_y) };
|
|
||||||
let event = Event::DeviceEvent { device_id: DEVICE_ID, event: motion_event };
|
|
||||||
events.push_back(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
let event = events.pop_front();
|
|
||||||
self.shared.pending_events.lock().unwrap().extend(events.into_iter());
|
|
||||||
event
|
|
||||||
},
|
|
||||||
|
|
||||||
appkit::NSScrollWheel => {
|
|
||||||
// If none of the windows received the scroll, return `None`.
|
|
||||||
if maybe_window.is_none() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
use events::MouseScrollDelta::{LineDelta, PixelDelta};
|
|
||||||
let delta = if ns_event.hasPreciseScrollingDeltas() == cocoa::base::YES {
|
|
||||||
PixelDelta((
|
|
||||||
ns_event.scrollingDeltaX() as f64,
|
|
||||||
ns_event.scrollingDeltaY() as f64,
|
|
||||||
).into())
|
|
||||||
} else {
|
|
||||||
// TODO: This is probably wrong
|
|
||||||
LineDelta(
|
|
||||||
ns_event.scrollingDeltaX() as f32,
|
|
||||||
ns_event.scrollingDeltaY() as f32,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let phase = match ns_event.phase() {
|
|
||||||
NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => TouchPhase::Started,
|
|
||||||
NSEventPhase::NSEventPhaseEnded => TouchPhase::Ended,
|
|
||||||
_ => TouchPhase::Moved,
|
|
||||||
};
|
|
||||||
self.shared.pending_events.lock().unwrap().push_back(Event::DeviceEvent {
|
|
||||||
device_id: DEVICE_ID,
|
|
||||||
event: DeviceEvent::MouseWheel {
|
|
||||||
delta: if ns_event.hasPreciseScrollingDeltas() == cocoa::base::YES {
|
|
||||||
PixelDelta((
|
|
||||||
ns_event.scrollingDeltaX() as f64,
|
|
||||||
ns_event.scrollingDeltaY() as f64,
|
|
||||||
).into())
|
|
||||||
} else {
|
|
||||||
LineDelta(
|
|
||||||
ns_event.scrollingDeltaX() as f32,
|
|
||||||
ns_event.scrollingDeltaY() as f32,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let window_event = WindowEvent::MouseWheel { device_id: DEVICE_ID, delta: delta, phase: phase, modifiers: event_mods(ns_event) };
|
|
||||||
Some(into_event(window_event))
|
|
||||||
},
|
|
||||||
|
|
||||||
appkit::NSEventTypePressure => {
|
|
||||||
let pressure = ns_event.pressure();
|
|
||||||
let stage = ns_event.stage();
|
|
||||||
let window_event = WindowEvent::TouchpadPressure { device_id: DEVICE_ID, pressure: pressure, stage: stage };
|
|
||||||
Some(into_event(window_event))
|
|
||||||
},
|
|
||||||
|
|
||||||
appkit::NSApplicationDefined => match ns_event.subtype() {
|
|
||||||
appkit::NSEventSubtype::NSApplicationActivatedEventType => {
|
|
||||||
Some(Event::Awakened)
|
|
||||||
},
|
|
||||||
_ => None,
|
|
||||||
},
|
|
||||||
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_proxy(&self) -> Proxy {
|
|
||||||
Proxy {}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Proxy {
|
|
||||||
pub fn wakeup(&self) -> Result<(), EventLoopClosed> {
|
|
||||||
// Awaken the event loop by triggering `NSApplicationActivatedEventType`.
|
|
||||||
unsafe {
|
|
||||||
let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil);
|
|
||||||
let event =
|
|
||||||
NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(
|
|
||||||
cocoa::base::nil,
|
|
||||||
appkit::NSApplicationDefined,
|
|
||||||
foundation::NSPoint::new(0.0, 0.0),
|
|
||||||
appkit::NSEventModifierFlags::empty(),
|
|
||||||
0.0,
|
|
||||||
0,
|
|
||||||
cocoa::base::nil,
|
|
||||||
appkit::NSEventSubtype::NSApplicationActivatedEventType,
|
|
||||||
0,
|
|
||||||
0);
|
|
||||||
appkit::NSApp().postEvent_atStart_(event, cocoa::base::NO);
|
|
||||||
foundation::NSAutoreleasePool::drain(pool);
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn char_to_keycode(c: char) -> Option<events::VirtualKeyCode> {
|
|
||||||
// We only translate keys that are affected by keyboard layout.
|
|
||||||
//
|
|
||||||
// Note that since keys are translated in a somewhat "dumb" way (reading character)
|
|
||||||
// there is a concern that some combination, i.e. Cmd+char, causes the wrong
|
|
||||||
// letter to be received, and so we receive the wrong key.
|
|
||||||
//
|
|
||||||
// Implementation reference: https://github.com/WebKit/webkit/blob/82bae82cf0f329dbe21059ef0986c4e92fea4ba6/Source/WebCore/platform/cocoa/KeyEventCocoa.mm#L626
|
|
||||||
Some(match c {
|
|
||||||
'a' | 'A' => events::VirtualKeyCode::A,
|
|
||||||
'b' | 'B' => events::VirtualKeyCode::B,
|
|
||||||
'c' | 'C' => events::VirtualKeyCode::C,
|
|
||||||
'd' | 'D' => events::VirtualKeyCode::D,
|
|
||||||
'e' | 'E' => events::VirtualKeyCode::E,
|
|
||||||
'f' | 'F' => events::VirtualKeyCode::F,
|
|
||||||
'g' | 'G' => events::VirtualKeyCode::G,
|
|
||||||
'h' | 'H' => events::VirtualKeyCode::H,
|
|
||||||
'i' | 'I' => events::VirtualKeyCode::I,
|
|
||||||
'j' | 'J' => events::VirtualKeyCode::J,
|
|
||||||
'k' | 'K' => events::VirtualKeyCode::K,
|
|
||||||
'l' | 'L' => events::VirtualKeyCode::L,
|
|
||||||
'm' | 'M' => events::VirtualKeyCode::M,
|
|
||||||
'n' | 'N' => events::VirtualKeyCode::N,
|
|
||||||
'o' | 'O' => events::VirtualKeyCode::O,
|
|
||||||
'p' | 'P' => events::VirtualKeyCode::P,
|
|
||||||
'q' | 'Q' => events::VirtualKeyCode::Q,
|
|
||||||
'r' | 'R' => events::VirtualKeyCode::R,
|
|
||||||
's' | 'S' => events::VirtualKeyCode::S,
|
|
||||||
't' | 'T' => events::VirtualKeyCode::T,
|
|
||||||
'u' | 'U' => events::VirtualKeyCode::U,
|
|
||||||
'v' | 'V' => events::VirtualKeyCode::V,
|
|
||||||
'w' | 'W' => events::VirtualKeyCode::W,
|
|
||||||
'x' | 'X' => events::VirtualKeyCode::X,
|
|
||||||
'y' | 'Y' => events::VirtualKeyCode::Y,
|
|
||||||
'z' | 'Z' => events::VirtualKeyCode::Z,
|
|
||||||
'1' | '!' => events::VirtualKeyCode::Key1,
|
|
||||||
'2' | '@' => events::VirtualKeyCode::Key2,
|
|
||||||
'3' | '#' => events::VirtualKeyCode::Key3,
|
|
||||||
'4' | '$' => events::VirtualKeyCode::Key4,
|
|
||||||
'5' | '%' => events::VirtualKeyCode::Key5,
|
|
||||||
'6' | '^' => events::VirtualKeyCode::Key6,
|
|
||||||
'7' | '&' => events::VirtualKeyCode::Key7,
|
|
||||||
'8' | '*' => events::VirtualKeyCode::Key8,
|
|
||||||
'9' | '(' => events::VirtualKeyCode::Key9,
|
|
||||||
'0' | ')' => events::VirtualKeyCode::Key0,
|
|
||||||
'=' | '+' => events::VirtualKeyCode::Equals,
|
|
||||||
'-' | '_' => events::VirtualKeyCode::Minus,
|
|
||||||
']' | '}' => events::VirtualKeyCode::RBracket,
|
|
||||||
'[' | '{' => events::VirtualKeyCode::LBracket,
|
|
||||||
'\''| '"' => events::VirtualKeyCode::Apostrophe,
|
|
||||||
';' | ':' => events::VirtualKeyCode::Semicolon,
|
|
||||||
'\\'| '|' => events::VirtualKeyCode::Backslash,
|
|
||||||
',' | '<' => events::VirtualKeyCode::Comma,
|
|
||||||
'/' | '?' => events::VirtualKeyCode::Slash,
|
|
||||||
'.' | '>' => events::VirtualKeyCode::Period,
|
|
||||||
'`' | '~' => events::VirtualKeyCode::Grave,
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn scancode_to_keycode(code: c_ushort) -> Option<events::VirtualKeyCode> {
|
|
||||||
Some(match code {
|
|
||||||
0x00 => events::VirtualKeyCode::A,
|
|
||||||
0x01 => events::VirtualKeyCode::S,
|
|
||||||
0x02 => events::VirtualKeyCode::D,
|
|
||||||
0x03 => events::VirtualKeyCode::F,
|
|
||||||
0x04 => events::VirtualKeyCode::H,
|
|
||||||
0x05 => events::VirtualKeyCode::G,
|
|
||||||
0x06 => events::VirtualKeyCode::Z,
|
|
||||||
0x07 => events::VirtualKeyCode::X,
|
|
||||||
0x08 => events::VirtualKeyCode::C,
|
|
||||||
0x09 => events::VirtualKeyCode::V,
|
|
||||||
//0x0a => World 1,
|
|
||||||
0x0b => events::VirtualKeyCode::B,
|
|
||||||
0x0c => events::VirtualKeyCode::Q,
|
|
||||||
0x0d => events::VirtualKeyCode::W,
|
|
||||||
0x0e => events::VirtualKeyCode::E,
|
|
||||||
0x0f => events::VirtualKeyCode::R,
|
|
||||||
0x10 => events::VirtualKeyCode::Y,
|
|
||||||
0x11 => events::VirtualKeyCode::T,
|
|
||||||
0x12 => events::VirtualKeyCode::Key1,
|
|
||||||
0x13 => events::VirtualKeyCode::Key2,
|
|
||||||
0x14 => events::VirtualKeyCode::Key3,
|
|
||||||
0x15 => events::VirtualKeyCode::Key4,
|
|
||||||
0x16 => events::VirtualKeyCode::Key6,
|
|
||||||
0x17 => events::VirtualKeyCode::Key5,
|
|
||||||
0x18 => events::VirtualKeyCode::Equals,
|
|
||||||
0x19 => events::VirtualKeyCode::Key9,
|
|
||||||
0x1a => events::VirtualKeyCode::Key7,
|
|
||||||
0x1b => events::VirtualKeyCode::Minus,
|
|
||||||
0x1c => events::VirtualKeyCode::Key8,
|
|
||||||
0x1d => events::VirtualKeyCode::Key0,
|
|
||||||
0x1e => events::VirtualKeyCode::RBracket,
|
|
||||||
0x1f => events::VirtualKeyCode::O,
|
|
||||||
0x20 => events::VirtualKeyCode::U,
|
|
||||||
0x21 => events::VirtualKeyCode::LBracket,
|
|
||||||
0x22 => events::VirtualKeyCode::I,
|
|
||||||
0x23 => events::VirtualKeyCode::P,
|
|
||||||
0x24 => events::VirtualKeyCode::Return,
|
|
||||||
0x25 => events::VirtualKeyCode::L,
|
|
||||||
0x26 => events::VirtualKeyCode::J,
|
|
||||||
0x27 => events::VirtualKeyCode::Apostrophe,
|
|
||||||
0x28 => events::VirtualKeyCode::K,
|
|
||||||
0x29 => events::VirtualKeyCode::Semicolon,
|
|
||||||
0x2a => events::VirtualKeyCode::Backslash,
|
|
||||||
0x2b => events::VirtualKeyCode::Comma,
|
|
||||||
0x2c => events::VirtualKeyCode::Slash,
|
|
||||||
0x2d => events::VirtualKeyCode::N,
|
|
||||||
0x2e => events::VirtualKeyCode::M,
|
|
||||||
0x2f => events::VirtualKeyCode::Period,
|
|
||||||
0x30 => events::VirtualKeyCode::Tab,
|
|
||||||
0x31 => events::VirtualKeyCode::Space,
|
|
||||||
0x32 => events::VirtualKeyCode::Grave,
|
|
||||||
0x33 => events::VirtualKeyCode::Back,
|
|
||||||
//0x34 => unkown,
|
|
||||||
0x35 => events::VirtualKeyCode::Escape,
|
|
||||||
0x36 => events::VirtualKeyCode::RWin,
|
|
||||||
0x37 => events::VirtualKeyCode::LWin,
|
|
||||||
0x38 => events::VirtualKeyCode::LShift,
|
|
||||||
//0x39 => Caps lock,
|
|
||||||
0x3a => events::VirtualKeyCode::LAlt,
|
|
||||||
0x3b => events::VirtualKeyCode::LControl,
|
|
||||||
0x3c => events::VirtualKeyCode::RShift,
|
|
||||||
0x3d => events::VirtualKeyCode::RAlt,
|
|
||||||
0x3e => events::VirtualKeyCode::RControl,
|
|
||||||
//0x3f => Fn key,
|
|
||||||
0x40 => events::VirtualKeyCode::F17,
|
|
||||||
0x41 => events::VirtualKeyCode::Decimal,
|
|
||||||
//0x42 -> unkown,
|
|
||||||
0x43 => events::VirtualKeyCode::Multiply,
|
|
||||||
//0x44 => unkown,
|
|
||||||
0x45 => events::VirtualKeyCode::Add,
|
|
||||||
//0x46 => unkown,
|
|
||||||
0x47 => events::VirtualKeyCode::Numlock,
|
|
||||||
//0x48 => KeypadClear,
|
|
||||||
0x49 => events::VirtualKeyCode::VolumeUp,
|
|
||||||
0x4a => events::VirtualKeyCode::VolumeDown,
|
|
||||||
0x4b => events::VirtualKeyCode::Divide,
|
|
||||||
0x4c => events::VirtualKeyCode::NumpadEnter,
|
|
||||||
0x4e => events::VirtualKeyCode::Subtract,
|
|
||||||
//0x4d => unkown,
|
|
||||||
0x4e => events::VirtualKeyCode::Subtract,
|
|
||||||
0x4f => events::VirtualKeyCode::F18,
|
|
||||||
0x50 => events::VirtualKeyCode::F19,
|
|
||||||
0x51 => events::VirtualKeyCode::NumpadEquals,
|
|
||||||
0x52 => events::VirtualKeyCode::Numpad0,
|
|
||||||
0x53 => events::VirtualKeyCode::Numpad1,
|
|
||||||
0x54 => events::VirtualKeyCode::Numpad2,
|
|
||||||
0x55 => events::VirtualKeyCode::Numpad3,
|
|
||||||
0x56 => events::VirtualKeyCode::Numpad4,
|
|
||||||
0x57 => events::VirtualKeyCode::Numpad5,
|
|
||||||
0x58 => events::VirtualKeyCode::Numpad6,
|
|
||||||
0x59 => events::VirtualKeyCode::Numpad7,
|
|
||||||
0x5a => events::VirtualKeyCode::F20,
|
|
||||||
0x5b => events::VirtualKeyCode::Numpad8,
|
|
||||||
0x5c => events::VirtualKeyCode::Numpad9,
|
|
||||||
0x5d => events::VirtualKeyCode::Yen,
|
|
||||||
//0x5e => JIS Ro,
|
|
||||||
//0x5f => unkown,
|
|
||||||
0x60 => events::VirtualKeyCode::F5,
|
|
||||||
0x61 => events::VirtualKeyCode::F6,
|
|
||||||
0x62 => events::VirtualKeyCode::F7,
|
|
||||||
0x63 => events::VirtualKeyCode::F3,
|
|
||||||
0x64 => events::VirtualKeyCode::F8,
|
|
||||||
0x65 => events::VirtualKeyCode::F9,
|
|
||||||
//0x66 => JIS Eisuu (macOS),
|
|
||||||
0x67 => events::VirtualKeyCode::F11,
|
|
||||||
//0x68 => JIS Kana (macOS),
|
|
||||||
0x69 => events::VirtualKeyCode::F13,
|
|
||||||
0x6a => events::VirtualKeyCode::F16,
|
|
||||||
0x6b => events::VirtualKeyCode::F14,
|
|
||||||
//0x6c => unkown,
|
|
||||||
0x6d => events::VirtualKeyCode::F10,
|
|
||||||
//0x6e => unkown,
|
|
||||||
0x6f => events::VirtualKeyCode::F12,
|
|
||||||
//0x70 => unkown,
|
|
||||||
0x71 => events::VirtualKeyCode::F15,
|
|
||||||
0x72 => events::VirtualKeyCode::Insert,
|
|
||||||
0x73 => events::VirtualKeyCode::Home,
|
|
||||||
0x74 => events::VirtualKeyCode::PageUp,
|
|
||||||
0x75 => events::VirtualKeyCode::Delete,
|
|
||||||
0x76 => events::VirtualKeyCode::F4,
|
|
||||||
0x77 => events::VirtualKeyCode::End,
|
|
||||||
0x78 => events::VirtualKeyCode::F2,
|
|
||||||
0x79 => events::VirtualKeyCode::PageDown,
|
|
||||||
0x7a => events::VirtualKeyCode::F1,
|
|
||||||
0x7b => events::VirtualKeyCode::Left,
|
|
||||||
0x7c => events::VirtualKeyCode::Right,
|
|
||||||
0x7d => events::VirtualKeyCode::Down,
|
|
||||||
0x7e => events::VirtualKeyCode::Up,
|
|
||||||
//0x7f => unkown,
|
|
||||||
|
|
||||||
0xa => events::VirtualKeyCode::Caret,
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_function_keys(
|
|
||||||
s: &String
|
|
||||||
) -> Option<events::VirtualKeyCode> {
|
|
||||||
if let Some(ch) = s.encode_utf16().next() {
|
|
||||||
return Some(match ch {
|
|
||||||
0xf718 => events::VirtualKeyCode::F21,
|
|
||||||
0xf719 => events::VirtualKeyCode::F22,
|
|
||||||
0xf71a => events::VirtualKeyCode::F23,
|
|
||||||
0xf71b => events::VirtualKeyCode::F24,
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn event_mods(event: cocoa::base::id) -> ModifiersState {
|
|
||||||
let flags = unsafe {
|
|
||||||
NSEvent::modifierFlags(event)
|
|
||||||
};
|
|
||||||
ModifiersState {
|
|
||||||
shift: flags.contains(NSEventModifierFlags::NSShiftKeyMask),
|
|
||||||
ctrl: flags.contains(NSEventModifierFlags::NSControlKeyMask),
|
|
||||||
alt: flags.contains(NSEventModifierFlags::NSAlternateKeyMask),
|
|
||||||
logo: flags.contains(NSEventModifierFlags::NSCommandKeyMask),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_scancode(event: cocoa::base::id) -> c_ushort {
|
|
||||||
// In AppKit, `keyCode` refers to the position (scancode) of a key rather than its character,
|
|
||||||
// and there is no easy way to navtively retrieve the layout-dependent character.
|
|
||||||
// In winit, we use keycode to refer to the key's character, and so this function aligns
|
|
||||||
// AppKit's terminology with ours.
|
|
||||||
unsafe {
|
|
||||||
msg_send![event, keyCode]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn modifier_event(
|
|
||||||
ns_event: cocoa::base::id,
|
|
||||||
keymask: NSEventModifierFlags,
|
|
||||||
was_key_pressed: bool,
|
|
||||||
) -> Option<WindowEvent> {
|
|
||||||
if !was_key_pressed && NSEvent::modifierFlags(ns_event).contains(keymask)
|
|
||||||
|| was_key_pressed && !NSEvent::modifierFlags(ns_event).contains(keymask) {
|
|
||||||
let state = if was_key_pressed {
|
|
||||||
ElementState::Released
|
|
||||||
} else {
|
|
||||||
ElementState::Pressed
|
|
||||||
};
|
|
||||||
|
|
||||||
let scancode = get_scancode(ns_event);
|
|
||||||
let virtual_keycode = scancode_to_keycode(scancode);
|
|
||||||
Some(WindowEvent::KeyboardInput {
|
|
||||||
device_id: DEVICE_ID,
|
|
||||||
input: KeyboardInput {
|
|
||||||
state,
|
|
||||||
scancode: scancode as u32,
|
|
||||||
virtual_keycode,
|
|
||||||
modifiers: event_mods(ns_event),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constant device ID, to be removed when this backend is updated to report real device IDs.
|
|
||||||
pub const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId);
|
|
||||||
|
|
|
@ -95,6 +95,7 @@ pub const kCGDesktopIconWindowLevelKey: NSInteger = 18;
|
||||||
pub const kCGCursorWindowLevelKey: NSInteger = 19;
|
pub const kCGCursorWindowLevelKey: NSInteger = 19;
|
||||||
pub const kCGNumberOfWindowLevelKeys: NSInteger = 20;
|
pub const kCGNumberOfWindowLevelKeys: NSInteger = 20;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum NSWindowLevel {
|
pub enum NSWindowLevel {
|
||||||
NSNormalWindowLevel = kCGBaseWindowLevelKey as _,
|
NSNormalWindowLevel = kCGBaseWindowLevelKey as _,
|
||||||
NSFloatingWindowLevel = kCGFloatingWindowLevelKey as _,
|
NSFloatingWindowLevel = kCGFloatingWindowLevelKey as _,
|
||||||
|
|
|
@ -1,9 +1,30 @@
|
||||||
#![cfg(target_os = "macos")]
|
#![cfg(target_os = "macos")]
|
||||||
|
|
||||||
pub use self::event_loop::{EventLoop, Proxy as EventLoopProxy};
|
mod app;
|
||||||
pub use self::monitor::MonitorHandle;
|
mod app_delegate;
|
||||||
pub use self::window::{Id as WindowId, PlatformSpecificWindowBuilderAttributes, Window2};
|
mod app_state;
|
||||||
use std::sync::Arc;
|
mod event;
|
||||||
|
mod event_loop;
|
||||||
|
mod ffi;
|
||||||
|
mod monitor;
|
||||||
|
mod observer;
|
||||||
|
mod util;
|
||||||
|
mod view;
|
||||||
|
mod window;
|
||||||
|
mod window_delegate;
|
||||||
|
|
||||||
|
use std::{ops::Deref, sync::Arc};
|
||||||
|
|
||||||
|
use {
|
||||||
|
event::DeviceId as RootDeviceId, window::{CreationError, WindowAttributes},
|
||||||
|
};
|
||||||
|
pub use self::{
|
||||||
|
event_loop::{EventLoop, EventLoopWindowTarget, Proxy as EventLoopProxy},
|
||||||
|
monitor::MonitorHandle,
|
||||||
|
window::{
|
||||||
|
Id as WindowId, PlatformSpecificWindowBuilderAttributes, UnownedWindow,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct DeviceId;
|
pub struct DeviceId;
|
||||||
|
@ -14,38 +35,33 @@ impl DeviceId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use {CreationError};
|
// Constant device ID; to be removed when if backend is updated to report real device IDs.
|
||||||
|
pub(crate) const DEVICE_ID: RootDeviceId = RootDeviceId(DeviceId);
|
||||||
|
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
pub window: Arc<Window2>,
|
window: Arc<UnownedWindow>,
|
||||||
|
// We keep this around so that it doesn't get dropped until the window does.
|
||||||
|
_delegate: util::IdRef,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::ops::Deref for Window {
|
unsafe impl Send for Window {}
|
||||||
type Target = Window2;
|
unsafe impl Sync for Window {}
|
||||||
|
|
||||||
|
impl Deref for Window {
|
||||||
|
type Target = UnownedWindow;
|
||||||
#[inline]
|
#[inline]
|
||||||
fn deref(&self) -> &Window2 {
|
fn deref(&self) -> &Self::Target {
|
||||||
&*self.window
|
&*self.window
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
|
pub fn new<T: 'static>(
|
||||||
pub fn new(event_loop: &EventLoop,
|
_window_target: &EventLoopWindowTarget<T>,
|
||||||
attributes: ::WindowAttributes,
|
attributes: WindowAttributes,
|
||||||
pl_attribs: PlatformSpecificWindowBuilderAttributes) -> Result<Self, CreationError>
|
pl_attribs: PlatformSpecificWindowBuilderAttributes,
|
||||||
{
|
) -> Result<Self, CreationError> {
|
||||||
let weak_shared = Arc::downgrade(&event_loop.shared);
|
let (window, _delegate) = UnownedWindow::new(attributes, pl_attribs)?;
|
||||||
let window = Arc::new(try!(Window2::new(weak_shared, attributes, pl_attribs)));
|
Ok(Window { window, _delegate })
|
||||||
let weak_window = Arc::downgrade(&window);
|
|
||||||
event_loop.shared.windows.lock().unwrap().push(weak_window);
|
|
||||||
Ok(Window { window: window })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod event_loop;
|
|
||||||
mod ffi;
|
|
||||||
mod monitor;
|
|
||||||
mod util;
|
|
||||||
mod view;
|
|
||||||
mod window;
|
|
||||||
|
|
|
@ -1,23 +1,19 @@
|
||||||
use std::collections::VecDeque;
|
use std::{collections::VecDeque, fmt};
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use cocoa::appkit::NSScreen;
|
use cocoa::{appkit::NSScreen, base::{id, nil}, foundation::{NSString, NSUInteger}};
|
||||||
use cocoa::base::{id, nil};
|
|
||||||
use cocoa::foundation::{NSString, NSUInteger};
|
|
||||||
use core_graphics::display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds};
|
use core_graphics::display::{CGDirectDisplayID, CGDisplay, CGDisplayBounds};
|
||||||
|
|
||||||
use {PhysicalPosition, PhysicalSize};
|
use dpi::{PhysicalPosition, PhysicalSize};
|
||||||
use super::EventLoop;
|
use platform_impl::platform::util::IdRef;
|
||||||
use super::window::{IdRef, Window2};
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct MonitorHandle(CGDirectDisplayID);
|
pub struct MonitorHandle(CGDirectDisplayID);
|
||||||
|
|
||||||
fn get_available_monitors() -> VecDeque<MonitorHandle> {
|
pub fn get_available_monitors() -> VecDeque<MonitorHandle> {
|
||||||
if let Ok(displays) = CGDisplay::active_displays() {
|
if let Ok(displays) = CGDisplay::active_displays() {
|
||||||
let mut monitors = VecDeque::with_capacity(displays.len());
|
let mut monitors = VecDeque::with_capacity(displays.len());
|
||||||
for d in displays {
|
for display in displays {
|
||||||
monitors.push_back(MonitorHandle(d));
|
monitors.push_back(MonitorHandle(display));
|
||||||
}
|
}
|
||||||
monitors
|
monitors
|
||||||
} else {
|
} else {
|
||||||
|
@ -26,41 +22,12 @@ fn get_available_monitors() -> VecDeque<MonitorHandle> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_primary_monitor() -> MonitorHandle {
|
pub fn get_primary_monitor() -> MonitorHandle {
|
||||||
let id = MonitorHandle(CGDisplay::main().id);
|
MonitorHandle(CGDisplay::main().id)
|
||||||
id
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EventLoop {
|
|
||||||
#[inline]
|
|
||||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorHandle> {
|
|
||||||
get_available_monitors()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn get_primary_monitor(&self) -> MonitorHandle {
|
|
||||||
get_primary_monitor()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn make_monitor_from_display(id: CGDirectDisplayID) -> MonitorHandle {
|
|
||||||
let id = MonitorHandle(id);
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Window2 {
|
|
||||||
#[inline]
|
|
||||||
pub fn get_available_monitors(&self) -> VecDeque<MonitorHandle> {
|
|
||||||
get_available_monitors()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn get_primary_monitor(&self) -> MonitorHandle {
|
|
||||||
get_primary_monitor()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for MonitorHandle {
|
impl fmt::Debug for MonitorHandle {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
// TODO: Do this using the proper fmt API
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct MonitorHandle {
|
struct MonitorHandle {
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
|
@ -83,6 +50,10 @@ impl fmt::Debug for MonitorHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MonitorHandle {
|
impl MonitorHandle {
|
||||||
|
pub fn new(id: CGDirectDisplayID) -> Self {
|
||||||
|
MonitorHandle(id)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_name(&self) -> Option<String> {
|
pub fn get_name(&self) -> Option<String> {
|
||||||
let MonitorHandle(display_id) = *self;
|
let MonitorHandle(display_id) = *self;
|
||||||
let screen_num = CGDisplay::new(display_id).model_number();
|
let screen_num = CGDisplay::new(display_id).model_number();
|
||||||
|
|
259
src/platform_impl/macos/observer.rs
Normal file
259
src/platform_impl/macos/observer.rs
Normal file
|
@ -0,0 +1,259 @@
|
||||||
|
use std::{self, ptr, os::raw::*, time::Instant};
|
||||||
|
|
||||||
|
use platform_impl::platform::app_state::AppState;
|
||||||
|
|
||||||
|
#[link(name = "CoreFoundation", kind = "framework")]
|
||||||
|
extern {
|
||||||
|
pub static kCFRunLoopDefaultMode: CFRunLoopMode;
|
||||||
|
pub static kCFRunLoopCommonModes: CFRunLoopMode;
|
||||||
|
|
||||||
|
pub fn CFRunLoopGetMain() -> CFRunLoopRef;
|
||||||
|
pub fn CFRunLoopWakeUp(rl: CFRunLoopRef);
|
||||||
|
|
||||||
|
pub fn CFRunLoopObserverCreate(
|
||||||
|
allocator: CFAllocatorRef,
|
||||||
|
activities: CFOptionFlags,
|
||||||
|
repeats: Boolean,
|
||||||
|
order: CFIndex,
|
||||||
|
callout: CFRunLoopObserverCallBack,
|
||||||
|
context: *mut CFRunLoopObserverContext,
|
||||||
|
) -> CFRunLoopObserverRef;
|
||||||
|
pub fn CFRunLoopAddObserver(
|
||||||
|
rl: CFRunLoopRef,
|
||||||
|
observer: CFRunLoopObserverRef,
|
||||||
|
mode: CFRunLoopMode,
|
||||||
|
);
|
||||||
|
|
||||||
|
pub fn CFRunLoopTimerCreate(
|
||||||
|
allocator: CFAllocatorRef,
|
||||||
|
fireDate: CFAbsoluteTime,
|
||||||
|
interval: CFTimeInterval,
|
||||||
|
flags: CFOptionFlags,
|
||||||
|
order: CFIndex,
|
||||||
|
callout: CFRunLoopTimerCallBack,
|
||||||
|
context: *mut CFRunLoopTimerContext,
|
||||||
|
) -> CFRunLoopTimerRef;
|
||||||
|
pub fn CFRunLoopAddTimer(
|
||||||
|
rl: CFRunLoopRef,
|
||||||
|
timer: CFRunLoopTimerRef,
|
||||||
|
mode: CFRunLoopMode,
|
||||||
|
);
|
||||||
|
pub fn CFRunLoopTimerSetNextFireDate(
|
||||||
|
timer: CFRunLoopTimerRef,
|
||||||
|
fireDate: CFAbsoluteTime,
|
||||||
|
);
|
||||||
|
pub fn CFRunLoopTimerInvalidate(time: CFRunLoopTimerRef);
|
||||||
|
|
||||||
|
pub fn CFRunLoopSourceCreate(
|
||||||
|
allocator: CFAllocatorRef,
|
||||||
|
order: CFIndex,
|
||||||
|
context: *mut CFRunLoopSourceContext,
|
||||||
|
) -> CFRunLoopSourceRef;
|
||||||
|
pub fn CFRunLoopAddSource(
|
||||||
|
rl: CFRunLoopRef,
|
||||||
|
source: CFRunLoopSourceRef,
|
||||||
|
mode: CFRunLoopMode,
|
||||||
|
);
|
||||||
|
pub fn CFRunLoopSourceInvalidate(source: CFRunLoopSourceRef);
|
||||||
|
pub fn CFRunLoopSourceSignal(source: CFRunLoopSourceRef);
|
||||||
|
|
||||||
|
pub fn CFAbsoluteTimeGetCurrent() -> CFAbsoluteTime;
|
||||||
|
pub fn CFRelease(cftype: *const c_void);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Boolean = u8;
|
||||||
|
const FALSE: Boolean = 0;
|
||||||
|
const TRUE: Boolean = 1;
|
||||||
|
|
||||||
|
pub enum CFAllocator {}
|
||||||
|
pub type CFAllocatorRef = *mut CFAllocator;
|
||||||
|
pub enum CFRunLoop {}
|
||||||
|
pub type CFRunLoopRef = *mut CFRunLoop;
|
||||||
|
pub type CFRunLoopMode = CFStringRef;
|
||||||
|
pub enum CFRunLoopObserver {}
|
||||||
|
pub type CFRunLoopObserverRef = *mut CFRunLoopObserver;
|
||||||
|
pub enum CFRunLoopTimer {}
|
||||||
|
pub type CFRunLoopTimerRef = *mut CFRunLoopTimer;
|
||||||
|
pub enum CFRunLoopSource {}
|
||||||
|
pub type CFRunLoopSourceRef = *mut CFRunLoopSource;
|
||||||
|
pub enum CFString {}
|
||||||
|
pub type CFStringRef = *const CFString;
|
||||||
|
|
||||||
|
pub type CFHashCode = c_ulong;
|
||||||
|
pub type CFIndex = c_long;
|
||||||
|
pub type CFOptionFlags = c_ulong;
|
||||||
|
pub type CFRunLoopActivity = CFOptionFlags;
|
||||||
|
|
||||||
|
pub type CFAbsoluteTime = CFTimeInterval;
|
||||||
|
pub type CFTimeInterval = f64;
|
||||||
|
|
||||||
|
pub const kCFRunLoopEntry: CFRunLoopActivity = 0;
|
||||||
|
pub const kCFRunLoopBeforeWaiting: CFRunLoopActivity = 1 << 5;
|
||||||
|
pub const kCFRunLoopAfterWaiting: CFRunLoopActivity = 1 << 6;
|
||||||
|
pub const kCFRunLoopExit: CFRunLoopActivity = 1 << 7;
|
||||||
|
|
||||||
|
pub type CFRunLoopObserverCallBack = extern "C" fn(
|
||||||
|
observer: CFRunLoopObserverRef,
|
||||||
|
activity: CFRunLoopActivity,
|
||||||
|
info: *mut c_void,
|
||||||
|
);
|
||||||
|
pub type CFRunLoopTimerCallBack = extern "C" fn(
|
||||||
|
timer: CFRunLoopTimerRef,
|
||||||
|
info: *mut c_void
|
||||||
|
);
|
||||||
|
|
||||||
|
pub enum CFRunLoopObserverContext {}
|
||||||
|
pub enum CFRunLoopTimerContext {}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct CFRunLoopSourceContext {
|
||||||
|
pub version: CFIndex,
|
||||||
|
pub info: *mut c_void,
|
||||||
|
pub retain: extern "C" fn(*const c_void) -> *const c_void,
|
||||||
|
pub release: extern "C" fn(*const c_void),
|
||||||
|
pub copyDescription: extern "C" fn(*const c_void) -> CFStringRef,
|
||||||
|
pub equal: extern "C" fn(*const c_void, *const c_void) -> Boolean,
|
||||||
|
pub hash: extern "C" fn(*const c_void) -> CFHashCode,
|
||||||
|
pub schedule: extern "C" fn(*mut c_void, CFRunLoopRef, CFRunLoopMode),
|
||||||
|
pub cancel: extern "C" fn(*mut c_void, CFRunLoopRef, CFRunLoopMode),
|
||||||
|
pub perform: extern "C" fn(*mut c_void),
|
||||||
|
}
|
||||||
|
|
||||||
|
// begin is queued with the highest priority to ensure it is processed before other observers
|
||||||
|
extern fn control_flow_begin_handler(
|
||||||
|
_: CFRunLoopObserverRef,
|
||||||
|
activity: CFRunLoopActivity,
|
||||||
|
_: *mut c_void,
|
||||||
|
) {
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
match activity {
|
||||||
|
kCFRunLoopAfterWaiting => {
|
||||||
|
//trace!("Triggered `CFRunLoopAfterWaiting`");
|
||||||
|
AppState::wakeup();
|
||||||
|
//trace!("Completed `CFRunLoopAfterWaiting`");
|
||||||
|
},
|
||||||
|
kCFRunLoopEntry => unimplemented!(), // not expected to ever happen
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// end is queued with the lowest priority to ensure it is processed after other observers
|
||||||
|
// without that, LoopDestroyed would get sent after EventsCleared
|
||||||
|
extern fn control_flow_end_handler(
|
||||||
|
_: CFRunLoopObserverRef,
|
||||||
|
activity: CFRunLoopActivity,
|
||||||
|
_: *mut c_void,
|
||||||
|
) {
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
match activity {
|
||||||
|
kCFRunLoopBeforeWaiting => {
|
||||||
|
//trace!("Triggered `CFRunLoopBeforeWaiting`");
|
||||||
|
AppState::cleared();
|
||||||
|
//trace!("Completed `CFRunLoopBeforeWaiting`");
|
||||||
|
},
|
||||||
|
kCFRunLoopExit => (),//unimplemented!(), // not expected to ever happen
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RunLoop(CFRunLoopRef);
|
||||||
|
|
||||||
|
impl RunLoop {
|
||||||
|
unsafe fn get() -> Self {
|
||||||
|
RunLoop(CFRunLoopGetMain())
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn add_observer(
|
||||||
|
&self,
|
||||||
|
flags: CFOptionFlags,
|
||||||
|
priority: CFIndex,
|
||||||
|
handler: CFRunLoopObserverCallBack,
|
||||||
|
) {
|
||||||
|
let observer = CFRunLoopObserverCreate(
|
||||||
|
ptr::null_mut(),
|
||||||
|
flags,
|
||||||
|
TRUE, // Indicates we want this to run repeatedly
|
||||||
|
priority, // The lower the value, the sooner this will run
|
||||||
|
handler,
|
||||||
|
ptr::null_mut(),
|
||||||
|
);
|
||||||
|
CFRunLoopAddObserver(self.0, observer, kCFRunLoopDefaultMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_control_flow_observers() {
|
||||||
|
unsafe {
|
||||||
|
let run_loop = RunLoop::get();
|
||||||
|
run_loop.add_observer(
|
||||||
|
kCFRunLoopEntry | kCFRunLoopAfterWaiting,
|
||||||
|
CFIndex::min_value(),
|
||||||
|
control_flow_begin_handler,
|
||||||
|
);
|
||||||
|
run_loop.add_observer(
|
||||||
|
kCFRunLoopExit | kCFRunLoopBeforeWaiting,
|
||||||
|
CFIndex::max_value(),
|
||||||
|
control_flow_end_handler,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct EventLoopWaker {
|
||||||
|
timer: CFRunLoopTimerRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for EventLoopWaker {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
CFRunLoopTimerInvalidate(self.timer);
|
||||||
|
CFRelease(self.timer as _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for EventLoopWaker {
|
||||||
|
fn default() -> EventLoopWaker {
|
||||||
|
extern fn wakeup_main_loop(_timer: CFRunLoopTimerRef, _info: *mut c_void) {}
|
||||||
|
unsafe {
|
||||||
|
// create a timer with a 1µs interval (1ns does not work) to mimic polling.
|
||||||
|
// it is initially setup with a first fire time really far into the
|
||||||
|
// future, but that gets changed to fire immediatley in did_finish_launching
|
||||||
|
let timer = CFRunLoopTimerCreate(
|
||||||
|
ptr::null_mut(),
|
||||||
|
std::f64::MAX,
|
||||||
|
0.000_000_1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
wakeup_main_loop,
|
||||||
|
ptr::null_mut(),
|
||||||
|
);
|
||||||
|
CFRunLoopAddTimer(CFRunLoopGetMain(), timer, kCFRunLoopCommonModes);
|
||||||
|
EventLoopWaker { timer }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EventLoopWaker {
|
||||||
|
pub fn stop(&mut self) {
|
||||||
|
unsafe { CFRunLoopTimerSetNextFireDate(self.timer, std::f64::MAX) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start(&mut self) {
|
||||||
|
unsafe { CFRunLoopTimerSetNextFireDate(self.timer, std::f64::MIN) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_at(&mut self, instant: Instant) {
|
||||||
|
let now = Instant::now();
|
||||||
|
if now >= instant {
|
||||||
|
self.start();
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
let current = CFAbsoluteTimeGetCurrent();
|
||||||
|
let duration = instant - now;
|
||||||
|
let fsecs = duration.subsec_nanos() as f64 / 1_000_000_000.0
|
||||||
|
+ duration.as_secs() as f64;
|
||||||
|
CFRunLoopTimerSetNextFireDate(self.timer, current + fsecs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
327
src/platform_impl/macos/util/async.rs
Normal file
327
src/platform_impl/macos/util/async.rs
Normal file
|
@ -0,0 +1,327 @@
|
||||||
|
use std::{os::raw::c_void, sync::{Mutex, Weak}};
|
||||||
|
|
||||||
|
use cocoa::{
|
||||||
|
appkit::{CGFloat, NSWindow, NSWindowStyleMask},
|
||||||
|
base::{id, nil},
|
||||||
|
foundation::{NSAutoreleasePool, NSPoint, NSSize},
|
||||||
|
};
|
||||||
|
use dispatch::ffi::{dispatch_async_f, dispatch_get_main_queue, dispatch_sync_f};
|
||||||
|
|
||||||
|
use dpi::LogicalSize;
|
||||||
|
use platform_impl::platform::{ffi, window::SharedState};
|
||||||
|
|
||||||
|
unsafe fn set_style_mask(nswindow: id, nsview: id, mask: NSWindowStyleMask) {
|
||||||
|
nswindow.setStyleMask_(mask);
|
||||||
|
// If we don't do this, key handling will break
|
||||||
|
// (at least until the window is clicked again/etc.)
|
||||||
|
nswindow.makeFirstResponder_(nsview);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SetStyleMaskData {
|
||||||
|
nswindow: id,
|
||||||
|
nsview: id,
|
||||||
|
mask: NSWindowStyleMask,
|
||||||
|
}
|
||||||
|
impl SetStyleMaskData {
|
||||||
|
fn new_ptr(
|
||||||
|
nswindow: id,
|
||||||
|
nsview: id,
|
||||||
|
mask: NSWindowStyleMask,
|
||||||
|
) -> *mut Self {
|
||||||
|
Box::into_raw(Box::new(SetStyleMaskData { nswindow, nsview, mask }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extern fn set_style_mask_callback(context: *mut c_void) {
|
||||||
|
unsafe {
|
||||||
|
let context_ptr = context as *mut SetStyleMaskData;
|
||||||
|
{
|
||||||
|
let context = &*context_ptr;
|
||||||
|
set_style_mask(context.nswindow, context.nsview, context.mask);
|
||||||
|
}
|
||||||
|
Box::from_raw(context_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Always use this function instead of trying to modify `styleMask` directly!
|
||||||
|
// `setStyleMask:` isn't thread-safe, so we have to use Grand Central Dispatch.
|
||||||
|
// Otherwise, this would vomit out errors about not being on the main thread
|
||||||
|
// and fail to do anything.
|
||||||
|
pub unsafe fn set_style_mask_async(nswindow: id, nsview: id, mask: NSWindowStyleMask) {
|
||||||
|
let context = SetStyleMaskData::new_ptr(nswindow, nsview, mask);
|
||||||
|
dispatch_async_f(
|
||||||
|
dispatch_get_main_queue(),
|
||||||
|
context as *mut _,
|
||||||
|
set_style_mask_callback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
pub unsafe fn set_style_mask_sync(nswindow: id, nsview: id, mask: NSWindowStyleMask) {
|
||||||
|
let context = SetStyleMaskData::new_ptr(nswindow, nsview, mask);
|
||||||
|
dispatch_sync_f(
|
||||||
|
dispatch_get_main_queue(),
|
||||||
|
context as *mut _,
|
||||||
|
set_style_mask_callback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SetContentSizeData {
|
||||||
|
nswindow: id,
|
||||||
|
size: LogicalSize,
|
||||||
|
}
|
||||||
|
impl SetContentSizeData {
|
||||||
|
fn new_ptr(
|
||||||
|
nswindow: id,
|
||||||
|
size: LogicalSize,
|
||||||
|
) -> *mut Self {
|
||||||
|
Box::into_raw(Box::new(SetContentSizeData { nswindow, size }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extern fn set_content_size_callback(context: *mut c_void) {
|
||||||
|
unsafe {
|
||||||
|
let context_ptr = context as *mut SetContentSizeData;
|
||||||
|
{
|
||||||
|
let context = &*context_ptr;
|
||||||
|
NSWindow::setContentSize_(
|
||||||
|
context.nswindow,
|
||||||
|
NSSize::new(
|
||||||
|
context.size.width as CGFloat,
|
||||||
|
context.size.height as CGFloat,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Box::from_raw(context_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// `setContentSize:` isn't thread-safe either, though it doesn't log any errors
|
||||||
|
// and just fails silently. Anyway, GCD to the rescue!
|
||||||
|
pub unsafe fn set_content_size_async(nswindow: id, size: LogicalSize) {
|
||||||
|
let context = SetContentSizeData::new_ptr(nswindow, size);
|
||||||
|
dispatch_async_f(
|
||||||
|
dispatch_get_main_queue(),
|
||||||
|
context as *mut _,
|
||||||
|
set_content_size_callback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SetFrameTopLeftPointData {
|
||||||
|
nswindow: id,
|
||||||
|
point: NSPoint,
|
||||||
|
}
|
||||||
|
impl SetFrameTopLeftPointData {
|
||||||
|
fn new_ptr(
|
||||||
|
nswindow: id,
|
||||||
|
point: NSPoint,
|
||||||
|
) -> *mut Self {
|
||||||
|
Box::into_raw(Box::new(SetFrameTopLeftPointData { nswindow, point }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extern fn set_frame_top_left_point_callback(context: *mut c_void) {
|
||||||
|
unsafe {
|
||||||
|
let context_ptr = context as *mut SetFrameTopLeftPointData;
|
||||||
|
{
|
||||||
|
let context = &*context_ptr;
|
||||||
|
NSWindow::setFrameTopLeftPoint_(context.nswindow, context.point);
|
||||||
|
}
|
||||||
|
Box::from_raw(context_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// `setFrameTopLeftPoint:` isn't thread-safe, but fortunately has the courtesy
|
||||||
|
// to log errors.
|
||||||
|
pub unsafe fn set_frame_top_left_point_async(nswindow: id, point: NSPoint) {
|
||||||
|
let context = SetFrameTopLeftPointData::new_ptr(nswindow, point);
|
||||||
|
dispatch_async_f(
|
||||||
|
dispatch_get_main_queue(),
|
||||||
|
context as *mut _,
|
||||||
|
set_frame_top_left_point_callback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SetLevelData {
|
||||||
|
nswindow: id,
|
||||||
|
level: ffi::NSWindowLevel,
|
||||||
|
}
|
||||||
|
impl SetLevelData {
|
||||||
|
fn new_ptr(
|
||||||
|
nswindow: id,
|
||||||
|
level: ffi::NSWindowLevel,
|
||||||
|
) -> *mut Self {
|
||||||
|
Box::into_raw(Box::new(SetLevelData { nswindow, level }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extern fn set_level_callback(context: *mut c_void) {
|
||||||
|
unsafe {
|
||||||
|
let context_ptr = context as *mut SetLevelData;
|
||||||
|
{
|
||||||
|
let context = &*context_ptr;
|
||||||
|
context.nswindow.setLevel_(context.level as _);
|
||||||
|
}
|
||||||
|
Box::from_raw(context_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// `setFrameTopLeftPoint:` isn't thread-safe, and fails silently.
|
||||||
|
pub unsafe fn set_level_async(nswindow: id, level: ffi::NSWindowLevel) {
|
||||||
|
let context = SetLevelData::new_ptr(nswindow, level);
|
||||||
|
dispatch_async_f(
|
||||||
|
dispatch_get_main_queue(),
|
||||||
|
context as *mut _,
|
||||||
|
set_level_callback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ToggleFullScreenData {
|
||||||
|
nswindow: id,
|
||||||
|
nsview: id,
|
||||||
|
not_fullscreen: bool,
|
||||||
|
shared_state: Weak<Mutex<SharedState>>,
|
||||||
|
}
|
||||||
|
impl ToggleFullScreenData {
|
||||||
|
fn new_ptr(
|
||||||
|
nswindow: id,
|
||||||
|
nsview: id,
|
||||||
|
not_fullscreen: bool,
|
||||||
|
shared_state: Weak<Mutex<SharedState>>,
|
||||||
|
) -> *mut Self {
|
||||||
|
Box::into_raw(Box::new(ToggleFullScreenData {
|
||||||
|
nswindow,
|
||||||
|
nsview,
|
||||||
|
not_fullscreen,
|
||||||
|
shared_state,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extern fn toggle_full_screen_callback(context: *mut c_void) {
|
||||||
|
unsafe {
|
||||||
|
let context_ptr = context as *mut ToggleFullScreenData;
|
||||||
|
{
|
||||||
|
let context = &*context_ptr;
|
||||||
|
|
||||||
|
// `toggleFullScreen` doesn't work if the `StyleMask` is none, so we
|
||||||
|
// set a normal style temporarily. The previous state will be
|
||||||
|
// restored in `WindowDelegate::window_did_exit_fullscreen`.
|
||||||
|
if context.not_fullscreen {
|
||||||
|
let curr_mask = context.nswindow.styleMask();
|
||||||
|
let required = NSWindowStyleMask::NSTitledWindowMask
|
||||||
|
| NSWindowStyleMask::NSResizableWindowMask;
|
||||||
|
if !curr_mask.contains(required) {
|
||||||
|
set_style_mask(context.nswindow, context.nsview, required);
|
||||||
|
if let Some(shared_state) = context.shared_state.upgrade() {
|
||||||
|
trace!("Locked shared state in `toggle_full_screen_callback`");
|
||||||
|
let mut shared_state_lock = shared_state.lock().unwrap();
|
||||||
|
(*shared_state_lock).saved_style = Some(curr_mask);
|
||||||
|
trace!("Unlocked shared state in `toggle_full_screen_callback`");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.nswindow.toggleFullScreen_(nil);
|
||||||
|
}
|
||||||
|
Box::from_raw(context_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// `toggleFullScreen` is thread-safe, but our additional logic to account for
|
||||||
|
// window styles isn't.
|
||||||
|
pub unsafe fn toggle_full_screen_async(
|
||||||
|
nswindow: id,
|
||||||
|
nsview: id,
|
||||||
|
not_fullscreen: bool,
|
||||||
|
shared_state: Weak<Mutex<SharedState>>,
|
||||||
|
) {
|
||||||
|
let context = ToggleFullScreenData::new_ptr(
|
||||||
|
nswindow,
|
||||||
|
nsview,
|
||||||
|
not_fullscreen,
|
||||||
|
shared_state,
|
||||||
|
);
|
||||||
|
dispatch_async_f(
|
||||||
|
dispatch_get_main_queue(),
|
||||||
|
context as *mut _,
|
||||||
|
toggle_full_screen_callback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OrderOutData {
|
||||||
|
nswindow: id,
|
||||||
|
}
|
||||||
|
impl OrderOutData {
|
||||||
|
fn new_ptr(nswindow: id) -> *mut Self {
|
||||||
|
Box::into_raw(Box::new(OrderOutData { nswindow }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extern fn order_out_callback(context: *mut c_void) {
|
||||||
|
unsafe {
|
||||||
|
let context_ptr = context as *mut OrderOutData;
|
||||||
|
{
|
||||||
|
let context = &*context_ptr;
|
||||||
|
context.nswindow.orderOut_(nil);
|
||||||
|
}
|
||||||
|
Box::from_raw(context_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// `orderOut:` isn't thread-safe. Calling it from another thread actually works,
|
||||||
|
// but with an odd delay.
|
||||||
|
pub unsafe fn order_out_async(nswindow: id) {
|
||||||
|
let context = OrderOutData::new_ptr(nswindow);
|
||||||
|
dispatch_async_f(
|
||||||
|
dispatch_get_main_queue(),
|
||||||
|
context as *mut _,
|
||||||
|
order_out_callback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MakeKeyAndOrderFrontData {
|
||||||
|
nswindow: id,
|
||||||
|
}
|
||||||
|
impl MakeKeyAndOrderFrontData {
|
||||||
|
fn new_ptr(nswindow: id) -> *mut Self {
|
||||||
|
Box::into_raw(Box::new(MakeKeyAndOrderFrontData { nswindow }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extern fn make_key_and_order_front_callback(context: *mut c_void) {
|
||||||
|
unsafe {
|
||||||
|
let context_ptr = context as *mut MakeKeyAndOrderFrontData;
|
||||||
|
{
|
||||||
|
let context = &*context_ptr;
|
||||||
|
context.nswindow.makeKeyAndOrderFront_(nil);
|
||||||
|
}
|
||||||
|
Box::from_raw(context_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// `makeKeyAndOrderFront:` isn't thread-safe. Calling it from another thread
|
||||||
|
// actually works, but with an odd delay.
|
||||||
|
pub unsafe fn make_key_and_order_front_async(nswindow: id) {
|
||||||
|
let context = MakeKeyAndOrderFrontData::new_ptr(nswindow);
|
||||||
|
dispatch_async_f(
|
||||||
|
dispatch_get_main_queue(),
|
||||||
|
context as *mut _,
|
||||||
|
make_key_and_order_front_callback,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CloseData {
|
||||||
|
nswindow: id,
|
||||||
|
}
|
||||||
|
impl CloseData {
|
||||||
|
fn new_ptr(nswindow: id) -> *mut Self {
|
||||||
|
Box::into_raw(Box::new(CloseData { nswindow }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extern fn close_callback(context: *mut c_void) {
|
||||||
|
unsafe {
|
||||||
|
let context_ptr = context as *mut CloseData;
|
||||||
|
{
|
||||||
|
let context = &*context_ptr;
|
||||||
|
let pool = NSAutoreleasePool::new(nil);
|
||||||
|
context.nswindow.close();
|
||||||
|
pool.drain();
|
||||||
|
}
|
||||||
|
Box::from_raw(context_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// `close:` is thread-safe, but we want the event to be triggered from the main
|
||||||
|
// thread. Though, it's a good idea to look into that more...
|
||||||
|
pub unsafe fn close_async(nswindow: id) {
|
||||||
|
let context = CloseData::new_ptr(nswindow);
|
||||||
|
dispatch_async_f(
|
||||||
|
dispatch_get_main_queue(),
|
||||||
|
context as *mut _,
|
||||||
|
close_callback,
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,11 +1,10 @@
|
||||||
use cocoa::{
|
use cocoa::{
|
||||||
appkit::NSImage, base::{id, nil, YES},
|
appkit::NSImage, base::{id, nil},
|
||||||
foundation::{NSDictionary, NSPoint, NSString},
|
foundation::{NSDictionary, NSPoint, NSString},
|
||||||
};
|
};
|
||||||
use objc::runtime::Sel;
|
use objc::runtime::Sel;
|
||||||
|
|
||||||
use super::IntoOption;
|
use window::MouseCursor;
|
||||||
use MouseCursor;
|
|
||||||
|
|
||||||
pub enum Cursor {
|
pub enum Cursor {
|
||||||
Native(&'static str),
|
Native(&'static str),
|
||||||
|
@ -89,21 +88,17 @@ impl Cursor {
|
||||||
};
|
};
|
||||||
msg_send![class, performSelector:sel]
|
msg_send![class, performSelector:sel]
|
||||||
},
|
},
|
||||||
Cursor::WebKit(cursor_name) => load_webkit_cursor(cursor_name)
|
Cursor::WebKit(cursor_name) => load_webkit_cursor(cursor_name),
|
||||||
.unwrap_or_else(|message| {
|
|
||||||
warn!("{}", message);
|
|
||||||
Self::default().load()
|
|
||||||
}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that loading `busybutclickable` with this code won't animate the frames;
|
// Note that loading `busybutclickable` with this code won't animate the frames;
|
||||||
// instead you'll just get them all in a column.
|
// instead you'll just get them all in a column.
|
||||||
unsafe fn load_webkit_cursor(cursor_name_str: &str) -> Result<id, String> {
|
pub unsafe fn load_webkit_cursor(cursor_name: &str) -> id {
|
||||||
static CURSOR_ROOT: &'static str = "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors";
|
static CURSOR_ROOT: &'static str = "/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/HIServices.framework/Versions/A/Resources/cursors";
|
||||||
let cursor_root = NSString::alloc(nil).init_str(CURSOR_ROOT);
|
let cursor_root = NSString::alloc(nil).init_str(CURSOR_ROOT);
|
||||||
let cursor_name = NSString::alloc(nil).init_str(cursor_name_str);
|
let cursor_name = NSString::alloc(nil).init_str(cursor_name);
|
||||||
let cursor_pdf = NSString::alloc(nil).init_str("cursor.pdf");
|
let cursor_pdf = NSString::alloc(nil).init_str("cursor.pdf");
|
||||||
let cursor_plist = NSString::alloc(nil).init_str("info.plist");
|
let cursor_plist = NSString::alloc(nil).init_str("info.plist");
|
||||||
let key_x = NSString::alloc(nil).init_str("hotx");
|
let key_x = NSString::alloc(nil).init_str("hotx");
|
||||||
|
@ -119,20 +114,11 @@ unsafe fn load_webkit_cursor(cursor_name_str: &str) -> Result<id, String> {
|
||||||
stringByAppendingPathComponent:cursor_plist
|
stringByAppendingPathComponent:cursor_plist
|
||||||
];
|
];
|
||||||
|
|
||||||
let image = NSImage::alloc(nil)
|
let image = NSImage::alloc(nil).initByReferencingFile_(pdf_path);
|
||||||
.initByReferencingFile_(pdf_path)
|
let info = NSDictionary::dictionaryWithContentsOfFile_(
|
||||||
// This will probably never be `None`, since images are loaded lazily...
|
nil,
|
||||||
.into_option()
|
info_path,
|
||||||
// because of that, we need to check for validity.
|
);
|
||||||
.filter(|image| image.isValid() == YES)
|
|
||||||
.ok_or_else(||
|
|
||||||
format!("Failed to read image for `{}` cursor", cursor_name_str)
|
|
||||||
)?;
|
|
||||||
let info = NSDictionary::dictionaryWithContentsOfFile_(nil, info_path)
|
|
||||||
.into_option()
|
|
||||||
.ok_or_else(||
|
|
||||||
format!("Failed to read info for `{}` cursor", cursor_name_str)
|
|
||||||
)?;
|
|
||||||
let x = info.valueForKey_(key_x);
|
let x = info.valueForKey_(key_x);
|
||||||
let y = info.valueForKey_(key_y);
|
let y = info.valueForKey_(key_y);
|
||||||
let point = NSPoint::new(
|
let point = NSPoint::new(
|
||||||
|
@ -140,10 +126,8 @@ unsafe fn load_webkit_cursor(cursor_name_str: &str) -> Result<id, String> {
|
||||||
msg_send![y, doubleValue],
|
msg_send![y, doubleValue],
|
||||||
);
|
);
|
||||||
let cursor: id = msg_send![class!(NSCursor), alloc];
|
let cursor: id = msg_send![class!(NSCursor), alloc];
|
||||||
let cursor: id = msg_send![cursor, initWithImage:image hotSpot:point];
|
msg_send![cursor,
|
||||||
cursor
|
initWithImage:image
|
||||||
.into_option()
|
hotSpot:point
|
||||||
.ok_or_else(||
|
]
|
||||||
format!("Failed to initialize `{}` cursor", cursor_name_str)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,85 @@
|
||||||
|
mod async;
|
||||||
mod cursor;
|
mod cursor;
|
||||||
mod into_option;
|
|
||||||
|
|
||||||
pub use self::{cursor::Cursor, into_option::IntoOption};
|
pub use self::{async::*, cursor::*};
|
||||||
|
|
||||||
use cocoa::appkit::NSWindowStyleMask;
|
use std::ops::Deref;
|
||||||
use cocoa::base::{id, nil};
|
use std::ops::BitAnd;
|
||||||
use cocoa::foundation::{NSRect, NSUInteger};
|
|
||||||
|
use cocoa::{
|
||||||
|
appkit::{NSApp, NSWindowStyleMask},
|
||||||
|
base::{id, nil},
|
||||||
|
foundation::{NSAutoreleasePool, NSRect, NSUInteger},
|
||||||
|
};
|
||||||
use core_graphics::display::CGDisplay;
|
use core_graphics::display::CGDisplay;
|
||||||
use objc::runtime::{Class, Object};
|
use objc::runtime::{BOOL, Class, Object, Sel, YES};
|
||||||
|
|
||||||
use platform_impl::platform::ffi;
|
use platform_impl::platform::ffi;
|
||||||
use platform_impl::platform::window::IdRef;
|
|
||||||
|
// Replace with `!` once stable
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Never {}
|
||||||
|
|
||||||
|
pub fn has_flag<T>(bitset: T, flag: T) -> bool
|
||||||
|
where T:
|
||||||
|
Copy + PartialEq + BitAnd<T, Output = T>
|
||||||
|
{
|
||||||
|
bitset & flag == flag
|
||||||
|
}
|
||||||
|
|
||||||
pub const EMPTY_RANGE: ffi::NSRange = ffi::NSRange {
|
pub const EMPTY_RANGE: ffi::NSRange = ffi::NSRange {
|
||||||
location: ffi::NSNotFound as NSUInteger,
|
location: ffi::NSNotFound as NSUInteger,
|
||||||
length: 0,
|
length: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub struct IdRef(id);
|
||||||
|
|
||||||
|
impl IdRef {
|
||||||
|
pub fn new(inner: id) -> IdRef {
|
||||||
|
IdRef(inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn retain(inner: id) -> IdRef {
|
||||||
|
if inner != nil {
|
||||||
|
let () = unsafe { msg_send![inner, retain] };
|
||||||
|
}
|
||||||
|
IdRef(inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn non_nil(self) -> Option<IdRef> {
|
||||||
|
if self.0 == nil { None } else { Some(self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for IdRef {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.0 != nil {
|
||||||
|
unsafe {
|
||||||
|
let pool = NSAutoreleasePool::new(nil);
|
||||||
|
let () = msg_send![self.0, release];
|
||||||
|
pool.drain();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for IdRef {
|
||||||
|
type Target = id;
|
||||||
|
fn deref<'a>(&'a self) -> &'a id {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for IdRef {
|
||||||
|
fn clone(&self) -> IdRef {
|
||||||
|
if self.0 != nil {
|
||||||
|
let _: id = unsafe { msg_send![self.0, retain] };
|
||||||
|
}
|
||||||
|
IdRef(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// For consistency with other platforms, this will...
|
// For consistency with other platforms, this will...
|
||||||
// 1. translate the bottom-left window corner into the top-left window corner
|
// 1. translate the bottom-left window corner into the top-left window corner
|
||||||
// 2. translate the coordinate from a bottom-left origin coordinate system to a top-left one
|
// 2. translate the coordinate from a bottom-left origin coordinate system to a top-left one
|
||||||
|
@ -24,11 +87,24 @@ pub fn bottom_left_to_top_left(rect: NSRect) -> f64 {
|
||||||
CGDisplay::main().pixels_high() as f64 - (rect.origin.y + rect.size.height)
|
CGDisplay::main().pixels_high() as f64 - (rect.origin.y + rect.size.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn set_style_mask(window: id, view: id, mask: NSWindowStyleMask) {
|
pub unsafe fn superclass<'a>(this: &'a Object) -> &'a Class {
|
||||||
use cocoa::appkit::NSWindow;
|
let superclass: id = msg_send![this, superclass];
|
||||||
window.setStyleMask_(mask);
|
&*(superclass as *const _)
|
||||||
// If we don't do this, key handling will break. Therefore, never call `setStyleMask` directly!
|
}
|
||||||
window.makeFirstResponder_(view);
|
|
||||||
|
pub unsafe fn create_input_context(view: id) -> IdRef {
|
||||||
|
let input_context: id = msg_send![class!(NSTextInputContext), alloc];
|
||||||
|
let input_context: id = msg_send![input_context, initWithClient:view];
|
||||||
|
IdRef::new(input_context)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub unsafe fn open_emoji_picker() {
|
||||||
|
let () = msg_send![NSApp(), orderFrontCharacterPalette:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern fn yes(_: &Object, _: Sel) -> BOOL {
|
||||||
|
YES
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn toggle_style_mask(window: id, view: id, mask: NSWindowStyleMask, on: bool) {
|
pub unsafe fn toggle_style_mask(window: id, view: id, mask: NSWindowStyleMask, on: bool) {
|
||||||
|
@ -45,19 +121,3 @@ pub unsafe fn toggle_style_mask(window: id, view: id, mask: NSWindowStyleMask, o
|
||||||
window.makeFirstResponder_(view);
|
window.makeFirstResponder_(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn superclass<'a>(this: &'a Object) -> &'a Class {
|
|
||||||
let superclass: id = msg_send![this, superclass];
|
|
||||||
&*(superclass as *const _)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn create_input_context(view: id) -> IdRef {
|
|
||||||
let input_context: id = msg_send![class!(NSTextInputContext), alloc];
|
|
||||||
let input_context: id = msg_send![input_context, initWithClient:view];
|
|
||||||
IdRef::new(input_context)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub unsafe fn open_emoji_picker() {
|
|
||||||
let app: id = msg_send![class!(NSApplication), sharedApplication];
|
|
||||||
let _: () = msg_send![app, orderFrontCharacterPalette:nil];
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,66 +1,74 @@
|
||||||
// This is a pretty close port of the implementation in GLFW:
|
use std::{
|
||||||
// https://github.com/glfw/glfw/blob/7ef34eb06de54dd9186d3d21a401b2ef819b59e7/src/cocoa_window.m
|
boxed::Box, collections::VecDeque, os::raw::*, slice, str,
|
||||||
|
sync::{Arc, Mutex, Weak},
|
||||||
|
};
|
||||||
|
|
||||||
use std::{slice, str};
|
use cocoa::{
|
||||||
use std::boxed::Box;
|
appkit::{NSApp, NSEvent, NSEventModifierFlags, NSEventPhase, NSView, NSWindow},
|
||||||
use std::collections::VecDeque;
|
base::{id, nil}, foundation::{NSPoint, NSRect, NSSize, NSString, NSUInteger},
|
||||||
use std::os::raw::*;
|
};
|
||||||
use std::sync::{Arc, Mutex, Weak};
|
use objc::{declare::ClassDecl, runtime::{BOOL, Class, NO, Object, Protocol, Sel, YES}};
|
||||||
|
|
||||||
use cocoa::base::{id, nil};
|
use {
|
||||||
use cocoa::appkit::{NSEvent, NSView, NSWindow};
|
event::{
|
||||||
use cocoa::foundation::{NSPoint, NSRect, NSSize, NSString, NSUInteger};
|
DeviceEvent, ElementState, Event, KeyboardInput, MouseButton,
|
||||||
use objc::declare::ClassDecl;
|
MouseScrollDelta, TouchPhase, VirtualKeyCode, WindowEvent,
|
||||||
use objc::runtime::{Class, Object, Protocol, Sel, BOOL, YES};
|
},
|
||||||
|
window::WindowId,
|
||||||
|
};
|
||||||
|
use platform_impl::platform::{
|
||||||
|
app_state::AppState, DEVICE_ID,
|
||||||
|
event::{check_function_keys, event_mods, modifier_event, char_to_keycode, get_scancode, scancode_to_keycode},
|
||||||
|
util::{self, IdRef}, ffi::*, window::get_window_id,
|
||||||
|
};
|
||||||
|
|
||||||
use {ElementState, Event, KeyboardInput, MouseButton, WindowEvent, WindowId};
|
#[derive(Default)]
|
||||||
use platform_impl::platform::event_loop::{DEVICE_ID, event_mods, Shared, to_virtual_key_code, check_additional_virtual_key_codes, get_scancode};
|
struct Modifiers {
|
||||||
use platform_impl::platform::util;
|
shift_pressed: bool,
|
||||||
use platform_impl::platform::ffi::*;
|
ctrl_pressed: bool,
|
||||||
use platform_impl::platform::window::{get_window_id, IdRef};
|
win_pressed: bool,
|
||||||
use event;
|
alt_pressed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
struct ViewState {
|
struct ViewState {
|
||||||
window: id,
|
nswindow: id,
|
||||||
shared: Weak<Shared>,
|
pub cursor: Arc<Mutex<util::Cursor>>,
|
||||||
cursor: Arc<Mutex<util::Cursor>>,
|
|
||||||
ime_spot: Option<(f64, f64)>,
|
ime_spot: Option<(f64, f64)>,
|
||||||
raw_characters: Option<String>,
|
raw_characters: Option<String>,
|
||||||
is_key_down: bool,
|
is_key_down: bool,
|
||||||
|
modifiers: Modifiers,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_view(window: id, shared: Weak<Shared>) -> (IdRef, Weak<Mutex<util::Cursor>>) {
|
pub fn new_view(nswindow: id) -> (IdRef, Weak<Mutex<util::Cursor>>) {
|
||||||
let cursor = Default::default();
|
let cursor = Default::default();
|
||||||
let cursor_access = Arc::downgrade(&cursor);
|
let cursor_access = Arc::downgrade(&cursor);
|
||||||
let state = ViewState {
|
let state = ViewState {
|
||||||
window,
|
nswindow,
|
||||||
shared,
|
|
||||||
cursor,
|
cursor,
|
||||||
ime_spot: None,
|
ime_spot: None,
|
||||||
raw_characters: None,
|
raw_characters: None,
|
||||||
is_key_down: false,
|
is_key_down: false,
|
||||||
|
modifiers: Default::default(),
|
||||||
};
|
};
|
||||||
unsafe {
|
unsafe {
|
||||||
// This is free'd in `dealloc`
|
// This is free'd in `dealloc`
|
||||||
let state_ptr = Box::into_raw(Box::new(state)) as *mut c_void;
|
let state_ptr = Box::into_raw(Box::new(state)) as *mut c_void;
|
||||||
let view: id = msg_send![VIEW_CLASS.0, alloc];
|
let nsview: id = msg_send![VIEW_CLASS.0, alloc];
|
||||||
(IdRef::new(msg_send![view, initWithWinit:state_ptr]), cursor_access)
|
(IdRef::new(msg_send![nsview, initWithWinit:state_ptr]), cursor_access)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_ime_spot(view: id, input_context: id, x: f64, y: f64) {
|
pub unsafe fn set_ime_spot(nsview: id, input_context: id, x: f64, y: f64) {
|
||||||
unsafe {
|
let state_ptr: *mut c_void = *(*nsview).get_mut_ivar("winitState");
|
||||||
let state_ptr: *mut c_void = *(*view).get_mut_ivar("winitState");
|
let state = &mut *(state_ptr as *mut ViewState);
|
||||||
let state = &mut *(state_ptr as *mut ViewState);
|
let content_rect = NSWindow::contentRectForFrameRect_(
|
||||||
let content_rect = NSWindow::contentRectForFrameRect_(
|
state.nswindow,
|
||||||
state.window,
|
NSWindow::frame(state.nswindow),
|
||||||
NSWindow::frame(state.window),
|
);
|
||||||
);
|
let base_x = content_rect.origin.x as f64;
|
||||||
let base_x = content_rect.origin.x as f64;
|
let base_y = (content_rect.origin.y + content_rect.size.height) as f64;
|
||||||
let base_y = (content_rect.origin.y + content_rect.size.height) as f64;
|
state.ime_spot = Some((base_x + x, base_y - y));
|
||||||
state.ime_spot = Some((base_x + x, base_y - y));
|
let _: () = msg_send![input_context, invalidateCharacterCoordinates];
|
||||||
let _: () = msg_send![input_context, invalidateCharacterCoordinates];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ViewClass(*const Class);
|
struct ViewClass(*const Class);
|
||||||
|
@ -71,38 +79,61 @@ lazy_static! {
|
||||||
static ref VIEW_CLASS: ViewClass = unsafe {
|
static ref VIEW_CLASS: ViewClass = unsafe {
|
||||||
let superclass = class!(NSView);
|
let superclass = class!(NSView);
|
||||||
let mut decl = ClassDecl::new("WinitView", superclass).unwrap();
|
let mut decl = ClassDecl::new("WinitView", superclass).unwrap();
|
||||||
decl.add_method(sel!(dealloc), dealloc as extern fn(&Object, Sel));
|
decl.add_method(
|
||||||
|
sel!(dealloc),
|
||||||
|
dealloc as extern fn(&Object, Sel),
|
||||||
|
);
|
||||||
decl.add_method(
|
decl.add_method(
|
||||||
sel!(initWithWinit:),
|
sel!(initWithWinit:),
|
||||||
init_with_winit as extern fn(&Object, Sel, *mut c_void) -> id,
|
init_with_winit as extern fn(&Object, Sel, *mut c_void) -> id,
|
||||||
);
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(viewDidMoveToWindow),
|
||||||
|
view_did_move_to_window as extern fn(&Object, Sel),
|
||||||
|
);
|
||||||
decl.add_method(
|
decl.add_method(
|
||||||
sel!(drawRect:),
|
sel!(drawRect:),
|
||||||
draw_rect as extern fn(&Object, Sel, NSRect),
|
draw_rect as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(acceptsFirstResponder),
|
||||||
|
accepts_first_responder as extern fn(&Object, Sel) -> BOOL,
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(touchBar),
|
||||||
|
touch_bar as extern fn(&Object, Sel) -> BOOL,
|
||||||
);
|
);
|
||||||
decl.add_method(
|
decl.add_method(
|
||||||
sel!(resetCursorRects),
|
sel!(resetCursorRects),
|
||||||
reset_cursor_rects as extern fn(&Object, Sel),
|
reset_cursor_rects as extern fn(&Object, Sel),
|
||||||
);
|
);
|
||||||
decl.add_method(sel!(hasMarkedText), has_marked_text as extern fn(&Object, Sel) -> BOOL);
|
decl.add_method(
|
||||||
|
sel!(hasMarkedText),
|
||||||
|
has_marked_text as extern fn(&Object, Sel) -> BOOL,
|
||||||
|
);
|
||||||
decl.add_method(
|
decl.add_method(
|
||||||
sel!(markedRange),
|
sel!(markedRange),
|
||||||
marked_range as extern fn(&Object, Sel) -> NSRange,
|
marked_range as extern fn(&Object, Sel) -> NSRange,
|
||||||
);
|
);
|
||||||
decl.add_method(sel!(selectedRange), selected_range as extern fn(&Object, Sel) -> NSRange);
|
decl.add_method(
|
||||||
|
sel!(selectedRange),
|
||||||
|
selected_range as extern fn(&Object, Sel) -> NSRange,
|
||||||
|
);
|
||||||
decl.add_method(
|
decl.add_method(
|
||||||
sel!(setMarkedText:selectedRange:replacementRange:),
|
sel!(setMarkedText:selectedRange:replacementRange:),
|
||||||
set_marked_text as extern fn(&mut Object, Sel, id, NSRange, NSRange),
|
set_marked_text as extern fn(&mut Object, Sel, id, NSRange, NSRange),
|
||||||
);
|
);
|
||||||
decl.add_method(sel!(unmarkText), unmark_text as extern fn(&Object, Sel));
|
decl.add_method(
|
||||||
|
sel!(unmarkText),
|
||||||
|
unmark_text as extern fn(&Object, Sel),
|
||||||
|
);
|
||||||
decl.add_method(
|
decl.add_method(
|
||||||
sel!(validAttributesForMarkedText),
|
sel!(validAttributesForMarkedText),
|
||||||
valid_attributes_for_marked_text as extern fn(&Object, Sel) -> id,
|
valid_attributes_for_marked_text as extern fn(&Object, Sel) -> id,
|
||||||
);
|
);
|
||||||
decl.add_method(
|
decl.add_method(
|
||||||
sel!(attributedSubstringForProposedRange:actualRange:),
|
sel!(attributedSubstringForProposedRange:actualRange:),
|
||||||
attributed_substring_for_proposed_range
|
attributed_substring_for_proposed_range as extern fn(&Object, Sel, NSRange, *mut c_void) -> id,
|
||||||
as extern fn(&Object, Sel, NSRange, *mut c_void) -> id,
|
|
||||||
);
|
);
|
||||||
decl.add_method(
|
decl.add_method(
|
||||||
sel!(insertText:replacementRange:),
|
sel!(insertText:replacementRange:),
|
||||||
|
@ -114,28 +145,96 @@ lazy_static! {
|
||||||
);
|
);
|
||||||
decl.add_method(
|
decl.add_method(
|
||||||
sel!(firstRectForCharacterRange:actualRange:),
|
sel!(firstRectForCharacterRange:actualRange:),
|
||||||
first_rect_for_character_range
|
first_rect_for_character_range as extern fn(&Object, Sel, NSRange, *mut c_void) -> NSRect,
|
||||||
as extern fn(&Object, Sel, NSRange, *mut c_void) -> NSRect,
|
|
||||||
);
|
);
|
||||||
decl.add_method(
|
decl.add_method(
|
||||||
sel!(doCommandBySelector:),
|
sel!(doCommandBySelector:),
|
||||||
do_command_by_selector as extern fn(&Object, Sel, Sel),
|
do_command_by_selector as extern fn(&Object, Sel, Sel),
|
||||||
);
|
);
|
||||||
decl.add_method(sel!(keyDown:), key_down as extern fn(&Object, Sel, id));
|
decl.add_method(
|
||||||
decl.add_method(sel!(keyUp:), key_up as extern fn(&Object, Sel, id));
|
sel!(keyDown:),
|
||||||
decl.add_method(sel!(insertTab:), insert_tab as extern fn(&Object, Sel, id));
|
key_down as extern fn(&Object, Sel, id),
|
||||||
decl.add_method(sel!(insertBackTab:), insert_back_tab as extern fn(&Object, Sel, id));
|
);
|
||||||
decl.add_method(sel!(mouseDown:), mouse_down as extern fn(&Object, Sel, id));
|
decl.add_method(
|
||||||
decl.add_method(sel!(mouseUp:), mouse_up as extern fn(&Object, Sel, id));
|
sel!(keyUp:),
|
||||||
decl.add_method(sel!(rightMouseDown:), right_mouse_down as extern fn(&Object, Sel, id));
|
key_up as extern fn(&Object, Sel, id),
|
||||||
decl.add_method(sel!(rightMouseUp:), right_mouse_up as extern fn(&Object, Sel, id));
|
);
|
||||||
decl.add_method(sel!(otherMouseDown:), other_mouse_down as extern fn(&Object, Sel, id));
|
decl.add_method(
|
||||||
decl.add_method(sel!(otherMouseUp:), other_mouse_up as extern fn(&Object, Sel, id));
|
sel!(flagsChanged:),
|
||||||
decl.add_method(sel!(mouseMoved:), mouse_moved as extern fn(&Object, Sel, id));
|
flags_changed as extern fn(&Object, Sel, id),
|
||||||
decl.add_method(sel!(mouseDragged:), mouse_dragged as extern fn(&Object, Sel, id));
|
);
|
||||||
decl.add_method(sel!(rightMouseDragged:), right_mouse_dragged as extern fn(&Object, Sel, id));
|
decl.add_method(
|
||||||
decl.add_method(sel!(otherMouseDragged:), other_mouse_dragged as extern fn(&Object, Sel, id));
|
sel!(insertTab:),
|
||||||
decl.add_method(sel!(_wantsKeyDownForEvent:), wants_key_down_for_event as extern fn(&Object, Sel, id) -> BOOL);
|
insert_tab as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(insertBackTab:),
|
||||||
|
insert_back_tab as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(mouseDown:),
|
||||||
|
mouse_down as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(mouseUp:),
|
||||||
|
mouse_up as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(rightMouseDown:),
|
||||||
|
right_mouse_down as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(rightMouseUp:),
|
||||||
|
right_mouse_up as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(otherMouseDown:),
|
||||||
|
other_mouse_down as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(otherMouseUp:),
|
||||||
|
other_mouse_up as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(mouseMoved:),
|
||||||
|
mouse_moved as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(mouseDragged:),
|
||||||
|
mouse_dragged as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(rightMouseDragged:),
|
||||||
|
right_mouse_dragged as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(otherMouseDragged:),
|
||||||
|
other_mouse_dragged as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(mouseEntered:),
|
||||||
|
mouse_entered as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(mouseExited:),
|
||||||
|
mouse_exited as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(scrollWheel:),
|
||||||
|
scroll_wheel as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(pressureChangeWithEvent:),
|
||||||
|
pressure_change_with_event as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(_wantsKeyDownForEvent:),
|
||||||
|
wants_key_down_for_event as extern fn(&Object, Sel, id) -> BOOL,
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(cancelOperation:),
|
||||||
|
cancel_operation as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
decl.add_ivar::<*mut c_void>("winitState");
|
decl.add_ivar::<*mut c_void>("winitState");
|
||||||
decl.add_ivar::<id>("markedText");
|
decl.add_ivar::<id>("markedText");
|
||||||
let protocol = Protocol::get("NSTextInputClient").unwrap();
|
let protocol = Protocol::get("NSTextInputClient").unwrap();
|
||||||
|
@ -167,27 +266,43 @@ extern fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> id {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn draw_rect(this: &Object, _sel: Sel, rect: NSRect) {
|
extern fn view_did_move_to_window(this: &Object, _sel: Sel) {
|
||||||
|
trace!("Triggered `viewDidMoveToWindow`");
|
||||||
|
unsafe {
|
||||||
|
let rect: NSRect = msg_send![this, visibleRect];
|
||||||
|
let _: () = msg_send![this,
|
||||||
|
addTrackingRect:rect
|
||||||
|
owner:this
|
||||||
|
userData:nil
|
||||||
|
assumeInside:NO
|
||||||
|
];
|
||||||
|
}
|
||||||
|
trace!("Completed `viewDidMoveToWindow`");
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn draw_rect(this: &Object, _sel: Sel, rect: id) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state_ptr as *mut ViewState);
|
let state = &mut *(state_ptr as *mut ViewState);
|
||||||
|
|
||||||
if let Some(shared) = state.shared.upgrade() {
|
AppState::queue_redraw(WindowId(get_window_id(state.nswindow)));
|
||||||
let window_event = Event::WindowEvent {
|
|
||||||
window_id: WindowId(get_window_id(state.window)),
|
|
||||||
event: WindowEvent::Refresh,
|
|
||||||
};
|
|
||||||
shared.pending_events
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.push_back(window_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
let superclass = util::superclass(this);
|
let superclass = util::superclass(this);
|
||||||
let () = msg_send![super(this, superclass), drawRect:rect];
|
let () = msg_send![super(this, superclass), drawRect:rect];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern fn accepts_first_responder(_this: &Object, _sel: Sel) -> BOOL {
|
||||||
|
YES
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is necessary to prevent a beefy terminal error on MacBook Pros:
|
||||||
|
// IMKInputSession [0x7fc573576ff0 presentFunctionRowItemTextInputViewWithEndpoint:completionHandler:] : [self textInputContext]=0x7fc573558e10 *NO* NSRemoteViewController to client, NSError=Error Domain=NSCocoaErrorDomain Code=4099 "The connection from pid 0 was invalidated from this process." UserInfo={NSDebugDescription=The connection from pid 0 was invalidated from this process.}, com.apple.inputmethod.EmojiFunctionRowItem
|
||||||
|
// TODO: Add an API extension for using `NSTouchBar`
|
||||||
|
extern fn touch_bar(_this: &Object, _sel: Sel) -> BOOL {
|
||||||
|
NO
|
||||||
|
}
|
||||||
|
|
||||||
extern fn reset_cursor_rects(this: &Object, _sel: Sel) {
|
extern fn reset_cursor_rects(this: &Object, _sel: Sel) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||||
|
@ -202,19 +317,22 @@ extern fn reset_cursor_rects(this: &Object, _sel: Sel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
extern fn has_marked_text(this: &Object, _sel: Sel) -> BOOL {
|
extern fn has_marked_text(this: &Object, _sel: Sel) -> BOOL {
|
||||||
//println!("hasMarkedText");
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
trace!("Triggered `hasMarkedText`");
|
||||||
let marked_text: id = *this.get_ivar("markedText");
|
let marked_text: id = *this.get_ivar("markedText");
|
||||||
|
trace!("Completed `hasMarkedText`");
|
||||||
(marked_text.length() > 0) as i8
|
(marked_text.length() > 0) as i8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn marked_range(this: &Object, _sel: Sel) -> NSRange {
|
extern fn marked_range(this: &Object, _sel: Sel) -> NSRange {
|
||||||
//println!("markedRange");
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
trace!("Triggered `markedRange`");
|
||||||
let marked_text: id = *this.get_ivar("markedText");
|
let marked_text: id = *this.get_ivar("markedText");
|
||||||
let length = marked_text.length();
|
let length = marked_text.length();
|
||||||
|
trace!("Completed `markedRange`");
|
||||||
if length > 0 {
|
if length > 0 {
|
||||||
NSRange::new(0, length - 1)
|
NSRange::new(0, length - 1)
|
||||||
} else {
|
} else {
|
||||||
|
@ -224,7 +342,8 @@ extern fn marked_range(this: &Object, _sel: Sel) -> NSRange {
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn selected_range(_this: &Object, _sel: Sel) -> NSRange {
|
extern fn selected_range(_this: &Object, _sel: Sel) -> NSRange {
|
||||||
//println!("selectedRange");
|
trace!("Triggered `selectedRange`");
|
||||||
|
trace!("Completed `selectedRange`");
|
||||||
util::EMPTY_RANGE
|
util::EMPTY_RANGE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,7 +354,7 @@ extern fn set_marked_text(
|
||||||
_selected_range: NSRange,
|
_selected_range: NSRange,
|
||||||
_replacement_range: NSRange,
|
_replacement_range: NSRange,
|
||||||
) {
|
) {
|
||||||
//println!("setMarkedText");
|
trace!("Triggered `setMarkedText`");
|
||||||
unsafe {
|
unsafe {
|
||||||
let marked_text_ref: &mut id = this.get_mut_ivar("markedText");
|
let marked_text_ref: &mut id = this.get_mut_ivar("markedText");
|
||||||
let _: () = msg_send![(*marked_text_ref), release];
|
let _: () = msg_send![(*marked_text_ref), release];
|
||||||
|
@ -248,10 +367,11 @@ extern fn set_marked_text(
|
||||||
};
|
};
|
||||||
*marked_text_ref = marked_text;
|
*marked_text_ref = marked_text;
|
||||||
}
|
}
|
||||||
|
trace!("Completed `setMarkedText`");
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn unmark_text(this: &Object, _sel: Sel) {
|
extern fn unmark_text(this: &Object, _sel: Sel) {
|
||||||
//println!("unmarkText");
|
trace!("Triggered `unmarkText`");
|
||||||
unsafe {
|
unsafe {
|
||||||
let marked_text: id = *this.get_ivar("markedText");
|
let marked_text: id = *this.get_ivar("markedText");
|
||||||
let mutable_string = marked_text.mutableString();
|
let mutable_string = marked_text.mutableString();
|
||||||
|
@ -259,10 +379,12 @@ extern fn unmark_text(this: &Object, _sel: Sel) {
|
||||||
let input_context: id = msg_send![this, inputContext];
|
let input_context: id = msg_send![this, inputContext];
|
||||||
let _: () = msg_send![input_context, discardMarkedText];
|
let _: () = msg_send![input_context, discardMarkedText];
|
||||||
}
|
}
|
||||||
|
trace!("Completed `unmarkText`");
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn valid_attributes_for_marked_text(_this: &Object, _sel: Sel) -> id {
|
extern fn valid_attributes_for_marked_text(_this: &Object, _sel: Sel) -> id {
|
||||||
//println!("validAttributesForMarkedText");
|
trace!("Triggered `validAttributesForMarkedText`");
|
||||||
|
trace!("Completed `validAttributesForMarkedText`");
|
||||||
unsafe { msg_send![class!(NSArray), array] }
|
unsafe { msg_send![class!(NSArray), array] }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,12 +394,14 @@ extern fn attributed_substring_for_proposed_range(
|
||||||
_range: NSRange,
|
_range: NSRange,
|
||||||
_actual_range: *mut c_void, // *mut NSRange
|
_actual_range: *mut c_void, // *mut NSRange
|
||||||
) -> id {
|
) -> id {
|
||||||
//println!("attributedSubstringForProposedRange");
|
trace!("Triggered `attributedSubstringForProposedRange`");
|
||||||
|
trace!("Completed `attributedSubstringForProposedRange`");
|
||||||
nil
|
nil
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn character_index_for_point(_this: &Object, _sel: Sel, _point: NSPoint) -> NSUInteger {
|
extern fn character_index_for_point(_this: &Object, _sel: Sel, _point: NSPoint) -> NSUInteger {
|
||||||
//println!("characterIndexForPoint");
|
trace!("Triggered `characterIndexForPoint`");
|
||||||
|
trace!("Completed `characterIndexForPoint`");
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,20 +411,20 @@ extern fn first_rect_for_character_range(
|
||||||
_range: NSRange,
|
_range: NSRange,
|
||||||
_actual_range: *mut c_void, // *mut NSRange
|
_actual_range: *mut c_void, // *mut NSRange
|
||||||
) -> NSRect {
|
) -> NSRect {
|
||||||
//println!("firstRectForCharacterRange");
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
trace!("Triggered `firstRectForCharacterRange`");
|
||||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state_ptr as *mut ViewState);
|
let state = &mut *(state_ptr as *mut ViewState);
|
||||||
let (x, y) = state.ime_spot.unwrap_or_else(|| {
|
let (x, y) = state.ime_spot.unwrap_or_else(|| {
|
||||||
let content_rect = NSWindow::contentRectForFrameRect_(
|
let content_rect = NSWindow::contentRectForFrameRect_(
|
||||||
state.window,
|
state.nswindow,
|
||||||
NSWindow::frame(state.window),
|
NSWindow::frame(state.nswindow),
|
||||||
);
|
);
|
||||||
let x = content_rect.origin.x;
|
let x = content_rect.origin.x;
|
||||||
let y = util::bottom_left_to_top_left(content_rect);
|
let y = util::bottom_left_to_top_left(content_rect);
|
||||||
(x, y)
|
(x, y)
|
||||||
});
|
});
|
||||||
|
trace!("Completed `firstRectForCharacterRange`");
|
||||||
NSRect::new(
|
NSRect::new(
|
||||||
NSPoint::new(x as _, y as _),
|
NSPoint::new(x as _, y as _),
|
||||||
NSSize::new(0.0, 0.0),
|
NSSize::new(0.0, 0.0),
|
||||||
|
@ -309,7 +433,7 @@ extern fn first_rect_for_character_range(
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range: NSRange) {
|
extern fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range: NSRange) {
|
||||||
//println!("insertText");
|
trace!("Triggered `insertText`");
|
||||||
unsafe {
|
unsafe {
|
||||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state_ptr as *mut ViewState);
|
let state = &mut *(state_ptr as *mut ViewState);
|
||||||
|
@ -331,46 +455,36 @@ extern fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range:
|
||||||
state.is_key_down = true;
|
state.is_key_down = true;
|
||||||
|
|
||||||
// We don't need this now, but it's here if that changes.
|
// We don't need this now, but it's here if that changes.
|
||||||
//let event: id = msg_send![class!(NSApp), currentEvent];
|
//let event: id = msg_send![NSApp(), currentEvent];
|
||||||
|
|
||||||
let mut events = VecDeque::with_capacity(characters.len());
|
let mut events = VecDeque::with_capacity(characters.len());
|
||||||
for character in string.chars() {
|
for character in string.chars() {
|
||||||
events.push_back(Event::WindowEvent {
|
events.push_back(Event::WindowEvent {
|
||||||
window_id: WindowId(get_window_id(state.window)),
|
window_id: WindowId(get_window_id(state.nswindow)),
|
||||||
event: WindowEvent::ReceivedCharacter(character),
|
event: WindowEvent::ReceivedCharacter(character),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(shared) = state.shared.upgrade() {
|
AppState::queue_events(events);
|
||||||
shared.pending_events
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.append(&mut events);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
trace!("Completed `insertText`");
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
|
extern fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
|
||||||
//println!("doCommandBySelector");
|
trace!("Triggered `doCommandBySelector`");
|
||||||
// Basically, we're sent this message whenever a keyboard event that doesn't generate a "human readable" character
|
// Basically, we're sent this message whenever a keyboard event that doesn't generate a "human readable" character
|
||||||
// happens, i.e. newlines, tabs, and Ctrl+C.
|
// happens, i.e. newlines, tabs, and Ctrl+C.
|
||||||
unsafe {
|
unsafe {
|
||||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state_ptr as *mut ViewState);
|
let state = &mut *(state_ptr as *mut ViewState);
|
||||||
|
|
||||||
let shared = if let Some(shared) = state.shared.upgrade() {
|
|
||||||
shared
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut events = VecDeque::with_capacity(1);
|
let mut events = VecDeque::with_capacity(1);
|
||||||
if command == sel!(insertNewline:) {
|
if command == sel!(insertNewline:) {
|
||||||
// The `else` condition would emit the same character, but I'm keeping this here both...
|
// The `else` condition would emit the same character, but I'm keeping this here both...
|
||||||
// 1) as a reminder for how `doCommandBySelector` works
|
// 1) as a reminder for how `doCommandBySelector` works
|
||||||
// 2) to make our use of carriage return explicit
|
// 2) to make our use of carriage return explicit
|
||||||
events.push_back(Event::WindowEvent {
|
events.push_back(Event::WindowEvent {
|
||||||
window_id: WindowId(get_window_id(state.window)),
|
window_id: WindowId(get_window_id(state.nswindow)),
|
||||||
event: WindowEvent::ReceivedCharacter('\r'),
|
event: WindowEvent::ReceivedCharacter('\r'),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -378,18 +492,16 @@ extern fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
|
||||||
if let Some(raw_characters) = raw_characters {
|
if let Some(raw_characters) = raw_characters {
|
||||||
for character in raw_characters.chars() {
|
for character in raw_characters.chars() {
|
||||||
events.push_back(Event::WindowEvent {
|
events.push_back(Event::WindowEvent {
|
||||||
window_id: WindowId(get_window_id(state.window)),
|
window_id: WindowId(get_window_id(state.nswindow)),
|
||||||
event: WindowEvent::ReceivedCharacter(character),
|
event: WindowEvent::ReceivedCharacter(character),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
shared.pending_events
|
AppState::queue_events(events);
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.append(&mut events);
|
|
||||||
}
|
}
|
||||||
|
trace!("Completed `doCommandBySelector`");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_characters(event: id, ignore_modifiers: bool) -> String {
|
fn get_characters(event: id, ignore_modifiers: bool) -> String {
|
||||||
|
@ -407,15 +519,14 @@ fn get_characters(event: id, ignore_modifiers: bool) -> String {
|
||||||
);
|
);
|
||||||
|
|
||||||
let string = str::from_utf8_unchecked(slice);
|
let string = str::from_utf8_unchecked(slice);
|
||||||
|
|
||||||
string.to_owned()
|
string.to_owned()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieves a layout-independent keycode given an event.
|
// Retrieves a layout-independent keycode given an event.
|
||||||
fn retrieve_keycode(event: id) -> Option<event::VirtualKeyCode> {
|
fn retrieve_keycode(event: id) -> Option<VirtualKeyCode> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get_code(ev: id, raw: bool) -> Option<event::VirtualKeyCode> {
|
fn get_code(ev: id, raw: bool) -> Option<VirtualKeyCode> {
|
||||||
let characters = get_characters(ev, raw);
|
let characters = get_characters(ev, raw);
|
||||||
characters.chars().next().map_or(None, |c| char_to_keycode(c))
|
characters.chars().next().map_or(None, |c| char_to_keycode(c))
|
||||||
}
|
}
|
||||||
|
@ -443,17 +554,18 @@ fn retrieve_keycode(event: id) -> Option<event::VirtualKeyCode> {
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn key_down(this: &Object, _sel: Sel, event: id) {
|
extern fn key_down(this: &Object, _sel: Sel, event: id) {
|
||||||
//println!("keyDown");
|
trace!("Triggered `keyDown`");
|
||||||
unsafe {
|
unsafe {
|
||||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state_ptr as *mut ViewState);
|
let state = &mut *(state_ptr as *mut ViewState);
|
||||||
let window_id = WindowId(get_window_id(state.window));
|
let window_id = WindowId(get_window_id(state.nswindow));
|
||||||
let characters = get_characters(event, false);
|
let characters = get_characters(event, false);
|
||||||
|
|
||||||
state.raw_characters = Some(characters.clone());
|
state.raw_characters = Some(characters.clone());
|
||||||
|
|
||||||
let scancode = get_scancode(event) as u32;
|
let scancode = get_scancode(event) as u32;
|
||||||
let virtual_keycode = retrieve_keycode(event);
|
let virtual_keycode = retrieve_keycode(event);
|
||||||
|
|
||||||
let is_repeat = msg_send![event, isARepeat];
|
let is_repeat = msg_send![event, isARepeat];
|
||||||
|
|
||||||
let window_event = Event::WindowEvent {
|
let window_event = Event::WindowEvent {
|
||||||
|
@ -469,36 +581,35 @@ extern fn key_down(this: &Object, _sel: Sel, event: id) {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(shared) = state.shared.upgrade() {
|
let pass_along = {
|
||||||
shared.pending_events
|
AppState::queue_event(window_event);
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.push_back(window_event);
|
|
||||||
// Emit `ReceivedCharacter` for key repeats
|
// Emit `ReceivedCharacter` for key repeats
|
||||||
if is_repeat && state.is_key_down{
|
if is_repeat && state.is_key_down {
|
||||||
for character in characters.chars() {
|
for character in characters.chars() {
|
||||||
let window_event = Event::WindowEvent {
|
AppState::queue_event(Event::WindowEvent {
|
||||||
window_id,
|
window_id,
|
||||||
event: WindowEvent::ReceivedCharacter(character),
|
event: WindowEvent::ReceivedCharacter(character),
|
||||||
};
|
});
|
||||||
shared.pending_events
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.push_back(window_event);
|
|
||||||
}
|
}
|
||||||
|
false
|
||||||
} else {
|
} else {
|
||||||
// Some keys (and only *some*, with no known reason) don't trigger `insertText`, while others do...
|
true
|
||||||
// So, we don't give repeats the opportunity to trigger that, since otherwise our hack will cause some
|
|
||||||
// keys to generate twice as many characters.
|
|
||||||
let array: id = msg_send![class!(NSArray), arrayWithObject:event];
|
|
||||||
let (): _ = msg_send![this, interpretKeyEvents:array];
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if pass_along {
|
||||||
|
// Some keys (and only *some*, with no known reason) don't trigger `insertText`, while others do...
|
||||||
|
// So, we don't give repeats the opportunity to trigger that, since otherwise our hack will cause some
|
||||||
|
// keys to generate twice as many characters.
|
||||||
|
let array: id = msg_send![class!(NSArray), arrayWithObject:event];
|
||||||
|
let _: () = msg_send![this, interpretKeyEvents:array];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
trace!("Completed `keyDown`");
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn key_up(this: &Object, _sel: Sel, event: id) {
|
extern fn key_up(this: &Object, _sel: Sel, event: id) {
|
||||||
//println!("keyUp");
|
trace!("Triggered `keyUp`");
|
||||||
unsafe {
|
unsafe {
|
||||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state_ptr as *mut ViewState);
|
let state = &mut *(state_ptr as *mut ViewState);
|
||||||
|
@ -509,7 +620,7 @@ extern fn key_up(this: &Object, _sel: Sel, event: id) {
|
||||||
let virtual_keycode = retrieve_keycode(event);
|
let virtual_keycode = retrieve_keycode(event);
|
||||||
|
|
||||||
let window_event = Event::WindowEvent {
|
let window_event = Event::WindowEvent {
|
||||||
window_id: WindowId(get_window_id(state.window)),
|
window_id: WindowId(get_window_id(state.nswindow)),
|
||||||
event: WindowEvent::KeyboardInput {
|
event: WindowEvent::KeyboardInput {
|
||||||
device_id: DEVICE_ID,
|
device_id: DEVICE_ID,
|
||||||
input: KeyboardInput {
|
input: KeyboardInput {
|
||||||
|
@ -521,13 +632,63 @@ extern fn key_up(this: &Object, _sel: Sel, event: id) {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(shared) = state.shared.upgrade() {
|
AppState::queue_event(window_event);
|
||||||
shared.pending_events
|
}
|
||||||
.lock()
|
trace!("Completed `keyUp`");
|
||||||
.unwrap()
|
}
|
||||||
.push_back(window_event);
|
|
||||||
|
extern fn flags_changed(this: &Object, _sel: Sel, event: id) {
|
||||||
|
trace!("Triggered `flagsChanged`");
|
||||||
|
unsafe {
|
||||||
|
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||||
|
let state = &mut *(state_ptr as *mut ViewState);
|
||||||
|
|
||||||
|
let mut events = VecDeque::with_capacity(4);
|
||||||
|
|
||||||
|
if let Some(window_event) = modifier_event(
|
||||||
|
event,
|
||||||
|
NSEventModifierFlags::NSShiftKeyMask,
|
||||||
|
state.modifiers.shift_pressed,
|
||||||
|
) {
|
||||||
|
state.modifiers.shift_pressed = !state.modifiers.shift_pressed;
|
||||||
|
events.push_back(window_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(window_event) = modifier_event(
|
||||||
|
event,
|
||||||
|
NSEventModifierFlags::NSControlKeyMask,
|
||||||
|
state.modifiers.ctrl_pressed,
|
||||||
|
) {
|
||||||
|
state.modifiers.ctrl_pressed = !state.modifiers.ctrl_pressed;
|
||||||
|
events.push_back(window_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(window_event) = modifier_event(
|
||||||
|
event,
|
||||||
|
NSEventModifierFlags::NSCommandKeyMask,
|
||||||
|
state.modifiers.win_pressed,
|
||||||
|
) {
|
||||||
|
state.modifiers.win_pressed = !state.modifiers.win_pressed;
|
||||||
|
events.push_back(window_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(window_event) = modifier_event(
|
||||||
|
event,
|
||||||
|
NSEventModifierFlags::NSAlternateKeyMask,
|
||||||
|
state.modifiers.alt_pressed,
|
||||||
|
) {
|
||||||
|
state.modifiers.alt_pressed = !state.modifiers.alt_pressed;
|
||||||
|
events.push_back(window_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
for event in events {
|
||||||
|
AppState::queue_event(Event::WindowEvent {
|
||||||
|
window_id: WindowId(get_window_id(state.nswindow)),
|
||||||
|
event,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
trace!("Completed `flagsChanged`");
|
||||||
}
|
}
|
||||||
|
|
||||||
extern fn insert_tab(this: &Object, _sel: Sel, _sender: id) {
|
extern fn insert_tab(this: &Object, _sel: Sel, _sender: id) {
|
||||||
|
@ -552,13 +713,45 @@ extern fn insert_back_tab(this: &Object, _sel: Sel, _sender: id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Allows us to receive Cmd-. (the shortcut for closing a dialog)
|
||||||
|
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=300620#c6
|
||||||
|
extern fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
|
||||||
|
trace!("Triggered `cancelOperation`");
|
||||||
|
unsafe {
|
||||||
|
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||||
|
let state = &mut *(state_ptr as *mut ViewState);
|
||||||
|
|
||||||
|
let scancode = 0x2f;
|
||||||
|
let virtual_keycode = scancode_to_keycode(scancode);
|
||||||
|
debug_assert_eq!(virtual_keycode, Some(VirtualKeyCode::Period));
|
||||||
|
|
||||||
|
let event: id = msg_send![NSApp(), currentEvent];
|
||||||
|
|
||||||
|
let window_event = Event::WindowEvent {
|
||||||
|
window_id: WindowId(get_window_id(state.nswindow)),
|
||||||
|
event: WindowEvent::KeyboardInput {
|
||||||
|
device_id: DEVICE_ID,
|
||||||
|
input: KeyboardInput {
|
||||||
|
state: ElementState::Pressed,
|
||||||
|
scancode: scancode as _,
|
||||||
|
virtual_keycode,
|
||||||
|
modifiers: event_mods(event),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
AppState::queue_event(window_event);
|
||||||
|
}
|
||||||
|
trace!("Completed `cancelOperation`");
|
||||||
|
}
|
||||||
|
|
||||||
fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: ElementState) {
|
fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: ElementState) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||||
let state = &mut *(state_ptr as *mut ViewState);
|
let state = &mut *(state_ptr as *mut ViewState);
|
||||||
|
|
||||||
let window_event = Event::WindowEvent {
|
let window_event = Event::WindowEvent {
|
||||||
window_id: WindowId(get_window_id(state.window)),
|
window_id: WindowId(get_window_id(state.nswindow)),
|
||||||
event: WindowEvent::MouseInput {
|
event: WindowEvent::MouseInput {
|
||||||
device_id: DEVICE_ID,
|
device_id: DEVICE_ID,
|
||||||
state: button_state,
|
state: button_state,
|
||||||
|
@ -567,12 +760,7 @@ fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: Elem
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(shared) = state.shared.upgrade() {
|
AppState::queue_event(window_event);
|
||||||
shared.pending_events
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.push_back(window_event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -624,7 +812,7 @@ fn mouse_motion(this: &Object, event: id) {
|
||||||
let y = view_rect.size.height as f64 - view_point.y as f64;
|
let y = view_rect.size.height as f64 - view_point.y as f64;
|
||||||
|
|
||||||
let window_event = Event::WindowEvent {
|
let window_event = Event::WindowEvent {
|
||||||
window_id: WindowId(get_window_id(state.window)),
|
window_id: WindowId(get_window_id(state.nswindow)),
|
||||||
event: WindowEvent::CursorMoved {
|
event: WindowEvent::CursorMoved {
|
||||||
device_id: DEVICE_ID,
|
device_id: DEVICE_ID,
|
||||||
position: (x, y).into(),
|
position: (x, y).into(),
|
||||||
|
@ -632,12 +820,7 @@ fn mouse_motion(this: &Object, event: id) {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(shared) = state.shared.upgrade() {
|
AppState::queue_event(window_event);
|
||||||
shared.pending_events
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.push_back(window_event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -657,7 +840,125 @@ extern fn other_mouse_dragged(this: &Object, _sel: Sel, event: id) {
|
||||||
mouse_motion(this, event);
|
mouse_motion(this, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern fn mouse_entered(this: &Object, _sel: Sel, event: id) {
|
||||||
|
trace!("Triggered `mouseEntered`");
|
||||||
|
unsafe {
|
||||||
|
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||||
|
let state = &mut *(state_ptr as *mut ViewState);
|
||||||
|
|
||||||
|
let enter_event = Event::WindowEvent {
|
||||||
|
window_id: WindowId(get_window_id(state.nswindow)),
|
||||||
|
event: WindowEvent::CursorEntered { device_id: DEVICE_ID },
|
||||||
|
};
|
||||||
|
|
||||||
|
let move_event = {
|
||||||
|
let window_point = event.locationInWindow();
|
||||||
|
let view_point: NSPoint = msg_send![this,
|
||||||
|
convertPoint:window_point
|
||||||
|
fromView:nil // convert from window coordinates
|
||||||
|
];
|
||||||
|
let view_rect: NSRect = msg_send![this, frame];
|
||||||
|
let x = view_point.x as f64;
|
||||||
|
let y = (view_rect.size.height - view_point.y) as f64;
|
||||||
|
Event::WindowEvent {
|
||||||
|
window_id: WindowId(get_window_id(state.nswindow)),
|
||||||
|
event: WindowEvent::CursorMoved {
|
||||||
|
device_id: DEVICE_ID,
|
||||||
|
position: (x, y).into(),
|
||||||
|
modifiers: event_mods(event),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
AppState::queue_event(enter_event);
|
||||||
|
AppState::queue_event(move_event);
|
||||||
|
}
|
||||||
|
trace!("Completed `mouseEntered`");
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn mouse_exited(this: &Object, _sel: Sel, _event: id) {
|
||||||
|
trace!("Triggered `mouseExited`");
|
||||||
|
unsafe {
|
||||||
|
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||||
|
let state = &mut *(state_ptr as *mut ViewState);
|
||||||
|
|
||||||
|
let window_event = Event::WindowEvent {
|
||||||
|
window_id: WindowId(get_window_id(state.nswindow)),
|
||||||
|
event: WindowEvent::CursorLeft { device_id: DEVICE_ID },
|
||||||
|
};
|
||||||
|
|
||||||
|
AppState::queue_event(window_event);
|
||||||
|
}
|
||||||
|
trace!("Completed `mouseExited`");
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
|
||||||
|
trace!("Triggered `scrollWheel`");
|
||||||
|
unsafe {
|
||||||
|
let delta = {
|
||||||
|
let (x, y) = (event.scrollingDeltaX(), event.scrollingDeltaY());
|
||||||
|
if event.hasPreciseScrollingDeltas() == YES {
|
||||||
|
MouseScrollDelta::PixelDelta((x as f64, y as f64).into())
|
||||||
|
} else {
|
||||||
|
MouseScrollDelta::LineDelta(x as f32, y as f32)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let phase = match event.phase() {
|
||||||
|
NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => TouchPhase::Started,
|
||||||
|
NSEventPhase::NSEventPhaseEnded => TouchPhase::Ended,
|
||||||
|
_ => TouchPhase::Moved,
|
||||||
|
};
|
||||||
|
|
||||||
|
let device_event = Event::DeviceEvent {
|
||||||
|
device_id: DEVICE_ID,
|
||||||
|
event: DeviceEvent::MouseWheel { delta },
|
||||||
|
};
|
||||||
|
|
||||||
|
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||||
|
let state = &mut *(state_ptr as *mut ViewState);
|
||||||
|
|
||||||
|
let window_event = Event::WindowEvent {
|
||||||
|
window_id: WindowId(get_window_id(state.nswindow)),
|
||||||
|
event: WindowEvent::MouseWheel {
|
||||||
|
device_id: DEVICE_ID,
|
||||||
|
delta,
|
||||||
|
phase,
|
||||||
|
modifiers: event_mods(event),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
AppState::queue_event(device_event);
|
||||||
|
AppState::queue_event(window_event);
|
||||||
|
}
|
||||||
|
trace!("Completed `scrollWheel`");
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) {
|
||||||
|
trace!("Triggered `pressureChangeWithEvent`");
|
||||||
|
unsafe {
|
||||||
|
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||||
|
let state = &mut *(state_ptr as *mut ViewState);
|
||||||
|
|
||||||
|
let pressure = event.pressure();
|
||||||
|
let stage = event.stage();
|
||||||
|
|
||||||
|
let window_event = Event::WindowEvent {
|
||||||
|
window_id: WindowId(get_window_id(state.nswindow)),
|
||||||
|
event: WindowEvent::TouchpadPressure {
|
||||||
|
device_id: DEVICE_ID,
|
||||||
|
pressure,
|
||||||
|
stage,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
AppState::queue_event(window_event);
|
||||||
|
}
|
||||||
|
trace!("Completed `pressureChangeWithEvent`");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allows us to receive Ctrl-Tab and Ctrl-Esc.
|
||||||
|
// Note that this *doesn't* help with any missing Cmd inputs.
|
||||||
// https://github.com/chromium/chromium/blob/a86a8a6bcfa438fa3ac2eba6f02b3ad1f8e0756f/ui/views/cocoa/bridged_content_view.mm#L816
|
// https://github.com/chromium/chromium/blob/a86a8a6bcfa438fa3ac2eba6f02b3ad1f8e0756f/ui/views/cocoa/bridged_content_view.mm#L816
|
||||||
extern fn wants_key_down_for_event(_this: &Object, _se: Sel, _event: id) -> BOOL {
|
extern fn wants_key_down_for_event(_this: &Object, _sel: Sel, _event: id) -> BOOL {
|
||||||
YES
|
YES
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
460
src/platform_impl/macos/window_delegate.rs
Normal file
460
src/platform_impl/macos/window_delegate.rs
Normal file
|
@ -0,0 +1,460 @@
|
||||||
|
use std::{f64, os::raw::c_void, sync::{Arc, Weak}};
|
||||||
|
|
||||||
|
use cocoa::{
|
||||||
|
appkit::{self, NSView, NSWindow}, base::{id, nil},
|
||||||
|
foundation::NSAutoreleasePool,
|
||||||
|
};
|
||||||
|
use objc::{runtime::{Class, Object, Sel, BOOL, YES, NO}, declare::ClassDecl};
|
||||||
|
|
||||||
|
use {dpi::LogicalSize, event::{Event, WindowEvent}, window::WindowId};
|
||||||
|
use platform_impl::platform::{
|
||||||
|
app_state::AppState, util::{self, IdRef},
|
||||||
|
window::{get_window_id, UnownedWindow},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct WindowDelegateState {
|
||||||
|
nswindow: IdRef, // never changes
|
||||||
|
nsview: IdRef, // never changes
|
||||||
|
|
||||||
|
window: Weak<UnownedWindow>,
|
||||||
|
|
||||||
|
// TODO: It's possible for delegate methods to be called asynchronously,
|
||||||
|
// causing data races / `RefCell` panics.
|
||||||
|
|
||||||
|
// This is set when WindowBuilder::with_fullscreen was set,
|
||||||
|
// see comments of `window_did_fail_to_enter_fullscreen`
|
||||||
|
initial_fullscreen: bool,
|
||||||
|
|
||||||
|
// During `windowDidResize`, we use this to only send Moved if the position changed.
|
||||||
|
previous_position: Option<(f64, f64)>,
|
||||||
|
|
||||||
|
// Used to prevent redundant events.
|
||||||
|
previous_dpi_factor: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowDelegateState {
|
||||||
|
pub fn new(
|
||||||
|
window: &Arc<UnownedWindow>,
|
||||||
|
initial_fullscreen: bool,
|
||||||
|
) -> Self {
|
||||||
|
let dpi_factor = window.get_hidpi_factor();
|
||||||
|
|
||||||
|
let mut delegate_state = WindowDelegateState {
|
||||||
|
nswindow: window.nswindow.clone(),
|
||||||
|
nsview: window.nsview.clone(),
|
||||||
|
window: Arc::downgrade(&window),
|
||||||
|
initial_fullscreen,
|
||||||
|
previous_position: None,
|
||||||
|
previous_dpi_factor: dpi_factor,
|
||||||
|
};
|
||||||
|
|
||||||
|
if dpi_factor != 1.0 {
|
||||||
|
delegate_state.emit_event(WindowEvent::HiDpiFactorChanged(dpi_factor));
|
||||||
|
delegate_state.emit_resize_event();
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate_state
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_window<F, T>(&mut self, callback: F) -> Option<T>
|
||||||
|
where F: FnOnce(&UnownedWindow) -> T
|
||||||
|
{
|
||||||
|
self.window
|
||||||
|
.upgrade()
|
||||||
|
.map(|ref window| callback(window))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn emit_event(&mut self, event: WindowEvent) {
|
||||||
|
let event = Event::WindowEvent {
|
||||||
|
window_id: WindowId(get_window_id(*self.nswindow)),
|
||||||
|
event,
|
||||||
|
};
|
||||||
|
AppState::queue_event(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn emit_resize_event(&mut self) {
|
||||||
|
let rect = unsafe { NSView::frame(*self.nsview) };
|
||||||
|
let size = LogicalSize::new(rect.size.width as f64, rect.size.height as f64);
|
||||||
|
let event = Event::WindowEvent {
|
||||||
|
window_id: WindowId(get_window_id(*self.nswindow)),
|
||||||
|
event: WindowEvent::Resized(size),
|
||||||
|
};
|
||||||
|
AppState::send_event_immediately(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_move_event(&mut self) {
|
||||||
|
let rect = unsafe { NSWindow::frame(*self.nswindow) };
|
||||||
|
let x = rect.origin.x as f64;
|
||||||
|
let y = util::bottom_left_to_top_left(rect);
|
||||||
|
let moved = self.previous_position != Some((x, y));
|
||||||
|
if moved {
|
||||||
|
self.previous_position = Some((x, y));
|
||||||
|
self.emit_event(WindowEvent::Moved((x, y).into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_delegate(window: &Arc<UnownedWindow>, initial_fullscreen: bool) -> IdRef {
|
||||||
|
let state = WindowDelegateState::new(window, initial_fullscreen);
|
||||||
|
unsafe {
|
||||||
|
// This is free'd in `dealloc`
|
||||||
|
let state_ptr = Box::into_raw(Box::new(state)) as *mut c_void;
|
||||||
|
let delegate: id = msg_send![WINDOW_DELEGATE_CLASS.0, alloc];
|
||||||
|
IdRef::new(msg_send![delegate, initWithWinit:state_ptr])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WindowDelegateClass(*const Class);
|
||||||
|
unsafe impl Send for WindowDelegateClass {}
|
||||||
|
unsafe impl Sync for WindowDelegateClass {}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref WINDOW_DELEGATE_CLASS: WindowDelegateClass = unsafe {
|
||||||
|
let superclass = class!(NSResponder);
|
||||||
|
let mut decl = ClassDecl::new("WinitWindowDelegate", superclass).unwrap();
|
||||||
|
|
||||||
|
decl.add_method(
|
||||||
|
sel!(dealloc),
|
||||||
|
dealloc as extern fn(&Object, Sel),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(initWithWinit:),
|
||||||
|
init_with_winit as extern fn(&Object, Sel, *mut c_void) -> id,
|
||||||
|
);
|
||||||
|
|
||||||
|
decl.add_method(
|
||||||
|
sel!(windowShouldClose:),
|
||||||
|
window_should_close as extern fn(&Object, Sel, id) -> BOOL,
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(windowWillClose:),
|
||||||
|
window_will_close as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(windowDidResize:),
|
||||||
|
window_did_resize as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(windowDidMove:),
|
||||||
|
window_did_move as extern fn(&Object, Sel, id));
|
||||||
|
decl.add_method(
|
||||||
|
sel!(windowDidChangeScreen:),
|
||||||
|
window_did_change_screen as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(windowDidChangeBackingProperties:),
|
||||||
|
window_did_change_backing_properties as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(windowDidBecomeKey:),
|
||||||
|
window_did_become_key as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(windowDidResignKey:),
|
||||||
|
window_did_resign_key as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
|
||||||
|
decl.add_method(
|
||||||
|
sel!(draggingEntered:),
|
||||||
|
dragging_entered as extern fn(&Object, Sel, id) -> BOOL,
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(prepareForDragOperation:),
|
||||||
|
prepare_for_drag_operation as extern fn(&Object, Sel, id) -> BOOL,
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(performDragOperation:),
|
||||||
|
perform_drag_operation as extern fn(&Object, Sel, id) -> BOOL,
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(concludeDragOperation:),
|
||||||
|
conclude_drag_operation as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(draggingExited:),
|
||||||
|
dragging_exited as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
|
||||||
|
decl.add_method(
|
||||||
|
sel!(windowDidEnterFullScreen:),
|
||||||
|
window_did_enter_fullscreen as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(windowWillEnterFullScreen:),
|
||||||
|
window_will_enter_fullscreen as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(windowDidExitFullScreen:),
|
||||||
|
window_did_exit_fullscreen as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(windowDidFailToEnterFullScreen:),
|
||||||
|
window_did_fail_to_enter_fullscreen as extern fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
|
||||||
|
decl.add_ivar::<*mut c_void>("winitState");
|
||||||
|
WindowDelegateClass(decl.register())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is definitely unsafe, but labeling that would increase
|
||||||
|
// boilerplate and wouldn't really clarify anything...
|
||||||
|
fn with_state<F: FnOnce(&mut WindowDelegateState) -> T, T>(this: &Object, callback: F) {
|
||||||
|
let state_ptr = unsafe {
|
||||||
|
let state_ptr: *mut c_void = *this.get_ivar("winitState");
|
||||||
|
&mut *(state_ptr as *mut WindowDelegateState)
|
||||||
|
};
|
||||||
|
callback(state_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn dealloc(this: &Object, _sel: Sel) {
|
||||||
|
with_state(this, |state| unsafe {
|
||||||
|
Box::from_raw(state as *mut WindowDelegateState);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> id {
|
||||||
|
unsafe {
|
||||||
|
let this: id = msg_send![this, init];
|
||||||
|
if this != nil {
|
||||||
|
(*this).set_ivar("winitState", state);
|
||||||
|
with_state(&*this, |state| {
|
||||||
|
let () = msg_send![*state.nswindow, setDelegate:this];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
|
||||||
|
trace!("Triggered `windowShouldClose:`");
|
||||||
|
with_state(this, |state| state.emit_event(WindowEvent::CloseRequested));
|
||||||
|
trace!("Completed `windowShouldClose:`");
|
||||||
|
NO
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn window_will_close(this: &Object, _: Sel, _: id) {
|
||||||
|
trace!("Triggered `windowWillClose:`");
|
||||||
|
with_state(this, |state| unsafe {
|
||||||
|
// `setDelegate:` retains the previous value and then autoreleases it
|
||||||
|
let pool = NSAutoreleasePool::new(nil);
|
||||||
|
// Since El Capitan, we need to be careful that delegate methods can't
|
||||||
|
// be called after the window closes.
|
||||||
|
let () = msg_send![*state.nswindow, setDelegate:nil];
|
||||||
|
pool.drain();
|
||||||
|
state.emit_event(WindowEvent::Destroyed);
|
||||||
|
});
|
||||||
|
trace!("Completed `windowWillClose:`");
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn window_did_resize(this: &Object, _: Sel, _: id) {
|
||||||
|
trace!("Triggered `windowDidResize:`");
|
||||||
|
with_state(this, |state| {
|
||||||
|
state.emit_resize_event();
|
||||||
|
state.emit_move_event();
|
||||||
|
});
|
||||||
|
trace!("Completed `windowDidResize:`");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This won't be triggered if the move was part of a resize.
|
||||||
|
extern fn window_did_move(this: &Object, _: Sel, _: id) {
|
||||||
|
trace!("Triggered `windowDidMove:`");
|
||||||
|
with_state(this, |state| {
|
||||||
|
state.emit_move_event();
|
||||||
|
});
|
||||||
|
trace!("Completed `windowDidMove:`");
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn window_did_change_screen(this: &Object, _: Sel, _: id) {
|
||||||
|
trace!("Triggered `windowDidChangeScreen:`");
|
||||||
|
with_state(this, |state| {
|
||||||
|
let dpi_factor = unsafe {
|
||||||
|
NSWindow::backingScaleFactor(*state.nswindow)
|
||||||
|
} as f64;
|
||||||
|
if state.previous_dpi_factor != dpi_factor {
|
||||||
|
state.previous_dpi_factor = dpi_factor;
|
||||||
|
state.emit_event(WindowEvent::HiDpiFactorChanged(dpi_factor));
|
||||||
|
state.emit_resize_event();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
trace!("Completed `windowDidChangeScreen:`");
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will always be called before `window_did_change_screen`.
|
||||||
|
extern fn window_did_change_backing_properties(this: &Object, _:Sel, _:id) {
|
||||||
|
trace!("Triggered `windowDidChangeBackingProperties:`");
|
||||||
|
with_state(this, |state| {
|
||||||
|
let dpi_factor = unsafe {
|
||||||
|
NSWindow::backingScaleFactor(*state.nswindow)
|
||||||
|
} as f64;
|
||||||
|
if state.previous_dpi_factor != dpi_factor {
|
||||||
|
state.previous_dpi_factor = dpi_factor;
|
||||||
|
state.emit_event(WindowEvent::HiDpiFactorChanged(dpi_factor));
|
||||||
|
state.emit_resize_event();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
trace!("Completed `windowDidChangeBackingProperties:`");
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn window_did_become_key(this: &Object, _: Sel, _: id) {
|
||||||
|
trace!("Triggered `windowDidBecomeKey:`");
|
||||||
|
with_state(this, |state| {
|
||||||
|
// TODO: center the cursor if the window had mouse grab when it
|
||||||
|
// lost focus
|
||||||
|
state.emit_event(WindowEvent::Focused(true));
|
||||||
|
});
|
||||||
|
trace!("Completed `windowDidBecomeKey:`");
|
||||||
|
}
|
||||||
|
|
||||||
|
extern fn window_did_resign_key(this: &Object, _: Sel, _: id) {
|
||||||
|
trace!("Triggered `windowDidResignKey:`");
|
||||||
|
with_state(this, |state| {
|
||||||
|
state.emit_event(WindowEvent::Focused(false));
|
||||||
|
});
|
||||||
|
trace!("Completed `windowDidResignKey:`");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoked when the dragged image enters destination bounds or frame
|
||||||
|
extern fn dragging_entered(this: &Object, _: Sel, sender: id) -> BOOL {
|
||||||
|
trace!("Triggered `draggingEntered:`");
|
||||||
|
|
||||||
|
use cocoa::appkit::NSPasteboard;
|
||||||
|
use cocoa::foundation::NSFastEnumeration;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
let pb: id = unsafe { msg_send![sender, draggingPasteboard] };
|
||||||
|
let filenames = unsafe { NSPasteboard::propertyListForType(pb, appkit::NSFilenamesPboardType) };
|
||||||
|
|
||||||
|
for file in unsafe { filenames.iter() } {
|
||||||
|
use cocoa::foundation::NSString;
|
||||||
|
use std::ffi::CStr;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let f = NSString::UTF8String(file);
|
||||||
|
let path = CStr::from_ptr(f).to_string_lossy().into_owned();
|
||||||
|
|
||||||
|
with_state(this, |state| {
|
||||||
|
state.emit_event(WindowEvent::HoveredFile(PathBuf::from(path)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
trace!("Completed `draggingEntered:`");
|
||||||
|
YES
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoked when the image is released
|
||||||
|
extern fn prepare_for_drag_operation(_: &Object, _: Sel, _: id) -> BOOL {
|
||||||
|
trace!("Triggered `prepareForDragOperation:`");
|
||||||
|
trace!("Completed `prepareForDragOperation:`");
|
||||||
|
YES
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoked after the released image has been removed from the screen
|
||||||
|
extern fn perform_drag_operation(this: &Object, _: Sel, sender: id) -> BOOL {
|
||||||
|
trace!("Triggered `performDragOperation:`");
|
||||||
|
|
||||||
|
use cocoa::appkit::NSPasteboard;
|
||||||
|
use cocoa::foundation::NSFastEnumeration;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
let pb: id = unsafe { msg_send![sender, draggingPasteboard] };
|
||||||
|
let filenames = unsafe { NSPasteboard::propertyListForType(pb, appkit::NSFilenamesPboardType) };
|
||||||
|
|
||||||
|
for file in unsafe { filenames.iter() } {
|
||||||
|
use cocoa::foundation::NSString;
|
||||||
|
use std::ffi::CStr;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let f = NSString::UTF8String(file);
|
||||||
|
let path = CStr::from_ptr(f).to_string_lossy().into_owned();
|
||||||
|
|
||||||
|
with_state(this, |state| {
|
||||||
|
state.emit_event(WindowEvent::DroppedFile(PathBuf::from(path)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
trace!("Completed `performDragOperation:`");
|
||||||
|
YES
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoked when the dragging operation is complete
|
||||||
|
extern fn conclude_drag_operation(_: &Object, _: Sel, _: id) {
|
||||||
|
trace!("Triggered `concludeDragOperation:`");
|
||||||
|
trace!("Completed `concludeDragOperation:`");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoked when the dragging operation is cancelled
|
||||||
|
extern fn dragging_exited(this: &Object, _: Sel, _: id) {
|
||||||
|
trace!("Triggered `draggingExited:`");
|
||||||
|
with_state(this, |state| state.emit_event(WindowEvent::HoveredFileCancelled));
|
||||||
|
trace!("Completed `draggingExited:`");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoked when before enter fullscreen
|
||||||
|
extern fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) {
|
||||||
|
trace!("Triggered `windowWillEnterFullscreen:`");
|
||||||
|
with_state(this, |state| state.with_window(|window| {
|
||||||
|
trace!("Locked shared state in `window_will_enter_fullscreen`");
|
||||||
|
window.shared_state.lock().unwrap().maximized = window.is_zoomed();
|
||||||
|
trace!("Unlocked shared state in `window_will_enter_fullscreen`");
|
||||||
|
}));
|
||||||
|
trace!("Completed `windowWillEnterFullscreen:`");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoked when entered fullscreen
|
||||||
|
extern fn window_did_enter_fullscreen(this: &Object, _: Sel, _: id) {
|
||||||
|
trace!("Triggered `windowDidEnterFullscreen:`");
|
||||||
|
with_state(this, |state| {
|
||||||
|
state.with_window(|window| {
|
||||||
|
let monitor = window.get_current_monitor();
|
||||||
|
trace!("Locked shared state in `window_did_enter_fullscreen`");
|
||||||
|
window.shared_state.lock().unwrap().fullscreen = Some(monitor);
|
||||||
|
trace!("Unlocked shared state in `window_will_enter_fullscreen`");
|
||||||
|
});
|
||||||
|
state.initial_fullscreen = false;
|
||||||
|
});
|
||||||
|
trace!("Completed `windowDidEnterFullscreen:`");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoked when exited fullscreen
|
||||||
|
extern fn window_did_exit_fullscreen(this: &Object, _: Sel, _: id) {
|
||||||
|
trace!("Triggered `windowDidExitFullscreen:`");
|
||||||
|
with_state(this, |state| state.with_window(|window| {
|
||||||
|
window.restore_state_from_fullscreen();
|
||||||
|
}));
|
||||||
|
trace!("Completed `windowDidExitFullscreen:`");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invoked when fail to enter fullscreen
|
||||||
|
///
|
||||||
|
/// When this window launch from a fullscreen app (e.g. launch from VS Code
|
||||||
|
/// terminal), it creates a new virtual destkop and a transition animation.
|
||||||
|
/// This animation takes one second and cannot be disable without
|
||||||
|
/// elevated privileges. In this animation time, all toggleFullscreen events
|
||||||
|
/// will be failed. In this implementation, we will try again by using
|
||||||
|
/// performSelector:withObject:afterDelay: until window_did_enter_fullscreen.
|
||||||
|
/// It should be fine as we only do this at initialzation (i.e with_fullscreen
|
||||||
|
/// was set).
|
||||||
|
///
|
||||||
|
/// From Apple doc:
|
||||||
|
/// In some cases, the transition to enter full-screen mode can fail,
|
||||||
|
/// due to being in the midst of handling some other animation or user gesture.
|
||||||
|
/// This method indicates that there was an error, and you should clean up any
|
||||||
|
/// work you may have done to prepare to enter full-screen mode.
|
||||||
|
extern fn window_did_fail_to_enter_fullscreen(this: &Object, _: Sel, _: id) {
|
||||||
|
trace!("Triggered `windowDidFailToEnterFullscreen:`");
|
||||||
|
with_state(this, |state| {
|
||||||
|
if state.initial_fullscreen {
|
||||||
|
let _: () = unsafe { msg_send![*state.nswindow,
|
||||||
|
performSelector:sel!(toggleFullScreen:)
|
||||||
|
withObject:nil
|
||||||
|
afterDelay: 0.5
|
||||||
|
] };
|
||||||
|
} else {
|
||||||
|
state.with_window(|window| window.restore_state_from_fullscreen());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
trace!("Completed `windowDidFailToEnterFullscreen:`");
|
||||||
|
}
|
|
@ -921,7 +921,6 @@ unsafe extern "system" fn public_window_callback<T>(
|
||||||
},
|
},
|
||||||
|
|
||||||
winuser::WM_CHAR => {
|
winuser::WM_CHAR => {
|
||||||
use std::mem;
|
|
||||||
use event::WindowEvent::ReceivedCharacter;
|
use event::WindowEvent::ReceivedCharacter;
|
||||||
let chr: char = mem::transmute(wparam as u32);
|
let chr: char = mem::transmute(wparam as u32);
|
||||||
subclass_input.send_event(Event::WindowEvent {
|
subclass_input.send_event(Event::WindowEvent {
|
||||||
|
@ -994,7 +993,6 @@ unsafe extern "system" fn public_window_callback<T>(
|
||||||
|
|
||||||
winuser::WM_MOUSEWHEEL => {
|
winuser::WM_MOUSEWHEEL => {
|
||||||
use event::MouseScrollDelta::LineDelta;
|
use event::MouseScrollDelta::LineDelta;
|
||||||
use event::TouchPhase;
|
|
||||||
|
|
||||||
let value = (wparam >> 16) as i16;
|
let value = (wparam >> 16) as i16;
|
||||||
let value = value as i32;
|
let value = value as i32;
|
||||||
|
@ -1010,7 +1008,6 @@ unsafe extern "system" fn public_window_callback<T>(
|
||||||
|
|
||||||
winuser::WM_MOUSEHWHEEL => {
|
winuser::WM_MOUSEHWHEEL => {
|
||||||
use event::MouseScrollDelta::LineDelta;
|
use event::MouseScrollDelta::LineDelta;
|
||||||
use event::TouchPhase;
|
|
||||||
|
|
||||||
let value = (wparam >> 16) as i16;
|
let value = (wparam >> 16) as i16;
|
||||||
let value = value as i32;
|
let value = value as i32;
|
||||||
|
|
0
src/util.rs
Normal file
0
src/util.rs
Normal file
Loading…
Reference in a new issue