Compare commits

...
277 Commits
Author SHA1 Message Date
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
Diego Waxemberg eac2d985e2 Created build v1.1.4 and removed empty icon sets 2016-12-14 22:05:47 -08:00
Cameron Gutman 61ea7f3f1c Update moonlight-common-c with support for GFE 3.2 2016-12-13 21:29:03 -08:00
Cameron Gutman 40f7faa1b1 Update moonlight-common-c project to recommended settings on Xcode 8.1 2016-10-29 19:55:22 -07:00
Diego Waxemberg 9e18b8021b Bumping app version for v1.1.3 2016-10-26 19:08:33 -07:00
Cameron Gutman ce95f2c153 Fix some pairing glitches and add a cancel button 2016-10-21 12:07:29 -07:00
Cameron Gutman fd50b71b16 Fix detection of running apps on GFE 3.1 2016-10-21 11:41:42 -07:00
Diego Waxemberg 39ab472cf6 Updated to use swift 3 2016-10-05 23:45:48 -07:00
Diego Waxemberg 48ba7eb2dc Bump version for v1.1.2 2016-10-05 23:00:46 -07:00
Diego Waxemberg 42edaa1bad Added DerivedData to gitignore 2016-10-05 22:58:38 -07:00
Diego Waxemberg 955fb59156 Merge branch 'master' of github.com:moonlight-stream/moonlight-ios 2016-10-05 22:57:43 -07:00
Diego Waxemberg 2fbc67ef00 Bumped version for v1.1.1 2016-10-05 22:56:40 -07:00
Cameron Gutman 23564a3028 Add support for GFE 3.0.7 2016-10-05 17:32:30 -07:00
Cameron Gutman 9a07d5a33a Display an error if attempting to close another session's app rather than silently failing 2016-09-24 21:26:50 -07:00
Cameron Gutman 84c037f235 Move the pull arrow up to the top of the left side and make it clickable on the PC view page 2016-09-24 20:39:43 -07:00
Cameron Gutman c13df82f5b Update common-c to fix Gen 3 servers 2016-09-24 19:09:21 -07:00
Cameron Gutman b15d61a5bf Allow existing host to be updated via the Add PC dialog 2016-09-24 18:53:52 -07:00
Cameron Gutman 5aff719984 Add message to enable GameStream for GFE 3.0 2016-09-24 18:51:55 -07:00
Cameron Gutman b0c2978405 Update common-c submodule with const char * change 2016-07-31 17:10:32 -07:00
Cameron Gutman 91dcb505de Fix displaying names of custom added apps on GFE 3.0 2016-07-31 14:40:08 -07:00
Cameron Gutman d1f35d938b Apply Xcode 8 recommended project settings 2016-07-31 12:02:35 -07:00
Cameron Gutman 359b822951 Migrate to Swift 2.3 for Xcode 8 2016-07-31 12:00:09 -07:00
Cameron Gutman 39580e4352 Fix wiki link 2016-05-30 12:23:41 -05:00
Cameron Gutman 7b3d099449 Use the same Controller object for player 0 and the on-screen controls so tapping on-screen buttons doesn't reset all physical controller input to 0. Fixes pressing L3 to run in some games. 2016-05-22 17:11:30 -05:00
Cameron Gutman b17c45e9ca Update common-c to include MTU fix for LTE streaming 2016-05-21 21:32:36 -05:00
Cameron Gutman 4c7a2cecdd Allow the user to slide between directions on the d-pad. This closes #222 2016-05-08 15:42:08 -04:00
Cameron Gutman 175074ece8 Tweaks to dragging behavior to match Moonlight Android 2016-05-08 15:09:43 -04:00
Cameron Gutman 8a5ec513bf Update common-c 2016-05-08 14:59:08 -04:00
Cameron Gutman 7cfb7f73a2 Fix some WOL issues including crashes and broken functionality with DNS names 2016-05-08 14:56:22 -04:00
Cameron Gutman c608685737 Update common-c with bugfixes 2016-04-27 02:24:54 -04:00
Cameron Gutman 3beca2fc72 Update moonlight-common-c to fix a couple crashing bugs 2016-04-18 16:46:59 -04:00
Cameron Gutman c59aafb4bb Merge pull request #215 from Mimiste/master
Drag and drop
2016-04-18 14:02:07 -04:00
Mimiste 256e40acdc 1 Second touch for the drag and drop
Changed the timer duration to 1 second to initiate the drag and drop
2016-04-18 19:44:00 +02:00
Mimiste 39b1b65e16 Merge remote-tracking branch 'moonlight-stream/master' 2016-04-16 18:56:02 +02:00
Cameron Gutman 1783abec13 Lock around LiStartConnection() and LiStopConnection() to fix thread-safety issues. This will hopefully address the crashes seen in 1.0.3 and 1.0.4. 2016-04-12 00:50:59 -04:00
David 983c65d399 Rewrote the Controller class in Swift (#216)
* Enabled Defines Modules for swift code

* Created bridge header, created swift Controller

-  Created Controller.swift
-  Created bridge header for use of objc in swift

* Finished porting Controller files to swift

* Added comments, created MoonlightUnitTest

-  Added comments for Controller.swift
-  Created MoonlightUnitTest for testing new class

* Started writing tests for ControllerUnitTests

-  General formatting
-  Wrote helper functions
-  Wrote tests for first four properties

* Finished writing Controller tests

* Removed commented out lines
2016-04-11 20:44:02 -07:00
Mimiste 4caa9672c9 Drag and drop
The drag and drop is now enabled only if touching with one finger.
The tap to drop does not fire another timer
2016-04-10 13:02:00 +02:00
Mimiste 15be6a3f75 Merge remote-tracking branch 'moonlight-stream/master' 2016-04-10 11:48:30 +02:00
Michelle Bergeron d38eb0a3ba s/current/currently 2016-04-09 23:12:59 -07:00
Mimiste e8ea942ad2 Drag and drop
Possibility to initiate a drag and drop

- Touch for 2 second to initiate a drag
- Move the cursor as usual
- Tap top drop
2016-04-08 00:12:59 +02:00
Diego Waxemberg cb68b10475 Removed "Headers" build phase from common and created v1.0.4 2016-03-31 20:36:07 -07:00
Cameron Gutman 11c854ce44 Update common-c to support Gen 7 2016-03-31 11:27:02 -04:00
Cameron Gutman 9636f4742b Implement pairing for Gen 7 servers 2016-03-31 11:20:18 -04:00
Cameron Gutman 5867317526 Move the submodule and subproject to their new locations 2016-03-31 07:59:35 -04:00
Cameron Gutman 8a8f7b6903 Remove old submodule 2016-03-31 07:23:37 -04:00
Diego Waxemberg 61b6e25a91 Bumping app version for 1.0.3 2016-03-08 22:35:06 -08:00
Diego Waxemberg 50cff820de Added app icon for ipad pro 2016-03-08 22:34:15 -08:00
Cameron Gutman 4e586a28c8 Update common-c to fix RTSP timeout issue 2016-03-08 13:15:33 -08:00
Cameron Gutman 0702b4ed57 Fix a performance regression in handling the applist response 2016-03-07 16:12:10 -08:00
Cameron Gutman 4a2132a682 Update common-c to support GFE 2.11 2016-03-07 14:45:05 -08:00
321 changed files with 36208 additions and 25136 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
+1
View File
@@ -1,3 +1,4 @@
**/xcuserdata/
**/Moonlight.xcscmblueprint
Build
DerivedData
+3 -3
View File
@@ -1,3 +1,3 @@
[submodule "limelight-common-c"]
path = limelight-common-c
url = git@github.com:limelight-stream/limelight-common-c.git
[submodule "moonlight-common/moonlight-common-c"]
path = moonlight-common/moonlight-common-c
url = https://github.com/moonlight-stream/moonlight-common-c.git
+2 -2
View File
@@ -28,8 +28,8 @@
</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"/>
<button key="customView" opaque="NO" contentMode="center" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" id="9JC-B5-BAE">
<rect key="frame" x="20" y="-1" width="109" height="46"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<animations/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
+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
+5 -5
View File
@@ -6,19 +6,19 @@
// 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*) createAESKeyFromSalt:(NSData*)saltedPIN;
- (NSData*) createAESKeyFromSaltSHA1:(NSData*)saltedPIN;
- (NSData*) createAESKeyFromSaltSHA256:(NSData*)saltedPIN;
- (NSData*) SHA1HashData:(NSData*)data;
- (NSData*) SHA256HashData:(NSData*)data;
- (NSData*) aesEncrypt:(NSData*)data withKey:(NSData*)key;
- (NSData*) aesDecrypt:(NSData*)data withKey:(NSData*)key;
- (bool) verifySignature:(NSData *)data withSignature:(NSData*)signature andCert:(NSData*)cert;
+110 -50
View File
@@ -16,19 +16,31 @@
#include <openssl/evp.h>
@implementation CryptoManager
static const int SHA1_DIGEST_LENGTH = 20;
static const int SHA1_HASH_LENGTH = 20;
static const int SHA256_HASH_LENGTH = 32;
static NSData* key = nil;
static NSData* cert = nil;
static NSData* p12 = nil;
- (NSData*) createAESKeyFromSalt:(NSData*)saltedPIN {
- (NSData*) createAESKeyFromSaltSHA1:(NSData*)saltedPIN {
return [[self SHA1HashData:saltedPIN] subdataWithRange:NSMakeRange(0, 16)];
}
- (NSData*) createAESKeyFromSaltSHA256:(NSData*)saltedPIN {
return [[self SHA256HashData:saltedPIN] subdataWithRange:NSMakeRange(0, 16)];
}
- (NSData*) SHA1HashData:(NSData*)data {
unsigned char sha1[SHA1_DIGEST_LENGTH];
unsigned char sha1[SHA1_HASH_LENGTH];
SHA1([data bytes], [data length], sha1);
NSData* bytes = [NSData dataWithBytes:sha1 length:20];
NSData* bytes = [NSData dataWithBytes:sha1 length:sizeof(sha1)];
return bytes;
}
- (NSData*) SHA256HashData:(NSData*)data {
unsigned char sha256[SHA256_HASH_LENGTH];
SHA256([data bytes], [data length], sha256);
NSData* bytes = [NSData dataWithBytes:sha256 length:sizeof(sha256)];
return bytes;
}
@@ -75,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);
@@ -110,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);
@@ -147,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!");
@@ -206,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;
+1 -16
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;
+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;
+46 -38
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];
}];
@@ -61,10 +86,10 @@
- (void) updateHost:(TemporaryHost *)host {
[_managedObjectContext performBlockAndWait:^{
// Add a new persistent managed object if one doesn't exist
Host* parent = [self getHostForTemporaryHost:host];
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
@@ -76,19 +101,20 @@
- (void) updateAppsForExistingHost:(TemporaryHost *)host {
[_managedObjectContext performBlockAndWait:^{
Host* parent = [self getHostForTemporaryHost:host];
Host* parent = [self getHostForTemporaryHost:host withHostRecords:[self fetchRecords:@"Host"]];
if (parent == nil) {
// The host must exist to be updated
return;
}
NSMutableSet *applist = [[NSMutableSet alloc] init];
NSArray *appRecords = [self fetchRecords:@"App"];
for (TemporaryApp* app in host.appList) {
// Add a new persistent managed object if one doesn't exist
App* parentApp = [self getAppForTemporaryApp:app];
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];
@@ -102,20 +128,6 @@
}];
}
- (void) updateIconForExistingApp:(TemporaryApp*)app {
[_managedObjectContext performBlockAndWait:^{
App* parentApp = [self getAppForTemporaryApp:app];
if (parentApp == nil) {
// The app must exist to be updated
return;
}
parentApp.image = app.image;
[self saveData];
}];
}
- (TemporarySettings*) getSettings {
__block TemporarySettings *tempSettings;
@@ -142,9 +154,9 @@
- (void) removeApp:(TemporaryApp*)app {
[_managedObjectContext performBlockAndWait:^{
App* managedApp = [self getAppForTemporaryApp:app];
App* managedApp = [self getAppForTemporaryApp:app withAppRecords:[self fetchRecords:@"App"]];
if (managedApp != nil) {
[_managedObjectContext deleteObject:managedApp];
[self->_managedObjectContext deleteObject:managedApp];
[self saveData];
}
}];
@@ -152,9 +164,9 @@
- (void) removeHost:(TemporaryHost*)host {
[_managedObjectContext performBlockAndWait:^{
Host* managedHost = [self getHostForTemporaryHost:host];
Host* managedHost = [self getHostForTemporaryHost:host withHostRecords:[self fetchRecords:@"Host"]];
if (managedHost != nil) {
[_managedObjectContext deleteObject:managedHost];
[self->_managedObjectContext deleteObject:managedHost];
[self saveData];
}
}];
@@ -162,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);
}
@@ -184,9 +196,7 @@
}
// Only call from within performBlockAndWait!!!
- (Host*) getHostForTemporaryHost:(TemporaryHost*)tempHost {
NSArray *hosts = [self fetchRecords:@"Host"];
- (Host*) getHostForTemporaryHost:(TemporaryHost*)tempHost withHostRecords:(NSArray*)hosts {
for (Host* host in hosts) {
if ([tempHost.uuid isEqualToString:host.uuid]) {
return host;
@@ -197,12 +207,10 @@
}
// Only call from within performBlockAndWait!!!
- (App*) getAppForTemporaryApp:(TemporaryApp*)tempApp {
NSArray *apps = [self fetchRecords:@"App"];
- (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
+2 -4
View File
@@ -6,16 +6,14 @@
// 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 (nonatomic) BOOL isRunning;
@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;
}
+9 -6
View File
@@ -6,22 +6,25 @@
// 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) 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 *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;
+25 -7
View File
@@ -8,15 +8,14 @@
#import "DataManager.h"
#import "TemporaryHost.h"
#import "Host.h"
#import "TemporaryApp.h"
#import "App.h"
@implementation TemporaryHost
- (id) init {
self = [super init];
self.appList = [[NSMutableSet alloc] init];
self.currentGame = @"0";
return self;
}
@@ -30,7 +29,11 @@
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];
@@ -45,12 +48,27 @@
}
- (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.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 = (NSInteger)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.
Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

@@ -1,76 +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" : "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: 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

+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

+2 -2
View File
@@ -6,8 +6,8 @@
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "logo_universal.png"
"filename" : "logo.png",
"scale" : "2x"
},
{
"idiom" : "universal",
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 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

+15 -12
View File
@@ -2,22 +2,25 @@
// Controller.h
// Moonlight
//
// Created by Diego Waxemberg on 2/1/15.
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
// Created by Cameron Gutman on 2/11/19.
// Copyright © 2019 Moonlight Game Streaming Project. All rights reserved.
//
#import <Foundation/Foundation.h>
@import GameController;
@interface Controller : NSObject
@property (nonatomic) int playerIndex;
@property (nonatomic) int lastButtonFlags;
@property (nonatomic) int emulatingButtonFlags;
@property (nonatomic) char lastLeftTrigger;
@property (nonatomic) char lastRightTrigger;
@property (nonatomic) short lastLeftStickX;
@property (nonatomic) short lastLeftStickY;
@property (nonatomic) short lastRightStickX;
@property (nonatomic) short lastRightStickY;
@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
+4 -6
View File
@@ -2,14 +2,12 @@
// Controller.m
// Moonlight
//
// Created by Diego Waxemberg on 2/1/15.
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
// Created by Cameron Gutman on 2/11/19.
// Copyright © 2019 Moonlight Game Streaming Project. All rights reserved.
//
#import "Controller.h"
#include "Controller.h"
@implementation Controller
@synthesize playerIndex;
@synthesize lastButtonFlags, emulatingButtonFlags, lastLeftTrigger, lastRightTrigger;
@synthesize lastLeftStickX, lastLeftStickY, lastRightStickX, lastRightStickY;
@end
+18 -2
View File
@@ -6,16 +6,28 @@
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
//
#import <Foundation/Foundation.h>
// Swift
#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;
@@ -30,6 +42,10 @@
-(void) updateFinished:(Controller*)controller;
-(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;
+253 -28
View File
@@ -7,28 +7,75 @@
//
#import "ControllerSupport.h"
#import "OnScreenControls.h"
#import "Controller.h"
#import "OnScreenControls.h"
#if !TARGET_OS_IPHONE
#import "Gamepad.h"
#import "Control.h"
#endif
#import "DataManager.h"
#include "Limelight.h"
@import GameController;
@import AudioToolbox;
@implementation ControllerSupport {
NSLock *_controllerStreamLock;
NSMutableDictionary *_controllers;
NSTimer *_rumbleTimer;
OnScreenControls *_osc;
char _controllerNumbers;
// This controller object is shared between on-screen controls
// and player 0
Controller *_player0osc;
#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
{
// 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);
}
}
}
-(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) {
@@ -105,17 +152,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];
}
}
@@ -139,7 +185,9 @@
{
[_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];
}
@@ -162,20 +210,24 @@
{
if (controller != NULL) {
controller.controllerPausedHandler = ^(GCController *controller) {
Controller* limeController = [_controllers objectForKey:[NSNumber numberWithInteger:controller.playerIndex]];
[self setButtonFlag:limeController flags:PLAY_FLAG];
[self updateFinished:limeController];
Controller* limeController = [self->_controllers objectForKey:[NSNumber numberWithInteger:controller.playerIndex]];
// Pause for 100 ms
usleep(100 * 1000);
[self clearButtonFlag:limeController flags:PLAY_FLAG];
[self updateFinished:limeController];
// 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;
@@ -193,6 +245,16 @@
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);
}
}
leftStickX = gamepad.leftThumbstick.xAxis.value * 0x7FFE;
leftStickY = gamepad.leftThumbstick.yAxis.value * 0x7FFE;
@@ -210,7 +272,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);
@@ -249,6 +311,12 @@
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;
}
}
break;
}
else if (controller.gamepad != NULL) {
@@ -258,7 +326,9 @@
}
}
#if TARGET_OS_IPHONE
[_osc setLevel:level];
#endif
}
-(void) initAutoOnScreenControlMode:(OnScreenControls*)osc
@@ -272,9 +342,20 @@
for (int i = 0; i < 4; i++) {
if (!(_controllerNumbers & (1 << i))) {
_controllerNumbers |= (1 << i);
Controller* limeController = [[Controller alloc] init];
controller.playerIndex = i;
limeController.playerIndex = i;
Controller* limeController;
if (i == 0) {
// Player 0 shares a controller object with the on-screen controls
limeController = _player0osc;
} else {
limeController = [[Controller alloc] init];
limeController.playerIndex = i;
}
limeController.gamepad = controller;
[_controllers setObject:limeController forKey:[NSNumber numberWithInteger:controller.playerIndex]];
Log(LOG_I, @"Assigning controller index: %d", i);
@@ -283,25 +364,153 @@
}
}
-(id) init
#if TARGET_OS_IPHONE
-(Controller*) getOscController {
return _player0osc;
}
#else
-(NSMutableDictionary*) getControllers {
return _controllers;
}
-(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;
}
+(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
@@ -314,26 +523,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
@@ -0,0 +1,4 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
+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
+137 -99
View File
@@ -41,10 +41,7 @@
UITouch* _bTouch;
UITouch* _xTouch;
UITouch* _yTouch;
UITouch* _upTouch;
UITouch* _downTouch;
UITouch* _leftTouch;
UITouch* _rightTouch;
UITouch* _dpadTouch;
UITouch* _lsTouch;
UITouch* _rsTouch;
UITouch* _startTouch;
@@ -119,8 +116,7 @@ static float L3_Y;
self = [self init];
_view = view;
_controllerSupport = controllerSupport;
_controller = [[Controller alloc] init];
_controller.playerIndex = 0;
_controller = [controllerSupport getOscController];
_edgeDelegate = swipeDelegate;
_deadTouches = [[NSMutableArray alloc] init];
@@ -203,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];
@@ -239,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
@@ -249,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;
}
}
@@ -292,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;
@@ -303,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
@@ -316,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
{
@@ -333,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 {
@@ -570,6 +589,26 @@ static float L3_Y;
[_controllerSupport updateRightStick:_controller x:0x7FFE * xStickVal y:0x7FFE * -yStickVal];
updated = true;
} else if (touch == _dpadTouch) {
[_controllerSupport clearButtonFlag:_controller
flags:UP_FLAG | DOWN_FLAG | LEFT_FLAG | RIGHT_FLAG];
// Allow the user to slide their finger to another d-pad button
if ([_upButton.presentationLayer hitTest:touchLocation]) {
[_controllerSupport setButtonFlag:_controller flags:UP_FLAG];
updated = true;
} else if ([_downButton.presentationLayer hitTest:touchLocation]) {
[_controllerSupport setButtonFlag:_controller flags:DOWN_FLAG];
updated = true;
} else if ([_leftButton.presentationLayer hitTest:touchLocation]) {
[_controllerSupport setButtonFlag:_controller flags:LEFT_FLAG];
updated = true;
} else if ([_rightButton.presentationLayer hitTest:touchLocation]) {
[_controllerSupport setButtonFlag:_controller flags:RIGHT_FLAG];
updated = true;
}
buttonTouch = true;
} else if (touch == _aTouch) {
buttonTouch = true;
} else if (touch == _bTouch) {
@@ -578,14 +617,6 @@ static float L3_Y;
buttonTouch = true;
} else if (touch == _yTouch) {
buttonTouch = true;
} else if (touch == _upTouch) {
buttonTouch = true;
} else if (touch == _downTouch) {
buttonTouch = true;
} else if (touch == _leftTouch) {
buttonTouch = true;
} else if (touch == _rightTouch) {
buttonTouch = true;
} else if (touch == _startTouch) {
buttonTouch = true;
} else if (touch == _selectTouch) {
@@ -637,19 +668,19 @@ static float L3_Y;
updated = true;
} else if ([_upButton.presentationLayer hitTest:touchLocation]) {
[_controllerSupport setButtonFlag:_controller flags:UP_FLAG];
_upTouch = touch;
_dpadTouch = touch;
updated = true;
} else if ([_downButton.presentationLayer hitTest:touchLocation]) {
[_controllerSupport setButtonFlag:_controller flags:DOWN_FLAG];
_downTouch = touch;
_dpadTouch = touch;
updated = true;
} else if ([_leftButton.presentationLayer hitTest:touchLocation]) {
[_controllerSupport setButtonFlag:_controller flags:LEFT_FLAG];
_leftTouch = touch;
_dpadTouch = touch;
updated = true;
} else if ([_rightButton.presentationLayer hitTest:touchLocation]) {
[_controllerSupport setButtonFlag:_controller flags:RIGHT_FLAG];
_rightTouch = touch;
_dpadTouch = touch;
updated = true;
} else if ([_startButton.presentationLayer hitTest:touchLocation]) {
[_controllerSupport setButtonFlag:_controller flags:PLAY_FLAG];
@@ -755,21 +786,10 @@ static float L3_Y;
[_controllerSupport clearButtonFlag:_controller flags:Y_FLAG];
_yTouch = nil;
updated = true;
} else if (touch == _upTouch) {
[_controllerSupport clearButtonFlag:_controller flags:UP_FLAG];
_upTouch = nil;
updated = true;
} else if (touch == _downTouch) {
[_controllerSupport clearButtonFlag:_controller flags:DOWN_FLAG];
_downTouch = nil;
updated = true;
} else if (touch == _leftTouch) {
[_controllerSupport clearButtonFlag:_controller flags:LEFT_FLAG];
_leftTouch = nil;
updated = true;
} else if (touch == _rightTouch) {
[_controllerSupport clearButtonFlag:_controller flags:RIGHT_FLAG];
_rightTouch = nil;
} else if (touch == _dpadTouch) {
[_controllerSupport clearButtonFlag:_controller
flags:UP_FLAG | DOWN_FLAG | LEFT_FLAG | RIGHT_FLAG];
_dpadTouch = nil;
updated = true;
} else if (touch == _startTouch) {
[_controllerSupport clearButtonFlag:_controller flags:PLAY_FLAG];
@@ -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;
+3 -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,7 +14,9 @@
@end
@interface StreamView : UIView
@interface StreamView : OSView <UITextFieldDelegate>
@property (nonatomic, retain) IBOutlet UITextField* keyInputField;
- (void) setupOnScreenControls:(ControllerSupport*)controllerSupport swipeDelegate:(id<EdgeDetectionDelegate>)swipeDelegate;
- (void) setMouseDeltaFactors:(float)x y:(float)y;
+215 -25
View File
@@ -11,15 +11,22 @@
#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;
NSDictionary<NSString *, NSNumber *> *dictCodes;
}
- (void) setMouseDeltaFactors:(float)x y:(float)y {
@@ -41,14 +48,34 @@
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
target:self
selector:@selector(onDragStart:)
userInfo:nil
repeats:NO];
}
}
}
- (void)onDragStart:(NSTimer*)timer {
if (!touchMoved && !isDragging){
isDragging = true;
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT);
}
}
@@ -69,8 +96,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) {
@@ -81,7 +113,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;
}
}
@@ -91,34 +129,186 @@
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
Log(LOG_D, @"Touch up");
if (![onScreenControls handleTouchUpEvent:touches]) {
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 {
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);
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT);
[dragTimer invalidate];
dragTimer = nil;
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];
// 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);
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);
});
}
}
// 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;
}
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
}
- (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
+16 -10
View File
@@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CADisableMinimumFrameDuration</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
@@ -17,11 +19,24 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0.2</string>
<string>2.6.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>GCSupportedGameControllers</key>
<array>
<dict>
<key>ProfileName</key>
<string>ExtendedGamepad</string>
</dict>
<dict>
<key>ProfileName</key>
<string>Gamepad</string>
</dict>
</array>
<key>GCSupportsControllerUserInteraction</key>
<true/>
<key>LSApplicationCategoryType</key>
<string>public.app-category.games</string>
<key>LSRequiresIPhoneOS</key>
@@ -31,15 +46,6 @@
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>UIAppFonts</key>
<array>
<string>Roboto-Black.ttf</string>
<string>Roboto-Bold.ttf</string>
<string>Roboto-Light.ttf</string>
<string>Roboto-Medium.ttf</string>
<string>Roboto-Regular.ttf</string>
<string>Roboto-Thin.ttf</string>
</array>
<key>UILaunchStoryboardName</key>
<string>Launch Screen</string>
<key>UIMainStoryboardFile</key>
+11 -4
View File
@@ -5,14 +5,21 @@
//
#import <Availability.h>
#include <TargetConditionals.h>
#ifndef __IPHONE_3_0
#warning "This project uses features only available in iOS SDK 3.0 and later."
#endif
#ifdef __OBJC__
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "Logger.h"
#endif
#elif TARGET_OS_MAC
#import <AppKit/AppKit.h>
#endif
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#import "Logger.h"
#include "OSPortabilityDefs.h"
#endif
@@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>Moonlight v1.0-2.xcdatamodel</string>
<string>Moonlight v1.3.xcdatamodel</string>
</dict>
</plist>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="9057" systemVersion="15B42" minimumToolsVersion="Automatic">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="13772" systemVersion="17D102" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
<entity name="App" representedClassName="App" syncable="YES">
<attribute name="id" attributeType="String" syncable="YES"/>
<attribute name="image" optional="YES" attributeType="Binary" allowsExternalBinaryDataStorage="YES" syncable="YES"/>
@@ -12,21 +12,22 @@
<attribute name="localAddress" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="mac" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="name" attributeType="String" syncable="YES"/>
<attribute name="pairState" optional="YES" attributeType="Integer 32" defaultValueString="0" syncable="YES"/>
<attribute name="pairState" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="uuid" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="appList" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="App" inverseName="host" inverseEntity="App" syncable="YES"/>
</entity>
<entity name="Settings" representedClassName="Settings" syncable="YES">
<attribute name="bitrate" attributeType="Integer 32" defaultValueString="10000" syncable="YES"/>
<attribute name="framerate" attributeType="Integer 32" defaultValueString="60" syncable="YES"/>
<attribute name="height" attributeType="Integer 32" defaultValueString="720" syncable="YES"/>
<attribute name="onscreenControls" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
<attribute name="bitrate" attributeType="Integer 32" defaultValueString="10000" usesScalarValueType="NO" syncable="YES"/>
<attribute name="framerate" attributeType="Integer 32" defaultValueString="60" usesScalarValueType="NO" syncable="YES"/>
<attribute name="height" attributeType="Integer 32" defaultValueString="720" usesScalarValueType="NO" syncable="YES"/>
<attribute name="onscreenControls" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
<attribute name="streamingRemotely" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="uniqueId" attributeType="String" syncable="YES"/>
<attribute name="width" attributeType="Integer 32" defaultValueString="1280" syncable="YES"/>
<attribute name="width" attributeType="Integer 32" defaultValueString="1280" usesScalarValueType="NO" syncable="YES"/>
</entity>
<elements>
<element name="App" positionX="0" positionY="54" width="128" height="105"/>
<element name="Host" positionX="0" positionY="0" width="128" height="163"/>
<element name="Settings" positionX="0" positionY="0" width="128" height="135"/>
<element name="Settings" positionX="0" positionY="0" width="128" height="150"/>
</elements>
</model>
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14135" systemVersion="17F77" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
<entity name="App" representedClassName="App" syncable="YES" codeGenerationType="class">
<attribute name="hdrSupported" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="id" attributeType="String" syncable="YES"/>
<attribute name="image" optional="YES" attributeType="Binary" allowsExternalBinaryDataStorage="YES" syncable="YES"/>
<attribute name="name" attributeType="String" syncable="YES"/>
<relationship name="host" maxCount="1" deletionRule="Nullify" destinationEntity="Host" inverseName="appList" inverseEntity="Host" syncable="YES"/>
</entity>
<entity name="Host" representedClassName="Host" syncable="YES" codeGenerationType="class">
<attribute name="address" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="externalAddress" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="localAddress" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="mac" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="name" attributeType="String" syncable="YES"/>
<attribute name="pairState" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="serverCodecModeSupport" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="uuid" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="appList" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="App" inverseName="host" inverseEntity="App" syncable="YES"/>
</entity>
<entity name="Settings" representedClassName="Settings" syncable="YES" codeGenerationType="class">
<attribute name="bitrate" attributeType="Integer 32" defaultValueString="10000" usesScalarValueType="NO" syncable="YES"/>
<attribute name="enableHdr" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="framerate" attributeType="Integer 32" defaultValueString="60" usesScalarValueType="NO" syncable="YES"/>
<attribute name="height" attributeType="Integer 32" defaultValueString="720" usesScalarValueType="NO" syncable="YES"/>
<attribute name="multiController" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
<attribute name="onscreenControls" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
<attribute name="optimizeGames" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
<attribute name="playAudioOnPC" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="streamingRemotely" attributeType="Boolean" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="uniqueId" attributeType="String" syncable="YES"/>
<attribute name="useHevc" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="width" attributeType="Integer 32" defaultValueString="1280" usesScalarValueType="NO" syncable="YES"/>
</entity>
<elements>
<element name="App" positionX="0" positionY="54" width="128" height="105"/>
<element name="Host" positionX="0" positionY="0" width="128" height="165"/>
<element name="Settings" positionX="0" positionY="0" width="128" height="225"/>
</elements>
</model>
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14135" systemVersion="17G65" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
<entity name="App" representedClassName="App" syncable="YES" codeGenerationType="class">
<attribute name="hdrSupported" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="id" attributeType="String" syncable="YES"/>
<attribute name="name" attributeType="String" syncable="YES"/>
<relationship name="host" maxCount="1" deletionRule="Nullify" destinationEntity="Host" inverseName="appList" inverseEntity="Host" syncable="YES"/>
</entity>
<entity name="Host" representedClassName="Host" syncable="YES" codeGenerationType="class">
<attribute name="address" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="externalAddress" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="localAddress" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="mac" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="name" attributeType="String" syncable="YES"/>
<attribute name="pairState" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="serverCodecModeSupport" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="uuid" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="appList" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="App" inverseName="host" inverseEntity="App" syncable="YES"/>
</entity>
<entity name="Settings" representedClassName="Settings" syncable="YES" codeGenerationType="class">
<attribute name="bitrate" attributeType="Integer 32" defaultValueString="10000" usesScalarValueType="NO" syncable="YES"/>
<attribute name="enableHdr" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="framerate" attributeType="Integer 32" defaultValueString="60" usesScalarValueType="NO" syncable="YES"/>
<attribute name="height" attributeType="Integer 32" defaultValueString="720" usesScalarValueType="NO" syncable="YES"/>
<attribute name="multiController" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
<attribute name="onscreenControls" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
<attribute name="optimizeGames" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
<attribute name="playAudioOnPC" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="streamingRemotely" attributeType="Boolean" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="uniqueId" attributeType="String" syncable="YES"/>
<attribute name="useHevc" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="width" attributeType="Integer 32" defaultValueString="1280" usesScalarValueType="NO" syncable="YES"/>
</entity>
<elements>
<element name="App" positionX="0" positionY="54" width="128" height="105"/>
<element name="Host" positionX="0" positionY="0" width="128" height="165"/>
<element name="Settings" positionX="0" positionY="0" width="128" height="225"/>
</elements>
</model>
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14460.32" systemVersion="18C54" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
<entity name="App" representedClassName="App" syncable="YES" codeGenerationType="class">
<attribute name="hdrSupported" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="id" attributeType="String" syncable="YES"/>
<attribute name="name" attributeType="String" syncable="YES"/>
<relationship name="host" maxCount="1" deletionRule="Nullify" destinationEntity="Host" inverseName="appList" inverseEntity="Host" syncable="YES"/>
</entity>
<entity name="Host" representedClassName="Host" syncable="YES" codeGenerationType="class">
<attribute name="address" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="externalAddress" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="localAddress" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="mac" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="name" attributeType="String" syncable="YES"/>
<attribute name="pairState" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="serverCert" optional="YES" attributeType="Binary" syncable="YES"/>
<attribute name="serverCodecModeSupport" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="uuid" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="appList" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="App" inverseName="host" inverseEntity="App" syncable="YES"/>
</entity>
<entity name="Settings" representedClassName="Settings" syncable="YES" codeGenerationType="class">
<attribute name="bitrate" attributeType="Integer 32" defaultValueString="10000" usesScalarValueType="NO" syncable="YES"/>
<attribute name="enableHdr" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="framerate" attributeType="Integer 32" defaultValueString="60" usesScalarValueType="NO" syncable="YES"/>
<attribute name="height" attributeType="Integer 32" defaultValueString="720" usesScalarValueType="NO" syncable="YES"/>
<attribute name="multiController" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
<attribute name="onscreenControls" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
<attribute name="optimizeGames" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
<attribute name="playAudioOnPC" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="streamingRemotely" attributeType="Boolean" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="uniqueId" attributeType="String" syncable="YES"/>
<attribute name="useHevc" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="width" attributeType="Integer 32" defaultValueString="1280" usesScalarValueType="NO" syncable="YES"/>
</entity>
<elements>
<element name="App" positionX="0" positionY="54" width="128" height="105"/>
<element name="Host" positionX="0" positionY="0" width="128" height="195"/>
<element name="Settings" positionX="0" positionY="0" width="128" height="225"/>
</elements>
</model>
+1 -1
View File
@@ -6,7 +6,6 @@
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "TemporaryApp.h"
#import "HttpManager.h"
#import "TemporaryHost.h"
@@ -22,5 +21,6 @@
- (id) initWithCallback:(id<AppAssetCallback>)callback;
- (void) retrieveAssetsFromHost:(TemporaryHost*)host;
- (void) stopRetrieving;
+ (NSString*) boxArtPathForApp:(TemporaryApp*)app;
@end
+17 -1
View File
@@ -19,6 +19,22 @@
static const int MAX_REQUEST_COUNT = 4;
+ (NSString*) boxArtPathForApp:(TemporaryApp*)app {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *filePath = [paths objectAtIndex:0];
// Keep app assets separate by host UUID
filePath = [filePath stringByAppendingPathComponent:app.host.uuid];
// Use the app ID as the file name
filePath = [filePath stringByAppendingPathComponent:app.id];
// Add a png extension
filePath = [filePath stringByAppendingPathExtension:@"png"];
return filePath;
}
- (id) initWithCallback:(id<AppAssetCallback>)callback {
self = [super init];
_callback = callback;
@@ -30,7 +46,7 @@ static const int MAX_REQUEST_COUNT = 4;
- (void) retrieveAssetsFromHost:(TemporaryHost*)host {
for (TemporaryApp* app in host.appList) {
if (app.image == nil) {
if (![[NSFileManager defaultManager] fileExistsAtPath:[AppAssetManager boxArtPathForApp:app]]) {
AppAssetRetriever* retriever = [[AppAssetRetriever alloc] init];
retriever.app = app;
retriever.host = host;
-1
View File
@@ -6,7 +6,6 @@
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "HttpResponse.h"
@interface AppAssetResponse : NSObject <Response>
+6 -3
View File
@@ -16,9 +16,12 @@
self.statusMessage = @"App asset has no status message";
self.statusCode = -1;
}
- (UIImage*) getImage {
UIImage* appImage = [[UIImage alloc] initWithData:self.data];
- (OSImage*) getImage {
#if TARGET_OS_IPHONE
OSImage* appImage = [[OSImage alloc] initWithData:self.data];
#else
OSImage* appImage = nil;
#endif
return appImage;
}
-1
View File
@@ -6,7 +6,6 @@
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "TemporaryHost.h"
#import "TemporaryApp.h"
#import "AppAssetManager.h"
+13 -7
View File
@@ -18,18 +18,24 @@ static const double RETRY_DELAY = 2; // seconds
static const int MAX_ATTEMPTS = 5;
- (void) main {
UIImage* appImage = nil;
int attempts = 0;
while (![self isCancelled] && appImage == nil && attempts++ < MAX_ATTEMPTS) {
HttpManager* hMan = [[HttpManager alloc] initWithHost:_host.activeAddress uniqueId:[IdManager getUniqueId] deviceName:deviceName cert:[CryptoManager readCertFromFile]];
while (![self isCancelled] && attempts++ < MAX_ATTEMPTS) {
HttpManager* hMan = [[HttpManager alloc] initWithHost:_host.activeAddress uniqueId:[IdManager getUniqueId] serverCert:_host.serverCert];
AppAssetResponse* appAssetResp = [[AppAssetResponse alloc] init];
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:appAssetResp withUrlRequest:[hMan newAppAssetRequestWithAppId:self.app.id]]];
#if TARGET_OS_IPHONE
if (appAssetResp.data != nil) {
NSString* boxArtPath = [AppAssetManager boxArtPathForApp:self.app];
[[NSFileManager defaultManager] createDirectoryAtPath:[boxArtPath stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:nil];
[appAssetResp.data writeToFile:boxArtPath atomically:NO];
break;
}
#else
appImage = [UIImage imageWithData:appAssetResp.data];
self.app.image = UIImagePNGRepresentation(appImage);
#endif
if (![self isCancelled] && appImage == nil) {
if (![self isCancelled]) {
[NSThread sleepForTimeInterval:RETRY_DELAY];
}
}
+5 -9
View File
@@ -19,7 +19,7 @@
static const char* TAG_APP = "App";
static const char* TAG_APP_TITLE = "AppTitle";
static const char* TAG_APP_ID = "ID";
static const char* TAG_APP_IS_RUNNING = "IsRunning";
static const char* TAG_HDR_SUPPORTED = "IsHdrSupported";
- (void)populateWithData:(NSData *)xml {
self.data = xml;
@@ -67,15 +67,13 @@ static const char* TAG_APP_IS_RUNNING = "IsRunning";
xmlNodePtr appInfoNode = node->xmlChildrenNode;
NSString* appName = @"";
NSString* appId = nil;
BOOL appIsRunning = NO;
NSString* hdrSupported = @"0";
while (appInfoNode != NULL) {
if (!xmlStrcmp(appInfoNode->name, (xmlChar*)TAG_APP_TITLE)) {
xmlChar* nodeVal = xmlNodeListGetString(docPtr, appInfoNode->xmlChildrenNode, 1);
if (nodeVal != NULL) {
appName = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding];
xmlFree(nodeVal);
} else {
appName = @"";
}
} else if (!xmlStrcmp(appInfoNode->name, (xmlChar*)TAG_APP_ID)) {
xmlChar* nodeVal = xmlNodeListGetString(docPtr, appInfoNode->xmlChildrenNode, 1);
@@ -83,13 +81,11 @@ static const char* TAG_APP_IS_RUNNING = "IsRunning";
appId = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding];
xmlFree(nodeVal);
}
} else if (!xmlStrcmp(appInfoNode->name, (xmlChar*)TAG_APP_IS_RUNNING)) {
} else if (!xmlStrcmp(appInfoNode->name, (xmlChar*)TAG_HDR_SUPPORTED)) {
xmlChar* nodeVal = xmlNodeListGetString(docPtr, appInfoNode->xmlChildrenNode, 1);
if (nodeVal != NULL) {
appIsRunning = [[[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding] isEqualToString:@"1"];
hdrSupported = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding];
xmlFree(nodeVal);
} else {
appIsRunning = NO;
}
}
appInfoNode = appInfoNode->next;
@@ -98,7 +94,7 @@ static const char* TAG_APP_IS_RUNNING = "IsRunning";
TemporaryApp* app = [[TemporaryApp alloc] init];
app.name = appName;
app.id = appId;
app.isRunning = appIsRunning;
app.hdrSupported = [hdrSupported intValue] != 0;
[_appList addObject:app];
}
}
+21
View File
@@ -0,0 +1,21 @@
//
// ConnectionHelper.h
// Moonlight macOS
//
// Created by Felix Kratz on 22.03.18.
// Copyright © 2018 Felix Kratz. All rights reserved.
//
#import "AppListResponse.h"
#ifndef ConnectionHelper_h
#define ConnectionHelper_h
@interface ConnectionHelper : NSObject
+(AppListResponse*) getAppListForHostWithHostIP:(NSString*) hostIP serverCert:(NSData*) serverCert uniqueID:(NSString*) uniqueId;
@end
#endif /* ConnectionHelper_h */
+40
View File
@@ -0,0 +1,40 @@
//
// ConnectionHelper.m
// Moonlight macOS
//
// Created by Felix Kratz on 22.03.18.
// Copyright © 2018 Felix Kratz. All rights reserved.
//
#import "ConnectionHelper.h"
#import "ServerInfoResponse.h"
#import "HttpManager.h"
#import "PairManager.h"
#import "DiscoveryManager.h"
@implementation ConnectionHelper
+(AppListResponse*) getAppListForHostWithHostIP:(NSString*) hostIP serverCert:(NSData*) cert uniqueID:(NSString*) uniqueId {
HttpManager* hMan = [[HttpManager alloc] initWithHost:hostIP uniqueId:uniqueId serverCert:cert];
// Try up to 5 times to get the app list
AppListResponse* appListResp;
for (int i = 0; i < 5; i++) {
appListResp = [[AppListResponse alloc] init];
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:appListResp withUrlRequest:[hMan newAppListRequest]]];
if (appListResp == nil || ![appListResp isStatusOk] || [appListResp getAppList] == nil) {
Log(LOG_W, @"Failed to get applist on try %d: %@", i, appListResp.statusMessage);
// Wait for one second then retry
[NSThread sleepForTimeInterval:1];
}
else {
Log(LOG_I, @"App list successfully retreived - took %d tries", i);
return appListResp;
}
}
return nil;
}
@end
-1
View File
@@ -6,7 +6,6 @@
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MDNSManager.h"
#import "TemporaryHost.h"
+80 -34
View File
@@ -41,16 +41,16 @@
_opQueue = [[NSOperationQueue alloc] init];
_mdnsMan = [[MDNSManager alloc] initWithCallback:self];
[CryptoManager generateKeyPairUsingSSl];
[CryptoManager generateKeyPairUsingSSL];
_uniqueId = [IdManager getUniqueId];
_cert = [CryptoManager readCertFromFile];
return self;
}
- (void) discoverHost:(NSString *)hostAddress withCallback:(void (^)(TemporaryHost *, NSString*))callback {
HttpManager* hMan = [[HttpManager alloc] initWithHost:hostAddress uniqueId:_uniqueId deviceName:deviceName cert:_cert];
HttpManager* hMan = [[HttpManager alloc] initWithHost:hostAddress uniqueId:_uniqueId serverCert:nil];
ServerInfoResponse* serverInfoResponse = [[ServerInfoResponse alloc] init];
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResponse withUrlRequest:[hMan newServerInfoRequest] fallbackError:401 fallbackRequest:[hMan newHttpServerInfoRequest]]];
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResponse withUrlRequest:[hMan newServerInfoRequest:false] fallbackError:401 fallbackRequest:[hMan newHttpServerInfoRequest]]];
TemporaryHost* host = nil;
if ([serverInfoResponse isStatusOk]) {
@@ -59,17 +59,21 @@
host.online = YES;
[serverInfoResponse populateHost:host];
if (![self addHostToDiscovery:host]) {
callback(nil, @"Host already added");
callback(nil, @"Host information updated");
} else {
callback(host, nil);
}
} else {
callback(nil, @"Could not connect to host");
callback(nil, @"Could not connect to host. Ensure GameStream is enabled in GeForce Experience on your PC.");
}
}
- (void) startDiscovery {
if (shouldDiscover) {
return;
}
Log(LOG_I, @"Starting discovery");
shouldDiscover = YES;
[_mdnsMan searchForHosts];
@@ -79,6 +83,10 @@
}
- (void) stopDiscovery {
if (!shouldDiscover) {
return;
}
Log(LOG_I, @"Stopping discovery");
shouldDiscover = NO;
[_mdnsMan stopSearching];
@@ -87,22 +95,65 @@
- (void) stopDiscoveryBlocking {
Log(LOG_I, @"Stopping discovery and waiting for workers to stop");
shouldDiscover = NO;
[_mdnsMan stopSearching];
[_opQueue cancelAllOperations];
if (shouldDiscover) {
shouldDiscover = NO;
[_mdnsMan stopSearching];
[_opQueue cancelAllOperations];
}
// Ensure we always wait, just in case discovery
// was stopped already but in an async manner that
// left operations in progress.
[_opQueue waitUntilAllOperationsAreFinished];
Log(LOG_I, @"All discovery workers stopped");
}
- (BOOL) addHostToDiscovery:(TemporaryHost *)host {
if (host.uuid.length > 0 && ![self isHostInDiscovery:host]) {
if (host.uuid.length == 0) {
return NO;
}
TemporaryHost *existingHost = [self getHostInDiscovery:host.uuid];
if (existingHost != nil) {
// NB: Our logic here depends on the fact that we never propagate
// the entire TemporaryHost to existingHost. In particular, when mDNS
// discovers a PC and we poll it, we will do so over HTTP which will
// not have accurate pair state. The fields explicitly copied below
// are accurate though.
// Update address of existing host
if (host.address != nil) {
existingHost.address = host.address;
}
if (host.localAddress != nil) {
existingHost.localAddress = host.localAddress;
}
if (host.externalAddress != nil) {
existingHost.externalAddress = host.externalAddress;
}
existingHost.activeAddress = host.activeAddress;
existingHost.online = host.online;
return NO;
}
else {
// If we were added without an explicit address,
// populate it from our other available addresses
if (host.address == nil) {
if (host.externalAddress != nil) {
host.address = host.externalAddress;
}
else {
host.address = host.localAddress;
}
}
[_hostQueue addObject:host];
if (shouldDiscover) {
[_opQueue addOperation:[self createWorkerForHost:host]];
}
return YES;
}
return NO;
}
- (void) removeHostFromDiscovery:(TemporaryHost *)host {
@@ -114,37 +165,32 @@
[_hostQueue removeObject:host];
}
// Override from MDNSCallback
- (void)updateHosts:(NSArray *)hosts {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Discover the hosts before adding to eliminate duplicates
for (TemporaryHost* host in hosts) {
Log(LOG_I, @"Found host through MDNS: %@:", host.name);
// Since this is on a background thread, we do not need to use the opQueue
DiscoveryWorker* worker = (DiscoveryWorker*)[self createWorkerForHost:host];
[worker discoverHost];
if ([self addHostToDiscovery:host]) {
Log(LOG_I, @"Adding host to discovery: %@", host.name);
[_callback updateAllHosts:_hostQueue];
} else {
Log(LOG_I, @"Not adding host to discovery: %@", host.name);
}
}
});
// Override from MDNSCallback - called in a worker thread
- (void)updateHost:(TemporaryHost*)host {
// Discover the hosts before adding to eliminate duplicates
Log(LOG_D, @"Found host through MDNS: %@:", host.name);
// Since this is on a background thread, we do not need to use the opQueue
DiscoveryWorker* worker = (DiscoveryWorker*)[self createWorkerForHost:host];
[worker discoverHost];
if ([self addHostToDiscovery:host]) {
Log(LOG_I, @"Found new host through MDNS: %@:", host.name);
[self->_callback updateAllHosts:self->_hostQueue];
} else {
Log(LOG_D, @"Found existing host through MDNS: %@", host.name);
}
}
- (BOOL) isHostInDiscovery:(TemporaryHost*)host {
for (int i = 0; i < _hostQueue.count; i++) {
TemporaryHost* discoveredHost = [_hostQueue objectAtIndex:i];
if (discoveredHost.uuid.length > 0 && [discoveredHost.uuid isEqualToString:host.uuid]) {
return YES;
- (TemporaryHost*) getHostInDiscovery:(NSString*)uuidString {
for (TemporaryHost* discoveredHost in _hostQueue) {
if (discoveredHost.uuid.length > 0 && [discoveredHost.uuid isEqualToString:uuidString]) {
return discoveredHost;
}
}
return NO;
return nil;
}
- (NSOperation*) createWorkerForHost:(TemporaryHost*)host {
DiscoveryWorker* worker = [[DiscoveryWorker alloc] initWithHost:host uniqueId:_uniqueId cert:_cert];
DiscoveryWorker* worker = [[DiscoveryWorker alloc] initWithHost:host uniqueId:_uniqueId];
return worker;
}
+1 -2
View File
@@ -6,12 +6,11 @@
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "TemporaryHost.h"
@interface DiscoveryWorker : NSOperation
- (id) initWithHost:(TemporaryHost*)host uniqueId:(NSString*)uniqueId cert:(NSData*)cert;
- (id) initWithHost:(TemporaryHost*)host uniqueId:(NSString*)uniqueId;
- (void) discoverHost;
- (TemporaryHost*) getHost;
+11 -18
View File
@@ -16,16 +16,14 @@
@implementation DiscoveryWorker {
TemporaryHost* _host;
NSString* _uniqueId;
NSData* _cert;
}
static const float POLL_RATE = 2.0f; // Poll every 2 seconds
- (id) initWithHost:(TemporaryHost*)host uniqueId:(NSString*)uniqueId cert:(NSData*)cert {
- (id) initWithHost:(TemporaryHost*)host uniqueId:(NSString*)uniqueId {
self = [super init];
_host = host;
_uniqueId = uniqueId;
_cert = cert;
return self;
}
@@ -48,12 +46,12 @@ static const float POLL_RATE = 2.0f; // Poll every 2 seconds
if (_host.localAddress != nil) {
[array addObject:_host.localAddress];
}
if (_host.externalAddress != nil) {
[array addObject:_host.externalAddress];
}
if (_host.address != nil) {
[array addObject:_host.address];
}
if (_host.externalAddress != nil) {
[array addObject:_host.externalAddress];
}
// Remove duplicate addresses from the list.
// This is done using an array rather than a set
@@ -88,8 +86,8 @@ static const float POLL_RATE = 2.0f; // Poll every 2 seconds
Log(LOG_D, @"%@ has %d unique addresses", _host.name, [addresses count]);
// Give the PC 3 tries to respond before declaring it offline
for (int i = 0; i < 3; i++) {
// Give the PC 2 tries to respond before declaring it offline
for (int i = 0; i < 2; i++) {
for (NSString *address in addresses) {
if (self.cancelled) {
// Get out without updating the status because
@@ -98,7 +96,7 @@ static const float POLL_RATE = 2.0f; // Poll every 2 seconds
return;
}
ServerInfoResponse* serverInfoResp = [self requestInfoAtAddress:address];
ServerInfoResponse* serverInfoResp = [self requestInfoAtAddress:address cert:_host.serverCert];
receivedResponse = [self checkResponse:serverInfoResp];
if (receivedResponse) {
[serverInfoResp populateHost:_host];
@@ -112,13 +110,9 @@ static const float POLL_RATE = 2.0f; // Poll every 2 seconds
}
if (receivedResponse) {
Log(LOG_I, @"Received serverinfo response on try %d", i);
Log(LOG_D, @"Received serverinfo response on try %d", i);
break;
}
else {
// Wait for one second then retry
[NSThread sleepForTimeInterval:1];
}
}
_host.online = receivedResponse;
@@ -127,14 +121,13 @@ static const float POLL_RATE = 2.0f; // Poll every 2 seconds
}
}
- (ServerInfoResponse*) requestInfoAtAddress:(NSString*)address {
- (ServerInfoResponse*) requestInfoAtAddress:(NSString*)address cert:(NSData*)cert {
HttpManager* hMan = [[HttpManager alloc] initWithHost:address
uniqueId:_uniqueId
deviceName:deviceName
cert:_cert];
serverCert:cert];
ServerInfoResponse* response = [[ServerInfoResponse alloc] init];
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:response
withUrlRequest:[hMan newServerInfoRequest]
withUrlRequest:[hMan newServerInfoRequest:true]
fallbackError:401 fallbackRequest:[hMan newHttpServerInfoRequest]]];
return response;
}

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