Author SHA1 Message Date
TrueZhuanJia 6db518f4b9 Merge pull request #65 from Qian23333/noWaterMark
Build IPAs / Build IPAs (push) Canceled after 0s
modify workflow to build Stage Manager version
2025-03-14 14:01:08 +08:00
Qian23333 c0fc1becb7 modify workflow to build Stage Manager version 2025-03-07 17:39:04 +08:00
TrueZhuangjia 70e5b3520c - fix the issue of previous selected on-screen widget get deleted when deleting legacy osc button
- coding improvements for on-screen widget deletion
2025-03-02 21:47:57 +08:00
TrueZhuangjia ebec9877b0 fix some traditional Chinese localization errors 2025-03-02 18:02:10 +08:00
TrueZhuangjia d9a5d8aaac tweak stick indicator border reaching visual feedback 2025-02-28 12:37:58 +08:00
TrueZhuangjia e58d05a85b slide from left to close settings menu 2025-02-26 23:46:39 +08:00
TrueZhuangjia 3efddab070 - vertical stick indicator offset
- smaller stick indicator cross
- animation for stick ball return
- border detection for stick ball
- allow zero alpha for custom on-screen touch pads
2025-02-26 22:48:10 +08:00
TrueZhuangjia a17a27005d fix on screen widget location shift while after quick tapping in layout tool 2025-02-23 20:42:31 +08:00
TrueZhuangjia a8f7f4679b github ipa build test 2025-02-23 19:39:21 +08:00
TrueZhuanJia 11c0119944 Merge pull request #60 from Qian23333/noWaterMark
actions build
2025-02-23 19:36:53 +08:00
TrueZhuangjia 80804ee7ac super combo custom on-screen button connected by "-" in key string 2025-02-23 16:12:23 +08:00
TrueZhuangjia 06e80823b8 try to fix: layoutTool unable to lock screen orientation in some conditions 2025-02-23 10:49:09 +08:00
TrueZhuangjia ecda390dc0 Merge branch 'DevDebug' into noWaterMark: tweak padding for iPad 2025-02-23 02:38:27 +08:00
TrueZhuanJia 481c443211 Merge pull request #57 from Acaki/feat/yuv444
Support YUV 4:4:4 streaming
2025-02-23 02:27:44 +08:00
Qian23333 80b7b8eaec Merge branch 'Moonlight-the-Fried-Fish:noWaterMark' into noWaterMark 2025-02-23 02:18:11 +08:00
TrueZhuangjia 1e33fc7935 tweak padding 2025-02-23 02:09:08 +08:00
Acaki 6b42d271ba refactor: adjust padding 2025-02-20 03:17:26 +08:00
Acaki 7823d1a60b refactor: remove unnecessary line 2025-02-20 01:16:01 +08:00
Acaki d23c4a5795 feat: update translations 2025-02-20 00:09:16 +08:00
Acaki c91c40e79f feat: update supportedVideoFormats according to YUV444 setting 2025-02-20 00:09:16 +08:00
Acaki 5302f418db feat: add YUV444 toggle 2025-02-20 00:09:16 +08:00
Acaki 6370bc187a fix: crypto build error 2025-02-20 00:09:16 +08:00
TrueZhuangjia 3e2dc4989e full screen version 2025-02-13 22:06:27 +08:00
TrueZhuangjia 715cb86ab9 - better alignment & resizing for on-screen widget view
- stage manager
2025-02-13 21:55:45 +08:00
TrueZhuangjia cf375eae84 full screen version 2025-02-10 00:57:33 +08:00
TrueZhuangjia 4d4ab333f4 - make dPad, select, start OSC button on-screen widgets rectangle
- on screen control & widget size factor up to 800%
- configurable borderWidth for on-screen widget views
stage manager version
2025-02-10 00:52:56 +08:00
TrueZhuangjia ca04c62ecd fullScreen version 2025-02-09 13:08:12 +08:00
TrueZhuangjia 34f632e7e4 - fix iPhone onScreen Widget sensitivity in storyboard
- fix the issue of unable to load touch point velocity factor higher than 200%
- 700% sensitivity available for touchPad mode mouse
- change maximum sensitivity for stick & on-screen mouse touchPad widget to 600%
stage manager version
2025-02-09 12:39:48 +08:00
TrueZhuangjia 4f952b5daa simplified statsOverlay, fullscreen version 2025-02-01 19:32:42 +08:00
TrueZhuangjia edcb9cf275 add simplified statsOverlay, stage manager version 2025-02-01 19:29:45 +08:00
TrueZhuangjia ac44efbbbe try to fix iPhone host view landscape lock 2025-01-31 09:14:39 +08:00
TrueZhuangjia e0a81dbd34 fullScreen version 2025-01-29 21:20:19 +08:00
TrueZhuangjia 301c8580c8 - onScreenWidget guidelines alignment with OnScreenControl CALayers
- stage manager version
2025-01-29 21:13:38 +08:00
TrueZhuangjia 9b9b278a14 - OnScreenButtonView refactored to OnScreenWidgetView
- oscLayer guideline alignment with onScreenWidgetViews
2025-01-29 19:37:31 +08:00
Qian23333 31c6569910 Create build.yml 2025-01-29 16:56:02 +08:00
TrueZhuangjia ed3a837200 fullscreen version 2025-01-29 16:11:24 +08:00
TrueZhuangjia 9cfa5f35cb fix touchPad sensitivity slider issue.
- stage manager version
2025-01-29 16:10:01 +08:00
TrueZhuangjia cc13b60411 in mainFrameView, lock landscape orientation for iPhone, lock current orientation for iPad
Fullscreen version
2025-01-28 18:59:18 +08:00
TrueZhuangjia caa4fccabd fullScreen version 2025-01-28 13:29:19 +08:00
TrueZhuangjia 4ba33002dd UI string updates for new onScreen widgets 2025-01-28 13:13:34 +08:00
TrueZhuangjia ef418be2df special overlay button "settings" implementation 2025-01-28 12:09:43 +08:00
TrueZhuangjia b7d8889180 sensitivity & stick indicator settings in touchPad profile config 2025-01-28 10:15:17 +08:00
TrueZhuangjia 18fe545533 UI construction for new TouchPad profile settings 2025-01-27 22:44:45 +08:00
TrueZhuangjia 87208b07a5 UI tweak 2025-01-27 01:55:51 +08:00
TrueZhuangjia c713eafac0 tweak visual effect for touchPads 2025-01-27 00:19:49 +08:00
TrueZhuangjia d493cba990 display "keystring" for touchPads in edit mode 2025-01-27 00:06:05 +08:00
TrueZhuangjia ae188fd999 MOUSEPAD implementation 2025-01-26 23:35:06 +08:00
TrueZhuangjia 5747656be7 implementation of round custom osc buttons in OnscreenButtonView.swift 2025-01-26 15:17:20 +08:00
TrueZhuangjia 4e85d3d079 DPAD implemention 2025-01-26 10:57:10 +08:00
TrueZhuangjia e2bd8b1512 implementation of WASDPAD & ARROWPAD 2025-01-25 22:39:02 +08:00
TrueZhuangjia 422c34f945 L3R3 button visual effect for onScreen touchPad 2025-01-25 22:04:07 +08:00
TrueZhuangjia 8d0b646fbe newTouchPad test - LRUD touchpad visual effect 2025-01-24 20:35:00 +08:00
TrueZhuangjia 2c4a7cc055 newTouchPad test phase3 2025-01-23 21:47:51 +08:00
TrueZhuangjia 54a31689f7 change annotations for unlock device orientation 2025-01-05 19:57:50 +08:00
TrueZhuangjia 6bf967b9d2 stage manager version 2025-01-05 13:55:29 +08:00
TrueZhuangjia b6abd71392 restore waterMark Text 2025-01-05 13:09:47 +08:00
TrueZhuangjia 8506640d7f change allowPortrait to unlockDisplayOrientationInStreaming 2025-01-05 12:54:08 +08:00
TrueZhuangjia 592b7acfc4 add legacy renderView mode 2025-01-05 12:02:15 +08:00
TrueZhuangjia 858843ff9a rename & refactor mouseMode to localMousePointerMode 2025-01-04 20:59:40 +08:00
TrueZhuangjia 9ffeba72fe stop user interaction for streamFrameController->renderView to fix multi-touch issue 2025-01-04 20:41:15 +08:00
TrueZhuangjia 4074823e4e newTouchPad test phase2 2025-01-04 19:48:57 +08:00
TrueZhuangjia b99172d932 Merge branch 'newTouchPad' into noWaterMark 2024-12-03 21:31:10 +08:00
TrueZhuangjia 017b03d200 new touchpad test 2024-12-03 21:30:23 +08:00
TrueZhuangjia ea12dd3c47 remove xcdatamodel v1.11 2024-12-03 21:28:38 +08:00
TrueZhuanJia 2b98543125 Merge pull request #43 from xzzpig/ExternalScreen
Add AirPlay support & Enhanced keyboard and mouse experience
2024-12-03 21:25:03 +08:00
xzzpig a0f13881bf Add key combo support 2024-12-01 18:16:46 +08:00
xzzpig 62ae63d7f6 Allow not capture mouse 2024-11-29 23:00:54 +08:00
xzzpig 589ca19e4f Add AirPlay support 2024-11-29 12:04:08 +08:00
TrueZhuangjia b1785b1fa5 fullscreen version 2024-11-25 20:37:00 +08:00
TrueZhuangjia 3fc26d279a stage manager version 2024-11-24 23:06:49 +08:00
TrueZhuangjia bde477558f - changes on background resolution update.
- disable allowPortrait setting option is force fullscreen is disabled in app bundle
2024-11-24 22:27:18 +08:00
TrueZhuangjia b5afe1fe29 onScreen touchPad control test 2024-11-24 21:30:23 +08:00
TrueZhuangjia 8b2470faf8 Merge branch 'noWaterMark' into DevDebug 2024-11-23 18:30:50 +08:00
TrueZhuangjia 7fcc3d0013 delete duplicated database entries in xcode project 2024-11-23 17:16:56 +08:00
TrueZhuangjia 153b6ce6fe annotations for onscreen controller stick move handling 2024-11-22 22:26:40 +08:00
TrueZhuangjia 777c825cca swift call obj-c test 2024-11-22 22:00:25 +08:00
TrueZhuangjia 33a8e02c5f update moonlight-common-c to 2024.10.21 2024-11-22 21:59:51 +08:00
TrueZhuangjia c797c9db1a restore runtime orientation limitation 2024-11-21 23:53:54 +08:00
TrueZhuangjia 766e30a066 Tips for screen orientation Change (non-FullScreen version) 2024-11-21 23:46:24 +08:00
TrueZhuangjia 3894d92f1b try to fix app crash issue triggered by manually adding host 2024-11-20 21:36:58 +08:00
TrueZhuangjia 2be2ffbcb6 change fullscreen/window resolution label for ipad 2024-11-20 21:02:00 +08:00
TrueZhuangjia 570dff2696 assign default settings for resolutionSelected & slide gesture screen edge 2024-11-20 12:53:28 +08:00
TrueZhuangjia be13295096 refactor app window resolution update to mainFrameView 2024-11-19 23:12:26 +08:00
TrueZhuanJia ea89d0a822 Merge pull request #37 from xzzpig/ExternalScreen
Add support to change app size
2024-11-19 21:02:28 +08:00
xzzpig d7bb9483e4 Update resolution table when mainView loaded 2024-11-19 14:56:33 +08:00
xzzpig 7699e47157 Modify the logic for saving resolution settings 2024-11-19 09:48:23 +08:00
xzzpig 978c50c271 Add support to change app size 2024-11-18 20:46:44 +08:00
TrueZhuanJia 0f5e644041 Merge pull request #26 from xzzpig/noWaterMark
修复某些特殊网络情况(网络掩码非24)时发送组播wol失败的问题 / Fix the issue of multicast WOL failure when sending in certain special network situations (such as network mask not being 24)
2024-11-08 21:11:24 +08:00
xzzpig c5e891eceb Fix the issue of multicast WOL failure when sending in certain special network situations (such as network mask not being 24) 2024-11-04 20:52:02 +08:00
TrueZhuangjia 147b2ce045 tweak watermark position 2024-11-04 20:06:23 +08:00
TrueZhuangjia 9553ec3d96 minor tweak on button down visual effect 2024-11-04 19:56:51 +08:00
TrueZhuangjia 890d3fbfe1 update waterMark 2024-11-04 19:45:36 +08:00
TrueZhuangjia b9ea8cc1a9 change button down effect for onScreenButtonView 2024-11-04 00:53:40 +08:00
TrueZhuangjia 141c9ee2ab change button down effect for onScreenButtonView 2024-11-03 23:31:17 +08:00
TrueZhuangjia f1811ca53d fix on-screen mouse middle button issue 2024-10-31 15:02:07 +08:00
TrueZhuangjia 5bf163f40c Merge branch 'DevDebug' into noWaterMark 2024-10-31 14:59:36 +08:00
TrueZhuangjia 395647cff9 代码注释 - 添加主机crash问题重点关注 2024-10-15 21:37:26 +08:00
TrueZhuangjia 3f3ffdca13 - fix disappearing on-screen sticks 2024-10-15 20:18:25 +08:00
TrueZhuangjia 34659cac70 tweak tap exclusion area size & its default value 2024-10-15 13:00:02 +08:00
TrueZhuangjia d8820df26e button size, height & alpha UISlider for iPhone storyboard 2024-10-15 12:42:06 +08:00
TrueZhuangjia 6495bad6fe button size, height & alpha labels with factors 2024-10-15 11:39:06 +08:00
TrueZhuangjia f918779fc8 adjustable on screen controller layer transparency 2024-10-15 10:48:53 +08:00
TrueZhuangjia 8999fd625d adjustable onScreenButtonView alpha(transparency) 2024-10-14 15:10:53 +08:00
TrueZhuangjia 113aaa1b81 button size & height slider labels 2024-10-13 23:33:22 +08:00
TrueZhuangjia ec29679c02 HD start & select buttons 2024-10-13 22:37:28 +08:00
TrueZhuangjia 962d19f3cb HD dPad buttons 2024-10-13 22:00:49 +08:00
TrueZhuangjia 3bec2d6e65 HD sticks 2024-10-13 19:14:47 +08:00
TrueZhuangjia 571812062f HD OSC round buttons 2024-10-13 17:27:28 +08:00
TrueZhuangjia c4801f9889 deal with invalid sizefactor 2024-10-13 09:15:25 +08:00
TrueZhuangjia ada2e6f480 resizable dPad buttons 2024-10-12 23:56:21 +08:00
TrueZhuangjia fadf023563 resizable on-screen sticks & single layer on-screen controller buttons 2024-10-12 19:32:58 +08:00
TrueZhuangjia 5db6aa0695 Merge branch 'noWaterMark' into buttonSizeTweak 2024-09-24 10:33:24 +08:00
TrueZhuangjia 1ca71bbc11 - Handles tap exclusion area for dPad 2024-09-24 10:32:11 +08:00
TrueZhuangjia 39b8429998 - on-screen button & on-screen controller resize test 2024-09-21 22:48:16 +08:00
TrueZhuangjia e05b626d51 9.0.263 build 63 2024-09-17 12:22:54 +08:00
TrueZhuangjia 146ca2353d - cancel mouseLeftButtonHeldDown flag 2024-09-17 11:11:27 +08:00
TrueZhuangjia 6d5c5fb0c2 - Fixed app crash caused by 2-finger mouse wheel event 2024-09-14 16:56:05 +08:00
TrueZhuangjia 3d5885f5bd - update UI string in settings 2024-09-14 16:20:54 +08:00
TrueZhuangjia 888ecad4d3 - implements upper screen edge touch detection for absolute touch mode 2024-09-14 16:03:43 +08:00
TrueZhuangjia b3a64fdf0f - minor fix 2024-09-14 14:14:43 +08:00
TrueZhuangjia dc8e361b6b - implements upper screen edge touch detection for relative touch(touchpad) mode 2024-09-14 14:02:04 +08:00
TrueZhuangjia 15bc488f71 - Completely refactor relative touch mode: implements quick double tap for dragging, simulates a real note book computer touchpad.
- Onscreen controllers, buttons & touchpad mouse able to work independently without blocking each other
- Tap exclusion area size upto 1000%
2024-09-14 12:29:31 +08:00
TrueZhuangjia 22b4cc6222 - add option to reverse physical mouse wheel direction
- add slider widget to adjust the size of tap exclusion area for on-screen buttons
2024-09-12 23:43:32 +08:00
TrueZhuangjia f5c03f8045 make on-screen buttons available for absolute touch 2024-09-12 15:20:55 +08:00
TrueZhuangjia 0a47becdc6 change default touch mode to native touch 2024-09-12 10:48:30 +08:00
TrueZhuangjia e4d264cc83 Change frame pacing zh-hans translation 2024-09-12 10:35:52 +08:00
True Zhuanjia 7c455b0daa 9.0.256 2024-09-04 14:19:10 +08:00
True Zhuanjia d2a1add105 remove invalid IP rule 2024-09-02 22:46:24 +08:00
True Zhuanjia dcdfbc7394 excludes on-screen controller & button taps for mouse scroller gesture in relative touch mode 2024-09-02 22:01:30 +08:00
True Zhuanjia 2b5a1c0551 Simplify border detection in native touch modes 2024-08-29 01:15:37 +08:00
True Zhuanjia cb034b96a4 Build 47: make bitrate stepping to 1Mbps under 70Mbps 2024-08-28 20:26:32 +08:00
True Zhuanjia e741f83c06 urgent fix: native touch pointer sensitivity tweak not working 2024-08-26 21:29:10 +08:00
True Zhuanjia b192360ef3 change watermark 2024-08-26 19:28:40 +08:00
True Zhuanjia a028191c82 make keyboard command delay 200ms 2024-08-24 23:46:04 +08:00
True Zhuanjia c6b27ad0e0 resolve conflicts between on-screen buttons & tap gestures 2024-08-24 23:29:18 +08:00
True Zhuanjia 1d1d4cbddf add mouse button function to commandManager & OnScreenButton 2024-08-24 16:48:17 +08:00
True Zhuanjia 747997f46c - Adjust stick style.
- Add + button for iphone
2024-08-23 21:41:29 +08:00
True Zhuanjia 805cf517bb adjust on screen keyboard button style 2024-08-22 23:20:50 +08:00
True Zhuanjia ea78b434d5 adjust UI 2024-08-22 22:38:53 +08:00
True Zhuanjia 69f5fbef31 - Resolving app crash which happens occasionally after slide from screen edge
- Slide from upper 40% of left & right screen edge to open in-stream menus will  no longer sends touch event to remote PC
2024-08-22 19:49:09 +08:00
True Zhuanjia db75cac9bc Build 31 touches spawned at the upper 40% screen edge (left or right) will not be sent to the remote PC 2024-08-20 23:16:26 +08:00
True Zhuanjia ba4af74a9d make on-screen L1/R1 & left & right stick larger by setting option 2024-08-20 21:18:06 +08:00
True Zhuanjia fb474237ee Build 29 oscPofileManViewController UI Improvement 2024-08-19 23:46:55 +08:00
True Zhuanjia d476f5d75b build 28, change on screen button visual effect color 2024-08-19 01:35:40 +08:00
True Zhuanjia 8720847f54 build 27, adjust font size for onscreen key label 2024-08-19 00:32:21 +08:00
True Zhuanjia a12bd80cc1 build 26, custom on screen keyboard localization 2024-08-18 23:50:32 +08:00
True Zhuanjia ab3c551357 implements custom on screen keyboard button 2024-08-18 23:29:00 +08:00
True Zhuanjia a1a7047370 make osdLayoutToolFingers configurable by settings menu 2024-08-17 21:29:44 +08:00
True Zhuanjia 29e43979a7 make OSC + native touch works 2024-08-17 18:59:54 +08:00
True Zhuanjia 6b25de1361 minor change 2024-08-17 01:18:34 +08:00
True Zhuanjia 53e68258f3 2nd time - completely resolving conflict between custom tap gesture & onscreen control taps 2024-08-16 21:45:50 +08:00
True Zhuanjia 6e3c982a22 Merge branch 'noWaterMark' into OSCDebug
# Conflicts:
#	Limelight/Input/StreamView.m
#	Limelight/ViewControllers/StreamFrameViewController.m
2024-08-15 22:29:55 +08:00
True Zhuanjia f0e812e9e2 iterate all customTapGestureRecognizer in OnScreenControls implementation to resolve conflict between tap gesture & osc touch 2024-08-15 21:55:01 +08:00
True Zhuanjia 9ff10135a8 - optimize _areVirtualControllerTap mechanism, fix onscreen game controller button stuck
- change keyboard toggle gesture finger number logic
2024-08-15 09:34:43 +08:00
True Zhuanjia 9618b7bb94 optimize _areVirtualControllerTap mechanism, fix onscreen game controller button stuck 2024-08-14 22:51:51 +08:00
True Zhuanjia fd768db70b onscreen keyboard profile test 2024-08-14 22:49:35 +08:00
True Zhuanjia 3c5b6ba795 udpate 2024-08-14 22:44:12 +08:00
True Zhuanjia add09210ba update 2024-08-11 21:59:21 +08:00
True Zhuanjia 6065b4f742 keyboard button profile test 2024-08-11 21:55:29 +08:00
True Zhuanjia d90bf747fc add OSC visual feedback & corresponding menu option.
adjust menu option layout for iPhone
2024-08-10 21:02:35 +08:00
True Zhuanjia ec8095ea9d fix Custom OSC abnormal touch (no response) after editing OSC profile 2024-08-10 11:52:24 +08:00
True Zhuanjia b11849e45b Merge branch 'onScreenKey test' into noWaterMark
# Conflicts:
#	Limelight/Input/ControllerSupport.m
2024-08-09 18:54:38 +08:00
True Zhuanjia e2f09d711d on screen key test 2024-08-09 18:51:12 +08:00
True Zhuanjia 04b68e3a61 test 2024-08-09 18:02:26 +08:00
True Zhuanjia b21b93ff71 cancel some restrictions for iOS 13.3 & lower 2024-08-09 11:40:41 +08:00
True Zhuanjia 9f9df09c61 test flight build (No.12) 2024-08-06 23:03:22 +08:00
True Zhuanjia c7700904e0 handle public ip 2024-08-03 20:57:32 +08:00
True Zhuanjia a8b45851cc rm waterMark 2024-08-03 18:43:33 +08:00
True Zhuanjia bf1a30505f try to fix invalid MAC issue that happens in some cases 2024-08-03 18:42:35 +08:00
True Zhuanjia 7a55fee258 fill sapces for all UI strings in settings view storyboard 2024-08-01 19:08:12 +08:00
True Zhuanjia abc7805aab add high bitrate warning to the settings view 2024-07-29 12:27:47 +08:00
True Zhuanjia 98db99b3ff minor improvement for command tool 2024-07-28 10:03:38 +08:00
True Zhuanjia 07d08918a3 improve entry selection after add / deleting entries in the command tool 2024-07-27 22:51:24 +08:00
True Zhuanjia 8371c9fedd fix unable to remove on-line paired host 2024-07-27 21:45:04 +08:00
True Zhuanjia b106e60880 add switch for attachWatermark 2024-07-27 12:45:08 +08:00
True Zhuanjia 81c04bc2b2 add watermark back to the host view in mainframe 2024-07-27 12:43:04 +08:00
True Zhuanjia 945a4c67e4 improve add / duplicate button 2024-07-26 22:24:28 +08:00
True Zhuanjia 95644075ec improve entry tapping flash effect for cmd tool tableview cells 2024-07-26 21:57:38 +08:00
True Zhuanjia 19adb5e106 update annotation 2024-07-26 21:44:49 +08:00
True Zhuanjia ff789200b9 add more default entries for cmd tool 2024-07-26 21:35:46 +08:00
True Zhuanjia b53b0784aa implement default entries for command tool. change add button to add/duplicate 2024-07-26 21:05:10 +08:00
True Zhuanjia 69056332d3 change command tool view constraint & transparency 2024-07-26 20:15:07 +08:00
True Zhuanjia f5342caca6 update placeholder for alias input field 2024-07-26 19:53:36 +08:00
True Zhuanjia 039bf549a9 refactor coding & settings menu for command sending tool 2024-07-26 00:19:39 +08:00
True Zhuanjia ea9e335a36 refactor Cancel button in osc layout tool to Exit button 2024-07-25 21:57:31 +08:00
True Zhuanjia 46e3f85903 localize send command view 2024-07-25 21:31:06 +08:00
True Zhuanjia b00d836c13 add pin button for the sending cmd view 2024-07-25 21:02:05 +08:00
True Zhuanjia b587279450 update 2024-07-25 01:07:17 +08:00
True Zhuanjia 279ef707d5 update 2024-07-25 01:06:52 +08:00
True Zhuanjia 7e8be94530 implement async keyboard event sending 2024-07-25 00:30:02 +08:00
True Zhuanjia c8262da5d7 sending keyboard cmd 2024-07-24 23:50:16 +08:00
True Zhuanjia 81beb35809 keyboard command test 2024-07-24 22:56:46 +08:00
True Zhuanjia eb302e5d19 correct CTRL & ALT keycode for toolbar 2024-07-24 22:15:36 +08:00
True Zhuanjia 9c54168173 cancel filling selected alias for the input field for new keyboard cmd entries 2024-07-24 20:23:30 +08:00
True Zhuanjia 3eb233ffe2 handling empty alias for keyboardCmdSring 2024-07-24 20:18:10 +08:00
True Zhuanjia cf21ca9503 conforming RemoteCommand class to NSSecureCoding 2024-07-24 20:09:16 +08:00
True Zhuanjia b6ecdcd0ea make key mapping static 2024-07-24 19:29:52 +08:00
True Zhuanjia a975b0e24a keep entry of send cmd manager selected 2024-07-24 19:20:54 +08:00
True Zhuanjia 84b198ed29 implement regular expression check 2024-07-24 18:56:41 +08:00
True Zhuanjia 4cff75c531 tweak view contrain 2024-07-24 13:02:13 +08:00
True Zhuanjia 916e9b65a6 implementation of alias for the send special command entries 2024-07-24 12:36:01 +08:00
True Zhuanjia eb6adea47c UI & Data persistence for Send Special Keys menu 2024-07-23 23:52:20 +08:00
True Zhuanjia 458daddf38 disable exitSwipeRecognizer for settings view 2024-07-22 12:49:26 +08:00
True Zhuanjia ea98fe1fbf minor changes 2024-07-21 10:39:18 +08:00
True Zhuanjia c3b7814542 update provisioning 2024-07-21 01:50:31 +08:00
True Zhuanjia abe414765e try to fix LAN host waking failure. 2024-07-21 01:14:17 +08:00
True Zhuanjia 9a93022478 update storyboard UI string for "allow portrait" setting 2024-07-20 20:04:03 +08:00
True Zhuanjia 033265d58e disallow orientation change in mainframe host view (need iOS16+) 2024-07-20 19:40:49 +08:00
True Zhuanjia 2bcdd25cc2 fix onscreen control not working after enabled from realtime game back menu, if the stream view is launched without onscreen control in the first time 2024-07-20 16:39:22 +08:00
True Zhuanjia c38dfc2c20 exclude bottom right corner for coord reset in high touch pointer velocity condition 2024-07-16 20:56:06 +08:00
True Zhuanjia ef5c77d8df fix touch interruption on the left side of screen under high pointer velocity (sensitivity) 2024-07-15 13:01:18 +08:00
True Zhuanjia b05c3c7e4a on iPhone, force close expanded settings view if target device orientation will be portrait. 2024-07-14 18:44:02 +08:00
True Zhuanjia 606ec51108 update NSLog for velocFactor 2024-07-14 18:24:14 +08:00
True Zhuanjia 3dbc97701d change pointer reset coordinate, change max pointer velocity 2024-07-14 18:19:16 +08:00
True Zhuanjia 31b3df08cc coord reset for extremely high pointer velocity, core implementation 2024-07-14 16:41:26 +08:00
True Zhuanjia 01718282ff single Host view refresh bug fix 2024-07-14 01:05:40 +08:00
True Zhuanjia 1b5f414c89 starRail pointer veloc tweak enchancement 2024-07-13 22:52:17 +08:00
True Zhuanjia a441670591 temporally enable allowPortrait selctor 2024-07-13 18:57:20 +08:00
True Zhuanjia 7c0f9b65f1 update annotation 2024-07-13 18:28:57 +08:00
True Zhuanjia 837a75c477 get isIPhonePortrait flag ready for release 2024-07-13 18:18:19 +08:00
True Zhuanjia 0a449c25eb Test: disable settings view in portrait mode for iphone (prevents buggy settings view), flag not ready for release 2024-07-13 17:51:19 +08:00
True Zhuanjia 88d5038f94 make allowportrait selector available on iOS16+ only 2024-07-13 14:32:50 +08:00
True Zhuanjia 2de3197ca9 runtime orientation limitation, tested on iOS17.2 (probably need iOS16+, not working with iOS14) 2024-07-13 13:55:10 +08:00
True Zhuanjia c72accb3cb scroll view update upon orientation change 2024-07-12 23:30:08 +08:00
True Zhuanjia 00d89f12e8 correct landscape host view well device sensor is portrait 2024-07-12 22:48:53 +08:00
True Zhuanjia 565e31983c landscape limitation test 2024-07-12 22:08:34 +08:00
True Zhuanjia 06aa772a02 orientation change test 2024-07-12 21:13:19 +08:00
True Zhuanjia a5b1cd6c99 revert to both rotatable 2024-07-12 20:33:55 +08:00
True Zhuanjia d84be68f4c udpate 2024-07-12 20:31:26 +08:00
True Zhuanjia e01b51bf37 back to landscape only 2024-07-12 00:12:38 +08:00
True Zhuanjia 9c6bf5c16f correct method name 2024-07-12 00:09:37 +08:00
True Zhuanjia 9bf4ba9af9 adjust widgets alignment 2024-07-12 00:00:22 +08:00
True Zhuanjia a37c7ed8f8 go back to stream view button for iphone 2024-07-11 23:50:32 +08:00
True Zhuanjia 36ba82dd16 go back to stream view button on ipad 2024-07-11 23:32:17 +08:00
True Zhuanjia 802f8714ef cancel immediate opening of osc layout tool in setting view during streaming 2024-07-11 03:13:49 +08:00
True Zhuanjia bf6cf21084 resolve absolute touch gesture 2024-07-11 02:54:36 +08:00
True Zhuanjia 30df8aec8a full auto screen resolution update in background 2024-07-11 01:34:16 +08:00
True Zhuanjia 84bffdd287 cancel auto osc mode 2024-07-11 01:14:24 +08:00
True Zhuanjia 6c4b5321e1 update iphone storyboard cn localization 2024-07-11 00:38:25 +08:00
True Zhuanjia 14ef07f072 fix gesture reconfig in real time streamview 2024-07-11 00:28:27 +08:00
True Zhuanjia 52c2a831d9 update statsOverlay localization 2024-07-10 02:22:25 +08:00
True Zhuanjia d427270699 localize statsOverlay 2024-07-09 23:39:17 +08:00
True Zhuanjia b25f2d0916 add statsOverlay realtime setting config 2024-07-09 23:12:04 +08:00
True Zhuanjia 56fc18c378 add exit button by storyboard, config widget disabling in streamview settingview 2024-07-09 22:42:01 +08:00
True Zhuanjia 38343c20cb get stream real time setting menu working 2024-07-08 23:59:42 +08:00
True Zhuanjia 6f1773d512 Real Time setting menu in streamView beta 2024-07-08 20:45:48 +08:00
True Zhuanjia d9fc1fc5c9 fix swipeToExitDistance loading problem 2024-07-08 19:57:51 +08:00
True Zhuanjia 3ad8b0db71 backmenutmp 2024-07-08 19:43:23 +08:00
True Zhuanjia a021784d48 replace force expand & close of settings view with background resolution width & height swap 2024-07-08 19:06:59 +08:00
True Zhuanjia 072f7bdffb force reload setting after quitting remote session 2024-07-07 13:38:11 +08:00
True Zhuanjia b8a3aa5d8c fix physical mouse wheel direction 2024-07-07 13:09:58 +08:00
True Zhuanjia ee91177ca6 fix OSCLayoutTool multi-reload bug 2024-07-07 12:34:59 +08:00
True Zhuanjia 3f02a76c3d update iphone storyboard 2024-07-07 12:34:04 +08:00
True Zhuanjia c88753c7e9 HybridNativeTouchMode 2024-07-07 12:19:03 +08:00
True Zhuanjia 94addc69f7 res auto load improvement 2024-07-07 03:48:53 +08:00
True Zhuanjia b4cfe21074 fix setting view won't expand after quitting remote session 2024-07-05 19:35:07 +08:00
True Zhuanjia 8437c8ada1 update default touchMode setting in database 2024-07-05 17:49:40 +08:00
True Zhuanjia 0a5109823c add resolution auto update for orientation change 2024-07-05 17:41:45 +08:00
True Zhuanjia e863a5df6a update annotation 2024-07-04 23:13:58 +08:00
True Zhuanjia 547eb1c85d auto res table update on orientation change 2024-07-04 23:04:45 +08:00
True Zhuanjia 63c853d4ce add portrait mode 2024-07-04 20:57:57 +08:00
True Zhuanjia 329c9b7b92 add touch mode label description 2024-07-04 20:47:21 +08:00
True Zhuanjia 0789a2f27e native only & native touch mode 2024-07-04 01:04:00 +08:00
True Zhuanjia 382116ab21 add waterMark 2024-07-03 20:50:00 +08:00
True Zhuanjia 7290bff594 display zh-hans for 2 zh-hant localizations 2024-07-03 12:59:14 +08:00
True Zhuanjia 8b57c448ef fix mini5 menu widget visibility update problem.
update Chinese Localizatioin
2024-07-02 21:55:51 +08:00
True Zhuanjia e5cf54e92f update localization 2024-07-01 18:06:08 +08:00
True Zhuanjia 34d9875ff3 host discovery Chinese localization. Cancel LAN restriction 2024-06-30 23:46:21 +08:00
True Zhuanjia 070fbc8b52 Chinese Localization 2024-06-30 22:26:52 +08:00
True Zhuanjia 9d57c22757 Fix weird setting & select new host disabling after closing OSC layout tool 2024-06-29 20:21:45 +08:00
True Zhuanjia 93f1a2b4fb prevent default layout from being changed, fix fake layout change bug 2024-06-29 09:27:24 +08:00
True Zhuanjia a156962259 current profile UILabel 2024-06-28 19:32:15 +08:00
True Zhuanjia 9d8fbf789f iphone OSC profile manager transparency improvements 2024-06-28 14:22:35 +08:00
True Zhuanjia 0d91be217b iphone transparency + exit icon 2024-06-28 12:56:19 +08:00
True Zhuanjia 91a8fdc392 OSCLayoutTool Improvements 2024-06-28 10:27:06 +08:00
True Zhuanjia b79a762ce1 enhanced CustomOSC, allow users to rebase OSC in stream view 2024-06-26 03:16:41 +08:00
True Zhuanjia 46770f4588 Merge branch 'onScreenControlStuckFix' into CustomOSC-Integration 2024-06-25 02:11:12 +08:00
True Zhuanjia aae87d1684 fix onscreenControl Stuck 2024-06-25 02:08:43 +08:00
True Zhuanjia 0bac5f1ae9 CustomOSC first integration 2024-06-24 23:15:55 +08:00
True Zhuanjia 457a4e8725 Merge commit 'CustomOSCRebase' into CustomOSC-Integration
# Conflicts:
#	Limelight/ViewControllers/SettingsViewController.h
#	Limelight/ViewControllers/SettingsViewController.m
#	Limelight/iPad.storyboard
#	Limelight/iPhone.storyboard
2024-06-24 18:55:20 +08:00
True Zhuanjia a5c8224023 add mouse pointer velocity tweak for trackpad (relative) mode 2024-06-23 21:31:18 +08:00
True Zhuanjia 001deeee66 update annotation 2024-06-21 20:16:23 +08:00
True Zhuanjia 7847299c77 minior change 2024-06-21 01:04:50 +08:00
True Zhuanjia ac77447066 minor change 2024-06-20 23:32:48 +08:00
True Zhuanjia f09c7be18d NativeTouchHandler improvement, general coding improvements. 2024-06-20 22:52:38 +08:00
True Zhuanjia ee4eb35fe3 improved coding for native touch 2024-06-20 11:51:46 +08:00
True Zhuanjia 8834f492b4 tweak RIGHTCLICK_TAP_DOWN_TIME_THRESHOLD_S for relative touch 2024-06-18 13:03:01 +08:00
True Zhuanjia f2cf788d86 minor change 2024-06-18 02:25:08 +08:00
True Zhuanjia c3c7e670fc resolve gesture competition in non-native touch modes 2024-06-18 02:12:35 +08:00
True Zhuanjia c3d44647f9 CustomTapGestureRecognizer improvement 2024-06-17 22:56:55 +08:00
True Zhuanjia 115d540b7d UI string improvement 2024-06-17 22:19:58 +08:00
True Zhuanjia ec4299fd88 Right click gesture & scroll gesture improvement in relative touch (trackpad) mode;
CustomTapGestureRecognizer improvement.
2024-06-17 21:45:11 +08:00
True Zhuanjia c6785e3728 minor improvement 2024-06-17 01:10:15 +08:00
True Zhuanjia b2e4d45025 minor change 2024-06-17 00:33:26 +08:00
True Zhuanjia 4170376945 del unnecessary files 2024-06-17 00:26:29 +08:00
True Zhuanjia 01d2a937ca update annotation 2024-06-17 00:20:39 +08:00
True Zhuanjia 4e3be8513d recover absolute touch related code. StreamFrameViewController gesture recovered 2024-06-17 00:03:46 +08:00
True Zhuanjia c8f0d8bf0b recover absolute touch related codes 2024-06-16 23:42:07 +08:00
True Zhuanjia bfe02ed6a3 minior change 2024-06-16 22:56:53 +08:00
True Zhuanjia 3442df0ca4 remove deprecated codes in StreamView.m 2024-06-16 22:15:58 +08:00
True Zhuanjia b85a1e9c07 minor change 2024-06-16 21:01:28 +08:00
True Zhuanjia e7fd911fb8 Refactor NativeTouchHandler, restore absolute touch, Bug fixes 2024-06-16 15:48:55 +08:00
True Zhuanjia d673c4900c more & higher bit rate options. 2024-06-15 21:19:46 +08:00
True Zhuanjia 49fb6dbbd6 pointerVelocityMD_defaultChange 2024-06-14 19:08:26 +08:00
True Zhuanjia 5c88787401 fix null relative coordinate when pointerVelocityfactor is 100% 2024-06-12 09:16:17 +08:00
True Zhuanjia 873269e410 Native touch oriented improvements 2024-06-01 16:37:04 +08:00
Long Le 6740a7130b includes on screen controls 2023-06-27 14:13:05 -07:00
599 changed files with 151409 additions and 2013 deletions
+45
View File
@@ -0,0 +1,45 @@
name: Build IPAs
on:
push:
branches: [ "noWaterMark" ]
jobs:
build:
name: Build IPAs
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Checkout submodules
run: "git submodule update --init --recursive"
- name: Build FullScreen Version
run: |
plutil -replace UIRequiresFullScreen -bool true Limelight/Limelight-Info.plist
xcodebuild -verbose -scheme Moonlight-ZWM -sdk iphoneos -configuration AppStoreDistribution archive -archivePath $PWD/build/MoonlightFullScreen.xcarchive CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED="NO" CODE_SIGN_ENTITLEMENTS="" CODE_SIGNING_ALLOWED="NO"
mkdir -p ./build/IPA_FullScreen && mkdir -p ./build/IPA_FullScreen/Payload
cp -r ./build/MoonlightFullScreen.xcarchive/Products/Applications/Moonlight-ZWM.app ./build/IPA_FullScreen/Payload/Moonlight-ZWM.app
cd ./build/IPA_FullScreen && zip -r ../../build/Moonlight-ZWM_FullScreen.ipa ./Payload/* && cd ../..
- name: Upload FullScreen IPA to Artifacts
uses: actions/upload-artifact@v4
with:
name: Moonlight-ZWM_FullScreen.ipa
path: build/Moonlight-ZWM_FullScreen.ipa
- name: Build StageManager Version
run: |
plutil -replace UIRequiresFullScreen -bool false Limelight/Limelight-Info.plist
xcodebuild -verbose -scheme Moonlight-ZWM -sdk iphoneos -configuration AppStoreDistribution archive -archivePath $PWD/build/MoonlightStageManager.xcarchive CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED="NO" CODE_SIGN_ENTITLEMENTS="" CODE_SIGNING_ALLOWED="NO"
mkdir -p ./build/IPA_StageManager && mkdir -p ./build/IPA_StageManager/Payload
cp -r ./build/MoonlightStageManager.xcarchive/Products/Applications/Moonlight-ZWM.app ./build/IPA_StageManager/Payload/Moonlight-ZWM.app
cd ./build/IPA_StageManager && zip -r ../../build/Moonlight-ZWM_StageManager.ipa ./Payload/* && cd ../..
- name: Upload StageManager IPA to Artifacts
uses: actions/upload-artifact@v4
with:
name: Moonlight-ZWM_StageManager.ipa
path: build/Moonlight-ZWM_StageManager.ipa
+1 -1
View File
@@ -1,6 +1,6 @@
**/xcuserdata/
**/Moonlight.xcscmblueprint
**/xcschemes/*.xcscheme
.DS_Store
Build
DerivedData
*.xccheckout
+25 -6
View File
@@ -7,6 +7,8 @@
//
#import "AppDelegate.h"
#import "MainFrameViewController.h"
#import "Moonlight-Swift.h"
@implementation AppDelegate
@@ -22,17 +24,34 @@ static NSString* DB_NAME = @"Moonlight_tvOS.bin";
static NSString* DB_NAME = @"Limelight_iOS.sqlite";
#endif
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
#if !TARGET_OS_TV
UIApplicationShortcutItem* shortcut = [launchOptions valueForKey:UIApplicationLaunchOptionsShortcutItemKey];
if (shortcut != nil) {
_pcUuidToLoad = (NSString*)[shortcut.userInfo objectForKey:@"UUID"];
/*
// orietation limitatioin test
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
UIViewController *topController = window.rootViewController;
while (topController.presentedViewController) {
topController = topController.presentedViewController;
}
#endif
return [topController supportedInterfaceOrientations];
}
*/
/*
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
NSLog(@"orientation limit");
return UIInterfaceOrientationMaskLandscape;
}
*/
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for command tool customization after application launch (works only when user default is nil)
[CommandManager presetDefaultCommands];
return YES;
}
#if !TARGET_OS_TV
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL succeeded))completionHandler {
_pcUuidToLoad = (NSString*)[shortcutItem.userInfo objectForKey:@"UUID"];
_shortcutCompletionHandler = completionHandler;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-2
View File
@@ -96,7 +96,6 @@ static NSData* p12 = nil;
bio = BIO_new(BIO_s_mem());
i2d_X509_bio(bio, x509);
X509_free(x509);
BUF_MEM* mem;
BIO_get_mem_ptr(bio, &mem);
@@ -223,7 +222,6 @@ static NSData* p12 = nil;
+ (NSData *)getSignatureFromCert:(NSData *)cert {
BIO* bio = BIO_new_mem_buf([cert bytes], (int)[cert length]);
X509* x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
BIO_free(bio);
if (!x509) {
Log(LOG_E, @"Unable to parse certificate in memory!");
+9 -19
View File
@@ -4,12 +4,10 @@
#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <OpenSSL/provider.h>
#include <OpenSSL/rsa.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <OpenSSL/rand.h>
#include <openssl/rand.h>
static const int NUM_BITS = 2048;
static const int SERIAL = 0;
@@ -63,30 +61,22 @@ void mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) {
}
struct CertKeyPair generateCertKeyPair(void) {
BIO *bio_err;
X509 *x509 = NULL;
EVP_PKEY *pkey = NULL;
PKCS12 *p12 = NULL;
bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
mkcert(&x509, &pkey, NUM_BITS, SERIAL, NUM_YEARS);
char* pass = "limelight";
p12 = PKCS12_create(pass,
"GameStream",
pkey,
x509,
NULL,
NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
-1, // disable certificate encryption
2048,
-1, // disable the automatic MAC
0);
// MAC it ourselves with SHA1 since iOS refuses to load anything else.
PKCS12_set_mac(p12, pass, -1, NULL, 0, 1, EVP_sha1());
p12 = PKCS12_create("limelight", "GameStream", pkey, x509, NULL, 0, 0, 0, 0, 0);
if (p12 == NULL) {
printf("Error generating a valid PKCS12 certificate.\n");
}
BIO_free(bio_err);
return (CertKeyPair){x509, pkey, p12};
}
+1
View File
@@ -9,6 +9,7 @@
#ifndef Limelight_mkcert_h
#define Limelight_mkcert_h
#include <sys/_types/_time_t.h>
#include <openssl/x509v3.h>
#include <openssl/pkcs12.h>
+24 -4
View File
@@ -17,25 +17,45 @@
framerate:(NSInteger)framerate
height:(NSInteger)height
width:(NSInteger)width
audioConfig:(NSInteger)audioConfig
audioConfig:(NSInteger)audioConfig
onscreenControls:(NSInteger)onscreenControls
keyboardToggleFingers:(NSInteger)keyboardToggleFingers
oscLayoutToolFingers:(NSInteger)oscLayoutToolFingers
slideToSettingsScreenEdge:(NSInteger)slideToSettingsScreenEdge
slideToSettingsDistance:(CGFloat)slideToSettingsDistance
pointerVelocityModeDivider:(CGFloat)pointerVelocityModeDivider
touchPointerVelocityFactor:(CGFloat)touchPointerVelocityFactor
mousePointerVelocityFactor:(CGFloat)mousePointerVelocityFactor
oscTapExlusionAreaSize:(CGFloat)oscTapExlusionAreaSize
reverseMouseWheelDirection:(BOOL)reverseMouseWheelDirection
largerStickLR1:(BOOL)largerStickLR1
liftStreamViewForKeyboard:(BOOL)liftStreamViewForKeyboard
showKeyboardToolbar:(BOOL)showKeyboardToolbar
optimizeGames:(BOOL)optimizeGames
multiController:(BOOL)multiController
swapABXYButtons:(BOOL)swapABXYButtons
audioOnPC:(BOOL)audioOnPC
preferredCodec:(uint32_t)preferredCodec
enableYUV444:(BOOL)enableYUV444
useFramePacing:(BOOL)useFramePacing
enableHdr:(BOOL)enableHdr
btMouseSupport:(BOOL)btMouseSupport
absoluteTouchMode:(BOOL)absoluteTouchMode
statsOverlay:(BOOL)statsOverlay;
// absoluteTouchMode:(BOOL)absoluteTouchMode
touchMode:(NSInteger)touchMode
statsOverlayLevel:(NSInteger)statsOverlayLevel
statsOverlayEnabled:(BOOL)statsOverlayEnabled
unlockDisplayOrientation:(BOOL)unlockDisplayOrientation
resolutionSelected:(NSInteger)resolutionSelected
externalDisplayMode:(NSInteger)externalDisplayMode
localMousePointerMode:(NSInteger)localMousePointerMode;
- (NSArray*) getHosts;
- (void) updateHost:(TemporaryHost*)host;
- (void) updateAppsForExistingHost:(TemporaryHost *)host;
- (void) removeHost:(TemporaryHost*)host;
- (void) removeApp:(TemporaryApp*)app;
- (Settings*) retrieveSettings;
- (void) saveData;
- (TemporarySettings*) getSettings;
- (void) updateUniqueId:(NSString*)uniqueId;
+42 -5
View File
@@ -58,16 +58,35 @@
width:(NSInteger)width
audioConfig:(NSInteger)audioConfig
onscreenControls:(NSInteger)onscreenControls
keyboardToggleFingers:(NSInteger)keyboardToggleFingers
oscLayoutToolFingers:(NSInteger)oscLayoutToolFingers
slideToSettingsScreenEdge:(NSInteger)slideToSettingsScreenEdge
slideToSettingsDistance:(CGFloat)slideToSettingsDistance
pointerVelocityModeDivider:(CGFloat)pointerVelocityModeDivider
touchPointerVelocityFactor:(CGFloat)touchPointerVelocityFactor
mousePointerVelocityFactor:(CGFloat)mousePointerVelocityFactor
oscTapExlusionAreaSize:(CGFloat)oscTapExlusionAreaSize
reverseMouseWheelDirection:(BOOL)reverseMouseWheelDirection
largerStickLR1:(BOOL)largerStickLR1
liftStreamViewForKeyboard:(BOOL)liftStreamViewForKeyboard
showKeyboardToolbar:(BOOL)showKeyboardToolbar
optimizeGames:(BOOL)optimizeGames
multiController:(BOOL)multiController
swapABXYButtons:(BOOL)swapABXYButtons
audioOnPC:(BOOL)audioOnPC
preferredCodec:(uint32_t)preferredCodec
enableYUV444:(BOOL)enableYUV444
useFramePacing:(BOOL)useFramePacing
enableHdr:(BOOL)enableHdr
btMouseSupport:(BOOL)btMouseSupport
absoluteTouchMode:(BOOL)absoluteTouchMode
statsOverlay:(BOOL)statsOverlay {
// absoluteTouchMode:(BOOL)absoluteTouchMode
touchMode:(NSInteger)touchMode
statsOverlayLevel:(NSInteger)statsOverlayLevel
statsOverlayEnabled:(BOOL)statsOverlayEnabled
unlockDisplayOrientation:(BOOL)unlockDisplayOrientation
resolutionSelected:(NSInteger)resolutionSelected
externalDisplayMode:(NSInteger)externalDisplayMode
localMousePointerMode:(NSInteger)localMousePointerMode {
[_managedObjectContext performBlockAndWait:^{
Settings* settingsToSave = [self retrieveSettings];
@@ -77,17 +96,35 @@
settingsToSave.width = [NSNumber numberWithInteger:width];
settingsToSave.audioConfig = [NSNumber numberWithInteger:audioConfig];
settingsToSave.onscreenControls = [NSNumber numberWithInteger:onscreenControls];
settingsToSave.keyboardToggleFingers = [NSNumber numberWithInteger:(uint16_t)keyboardToggleFingers];
settingsToSave.oscLayoutToolFingers = [NSNumber numberWithInteger:(uint16_t)oscLayoutToolFingers];
settingsToSave.slideToSettingsScreenEdge = [NSNumber numberWithInteger:(uint32_t)slideToSettingsScreenEdge];
settingsToSave.slideToSettingsDistance = [NSNumber numberWithFloat:slideToSettingsDistance];
settingsToSave.pointerVelocityModeDivider = [NSNumber numberWithFloat:pointerVelocityModeDivider];
settingsToSave.touchPointerVelocityFactor = [NSNumber numberWithFloat:touchPointerVelocityFactor];
settingsToSave.mousePointerVelocityFactor = [NSNumber numberWithFloat:mousePointerVelocityFactor];
settingsToSave.oscTapExlusionAreaSize = [NSNumber numberWithFloat:oscTapExlusionAreaSize];
settingsToSave.reverseMouseWheelDirection = reverseMouseWheelDirection;
settingsToSave.largerStickLR1 = largerStickLR1;
settingsToSave.liftStreamViewForKeyboard = liftStreamViewForKeyboard;
settingsToSave.showKeyboardToolbar = showKeyboardToolbar;
settingsToSave.optimizeGames = optimizeGames;
settingsToSave.multiController = multiController;
settingsToSave.swapABXYButtons = swapABXYButtons;
settingsToSave.playAudioOnPC = audioOnPC;
settingsToSave.preferredCodec = preferredCodec;
settingsToSave.enableYUV444 = enableYUV444;
settingsToSave.useFramePacing = useFramePacing;
settingsToSave.enableHdr = enableHdr;
settingsToSave.btMouseSupport = btMouseSupport;
settingsToSave.absoluteTouchMode = absoluteTouchMode;
settingsToSave.statsOverlay = statsOverlay;
// settingsToSave.absoluteTouchMode = absoluteTouchMode;
settingsToSave.touchMode = [NSNumber numberWithInteger:(uint16_t)touchMode];
settingsToSave.statsOverlayEnabled = statsOverlayEnabled;
settingsToSave.statsOverlayLevel = [NSNumber numberWithInteger:(uint16_t)statsOverlayLevel];
settingsToSave.unlockDisplayOrientation = unlockDisplayOrientation;
settingsToSave.resolutionSelected = [NSNumber numberWithInteger:resolutionSelected];
settingsToSave.externalDisplayMode = [NSNumber numberWithInteger:externalDisplayMode];
settingsToSave.localMousePointerMode = [NSNumber numberWithInteger:localMousePointerMode];
[self saveData];
}];
}
+4 -4
View File
@@ -28,7 +28,7 @@
self.externalAddress = host.externalAddress;
self.localAddress = host.localAddress;
self.ipv6Address = host.ipv6Address;
self.mac = host.mac;
if(!(host.mac == nil || [host.mac isEqualToString:@"00:00:00:00:00:00"])) self.mac = host.mac; // try to fix invalid mac happens in some cases
self.name = host.name;
self.uuid = host.uuid;
self.serverCodecModeSupport = host.serverCodecModeSupport;
@@ -70,9 +70,9 @@
if (self.ipv6Address != nil) {
parentHost.ipv6Address = self.ipv6Address;
}
if (self.mac != nil) {
parentHost.mac = self.mac;
}
// try to fix invalid mac happens in some cases
// 添加主机crash问题重点关注
if (!(self.mac == nil || [self.mac isEqualToString:@"00:00:00:00:00:00"])) parentHost.mac = self.mac;
if (self.serverCert != nil) {
parentHost.serverCert = self.serverCert;
}
+27 -2
View File
@@ -7,6 +7,12 @@
//
#import "Settings+CoreDataClass.h"
#define RELATIVE_TOUCH 0
#define REGULAR_NATIVE_TOUCH 1
#define PURE_NATIVE_TOUCH 2
#define ABSOLUTE_TOUCH 3
#define OSC_TOOL_FINGERS 4
@interface TemporarySettings : NSObject
@@ -18,13 +24,27 @@
@property (nonatomic, retain) NSNumber * width;
@property (nonatomic, retain) NSNumber * audioConfig;
@property (nonatomic, retain) NSNumber * onscreenControls;
@property (nonatomic, retain) NSNumber * keyboardToggleFingers;
@property (nonatomic, retain) NSNumber * oscLayoutToolFingers;
@property (nonatomic, retain) NSNumber * slideToSettingsScreenEdge;
@property (nonatomic, retain) NSNumber * slideToSettingsDistance;
@property (nonatomic, retain) NSNumber * oscTapExlusionAreaSize;
@property (nonatomic, retain) NSNumber * touchPointerVelocityFactor;
@property (nonatomic, retain) NSNumber * mousePointerVelocityFactor;
@property (nonatomic, retain) NSNumber * pointerVelocityModeDivider;
@property (nonatomic, retain) NSString * uniqueId;
@property (nonatomic, retain) NSNumber * resolutionSelected;
@property (nonatomic, retain) NSNumber * externalDisplayMode;
@property (nonatomic, retain) NSNumber * localMousePointerMode;
@property (nonatomic) enum {
CODEC_PREF_AUTO,
CODEC_PREF_H264,
CODEC_PREF_HEVC,
CODEC_PREF_AV1,
} preferredCodec;
@property (nonatomic) BOOL enableYUV444;
@property (nonatomic) BOOL reverseMouseWheelDirection;
@property (nonatomic) BOOL largerStickLR1;
@property (nonatomic) BOOL useFramePacing;
@property (nonatomic) BOOL multiController;
@property (nonatomic) BOOL swapABXYButtons;
@@ -32,8 +52,13 @@
@property (nonatomic) BOOL optimizeGames;
@property (nonatomic) BOOL enableHdr;
@property (nonatomic) BOOL btMouseSupport;
@property (nonatomic) BOOL absoluteTouchMode;
@property (nonatomic) BOOL statsOverlay;
// @property (nonatomic) BOOL absoluteTouchMode;
@property (nonatomic, retain) NSNumber * touchMode;
@property (nonatomic, retain) NSNumber * statsOverlayLevel;
@property (nonatomic) BOOL statsOverlayEnabled;
@property (nonatomic) BOOL liftStreamViewForKeyboard;
@property (nonatomic) BOOL showKeyboardToolbar;
@property (nonatomic) BOOL unlockDisplayOrientation;
- (id) initFromSettings:(Settings*)settings;
+22 -2
View File
@@ -37,6 +37,7 @@
self.audioConfig = [NSNumber numberWithInteger:[[NSUserDefaults standardUserDefaults] integerForKey:@"audioConfig"]];
assert([self.audioConfig intValue] != 0);
self.preferredCodec = (typeof(self.preferredCodec))[[NSUserDefaults standardUserDefaults] integerForKey:@"preferredCodec"];
self.enableYUV444 = [[NSUserDefaults standardUserDefaults] boolForKey:@"enableYUV444"];
self.useFramePacing = [[NSUserDefaults standardUserDefaults] integerForKey:@"useFramePacing"] != 0;
self.playAudioOnPC = [[NSUserDefaults standardUserDefaults] boolForKey:@"audioOnPC"];
self.enableHdr = [[NSUserDefaults standardUserDefaults] boolForKey:@"enableHdr"];
@@ -75,6 +76,7 @@
self.width = settings.width;
self.audioConfig = settings.audioConfig;
self.preferredCodec = settings.preferredCodec;
self.enableYUV444 = settings.enableYUV444;
self.useFramePacing = settings.useFramePacing;
self.playAudioOnPC = settings.playAudioOnPC;
self.enableHdr = settings.enableHdr;
@@ -82,9 +84,27 @@
self.multiController = settings.multiController;
self.swapABXYButtons = settings.swapABXYButtons;
self.onscreenControls = settings.onscreenControls;
self.reverseMouseWheelDirection = settings.reverseMouseWheelDirection;
self.largerStickLR1 = settings.largerStickLR1;
self.btMouseSupport = settings.btMouseSupport;
self.absoluteTouchMode = settings.absoluteTouchMode;
self.statsOverlay = settings.statsOverlay;
// self.absoluteTouchMode = settings.absoluteTouchMode;
self.touchMode = settings.touchMode;
self.statsOverlayLevel = settings.statsOverlayLevel;
self.statsOverlayEnabled = settings.statsOverlayEnabled;
self.keyboardToggleFingers = settings.keyboardToggleFingers;
self.oscLayoutToolFingers = settings.oscLayoutToolFingers;
self.slideToSettingsScreenEdge = settings.slideToSettingsScreenEdge;
self.slideToSettingsDistance = settings.slideToSettingsDistance;
self.liftStreamViewForKeyboard = settings.liftStreamViewForKeyboard;
self.showKeyboardToolbar = settings.showKeyboardToolbar;
self.oscTapExlusionAreaSize = settings.oscTapExlusionAreaSize;
self.touchPointerVelocityFactor = settings.touchPointerVelocityFactor;
self.mousePointerVelocityFactor = settings.mousePointerVelocityFactor;
self.pointerVelocityModeDivider = settings.pointerVelocityModeDivider;
self.unlockDisplayOrientation = settings.unlockDisplayOrientation;
self.resolutionSelected = settings.resolutionSelected;
self.externalDisplayMode = settings.externalDisplayMode;
self.localMousePointerMode = settings.localMousePointerMode;
#endif
self.uniqueId = settings.uniqueId;
@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "a_button_HD.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

@@ -0,0 +1,25 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "arrow_uturn_backward_square-80x80@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "arrow_uturn_backward_square-120x120@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

@@ -0,0 +1,25 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "arrow_uturn_backward_square_fill-80x80@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "arrow_uturn_backward_square_fill-120x120.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "b_button_HD.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

@@ -0,0 +1,25 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "chevron_compact_down@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "chevron_compact_down@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 848 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,25 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "chevron_compact_up@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "chevron_compact_up@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 833 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "DownButtonHD.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

+21
View File
@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "l1_button_HD.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

+21
View File
@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "l2_button_HD.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "LeftButtonHD.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "OSCLayoutExit_80x80@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "OSCLayoutExit_120x120@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 B

+21
View File
@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "r1_button_HD.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

+21
View File
@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "r2_button_HD.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "RightButtonHD.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "SelectButtonHD.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -0,0 +1,25 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "square_and_arrow_down-80x80@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "square_and_arrow_down-120x120@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

@@ -0,0 +1,25 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "square_and_arrow_up_on_square-80x80@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "square_and_arrow_up_on_square-120x120@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 950 B

@@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "stick_inner_48x49@1x.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "stick_inner_96x97@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x",
"filename" : "stick_inner_144x146@3x.png"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "StartButtonHD.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "StickInnerHD.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "StickOuterHD.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

+25
View File
@@ -0,0 +1,25 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "trash_square-80x80@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "trash_square-120x120@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "trash_square_fill-80x80@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "trash_square_fill-120x120@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "UpButtonHD.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "x_button_HD.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "y_button_HD.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

+40 -11
View File
@@ -23,18 +23,32 @@
#define DOUBLE_TAP_DEAD_ZONE_DELTA 0.025f
@implementation AbsoluteTouchHandler {
StreamView* view;
StreamView* streamView;
NSTimer* longPressTimer;
UITouch* lastTouchDown;
CGPoint lastTouchDownLocation;
UITouch* lastTouchUp;
CGPoint lastTouchUpLocation;
// upper screen edge check
bool touchPointSpawnedAtUpperScreenEdge;
CGFloat slideGestureVerticalThreshold;
CGFloat screenWidthWithThreshold;
CGFloat EDGE_TOLERANCE;
}
- (id)initWithView:(StreamView*)view {
self = [self init];
self->view = view;
self->streamView = view;
// upper screen check
EDGE_TOLERANCE = 15.0;
slideGestureVerticalThreshold = CGRectGetHeight([[UIScreen mainScreen] bounds]) * 0.4;
screenWidthWithThreshold = CGRectGetWidth([[UIScreen mainScreen] bounds]) - EDGE_TOLERANCE;
self->touchPointSpawnedAtUpperScreenEdge = false;
return self;
}
@@ -45,19 +59,28 @@
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint initialPoint = [[touches anyObject] locationInView:streamView];
if(initialPoint.y < slideGestureVerticalThreshold && (initialPoint.x < EDGE_TOLERANCE || initialPoint.x > screenWidthWithThreshold)) {
self->touchPointSpawnedAtUpperScreenEdge = true;
return; // we're done here. this touch event will not be sent to the remote PC.
}
touchPointSpawnedAtUpperScreenEdge = false; // reset this flag immediately if we get a touch event passing the check above, this fixes irresponsive touch after closing the command tool menu.
// Ignore touch down events with more than one finger
if ([[event allTouches] count] > 1) {
return;
}
UITouch* touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView:view];
CGPoint touchLocation = [touch locationInView:streamView];
// Don't reposition for finger down events within the deadzone. This makes double-clicking easier.
if (touch.timestamp - lastTouchUp.timestamp > DOUBLE_TAP_DEAD_ZONE_DELAY ||
sqrt(pow((touchLocation.x / view.bounds.size.width) - (lastTouchUpLocation.x / view.bounds.size.width), 2) +
pow((touchLocation.y / view.bounds.size.height) - (lastTouchUpLocation.y / view.bounds.size.height), 2)) > DOUBLE_TAP_DEAD_ZONE_DELTA) {
[view updateCursorLocation:touchLocation isMouse:NO];
sqrt(pow((touchLocation.x / streamView.bounds.size.width) - (lastTouchUpLocation.x / streamView.bounds.size.width), 2) +
pow((touchLocation.y / streamView.bounds.size.height) - (lastTouchUpLocation.y / streamView.bounds.size.height), 2)) > DOUBLE_TAP_DEAD_ZONE_DELTA) {
[streamView updateCursorLocation:touchLocation isMouse:NO];
}
// Press the left button down
@@ -75,25 +98,31 @@
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if(touchPointSpawnedAtUpperScreenEdge) return; // we're done here. this touch event will not be sent to the remote PC.
// Ignore touch move events with more than one finger
if ([[event allTouches] count] > 1) {
return;
}
UITouch* touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView:view];
CGPoint touchLocation = [touch locationInView:streamView];
if (sqrt(pow((touchLocation.x / view.bounds.size.width) - (lastTouchDownLocation.x / view.bounds.size.width), 2) +
pow((touchLocation.y / view.bounds.size.height) - (lastTouchDownLocation.y / view.bounds.size.height), 2)) > LONG_PRESS_ACTIVATION_DELTA) {
if (sqrt(pow((touchLocation.x / streamView.bounds.size.width) - (lastTouchDownLocation.x / streamView.bounds.size.width), 2) +
pow((touchLocation.y / streamView.bounds.size.height) - (lastTouchDownLocation.y / streamView.bounds.size.height), 2)) > LONG_PRESS_ACTIVATION_DELTA) {
// Moved too far since touch down. Cancel the long press timer.
[longPressTimer invalidate];
longPressTimer = nil;
}
[view updateCursorLocation:[[touches anyObject] locationInView:view] isMouse:NO];
[streamView updateCursorLocation:[[touches anyObject] locationInView:streamView] isMouse:NO];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if(touchPointSpawnedAtUpperScreenEdge) return; // we're done here. this touch event will not be sent to the remote PC.
// Only fire this logic if all touches have ended
if ([[event allTouches] count] == [touches count]) {
// Cancel the long press timer
@@ -108,7 +137,7 @@
// Remember this last touch for touch-down deadzoning
lastTouchUp = [touches anyObject];
lastTouchUpLocation = [lastTouchUp locationInView:view];
lastTouchUpLocation = [lastTouchUp locationInView:streamView];
}
}
+492
View File
@@ -0,0 +1,492 @@
//
// KeyManager.swift
// Moonlight-ZWM
//
// Created by ZWM on 2024/7/23.
// Copyright © 2024 Moonlight Game Streaming Project. All rights reserved.
//
import Foundation
import UIKit
// Define the RemoteCommand class
@objc public class RemoteCommand: NSObject, NSSecureCoding {
// MARK: - NSSecureCoding
public static var supportsSecureCoding: Bool {
return true
}
// MARK: - Properties
@objc var keyboardCmdString: String
@objc var alias: String
// MARK: - Initialization
init(keyboardCmdString: String, alias: String) {
self.keyboardCmdString = keyboardCmdString
self.alias = alias
}
// MARK: - NSSecureCoding
required public init?(coder: NSCoder) {
guard let keyboardCmdString = coder.decodeObject(of: NSString.self, forKey: "keyboardCmdString") as String?,
let alias = coder.decodeObject(of: NSString.self, forKey: "alias") as String? else {
return nil
}
self.keyboardCmdString = keyboardCmdString
self.alias = alias
}
public func encode(with coder: NSCoder) {
coder.encode(keyboardCmdString, forKey: "keyboardCmdString")
coder.encode(alias, forKey: "alias")
}
}
// Define the CommandManager class
@objc public class CommandManager: NSObject {
@objc public static let shared = CommandManager()
@objc public static let mouseButtonMappings: [String: Int32] = ["M_LEFT" : BUTTON_LEFT,
"M_MIDDLE" : BUTTON_MIDDLE,
"M_RIGHT" : BUTTON_RIGHT,
"M_X1" : BUTTON_X1,
"M_X2" : BUTTON_X2]
@objc public static let oscButtonMappings: [String: Int32] = ["OSCA" : A_FLAG,
"OSCB" : B_FLAG,
"OSCX" : X_FLAG,
"OSCY" : Y_FLAG,
"OSCL1" : LB_FLAG,
"OSCR1" : RB_FLAG,
"OSCL3" : LS_CLK_FLAG,
"OSCR3" : RS_CLK_FLAG,
"OSCSTART" : PLAY_FLAG,
"OSCSELECT" : BACK_FLAG,
"OSCUP" : UP_FLAG,
"OSCDOWN" : DOWN_FLAG,
"OSCLEFT" : LEFT_FLAG,
"OSCRIGHT" : RIGHT_FLAG,
"OSCL2" : 0,
"OSCR2" : 0]
@objc public static let oscRectangleButtonCmds: [String] = [
"OSCUP",
"OSCDOWN",
"OSCLEFT",
"OSCRIGHT",
"OSCSTART",
"OSCSELECT"]
@objc public static let touchPadCmds: [String] = ["LSVPAD", "RSVPAD", "LSPAD", "RSPAD", "MOUSEPAD", "DPAD", "WASDPAD", "ARROWPAD"]
@objc public static let stickTouchPads: [String] = ["LSVPAD", "RSVPAD", "LSPAD", "RSPAD"]
@objc public static let nonVectorStickPads: [String] = ["LSPAD", "RSPAD"]
@objc public static let specialOverlayButtonCmds: [String] = ["SETTINGS"]
static let keyboardButtonMappings: [String: Int16] = [
// Windows Key Codes
"CTRL": 0x11, // VK_CONTROL
"SHIFT": 0x10, // VK_SHIFT
"ALT": 0x12, // VK_MENU
"F1": 0x70, // VK_F1
"F2": 0x71, // VK_F2
"F3": 0x72, // VK_F3
"F4": 0x73, // VK_F4
"F5": 0x74, // VK_F5
"F6": 0x75, // VK_F6
"F7": 0x76, // VK_F7
"F8": 0x77, // VK_F8
"F9": 0x78, // VK_F9
"F10": 0x79, // VK_F10
"F11": 0x7A, // VK_F11
"F12": 0x7B, // VK_F12
"A": 0x41, // 'A' key
"B": 0x42, // 'B' key
"C": 0x43, // 'C' key
"D": 0x44, // 'D' key
"E": 0x45, // 'E' key
"F": 0x46, // 'F' key
"G": 0x47, // 'G' key
"H": 0x48, // 'H' key
"I": 0x49, // 'I' key
"J": 0x4A, // 'J' key
"K": 0x4B, // 'K' key
"L": 0x4C, // 'L' key
"M": 0x4D, // 'M' key
"N": 0x4E, // 'N' key
"O": 0x4F, // 'O' key
"P": 0x50, // 'P' key
"Q": 0x51, // 'Q' key
"R": 0x52, // 'R' key
"S": 0x53, // 'S' key
"T": 0x54, // 'T' key
"U": 0x55, // 'U' key
"V": 0x56, // 'V' key
"W": 0x57, // 'W' key
"X": 0x58, // 'X' key
"Y": 0x59, // 'Y' key
"Z": 0x5A, // 'Z' key
"0": 0x30, // '0' key
"1": 0x31, // '1' key
"2": 0x32, // '2' key
"3": 0x33, // '3' key
"4": 0x34, // '4' key
"5": 0x35, // '5' key
"6": 0x36, // '6' key
"7": 0x37, // '7' key
"8": 0x38, // '8' key
"9": 0x39, // '9' key
"ESC": 0x1B, // VK_ESCAPE
"SPACE": 0x20, // VK_SPACE
"ENTER": 0x0D, // VK_RETURN
"TAB": 0x09, // VK_TAB
"BACKSPACE": 0x08, // VK_BACK
"INSERT": 0x2D, // VK_INSERT
"DEL": 0x2E, // VK_DELETE
"HOME": 0x24, // VK_HOME
"END": 0x23, // VK_END
"PG_UP": 0x21, // VK_PRIOR
"PG_DOWN": 0x22, // VK_NEXT
"UP_ARROW": 0x26, // VK_UP
"DOWN_ARROW": 0x28, // VK_DOWN
"LEFT_ARROW": 0x25, // VK_LEFT
"RIGHT_ARROW": 0x27, // VK_RIGHT
"NUM_LCK": 0x90, // VK_NUMLOCK
"SCR_LCK": 0x91, // VK_SCROLL
"CAPS_LOCK": 0x14, // VK_CAPITAL
"PAUSE": 0x13, // VK_PAUSE
"PR_SCR": 0x2C, // VK_SNAPSHOT
"NUMPAD0": 0x60, // VK_NUMPAD0
"NUMPAD1": 0x61, // VK_NUMPAD1
"NUMPAD2": 0x62, // VK_NUMPAD2
"NUMPAD3": 0x63, // VK_NUMPAD3
"NUMPAD4": 0x64, // VK_NUMPAD4
"NUMPAD5": 0x65, // VK_NUMPAD5
"NUMPAD6": 0x66, // VK_NUMPAD6
"NUMPAD7": 0x67, // VK_NUMPAD7
"NUMPAD8": 0x68, // VK_NUMPAD8
"NUMPAD9": 0x69, // VK_NUMPAD9
"MULTIPLY": 0x6A, // VK_MULTIPLY
"ADD": 0x6B, // VK_ADD
"SUBTRACT": 0x6D, // VK_SUBTRACT
"DECIMAL": 0x6E, // VK_DECIMAL
"DIVIDE": 0x6F, // VK_DIVIDE
"SEMI_COLON": 0xBA, // VK_OEM_1
"EQUALS": 0xBB, // VK_OEM_PLUS
"COMMA": 0xBC, // VK_OEM_COMMA
"MINUS": 0xBD, // VK_OEM_MINUS
"PERIOD": 0xBE, // VK_OEM_PERIOD
"FORWARD_SLASH": 0xBF, // VK_OEM_2
"GRAVE_ACCENT": 0xC0, // VK_OEM_3
"OPEN_BRACKET": 0xDB, // VK_OEM_4
"BACKSLASH": 0xDC, // VK_OEM_5
"CLOSE_BRACKET": 0xDD, // VK_OEM_6
"SINGLE_QUOTE": 0xDE, // VK_OEM_7
"VOLUME_MUTE": 0xAD, // VK_VOLUME_MUTE
"VOLUME_DOWN": 0xAE, // VK_VOLUME_DOWN
"VOLUME_UP": 0xAF, // VK_VOLUME_UP
"MEDIA_NEXT": 0xB0, // VK_MEDIA_NEXT_TRACK
"MEDIA_PREV": 0xB1, // VK_MEDIA_PREV_TRACK
"MEDIA_STOP": 0xB2, // VK_MEDIA_STOP
"MEDIA_PLAY_PAUSE": 0xB3, // VK_MEDIA_PLAY_PAUSE
"LAUNCH_MAIL": 0xB4, // VK_LAUNCH_MAIL
"LAUNCH_MEDIA_SELECT": 0xB5, // VK_LAUNCH_MEDIA_SELECT
"LAUNCH_APP1": 0xB6, // VK_LAUNCH_APP1
"LAUNCH_APP2": 0xB7, // VK_LAUNCH_APP2
"WIN": 0x5B,
"LEFT_WIN": 0x5B, // VK_LWIN
"RIGHT_WIN": 0x5C, // VK_RWIN
"APPS": 0x5D, // VK_APPS
// macOS Key Codes
"CMD": 0x37, // ⌘ Command
"OPT": 0x3A, // ⌥ Option
"CONTROL": 0x3B, // ⌃ Control
"SHIFT_MAC": 0x38, // ⇧ Shift
"FUNCTION": 0x3F, // fn
"DELETE_MAC": 0x75, // Forward Delete
"RETURN_MAC": 0x24, // Return
"ENTER_MAC": 0x4C, // Enter
"ESCAPE_MAC": 0x35, // Escape
"TAB_MAC": 0x30, // Tab
"SPACE_MAC": 0x31, // Space
"UP_ARROW_MAC": 0x7E, // Up Arrow
"DOWN_ARROW_MAC": 0x7D, // Down Arrow
"LEFT_ARROW_MAC": 0x7B, // Left Arrow
"RIGHT_ARROW_MAC": 0x7C, // Right Arrow
"F1_MAC": 0x7A, // F1
"F2_MAC": 0x78, // F2
"F3_MAC": 0x63, // F3
"F4_MAC": 0x76, // F4
"F5_MAC": 0x60, // F5
"F6_MAC": 0x61, // F6
"F7_MAC": 0x62, // F7
"F8_MAC": 0x64, // F8
"F9_MAC": 0x65, // F9
"F10_MAC": 0x6D, // F10
"F11_MAC": 0x67, // F11
"F12_MAC": 0x6F, // F12
"0_MAC": 0x52, // 0
"1_MAC": 0x53, // 1
"2_MAC": 0x54, // 2
"3_MAC": 0x55, // 3
"4_MAC": 0x56, // 4
"5_MAC": 0x57, // 5
"6_MAC": 0x58, // 6
"7_MAC": 0x59, // 7
"8_MAC": 0x5A, // 8
"9_MAC": 0x5B, // 9
"NUMPAD0_MAC": 0x4F, // Numpad 0
"NUMPAD1_MAC": 0x50, // Numpad 1
"NUMPAD2_MAC": 0x51, // Numpad 2
"NUMPAD3_MAC": 0x52, // Numpad 3
"NUMPAD4_MAC": 0x53, // Numpad 4
"NUMPAD5_MAC": 0x54, // Numpad 5
"NUMPAD6_MAC": 0x55, // Numpad 6
"NUMPAD7_MAC": 0x56, // Numpad 7
"NUMPAD8_MAC": 0x57, // Numpad 8
"NUMPAD9_MAC": 0x58, // Numpad 9
"NUMPAD_ADD_MAC": 0x45, // Numpad Add
"NUMPAD_SUBTRACT_MAC": 0x4A, // Numpad Subtract
"NUMPAD_MULTIPLY_MAC": 0x43, // Numpad Multiply
"NUMPAD_DIVIDE_MAC": 0x4B, // Numpad Divide
"NUMPAD_DECIMAL_MAC": 0x41, // Numpad Decimal
]
private var commands: [RemoteCommand] = []
public weak var viewController: CommandManagerViewController?
private override init() {
super.init()
loadCommands()
}
@objc static func presetDefaultCommands() {
let defaults = UserDefaults.standard
//if true { // save default entries if the data is empty.
if defaults.data(forKey: "savedCommands") == nil { // save default entries if the data is empty.
let defaultCommands: [RemoteCommand] = [
RemoteCommand(keyboardCmdString: "WIN", alias: "WIN"),
RemoteCommand(keyboardCmdString: "F11", alias: "F11"),
RemoteCommand(keyboardCmdString: "ESC", alias: "ESC"),
RemoteCommand(keyboardCmdString: "CTRL+SHIFT+ESC", alias: "任务管理器(Task Manager)"),
RemoteCommand(keyboardCmdString: "ALT+F1", alias: "N卡截图(Nvidia Screenshot)"),
RemoteCommand(keyboardCmdString: "ALT+F9", alias: "N卡录屏(Nvidia Screen Recording)"),
RemoteCommand(keyboardCmdString: "ALT+F4", alias: "关闭窗口(ALT+F4)"),
RemoteCommand(keyboardCmdString: "CTRL+A", alias: "全选(Select All)"),
RemoteCommand(keyboardCmdString: "CTRL+C", alias: "复制(Copy)"),
RemoteCommand(keyboardCmdString: "CTRL+V", alias: "粘贴(Paste)"),
RemoteCommand(keyboardCmdString: "WIN+D", alias: "切换桌面(Switch to Desktop)"),
RemoteCommand(keyboardCmdString: "WIN+P", alias: "多显模式(Project)"),
RemoteCommand(keyboardCmdString: "WIN+G", alias: "Xbox Game Bar"),
RemoteCommand(keyboardCmdString: "SHIFT+TAB", alias: "Steam Overlay"),
]
let data = try? NSKeyedArchiver.archivedData(withRootObject: defaultCommands, requiringSecureCoding: false)
defaults.set(data, forKey: "savedCommands")
}
}
@objc public func createTestKeyMappings() -> [String: Int16] {
return CommandManager.keyboardButtonMappings
}
// extractKeyStrings from keyboardCMDString
@objc public func extractKeyStringsFromComboCommand(from input: String) -> [String]? {
let keys = CommandManager.keyboardButtonMappings.keys.joined(separator: "|")
let pattern = "^(?:(\(keys))(?:\\+(\(keys))*)*)$"
guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else {
print("Failed to create regex")
return nil
}
let range = NSRange(location: 0, length: input.utf16.count)
guard let match = regex.firstMatch(in: input, options: [], range: range) else {
print("No match found for input: \(input)")
return nil
}
print("Regex matched for input: \(input)")
let matchedString = (input as NSString).substring(with: match.range(at: 0))
let keyStrings = matchedString.split(separator: "+").map { String($0) }
guard !keyStrings.isEmpty else {
print("No key strings found in the matched string")
return nil
}
var validKeyStrings: [String] = []
for key in keyStrings {
if CommandManager.keyboardButtonMappings.keys.contains(key) {
validKeyStrings.append(key)
} else {
print(" '\(key)' is not defined in key mappings")
return nil //treat any illegal string as a whole
}
}
if validKeyStrings.isEmpty {
print("No valid key strings found in the matched string")
return nil
}
for (index, key) in validKeyStrings.enumerated() {
print("Valid Key \(index): \(key)")
}
return validKeyStrings
}
//super combo key button strings
@objc public func extractKeyStringsFromComboKeys(from input: String) -> [String]? {
let combinedStrings = [CommandManager.keyboardButtonMappings.keys.map { $0 as String },
CommandManager.oscButtonMappings.keys.map { $0 as String },
CommandManager.mouseButtonMappings.keys.map { $0 as String }]
.lazy // 零内存开销
.flatMap { $0 } // 三维展开
.map(String.init(describing:)) // 安全类型转换
let keys = combinedStrings.joined(separator: "|")
// print(keys)
let pattern = "^(?:(\(keys))(?:\\-(\(keys))*)*)$"
guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else {
print("Failed to create regex")
return nil
}
let range = NSRange(location: 0, length: input.utf16.count)
guard let match = regex.firstMatch(in: input, options: [], range: range) else {
print("No match found for input: \(input)")
return nil
}
print("Regex matched for input: \(input)")
let matchedString = (input as NSString).substring(with: match.range(at: 0))
let keyStrings = matchedString.split(separator: "-").map { String($0) }
guard !keyStrings.isEmpty else {
print("No key strings found in the matched string")
return nil
}
var validKeyStrings: [String] = []
for key in keyStrings {
validKeyStrings.append(key)
}
if validKeyStrings.isEmpty {
print("No valid key strings found in the matched string")
return nil
}
for (index, key) in validKeyStrings.enumerated() {
print("Valid Key \(index): \(key)")
}
return validKeyStrings
}
@objc public func addCommand(_ command: RemoteCommand) -> Bool {
command.keyboardCmdString = command.keyboardCmdString.uppercased() // convert all letters to upper case
if(command.alias.trimmingCharacters(in: .whitespacesAndNewlines).count == 0) {command.alias = command.keyboardCmdString} // copy cmd string as alias when alias is empty
let keyStrings = extractKeyStringsFromComboCommand(from: command.keyboardCmdString)
if (keyStrings == nil) {return false} // in case of non-keyboard command strings, return false
commands.append(command)
saveCommands()
viewController?.reloadTableView() // don't know why but this reload has to be called from the CommandManager, doesn't work by calling it in the viewcontroller, probably related with the dialog box.
return true
}
@objc public func deleteCommand(at index: Int) {
guard index >= 0 && index < commands.count else {
return
}
commands.remove(at: index)
saveCommands()
}
@objc public func getAllCommands() -> [RemoteCommand] {
return commands
}
private func loadCommands() {
if let savedCommandsData = UserDefaults.standard.data(forKey: "savedCommands") {
do {
// Attempt to unarchive the data into an array of RemoteCommand
if let savedCommands = try NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, RemoteCommand.self], from: savedCommandsData) as? [RemoteCommand] {
// Assign the unarchived commands to your property
print(" Assign the unarchived commands to your property ")
commands = savedCommands
} else {
// Handle the case where the data could not be unarchived into the expected type
print("Data could not be unarchived into [RemoteCommand]")
}
} catch {
// Handle any errors that occur during unarchiving
print("Failed to unarchive savedCommands with error: \(error)")
}
}
}
private func saveCommands() {
if let data = try? NSKeyedArchiver.archivedData(withRootObject: commands, requiringSecureCoding: false) {
UserDefaults.standard.set(data, forKey: "savedCommands")
}
}
@objc public func sendKeyComboCommand(keyboardCmdStrings: [String], delay: TimeInterval = 0.2, index: Int = 0) { // we need a large delay for WAN streaming
// 如果已处理完所有按键,则开始释放按键
guard index < keyboardCmdStrings.count else {
// 释放按键
for keyStr in keyboardCmdStrings.reversed() { // 从后往前释放按键
if let keyCode = CommandManager.keyboardButtonMappings[keyStr] {
LiSendKeyboardEvent(keyCode, Int8(KEY_ACTION_UP), 0) // 释放按键
}
}
return
}
// 获取当前按键的映射值
if let keyCode = CommandManager.keyboardButtonMappings[keyboardCmdStrings[index]] {
// 发送当前按键的按下事件
LiSendKeyboardEvent(keyCode, Int8(KEY_ACTION_DOWN), 0)
// 延迟后递归处理下一个按键
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
self.sendKeyComboCommand(keyboardCmdStrings: keyboardCmdStrings, delay: delay, index: index + 1)
}
} else {
print("No mapping found for \(keyboardCmdStrings[index])")
// 如果当前按键没有映射,跳过当前按键并继续下一个
self.sendKeyComboCommand(keyboardCmdStrings: keyboardCmdStrings, delay: delay, index: index + 1)
}
}
@objc public func sendKeyComboDown(keyboardCmdStrings: [String]) { // we need a large delay for WAN streaming
for keyStr in keyboardCmdStrings {
if let keyCode = CommandManager.keyboardButtonMappings[keyStr] {
LiSendKeyboardEvent(keyCode, Int8(KEY_ACTION_DOWN), 0) // 释放按键
}
}
}
@objc public func sendKeyComboUp(keyboardCmdStrings: [String]) { // we need a large delay for WAN streaming
for keyStr in keyboardCmdStrings {
if let keyCode = CommandManager.keyboardButtonMappings[keyStr] {
LiSendKeyboardEvent(keyCode, Int8(KEY_ACTION_UP), 0) // 释放按键
}
}
return
}
}
+2
View File
@@ -46,6 +46,8 @@
-(void) setMotionEventState:(uint16_t)controllerNumber motionType:(uint8_t)motionType reportRateHz:(uint16_t)reportRateHz;
-(void) setControllerLed:(uint16_t)controllerNumber r:(uint8_t)r g:(uint8_t)g b:(uint8_t)b;
-(uint16_t) getActiveGamepadMask;
+(int) getConnectedGamepadMask:(StreamConfiguration*)streamConfig;
-(NSUInteger) getConnectedGamepadCount;
+38 -14
View File
@@ -46,6 +46,7 @@ static const double MOUSE_SPEED_DIVISOR = 1.25;
char _controllerNumbers;
bool _multiController;
bool _swapABXYButtons;
bool _captureMouse;
}
// UPDATE_BUTTON_FLAG(controller, flag, pressed)
@@ -354,6 +355,10 @@ static const double MOUSE_SPEED_DIVISOR = 1.25;
rightStickY = MAX_MAGNITUDE(rightStickY, controller.mergedWithController.lastRightStickY);
}
//NSLog(@"gamepadMask: %@", [self binaryRepresentationOfInteger:buttonFlags]); // we got the pressed OSC buttons here.
// Player 1 is always present for OSC
LiSendMultiControllerEvent(_multiController ? controller.playerIndex : 0, [self getActiveGamepadMask],
buttonFlags, leftTrigger, rightTrigger,
@@ -370,6 +375,19 @@ static const double MOUSE_SPEED_DIVISOR = 1.25;
}
}
- (NSString *)binaryRepresentationOfInteger:(int)number {
NSMutableString *binaryString = [NSMutableString string];
int numBits = sizeof(number) * 8;
for (int i = numBits - 1; i >= 0; i--) {
[binaryString appendString:((number >> i) & 1) ? @"1" : @"0"];
}
return binaryString;
}
+(BOOL) hasKeyboardOrMouse {
if (@available(iOS 14.0, tvOS 14.0, *)) {
return GCMouse.mice.count > 0 || GCKeyboard.coalescedKeyboard != nil;
@@ -847,20 +865,25 @@ static const double MOUSE_SPEED_DIVISOR = 1.25;
}
-(void) registerMouseCallbacks:(GCMouse*) mouse API_AVAILABLE(ios(14.0)) {
mouse.mouseInput.mouseMovedHandler = ^(GCMouseInput * _Nonnull mouse, float deltaX, float deltaY) {
self->accumulatedDeltaX += deltaX / MOUSE_SPEED_DIVISOR;
self->accumulatedDeltaY += -deltaY / MOUSE_SPEED_DIVISOR;
short truncatedDeltaX = (short)self->accumulatedDeltaX;
short truncatedDeltaY = (short)self->accumulatedDeltaY;
if (truncatedDeltaX != 0 || truncatedDeltaY != 0) {
LiSendMouseMoveEvent(truncatedDeltaX, truncatedDeltaY);
if (_captureMouse){
mouse.mouseInput.mouseMovedHandler = ^(GCMouseInput * _Nonnull mouse, float deltaX, float deltaY) {
self->accumulatedDeltaX += deltaX / MOUSE_SPEED_DIVISOR;
self->accumulatedDeltaY += -deltaY / MOUSE_SPEED_DIVISOR;
self->accumulatedDeltaX -= truncatedDeltaX;
self->accumulatedDeltaY -= truncatedDeltaY;
}
};
short truncatedDeltaX = (short)self->accumulatedDeltaX;
short truncatedDeltaY = (short)self->accumulatedDeltaY;
if (truncatedDeltaX != 0 || truncatedDeltaY != 0) {
LiSendMouseMoveEvent(truncatedDeltaX, truncatedDeltaY);
self->accumulatedDeltaX -= truncatedDeltaX;
self->accumulatedDeltaY -= truncatedDeltaY;
}
};
} else {
mouse.mouseInput.mouseMovedHandler = nil;
}
mouse.mouseInput.leftButton.pressedChangedHandler = ^(GCControllerButtonInput * _Nonnull button, float value, BOOL pressed) {
LiSendMouseButtonEvent(pressed ? BUTTON_ACTION_PRESS : BUTTON_ACTION_RELEASE, BUTTON_LEFT);
@@ -1061,7 +1084,7 @@ static const double MOUSE_SPEED_DIVISOR = 1.25;
// Even if no gamepads are present, we will always count one if OSC is enabled,
// or it's set to auto and no keyboard or mouse is present. Absolute touch mode
// disables the OSC.
if (level != OnScreenControlsLevelOff && (![ControllerSupport hasKeyboardOrMouse] || level != OnScreenControlsLevelAuto) && !settings.absoluteTouchMode) {
if (level != OnScreenControlsLevelOff && (![ControllerSupport hasKeyboardOrMouse] || level != OnScreenControlsLevelAuto) && (settings.touchMode.intValue == RELATIVE_TOUCH)) {
mask |= 0x1;
}
@@ -1103,6 +1126,7 @@ static const double MOUSE_SPEED_DIVISOR = 1.25;
}
}
_captureMouse = (streamConfig.localMousePointerMode == 0);
if (@available(iOS 14.0, tvOS 14.0, *)) {
for (GCMouse* mouse in [GCMouse mice]) {
[self registerMouseCallbacks:mouse];
@@ -0,0 +1,37 @@
//
// LayoutOnScreenControls.h
// Moonlight
//
// Created by Long Le on 9/26/22.
// Copyright © 2022 Moonlight Game Streaming Project. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "OnScreenControls.h"
#import "Moonlight-Swift.h"
NS_ASSUME_NONNULL_BEGIN
/**
This object is a subclass of 'OnScreenControls' and adds additional properties and functions which allow the user to drag and drop each of the 19 on screen controller buttons in order to change their positions on screen. The object is used in the 'LayoutOnScreenControlsViewController' thus allowing the user to drag, drop, hide, unhide, on screen controller buttons.
Note that this is in contrast to the game stream view which displays an 'OnScreenControls' object on screen that allows the app to register taps on each button as controller input. It does not (and naturally should not) allow the user to move the buttons around the screen.
*/
@interface LayoutOnScreenControls : OnScreenControls
@property UIView* _view;
@property NSMutableArray *layoutChanges;
@property CALayer *layerBeingDragged;
@property UIViewController *layoutToolVC;
- (id) initWithView:(UIView*)view controllerSup:(ControllerSupport*)controllerSupport
streamConfig:(StreamConfiguration*)streamConfig oscLevel:(int)oscLevel;
- (CALayer*) controllerLayerFromName:(NSString*)name;
- (BOOL) isLayer:(CALayer*)layer hoveringOverButton:(UIButton*)button;
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
// - (void) updateGuidelinesForOnScreenWidget:(OnScreenWidgetView* )widget;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,337 @@
//
// LayoutOnScreenControls.m
// Moonlight
//
// Created by Long Le on 9/26/22.
// Copyright © 2022 Moonlight Game Streaming Project. All rights reserved.
//
#import "LayoutOnScreenControls.h"
#import "OSCProfilesTableViewController.h"
#import "OnScreenButtonState.h"
#import "OSCProfilesManager.h"
@interface LayoutOnScreenControls ()
@end
@implementation LayoutOnScreenControls {
UIButton *trashCanButton;
UIView *horizontalGuideline;
UIView *verticalGuideline;
}
@synthesize layerBeingDragged;
@synthesize _view;
@synthesize layoutChanges;
- (id) initWithView:(UIView*)view controllerSup:(ControllerSupport*)controllerSupport streamConfig:(StreamConfiguration*)streamConfig oscLevel:(int)oscLevel {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(updateGuidelinesForOnScreenWidget:)
name:@"OnScreenWidgetMovedByTouch" // This is a special notification for reloading the on screen keyboard buttons. which can't be executed by _oscProfilesTableViewController.needToUpdateOscLayoutTVC code block, and has to be triggered by a notification
object:nil];
_view = view;
_view.multipleTouchEnabled = false;
self = [super initWithView:view controllerSup:controllerSupport streamConfig:streamConfig];
self.isLayingOut = true; // make sure the super class have a flag to tell it's currently laying out the OSCs, intead play mode.
self._level = oscLevel;
layoutChanges = [[NSMutableArray alloc] init]; // will contain OSC button layout changes the user has made for this profile
[self drawButtons];
[self drawGuidelines]; // add the blue guidelines that appear when button is being tapped and dragged
return self;
}
#pragma mark - Drawing
/**
* This method overrides the superclass's drawButtons method. The purpose of this method is to create a dPad parent layer, and add the four dPad buttons to it so that the user can drag the entire dPad around, directional buttons and all, as one unit as is the expected behavior. Note that we do not want the four dPad buttons to be child layers of a CALayer parent layer on the game stream view since the touch logic implemented for the four dPad buttons on the game stream view is written assuming the dPad buttons are not children of another parent CALayer
*/
- (void) drawButtons {
[super setDPadCenter]; // Set custom position for D-Pad here
[super setAnalogStickPositions]; // Set custom position for analog sticks here
[super drawBumpers];
[super drawButtons];
UIImage* downButtonImage = [UIImage imageNamed:@"DownButton"];
UIImage* rightButtonImage = [UIImage imageNamed:@"RightButton"];
UIImage* upButtonImage = [UIImage imageNamed:@"UpButton"];
UIImage* leftButtonImage = [UIImage imageNamed:@"LeftButton"];
// create dPad background layer
self._dPadBackground = [CALayer layer];
self._dPadBackground.name = @"dPad";
self._dPadBackground.frame = CGRectMake(self.D_PAD_CENTER_X,
self.D_PAD_CENTER_Y,
self._leftButton.frame.size.width * 2 + BUTTON_DIST,
self._leftButton.frame.size.width * 2 + BUTTON_DIST);
self._dPadBackground.position = CGPointMake(self.D_PAD_CENTER_X, self.D_PAD_CENTER_Y); // since dPadBackground's dimensions have change after settings its width and height you need to reset its position again here
[self.OSCButtonLayers addObject:self._dPadBackground];
[_view.layer addSublayer:self._dPadBackground];
// add dPad buttons to parent layer
[self._dPadBackground addSublayer:self._downButton];
[self._dPadBackground addSublayer:self._rightButton];
[self._dPadBackground addSublayer:self._upButton];
[self._dPadBackground addSublayer:self._leftButton];
[self resizeControllerLayerWith:self._dPadBackground and:_dPadSizeFactor];
/*
// reposition each dPad button within their parent dPadBackground layer
self._downButton.frame = CGRectMake(self._dPadBackground.frame.size.width/3, self._dPadBackground.frame.size.height/2 + D_PAD_DIST, downButtonImage.size.width, downButtonImage.size.height);
self._rightButton.frame = CGRectMake(self._dPadBackground.frame.size.width/2 + D_PAD_DIST, self._dPadBackground.frame.size.height/3, rightButtonImage.size.width, rightButtonImage.size.height);
self._upButton.frame = CGRectMake(self._dPadBackground.frame.size.width/3, 0, upButtonImage.size.width, upButtonImage.size.height);
self._leftButton.frame = CGRectMake(0, self._dPadBackground.frame.size.height/3, leftButtonImage.size.width, leftButtonImage.size.height);*/
}
/**
* draws a horizontal and vertical line that is made visible and positioned over whichever button the user is dragging around the screen
*/
- (void) drawGuidelines {
horizontalGuideline = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self._view.frame.size.width * 2, 2)];
horizontalGuideline.backgroundColor = [UIColor blueColor];
horizontalGuideline.hidden = YES;
[self._view addSubview: horizontalGuideline];
verticalGuideline = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 2, self._view.frame.size.height * 2)];
verticalGuideline.backgroundColor = [UIColor blueColor];
verticalGuideline.hidden = YES;
[self._view addSubview: verticalGuideline];
}
- (void) updateGuidelinesForOnScreenWidget: (NSNotification *)notification{
/* have guidelines follow wherever the user is touching on the screen */
OnScreenWidgetView* widget = notification.object;
horizontalGuideline.hidden = verticalGuideline.hidden = NO;
horizontalGuideline.center = verticalGuideline.center = CGPointMake(CGRectGetMidX(widget.frame), CGRectGetMidY(widget.frame));
/*
Telegraph to the user whether the horizontal and/or vertical guidelines line up with one or more of the buttons on screen by doing the following:
-Change horizontal guideline color to yellow if its y-position is almost equal to that of one of the buttons on screen.
-Change vertical guideline color to yellow if its x-position is almost equal to that of one of the buttons on screen.
*/
horizontalGuideline.backgroundColor = [UIColor blueColor]; // change horizontal guideline back to blue if it doesn't line up with one of the on screen buttons
for (CALayer *button in self.OSCButtonLayers) { // horizontal guideline position check
if ((layerBeingDragged != button) && !button.isHidden) {
if ((horizontalGuideline.center.y < button.position.y + 1) &&
(horizontalGuideline.center.y > button.position.y - 1)) {
horizontalGuideline.backgroundColor = [UIColor yellowColor];
break;
}
}
}
for(UIView* view in self.layoutToolVC.view.subviews){
if ([view isKindOfClass:[OnScreenWidgetView class]] && view != widget) {
if ((horizontalGuideline.center.y < view.center.y + 1) && (horizontalGuideline.center.y > view.center.y - 1)) {
horizontalGuideline.backgroundColor = [UIColor yellowColor];
break;
}
}
}
verticalGuideline.backgroundColor = [UIColor blueColor]; // change vertical guideline back to blue if it doesn't line up with one of the on screen buttons
for (CALayer *button in self.OSCButtonLayers) { // vertical guideline position check
if ((layerBeingDragged != button) && !button.isHidden) {
if ((verticalGuideline.center.x < button.position.x + 1) &&
(verticalGuideline.center.x > button.position.x - 1)) {
verticalGuideline.backgroundColor = [UIColor yellowColor];
break;
}
}
}
for(UIView* view in self.layoutToolVC.view.subviews){
if ([view isKindOfClass:[OnScreenWidgetView class]] && view != widget) {
if ((verticalGuideline.center.x < view.center.x + 1) && (verticalGuideline.center.x > view.center.x - 1)) {
verticalGuideline.backgroundColor = [UIColor yellowColor];
break;
}
}
}
}
#pragma mark - Queries
/* used to determine whether user is dragging an OSC button (of type CALayer) over the trash can with the intent of hiding that button */
- (BOOL) isLayer:(CALayer *)layer hoveringOverButton:(UIButton *)button {
CGRect buttonConvertedRect = [self._view convertRect:button.imageView.frame fromView:button.superview];
if (CGRectIntersectsRect(layer.frame, buttonConvertedRect)) {
return YES;
}
else {
return NO;
}
}
/* returns reference to button layer object given the button's name*/
- (CALayer*)controllerLayerFromName: (NSString*)name {
for (CALayer *buttonLayer in self.OSCButtonLayers) {
if ([buttonLayer.name isEqualToString:name]) {
return buttonLayer;
}
}
return nil;
}
#pragma mark - Touch
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Reset variables related to on screen controller button drag and drop routine. These variables should be reset in 'touchesCancelled' or 'touchesEnded' but these may not be called in unaccounted-for edge cases such as when the user opens various OS-level control center related views by dragging down from the top of the screen, or dragging up from the bottom of the screen. Since Apple likes to add new control centers and new ways of opening them (i.e. Dynamic Island on iPhone 14 Pro) it's best to reset these variables here when the user is beginning a new on screen controller button drag routine */
[[NSNotificationCenter defaultCenter] postNotificationName:@"LegacyOscCALayerSelectedNotification" object:self];
layerBeingDragged = nil;
horizontalGuideline.backgroundColor = [UIColor blueColor];
verticalGuideline.backgroundColor = [UIColor blueColor];
for (UITouch* touch in touches) { // Process touch
CGPoint touchLocation = [touch locationInView:_view];
touchLocation = [[touch view] convertPoint:touchLocation toView:nil];
CALayer *layer = [_view.layer hitTest:touchLocation];
/* Don't let user drag and move anything other than on screen controller buttons, which are CALayer types. The reason is that 'LayoutOnScreenControls' should only be responsible for managing and letting users move on screen controller buttons. Since this class's view is currently set to be set equal to the 'LayoutOnScreenControlsViewController' view it belongs to, we need to make sure touches on the VC's objects don't propagate down to 'LayoutOnScreenControls'. Weird stuff can happen to the UI buttons that belong to that VC (trash can button, undo button, save button, etc), such as them being dragged around the screen with the user's touches */
for (UIView *subview in self._view.subviews) {
if (CGRectContainsPoint(subview.frame, touchLocation)) {
if (![subview isKindOfClass:[CALayer class]]) {
return;
}
}
}
if (layer == self._upButton ||
layer == self._downButton ||
layer == self._leftButton ||
layer == self._rightButton) { // don't let user drag individual dPad buttons
layerBeingDragged = self._dPadBackground;
}
else if (layer == self._rightStick) { // only let user drag right stick's background, not the inner analog stick itself
layerBeingDragged = self._rightStickBackground;
}
else if (layer == self._leftStick) { // only let user drag left stick's background, not the inner analog stick itself
layerBeingDragged = self._leftStickBackground;
}
else if(layer == self._aButton ||
layer == self._bButton ||
layer == self._xButton ||
layer == self._yButton ||
layer == self._l1Button ||
layer == self._l2Button ||
layer == self._r1Button ||
layer == self._r2Button ||
layer == self._selectButton ||
layer == self._startButton){ // we must iterates all the controller button layers manually here, to prevent other widgets being dragged accidently
layerBeingDragged = layer;
}
[[NSNotificationCenter defaultCenter] postNotificationName:@"ControllerCALayerSelected" object:layerBeingDragged]; // inform that a CALayer of onScreen controller has being selected
/* save the name, position, and visibility of button being touched in array in case user wants to undo the change later */
OnScreenButtonState *onScreenButtonState = [[OnScreenButtonState alloc] initWithButtonName:layerBeingDragged.name buttonType:LegacyOscButton andPosition:layerBeingDragged.position];
// add hidden attr here
onScreenButtonState.isHidden = layerBeingDragged.isHidden;
[layoutChanges addObject:onScreenButtonState];
[[NSNotificationCenter defaultCenter] postNotificationName:@"OSCLayoutChanged" object:self]; // lets the view controller know whether to fade the undo button in or out depending on whether there are any further OSC layout changes the user is allowed to undo
/* make guide lines visible and position them over the button the user is touching */
horizontalGuideline.center = layerBeingDragged.position;
horizontalGuideline.hidden = NO;
verticalGuideline.center = layerBeingDragged.position;
verticalGuideline.hidden = NO;
}
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView:_view];
layerBeingDragged.position = touchLocation; // move object to touch location
/* have guidelines follow wherever the user is touching on the screen */
horizontalGuideline.center = layerBeingDragged.position;
verticalGuideline.center = layerBeingDragged.position;
/*
Telegraph to the user whether the horizontal and/or vertical guidelines line up with one or more of the buttons on screen by doing the following:
-Change horizontal guideline color to yellow if its y-position is almost equal to that of one of the buttons on screen.
-Change vertical guideline color to yellow if its x-position is almost equal to that of one of the buttons on screen.
*/
horizontalGuideline.backgroundColor = [UIColor blueColor]; // change horizontal guideline back to blue if it doesn't line up with one of the on screen buttons
for (CALayer *button in self.OSCButtonLayers) { // horizontal guideline position check
if ((layerBeingDragged != button) && !button.isHidden) {
if ((horizontalGuideline.center.y < button.position.y + 1) &&
(horizontalGuideline.center.y > button.position.y - 1)) {
horizontalGuideline.backgroundColor = [UIColor yellowColor];
break;
}
}
}
// alignment with onScreenWidgets
for(UIView* view in self.layoutToolVC.view.subviews){
if ([view isKindOfClass:[OnScreenWidgetView class]]) {
if ((horizontalGuideline.center.y < view.center.y + 1) && (horizontalGuideline.center.y > view.center.y - 1)) {
horizontalGuideline.backgroundColor = [UIColor yellowColor];
break;
}
}
}
verticalGuideline.backgroundColor = [UIColor blueColor]; // change vertical guideline back to blue if it doesn't line up with one of the on screen buttons
for (CALayer *button in self.OSCButtonLayers) { // vertical guideline position check
if ((layerBeingDragged != button) && !button.isHidden) {
if ((verticalGuideline.center.x < button.position.x + 1) &&
(verticalGuideline.center.x > button.position.x - 1)) {
verticalGuideline.backgroundColor = [UIColor yellowColor];
break;
}
}
}
// alignment with onScreenWidgets
for(UIView* view in self.layoutToolVC.view.subviews){
if ([view isKindOfClass:[OnScreenWidgetView class]]) {
if ((verticalGuideline.center.x < view.center.x + 1) && (verticalGuideline.center.x > view.center.x - 1)) {
verticalGuideline.backgroundColor = [UIColor yellowColor];
break;
}
}
}
}
- (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
layerBeingDragged = nil;
horizontalGuideline.hidden = YES;
verticalGuideline.hidden = YES;
horizontalGuideline.backgroundColor = [UIColor blueColor];
verticalGuideline.backgroundColor = [UIColor blueColor];
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
layerBeingDragged = nil;
horizontalGuideline.hidden = YES;
verticalGuideline.hidden = YES;
horizontalGuideline.backgroundColor = [UIColor blueColor];
verticalGuideline.backgroundColor = [UIColor blueColor];
}
@end
+32
View File
@@ -0,0 +1,32 @@
//
// OSCProfile.h
// Moonlight
//
// Created by Long Le on 12/22/22.
// Copyright © 2022 Moonlight Game Streaming Project. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "OnScreenButtonState.h"
NS_ASSUME_NONNULL_BEGIN
/**
This object contains information pertaining to any of the user created, custom on screen controller layout configurations, or 'profiles.' The object contains a 'name' property for easy reference, as well as an 'isSelected' property which is used to determine whether this particular custom OSC layout should show on screen during game stream view. Only one 'OSCProfile' is set to 'isSelected' at any given time. The object also contains an array of 'OnScreenButtonStates' which provides information that allows us to move and hide/unhide each of the 19 on screen buttons. Note that the 'buttonStates' property should contain an NSMutableArray of ENCODED 'OnScreenButtonState' objects. This allows us to save the 'OSCProfile' object to NSUserDefaults.
Additionally the 'OSCProfile' object adopts encoding and decoding protocols so that we can encode the object before saving it to NSUserDefaults. By saving this object to NSUserDefaults we allow the user to save and load their custom on screen controller button layouts between app launches
*/
@interface OSCProfile : NSObject <NSCoding, NSSecureCoding>
@property NSString *name;
@property NSMutableArray <OnScreenButtonState *> *buttonStates;
@property BOOL isSelected;
- (id) initWithName:(NSString*)name buttonStates:(NSMutableArray*)buttonStates isSelected:(BOOL)isSelected;
+ (BOOL) supportsSecureCoding;
- (id) initWithCoder:(NSCoder*)decoder;
- (void) encodeWithCoder:(NSCoder*)encoder;
@end
NS_ASSUME_NONNULL_END
+43
View File
@@ -0,0 +1,43 @@
//
// OSCProfile.m
// Moonlight
//
// Created by Long Le on 12/22/22.
// Copyright © 2022 Moonlight Game Streaming Project. All rights reserved.
//
#import "OSCProfile.h"
@implementation OSCProfile
- (id) initWithName:(NSString*)name buttonStates:(NSMutableArray*)buttonStates isSelected:(BOOL)isSelected {
if ((self = [self init])) {
self.name = name;
self.buttonStates = buttonStates;
self.isSelected = isSelected;
}
return self;
}
+ (BOOL) supportsSecureCoding {
return YES;
}
- (void) encodeWithCoder:(NSCoder*)encoder {
[encoder encodeObject:self.name forKey:@"name"];
[encoder encodeObject:self.buttonStates forKey:@"buttonStates"];
[encoder encodeBool:self.isSelected forKey:@"isSelected"];
}
- (id) initWithCoder:(NSCoder*)decoder {
if (self = [super init]) {
self.name = [decoder decodeObjectForKey:@"name"];
self.buttonStates = [decoder decodeObjectForKey:@"buttonStates"];
self.isSelected = [decoder decodeBoolForKey:@"isSelected"];
}
return self;
}
@end
@@ -0,0 +1,69 @@
//
// OSCProfilesManager.h
// Moonlight
//
// Created by Long Le on 1/1/23.
// Copyright © 2023 Moonlight Game Streaming Project. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "OSCProfile.h"
NS_ASSUME_NONNULL_BEGIN
/**
This singleton object can be accessed from any class and provides methods to get and set on screen controller profile related data.
Note that the implementation file contains a number of 'Helper' methods. These helper methods are only used in this class's implementation file and help to reduce re-writing large blocks of code that are called multiple times throughout the file
*/
@interface OSCProfilesManager : NSObject
+ (OSCProfilesManager *) sharedManager;
+ (void) setOnScreenWidgetViewsSet:(NSMutableSet* )set;
#pragma mark - Getters
/**
* Returns an array of decoded profile objects
*/
- (NSMutableArray *) getAllProfiles;
/**
* Returns the OSC Profile that is currently selected to be displayed on screen during game streaming
*/
- (OSCProfile *) getSelectedProfile;
/**
* Returns the index of the 'selected' profile within the array it's in
*/
- (NSInteger) getIndexOfSelectedProfile;
#pragma mark - Setters
/**
* Sets the profile object with the particular 'name' as the selected profile to be displayed on screen during game streaming
*/
- (void) setProfileToSelected:(NSString *)name;
/**
* Saves a profile object with a particular 'name' and an array of button layers (the CALayer button layers are the objects currently visible on screen) to persistent storage
*/
- (void) saveProfileWithName:(NSString*)name andButtonLayers:(NSMutableArray *)buttonLayers;
- (bool) updateSelectedProfile:(NSMutableArray *) oscButtonLayers;
/**
* Delete current selected profile.
*/
- (void) deleteCurrentSelectedProfile;
#pragma mark - Queries
/**
* Lets the caller of this method know whether a profile with a given name already exists in persistent storage
*/
- (BOOL) profileNameAlreadyExist:(NSString*)name;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,333 @@
//
// OSCProfilesManager.m
// Moonlight
//
// Created by Long Le on 1/1/23.
// Copyright © 2023 Moonlight Game Streaming Project. All rights reserved.
//
#import "OSCProfilesManager.h"
#import "OnScreenButtonState.h"
#import "Moonlight-Swift.h"
#import "LayoutOnScreenControlsViewController.h"
#import "OnScreenControls.h"
@implementation OSCProfilesManager
static NSMutableSet *OnScreenWidgetViews;
#pragma mark - Initializer
+ (OSCProfilesManager *) sharedManager {
static OSCProfilesManager *_sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedManager = [[self alloc] init];
});
return _sharedManager;
}
#pragma mark - Class Helper Methods
+ (void) setOnScreenWidgetViewsSet:(NSMutableSet* )set{
OnScreenWidgetViews = set;
}
/**
* Returns the profile whose 'name' property has a value that is equal to the 'name' passed into the method
*/
- (OSCProfile *) OSCProfileWithName:(NSString*)name {
NSMutableArray *profiles = [self getAllProfiles];
for (OSCProfile *profile in profiles) {
if ([profile.name isEqualToString:name]) {
return profile;
}
}
return nil;
}
/**
* Returns an array of encoded 'OSCProfile' objects from the array of decoded 'OSCProfile' objects passed into this method
*/
- (NSMutableArray *) encodedProfilesFromArray:(NSMutableArray *)profiles {
NSMutableArray *profilesEncoded = [[NSMutableArray alloc] init];
/* encode each profile and add them back into an array */
for (OSCProfile *profile in profiles) { //
NSData *profileEncoded = [NSKeyedArchiver archivedDataWithRootObject:profile requiringSecureCoding:YES error:nil];
[profilesEncoded addObject:profileEncoded];
}
return profilesEncoded;
}
/**
* Delete an 'OSCProfile' object of specific index for another in the 'OSCProfile' objects array stored in persistent storage
*/
-(void)deleteCurrentSelectedProfile{
NSMutableArray *profiles = [self getAllProfiles];
NSInteger selectedIndex = [self getIndexOfSelectedProfile];
/* Remove the selected profile */
if(selectedIndex != 0) [profiles removeObjectAtIndex:selectedIndex]; //deleting default profile (index 0) is not allowed
else {
NSLog(@"Default profile!"); // to be updated here.
}
selectedIndex--;
if(selectedIndex < 0) selectedIndex = 0;
OSCProfile *newSelectedProfile = [profiles objectAtIndex:selectedIndex];
newSelectedProfile.isSelected = YES;
NSMutableArray *profilesEncoded = [self encodedProfilesFromArray:profiles]; // encode each 'profile' object in the array and add them to a new array
/* Encode the array itself, NOT the objects in the array, which are already encoded. Save array to persistent storage */
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:profilesEncoded requiringSecureCoding:YES error:nil];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"OSCProfiles"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
/**
* Replaces one 'OSCProfile' object for another in the 'OSCProfile' objects array stored in persistent storage
*/
- (void) replaceProfile:(OSCProfile*)oldProfile withProfile:(OSCProfile*)newProfile {
NSMutableArray *profiles = [self getAllProfiles];
for (OSCProfile *profile in profiles) { // set all profiles' 'isSelected' property to NO since the new profile we're saving over the old profile with will be the 'selected' profile
profile.isSelected = NO;
}
/* Set the new profile as the selected one. The reasoning behind this is that this method is currently being used when the user saves over an existing profile with another profile that has the same name. The expected behavior is that the newly saved profile becomes the selected profile which will show on screen when they launch the game stream view */
newProfile.isSelected = YES;
/* Remove the old profile from the array and insert the new profile into its place */
int index = 0;
for (int i = 0; i < profiles.count; i++) {
if ([[profiles[i] name] isEqualToString: oldProfile.name]) {
index = i;
}
}
[profiles removeObjectAtIndex:index];
[profiles insertObject:newProfile atIndex:index];
NSMutableArray *profilesEncoded = [self encodedProfilesFromArray:profiles]; // encode each 'profile' object in the array and add them to a new array
/* Encode the array itself, NOT the objects in the array, which are already encoded. Save array to persistent storage */
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:profilesEncoded requiringSecureCoding:YES error:nil];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"OSCProfiles"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
#pragma mark - Globally Accessible Methods
#pragma mark - Getters
- (NSMutableArray *) getAllProfiles {
NSData *profilesArrayEncoded = [[NSUserDefaults standardUserDefaults] objectForKey: @"OSCProfiles"]; // Get the encoded array of encoded OSC profiles from persistent storage
NSSet *classes = [NSSet setWithObjects:[NSString class], [NSMutableData class], [NSMutableArray class], [OSCProfile class], [OnScreenButtonState class], nil];
NSMutableArray *profilesEncoded = [NSKeyedUnarchiver unarchivedObjectOfClasses:classes fromData:profilesArrayEncoded error:nil]; // Decode the encoded array itself, NOT the objects contained in the array
/* Decode each of the encoded profiles, place them into an array, then return the array */
NSMutableArray *profilesDecoded = [[NSMutableArray alloc] init];
OSCProfile *profileDecoded;
for (NSData *profileEncoded in profilesEncoded) {
profileDecoded = [NSKeyedUnarchiver unarchivedObjectOfClasses: classes fromData:profileEncoded error: nil];
[profilesDecoded addObject: profileDecoded];
}
return profilesDecoded;
}
- (OSCProfile *) getSelectedProfile {
NSMutableArray *profiles = [self getAllProfiles];
for (OSCProfile *profile in profiles) {
if (profile.isSelected) {
return profile;
}
}
return nil;
}
- (NSInteger) getIndexOfSelectedProfile {
NSMutableArray *profiles = [self getAllProfiles];
for (OSCProfile *profile in profiles) {
if (profile.isSelected == YES) {
return [profiles indexOfObject:profile];
}
}
return 0; // if none of the profiles in the array have their 'isSelected' property set to YES (which should not be possible) return the 'Default' profile as the 'selected' profile
}
#pragma mark - Setters
- (void) setProfileToSelected:(NSString *)name {
NSMutableArray *profiles = [self getAllProfiles];
/* Iterate through each profile. If its name equals the value of the 'name' parameter passed into this method then set the profile's 'isSelected' property to YES, otherwise set the value to NO */
for (OSCProfile *profile in profiles) {
if ([profile.name isEqualToString:name]) {
profile.isSelected = YES;
}
else {
profile.isSelected = NO;
}
}
NSMutableArray *profilesEncoded = [self encodedProfilesFromArray:profiles]; // encode each 'profile' object in the array and add them to a new array
/* Encode the array itself, NOT the objects inside the array, which have already been encoded by this point */
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:profilesEncoded requiringSecureCoding:YES error:nil];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"OSCProfiles"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (bool) updateSelectedProfile:(NSMutableArray *) oscButtonLayers {
NSMutableArray* buttonStatesEncoded = [self convertOnScreenControllerAndButtonsToButtonStates:oscButtonLayers];
if([self getIndexOfSelectedProfile]==0) return false;
OSCProfile *newProfile = [[OSCProfile alloc] initWithName:[self getSelectedProfile].name
buttonStates:buttonStatesEncoded isSelected:YES]; // create a new 'OSCProfile'. Set the array of encoded button states created above to the 'buttonStates' property of the new profile, along with a 'name'. Set 'isSelected' argument to YES which will set this saved profile as the one that will show up in the game stream view
/* set all saved OSCProfiles 'isSelected' property to NO since the new profile you're adding will be set as the selected profile */
NSMutableArray *profiles = [self getAllProfiles];
for (OSCProfile *profile in profiles) {
profile.isSelected = NO;
}
[self replaceProfile:[self getSelectedProfile] withProfile:newProfile];
return true;
}
- (void) saveProfileWithName:(NSString*)name andButtonLayers:(NSMutableArray *)oscButtonLayers {
NSMutableArray* buttonStatesEncoded = [self convertOnScreenControllerAndButtonsToButtonStates:oscButtonLayers];
OSCProfile *newProfile = [[OSCProfile alloc] initWithName:name
buttonStates:buttonStatesEncoded isSelected:YES]; // create a new 'OSCProfile'. Set the array of encoded button states created above to the 'buttonStates' property of the new profile, along with a 'name'. Set 'isSelected' argument to YES which will set this saved profile as the one that will show up in the game stream view
/* set all saved OSCProfiles 'isSelected' property to NO since the new profile you're adding will be set as the selected profile */
NSMutableArray *profiles = [self getAllProfiles];
for (OSCProfile *profile in profiles) {
profile.isSelected = NO;
}
if ([self profileNameAlreadyExist:name]) { // if a saved profile with the same 'name' already exists in persistent storage then overwrite it and save the change to persistent storage
[self replaceProfile:[self OSCProfileWithName:name] withProfile:newProfile];
}
else { // otherwise encode then add the new profile to the end of the OSCProfiles array
NSData *newProfileEncoded = [NSKeyedArchiver archivedDataWithRootObject:newProfile requiringSecureCoding:YES error:nil];
NSMutableArray *profilesEncoded = [self encodedProfilesFromArray:profiles];
[profilesEncoded addObject:newProfileEncoded];
/* Encode the 'profilesEncoded' array itself, NOT the objects in the 'profilesEncoded' array, all of which are already encoded by this point */
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:profilesEncoded requiringSecureCoding:YES error:nil];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"OSCProfiles"];
[[NSUserDefaults standardUserDefaults] synchronize];
// saving test:
/*
NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *path = [documentsPath stringByAppendingPathComponent:@"newDefault.bin"];
[newProfileEncoded writeToFile:path atomically:YES];
NSLog(@"默认 Profile 数据已保存到: %@", path);
*/
}
}
- (NSMutableArray* ) convertOnScreenControllerAndButtonsToButtonStates:(NSMutableArray *) oscButtonLayers {
/* iterate through each OSC button the user sees on screen, create an 'OnScreenButtonState' object from each button, encode each object, and then add each object to an array */
/*
NSSet *validPositionButtonNames = [NSSet setWithObjects:
@"l2Button",
@"l1Button",
@"dPad",
@"selectButton",
@"leftStickBackground",
@"rightStickBackground",
@"r2Button",
@"r1Button",
@"aButton",
@"bButton",
@"xButton",
@"yButton",
@"startButton",
nil]; */
NSMutableArray *buttonStatesEncoded = [[NSMutableArray alloc] init];
// save on-screen game controller buttons & sticks as buttonstate:
for (CALayer *oscButtonLayer in oscButtonLayers) {
OnScreenButtonState *buttonState = [[OnScreenButtonState alloc] initWithButtonName:oscButtonLayer.name buttonType:LegacyOscButton andPosition:oscButtonLayer.position];
// add hidden attr here
buttonState.isHidden = oscButtonLayer.isHidden;
buttonState.oscLayerSizeFactor = [OnScreenControls getControllerLayerSizeFactor:oscButtonLayer];
buttonState.backgroundAlpha = oscButtonLayer.opacity;
NSLog(@"oscLayerName: %@, opacity: %f, ", oscButtonLayer.name, buttonState.backgroundAlpha);
// buttonState.oscLayerSizeFactor = oscButtonLayer.bounds;
NSData *buttonStateEncoded = [NSKeyedArchiver archivedDataWithRootObject:buttonState requiringSecureCoding:YES error:nil];
[buttonStatesEncoded addObject: buttonStateEncoded];
}
// save on-screen widget views (keyboard & mouse command) as buttonstate:
for(OnScreenWidgetView* widgetView in OnScreenWidgetViews){
OnScreenButtonState *buttonState = [[OnScreenButtonState alloc] initWithButtonName:widgetView.keyString buttonType:CustomOnScreenWidget andPosition:widgetView.center];
buttonState.alias = widgetView.keyLabel;
buttonState.timestamp = widgetView.timestamp;
buttonState.widthFactor = widgetView.widthFactor;
buttonState.heightFactor = widgetView.heightFactor;
buttonState.backgroundAlpha = widgetView.backgroundAlpha;
buttonState.borderWidth = widgetView.borderWidth;
buttonState.sensitivityFactor = widgetView.sensitivityFactor;
buttonState.stickIndicatorOffset = widgetView.stickIndicatorOffset;
NSData *buttonStateEncoded = [NSKeyedArchiver archivedDataWithRootObject:buttonState requiringSecureCoding:YES error:nil];
[buttonStatesEncoded addObject: buttonStateEncoded];
}
return buttonStatesEncoded;
}
#pragma mark - Queries
- (BOOL) profileNameAlreadyExist:(NSString*)name {
NSMutableArray *profiles = [self getAllProfiles];
/* Iterate through the decoded profiles and return 'YES' if one of the profiles' 'name' properties equals the 'name' passed into this method */
for (OSCProfile *profile in profiles) {
if ([profile.name isEqualToString:name]) {
return YES;
}
}
return NO;
}
@end
@@ -0,0 +1,52 @@
//
// OnScreenButtonState.h
// Moonlight
//
// Created by Long Le on 10/20/22.
// Copyright © 2022 Moonlight Game Streaming Project. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
This object is used to save positional and visibility information for any particular on screen virtual controller button.
We are able to associate this 'OnScreenButtonState' object and its corresponding CALayer onscreen controller button by setting their 'name' properties equal; in our particular case we give them descriptive names such as 'aButton', 'upButton', 'leftStick', etc. By keeping references to the 19 on screen controller buttons (CALayers) in an array, and creating 19 'OnScreenButtonState' objects with names corresponding to these 19 CALayers and keeping them in an array, we can iterate through both arrays to find OSC buttons (CALayers) with the same name as one of the 'OnScreenButtonStateObjects' and then set the CALayer on screen control button's 'position' and 'hidden' property according to the value of the 'OnScreenButtonState' objects 'position' and 'isHidden' properties.
Naturally we would like the user to be able to save their controller layout configurations so that they can load them between app launches, so we adopt encoding/decoding related protocols so we encode these 'OnScreenButtonState' objects and save them to NSUserDefaults
*/
// OS Button state info obj to be modified
@interface OnScreenButtonState : NSObject <NSCoding, NSSecureCoding>
@property NSString *name;
@property NSString *alias;
@property CGPoint position;
@property (nonatomic, assign) BOOL isHidden;
@property (nonatomic, assign) uint8_t buttonType;
@property (nonatomic, assign) NSTimeInterval timestamp;
@property (nonatomic, assign) CGFloat widthFactor; // for OnScreenWidgetView
@property (nonatomic, assign) CGFloat heightFactor; // for OnScreenWidgetView
@property (nonatomic, assign) CGFloat borderWidth; // for OnScreenWidgetView
@property (nonatomic, assign) CGFloat sensitivityFactor; // for OnScreenWidgetView
@property (nonatomic, assign) CGFloat stickIndicatorOffset; // for OnScreenWidgetView
@property (nonatomic, assign) CGFloat oscLayerSizeFactor; // for OnScreenController CALayer
@property (nonatomic, assign) CGFloat backgroundAlpha; // for OnScreenController CALayer
// @property (nonatomic, assign) BOOL hasValidPosition;
typedef NS_ENUM(NSInteger, OnScreenButtonType) {
LegacyOscButton,
CustomOnScreenWidget
};
- (id) initWithButtonName:(NSString*)name buttonType:(uint8_t)buttonType andPosition:(CGPoint)position;
+ (BOOL) supportsSecureCoding;
- (void) encodeWithCoder:(NSCoder*)encoder;
- (id) initWithCoder:(NSCoder*)decoder;
@end
NS_ASSUME_NONNULL_END
@@ -0,0 +1,64 @@
//
// OnScreenButtonState.m
// Moonlight
//
// Created by Long Le on 10/20/22.
// Copyright © 2022 Moonlight Game Streaming Project. All rights reserved.
//
#import "OnScreenButtonState.h"
@implementation OnScreenButtonState
- (id) initWithButtonName:(NSString*)name buttonType:(uint8_t)buttonType andPosition:(CGPoint)position {
if ((self = [self init])) {
self.name = name;
//self.isHidden = isHidden;
self.position = position;
self.buttonType = buttonType;
}
return self;
}
+ (BOOL) supportsSecureCoding {
return YES;
}
- (void) encodeWithCoder:(NSCoder*)encoder {
[encoder encodeObject:self.name forKey:@"name"];
[encoder encodeObject:self.alias forKey:@"alias"];
[encoder encodeDouble:self.timestamp forKey:@"timestamp"];
[encoder encodeInt:self.buttonType forKey:@"buttonType"];
[encoder encodeCGPoint:self.position forKey:@"position"];
[encoder encodeBool:self.isHidden forKey:@"isHidden"];
[encoder encodeFloat:self.widthFactor forKey:@"widthFactor"];
[encoder encodeFloat:self.heightFactor forKey:@"heightFactor"];
[encoder encodeFloat:self.sensitivityFactor forKey:@"velocityFactor"];
[encoder encodeFloat:self.stickIndicatorOffset forKey:@"stickIndicatorOffset"];
[encoder encodeFloat:self.oscLayerSizeFactor forKey:@"oscLayerSizeFactor"];
[encoder encodeFloat:self.backgroundAlpha forKey:@"backgroundAlpha"];
[encoder encodeFloat:self.borderWidth forKey:@"borderWidth"];
}
- (id) initWithCoder:(NSCoder*)decoder {
if (self = [super init]) {
self.name = [decoder decodeObjectForKey:@"name"];
self.alias = [decoder decodeObjectForKey:@"alias"];
self.timestamp = [decoder decodeDoubleForKey:@"timestamp"];
self.buttonType = [decoder decodeIntForKey:@"buttonType"];
self.position = [decoder decodeCGPointForKey:@"position"];
self.isHidden = [decoder decodeBoolForKey:@"isHidden"];
self.widthFactor = [decoder decodeFloatForKey:@"widthFactor"];
self.heightFactor = [decoder decodeFloatForKey:@"heightFactor"];
self.sensitivityFactor = [decoder decodeFloatForKey:@"velocityFactor"];
self.stickIndicatorOffset = [decoder decodeFloatForKey:@"stickIndicatorOffset"];
self.oscLayerSizeFactor = [decoder decodeFloatForKey:@"oscLayerSizeFactor"];
self.backgroundAlpha = [decoder decodeFloatForKey:@"backgroundAlpha"];
self.borderWidth = [decoder decodeFloatForKey:@"borderWidth"];
}
return self;
}
@end
@@ -0,0 +1,28 @@
//
// CustomTapGestureRecognizer.h
// Moonlight
//
// Created by ZWM on 2024/5/15.
// Copyright © 2024 Moonlight Game Streaming Project. All rights reserved.
//
#ifndef CustomTapGestureRecognizer_h
#define CustomTapGestureRecognizer_h
@interface CustomTapGestureRecognizer : UIGestureRecognizer{
CGFloat lowestTouchPointYCoord;
}
@property (nonatomic, assign) uint8_t numberOfTouchesRequired;
@property (nonatomic, assign) bool immediateTriggering; // if enabled, trigger the signal on touchesBegan stage.
@property (nonatomic, assign) double tapDownTimeThreshold; // tap down threshold in seconds.
@property (nonatomic, assign) bool isOnScreenControllerBeingPressed; // will be set by the onscreencontrol class
//@property (nonatomic, assign) bool haveOnScreenButtonsOnStreamview;
@property (nonatomic, readonly) CGFloat lowestTouchPointHeight;
@property (nonatomic, readonly) bool gestureCaptured;
@property (nonatomic, readonly) NSTimeInterval gestureCapturedTime;
@end
#endif /* CustomTapGestureRecognizer_h */
@@ -0,0 +1,112 @@
//
// CustomTapGestureRecognizer.m
// Moonlight
//
// Created by ZWM on 2024/5/15.
// Copyright © 2024 Moonlight Game Streaming Project. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIGestureRecognizerSubclass.h>
#import "CustomTapGestureRecognizer.h"
#import "Moonlight-Swift.h"
// The most accurate & reliable tap gesture recognizer of iOS:
// - Almost 100% recoginition rate. UITapGestureRecognizer of Apple API fails frequently, just a piece of crap.
// - When immediateTriggering is set to false (for native multi-touch):
// Gesture signal will be triggered on touchesEnded stage, multi finger touch operations will not be interrupted by the arising keyboard.
// Instances of different [numberOfTouchesRequired] barely compete with each other, for example, the chance of 3-finger gesture get triggered by 4 or 5 finger tap is very small.
// - Set property immediateTriggering to true, to ensure the priority of keyboard toggle in non-native touch mode, in compete with 2-finger gestures.
// - This recognizer also provides properties like gestureCapturedTime, to be accessed outside the class for useful purpose.
@implementation CustomTapGestureRecognizer
static CGFloat screenHeightInPoints;
static CGFloat screenWidthInPoints;
- (instancetype)initWithTarget:(nullable id)target action:(nullable SEL)action {
self = [super initWithTarget:target action:action];
screenHeightInPoints = CGRectGetHeight([[UIScreen mainScreen] bounds]);
screenWidthInPoints = CGRectGetWidth([[UIScreen mainScreen] bounds]);
lowestTouchPointYCoord = 0.0;
_numberOfTouchesRequired = 3;
_immediateTriggering = false;
_tapDownTimeThreshold = 0.3;
_gestureCaptured = false;
_isOnScreenControllerBeingPressed = false;
return self;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// Check if the number of touches and taps meets the required criteria
if ([[event allTouches] count] == _numberOfTouchesRequired) {
_gestureCapturedTime = CACurrentMediaTime();
_gestureCaptured = true;
for(UITouch *touch in [event allTouches]){
if(lowestTouchPointYCoord < [touch locationInView:self.view].y) lowestTouchPointYCoord = [touch locationInView:self.view].y;
}
// this mechanism is deprecated.
/* if(_numberOfTouchesRequired == 2){
NSArray *twoTouches = [[event allTouches] allObjects];
_areVirtualControllerTaps = fabs([twoTouches[1] locationInView:self.view].x - [twoTouches[0] locationInView:self.view].x) > screenWidthInPoints/3;
} */
_lowestTouchPointHeight = screenHeightInPoints - lowestTouchPointYCoord;
if(_immediateTriggering){
lowestTouchPointYCoord = 0.0; //reset for next recoginition
self.state = UIGestureRecognizerStateRecognized;
return;
}
self.state = UIGestureRecognizerStatePossible;
}
if ([[event allTouches] count] > _numberOfTouchesRequired) {
_gestureCaptured = false;
self.state = UIGestureRecognizerStateFailed;
}
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
// [super touchesEnded:touches withEvent:event];
if(_immediateTriggering) return;
uint8_t allTouchesCount = [[event allTouches] count];
if(allTouchesCount > _numberOfTouchesRequired) {
_gestureCaptured = false;
self.state = UIGestureRecognizerStateFailed;
}
else if(_gestureCaptured && allTouchesCount == [touches count] && !_isOnScreenControllerBeingPressed && ![self isOnScreenWidgetViewBeingPressed]){ //must exclude virtual controller & onscreen button taps here to prevent stucked button, _areVirtualControllerTaps flag is set by onscreencontrols, containOnScreenButtonsTaps will be returned by iterating all widget views in streamframeview
_gestureCaptured = false; //reset for next recognition
if((CACurrentMediaTime() - _gestureCapturedTime) < _tapDownTimeThreshold){
lowestTouchPointYCoord = 0.0; //reset for next recognition
self.state = UIGestureRecognizerStateRecognized;
}
}
if (allTouchesCount == [touches count]) _isOnScreenControllerBeingPressed = false; // need to reset this flag anyway, when all fingers are lefting
}
// it was a not a perfect choice to code OnScreenWidgetView in Swift...
// we're unable to import this class to swift codebase by the bridging header,and have to exlucde the onscreen button taps here
// by iterating every widget view instances. but it's basically ok since the number of widgetViews are always limited.
// and this method will only be called when the recognizer is active & and the taps passes all the checks and is about to ge triggered.
- (bool)isOnScreenWidgetViewBeingPressed {
NSTimeInterval allFingersTapDownTime = CACurrentMediaTime() - _gestureCapturedTime; //RIGHTCLICK_TAP_DOWN_TIME_THRESHOLD_S has been passed to this recognizer as _tapDownTimeShreshold, we'll decide how to deal with pressed flag of on-screen widget views based on tapDownTime
bool gotOneWidgetPressed = false;
// the tap gestures are all added to the streamFrameTopLayerView, where all the OnScreenWidgetViews are added. so we can iterate them in this way:
for(UIView* view in self.view.subviews){
if ([view isKindOfClass:[OnScreenWidgetView class]]) {
OnScreenWidgetView* widgetView = (OnScreenWidgetView*) view;
if(gotOneWidgetPressed && allFingersTapDownTime <= _tapDownTimeThreshold){ // once we have just 1 button pressed already,& the tapDownTime has not exceeded the threshold, we'll reset pressed flag for the all the widgetViews.
widgetView.pressed = false; //reset the flag for widgetView
continue;
}
if(widgetView.pressed){
gotOneWidgetPressed = true; //got one button pressed
if(allFingersTapDownTime <= _tapDownTimeThreshold) widgetView.pressed = false; // reset the flag for current widgetView if tapDowntime is still within the threshold, if not, leave the flag for mouse scroller gesture in relative touch mode.
}
}
}
return gotOneWidgetPressed;
}
@end
+3
View File
@@ -248,6 +248,9 @@
case UIKeyboardHIDUsageKeyboardRightAlt:
keyCode = 0xA5;
break;
case UIKeyboardHIDUsageKeyboardErrorUndefined:
NSLog(@"Undefined key pressed");
return false;
case 669: // This value corresponds to the "Globe" or "Language" key on most Apple branded iPad keyboards.
keyCode = 0x1B; // This value corresponds to "Escape", which is missing from most Apple branded iPad keyboards.
break;
+25
View File
@@ -0,0 +1,25 @@
//
// NativeTouchHandler.h
// Moonlight
//
// Created by ZWM on 2024/6/16.
// Copyright © 2024 Moonlight Game Streaming Project. All rights reserved.
//
#import "StreamView.h"
#import "DataManager.h"
#include <Limelight.h>
NS_ASSUME_NONNULL_BEGIN
@interface NativeTouchHandler : UIResponder
@property (nonatomic, assign) NSMutableSet<UITouch *>* touchesCapturedByOnScreenButtons;
- (id)initWithView:(StreamView* )view andSettings:(TemporarySettings*)settings;
@end
NS_ASSUME_NONNULL_END
+164
View File
@@ -0,0 +1,164 @@
//
// NativeTouchHandler.m
// Moonlight
//
// Created by ZWM on 2024/6/16.
// Copyright © 2024 Moonlight Game Streaming Project. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "NativeTouchHandler.h"
#import "NativeTouchPointer.h"
#import "OnScreenControls.h"
#import "StreamView.h"
#include <Limelight.h>
@implementation NativeTouchHandler {
StreamView* streamView;
TemporarySettings* currentSettings;
bool activateCoordSelector;
bool touchPointSpawnedAtUpperScreenEdge;
// Use a Dictionary to store UITouch object's memory address as key, and pointerId as value,字典存放UITouch对象地址和pointerId映射关系
// pointerId will be generated from a pre-defined pool
// Use a NSSet store active pointerId,
NSMutableDictionary *pointerIdDict; //pointerId Dict for active touches.
NSMutableSet<NSNumber *> *activePointerIds; //pointerId Set for active touches.
NSMutableSet<NSNumber *> *pointerIdPool; //pre-defined pool of pointerIds.
NSMutableSet<NSNumber *> *unassignedPointerIds;
CGFloat slideGestureVerticalThreshold;
CGFloat screenWidthWithThreshold;
CGFloat EDGE_TOLERANCE;
}
- (id)initWithView:(StreamView*)view andSettings:(TemporarySettings*)settings{
self = [super init];
self->streamView = view;
self->currentSettings = settings;
self->activateCoordSelector = currentSettings.pointerVelocityModeDivider.floatValue != 1.0;
// self->notPureNativeTouchMode = !(currentSettings.touchMode.intValue == PURE_NATIVE_TOUCH);
self->pointerIdDict = [NSMutableDictionary dictionary];
self->pointerIdPool = [NSMutableSet set];
for (uint8_t i = 0; i <= 10; i++) { //ipadOS supports upto 11 finger touches
[self->pointerIdPool addObject:@(i)];
}
self->activePointerIds = [NSMutableSet set];
// self->excludedPointerIds = [[NSMutableSet alloc] init];
self->touchPointSpawnedAtUpperScreenEdge = false;
EDGE_TOLERANCE = 15.0;
slideGestureVerticalThreshold = CGRectGetHeight([[UIScreen mainScreen] bounds]) * 0.4;
screenWidthWithThreshold = CGRectGetWidth([[UIScreen mainScreen] bounds]) - EDGE_TOLERANCE;
[NativeTouchPointer setPointerVelocityDivider:settings.pointerVelocityModeDivider.floatValue];
[NativeTouchPointer setPointerVelocityFactor:settings.touchPointerVelocityFactor.floatValue];
[NativeTouchPointer initContextWithView:self->streamView];
//_touchesCapturedByOnScreenButtons = [[NSMutableSet alloc] init];
return self;
}
// generate & populate pointerId into NSDict & NSSet, called in touchesBegan
- (void)handleTouchDown:(UITouch*)touch{
//populate pointerId
uintptr_t memAddrValue = (uintptr_t)touch;
unassignedPointerIds = [pointerIdPool mutableCopy]; //reset unassignedPointerIds
[unassignedPointerIds minusSet:activePointerIds];
uint8_t pointerId = [[unassignedPointerIds anyObject] unsignedIntValue];
[pointerIdDict setObject:@(pointerId) forKey:@(memAddrValue)];
[activePointerIds addObject:@(pointerId)];
//check if touch point is spawned on the left or right upper half screen edges, event to remote PC. this is for better handling in-stream slide gesture
CGPoint initialPoint = [touch locationInView:self->streamView];
if(initialPoint.y < slideGestureVerticalThreshold && (initialPoint.x < EDGE_TOLERANCE || initialPoint.x > screenWidthWithThreshold)) {
self->touchPointSpawnedAtUpperScreenEdge = true;
// [excludedPointerIds addObject:@(pointerId)];
}
}
// remove pointerId in touchesEnded or touchesCancelled
- (void)removePointerId:(UITouch*)touch{
uintptr_t memAddrValue = (uintptr_t)touch;
NSNumber* pointerIdObj = [pointerIdDict objectForKey:@(memAddrValue)];
if(pointerIdObj != nil){
[activePointerIds removeObject:pointerIdObj];
[pointerIdDict removeObjectForKey:@(memAddrValue)];
// if([excludedPointerIds containsObject:pointerIdObj]) [excludedPointerIds removeObject:pointerIdObj]; // remove pointer id from excludedPointerId NSSet
}
}
// 从字典中获取UITouch事件对应的pointerId
// called in method of sendTouchEvent
- (uint8_t) retrievePointerIdFromDict:(UITouch*)touch{
return [[pointerIdDict objectForKey:@((uintptr_t)touch)] unsignedIntValue];
}
- (void)sendTouchEvent:(UITouch*)touch withTouchtype:(uint8_t)touchType{
if(touchPointSpawnedAtUpperScreenEdge) return; // we're done here. this touch event will not be sent to the remote PC. and this must be checked after coord selector finishes populating new relative coords, or the app will crash!
CGPoint targetCoords;
// NSLog(@"excluded count: %d", (uint32_t)[excludedPointerIds count]);
if(activateCoordSelector && touch.phase == UITouchPhaseMoved) targetCoords = [NativeTouchPointer selectCoordsFor:touch]; // coordinates of touch pointer replaced to relative ones here.
else targetCoords = [touch locationInView:streamView];
CGPoint location = [streamView adjustCoordinatesForVideoArea:targetCoords];
CGSize videoSize = [streamView getVideoAreaSize];
CGFloat normalizedX = location.x / videoSize.width;
CGFloat normalizedY = location.y / videoSize.height;
uint8_t pointerId = [self retrievePointerIdFromDict:touch];
if([NativeTouchPointer getPointerObjFromDict:touch].needResetCoords){ // access whether the current pointer has reached the boundary, and need a coord reset.
LiSendTouchEvent(LI_TOUCH_EVENT_UP, pointerId, normalizedX, normalizedY, 0, 0, 0, 0); //event must sent from the lowest level directy by LiSendTouchEvent to simulate continous dragging to another point on screen
LiSendTouchEvent(LI_TOUCH_EVENT_DOWN, pointerId, 0.3, 0.4, 0, 0, 0, 0);
}else LiSendTouchEvent(touchType, pointerId, normalizedX, normalizedY,(touch.force / touch.maximumPossibleForce) / sin(touch.altitudeAngle),0.0f, 0.0f,[streamView getRotationFromAzimuthAngle:[touch azimuthAngleInView:streamView]]);
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch* touch in touches){
// continue to the next loop if current touch is already captured by OSC. works only in regular native touch
if([OnScreenControls.touchAddrsCapturedByOnScreenControls containsObject:@((uintptr_t)touch)]) continue;
[self handleTouchDown:touch]; //generate & populate pointerId
if(activateCoordSelector) [NativeTouchPointer populatePointerObjIntoDict:touch];
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_DOWN];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
// NSLog(@"captured by OSB touches: %d", (uint32_t)[OnScreenControls.touchAddrsCapturedByOnScreenControls count]);
for (UITouch* touch in touches){
// continue to the next loop if current touch is already captured by OSC. works only in regular native touch
if([OnScreenControls.touchAddrsCapturedByOnScreenControls containsObject:@((uintptr_t)touch)]) continue;
if(activateCoordSelector) [NativeTouchPointer updatePointerObjInDict:touch];
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_MOVE];
[[NativeTouchPointer getPointerObjFromDict:touch] doesNeedResetCoords]; // execute the judging of doesReachBoundary for current pointer instance. (happens after the event is sent to Sunshine service)
}
return;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
for (UITouch* touch in touches){
// continue to the next loop if current touch is already captured by OSC. works only in regular native touch
if([OnScreenControls.touchAddrsCapturedByOnScreenControls containsObject:@((uintptr_t)touch)]) continue;
[self sendTouchEvent:touch withTouchtype:LI_TOUCH_EVENT_UP]; //send touch event before remove pointerId
[self removePointerId:touch]; //then remove pointerId
if(activateCoordSelector) [NativeTouchPointer removePointerObjFromDict:touch];
}
if(touchPointSpawnedAtUpperScreenEdge && [[event allTouches] count] == [touches count]) touchPointSpawnedAtUpperScreenEdge = false;
return;
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchesEnded:touches withEvent:event];
}
@end
+37
View File
@@ -0,0 +1,37 @@
//
// NativeTouchPointer.h
// Moonlight
//
// Created by ZWM on 2024/5/14.
// Copyright © 2024 Moonlight Game Streaming Project. All rights reserved.
//
#import "StreamView.h"
#import "NativeTouchHandler.h"
NS_ASSUME_NONNULL_BEGIN
@interface NativeTouchPointer : NSObject
@property (nonatomic, assign) bool needResetCoords;
+ (void)initContextWithView:(StreamView *)view;
+ (void)setPointerVelocityDivider:(CGFloat)dividerLocation;
+ (void)setPointerVelocityFactor:(CGFloat)velocityFactor;
+ (NativeTouchPointer* )getPointerObjFromDict:(UITouch*)touch;
- (bool)doesNeedResetCoords;
+ (void)populatePointerObjIntoDict:(UITouch*)touch;
+ (void)removePointerObjFromDict:(UITouch*)touch;
+ (void)updatePointerObjInDict:(UITouch *)touch;
+ (CGPoint)selectCoordsFor:(UITouch *)touch;
- (instancetype)initWithTouch:(UITouch *)touch;
@end
NS_ASSUME_NONNULL_END
+125
View File
@@ -0,0 +1,125 @@
//
// NativeTouchPointer.m
// Moonlight
//
// Created by ZWM on 2024/5/14.
// Copyright © 2024 Moonlight Game Streaming Project. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "NativeTouchPointer.h"
#include <Limelight.h>
// native touch pointer ojbect that stores & manipulates touch coordinates
static NSMutableDictionary *pointerObjDict;
static CGFloat pointerVelocityFactor;
static CGFloat pointerVelocityDivider;
static CGFloat pointerVelocityDividerLocationByPoints;
static CGFloat streamViewHeight;
static CGFloat streamViewWidth;
static CGFloat fixedResetCoordX;
static CGFloat fixedResetCoordY;
static StreamView* _streamView;
@implementation NativeTouchPointer{
CGPoint initialPoint;
CGPoint latestPoint;
CGPoint previousPoint;
CGPoint latestRelativePoint;
CGPoint previousRelativePoint;
bool useRelativeCoords;
}
- (instancetype)initWithTouch:(UITouch *)touch{
self = [super init];
self->initialPoint = [touch locationInView:_streamView];
self->latestPoint = self->initialPoint;
self->latestRelativePoint = self->initialPoint;
return self;
}
- (bool)doesNeedResetCoords{
bool boundaryReached = (latestRelativePoint.x > streamViewWidth || latestRelativePoint.x < 0.0f || latestRelativePoint.y > streamViewHeight || latestRelativePoint.y < 0.0f);
bool withinExcludedArea = (initialPoint.x > streamViewWidth * (0.75) && initialPoint.x < streamViewWidth) && (initialPoint.y > streamViewHeight * (0.8) && initialPoint.y < streamViewHeight);
_needResetCoords = (pointerVelocityFactor > 1.0f) && self->useRelativeCoords && boundaryReached && !withinExcludedArea;
// boundary detection & coordinates reset to the specific point for HK:StarTrail(needs a very high pointer velocity)
// must exclude touch pointer that uses native coords instead of relative ones.
// also exclude touch pointer created within the bottom right corner
return _needResetCoords;
}
- (void)updatePointerCoords:(UITouch *)touch{
previousPoint = latestPoint;
latestPoint = [touch locationInView:_streamView];
previousRelativePoint = latestRelativePoint;
if(_needResetCoords){// boundary detection & coordinates reset to the central screen point for HK:StarTrail(needs a very high pointer velocity); // boundary detection & coordinates reset to specific point for HK:StarTrail(needs a very high pointer velocity)
previousRelativePoint.x = fixedResetCoordX;
previousRelativePoint.y = fixedResetCoordY;
}
latestRelativePoint.x = previousRelativePoint.x + pointerVelocityFactor * (latestPoint.x - previousPoint.x);
latestRelativePoint.y = previousRelativePoint.y + pointerVelocityFactor * (latestPoint.y - previousPoint.y);
}
+ (void)initContextWithView:(StreamView *)view {
_streamView = view;
streamViewWidth = _streamView.frame.size.width;
fixedResetCoordX = streamViewWidth * 0.3;
streamViewHeight = _streamView.frame.size.height;
fixedResetCoordY = streamViewHeight * 0.4;
NSLog(@"stream wdith %f, stream height %f", streamViewWidth, streamViewHeight);
pointerObjDict = [NSMutableDictionary dictionary];
pointerVelocityDividerLocationByPoints = CGRectGetWidth([[UIScreen mainScreen] bounds]) * pointerVelocityDivider;
NSLog(@"pointerVelocityDivider: %.2f", pointerVelocityDivider);
NSLog(@"pointerVelocityFactor: %.2f", pointerVelocityFactor);
NSLog(@"pointerVelocityDividerLocationByPoints: %.2f", pointerVelocityDividerLocationByPoints);
}
+ (void)setPointerVelocityDivider:(CGFloat)dividerLocation{
pointerVelocityDivider = dividerLocation;
}
+ (void)setPointerVelocityFactor:(CGFloat)velocityFactor{
pointerVelocityFactor = velocityFactor;
}
+ (void)populatePointerObjIntoDict:(UITouch*)touch{
[pointerObjDict setObject:[[NativeTouchPointer alloc] initWithTouch:touch] forKey:@((uintptr_t)touch)];
}
+ (void)removePointerObjFromDict:(UITouch*)touch{
uintptr_t memAddrValue = (uintptr_t)touch;
NativeTouchPointer* pointer = [pointerObjDict objectForKey:@(memAddrValue)];
if(pointer != nil){
[pointerObjDict removeObjectForKey:@(memAddrValue)];
}
}
+ (NativeTouchPointer* )getPointerObjFromDict:(UITouch*)touch{
return [pointerObjDict objectForKey:@((uintptr_t)touch)];
}
+ (void)updatePointerObjInDict:(UITouch *)touch{
[[pointerObjDict objectForKey:@((uintptr_t)touch)] updatePointerCoords:touch];
}
+ (CGPoint)selectCoordsFor:(UITouch *)touch{
NativeTouchPointer *pointer = [pointerObjDict objectForKey:@((uintptr_t)touch)];
if(pointer == nil) return CGPointMake(0, 0);
if((pointer -> initialPoint).x > pointerVelocityDividerLocationByPoints){ //if first contact coords locates on the right side of divider.
pointer -> useRelativeCoords = true;
return pointer->latestRelativePoint;
}
else{
pointer -> useRelativeCoords = false;
return[touch locationInView:_streamView];
}
}
@end
+104 -4
View File
@@ -7,30 +7,130 @@
//
#import <Foundation/Foundation.h>
#import "ControllerSupport.h"
#import "CustomTapGestureRecognizer.h"
#import "OSCProfile.h"
#define MIN_CONTROLLER_LAYER_ALPHA 0.23
@class ControllerSupport;
@class StreamConfiguration;
@interface OnScreenControls : NSObject
// sending self as an instance to OnScreenWidgetView or other related classes
@protocol InstanceProviderDelegate <NSObject>
- (void)getOnScreenControlsInstance:(id)sender; // Renamed delegate method
@end
static const float D_PAD_DIST = 10;
static const float BUTTON_DIST = 20;
@interface OnScreenControls : NSObject{
@protected
CGFloat _leftStickSizeFactor;
CGFloat _leftStickOpacity;
CGFloat _rightStickSizeFactor;
CGFloat _rightStickOpacity;
CGFloat _dPadSizeFactor;
}
typedef NS_ENUM(NSInteger, OnScreenControlsLevel) {
OnScreenControlsLevelOff,
OnScreenControlsLevelAuto,
OnScreenControlsLevelSimple,
OnScreenControlsLevelFull,
OnScreenControlsLevelCustom,
OnScreenControlsLevelAuto, // move it here instead of delete , to ensure integrity of the codes
// Internal levels selected by ControllerSupport
OnScreenControlsLevelAutoGCGamepad,
OnScreenControlsLevelAutoGCExtendedGamepad,
OnScreenControlsLevelAutoGCExtendedGamepadWithStickButtons
};
@property (nonatomic, weak) id<InstanceProviderDelegate> delegate; // Delegate property
- (void)sendInstance;
// @property (nonatomic, assign) CustomTapGestureRecognizer* mouseRightClickTapRecognizer; // this object will be passed to onscreencontrols class for areVirtualControllerTaps flag setting
@property (nonatomic, assign) bool isLayingOut;
//@property (nonatomic) NSMutableSet<UITouch* >* touchesCapturedByOnScreenButtons;
@property (nonatomic, assign) CGRect standardRoundButtonBounds;
@property (nonatomic, assign) CGRect standardRectangleButtonBounds;
@property (nonatomic, assign) CGRect standardStickBounds;
@property (nonatomic, assign) CGRect standardStickBackgroundBounds;
@property (nonatomic, assign) CGRect standardUpDownButtonBounds;
@property (nonatomic, assign) CGRect standardLeftRightButtonBounds;
@property (nonatomic, assign) CGPoint leftStickCenter;
@property (nonatomic, assign) CGPoint rightStickCenter;
@property CALayer* _aButton;
@property (nonatomic, assign) CGFloat aButtonSizeFactor;
@property CALayer* _bButton;
@property (nonatomic, assign) CGFloat bButtonSizeFactor;
@property CALayer* _xButton;
@property (nonatomic, assign) CGFloat xButtonSizeFactor;
@property CALayer* _yButton;
@property (nonatomic, assign) CGFloat yButtonSizeFactor;
@property CALayer* _startButton;
@property CALayer* _selectButton;
@property CALayer* _r1Button;
@property CALayer* _r2Button;
@property CALayer* _r3Button;
@property CALayer* _l1Button;
@property CALayer* _l2Button;
@property CALayer* _l3Button;
@property CALayer* _upButton;
@property CALayer* _downButton;
@property CALayer* _leftButton;
@property CALayer* _rightButton;
@property CALayer* _leftStickBackground;
@property CALayer* _leftStick;
@property CALayer* _rightStickBackground;
@property CALayer* _rightStick;
@property CALayer* _dPadBackground; // parent layer that contains each individual dPad button so user can drag them around the screen together
@property float D_PAD_CENTER_X;
@property float D_PAD_CENTER_Y;
@property OnScreenControlsLevel _level;
@property NSMutableArray *OSCButtonLayers;
+ (NSMutableSet* )touchAddrsCapturedByOnScreenControls;
+ (void) testMethod;
- (void) sendRightStickTouchPadEvent:(CGFloat) stickX : (CGFloat) stickY;
- (void) clearRightStickTouchPadFlag;
- (void) sendLeftStickTouchPadEvent:(CGFloat) stickX : (CGFloat) stickY;
- (void) clearLeftStickTouchPadFlag;
- (void) pressDownControllerButton: (int)flag;
- (void) releaseControllerButton: (int)flag;
- (void) updateLeftTrigger:(unsigned char)input;
- (void) updateRightTrigger:(unsigned char)input;
- (id) initWithView:(UIView*)view controllerSup:(ControllerSupport*)controllerSupport streamConfig:(StreamConfiguration*)streamConfig;
- (BOOL) handleTouchDownEvent:(NSSet*)touches;
- (BOOL) handleTouchUpEvent:(NSSet*)touches;
- (BOOL) handleTouchMovedEvent:(NSSet*)touches;
- (void) setLevel:(OnScreenControlsLevel)level;
- (OnScreenControlsLevel) getLevel;
- (void) show;
- (void) setupComplexControls;
- (void) drawButtons;
- (void) drawBumpers;
- (void) updateControls;
- (OnScreenControlsLevel) getLevel;
- (void) setDPadCenter;
- (void) setAnalogStickPositions;
- (void) positionAndResizeSingleControllerLayers;
- (void) resizeControllerLayerWith:(CALayer*)layer and:(CGFloat)sizeFactor;
- (void) adjustControllerLayerOpacityWith:(CALayer*)layer and:(CGFloat)alpha;
+ (CGFloat) getControllerLayerSizeFactor:(CALayer*)layer;
- (CGFloat) getControllerLayerOpacity:(CALayer*)layer;
@end

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