Compare commits

...
306 Commits
Author SHA1 Message Date
Diego Waxemberg 15fe5a06c2 Bumping version for v3.0.0 build 3 2019-09-22 22:14:27 -07:00
Cameron Gutman 8d4c7bd705 Fix Steam app detection if GFE gives us a lower-case path 2019-09-22 10:31:23 -07:00
Cameron Gutman b0d85abfa6 Increase audio circular buffer size to reduce glitching 2019-09-22 10:20:30 -07:00
Diego Waxemberg 4295017324 Bumping version for v3.0.0 build 2
Pulling in fix for assets
2019-09-21 23:26:09 -07:00
Cameron Gutman d80d70215a Add App Store screenshots 2019-09-21 16:45:45 -07:00
Cameron Gutman a46f905aa6 Fix case of Computer asset name 2019-09-21 14:32:12 -07:00
Diego Waxemberg 453c50d505 Bumping version for v3.0.0 2019-09-20 20:26:09 -07:00
Cameron Gutman 61c7d9f2b3 Disable bounce-back on the reveal view controller 2019-09-18 19:59:24 -07:00
Cameron Gutman 34707e7743 Update launch screen to match new UI 2019-09-18 19:45:27 -07:00
Cameron Gutman 9a9b9651a1 Address several iOS and tvOS compilation warnings 2019-09-15 15:46:23 -07:00
Cameron Gutman d4576a8739 Suppress availability warning for overrideUserInterfaceStyle field 2019-09-15 15:29:29 -07:00
Cameron Gutman 87824c2650 Fix control stream connection error on multi-homed hosts 2019-09-14 14:14:26 -07:00
Cameron Gutman 283e735f90 Remove the successful quit dialog since it's redundant and annoying 2019-09-14 13:08:47 -07:00
Cameron Gutman 202f968b98 Rework app labels on iOS to reuse title UILabel and use correct size and insets 2019-09-14 12:59:40 -07:00
Cameron Gutman 62df1cd5ba Reduce minimum spacing to 15 to allow an extra column on iPhone SE 2019-09-14 12:47:21 -07:00
Cameron Gutman f24c28c848 Switch back to text for navigation buttons 2019-09-14 12:46:50 -07:00
Cameron Gutman 8fb465d3ca Fix tvOS build warning 2019-09-14 12:28:37 -07:00
Cameron Gutman 29dff2320b Fix content insets for host and app scroll views 2019-09-14 12:28:21 -07:00
Cameron Gutman a40b30016b Rewrite storyboards with Xcode 11 GM 2019-09-14 12:07:38 -07:00
Cameron Gutman 409b262a4f Add a hack as a possible workaround for #372 2019-09-14 11:49:09 -07:00
Cameron Gutman 73197e4378 Clean up and fix storyboards 2019-09-14 11:34:22 -07:00
Cameron Gutman f51dbe4295 Fix some Xcode 11 warnings 2019-09-11 19:12:11 -07:00
Cameron Gutman 597aaa1bab Cleanup controller support code using the iOS 13 SDK directly 2019-09-11 18:44:37 -07:00
Cameron Gutman b9656da8a5 Use Xcode 11 image for Travis CI 2019-09-11 18:27:45 -07:00
Cameron Gutman 422db1898c Improve manually added Steam app detection heuristic to prevent FPs on apps with "Steam" in the name 2019-09-08 18:56:18 -07:00
Cameron Gutman 56691791b8 Fix race condition that could cause safe area insets to be missing 2019-09-01 12:26:39 -07:00
Cameron Gutman de2a5d3cd7 Fix overlays not being affected by button press and spinner eating touch events 2019-09-01 12:15:26 -07:00
Cameron Gutman 49fc10191c Fix transparent app grid background being incorrectly overridden 2019-08-31 20:19:03 -07:00
Cameron Gutman bf8aaa8198 Refresh app icon when the running state changes on tvOS 2019-08-31 19:52:55 -07:00
Cameron Gutman 9a4cdc9c94 Replace updating overlay icon with spinner 2019-08-31 16:02:37 -07:00
Cameron Gutman f6153fe818 Improve selection visibility on tvOS 2019-08-30 22:33:32 -07:00
Cameron Gutman f2fa9e18ea Reorder tvOS settings and fix default bitrate for 1080p60 2019-08-30 21:51:10 -07:00
Cameron Gutman aa013be6e6 Update settings config for tvOS 2019-08-30 21:44:12 -07:00
Cameron Gutman 7e21b131ac Redesign navigation bar on iOS 2019-08-30 21:15:11 -07:00
Cameron Gutman 97681f3a0d Increase size of PC icon 2019-08-30 20:00:00 -07:00
Cameron Gutman e476cc407c Fix crash on pre-iOS 13 2019-08-30 19:55:49 -07:00
Cameron Gutman f2e03bbf1f Always use dark mode on tvOS 2019-08-30 19:40:17 -07:00
Cameron Gutman a6cef32c9a Increase size of PC icons on tvOS 2019-08-30 19:37:16 -07:00
Cameron Gutman 6d94897c21 Distinguish between offline and unknown hosts 2019-08-30 19:19:23 -07:00
Cameron Gutman a065c78b5f Restyle UIComputerView to match other clients 2019-08-30 18:36:14 -07:00
Cameron Gutman 83479719a4 Fix styles on settings slideout on iOS 13 2019-08-30 17:16:56 -07:00
Cameron Gutman fd4ede58ca Reduce the size of the audio circular buffer to reduce peak latency 2019-08-29 22:00:27 -07:00
Cameron Gutman 7ed5a9831f Fix settings slideout scrolling too far on iOS 13 2019-08-29 21:57:51 -07:00
Cameron Gutman 42b7fd35ce Fix surround sound audio playing back silence 2019-08-29 21:07:10 -07:00
Cameron Gutman 42c97eafaa Fix slightly offset titles 2019-08-29 20:34:51 -07:00
Cameron Gutman 5b82be0546 Rewrite UIAppView rendering to consolidate tvOS and iOS codepaths 2019-08-29 20:32:18 -07:00
Cameron Gutman 1529058f52 Increase font size for app names 2019-08-29 19:00:42 -07:00
Cameron Gutman 3dcca3a922 Allow click and drag via tvOS remote trackpad 2019-08-29 18:55:33 -07:00
Cameron Gutman 9b8f230c46 Prevent gesture recognizers from eating our touch events. Fixes #375 2019-08-28 21:51:52 -07:00
Cameron Gutman 7699d9e574 Implement 5.1 surround sound support. Fixes #371 2019-08-26 22:46:35 -07:00
Cameron Gutman 641e1c5066 Update common-c for parallel STUN requests 2019-08-20 19:08:25 -07:00
Cameron Gutman 617ef7c6a6 Update common-c to support STUN fallback 2019-08-07 23:15:33 -07:00
Cameron Gutman c2620b8321 Fix tvOS build 2019-08-07 00:53:58 -07:00
Cameron Gutman 9e91d136bf Add Travis CI badge to readme 2019-08-07 00:48:26 -07:00
Cameron Gutman 9960d2a2ff Add Travis CI support 2019-08-07 00:40:54 -07:00
Cameron Gutman 311e77e01a Remove unit test for the old Swift controller class 2019-08-07 00:06:54 -07:00
Cameron Gutman c08df921da Update common-c to fix termination detection issue on GFE 3.19.0.107 2019-07-28 12:58:25 -04:00
Cameron Gutman ba4e55aac4 Add on-screen connection status warning 2019-07-23 18:43:41 -04:00
Cameron Gutman af16f03703 Stringify IPv6 addresses with scope IDs if present 2019-07-15 01:05:21 -07:00
Cameron Gutman 8189d54703 Remove forced population of host.address 2019-07-15 01:00:50 -07:00
Cameron Gutman ac8514e3cb Update common-c for IPv6 optimizations 2019-07-15 00:16:54 -07:00
Cameron Gutman 8f4b8da3ce Handle IPv6-only servers 2019-07-14 22:55:41 -07:00
Cameron Gutman 8337b3e708 Add support for zero configuration IPv6 streaming 2019-07-14 19:17:17 -07:00
Cameron Gutman cb527baead Generate SHA-256 client certificates instead of SHA-1 2019-07-05 22:20:55 -07:00
Cameron Gutman ed10612e2c Allow pairing to complete in the background to allow users to type the PIN using remote desktop apps 2019-07-05 22:18:30 -07:00
Diego Waxemberg 7d3eaeef83 Creating a new 2.7.0 build 2019-06-05 20:22:31 -07:00
Cameron Gutman dd5ecb37c4 Add support for Start and Select on iOS 13. Fixes #367 2019-06-05 19:54:56 -07:00
Cameron Gutman 636143ee97 Disable the 4K option for SoCs older than the A9 2019-06-05 18:59:40 -07:00
Cameron Gutman 28ff29bb96 Disable the HEVC toggle for devices that don't support HEVC 2019-06-05 18:41:30 -07:00
Cameron Gutman a508c6d855 Remove default Steam app to address Apple App Review rejection 2019-06-04 18:37:52 -07:00
Diego Waxemberg dbd6eabd24 Bumping app version for v2.7.0 2019-05-31 18:54:02 -07:00
Cameron Gutman ad0a5935f7 Don't compile asserts into release builds 2019-05-31 14:46:20 -05:00
Cameron Gutman dcd2dc97a6 Merge pull request #366 from MichaelMKenny/4K-120-bitrate-fix
Capped bitrate at 100Mbps.
2019-05-31 14:04:10 -05:00
Michael Kenny f92544c7d2 Capped bitrate at 100Mbps.
Choosing 4K 120Hz was causing an assert/crash.
2019-05-31 15:04:20 +09:30
Cameron Gutman 797fa0f492 Add 4K resolution option. Fixes #364 2019-05-27 10:37:26 -07:00
Cameron Gutman ff52adf4f8 Improve pairing messages 2019-05-27 10:27:10 -07:00
Cameron Gutman 8bb49ada11 Disable vibration for expedited review 2019-05-27 10:12:02 -07:00
Cameron Gutman 3f709b616b Fix video on GFE 3.19 2019-05-27 10:06:57 -07:00
Diego Waxemberg 83694f8141 Bumping version for v2.6.0 2019-04-29 19:20:46 -07:00
Cameron Gutman a4352aae4d Update common-c to pick up recent performance and bandwidth improvements 2019-04-27 15:01:40 -07:00
Cameron Gutman 2ac19336d6 Rename Multi-Controller Off to Single 2019-04-27 14:25:58 -07:00
Cameron Gutman 63b09c5b27 Remove the old message callbacks 2019-04-27 14:00:58 -07:00
Andrew Scagnelli b2c2f0837a more appletv tweaks (#351)
* redo now playing indicator

- instead of drawing an arrow overlay, add an offset green shadow

* do not show "Connection Terminated" on code 0

if an application quits successfully because it was asked to do so by
the user (errorCode = 0), then do not display an alert -- just go back
to the prior view.

* fix: add missing toggle for mutli-controller support
2019-02-13 21:03:09 -08:00
Cameron Gutman 335b5aef1f Add rumble support 2019-02-11 19:22:18 -08:00
Cameron Gutman 918e1248f2 Store GCController in Controller object to allow us to fetch controller attributes later 2019-02-11 18:21:04 -08:00
Cameron Gutman 442a791e0c Fix SOPS issues when streaming at 120 FPS 2019-02-09 21:03:48 -08:00
Cameron Gutman 573b93329d Merge pull request #354 from eraser2021999/master
Simple solution to solve focus issue on Apple TV.
2019-01-04 20:27:43 -08:00
Cameron Gutman 40359c5e32 Use stun.moonlight-stream.org for STUN 2019-01-04 20:23:46 -08:00
Cameron Gutman 3b01b50503 Update common-c with strict bitrate fix 2019-01-04 20:19:34 -08:00
Bogdan Coticopol 76b55cc230 Simple solution to solve focus issue on Apple TV. 2019-01-02 00:16:26 +02:00
Diego Waxemberg e136cd96c1 Bumping app version for 2.5.0 2018-12-29 10:32:02 -05:00
Cameron Gutman 5028eaed59 Fix crash presenting the loading frame while old one is still dismissing 2018-12-28 20:51:29 -08:00
Cameron Gutman d18c713684 Fix tip display on iPhone SE 2018-12-28 20:32:22 -08:00
Cameron Gutman 08e169eb23 Rewrite loading frame management (again) 2018-12-28 16:45:05 -08:00
Cameron Gutman 611358e35c Fix pairing code that didn't always call one of the pairing completion callbacks 2018-12-28 15:50:13 -08:00
Cameron Gutman 63275130bb Fix duplicate connection termination attempt due to race condition after calling LiInterruptConnection() 2018-12-28 15:31:18 -08:00
Cameron Gutman 7c9313e04d Ensure we don't deadlock if somehow a loading frame ends up under the active view 2018-12-28 02:09:57 -08:00
Cameron Gutman d466a0af07 Only cleanup the stream on termination 2018-12-28 02:03:21 -08:00
Cameron Gutman c8adc184c9 Use a semaphore to ensure the loading frame is safely shown and dismissed 2018-12-28 02:01:40 -08:00
Cameron Gutman d40a29df71 Ensure we can't enter the app grid without having a pinned cert 2018-12-28 01:32:53 -08:00
Cameron Gutman 85820bf812 Use the real online status rather than always assuming YES 2018-12-28 01:24:18 -08:00
Cameron Gutman 5d4d10ac8a Fix displaying GFE error messages for launch and resume 2018-12-28 01:16:56 -08:00
Cameron Gutman eca018ad15 Set host online immediately after using Add PC dialog to update host address 2018-12-28 00:54:26 -08:00
Cameron Gutman 90e582a263 Assert only in the cert untrusted error path 2018-12-28 00:45:32 -08:00
Cameron Gutman 3717228e28 Fix app grid running indicator not being correct immediately after returning from stream 2018-12-28 00:32:19 -08:00
Cameron Gutman 8c0997ddcd Update common-c to fix GFE 3.16 bitrate issue 2018-12-28 00:14:04 -08:00
Cameron Gutman d722c9e18d Use default auth handling if the pinning fails 2018-12-27 21:07:17 -08:00
Cameron Gutman 94c9433b99 Fix running state refresh on app grid 2018-12-27 19:54:27 -08:00
Cameron Gutman befef62b2f Log parsed XML attributes 2018-12-27 19:50:51 -08:00
Cameron Gutman dc00c1e3e7 Fix leak of UIComputerViews that poll forever 2018-12-27 19:50:29 -08:00
Cameron Gutman 3555d60a9b Add a quit tip for iOS and tvOS 2018-12-22 21:43:37 -08:00
Cameron Gutman bd8b9f8bfb Use a shared UID for all Moonlight clients 2018-12-22 21:02:24 -08:00
Cameron Gutman 72dcd76e37 Require cert pinning for HTTPS 2018-12-22 20:39:13 -08:00
Cameron Gutman fbae7f88b5 Add server cert pinning after pairing 2018-12-22 00:05:48 -08:00
Cameron Gutman 791a5b1ea1 Update project with Xcode recommendations 2018-12-07 21:48:54 -08:00
Cameron Gutman 8c5a82573b Update readme to new URL 2018-12-03 21:43:19 -08:00
Cameron Gutman cc56a36bbd Properly set GCSupportedGameControllers and GCSupportsControllerUserInteraction plist values 2018-12-01 15:00:24 -08:00
Cameron Gutman 336dbeed98 Remove loading frame from Add Host dialog flow 2018-11-28 18:43:45 -08:00
Cameron Gutman f8613cdbfd Fix broken app and host taps on iOS 8 2018-11-28 18:43:20 -08:00
Cameron Gutman a5961dd9b5 Use lower MTU for remote streaming and higher MTU for local networks 2018-11-21 21:03:08 -08:00
Cameron Gutman 17a5866fdd Merge pull request #341 from ascagnel/tv-fixes
tvOS app UI/UX fixes
2018-11-19 20:16:50 -08:00
Diego Waxemberg 5e77380636 Bumping app version for 2.4.0 2018-11-17 16:00:51 -08:00
Cameron Gutman 845af246b5 Fix a few cases where the loading frame could be left on-screen 2018-11-16 20:17:03 -08:00
Cameron Gutman 87cac4e562 Fix host long press causing duplicate menus to show up 2018-11-16 20:05:06 -08:00
Cameron Gutman 70eb4fd69d Allow waking non-paired hosts 2018-11-16 19:30:05 -08:00
Cameron Gutman c5120bec37 Remove wait between retries after polling failure 2018-11-16 19:14:00 -08:00
Cameron Gutman 24fc9ac0a9 Change "host already added" message to be more clear 2018-11-16 19:03:23 -08:00
Cameron Gutman 2c0624f1e7 Improve handling of non-local addresses and remote streaming 2018-11-16 19:02:31 -08:00
Cameron Gutman 8b227f28f7 Update common-c to fix NAT64 2018-11-16 00:41:23 -08:00
Cameron Gutman 23e9c3f236 Fix crash when the loading spinner is dismissed before fully presenting 2018-11-15 22:02:05 -08:00
Cameron Gutman eefde316de Fix bug that can cause spinner to get stuck 2018-11-15 21:11:02 -08:00
Cameron Gutman a3cfd787de Update common-c for GFE 3.16 2018-11-15 13:37:28 -08:00
Cameron Gutman 410d0d367b Fix handling of missing L3 and R3 buttons and adjust on-screen controls based on L3/R3 presence 2018-11-03 14:01:32 -07:00
Cameron Gutman 39b04d46a4 Add support for L3 and R3 on iOS 12.1 2018-10-31 18:08:34 -07:00
Andrew Scagnelli 0e8647ad07 feat: added settings bundle 2018-10-28 16:23:24 -04:00
Diego Waxemberg 73091e7e8c Creating build for 2.3.0 2018-10-27 12:55:02 -07:00
Cameron Gutman 16a2ab3941 Fix settings not saving on the first stream if the settings slideout is not closed 2018-10-27 11:57:29 -07:00
Cameron Gutman 39a2e8be78 Log an error if STUN fails 2018-10-26 20:51:31 -07:00
Cameron Gutman 8e6dadfa79 Use STUN to get the WAN address for PCs discovered by mDNS 2018-10-26 20:28:59 -07:00
Andrew Scagnelli c2134585df fix: apply tvos style to missing icon 2018-10-24 00:08:06 -04:00
Andrew Scagnelli dedcbd9d3e feat: update text color
do not apply UIComputerView text color so it uses the theme default
2018-10-23 23:28:07 -04:00
Andrew Scagnelli bd379677a2 feat: updated menu button handling
- on tapping menu once (or chording with LB/RB), behave as
start/select/special
- on doube-tapping menu, quit the stream
2018-10-23 23:18:56 -04:00
Andrew Scagnelli 466ebe2840 disable focus effects on overlay icon 2018-10-22 23:45:17 -04:00
Cameron Gutman 736887b945 Add 120 FPS option for devices with high refresh rate displays 2018-10-22 20:24:51 -07:00
Cameron Gutman 993bfca0ae Allow bitrate slider to go to 100 Mbps 2018-10-22 19:27:49 -07:00
Cameron Gutman 5ad87f7400 Hide the textbox so it doesn't obstruct normal UI controls. Fixes #344. 2018-10-22 19:06:52 -07:00
Andrew Scagnelli e862dbd6b6 adding some basic placeholder icons 2018-10-19 01:05:48 -04:00
Andrew Scagnelli befde5eb28 system theme, tvos icon styling
- using tvos icon styling
- updated to use system theme (light/dark) instead of always being grey
2018-10-19 00:03:14 -04:00
Andrew Scagnelli 7fd3b8df1c on tvOS, do not quit the stream on pressing B
on tvOS (maybe also on other cases where you're using a "real" gamepad?),
use GCEventViewController so the system-level "back" event doesn't quit
the stream
2018-10-18 00:04:04 -04:00
Diego Waxemberg cee501d9b4 Bumping app version for 2.2.0 2018-10-08 21:14:18 -07:00
Cameron Gutman a103c0fd45 Update common-c to use split frame encode on HEVC 2018-10-08 20:26:32 -07:00
Cameron Gutman bf71ae003f Add all modifier flag combinations 2018-10-08 20:25:43 -07:00
Cameron Gutman 5b102ce3d3 Merge pull request #335 from tranchis/ios-keyboard
Improve keyboard support
2018-09-27 21:44:11 -07:00
Sergio Alvarez-Napagao c2012fa195 Improve keyboard support 2018-08-30 21:06:36 +02:00
Cameron Gutman 003fc8c1f8 Improve keyboard handling for unexpected inputs and multi-character input (such as dictation) 2018-08-29 10:02:58 -07:00
Cameron Gutman 9123033bb3 Remove help links from tvOS version due to lack of browser and WebKit 2018-08-29 09:36:51 -07:00
Cameron Gutman 253144188c Purge box art cache on low memory warning 2018-08-29 09:24:47 -07:00
Cameron Gutman 4ccb9bebbf Fix stream cleanup when pressing Menu on tvOS 2018-08-29 09:21:49 -07:00
Cameron Gutman 49f647748c Fix discovery being stopped after returning from Add PC dialog 2018-08-29 07:52:33 -07:00
Cameron Gutman e2e8f0121d Persist the database in NSUserDefaults on tvOS 2018-08-28 17:42:59 -07:00
Cameron Gutman b5ea4c3e50 Change URL timeout to 4 seconds to improve offline behavior 2018-08-27 01:55:34 -07:00
Cameron Gutman 90d47004e7 Avoid storing images in the database 2018-08-27 01:54:58 -07:00
Cameron Gutman 450960eaaf Fix credential storage on tvOS 2018-08-27 00:34:34 -07:00
Diego Waxemberg 44550baaa9 Changing directories for storage on tvOS because tvOS does not allow permanent storage 2018-08-26 21:54:49 -07:00
Cameron Gutman 293dc26b2e Fix the navigation bar look 2018-08-26 19:03:22 -07:00
Cameron Gutman 109fa6f463 Set the title of the view to the selected PC 2018-08-26 17:05:13 -07:00
Cameron Gutman 44f64e1a35 Fix several UI issues 2018-08-26 16:43:45 -07:00
Cameron Gutman 2e479837ed Fix spinners on tvOS and mouse support using the remote 2018-08-26 15:55:39 -07:00
Cameron Gutman 1b681a8730 Allow tvOS to back out from app grid to PC grid 2018-08-26 15:05:32 -07:00
Cameron Gutman f988ce0278 Fix issues with the loading frame colliding with other views 2018-08-26 12:25:22 -07:00
Cameron Gutman 1ef5c87afc Share MainFrameViewController between iOS and tvOS 2018-08-26 11:56:27 -07:00
Diego Waxemberg f5b99b6b9c Can now stream to tvOS 2018-08-26 11:31:20 -07:00
Diego Waxemberg 485dd4b001 Merge branch 'tvOS' of github.com:moonlight-stream/moonlight-ios into tvOS 2018-08-26 11:17:20 -07:00
Diego Waxemberg 92e2c0d7cc app cells are now clickable in tvOS 2018-08-26 11:14:15 -07:00
Cameron Gutman 48ffca57ec Fix infinite loop of loading UI spinners 2018-08-26 11:09:15 -07:00
Cameron Gutman e83a996cd5 Re-port MainFrameViewController to tvOS to fix loading page issues 2018-08-26 10:59:31 -07:00
Diego Waxemberg 57191e3b9a added a highlight state for focused views (can now see what's selected in tvOS) 2018-08-26 10:53:28 -07:00
Diego Waxemberg eadcf3b3eb fixed 'unsafe' requests and can now click stuff 2018-08-26 01:15:53 -07:00
Cameron Gutman 71e0646659 Add code to handle discovery and game controllers 2018-08-26 00:48:29 -07:00
Diego Waxemberg f2edee1669 Merge branch 'tvOS' of github.com:moonlight-stream/moonlight-ios into tvOS 2018-08-26 00:33:43 -07:00
Diego Waxemberg 11948bf618 wip 2018-08-26 00:15:56 -07:00
Cameron Gutman 4a9294a4b7 Remove Roboto fonts, switch to iOS app delegate for tvOS, and add stream view and loading view 2018-08-26 00:14:08 -07:00
Cameron Gutman a9b8f753a2 Remove tvOS-specific data model 2018-08-25 23:32:43 -07:00
Diego Waxemberg 05761c787c working on tvOS UI 2018-08-25 23:22:52 -07:00
Cameron Gutman 9a7b3b1325 Build libopus for tvOS and iOS with bitcode 2018-08-25 21:17:08 -07:00
Cameron Gutman b04a4e72ff Rebuild OpenSSL with bitcode for macOS, iOS, and tvOS 2018-08-25 20:34:36 -07:00
Diego Waxemberg b0b9a614f3 Adding tvOS target 2018-08-25 20:33:03 -07:00
Diego Waxemberg 76ab786e94 Adding keyboard support 2018-08-25 15:22:32 -07:00
Cameron Gutman 8afcdc92d4 Update common-c to fix GFE 3.14.1 audio bitrate issues 2018-08-25 00:32:33 -07:00
Cameron Gutman 907a2cd59b Update README.md 2018-08-24 23:20:16 -07:00
Cameron Gutman a85b931d87 Enable stale and no-response bots to reduce inactive issues 2018-08-12 00:22:30 -07:00
Diego Waxemberg 62f1b3e595 Bumping app version for 2.1.0 2018-06-17 16:43:42 -07:00
Cameron Gutman b9381ad027 Update common-c to fix RTSP handshake error on quick reconnect 2018-06-09 21:54:25 -07:00
Cameron Gutman 98d01f7775 Ensure the C strings stay valid until connection-time. Fixes #228 2018-06-09 20:04:40 -07:00
Cameron Gutman de39c87185 Allow the stream to be inactive for 10 seconds before terminating 2018-06-09 16:29:48 -07:00
Cameron Gutman 10a00b0d28 Avoid leaving application lifetime notifications registered all the time 2018-06-09 16:28:37 -07:00
Diego Waxemberg 4d8788add3 Bumping app version for 2.0.0 2018-06-02 21:31:53 -07:00
Cameron Gutman 9c019be322 Fix subtle database corruption that would cause games common to 2 or more PCs to jump between them based on the last app list fetched 2018-06-02 20:44:12 -07:00
Cameron Gutman 80254513c4 Avoid blocking the main thread when delaying for button presses 2018-06-02 19:19:24 -07:00
Cameron Gutman 5a13e44fdb Use an exponential-ish bitrate slider 2018-06-02 19:09:38 -07:00
Cameron Gutman ec0a462092 Remove custom style on UISegmentedControl, since the default actually looks better 2018-06-02 18:14:20 -07:00
Cameron Gutman 25c7b18147 Add UI settings for HEVC, optimizing games, PC audio, multi-controller. Remove remote setting since it doesn't do much and might confuse users. 2018-06-02 18:13:32 -07:00
Cameron Gutman fb238e0d1d Log launch and resume requests 2018-06-02 17:50:48 -07:00
Cameron Gutman bfc0b89926 Add many new preferences to settings DB. Default HEVC to off due to reference frame invalidation not working 2018-06-02 17:10:52 -07:00
Cameron Gutman 2f0e219726 Always set the stream config from the data from the actual app we're going to start streaming, not just the one selected 2018-06-02 16:38:02 -07:00
Cameron Gutman 2305f33572 Fix scrolling jank on app list 2018-06-02 16:31:16 -07:00
Cameron Gutman d17f2f9dee Plumb multi-controller toggle 2018-06-02 13:52:58 -07:00
Cameron Gutman a28049a58c Plumb several new options into stream config and launch requests: optimizeGameSettings, playAudioOnPC, surround sound, and HDR 2018-06-02 13:25:53 -07:00
Cameron Gutman 2d2f71dcab Parse and persist ServerCodecModeSupport attribute for hosts 2018-06-02 13:10:10 -07:00
Cameron Gutman 59506a2364 Parse and remember the IsHdrSupported app attribute 2018-06-02 12:33:13 -07:00
Cameron Gutman 2cec53ab6b Fix typo in analog stick deadzone area 2018-06-02 01:38:52 -07:00
Cameron Gutman 42ef67d939 Let the spinner spin until video appears 2018-06-02 01:02:10 -07:00
Cameron Gutman 30ec68cd4e Ensure the progress messages aren't obscured by the video layer 2018-06-02 01:01:45 -07:00
Cameron Gutman 941c414930 Fix R2 being improperly sized on 3x DPI 2018-06-01 22:20:37 -07:00
Cameron Gutman b918005555 Improve OSC usability for iPad. Fixes #317 2018-06-01 22:09:24 -07:00
Cameron Gutman c74fa4d5b0 Change manual simple OSC mode to have useful buttons. Fixes #218 2018-06-01 21:18:41 -07:00
Cameron Gutman 1a71e040c0 Fix deadzones to dynamically detect controls on screen and add a deadzone for the analog sticks 2018-06-01 21:17:47 -07:00
Cameron Gutman 236c9d5c6e Use more aggressive mDNS querying for faster PC discovery, particularly in newly online PC scenarios 2018-06-01 20:23:39 -07:00
Cameron Gutman 862b8632e3 Fix DiscoveryManager trying to start discovery again on all machines when any machine is discovered 2018-06-01 19:50:32 -07:00
Cameron Gutman cdb5f51ab8 Quiet down PC discovery logging 2018-06-01 19:38:52 -07:00
Cameron Gutman 314f32388a Use the app name in the "Starting App" text 2018-06-01 19:11:10 -07:00
Cameron Gutman 39b0b429d6 Show loading spinner while waiting for apps to quit 2018-06-01 18:58:25 -07:00
Cameron Gutman 23a12ed101 Actually set CAPABILITY_DIRECT_SUBMIT and fix the bug it hides 2018-06-01 17:00:02 -07:00
Cameron Gutman 653a789110 Refresh app and host state when returning from streaming via error 2018-06-01 00:47:44 -07:00
Cameron Gutman 04b2fccaef Only interact with the AVSampleBufferDisplayLayer on the main thread 2018-06-01 00:31:41 -07:00
Cameron Gutman ec0124e255 Ensure we properly restart discovery after closing Control Center 2018-06-01 00:07:25 -07:00
Cameron Gutman 132a1fc410 Don't attempt to pair on entry to foreground if we've become unpaired 2018-05-31 01:53:09 -07:00
Cameron Gutman 63297c8483 Lan -> LAN 2018-05-31 01:37:13 -07:00
Cameron Gutman 5fd2c29a73 Avoid displaying error dialogs on passive actions like returning to foreground 2018-05-31 01:36:10 -07:00
Cameron Gutman e6e7ec9202 Avoid animating the loading frame 2018-05-31 01:22:03 -07:00
Cameron Gutman fcc770d820 Avoid firing off HTTP requests as we enter the background when entering home from streaming 2018-05-31 00:46:41 -07:00
Cameron Gutman 4243bcd5d1 Ensure double-starting or stopping DiscoveryManager and MDNSManager doesn't cause side-effects, since we do this when Home is pressed while streaming 2018-05-31 00:33:24 -07:00
Cameron Gutman 67b08cd8b9 Use LiInterruptConnection() to terminate connection attempts more quickly 2018-05-30 22:06:20 -07:00
Cameron Gutman e697938afc Re-enable the idle timer when streaming has ended 2018-05-30 22:04:06 -07:00
Cameron Gutman 275f6df8c1 Fix some minor issues with saving CoreData objects 2018-05-30 01:19:46 -07:00
Cameron Gutman aea2067e1c Silence the firehose of debug logging statements 2018-05-29 20:17:48 -07:00
Cameron Gutman b5241a0c30 Ok -> OK 2018-05-28 18:20:59 -07:00
Cameron Gutman 6cac2348b4 Add help button to error dialogs and use default style for non-destructive actions 2018-05-28 18:00:19 -07:00
Cameron Gutman 0293df7748 Rewrite audio playback to simplify code, reduce allocations, and eliminate priority inversion on the queue lock. This completely eliminates clicks and pops in audio for me on my iPhone SE and the emulator. 2018-05-28 17:38:09 -07:00
Cameron Gutman 391872ddb8 Ignore unsupported gamepads 2018-05-28 15:11:23 -07:00
Cameron Gutman 4bafefbe16 streamingRemotely should not be optional 2018-05-28 13:51:00 -07:00
Cameron Gutman 812f3f3717 Add 360p resolution support 2018-05-28 13:50:20 -07:00
Cameron Gutman e498736af6 Fix Moonlight macOS build 2018-05-28 13:25:18 -07:00
Cameron Gutman 1842a11f56 Fix errors saving PCs that were discovered via mDNS 2018-05-28 13:19:49 -07:00
Cameron Gutman 85f81ca8c1 Use CoreData codegen to resolve warnings about missing getters/setters on our DB objects 2018-05-28 12:40:15 -07:00
Cameron Gutman b792629943 Flip the pull arrow when the settings are open 2018-05-28 12:04:38 -07:00
Cameron Gutman 7df76fcd93 Allow the settings view to scroll on smaller iPhones 2018-05-28 11:39:04 -07:00
Cameron Gutman 8b60e79406 Minor common-c update to address a (currently) harmless warning 2018-05-28 10:49:41 -07:00
Cameron Gutman fcb6c7386c Update common-c to receive fixes to lower audio bitrate at low bandwidth settings 2018-05-28 10:36:17 -07:00
Cameron Gutman 2729fad47c Commit IDEWorkspaceChecks.plist 2018-05-28 10:35:36 -07:00
Cameron Gutman 41eb933e11 Update to XCode 9.3 recommended settings and fix "implict retain self" warnings 2018-04-21 22:20:27 -07:00
Felix Kratz f759f719e6 Dark Mode & Stream Overlay [macOS] (#315)
* dark mode & stream overlay

* removed all redundant imports

* update for the new xcode version with fixes for the new 'implicitly retains self warning'

* reworked the overlay view

* cleaning up unused variables

* small corrections
2018-04-22 00:44:22 -04:00
Cameron Gutman 74283a6763 Use HEVC on iOS 11.3 and later. Fixes #296 2018-04-10 18:57:02 -07:00
Cameron Gutman 0a19c5950a Update organization name in both projects 2018-03-27 00:38:47 -07:00
Cameron Gutman e8832ed746 Refactor new macOS code to reduce #ifs, increase shared code, and fix warnings 2018-03-27 00:34:38 -07:00
Felix Kratz 6cc165b589 Port for macOS (#311)
* merged moonlight-mac with moonlight-ios

* reverted to the original project.pbxproj

* cleaned up the code, fixed lots of unnecessary code duplications

* multicontroller support (not tested)

* new class that can be used for further modularization of the MainFrameViewController
2018-03-26 23:50:40 -07:00
Cameron Gutman 1c86c4485d Disable HEVC until iPhone X and other issues can be sorted out. See #296 2018-02-24 21:16:02 -08:00
Cameron Gutman 127e5ed54a Migrate to Swift 4.0 with @objc inferencing fixes for Moonlight build target 2018-02-19 14:43:50 -08:00
Cameron Gutman 3fee5d0a62 Fix build of unit tests 2018-02-19 14:33:20 -08:00
Cameron Gutman c913b3f04f Enable XCode 9 recommended warnings and fix occurrences of them in the code 2018-02-19 14:26:12 -08:00
Cameron Gutman dfd30c0624 Remove some old and unused references from the XCode project 2018-02-19 14:05:03 -08:00
Cameron Gutman 5117a6ede5 Replace dylibs with tbd 2018-02-19 13:56:50 -08:00
Cameron Gutman fb9c071243 Update moonlight-common-c 2018-02-19 00:25:57 -08:00
Cameron Gutman d0a8f0e0f1 Update README.md 2018-01-20 17:03:09 -08:00
Cameron Gutman 6bb6c40bec Report attached gamepads during launch to fix L4D2 2018-01-20 14:37:33 -08:00
Cameron Gutman af3bc82d0f Fix drag cancellation triggering on any movement and other drag bugs. Fixes #288 2018-01-20 01:51:50 -08:00
Cameron Gutman b9d5d94d70 Fix support for IPv6 literals 2018-01-20 01:30:29 -08:00
Cameron Gutman 43f4804194 Fix wake-on-lan failure on LAN after ARP cache expiration of target PC. Fixes #293 2018-01-19 20:18:39 -08:00
Diego Waxemberg be26ce9a8f Bumping app version for v1.7.0 2018-01-10 19:35:41 -08:00
Cameron Gutman ca562e2251 Update common-c to fix streaming on GFE 3.12 2018-01-10 00:45:38 -08:00
Cameron Gutman d7ee0916e9 Avoid crashing on iOS 11 betas released before iPhone X 2017-12-27 23:24:53 -08:00
Cameron Gutman 3bc9e050f1 Update common-c and use codec masks rather than exact formats 2017-12-03 11:33:11 -08:00
Cameron Gutman 4b9bd8398b Update for video decoder API changes 2017-11-18 18:17:22 -08:00
Diego Waxemberg 20a61dd9f4 Bumping app version for v1.6.0 2017-11-12 19:57:36 -08:00
Cameron Gutman dc4ecbf262 Update common-c to address HEVC corruption at 1080p60 2017-11-10 01:36:00 -08:00
Diego Waxemberg 68f55235ec Bumping app version for v1.5.0 2017-10-17 21:28:30 -07:00
Cameron Gutman 5434b653c7 Reduce bitrate when streaming HEVC to reduce bandwidth at better IQ than H.264 2017-10-17 01:07:41 -07:00
Cameron Gutman 25d7af9453 Fix pairing and game quitting issues with GFE 3.10 2017-10-16 23:59:00 -07:00
Diego Waxemberg 5584083809 Bumping app version for 1.4.0 2017-09-23 21:48:22 -07:00
Cameron Gutman 9f9aec70b0 Add 1024x1024 app icon 2017-09-23 21:47:33 -07:00
Cameron Gutman a8d1770871 Fix Main Thread Checker safety issue in DataManager 2017-09-15 00:51:37 -07:00
Cameron Gutman d0d60b8ffc Suppress system gesture detection during streaming 2017-09-15 00:30:51 -07:00
Cameron Gutman 86f7112789 Adjust settings view for iPhone X 2017-09-14 23:12:52 -07:00
Cameron Gutman da1115e529 Disable reference frame invalidation for HEVC 2017-09-14 22:03:20 -07:00
Cameron Gutman 593da7ef3e Merge branch 'hevc' 2017-09-14 21:54:46 -07:00
Cameron Gutman de5377a95d Only use HEVC if the client has hardware-accelerated decoding 2017-09-14 19:50:53 -07:00
Cameron Gutman 7d7500b187 Suppress accidental movements during drags and clicks 2017-09-10 23:14:13 -07:00
Cameron Gutman 45dbd46da1 Fix mouse cursor jumping on right click gesture where only the 2nd finger is lifted to complete the gesture 2017-09-10 22:58:43 -07:00
Cameron Gutman 89d8bc6009 Fix icon changing between launch screen and main view controller 2017-09-10 22:22:32 -07:00
Cameron Gutman b99988836a Implement HEVC decoding for iOS 11 devices 2017-09-10 00:00:40 -07:00
Cameron Gutman 3e94613eb3 Update common-c 2017-09-02 19:48:09 -07:00
Cameron Gutman 832b9189f8 Fix mDNS overriding manually specified addresses 2017-09-02 19:47:47 -07:00
Cameron Gutman 3464539bd3 Make iconography consistent with other Moonlight clients 2017-09-02 14:54:19 -07:00
Cameron Gutman 815592117d Update common-c 2017-06-11 14:54:17 -07:00
Cameron Gutman b51341ef54 Update to latest moonlight-common-c 2017-06-11 13:36:23 -07:00
Cameron Gutman 9ffbb5f155 Update moonlight-common-c with latency and FEC improvements 2017-06-08 21:09:21 -07:00
Cameron Gutman 9716867ade Update moonlight-common-c 2017-06-05 22:27:15 -07:00
Diego Waxemberg 7c141c1fd2 Bumping version for 1.2.0 2017-05-11 22:36:28 -07:00
Cameron Gutman 42c4e7ca58 Enable reference frame invalidation 2017-05-11 20:46:09 -07:00
Cameron Gutman 6a6ea4a79a Update moonlight-common-c 2017-05-11 20:12:06 -07:00
Cameron Gutman 5d0ca8451d Fix active game not being set when returning from streaming 2017-05-07 23:32:31 -07:00
Cameron Gutman 4641f68eb8 Update common-c with critical FEC fixes 2017-05-07 23:16:00 -07:00
Cameron Gutman dd33f57b9a Fix display of pairing failure dialog when machine is streaming 2017-05-07 16:51:57 -07:00
Cameron Gutman f6b225ca9c Update common-c with FEC changes 2017-05-06 16:09:56 -07:00
Cameron Gutman 4ac3f11ff8 Ensure that OSC always has Player 1 reserved 2017-02-06 19:17:27 -08:00
Cameron Gutman 62aaf27293 Update to new common-c to show the correct number of controllers on the host PC 2017-02-04 20:16:18 -08:00
345 changed files with 36040 additions and 25541 deletions
+8
View File
@@ -0,0 +1,8 @@
# ProBot No Response (https://probot.github.io/apps/no-response/)
daysUntilClose: 7
responseRequiredLabel: 'need more info'
closeComment: >
This issue has been automatically closed because there was no response to a
request for more information from the issue opener. Please leave a comment or
open a new issue if you have additional information related to this issue.
+14
View File
@@ -0,0 +1,14 @@
# ProBot Stale (https://probot.github.io/apps/stale/)
daysUntilStale: 90
daysUntilClose: 7
exemptLabels:
- accepted
- bug
- enhancement
- meta
staleLabel: stale
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs.
closeComment: false
+25
View File
@@ -0,0 +1,25 @@
language: objective-c
git:
depth: 1
matrix:
include:
- os: osx
osx_image: xcode11
env:
- SCHEME="Moonlight"
- TARGET="platform=iOS Simulator,OS=12.1,name=iPhone SE"
- os: osx
osx_image: xcode11
env:
- SCHEME="Moonlight"
- TARGET="platform=iOS Simulator,OS=12.1,name=iPad Pro (9.7-inch)"
- os: osx
osx_image: xcode11
env:
- SCHEME="Moonlight TV"
- TARGET="platform=tvOS Simulator,OS=12.1,name=Apple TV 4K"
script:
- set -o pipefail && xcodebuild -project Moonlight.xcodeproj -scheme "$SCHEME" -destination "$TARGET" build | xcpretty
+11 -27
View File
@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="8191" systemVersion="15A279b" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina6_1" orientation="landscape" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8154"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<navigationController id="vV9-32-ww6">
<simulatedOrientationMetrics key="simulatedOrientationMetrics" orientation="landscapeRight"/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="y2P-aE-Haa">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<rect key="frame" x="0.0" y="0.0" width="896" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<animations/>
<color key="barTintColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<color key="barTintColor" red="0.66666668653488159" green="0.66666668653488159" blue="0.66666668653488159" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</navigationBar>
<viewControllers>
<viewController id="l6V-ue-5Om">
@@ -22,30 +22,14 @@
<viewControllerLayoutGuide type="bottom" id="gBD-cy-cth"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="6lF-fc-kpK">
<rect key="frame" x="0.0" y="0.0" width="896" height="414"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<animations/>
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<color key="backgroundColor" red="0.3333333432674408" green="0.3333333432674408" blue="0.3333333432674408" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<navigationItem key="navigationItem" id="rW0-BH-s7l">
<barButtonItem key="leftBarButtonItem" enabled="NO" id="d9R-Zo-Uml">
<button key="customView" opaque="NO" contentMode="center" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="9JC-B5-BAE">
<rect key="frame" x="-23" y="-15" width="46" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<animations/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<state key="normal" title="Moonlight" image="Logo">
<color key="titleColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<color key="titleShadowColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
</state>
<variation key="widthClass=regular" preservesSuperviewLayoutMargins="NO"/>
</button>
</barButtonItem>
</navigationItem>
<navigationItem key="navigationItem" id="rW0-BH-s7l"/>
</viewController>
</viewControllers>
<point key="canvasLocation" x="34" y="149"/>
</navigationController>
</objects>
<resources>
<image name="Logo" width="32" height="32"/>
</resources>
</document>
+8
View File
@@ -6,11 +6,19 @@
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
//
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
#else
#import <Cocoa/Cocoa.h>
@interface AppDelegate : NSObject <NSApplicationDelegate>
@property (readonly, strong) NSPersistentContainer *persistentContainer;
@property (strong, nonatomic) NSWindow *window;
#endif
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
+94 -38
View File
@@ -16,35 +16,17 @@
static NSOperationQueue* mainQueue;
#if TARGET_OS_TV
static NSString* DB_NAME = @"Moonlight_tvOS.bin";
#elif TARGET_OS_IPHONE
static NSString* DB_NAME = @"Limelight_iOS.sqlite";
#else
static NSString* DB_NAME = @"moonlight_mac.sqlite";
#endif
#if TARGET_OS_IPHONE
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[UILabel appearance] setFont:[UIFont fontWithName:@"Roboto-Regular" size:[UIFont systemFontSize]]];
[[UIButton appearance].titleLabel setFont:[UIFont fontWithName:@"Roboto-Regular" size:[UIFont systemFontSize]]];
// Generate selected segment background image
CGSize borderImageSize = CGSizeMake(1.f, 100.f);
UIGraphicsBeginImageContext(borderImageSize);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillRect(context, CGRectMake(0.f, borderImageSize.height * 0.8, borderImageSize.width, borderImageSize.height));
UIImage *selectedSegmentBG = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Clear default border and background color
[[UISegmentedControl appearance] setBackgroundImage:[[UIImage alloc] init] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UISegmentedControl appearance] setDividerImage:[[UIImage alloc] init] forLeftSegmentState:UIControlStateNormal rightSegmentState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
// Set selected segment background image
[[UISegmentedControl appearance] setBackgroundImage:[selectedSegmentBG imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
// Change font on UISegmentedControl
[[UISegmentedControl appearance] setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:
[UIColor whiteColor], NSForegroundColorAttributeName,
[UIFont fontWithName:@"Roboto-Regular" size:[UIFont systemFontSize]], NSFontAttributeName, nil] forState:UIControlStateNormal];
return YES;
}
@@ -57,7 +39,7 @@ static NSOperationQueue* mainQueue;
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
@@ -76,15 +58,36 @@ static NSOperationQueue* mainQueue;
// Saves changes in the application's managed object context before the application terminates.
[self saveContext];
}
#else
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
[self saveContext];
}
#endif
- (void)saveContext
{
NSError *error = nil;
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
if (managedObjectContext != nil) {
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
Log(LOG_E, @"Critical database error: %@, %@", error, [error userInfo]);
}
[managedObjectContext performBlock:^{
if (![managedObjectContext hasChanges]) {
return;
}
NSError *error = nil;
if (![managedObjectContext save:&error]) {
Log(LOG_E, @"Critical database error: %@, %@", error, [error userInfo]);
}
#if TARGET_OS_TV
NSData* dbData = [NSData dataWithContentsOfURL:[[[[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:DB_NAME]];
[[NSUserDefaults standardUserDefaults] setObject:dbData forKey:DB_NAME];
#endif
}];
}
}
@@ -125,19 +128,30 @@ static NSOperationQueue* mainQueue;
return _persistentStoreCoordinator;
}
NSURL *storeURL = [self getStoreURL];
NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
NSString* storeType;
#if TARGET_OS_TV
// Use a binary store for tvOS since we will need exclusive access to the file
// to serialize into NSUserDefaults.
storeType = NSBinaryStoreType;
#else
storeType = NSSQLiteStoreType;
#endif
// We must ensure the persistent store is ready to opened
[self preparePersistentStore];
if (![_persistentStoreCoordinator addPersistentStoreWithType:storeType configuration:nil URL:[self getStoreURL] options:options error:&error]) {
// Log the error
Log(LOG_E, @"Critical database error: %@, %@", error, [error userInfo]);
// Drop the database
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil];
[self dropDatabase];
// Try again
return [self persistentStoreCoordinator];
@@ -154,8 +168,50 @@ static NSOperationQueue* mainQueue;
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
- (void) dropDatabase
{
// Delete the file on disk
[[NSFileManager defaultManager] removeItemAtURL:[self getStoreURL] error:nil];
#if TARGET_OS_TV
// Also delete the copy in the NSUserDefaults on tvOS
[[NSUserDefaults standardUserDefaults] removeObjectForKey:DB_NAME];
#endif
}
- (void) preparePersistentStore
{
#if TARGET_OS_TV
// On tvOS, we may need to inflate the DB from NSUserDefaults
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cacheDirectory = [paths objectAtIndex:0];
NSString *dbPath = [cacheDirectory stringByAppendingPathComponent:DB_NAME];
// Always prefer the on disk version
if (![[NSFileManager defaultManager] fileExistsAtPath:dbPath]) {
// If that is unavailable, inflate it from NSUserDefaults
NSData* data = [[NSUserDefaults standardUserDefaults] dataForKey:DB_NAME];
if (data != nil) {
Log(LOG_I, @"Inflating database from NSUserDefaults");
[data writeToFile:dbPath atomically:YES];
}
else {
Log(LOG_I, @"No database on disk or in NSUserDefaults");
}
}
else {
Log(LOG_I, @"Using cached database");
}
#endif
}
- (NSURL*) getStoreURL {
return [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Limelight_iOS.sqlite"];
#if TARGET_OS_TV
// We use the cache folder to store our database on tvOS
return [[[[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:DB_NAME];
#else
return [[self applicationDocumentsDirectory] URLByAppendingPathComponent:DB_NAME];
#endif
}
@end
+2 -4
View File
@@ -6,16 +6,14 @@
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface CryptoManager : NSObject
+ (void) generateKeyPairUsingSSl;
+ (void) generateKeyPairUsingSSL;
+ (NSData*) readCertFromFile;
+ (NSData*) readKeyFromFile;
+ (NSData*) readP12FromFile;
+ (NSData*) getSignatureFromCert:(NSData*)cert;
+ (NSData*) nullTerminateString:(NSData*)data;
+ (NSData*) pemToDer:(NSData*)pemCertBytes;
- (NSData*) createAESKeyFromSaltSHA1:(NSData*)saltedPIN;
- (NSData*) createAESKeyFromSaltSHA256:(NSData*)saltedPIN;
+94 -46
View File
@@ -87,17 +87,28 @@ static NSData* p12 = nil;
return (((int)[data length] + 15) / 16) * 16;
}
+ (NSData*) nullTerminateString:(NSData*)data {
NSMutableData* mutData = [NSMutableData dataWithData:data];
[mutData appendBytes:"" length:1];
return mutData;
+ (NSData*) pemToDer:(NSData*)pemCertBytes {
X509* x509;
BIO* bio = BIO_new_mem_buf([pemCertBytes bytes], (int)[pemCertBytes length]);
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
BIO_free(bio);
bio = BIO_new(BIO_s_mem());
i2d_X509_bio(bio, x509);
BUF_MEM* mem;
BIO_get_mem_ptr(bio, &mem);
NSData* ret = [[NSData alloc] initWithBytes:mem->data length:mem->length];
BIO_free(bio);
return ret;
}
- (bool) verifySignature:(NSData *)data withSignature:(NSData*)signature andCert:(NSData*)cert {
const char* buffer = [[CryptoManager nullTerminateString:cert] bytes];
X509* x509;
BIO* bio = BIO_new(BIO_s_mem());
BIO_puts(bio, buffer);
BIO* bio = BIO_new_mem_buf([cert bytes], (int)[cert length]);
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
BIO_free(bio);
@@ -122,9 +133,7 @@ static NSData* p12 = nil;
}
- (NSData *)signData:(NSData *)data withKey:(NSData *)key {
const char* buffer = [[CryptoManager nullTerminateString:key] bytes];
BIO* bio = BIO_new(BIO_s_mem());
BIO_puts(bio, buffer);
BIO* bio = BIO_new_mem_buf([key bytes], (int)[key length]);
EVP_PKEY* pkey;
pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
@@ -159,57 +168,60 @@ static NSData* p12 = nil;
return signedData;
}
// TODO: these three methods are almost identical, fix the copy-pasta
+ (NSData*) readCryptoObject:(NSString*)item {
#if TARGET_OS_TV
return [[NSUserDefaults standardUserDefaults] dataForKey:item];
#else
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *file = [documentsDirectory stringByAppendingPathComponent:item];
return [NSData dataWithContentsOfFile:file];
#endif
}
+ (void) writeCryptoObject:(NSString*)item data:(NSData*)data {
#if TARGET_OS_TV
[[NSUserDefaults standardUserDefaults] setObject:data forKey:item];
#else
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *file = [documentsDirectory stringByAppendingPathComponent:item];
[data writeToFile:file atomically:NO];
#endif
}
+ (NSData*) readCertFromFile {
if (cert == nil) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *certFile = [documentsDirectory stringByAppendingPathComponent:@"client.crt"];
cert = [NSData dataWithContentsOfFile:certFile];
cert = [CryptoManager readCryptoObject:@"client.crt"];
}
return cert;
}
+ (NSData*) readP12FromFile {
if (p12 == nil) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *p12File = [documentsDirectory stringByAppendingPathComponent:@"client.p12"];
p12 = [NSData dataWithContentsOfFile:p12File];
p12 = [CryptoManager readCryptoObject:@"client.p12"];
}
return p12;
}
+ (NSData*) readKeyFromFile {
if (key == nil) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *keyFile = [documentsDirectory stringByAppendingPathComponent:@"client.key"];
key = [NSData dataWithContentsOfFile:keyFile];
key = [CryptoManager readCryptoObject:@"client.key"];
}
return key;
}
+ (bool) keyPairExists {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *keyFile = [documentsDirectory stringByAppendingPathComponent:@"client.key"];
NSString *p12File = [documentsDirectory stringByAppendingPathComponent:@"client.p12"];
NSString *certFile = [documentsDirectory stringByAppendingPathComponent:@"client.crt"];
bool keyFileExists = [[NSFileManager defaultManager] fileExistsAtPath:keyFile];
bool p12FileExists = [[NSFileManager defaultManager] fileExistsAtPath:p12File];
bool certFileExists = [[NSFileManager defaultManager] fileExistsAtPath:certFile];
bool keyFileExists = [CryptoManager readCryptoObject:@"client.key"] != nil;
bool p12FileExists = [CryptoManager readCryptoObject:@"client.p12"] != nil;
bool certFileExists = [CryptoManager readCryptoObject:@"client.crt"] != nil;
return keyFileExists && p12FileExists && certFileExists;
}
+ (NSData *)getSignatureFromCert:(NSData *)cert {
const char* buffer = [[CryptoManager nullTerminateString:cert] bytes];
X509* x509;
BIO* bio = BIO_new(BIO_s_mem());
BIO_puts(bio, buffer);
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
BIO* bio = BIO_new_mem_buf([cert bytes], (int)[cert length]);
X509* x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
if (!x509) {
Log(LOG_E, @"Unable to parse certificate in memory!");
@@ -218,23 +230,59 @@ static NSData* p12 = nil;
return [NSData dataWithBytes:x509->signature->data length:x509->signature->length];
}
+ (void) generateKeyPairUsingSSl {
+ (NSData*)getKeyFromCertKeyPair:(CertKeyPair*)certKeyPair {
BIO* bio = BIO_new(BIO_s_mem());
PEM_write_bio_PrivateKey(bio, certKeyPair->pkey, NULL, NULL, 0, NULL, NULL);
BUF_MEM* mem;
BIO_get_mem_ptr(bio, &mem);
NSData* data = [NSData dataWithBytes:mem->data length:mem->length];
BIO_free(bio);
return data;
}
+ (NSData*)getP12FromCertKeyPair:(CertKeyPair*)certKeyPair {
BIO* bio = BIO_new(BIO_s_mem());
i2d_PKCS12_bio(bio, certKeyPair->p12);
BUF_MEM* mem;
BIO_get_mem_ptr(bio, &mem);
NSData* data = [NSData dataWithBytes:mem->data length:mem->length];
BIO_free(bio);
return data;
}
+ (NSData*)getCertFromCertKeyPair:(CertKeyPair*)certKeyPair {
BIO* bio = BIO_new(BIO_s_mem());
PEM_write_bio_X509(bio, certKeyPair->x509);
BUF_MEM* mem;
BIO_get_mem_ptr(bio, &mem);
NSData* data = [NSData dataWithBytes:mem->data length:mem->length];
BIO_free(bio);
return data;
}
+ (void) generateKeyPairUsingSSL {
static dispatch_once_t pred;
dispatch_once(&pred, ^{
if (![CryptoManager keyPairExists]) {
Log(LOG_I, @"Generating Certificate... ");
CertKeyPair certKeyPair = generateCertKeyPair();
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
NSString* certFile = [documentsDirectory stringByAppendingPathComponent:@"client.crt"];
NSString* keyPairFile = [documentsDirectory stringByAppendingPathComponent:@"client.key"];
NSString* p12File = [documentsDirectory stringByAppendingPathComponent:@"client.p12"];
NSData* certData = [CryptoManager getCertFromCertKeyPair:&certKeyPair];
NSData* p12Data = [CryptoManager getP12FromCertKeyPair:&certKeyPair];
NSData* keyData = [CryptoManager getKeyFromCertKeyPair:&certKeyPair];
//Log(LOG_D, @"Writing cert and key to: \n%@\n%@", certFile, keyPairFile);
saveCertKeyPair([certFile UTF8String], [p12File UTF8String], [keyPairFile UTF8String], certKeyPair);
freeCertKeyPair(certKeyPair);
[CryptoManager writeCryptoObject:@"client.crt" data:certData];
[CryptoManager writeCryptoObject:@"client.p12" data:p12Data];
[CryptoManager writeCryptoObject:@"client.key" data:keyData];
Log(LOG_I, @"Certificate created");
}
});
-2
View File
@@ -6,8 +6,6 @@
// Copyright © 2015 Moonlight Stream. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface IdManager : NSObject
+ (NSString*) getUniqueId;
+2 -49
View File
@@ -19,7 +19,7 @@ static const int NUM_YEARS = 10;
int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years);
int add_ext(X509 *cert, int nid, char *value);
struct CertKeyPair generateCertKeyPair() {
struct CertKeyPair generateCertKeyPair(void) {
BIO *bio_err;
X509 *x509 = NULL;
EVP_PKEY *pkey = NULL;
@@ -62,21 +62,6 @@ void freeCertKeyPair(struct CertKeyPair certKeyPair) {
PKCS12_free(certKeyPair.p12);
}
void saveCertKeyPair(const char* certFile, const char* p12File, const char* keyPairFile, CertKeyPair certKeyPair) {
FILE* certFilePtr = fopen(certFile, "w");
FILE* keyPairFilePtr = fopen(keyPairFile, "w");
FILE* p12FilePtr = fopen(p12File, "wb");
//TODO: error check
PEM_write_PrivateKey(keyPairFilePtr, certKeyPair.pkey, NULL, NULL, 0, NULL, NULL);
PEM_write_X509(certFilePtr, certKeyPair.x509);
i2d_PKCS12_fp(p12FilePtr, certKeyPair.p12);
fclose(p12FilePtr);
fclose(certFilePtr);
fclose(keyPairFilePtr);
}
int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) {
X509 *x;
EVP_PKEY *pk;
@@ -122,13 +107,7 @@ int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) {
*/
X509_set_issuer_name(x, name);
/* Add various extensions: standard extensions */
add_ext(x, NID_basic_constraints, "critical,CA:TRUE");
add_ext(x, NID_key_usage, "critical,keyCertSign,cRLSign");
add_ext(x, NID_subject_key_identifier, "hash");
if (!X509_sign(x, pk, EVP_sha1())) {
if (!X509_sign(x, pk, EVP_sha256())) {
goto err;
}
@@ -139,29 +118,3 @@ int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) {
err:
return(0);
}
/* Add extension using V3 code: we can set the config file as NULL
* because we wont reference any other sections.
*/
int add_ext(X509 *cert, int nid, char *value)
{
X509_EXTENSION *ex;
X509V3_CTX ctx;
/* This sets the 'context' of the extensions. */
/* No configuration database */
X509V3_set_ctx_nodb(&ctx);
/* Issuer and subject certs: both the target since it is self signed,
* no request and no CRL
*/
X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value);
if (!ex) {
return 0;
}
X509_add_ext(cert, ex, -1);
X509_EXTENSION_free(ex);
return 1;
}
+1 -2
View File
@@ -18,8 +18,7 @@ typedef struct CertKeyPair {
PKCS12 *p12;
} CertKeyPair;
struct CertKeyPair generateCertKeyPair();
struct CertKeyPair generateCertKeyPair(void);
void freeCertKeyPair(CertKeyPair);
void saveCertKeyPair(const char* certFile, const char* p12File, const char* keyPairFile, CertKeyPair certKeyPair);
#endif
@@ -1,25 +0,0 @@
//
// App+CoreDataProperties.h
// Moonlight
//
// Created by Diego Waxemberg on 7/10/15.
// Copyright © 2015 Limelight Stream. All rights reserved.
//
// Delete this file and regenerate it using "Create NSManagedObject Subclass…"
// to keep your implementation up to date with your model.
//
#import "App.h"
NS_ASSUME_NONNULL_BEGIN
@interface App (CoreDataProperties)
@property (nullable, nonatomic, retain) NSString *id;
@property (nullable, nonatomic, retain) NSData *image;
@property (nullable, nonatomic, retain) NSString *name;
@property (nullable, nonatomic, retain) Host *host;
@end
NS_ASSUME_NONNULL_END
-18
View File
@@ -1,18 +0,0 @@
//
// App.h
// Moonlight
//
// Created by Diego Waxemberg on 7/10/15.
// Copyright © 2015 Limelight Stream. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@class Host;
@interface App : NSManagedObject
@end
#import "App+CoreDataProperties.h"
-13
View File
@@ -1,13 +0,0 @@
//
// App.m
// Moonlight
//
// Created by Diego Waxemberg on 7/10/15.
// Copyright © 2015 Limelight Stream. All rights reserved.
//
#import "App.h"
@implementation App
@end
+11 -3
View File
@@ -6,7 +6,6 @@
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "AppDelegate.h"
#import "TemporaryHost.h"
#import "TemporaryApp.h"
@@ -14,12 +13,21 @@
@interface DataManager : NSObject
- (void) saveSettingsWithBitrate:(NSInteger)bitrate framerate:(NSInteger)framerate height:(NSInteger)height width:(NSInteger)width onscreenControls:(NSInteger)onscreenControls;
- (void) saveSettingsWithBitrate:(NSInteger)bitrate
framerate:(NSInteger)framerate
height:(NSInteger)height
width:(NSInteger)width
onscreenControls:(NSInteger)onscreenControls
remote:(BOOL)streamingRemotely
optimizeGames:(BOOL)optimizeGames
multiController:(BOOL)multiController
audioOnPC:(BOOL)audioOnPC
useHevc:(BOOL)useHevc
enableHdr:(BOOL)enableHdr;
- (NSArray*) getHosts;
- (void) updateHost:(TemporaryHost*)host;
- (void) updateAppsForExistingHost:(TemporaryHost *)host;
- (void) updateIconForExistingApp:(TemporaryApp*)app;
- (void) removeHost:(TemporaryHost*)host;
- (void) removeApp:(TemporaryApp*)app;
+38 -27
View File
@@ -9,7 +9,6 @@
#import "DataManager.h"
#import "TemporaryApp.h"
#import "TemporarySettings.h"
#import "Settings.h"
@implementation DataManager {
NSManagedObjectContext *_managedObjectContext;
@@ -19,9 +18,20 @@
- (id) init {
self = [super init];
_appDelegate = [[UIApplication sharedApplication] delegate];
// HACK: Avoid calling [UIApplication delegate] off the UI thread to keep
// Main Thread Checker happy.
if ([NSThread isMainThread]) {
_appDelegate = (AppDelegate *)[[OSApplication sharedApplication] delegate];
}
else {
dispatch_sync(dispatch_get_main_queue(), ^{
self->_appDelegate = (AppDelegate *)[[OSApplication sharedApplication] delegate];
});
}
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator:_appDelegate.persistentStoreCoordinator];
[_managedObjectContext setParentContext:[_appDelegate managedObjectContext]];
return self;
}
@@ -43,16 +53,31 @@
return uid;
}
- (void) saveSettingsWithBitrate:(NSInteger)bitrate framerate:(NSInteger)framerate height:(NSInteger)height width:(NSInteger)width onscreenControls:(NSInteger)onscreenControls {
- (void) saveSettingsWithBitrate:(NSInteger)bitrate
framerate:(NSInteger)framerate
height:(NSInteger)height
width:(NSInteger)width
onscreenControls:(NSInteger)onscreenControls
remote:(BOOL)streamingRemotely
optimizeGames:(BOOL)optimizeGames
multiController:(BOOL)multiController
audioOnPC:(BOOL)audioOnPC
useHevc:(BOOL)useHevc
enableHdr:(BOOL)enableHdr {
[_managedObjectContext performBlockAndWait:^{
Settings* settingsToSave = [self retrieveSettings];
settingsToSave.framerate = [NSNumber numberWithInteger:framerate];
// Bitrate is persisted in kbps
settingsToSave.bitrate = [NSNumber numberWithInteger:bitrate];
settingsToSave.height = [NSNumber numberWithInteger:height];
settingsToSave.width = [NSNumber numberWithInteger:width];
settingsToSave.onscreenControls = [NSNumber numberWithInteger:onscreenControls];
settingsToSave.streamingRemotely = streamingRemotely;
settingsToSave.optimizeGames = optimizeGames;
settingsToSave.multiController = multiController;
settingsToSave.playAudioOnPC = audioOnPC;
settingsToSave.useHevc = useHevc;
settingsToSave.enableHdr = enableHdr;
[self saveData];
}];
@@ -63,8 +88,8 @@
// Add a new persistent managed object if one doesn't exist
Host* parent = [self getHostForTemporaryHost:host withHostRecords:[self fetchRecords:@"Host"]];
if (parent == nil) {
NSEntityDescription* entity = [NSEntityDescription entityForName:@"Host" inManagedObjectContext:_managedObjectContext];
parent = [[Host alloc] initWithEntity:entity insertIntoManagedObjectContext:_managedObjectContext];
NSEntityDescription* entity = [NSEntityDescription entityForName:@"Host" inManagedObjectContext:self->_managedObjectContext];
parent = [[Host alloc] initWithEntity:entity insertIntoManagedObjectContext:self->_managedObjectContext];
}
// Push changes from the temp host to the persistent one
@@ -88,8 +113,8 @@
// Add a new persistent managed object if one doesn't exist
App* parentApp = [self getAppForTemporaryApp:app withAppRecords:appRecords];
if (parentApp == nil) {
NSEntityDescription* entity = [NSEntityDescription entityForName:@"App" inManagedObjectContext:_managedObjectContext];
parentApp = [[App alloc] initWithEntity:entity insertIntoManagedObjectContext:_managedObjectContext];
NSEntityDescription* entity = [NSEntityDescription entityForName:@"App" inManagedObjectContext:self->_managedObjectContext];
parentApp = [[App alloc] initWithEntity:entity insertIntoManagedObjectContext:self->_managedObjectContext];
}
[app propagateChangesToParent:parentApp withHost:parent];
@@ -103,20 +128,6 @@
}];
}
- (void) updateIconForExistingApp:(TemporaryApp*)app {
[_managedObjectContext performBlockAndWait:^{
App* parentApp = [self getAppForTemporaryApp:app withAppRecords:[self fetchRecords:@"App"]];
if (parentApp == nil) {
// The app must exist to be updated
return;
}
parentApp.image = app.image;
[self saveData];
}];
}
- (TemporarySettings*) getSettings {
__block TemporarySettings *tempSettings;
@@ -145,7 +156,7 @@
[_managedObjectContext performBlockAndWait:^{
App* managedApp = [self getAppForTemporaryApp:app withAppRecords:[self fetchRecords:@"App"]];
if (managedApp != nil) {
[_managedObjectContext deleteObject:managedApp];
[self->_managedObjectContext deleteObject:managedApp];
[self saveData];
}
}];
@@ -155,7 +166,7 @@
[_managedObjectContext performBlockAndWait:^{
Host* managedHost = [self getHostForTemporaryHost:host withHostRecords:[self fetchRecords:@"Host"]];
if (managedHost != nil) {
[_managedObjectContext deleteObject:managedHost];
[self->_managedObjectContext deleteObject:managedHost];
[self saveData];
}
}];
@@ -163,7 +174,7 @@
- (void) saveData {
NSError* error;
if (![_managedObjectContext save:&error]) {
if ([_managedObjectContext hasChanges] && ![_managedObjectContext save:&error]) {
Log(LOG_E, @"Unable to save hosts to database: %@", error);
}
@@ -199,7 +210,7 @@
- (App*) getAppForTemporaryApp:(TemporaryApp*)tempApp withAppRecords:(NSArray*)apps {
for (App* app in apps) {
if ([app.id isEqualToString:tempApp.id] &&
[app.host.uuid isEqualToString:app.host.uuid]) {
[app.host.uuid isEqualToString:tempApp.host.uuid]) {
return app;
}
}
@@ -1,38 +0,0 @@
//
// Host+CoreDataProperties.h
// Moonlight
//
// Created by Diego Waxemberg on 7/10/15.
// Copyright © 2015 Limelight Stream. All rights reserved.
//
// Delete this file and regenerate it using "Create NSManagedObject Subclass…"
// to keep your implementation up to date with your model.
//
#import "Host.h"
NS_ASSUME_NONNULL_BEGIN
@interface Host (CoreDataProperties)
@property (nullable, nonatomic, retain) NSString *address;
@property (nullable, nonatomic, retain) NSString *externalAddress;
@property (nullable, nonatomic, retain) NSString *localAddress;
@property (nullable, nonatomic, retain) NSString *mac;
@property (nullable, nonatomic, retain) NSString *name;
@property (nullable, nonatomic, retain) NSNumber *pairState;
@property (nullable, nonatomic, retain) NSString *uuid;
@property (nullable, nonatomic, retain) NSSet *appList;
@end
@interface Host (CoreDataGeneratedAccessors)
- (void)addAppListObject:(NSManagedObject *)value;
- (void)removeAppListObject:(NSManagedObject *)value;
- (void)addAppList:(NSSet *)values;
- (void)removeAppList:(NSSet *)values;
@end
NS_ASSUME_NONNULL_END
-17
View File
@@ -1,17 +0,0 @@
//
// Host.h
// Moonlight
//
// Created by Diego Waxemberg on 7/10/15.
// Copyright © 2015 Limelight Stream. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "Utils.h"
@interface Host : NSManagedObject
@end
#import "Host+CoreDataProperties.h"
-13
View File
@@ -1,13 +0,0 @@
//
// Host.m
// Moonlight
//
// Created by Diego Waxemberg on 7/10/15.
// Copyright © 2015 Limelight Stream. All rights reserved.
//
#import "Host.h"
@implementation Host
@end
-22
View File
@@ -1,22 +0,0 @@
//
// Settings.h
// Moonlight
//
// Created by Diego Waxemberg on 10/28/14.
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@interface Settings : NSManagedObject
@property (nonatomic, retain) NSNumber * bitrate;
@property (nonatomic, retain) NSNumber * framerate;
@property (nonatomic, retain) NSNumber * height;
@property (nonatomic, retain) NSNumber * width;
@property (nonatomic, retain) NSNumber * onscreenControls;
@property (nonatomic, retain) NSString * uniqueId;
@end
-21
View File
@@ -1,21 +0,0 @@
//
// Settings.m
// Moonlight
//
// Created by Diego Waxemberg on 10/28/14.
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
//
#import "Settings.h"
@implementation Settings
@dynamic bitrate;
@dynamic framerate;
@dynamic height;
@dynamic width;
@dynamic onscreenControls;
@dynamic uniqueId;
@end
+3 -3
View File
@@ -6,15 +6,15 @@
// Copyright © 2015 Moonlight Stream. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "TemporaryHost.h"
#import "App.h"
#import "App+CoreDataClass.h"
@interface TemporaryApp : NSObject
@property (nullable, nonatomic, retain) NSString *id;
@property (nullable, nonatomic, retain) NSData *image;
@property (nullable, nonatomic, retain) NSString *name;
@property (nullable, nonatomic, retain) NSString *installPath;
@property (nonatomic) BOOL hdrSupported;
@property (nullable, nonatomic, retain) TemporaryHost *host;
NS_ASSUME_NONNULL_BEGIN
+2 -2
View File
@@ -7,7 +7,6 @@
//
#import "TemporaryApp.h"
#import "App.h"
@implementation TemporaryApp
@@ -15,8 +14,8 @@
self = [self init];
self.id = app.id;
self.image = app.image;
self.name = app.name;
self.hdrSupported = app.hdrSupported;
self.host = tempHost;
return self;
@@ -25,6 +24,7 @@
- (void) propagateChangesToParent:(App*)parent withHost:(Host*)host {
parent.id = self.id;
parent.name = self.name;
parent.hdrSupported = self.hdrSupported;
parent.host = host;
}
+10 -7
View File
@@ -6,23 +6,26 @@
// Copyright © 2015 Moonlight Stream. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Utils.h"
#import "Host.h"
#import "Host+CoreDataClass.h"
@interface TemporaryHost : NSObject
@property (nonatomic) BOOL online;
@property (nonatomic) State state;
@property (nonatomic) PairState pairState;
@property (nonatomic, nullable) NSString * activeAddress;
@property (nonatomic, nullable) NSString * currentGame;
@property (nonatomic, nullable, retain) NSData *serverCert;
@property (nonatomic, nullable, retain) NSString *address;
@property (nonatomic, nullable, retain) NSString *externalAddress;
@property (nonatomic, nullable, retain) NSString *localAddress;
@property (nonatomic, nullable, retain) NSString *ipv6Address;
@property (nonatomic, nullable, retain) NSString *mac;
@property (nonatomic) int serverCodecModeSupport;
NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, retain) NSString *address;
@property (nonatomic, retain) NSString *externalAddress;
@property (nonatomic, retain) NSString *localAddress;
@property (nonatomic, retain) NSString *mac;
@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) NSString *uuid;
@property (nonatomic, retain) NSMutableSet *appList;
+29 -7
View File
@@ -8,9 +8,7 @@
#import "DataManager.h"
#import "TemporaryHost.h"
#import "Host.h"
#import "TemporaryApp.h"
#import "App.h"
@implementation TemporaryHost
@@ -18,6 +16,7 @@
self = [super init];
self.appList = [[NSMutableSet alloc] init];
self.currentGame = @"0";
self.state = StateUnknown;
return self;
}
@@ -28,10 +27,15 @@
self.address = host.address;
self.externalAddress = host.externalAddress;
self.localAddress = host.localAddress;
self.ipv6Address = host.ipv6Address;
self.mac = host.mac;
self.name = host.name;
self.uuid = host.uuid;
self.pairState = [host.pairState intValue];
self.serverCodecModeSupport = host.serverCodecModeSupport;
self.serverCert = host.serverCert;
// Ensure we don't use a stale cached pair state if we haven't pinned the cert yet
self.pairState = host.serverCert ? [host.pairState intValue] : PairStateUnpaired;
NSMutableSet *appList = [[NSMutableSet alloc] init];
@@ -46,12 +50,30 @@
}
- (void) propagateChangesToParent:(Host*)parentHost {
parentHost.address = self.address;
parentHost.externalAddress = self.externalAddress;
parentHost.localAddress = self.localAddress;
parentHost.mac = self.mac;
// Avoid overwriting existing data with nil if
// we don't have everything populated in the temporary
// host.
if (self.address != nil) {
parentHost.address = self.address;
}
if (self.externalAddress != nil) {
parentHost.externalAddress = self.externalAddress;
}
if (self.localAddress != nil) {
parentHost.localAddress = self.localAddress;
}
if (self.ipv6Address != nil) {
parentHost.ipv6Address = self.ipv6Address;
}
if (self.mac != nil) {
parentHost.mac = self.mac;
}
if (self.serverCert != nil) {
parentHost.serverCert = self.serverCert;
}
parentHost.name = self.name;
parentHost.uuid = self.uuid;
parentHost.serverCodecModeSupport = self.serverCodecModeSupport;
parentHost.pairState = [NSNumber numberWithInt:self.pairState];
}
+7 -2
View File
@@ -6,8 +6,7 @@
// Copyright © 2015 Moonlight Stream. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Settings.h"
#import "Settings+CoreDataClass.h"
@interface TemporarySettings : NSObject
@@ -19,6 +18,12 @@
@property (nonatomic, retain) NSNumber * width;
@property (nonatomic, retain) NSNumber * onscreenControls;
@property (nonatomic, retain) NSString * uniqueId;
@property (nonatomic) BOOL streamingRemotely;
@property (nonatomic) BOOL useHevc;
@property (nonatomic) BOOL multiController;
@property (nonatomic) BOOL playAudioOnPC;
@property (nonatomic) BOOL optimizeGames;
@property (nonatomic) BOOL enableHdr;
- (id) initFromSettings:(Settings*)settings;
+48
View File
@@ -7,6 +7,7 @@
//
#import "TemporarySettings.h"
#import "OnScreenControls.h"
@implementation TemporarySettings
@@ -15,12 +16,59 @@
self.parent = settings;
#if TARGET_OS_TV
NSInteger _bitrate = [[NSUserDefaults standardUserDefaults] integerForKey:@"bitrate"];
NSInteger _framerate = [[NSUserDefaults standardUserDefaults] integerForKey:@"framerate"];
if (_bitrate) {
self.bitrate = [NSNumber numberWithInteger:_bitrate];
} else {
self.bitrate = [NSNumber numberWithInteger:20000];
}
if (_framerate) {
self.framerate = [NSNumber numberWithInteger:_framerate];
} else {
self.framerate = [NSNumber numberWithInteger:60];
}
self.useHevc = [[NSUserDefaults standardUserDefaults] boolForKey:@"useHevc"] || NO;
self.playAudioOnPC = [[NSUserDefaults standardUserDefaults] boolForKey:@"audioOnPC"] || NO;
self.enableHdr = [[NSUserDefaults standardUserDefaults] boolForKey:@"enableHdr"] || NO;
self.optimizeGames = [[NSUserDefaults standardUserDefaults] boolForKey:@"optimizeGames"] || YES;
self.multiController = [[NSUserDefaults standardUserDefaults] boolForKey:@"multipleControllers"] || YES;
NSInteger _screenSize = [[NSUserDefaults standardUserDefaults] integerForKey:@"streamResolution"];
switch (_screenSize) {
case 0:
self.height = [NSNumber numberWithInteger:720];
self.width = [NSNumber numberWithInteger:1280];
break;
case 2:
self.height = [NSNumber numberWithInteger:2160];
self.width = [NSNumber numberWithInteger:3840];
break;
case 1:
default:
self.height = [NSNumber numberWithInteger:1080];
self.width = [NSNumber numberWithInteger:1920];
break;
}
self.onscreenControls = [NSNumber numberWithInteger:OnScreenControlsLevelOff];
#else
self.bitrate = settings.bitrate;
self.framerate = settings.framerate;
self.height = settings.height;
self.width = settings.width;
self.useHevc = settings.useHevc;
self.playAudioOnPC = settings.playAudioOnPC;
self.enableHdr = settings.enableHdr;
self.optimizeGames = settings.optimizeGames;
self.multiController = settings.multiController;
self.onscreenControls = settings.onscreenControls;
#endif
self.uniqueId = settings.uniqueId;
self.streamingRemotely = settings.streamingRemotely;
return self;
}
-203
View File
@@ -1,203 +0,0 @@
Font data copyright Google 2012
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,23 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "limelight_computer_add_icon_1x.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "limelight_computer_add_icon_2x.png"
},
{
"idiom" : "universal",
"scale" : "3x",
"filename" : "limelight_computer_add_icon_3x.png"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

@@ -2,12 +2,12 @@
"images" : [
{
"idiom" : "universal",
"filename" : "add.pdf",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "logo_universal.png"
"scale" : "2x"
},
{
"idiom" : "universal",
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

@@ -1,82 +1,108 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "app_icon_settings_58x58_@2x-1.png",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "app_icon_settings_87x87_@3x.png",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "app_icon_spotlight_80x80_@2x-1.png",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "app_icon_spotlight_120x120_@3x.png",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "app_icon_120x120_@2x.png",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "app_icon_180x180_@3x.png",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "app_icon_settings_29x29_@1x.png",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "app_icon_settings_58x58_@2x.png",
"filename" : "Icon-App-29x29@2x-1.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "app_icon_spotlight_40x40_@1x.png",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "app_icon_spotlight_80x80_@2x.png",
"filename" : "Icon-App-40x40@2x-1.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "app_icon_76x76_@1x.png",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "app_icon_152x152_@2x.png",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "app_icon_167x167_@2x.png",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "1024x icon.png",
"scale" : "1x"
}
],
"info" : {
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

@@ -0,0 +1,69 @@
%PDF-1.5
%µí®û
4 0 obj
<< /Length 5 0 R
/Filter /FlateDecode
>>
stream
xœ]‘]! „ß{Š^ÀÊ”Àâ1<‚Ùøó êý …E␍ »ÛN‡Yp°µƒmª‰×=)´³×•÷§À×7iØ·¸Hbä"‹= ¯3_\W«F­Æ!K©%­ðÁ½ÛñÎX¤@;$Ú®‹ ”mÊê@Œ¬Ç¿ ‰µµ+¸“!ïd�±ùË}œ#¡TÙ;7_Û«†,ÚLÙ˜EˆMHS~»ì8Ðà—î�$ëô¾±ÍX™F—ØÙ´<òŸ\W¾Í¤MlæÜûþeÓeÇÿÎ é¦Lg7
endstream
endobj
5 0 obj
212
endobj
3 0 obj
<<
/ExtGState <<
/a0 << /CA 0.3 /ca 0.3 >>
/a1 << /CA 1 /ca 1 >>
>>
>>
endobj
2 0 obj
<< /Type /Page % 1
/Parent 1 0 R
/MediaBox [ 0 0 225 225 ]
/Contents 4 0 R
/Group <<
/Type /Group
/S /Transparency
/I true
/CS /DeviceRGB
>>
/Resources 3 0 R
>>
endobj
1 0 obj
<< /Type /Pages
/Kids [ 2 0 R ]
/Count 1
>>
endobj
6 0 obj
<< /Producer (cairo 1.16.0 (https://cairographics.org))
/CreationDate (D:20190830180928-07'00)
>>
endobj
7 0 obj
<< /Type /Catalog
/Pages 1 0 R
>>
endobj
xref
0 8
0000000000 65535 f
0000000648 00000 n
0000000430 00000 n
0000000326 00000 n
0000000015 00000 n
0000000304 00000 n
0000000713 00000 n
0000000829 00000 n
trailer
<< /Size 8
/Root 7 0 R
/Info 6 0 R
>>
startxref
881
%%EOF
+4 -6
View File
@@ -2,18 +2,16 @@
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "limelight_computer_1x.png"
"filename" : "Computer.pdf",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "limelight_computer_2x.png"
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x",
"filename" : "limelight_computer_3x.png"
"scale" : "3x"
}
],
"info" : {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

@@ -2,17 +2,15 @@
"images" : [
{
"idiom" : "universal",
"filename" : "pull-arrow-light-14x31@1x.png",
"filename" : "error.pdf",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "pull-arrow-light-28x61@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "pull-arrow-light-42x92@3x.png",
"scale" : "3x"
}
],
@@ -0,0 +1,68 @@
%PDF-1.5
%µí®û
4 0 obj
<< /Length 5 0 R
/Filter /FlateDecode
>>
stream
xœeŒÑ Ã0 Dÿ5Å-Uªì(kt„Rhò¤Ýª8µ �t︓Bb�å¯B)öù̸=ó—Œõž1fã¸òä�W˜sW½P’jçjXbó†kG Ý’rî%c?õì=ÓãÍ?ÜðÒ~Óƒ~®L+·
endstream
endobj
5 0 obj
115
endobj
3 0 obj
<<
/ExtGState <<
/a0 << /CA 1 /ca 1 >>
>>
>>
endobj
2 0 obj
<< /Type /Page % 1
/Parent 1 0 R
/MediaBox [ 0 0 75 75 ]
/Contents 4 0 R
/Group <<
/Type /Group
/S /Transparency
/I true
/CS /DeviceRGB
>>
/Resources 3 0 R
>>
endobj
1 0 obj
<< /Type /Pages
/Kids [ 2 0 R ]
/Count 1
>>
endobj
6 0 obj
<< /Producer (cairo 1.16.0 (https://cairographics.org))
/CreationDate (D:20190830180932-07'00)
>>
endobj
7 0 obj
<< /Type /Catalog
/Pages 1 0 R
>>
endobj
xref
0 8
0000000000 65535 f
0000000517 00000 n
0000000301 00000 n
0000000229 00000 n
0000000015 00000 n
0000000207 00000 n
0000000582 00000 n
0000000698 00000 n
trailer
<< /Size 8
/Root 7 0 R
/Info 6 0 R
>>
startxref
750
%%EOF
+5 -6
View File
@@ -2,18 +2,17 @@
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "L2_60x60@1x.png"
"filename" : "L2_60x60@1x.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "L2_120x120@2x.png"
"filename" : "L2_120x120@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x",
"filename" : "L2_180x180@3x.png"
"scale" : "3x"
}
],
"info" : {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "lock.pdf",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,68 @@
%PDF-1.5
%µí®û
4 0 obj
<< /Length 5 0 R
/Filter /FlateDecode
>>
stream
xœUQKnÅ Üû¾@]ÌÇðŽñŽP!õu‘,ÚÞ_*ø¥"‚ÐÌØcZë�×Öξ�q¯Ÿ¾$|ýBÊ␍×wb+Ć�À<¨ßiÒ£ŠvW:9œ˜�O™•¸*˜ZΠšfwΰ çåœ;␍Ά ïZ§ÙÛá¸Ñ£ ×¢¨›y‚xœõ)Ñσd §r³�ËzÚa™Ç2ºUãòõp�oâè(|T'–JIrpóiI+4²Æ8´6§…êªÀd€5‘ÄxÏ␍v•Η¢_:'áiÔò tíöËïºk'¶vÎèP{ŽÙûЂ®¼$X2q|h“ê†Wv¦Q«?¹¹«‚ù_ùŸý Oøp…&
endstream
endobj
5 0 obj
267
endobj
3 0 obj
<<
/ExtGState <<
/a0 << /CA 1 /ca 1 >>
>>
>>
endobj
2 0 obj
<< /Type /Page % 1
/Parent 1 0 R
/MediaBox [ 0 0 75 75 ]
/Contents 4 0 R
/Group <<
/Type /Group
/S /Transparency
/I true
/CS /DeviceRGB
>>
/Resources 3 0 R
>>
endobj
1 0 obj
<< /Type /Pages
/Kids [ 2 0 R ]
/Count 1
>>
endobj
6 0 obj
<< /Producer (cairo 1.16.0 (https://cairographics.org))
/CreationDate (D:20190830180930-07'00)
>>
endobj
7 0 obj
<< /Type /Catalog
/Pages 1 0 R
>>
endobj
xref
0 8
0000000000 65535 f
0000000669 00000 n
0000000453 00000 n
0000000381 00000 n
0000000015 00000 n
0000000359 00000 n
0000000734 00000 n
0000000850 00000 n
trailer
<< /Size 8
/Root 7 0 R
/Info 6 0 R
>>
startxref
902
%%EOF
Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 901 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 812 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

+5 -6
View File
@@ -2,18 +2,17 @@
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "R2_60x60@1x.png"
"filename" : "R2_60x60@1x.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "R2_120x120@2x.png"
"filename" : "R2_120x120@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x",
"filename" : "R2_120x120@3x.png"
"scale" : "3x"
}
],
"info" : {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "settings.pdf",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,70 @@
%PDF-1.5
%µí®û
4 0 obj
<< /Length 5 0 R
/Filter /FlateDecode
>>
stream
xœeTIŽÜ0 ¼óú@‰Z,=#O ÌÌA}Hò ,.== °»d®Å¢j*òûVåq�t?èÕ„ßï÷ôýgIï¨]y¤V3�ôH­ç2Ù¡?¿ÁøvÐKÞm¹¥Þòåö÷ ƒþ|=òp,EØÏ„ö÷N4vnbjÏGª#OxRå\ÇL¯68Z+€™ß‰ �p´÷ä¼*G”iÆî3ÃÕ}ÓQ¯Þã ^Ê‘ûÜÿÄ<O¬ER„úR\¸Jï-ï2�‘G
´2×␍OA nŸ;J^CXFR¾V»!PïJ” 8 šÂ�øv8␍Ö,G³Î␍<Q°À.¾×¥VÛ�|ÄX¯Üa\2zí37)qT+fä‚䌗iGJ‚«¦ílyVîÒÏÖ K�iZã¦Wû¦ˆ Î–+¿L·±5jX�~µP‚J«šH�)»~6xïè␍^¼´@i°ŒÎE_wbަ.e‹«%V^PsÙg.ÙâêŒê´^ºrw€J0Ê„—°$Ã!@’ ti^[•­µiZØÅ€¬»‡PADt?áTúÃqê"zÔáô¤B�횕Ԕ Šz[±²naä·÷*ô Ë "·ÊÁ+Ã$úÇ;Hæ®*ò°2�á@qlÂóÙÁ³w‚␍WªÒωѣV9vYø2ºhžW„«Ê0™1s7pAz(—+©\ÎÞÃ�ÚU=êD±
l èk"␍š}�Ę•-_²—m }%ßO0ë›®½Ïë»-X·Ëö¾Ùú­€yÖ␍¶üÖZÛy^)&†¸nL(¯WÑÿWÓý ¿ ¾?1
endstream
endobj
5 0 obj
608
endobj
3 0 obj
<<
/ExtGState <<
/a0 << /CA 1 /ca 1 >>
>>
>>
endobj
2 0 obj
<< /Type /Page % 1
/Parent 1 0 R
/MediaBox [ 0 0 75 75 ]
/Contents 4 0 R
/Group <<
/Type /Group
/S /Transparency
/I true
/CS /DeviceRGB
>>
/Resources 3 0 R
>>
endobj
1 0 obj
<< /Type /Pages
/Kids [ 2 0 R ]
/Count 1
>>
endobj
6 0 obj
<< /Producer (cairo 1.16.0 (https://cairographics.org))
/CreationDate (D:20190830202122-07'00)
>>
endobj
7 0 obj
<< /Type /Catalog
/Pages 1 0 R
>>
endobj
xref
0 8
0000000000 65535 f
0000001010 00000 n
0000000794 00000 n
0000000722 00000 n
0000000015 00000 n
0000000700 00000 n
0000001075 00000 n
0000001191 00000 n
trailer
<< /Size 8
/Root 7 0 R
/Info 6 0 R
>>
startxref
1243
%%EOF
+21
View File
@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "up_arrow.pdf",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
+70
View File
@@ -0,0 +1,70 @@
%PDF-1.5
%µí®û
4 0 obj
<< /Length 5 0 R
/Filter /FlateDecode
>>
stream
xœUŽQ
ƒ@ Dÿsй€1ÙÍF=†G(…êÇú¡½?-e`ƒÌ$
5Ö¼ZIqh›Ð¾Ó—4qAî¨ó ÓsTdã¹dl>üØ�H… {º‰n|–KÏâé,Ë}°?‡ê5ôª™ÿ^øÐH;U$}
endstream
endobj
5 0 obj
114
endobj
3 0 obj
<<
/ExtGState <<
/a0 << /CA 1 /ca 1 >>
>>
>>
endobj
2 0 obj
<< /Type /Page % 1
/Parent 1 0 R
/MediaBox [ 0 0 75 75 ]
/Contents 4 0 R
/Group <<
/Type /Group
/S /Transparency
/I true
/CS /DeviceRGB
>>
/Resources 3 0 R
>>
endobj
1 0 obj
<< /Type /Pages
/Kids [ 2 0 R ]
/Count 1
>>
endobj
6 0 obj
<< /Producer (cairo 1.16.0 (https://cairographics.org))
/CreationDate (D:20190830203401-07'00)
>>
endobj
7 0 obj
<< /Type /Catalog
/Pages 1 0 R
>>
endobj
xref
0 8
0000000000 65535 f
0000000516 00000 n
0000000300 00000 n
0000000228 00000 n
0000000015 00000 n
0000000206 00000 n
0000000581 00000 n
0000000697 00000 n
trailer
<< /Size 8
/Root 7 0 R
/Info 6 0 R
>>
startxref
749
%%EOF
+26
View File
@@ -0,0 +1,26 @@
//
// Controller.h
// Moonlight
//
// Created by Cameron Gutman on 2/11/19.
// Copyright © 2019 Moonlight Game Streaming Project. All rights reserved.
//
@import GameController;
@interface Controller : NSObject
@property (nullable, nonatomic, retain) GCController* gamepad;
@property (nonatomic) int playerIndex;
@property (nonatomic) int lastButtonFlags;
@property (nonatomic) int emulatingButtonFlags;
@property (nonatomic) unsigned char lastLeftTrigger;
@property (nonatomic) unsigned char lastRightTrigger;
@property (nonatomic) short lastLeftStickX;
@property (nonatomic) short lastLeftStickY;
@property (nonatomic) short lastRightStickX;
@property (nonatomic) short lastRightStickY;
@property (nonatomic) unsigned short lowFreqMotor;
@property (nonatomic) unsigned short highFreqMotor;
@end
+13
View File
@@ -0,0 +1,13 @@
//
// Controller.m
// Moonlight
//
// Created by Cameron Gutman on 2/11/19.
// Copyright © 2019 Moonlight Game Streaming Project. All rights reserved.
//
#include "Controller.h"
@implementation Controller
@end
-26
View File
@@ -1,26 +0,0 @@
//
// Controller.swift
// Moonlight
//
// Created by David Aghassi on 4/11/16.
// Copyright © 2016 Moonlight Stream. All rights reserved.
//
import Foundation
@objc
/**
Defines a controller layout
*/
class Controller: NSObject {
// Swift requires initial properties
var playerIndex: CInt = 0 // Controller number (e.g. 1, 2 ,3 etc)
var lastButtonFlags: CInt = 0
var emulatingButtonFlags: CInt = 0
var lastLeftTrigger: CChar = 0 // Last left trigger pressed
var lastRightTrigger: CChar = 0 // Last right trigger pressed
var lastLeftStickX: CShort = 0 // Last X direction the left joystick went
var lastLeftStickY: CShort = 0 // Last Y direction the left joystick went
var lastRightStickX: CShort = 0 // Last X direction the right joystick went
var lastRightStickY: CShort = 0 // Last Y direction the right joystick went
}
+18 -6
View File
@@ -6,19 +6,28 @@
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
//
#import <Foundation/Foundation.h>
// Swift
#import "Moonlight-Swift.h"
@class Controller;
#if !TARGET_OS_IPHONE
#import "Gamepad.h"
#endif
#import "StreamConfiguration.h"
#import "Controller.h"
@class OnScreenControls;
@interface ControllerSupport : NSObject
-(id) init;
-(id) initWithConfig:(StreamConfiguration*)streamConfig;
#if TARGET_OS_IPHONE
-(void) initAutoOnScreenControlMode:(OnScreenControls*)osc;
-(void) cleanup;
-(Controller*) getOscController;
#else
-(void) assignGamepad:(struct Gamepad_device *)gamepad;
-(void) removeGamepad:(struct Gamepad_device *)gamepad;
-(NSMutableDictionary*) getControllers;
#endif
-(void) updateLeftStick:(Controller*)controller x:(short)x y:(short)y;
-(void) updateRightStick:(Controller*)controller x:(short)x y:(short)y;
@@ -32,7 +41,10 @@
-(void) clearButtonFlag:(Controller*)controller flags:(int)flags;
-(void) updateFinished:(Controller*)controller;
-(Controller*) getOscController;
-(void) rumble:(unsigned short)controllerNumber lowFreqMotor:(unsigned short)lowFreqMotor highFreqMotor:(unsigned short)highFreqMotor;
+(int) getConnectedGamepadMask:(StreamConfiguration*)streamConfig;
@property (nonatomic, strong) id connectObserver;
@property (nonatomic, strong) id disconnectObserver;
+267 -33
View File
@@ -7,18 +7,24 @@
//
#import "ControllerSupport.h"
#import "Controller.h"
#import "OnScreenControls.h"
#if !TARGET_OS_IPHONE
#import "Gamepad.h"
#import "Control.h"
#endif
#import "DataManager.h"
#include "Limelight.h"
// Swift
#import "Moonlight-Swift.h"
@class Controller;
@import GameController;
@import AudioToolbox;
@implementation ControllerSupport {
NSLock *_controllerStreamLock;
NSMutableDictionary *_controllers;
NSTimer *_rumbleTimer;
OnScreenControls *_osc;
@@ -26,16 +32,52 @@
// and player 0
Controller *_player0osc;
char _controllerNumbers;
#define EMULATING_SELECT 0x1
#define EMULATING_SPECIAL 0x2
bool _oscEnabled;
char _controllerNumbers;
bool _multiController;
}
// UPDATE_BUTTON_FLAG(controller, flag, pressed)
#define UPDATE_BUTTON_FLAG(controller, x, y) \
((y) ? [self setButtonFlag:controller flags:x] : [self clearButtonFlag:controller flags:x])
-(void) rumbleController: (Controller*)controller
{
#if 0
// Only vibrate if the amplitude is large enough
if (controller.lowFreqMotor > 0x5000 || controller.highFreqMotor > 0x5000) {
// If the gamepad is nil (on-screen controls) or it's attached to the device,
// then vibrate the device itself
if (controller.gamepad == nil || [controller.gamepad isAttachedToDevice]) {
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
}
}
#endif
}
-(void) rumble:(unsigned short)controllerNumber lowFreqMotor:(unsigned short)lowFreqMotor highFreqMotor:(unsigned short)highFreqMotor
{
Controller* controller = [_controllers objectForKey:[NSNumber numberWithInteger:controllerNumber]];
if (controller == nil && controllerNumber == 0 && _oscEnabled) {
// No physical controller, but we have on-screen controls
controller = _player0osc;
}
if (controller == nil) {
// No connected controller for this player
return;
}
// Update the motor levels for the rumble timer to grab next iteration
controller.lowFreqMotor = lowFreqMotor;
controller.highFreqMotor = highFreqMotor;
// Rumble now to ensure short vibrations aren't missed
[self rumbleController:controller];
}
-(void) updateLeftStick:(Controller*)controller x:(short)x y:(short)y
{
@synchronized(controller) {
@@ -112,17 +154,16 @@
-(void) updateButtonFlags:(Controller*)controller flags:(int)flags
{
@synchronized(controller) {
int releasedButtons = (controller.lastButtonFlags ^ flags) & ~flags;
int pressedButtons = (controller.lastButtonFlags ^ flags) & flags;
controller.lastButtonFlags = flags;
// This must be called before handleSpecialCombosPressed
// because we clear the original button flags there
int releasedButtons = (controller.lastButtonFlags ^ flags) & ~flags;
int pressedButtons = (controller.lastButtonFlags ^ flags) & flags;
[self handleSpecialCombosReleased:controller releasedButtons:releasedButtons];
[self handleSpecialCombosPressed:controller pressedButtons:pressedButtons];
}
}
@@ -146,11 +187,16 @@
{
[_controllerStreamLock lock];
@synchronized(controller) {
LiSendMultiControllerEvent(controller.playerIndex, controller.lastButtonFlags, controller.lastLeftTrigger, controller.lastRightTrigger, controller.lastLeftStickX, controller.lastLeftStickY, controller.lastRightStickX, controller.lastRightStickY);
// Player 1 is always present for OSC
LiSendMultiControllerEvent(_multiController ? controller.playerIndex : 0,
(_multiController ? _controllerNumbers : 1) | (_oscEnabled ? 1 : 0), controller.lastButtonFlags, controller.lastLeftTrigger, controller.lastRightTrigger, controller.lastLeftStickX, controller.lastLeftStickY, controller.lastRightStickX, controller.lastRightStickY);
}
[_controllerStreamLock unlock];
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-(void) unregisterControllerCallbacks:(GCController*) controller
{
if (controller != NULL) {
@@ -168,21 +214,32 @@
-(void) registerControllerCallbacks:(GCController*) controller
{
if (controller != NULL) {
controller.controllerPausedHandler = ^(GCController *controller) {
Controller* limeController = [_controllers objectForKey:[NSNumber numberWithInteger:controller.playerIndex]];
[self setButtonFlag:limeController flags:PLAY_FLAG];
[self updateFinished:limeController];
// Pause for 100 ms
usleep(100 * 1000);
[self clearButtonFlag:limeController flags:PLAY_FLAG];
[self updateFinished:limeController];
};
// On iOS 13, we want to use the new buttonMenu property which lets users hold down Start.
// On prior versions, we must use the controllerPausedHandler.
if (@available(iOS 13.0, tvOS 13.0, *)) {
// Nothing - buttonMenu is already handled in valueChangedHandler
}
else {
controller.controllerPausedHandler = ^(GCController *controller) {
Controller* limeController = [self->_controllers objectForKey:[NSNumber numberWithInteger:controller.playerIndex]];
// Get off the main thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self setButtonFlag:limeController flags:PLAY_FLAG];
[self updateFinished:limeController];
// Pause for 100 ms
usleep(100 * 1000);
[self clearButtonFlag:limeController flags:PLAY_FLAG];
[self updateFinished:limeController];
});
};
}
if (controller.extendedGamepad != NULL) {
controller.extendedGamepad.valueChangedHandler = ^(GCExtendedGamepad *gamepad, GCControllerElement *element) {
Controller* limeController = [_controllers objectForKey:[NSNumber numberWithInteger:gamepad.controller.playerIndex]];
Controller* limeController = [self->_controllers objectForKey:[NSNumber numberWithInteger:gamepad.controller.playerIndex]];
short leftStickX, leftStickY;
short rightStickX, rightStickY;
char leftTrigger, rightTrigger;
@@ -200,6 +257,26 @@
UPDATE_BUTTON_FLAG(limeController, LB_FLAG, gamepad.leftShoulder.pressed);
UPDATE_BUTTON_FLAG(limeController, RB_FLAG, gamepad.rightShoulder.pressed);
// Yay, iOS 12.1 now supports analog stick buttons
if (@available(iOS 12.1, tvOS 12.1, *)) {
if (gamepad.leftThumbstickButton != nil) {
UPDATE_BUTTON_FLAG(limeController, LS_CLK_FLAG, gamepad.leftThumbstickButton.pressed);
}
if (gamepad.rightThumbstickButton != nil) {
UPDATE_BUTTON_FLAG(limeController, RS_CLK_FLAG, gamepad.rightThumbstickButton.pressed);
}
}
if (@available(iOS 13.0, tvOS 13.0, *)) {
// Menu button is mandatory, so no need to check for nil
UPDATE_BUTTON_FLAG(limeController, PLAY_FLAG, gamepad.buttonMenu.pressed);
// Options button is optional (only present on Xbox One S and PS4 gamepads)
if (gamepad.buttonOptions != nil) {
UPDATE_BUTTON_FLAG(limeController, BACK_FLAG, gamepad.buttonOptions.pressed);
}
}
leftStickX = gamepad.leftThumbstick.xAxis.value * 0x7FFE;
leftStickY = gamepad.leftThumbstick.yAxis.value * 0x7FFE;
@@ -217,7 +294,7 @@
}
else if (controller.gamepad != NULL) {
controller.gamepad.valueChangedHandler = ^(GCGamepad *gamepad, GCControllerElement *element) {
Controller* limeController = [_controllers objectForKey:[NSNumber numberWithInteger:gamepad.controller.playerIndex]];
Controller* limeController = [self->_controllers objectForKey:[NSNumber numberWithInteger:gamepad.controller.playerIndex]];
UPDATE_BUTTON_FLAG(limeController, A_FLAG, gamepad.buttonA.pressed);
UPDATE_BUTTON_FLAG(limeController, B_FLAG, gamepad.buttonB.pressed);
UPDATE_BUTTON_FLAG(limeController, X_FLAG, gamepad.buttonX.pressed);
@@ -256,6 +333,18 @@
if (controller != NULL) {
if (controller.extendedGamepad != NULL) {
level = OnScreenControlsLevelAutoGCExtendedGamepad;
if (@available(iOS 12.1, tvOS 12.1, *)) {
if (controller.extendedGamepad.leftThumbstickButton != nil &&
controller.extendedGamepad.rightThumbstickButton != nil) {
level = OnScreenControlsLevelAutoGCExtendedGamepadWithStickButtons;
if (@available(iOS 13.0, tvOS 13.0, *)) {
if (controller.extendedGamepad.buttonOptions != nil) {
// Has L3/R3 and Select, so we can show nothing :)
level = OnScreenControlsLevelOff;
}
}
}
}
break;
}
else if (controller.gamepad != NULL) {
@@ -265,7 +354,9 @@
}
}
#if TARGET_OS_IPHONE
[_osc setLevel:level];
#endif
}
-(void) initAutoOnScreenControlMode:(OnScreenControls*)osc
@@ -282,6 +373,7 @@
controller.playerIndex = i;
Controller* limeController;
if (i == 0) {
// Player 0 shares a controller object with the on-screen controls
limeController = _player0osc;
@@ -289,6 +381,8 @@
limeController = [[Controller alloc] init];
limeController.playerIndex = i;
}
limeController.gamepad = controller;
[_controllers setObject:limeController forKey:[NSNumber numberWithInteger:controller.playerIndex]];
@@ -298,31 +392,155 @@
}
}
#if TARGET_OS_IPHONE
-(Controller*) getOscController {
return _player0osc;
}
#else
-(NSMutableDictionary*) getControllers {
return _controllers;
}
-(id) init
-(void) assignGamepad:(struct Gamepad_device *)gamepad {
for (int i = 0; i < 4; i++) {
if (!(_controllerNumbers & (1 << i))) {
_controllerNumbers |= (1 << i);
gamepad->deviceID = i;
NSLog(@"Gamepad device id: %u assigned", gamepad->deviceID);
Controller* limeController;
limeController = [[Controller alloc] init];
limeController.playerIndex = i;
[_controllers setObject:limeController forKey:[NSNumber numberWithInteger:i]];
break;
}
}
}
-(void) removeGamepad:(struct Gamepad_device *)gamepad {
_controllerNumbers &= ~(1 << gamepad->deviceID);
Log(LOG_I, @"Unassigning controller index: %ld", (long)gamepad->deviceID);
// Inform the server of the updated active gamepads before removing this controller
[self updateFinished:[_controllers objectForKey:[NSNumber numberWithInteger:gamepad->deviceID]]];
[_controllers removeObjectForKey:[NSNumber numberWithInteger:gamepad->deviceID]];
}
#endif
+(bool) isSupportedGamepad:(GCController*) controller {
return controller.extendedGamepad != nil || controller.gamepad != nil;
}
#pragma clang diagnostic pop
+(int) getGamepadCount {
int count = 0;
for (GCController* controller in [GCController controllers]) {
if ([ControllerSupport isSupportedGamepad:controller]) {
count++;
}
}
return count;
}
+(int) getConnectedGamepadMask:(StreamConfiguration*)streamConfig {
int mask = 0;
if (streamConfig.multiController) {
int i = 0;
for (GCController* controller in [GCController controllers]) {
if ([ControllerSupport isSupportedGamepad:controller]) {
mask |= 1 << i++;
}
}
}
else {
// Some games don't deal with having controller reconnected
// properly so always report controller 1 if not in MC mode
mask = 0x1;
}
#if TARGET_OS_IPHONE
DataManager* dataMan = [[DataManager alloc] init];
OnScreenControlsLevel level = (OnScreenControlsLevel)[[dataMan getSettings].onscreenControls integerValue];
// Even if no gamepads are present, we will always count one
// if OSC is enabled.
if (level != OnScreenControlsLevelOff) {
mask |= 0x1;
}
#endif
return mask;
}
-(void) rumbleTimer
{
for (int i = 0; i < 4; i++) {
Controller* controller = [_controllers objectForKey:[NSNumber numberWithInteger:i]];
if (controller == nil && i == 0 && _oscEnabled) {
// No physical controller, but we have on-screen controls
controller = _player0osc;
}
if (controller == nil) {
// No connected controller for this player
continue;
}
[self rumbleController:controller];
}
}
-(id) initWithConfig:(StreamConfiguration*)streamConfig
{
self = [super init];
_controllerStreamLock = [[NSLock alloc] init];
_controllers = [[NSMutableDictionary alloc] init];
_controllerNumbers = 0;
_multiController = streamConfig.multiController;
_player0osc = [[Controller alloc] init];
_player0osc.playerIndex = 0;
#if TARGET_OS_IPHONE
DataManager* dataMan = [[DataManager alloc] init];
_oscEnabled = (OnScreenControlsLevel)[[dataMan getSettings].onscreenControls integerValue] != OnScreenControlsLevelOff;
#else
_oscEnabled = false;
initGamepad(self);
Gamepad_detectDevices();
#endif
_rumbleTimer = [NSTimer scheduledTimerWithTimeInterval:0.20
target:self
selector:@selector(rumbleTimer)
userInfo:nil
repeats:YES];
Log(LOG_I, @"Number of supported controllers connected: %d", [ControllerSupport getGamepadCount]);
Log(LOG_I, @"Multi-controller: %d", _multiController);
Log(LOG_I, @"Number of controllers connected: %ld", (long)[[GCController controllers] count]);
for (GCController* controller in [GCController controllers]) {
[self assignController:controller];
[self registerControllerCallbacks:controller];
[self updateAutoOnScreenControlMode];
if ([ControllerSupport isSupportedGamepad:controller]) {
[self assignController:controller];
[self registerControllerCallbacks:controller];
[self updateAutoOnScreenControlMode];
}
}
self.connectObserver = [[NSNotificationCenter defaultCenter] addObserverForName:GCControllerDidConnectNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
Log(LOG_I, @"Controller connected!");
GCController* controller = note.object;
if (![ControllerSupport isSupportedGamepad:controller]) {
// Ignore micro gamepads and motion controllers
return;
}
[self assignController:controller];
// Register callbacks on the new controller
@@ -335,26 +553,42 @@
Log(LOG_I, @"Controller disconnected!");
GCController* controller = note.object;
if (![ControllerSupport isSupportedGamepad:controller]) {
// Ignore micro gamepads and motion controllers
return;
}
[self unregisterControllerCallbacks:controller];
[_controllers removeObjectForKey:[NSNumber numberWithInteger:controller.playerIndex]];
_controllerNumbers &= ~(1 << controller.playerIndex);
self->_controllerNumbers &= ~(1 << controller.playerIndex);
Log(LOG_I, @"Unassigning controller index: %ld", (long)controller.playerIndex);
// Unset the GCController on this object (in case it is the OSC, which will persist)
Controller* limeController = [self->_controllers objectForKey:[NSNumber numberWithInteger:controller.playerIndex]];
limeController.gamepad = nil;
// Inform the server of the updated active gamepads before removing this controller
[self updateFinished:limeController];
[self->_controllers removeObjectForKey:[NSNumber numberWithInteger:controller.playerIndex]];
// Re-evaluate the on-screen control mode
[self updateAutoOnScreenControlMode];
}];
return self;
}
-(void) cleanup
{
[_rumbleTimer invalidate];
_rumbleTimer = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self.connectObserver];
[[NSNotificationCenter defaultCenter] removeObserver:self.disconnectObserver];
[_controllers removeAllObjects];
_controllerNumbers = 0;
for (GCController* controller in [GCController controllers]) {
[self unregisterControllerCallbacks:controller];
if ([ControllerSupport isSupportedGamepad:controller]) {
[self unregisterControllerCallbacks:controller];
}
}
}
+21
View File
@@ -0,0 +1,21 @@
//
// KeyboardSupport.h
// Moonlight
//
// Created by Diego Waxemberg on 8/25/18.
// Copyright © 2018 Moonlight Game Streaming Project. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface KeyboardSupport : NSObject
struct KeyEvent {
u_short keycode;
u_short modifierKeycode;
u_char modifier;
};
+ (struct KeyEvent) translateKeyEvent:(unichar) inputChar withModifierFlags:(UIKeyModifierFlags)modifierFlags;
@end
+191
View File
@@ -0,0 +1,191 @@
//
// KeyboardSupport.m
// Moonlight
//
// Created by Diego Waxemberg on 8/25/18.
// Copyright © 2018 Moonlight Game Streaming Project. All rights reserved.
//
#import "KeyboardSupport.h"
#include <Limelight.h>
@implementation KeyboardSupport
+ (struct KeyEvent)translateKeyEvent:(unichar)inputChar withModifierFlags:(UIKeyModifierFlags)modifierFlags {
struct KeyEvent event;
event.keycode = 0;
event.modifier = 0;
event.modifierKeycode = 0;
switch (modifierFlags) {
case UIKeyModifierAlphaShift:
case UIKeyModifierShift:
[KeyboardSupport addShiftModifier:&event];
break;
case UIKeyModifierControl:
[KeyboardSupport addControlModifier:&event];
break;
case UIKeyModifierAlternate:
[KeyboardSupport addAltModifier:&event];
break;
case UIKeyModifierCommand:
case UIKeyModifierNumericPad:
break;
}
if (inputChar >= 0x30 && inputChar <= 0x39) {
// Numbers 0-9
event.keycode = inputChar;
} else if (inputChar >= 0x41 && inputChar <= 0x5A) {
// Capital letters
event.keycode = inputChar;
[KeyboardSupport addShiftModifier:&event];
} else if (inputChar >= 0x61 && inputChar <= 0x7A) {
// Lower case letters
event.keycode = inputChar - (0x61 - 0x41);
} switch (inputChar) {
case ' ': // Spacebar
event.keycode = 0x20;
break;
case '-': // Hyphen '-'
event.keycode = 0xBD;
break;
case '/': // Forward slash '/'
event.keycode = 0xBF;
break;
case ':': // Colon ':'
event.keycode = 0xBA;
[KeyboardSupport addShiftModifier:&event];
break;
case ';': // Semi-colon ';'
event.keycode = 0xBA;
break;
case '(': // Open parenthesis '('
event.keycode = 0x39; // '9'
[KeyboardSupport addShiftModifier:&event];
break;
case ')': // Close parenthesis ')'
event.keycode = 0x30; // '0'
[KeyboardSupport addShiftModifier:&event];
break;
case '$': // Dollar sign '$'
event.keycode = 0x34; // '4'
[KeyboardSupport addShiftModifier:&event];
break;
case '&': // Ampresand '&'
event.keycode = 0x37; // '7'
[KeyboardSupport addShiftModifier:&event];
break;
case '@': // At-sign '@'
event.keycode = 0x32; // '2'
[KeyboardSupport addShiftModifier:&event];
break;
case '"':
event.keycode = 0xDE;
[KeyboardSupport addShiftModifier:&event];
break;
case '\'':
event.keycode = 0xDE;
break;
case '!':
event.keycode = 0x31; // '1'
[KeyboardSupport addShiftModifier:&event];
break;
case '?':
event.keycode = 0xBF; // '/'
[KeyboardSupport addShiftModifier:&event];
break;
case ',':
event.keycode = 0xBC;
break;
case '<':
event.keycode = 0xBC;
[KeyboardSupport addShiftModifier:&event];
break;
case '.':
event.keycode = 0xBE;
break;
case '>':
event.keycode = 0xBE;
[KeyboardSupport addShiftModifier:&event];
break;
case '[':
event.keycode = 0xDB;
break;
case ']':
event.keycode = 0xDD;
break;
case '{':
event.keycode = 0xDB;
[KeyboardSupport addShiftModifier:&event];
break;
case '}':
event.keycode = 0xDD;
[KeyboardSupport addShiftModifier:&event];
break;
case '#':
event.keycode = 0x33; // '3'
[KeyboardSupport addShiftModifier:&event];
break;
case '%':
event.keycode = 0x35; // '5'
[KeyboardSupport addShiftModifier:&event];
break;
case '^':
event.keycode = 0x36; // '6'
[KeyboardSupport addShiftModifier:&event];
break;
case '*':
event.keycode = 0x38; // '8'
[KeyboardSupport addShiftModifier:&event];
break;
case '+':
event.keycode = 0xBB;
[KeyboardSupport addShiftModifier:&event];
break;
case '=':
event.keycode = 0xBB;
break;
case '_':
event.keycode = 0xBD;
[KeyboardSupport addShiftModifier:&event];
break;
case '\\':
event.keycode = 0xDC;
break;
case '|':
event.keycode = 0xDC;
[KeyboardSupport addShiftModifier:&event];
break;
case '~':
event.keycode = 0xC0;
[KeyboardSupport addShiftModifier:&event];
break;
case '`':
event.keycode = 0xC0;
break;
case '\t':
event.keycode = 0x09;
break;
default:
break;
}
return event;
}
+ (void) addShiftModifier:(struct KeyEvent*)event {
event->modifier = MODIFIER_SHIFT;
event->modifierKeycode = 0x10;
}
+ (void) addControlModifier:(struct KeyEvent*)event {
event->modifier = MODIFIER_CTRL;
event->modifierKeycode = 0x11;
}
+ (void) addAltModifier:(struct KeyEvent*)event {
event->modifier = MODIFIER_ALT;
event->modifierKeycode = 0x12;
}
@end
+3
View File
@@ -22,12 +22,15 @@ typedef NS_ENUM(NSInteger, OnScreenControlsLevel) {
// Internal levels selected by ControllerSupport
OnScreenControlsLevelAutoGCGamepad,
OnScreenControlsLevelAutoGCExtendedGamepad,
OnScreenControlsLevelAutoGCExtendedGamepadWithStickButtons
};
#if TARGET_OS_IPHONE
- (id) initWithView:(UIView*)view controllerSup:(ControllerSupport*)controllerSupport swipeDelegate:(id<EdgeDetectionDelegate>)edgeDelegate;
- (BOOL) handleTouchDownEvent:(NSSet*)touches;
- (BOOL) handleTouchUpEvent:(NSSet*)touches;
- (BOOL) handleTouchMovedEvent:(NSSet*)touches;
- (void) setLevel:(OnScreenControlsLevel)level;
#endif
@end
+109 -71
View File
@@ -8,12 +8,9 @@
#import "OnScreenControls.h"
#import "ControllerSupport.h"
//#import "Controller.h"
#import "Controller.h"
#include "Limelight.h"
#import "Moonlight-Swift.h"
@class Controller;
#define UPDATE_BUTTON(x, y) (buttonFlags = \
(y) ? (buttonFlags | (x)) : (buttonFlags & ~(x)))
@@ -123,7 +120,7 @@ static float L3_Y;
_edgeDelegate = swipeDelegate;
_deadTouches = [[NSMutableArray alloc] init];
_iPad = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad);
_iPad = ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad);
_controlArea = CGRectMake(0, 0, _view.frame.size.width, _view.frame.size.height);
if (_iPad)
{
@@ -202,15 +199,27 @@ static float L3_Y;
[self hideSticks];
[self drawL3R3];
break;
case OnScreenControlsLevelAutoGCExtendedGamepadWithStickButtons:
// This variant of GCExtendedGamepad has L3 and R3 but
// is still missing Select
[self setupExtendedGamepadControls];
[self hideButtons];
[self hideBumpers];
[self hideTriggers];
[self hideL3R3];
[self drawStartSelect];
[self hideSticks];
break;
case OnScreenControlsLevelSimple:
[self setupSimpleControls];
[self drawTriggers];
[self drawStartSelect];
[self drawL3R3];
[self hideButtons];
[self hideTriggers];
[self hideL3R3];
[self hideBumpers];
[self hideSticks];
[self drawStartSelect];
[self drawButtons];
break;
case OnScreenControlsLevelFull:
[self setupComplexControls];
@@ -238,8 +247,22 @@ static float L3_Y;
// Start with the default complex layout
[self setupComplexControls];
START_X = _controlArea.size.width * .95 + _controlArea.origin.x;
START_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
SELECT_X = _controlArea.size.width * .05 + _controlArea.origin.x;
SELECT_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
L3_Y = _controlArea.size.height * .85 + _controlArea.origin.y;
R3_Y = _controlArea.size.height * .85 + _controlArea.origin.y;
if (_iPad) {
L3_X = _controlArea.size.width * .15 + _controlArea.origin.x;
R3_X = _controlArea.size.width * .85 + _controlArea.origin.x;
}
else {
L3_X = _controlArea.size.width * .25 + _controlArea.origin.x;
R3_X = _controlArea.size.width * .75 + _controlArea.origin.x;
}
}
// For GCGamepad controls we move triggers, start, and select
@@ -248,41 +271,21 @@ static float L3_Y;
// Start with the default complex layout
[self setupComplexControls];
START_X = _controlArea.size.width * .6 + _controlArea.origin.x;
START_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
L2_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
L2_X = _controlArea.size.width * .05 + _controlArea.origin.x;
SELECT_X = _controlArea.size.width * .4 + _controlArea.origin.x;
SELECT_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
R2_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
R2_X = _controlArea.size.width * .95 + _controlArea.origin.x;
L2_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
L2_X = _controlArea.size.width * .1 + _controlArea.origin.x;
R2_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
R2_X = _controlArea.size.width * .9 + _controlArea.origin.x;
START_X = _controlArea.size.width * .95 + _controlArea.origin.x;
START_Y = _controlArea.size.height * .95 + _controlArea.origin.y;
SELECT_X = _controlArea.size.width * .05 + _controlArea.origin.x;
SELECT_Y = _controlArea.size.height * .95 + _controlArea.origin.y;
if (_iPad) {
START_X = _controlArea.size.width * .75 + _controlArea.origin.x;
START_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
SELECT_X = _controlArea.size.width * .25 + _controlArea.origin.x;
SELECT_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
// The analog sticks are kept closer to the sides on iPad
LS_CENTER_X = _controlArea.size.width * .18 + _controlArea.origin.x;
LS_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
RS_CENTER_X = _controlArea.size.width * .82 + _controlArea.origin.x;
RS_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
} else {
START_X = _controlArea.size.width * .6 + _controlArea.origin.x;
START_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
SELECT_X = _controlArea.size.width * .4 + _controlArea.origin.x;
SELECT_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
LS_CENTER_X = _controlArea.size.width * .25 + _controlArea.origin.x;
LS_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
RS_CENTER_X = _controlArea.size.width * .75 + _controlArea.origin.x;
RS_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
LS_CENTER_X = _controlArea.size.width * .15 + _controlArea.origin.x;
RS_CENTER_X = _controlArea.size.width * .85 + _controlArea.origin.x;
}
}
@@ -291,10 +294,7 @@ static float L3_Y;
// Start with the default complex layout
[self setupComplexControls];
START_X = _controlArea.size.width * .6 + _controlArea.origin.x;
START_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
SELECT_X = _controlArea.size.width * .4 + _controlArea.origin.x;
SELECT_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
L2_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
@@ -302,6 +302,20 @@ static float L3_Y;
R2_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
R2_X = _controlArea.size.width * .9 + _controlArea.origin.x;
if (_iPad) {
// Lower the D-pad and buttons on iPad
D_PAD_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
BUTTON_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
// Move Start and Select closer to sides
SELECT_X = _controlArea.size.width * .2 + _controlArea.origin.x;
START_X = _controlArea.size.width * .8 + _controlArea.origin.x;
}
else {
SELECT_X = _controlArea.size.width * .4 + _controlArea.origin.x;
START_X = _controlArea.size.width * .6 + _controlArea.origin.x;
}
}
- (void) setupComplexControls
@@ -315,9 +329,9 @@ static float L3_Y;
{
// The analog sticks are kept closer to the sides on iPad
LS_CENTER_X = _controlArea.size.width * .22 + _controlArea.origin.x;
LS_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
LS_CENTER_Y = _controlArea.size.height * .80 + _controlArea.origin.y;
RS_CENTER_X = _controlArea.size.width * .77 + _controlArea.origin.x;
RS_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
RS_CENTER_Y = _controlArea.size.height * .80 + _controlArea.origin.y;
}
else
{
@@ -332,18 +346,24 @@ static float L3_Y;
SELECT_X = _controlArea.size.width * .1 + _controlArea.origin.x;
SELECT_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
L1_X = _controlArea.size.width * .1 + _controlArea.origin.x;
L1_Y = _controlArea.size.height * .27 + _controlArea.origin.y;
L2_X = _controlArea.size.width * .1 + _controlArea.origin.x;
L2_Y = _controlArea.size.height * .1 + _controlArea.origin.y;
R1_X = _controlArea.size.width * .9 + _controlArea.origin.x;
R1_Y = _controlArea.size.height * .27 + _controlArea.origin.y;
R2_X = _controlArea.size.width * .9 + _controlArea.origin.x;
R2_Y = _controlArea.size.height * .1 + _controlArea.origin.y;
L3_X = _controlArea.size.width * .25 + _controlArea.origin.x;
L3_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
R3_X = _controlArea.size.width * .75 + _controlArea.origin.x;
R3_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
if (_iPad) {
// Move L/R buttons closer to the side on iPad
L1_X = _controlArea.size.width * .05 + _controlArea.origin.x;
L2_X = _controlArea.size.width * .05 + _controlArea.origin.x;
R1_X = _controlArea.size.width * .95 + _controlArea.origin.x;
R2_X = _controlArea.size.width * .95 + _controlArea.origin.x;
}
else {
L1_X = _controlArea.size.width * .1 + _controlArea.origin.x;
L2_X = _controlArea.size.width * .1 + _controlArea.origin.x;
R1_X = _controlArea.size.width * .9 + _controlArea.origin.x;
R2_X = _controlArea.size.width * .9 + _controlArea.origin.x;
}
}
- (void) drawButtons {
@@ -836,26 +856,31 @@ static float L3_Y;
}
- (BOOL) isInDeadZone:(UITouch*) touch {
switch (_level) {
case OnScreenControlsLevelFull:
return [self isDpadDeadZone:touch]
|| [self isAbxyDeadZone:touch]
|| [self isTriggerDeadZone:touch]
|| [self isBumperDeadZone:touch]
|| [self isStartSelectDeadZone:touch];
case OnScreenControlsLevelSimple:
return [self isTriggerDeadZone:touch]
|| [self isStartSelectDeadZone:touch]
|| [self isL3R3DeadZone:touch];
case OnScreenControlsLevelAutoGCExtendedGamepad:
return [self isL3R3DeadZone:touch]
|| [self isStartSelectDeadZone:touch];
case OnScreenControlsLevelAutoGCGamepad:
return [self isTriggerDeadZone:touch]
|| [self isStartSelectDeadZone:touch];
default:
return false;
// Dynamically evaluate deadzones based on the controls
// on screen at the time
if (_leftButton.superlayer != nil && [self isDpadDeadZone:touch]) {
return true;
}
else if (_aButton.superlayer != nil && [self isAbxyDeadZone:touch]) {
return true;
}
else if (_l2Button.superlayer != nil && [self isTriggerDeadZone:touch]) {
return true;
}
else if (_l1Button.superlayer != nil && [self isBumperDeadZone:touch]) {
return true;
}
else if (_startButton.superlayer != nil && [self isStartSelectDeadZone:touch]) {
return true;
}
else if (_l3Button.superlayer != nil && [self isL3R3DeadZone:touch]) {
return true;
}
else if (_leftStickBackground.superlayer != nil && [self isStickDeadZone:touch]) {
return true;
}
return false;
}
- (BOOL) isDpadDeadZone:(UITouch*) touch {
@@ -926,6 +951,19 @@ static float L3_Y;
endY:_view.frame.origin.y + _view.frame.size.height];
}
- (BOOL) isStickDeadZone:(UITouch*) touch {
return [self isDeadZone:touch
startX:_leftStickBackground.frame.origin.x - 15
startY:_leftStickBackground.frame.origin.y - 15
endX:_leftStickBackground.frame.origin.x + _leftStickBackground.frame.size.width + 15
endY:_view.frame.origin.y + _view.frame.size.height]
|| [self isDeadZone:touch
startX:_rightStickBackground.frame.origin.x - 15
startY:_rightStickBackground.frame.origin.y - 15
endX:_rightStickBackground.frame.origin.x + _rightStickBackground.frame.size.width + 15
endY:_view.frame.origin.y + _view.frame.size.height];
}
- (BOOL) isDeadZone:(UITouch*) touch startX:(float)deadZoneStartX startY:(float)deadZoneStartY endX:(float)deadZoneEndX endY:(float)deadZoneEndY {
deadZoneStartX -= DEAD_ZONE_PADDING;
deadZoneStartY -= DEAD_ZONE_PADDING;
+4 -2
View File
@@ -6,7 +6,6 @@
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "ControllerSupport.h"
@protocol EdgeDetectionDelegate <NSObject>
@@ -15,8 +14,11 @@
@end
@interface StreamView : UIView
@interface StreamView : OSView <UITextFieldDelegate>
@property (nonatomic, retain) IBOutlet UITextField* keyInputField;
- (void) setupStreamView;
- (void) setupOnScreenControls:(ControllerSupport*)controllerSupport swipeDelegate:(id<EdgeDetectionDelegate>)swipeDelegate;
- (void) setMouseDeltaFactors:(float)x y:(float)y;
+240 -26
View File
@@ -11,18 +11,27 @@
#import "OnScreenControls.h"
#import "DataManager.h"
#import "ControllerSupport.h"
#import "KeyboardSupport.h"
@implementation StreamView {
CGPoint touchLocation;
CGPoint touchLocation, originalLocation;
BOOL touchMoved;
OnScreenControls* onScreenControls;
BOOL isInputingText;
BOOL isDragging;
NSTimer* dragTimer;
float xDeltaFactor;
float yDeltaFactor;
float screenFactor;
#if TARGET_OS_TV
UIGestureRecognizer* remotePressRecognizer;
UIGestureRecognizer* remoteLongPressRecognizer;
#endif
NSDictionary<NSString *, NSNumber *> *dictCodes;
}
- (void) setMouseDeltaFactors:(float)x y:(float)y {
@@ -32,6 +41,19 @@
screenFactor = [[UIScreen mainScreen] scale];
}
- (void) setupStreamView {
#if TARGET_OS_TV
remotePressRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(remoteButtonPressed:)];
remotePressRecognizer.allowedPressTypes = @[@(UIPressTypeSelect)];
remoteLongPressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(remoteButtonLongPressed:)];
remoteLongPressRecognizer.allowedPressTypes = @[@(UIPressTypeSelect)];
[self addGestureRecognizer:remotePressRecognizer];
[self addGestureRecognizer:remoteLongPressRecognizer];
#endif
}
- (void) setupOnScreenControls:(ControllerSupport*)controllerSupport swipeDelegate:(id<EdgeDetectionDelegate>)swipeDelegate {
onScreenControls = [[OnScreenControls alloc] initWithView:self controllerSup:controllerSupport swipeDelegate:swipeDelegate];
DataManager* dataMan = [[DataManager alloc] init];
@@ -44,13 +66,19 @@
Log(LOG_I, @"Setting manual on-screen controls level: %d", (int)level);
[onScreenControls setLevel:level];
}
[self becomeFirstResponder];
}
- (Boolean)isConfirmedMove:(CGPoint)currentPoint from:(CGPoint)originalPoint {
// Movements of greater than 10 pixels are considered confirmed
return hypotf(originalPoint.x - currentPoint.x, originalPoint.y - currentPoint.y) >= 10;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
Log(LOG_D, @"Touch down");
if (![onScreenControls handleTouchDownEvent:touches]) {
UITouch *touch = [[event allTouches] anyObject];
touchLocation = [touch locationInView:self];
originalLocation = touchLocation = [touch locationInView:self];
touchMoved = false;
if ([[event allTouches] count] == 1 && !isDragging) {
dragTimer = [NSTimer scheduledTimerWithTimeInterval:0.650
@@ -71,8 +99,6 @@
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if (![onScreenControls handleTouchMovedEvent:touches]) {
[dragTimer invalidate];
dragTimer = nil;
if ([[event allTouches] count] == 1) {
UITouch *touch = [[event allTouches] anyObject];
CGPoint currentLocation = [touch locationInView:self];
@@ -88,8 +114,13 @@
if (deltaX != 0 || deltaY != 0) {
LiSendMouseMoveEvent(deltaX, deltaY);
touchMoved = true;
touchLocation = currentLocation;
// If we've moved far enough to confirm this wasn't just human/machine error,
// mark it as such.
if ([self isConfirmedMove:touchLocation from:originalLocation]) {
touchMoved = true;
}
}
}
} else if ([[event allTouches] count] == 2) {
@@ -100,7 +131,13 @@
if (touchLocation.y != avgLocation.y) {
LiSendScrollEvent(avgLocation.y - touchLocation.y);
}
touchMoved = true;
// If we've moved far enough to confirm this wasn't just human/machine error,
// mark it as such.
if ([self isConfirmedMove:firstLocation from:originalLocation]) {
touchMoved = true;
}
touchLocation = avgLocation;
}
}
@@ -112,32 +149,67 @@
if (![onScreenControls handleTouchUpEvent:touches]) {
[dragTimer invalidate];
dragTimer = nil;
if (!touchMoved) {
if ([[event allTouches] count] == 2) {
Log(LOG_D, @"Sending right mouse button press");
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_RIGHT);
// Wait 100 ms to simulate a real button press
usleep(100 * 1000);
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_RIGHT);
} else {
if (!isDragging){
Log(LOG_D, @"Sending left mouse button press");
if (isDragging) {
isDragging = false;
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT);
} else if (!touchMoved) {
if ([[event allTouches] count] == 3) {
if (isInputingText) {
Log(LOG_D, @"Closing the keyboard");
[_keyInputField resignFirstResponder];
isInputingText = false;
} else {
Log(LOG_D, @"Opening the keyboard");
// Prepare the textbox used to capture keyboard events.
_keyInputField.delegate = self;
_keyInputField.text = @"0";
[_keyInputField becomeFirstResponder];
[_keyInputField addTarget:self action:@selector(onKeyboardPressed:) forControlEvents:UIControlEventEditingChanged];
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT);
// Undo causes issues for our state management, so turn it off
[_keyInputField.undoManager disableUndoRegistration];
isInputingText = true;
}
} else if ([[event allTouches] count] == 2) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
Log(LOG_D, @"Sending right mouse button press");
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_RIGHT);
// Wait 100 ms to simulate a real button press
usleep(100 * 1000);
}
isDragging = false;
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT);
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_RIGHT);
});
} else if ([[event allTouches] count] == 1) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
if (!self->isDragging){
Log(LOG_D, @"Sending left mouse button press");
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT);
// Wait 100 ms to simulate a real button press
usleep(100 * 1000);
}
self->isDragging = false;
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT);
});
}
}
else if (isDragging) {
isDragging = false;
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT);
// We we're moving from 2+ touches to 1. Synchronize the current position
// of the active finger so we don't jump unexpectedly on the next touchesMoved
// callback when finger 1 switches on us.
if ([[event allTouches] count] - [touches count] == 1) {
NSMutableSet *activeSet = [[NSMutableSet alloc] initWithCapacity:[[event allTouches] count]];
[activeSet unionSet:[event allTouches]];
[activeSet minusSet:touches];
touchLocation = [[activeSet anyObject] locationInView:self];
// Mark this touch as moved so we don't send a left mouse click if the user
// right clicks without moving their other finger.
touchMoved = true;
}
}
}
@@ -145,5 +217,147 @@
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
}
#if TARGET_OS_TV
- (void)remoteButtonPressed:(id)sender {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
Log(LOG_D, @"Sending left mouse button press");
// Mark this as touchMoved to avoid a duplicate press on touch up
self->touchMoved = true;
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT);
// Wait 100 ms to simulate a real button press
usleep(100 * 1000);
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT);
});
}
- (void)remoteButtonLongPressed:(id)sender {
Log(LOG_D, @"Holding left mouse button");
isDragging = true;
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT);
}
#endif
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
// Disable all gesture recognizers to prevent them from eating our touches.
// This can happen on iOS 13 where the 3 finger tap gesture is taken over for
// displaying custom edit controls.
return NO;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
// This method is called when the "Return" key is pressed.
LiSendKeyboardEvent(0x0d, KEY_ACTION_DOWN, 0);
usleep(50 * 1000);
LiSendKeyboardEvent(0x0d, KEY_ACTION_UP, 0);
return NO;
}
- (void)onKeyboardPressed:(UITextField *)textField {
NSString* inputText = textField.text;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// If the text became empty, we know the user pressed the backspace key.
if ([inputText isEqual:@""]) {
LiSendKeyboardEvent(0x08, KEY_ACTION_DOWN, 0);
usleep(50 * 1000);
LiSendKeyboardEvent(0x08, KEY_ACTION_UP, 0);
} else {
// Character 0 will be our known sentinel value
for (int i = 1; i < [inputText length]; i++) {
struct KeyEvent event = [KeyboardSupport translateKeyEvent:[inputText characterAtIndex:i] withModifierFlags:0];
if (event.keycode == 0) {
// If we don't know the code, don't send anything.
Log(LOG_W, @"Unknown key code: [%c]", [inputText characterAtIndex:i]);
continue;
}
[self sendLowLevelEvent:event];
}
}
});
// Reset text field back to known state
textField.text = @"0";
// Move the insertion point back to the end of the text box
UITextRange *textRange = [textField textRangeFromPosition:textField.endOfDocument toPosition:textField.endOfDocument];
[textField setSelectedTextRange:textRange];
}
- (void)specialCharPressed:(UIKeyCommand *)cmd {
struct KeyEvent event = [KeyboardSupport translateKeyEvent:0x20 withModifierFlags:[cmd modifierFlags]];
event.keycode = [[dictCodes valueForKey:[cmd input]] intValue];
[self sendLowLevelEvent:event];
}
- (void)keyPressed:(UIKeyCommand *)cmd {
struct KeyEvent event = [KeyboardSupport translateKeyEvent:[[cmd input] characterAtIndex:0] withModifierFlags:[cmd modifierFlags]];
[self sendLowLevelEvent:event];
}
- (void)sendLowLevelEvent:(struct KeyEvent)event {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// When we want to send a modified key (like uppercase letters) we need to send the
// modifier ("shift") seperately from the key itself.
if (event.modifier != 0) {
LiSendKeyboardEvent(event.modifierKeycode, KEY_ACTION_DOWN, event.modifier);
}
LiSendKeyboardEvent(event.keycode, KEY_ACTION_DOWN, event.modifier);
usleep(50 * 1000);
LiSendKeyboardEvent(event.keycode, KEY_ACTION_UP, event.modifier);
if (event.modifier != 0) {
LiSendKeyboardEvent(event.modifierKeycode, KEY_ACTION_UP, event.modifier);
}
});
}
- (BOOL)canBecomeFirstResponder {
return YES;
}
- (NSArray<UIKeyCommand *> *)keyCommands
{
NSString *charset = @"qwertyuiopasdfghjklzxcvbnm1234567890\t§[]\\'\"/.,`<>-´ç+`¡'º;ñ= ";
NSMutableArray<UIKeyCommand *> * commands = [NSMutableArray<UIKeyCommand *> array];
dictCodes = [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithInt: 0x0d], @"\r", [NSNumber numberWithInt: 0x08], @"\b", [NSNumber numberWithInt: 0x1b], UIKeyInputEscape, [NSNumber numberWithInt: 0x28], UIKeyInputDownArrow, [NSNumber numberWithInt: 0x26], UIKeyInputUpArrow, [NSNumber numberWithInt: 0x25], UIKeyInputLeftArrow, [NSNumber numberWithInt: 0x27], UIKeyInputRightArrow, nil];
[charset enumerateSubstringsInRange:NSMakeRange(0, charset.length)
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
[commands addObject:[UIKeyCommand keyCommandWithInput:substring modifierFlags:0 action:@selector(keyPressed:)]];
[commands addObject:[UIKeyCommand keyCommandWithInput:substring modifierFlags:UIKeyModifierShift action:@selector(keyPressed:)]];
[commands addObject:[UIKeyCommand keyCommandWithInput:substring modifierFlags:UIKeyModifierControl action:@selector(keyPressed:)]];
[commands addObject:[UIKeyCommand keyCommandWithInput:substring modifierFlags:UIKeyModifierAlternate action:@selector(keyPressed:)]];
}];
for (NSString *c in [dictCodes keyEnumerator]) {
[commands addObject:[UIKeyCommand keyCommandWithInput:c
modifierFlags:0
action:@selector(specialCharPressed:)]];
[commands addObject:[UIKeyCommand keyCommandWithInput:c
modifierFlags:UIKeyModifierShift
action:@selector(specialCharPressed:)]];
[commands addObject:[UIKeyCommand keyCommandWithInput:c
modifierFlags:UIKeyModifierShift | UIKeyModifierAlternate
action:@selector(specialCharPressed:)]];
[commands addObject:[UIKeyCommand keyCommandWithInput:c
modifierFlags:UIKeyModifierShift | UIKeyModifierControl
action:@selector(specialCharPressed:)]];
[commands addObject:[UIKeyCommand keyCommandWithInput:c
modifierFlags:UIKeyModifierControl
action:@selector(specialCharPressed:)]];
[commands addObject:[UIKeyCommand keyCommandWithInput:c
modifierFlags:UIKeyModifierControl | UIKeyModifierAlternate
action:@selector(specialCharPressed:)]];
[commands addObject:[UIKeyCommand keyCommandWithInput:c
modifierFlags:UIKeyModifierAlternate
action:@selector(specialCharPressed:)]];
}
return commands;
}
@end

Some files were not shown because too many files have changed in this diff Show More