diff --git a/CitraTouchControl/App.config b/CitraTouchControl/App.config
index f9f6abd..f216d88 100644
--- a/CitraTouchControl/App.config
+++ b/CitraTouchControl/App.config
@@ -55,6 +55,9 @@
50
+
+ False
+
diff --git a/CitraTouchControl/GlobalVars.cs b/CitraTouchControl/GlobalVars.cs
index cc55900..dffae86 100644
--- a/CitraTouchControl/GlobalVars.cs
+++ b/CitraTouchControl/GlobalVars.cs
@@ -19,5 +19,6 @@ class GlobalVars
internal static bool IsTouchEnabled = false;
internal static bool AreControlsHidden = false;
internal static int KeyPressDuration = 50;
+ internal static bool IsTapOnly = false;
}
}
diff --git a/CitraTouchControl/MainWindow.xaml.cs b/CitraTouchControl/MainWindow.xaml.cs
index 23f20bb..345db43 100644
--- a/CitraTouchControl/MainWindow.xaml.cs
+++ b/CitraTouchControl/MainWindow.xaml.cs
@@ -72,6 +72,7 @@ private void Window_Loaded(object sender, RoutedEventArgs e)
GlobalVars.AreControlsHidden = true;
ToggleControls(true);
}
+ GlobalVars.IsTapOnly = Properties.Settings.Default.IsTapOnly;
GlobalVars.KeyPressDuration = Properties.Settings.Default.KeyPressDuration;
}
@@ -88,6 +89,9 @@ private void controlButton_TouchDown(object sender, TouchEventArgs e)
///
private void controlButton_TouchUp(object sender, TouchEventArgs e)
{
+ // ignore TouchUp in TapOnly mode
+ if (GlobalVars.IsTapOnly)
+ return;
e.Handled = true;
ControlButtonUp(sender);
}
@@ -97,6 +101,9 @@ private void controlButton_TouchUp(object sender, TouchEventArgs e)
///
private void controlButton_TouchLeave(object sender, TouchEventArgs e)
{
+ // ignore TouchLeave in TapOnly mode
+ if (GlobalVars.IsTapOnly)
+ return;
ControlButtonUp(sender);
}
@@ -140,16 +147,24 @@ private void ControlButtonDown(object sender)
// actually send keydown
SendMessage(citraMainControlHwnd, WM_KEYDOWN, key, IntPtr.Zero);
- // add pressed key to list
- if (!pressedControls.Contains(key))
- pressedControls.Add(key);
+ // if TapMode is tap only then send KeyUp directly
+ if (GlobalVars.IsTapOnly)
+ {
+ SendButtonUp(key);
+ }
+ else
+ {
+ // add pressed key to list
+ if (!pressedControls.Contains(key))
+ pressedControls.Add(key);
+ }
}
///
/// Control leave handler which supports asynchronous waiting.
///
/// The caller UI element.
- private async void ControlButtonUp(object sender)
+ private void ControlButtonUp(object sender)
{
// get calling button
var button = (sender as System.Windows.Controls.Image);
@@ -165,6 +180,15 @@ private async void ControlButtonUp(object sender)
return;
pressedControls.Remove(key);
+ SendButtonUp(key);
+ }
+
+ ///
+ /// Sends a KeyButtonUp after a short delay.
+ ///
+ /// The IntPtr of the key.
+ private async void SendButtonUp(IntPtr key)
+ {
// wait 50ms asynchronously, then send keyup of selected button
await Task.Delay(GlobalVars.KeyPressDuration);
SendMessage(citraMainControlHwnd, WM_KEYUP, key, IntPtr.Zero);
@@ -332,37 +356,6 @@ private struct RECT
#region UNUSED
/*
- private const int WS_EX_TRANSPARENT = 0x00000020;
- private const int GWL_EXSTYLE = (-20);
-
- [DllImport("user32.dll")]
- private static extern Int32 GetWindowLong(IntPtr hwnd, int index);
-
- [DllImport("user32.dll")]
- private static extern Int32 SetWindowLong(IntPtr hwnd, int index, int newStyle);
-
- public static void SetWindowExTransparent(IntPtr hwnd)
- {
- var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
- SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT);
- }
-
- public static void SetWindowExNormal(IntPtr hwnd)
- {
- var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
- SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle & ~WS_EX_TRANSPARENT);
- }
-
- ///
- /// Make window clickthrough and hittest invisible.
- ///
- protected override void OnSourceInitialized(EventArgs e)
- {
- base.OnSourceInitialized(e);
- var hwnd = new System.Windows.Interop.WindowInteropHelper(this).Handle;
- SetWindowExTransparent(hwnd);
- }
-
///
/// Uses FindWindowEx() to recursively search for a child window with the given class and/or title.
///
diff --git a/CitraTouchControl/MenuWindow.xaml b/CitraTouchControl/MenuWindow.xaml
index af1a799..5970ecb 100644
--- a/CitraTouchControl/MenuWindow.xaml
+++ b/CitraTouchControl/MenuWindow.xaml
@@ -5,7 +5,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CitraTouchControl"
mc:Ignorable="d"
- Title="CitraTouchControl - Menu" Height="450" Width="400" Topmost="True" WindowStartupLocation="CenterOwner" WindowStyle="ToolWindow" Background="LightGray" Loaded="Window_Loaded" Closing="Window_Closing">
+ Title="CitraTouchControl - Menu" Height="475" Width="400" Topmost="True" WindowStartupLocation="CenterOwner" WindowStyle="ToolWindow" Background="LightGray" Loaded="Window_Loaded" Closing="Window_Closing">
@@ -20,15 +20,17 @@
+
-
-
-
-
+
+
+
+
-
+
-
-
+
+
+
diff --git a/CitraTouchControl/MenuWindow.xaml.cs b/CitraTouchControl/MenuWindow.xaml.cs
index 89564e9..0543250 100644
--- a/CitraTouchControl/MenuWindow.xaml.cs
+++ b/CitraTouchControl/MenuWindow.xaml.cs
@@ -22,6 +22,8 @@ private void Window_Loaded(object sender, RoutedEventArgs e)
bTouch.Content = "Disable Touch";
if (GlobalVars.AreControlsHidden)
bControls.Content = "Show Controls";
+ if (GlobalVars.IsTapOnly)
+ bTap.Content = "Touch: Tap & Hold";
bDuration.Content = "KeyPress: " + GlobalVars.KeyPressDuration + "ms";
}
@@ -86,13 +88,22 @@ private void bControls_Click(object sender, RoutedEventArgs e)
}
///
- /// Shows key binding window.
+ /// Toggle TapMode.
///
- private void bKeys_Click(object sender, RoutedEventArgs e)
+ private void bTap_Click(object sender, RoutedEventArgs e)
{
- KeysWindow kW = new KeysWindow();
- kW.Owner = this;
- kW.Show();
+ if (!GlobalVars.IsTapOnly)
+ {
+ bTap.Content = "Touch: Tap & Hold";
+ Properties.Settings.Default.IsTapOnly = true;
+ GlobalVars.IsTapOnly = true;
+ }
+ else
+ {
+ bTap.Content = "Touch: Tap only";
+ Properties.Settings.Default.IsTapOnly = false;
+ GlobalVars.IsTapOnly = false;
+ }
}
///
@@ -116,6 +127,16 @@ private void bDMinus_Click(object sender, RoutedEventArgs e)
bDuration.Content = "KeyPress: " + GlobalVars.KeyPressDuration + "ms";
}
+ ///
+ /// Shows key binding window.
+ ///
+ private void bKeys_Click(object sender, RoutedEventArgs e)
+ {
+ KeysWindow kW = new KeysWindow();
+ kW.Owner = this;
+ kW.Show();
+ }
+
///
/// Exits the whole application.
///
diff --git a/CitraTouchControl/Properties/Settings.Designer.cs b/CitraTouchControl/Properties/Settings.Designer.cs
index 3398edd..5017395 100644
--- a/CitraTouchControl/Properties/Settings.Designer.cs
+++ b/CitraTouchControl/Properties/Settings.Designer.cs
@@ -202,5 +202,17 @@ public int KeyPressDuration {
this["KeyPressDuration"] = value;
}
}
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool IsTapOnly {
+ get {
+ return ((bool)(this["IsTapOnly"]));
+ }
+ set {
+ this["IsTapOnly"] = value;
+ }
+ }
}
}
diff --git a/CitraTouchControl/Properties/Settings.settings b/CitraTouchControl/Properties/Settings.settings
index 6bd5403..3384d22 100644
--- a/CitraTouchControl/Properties/Settings.settings
+++ b/CitraTouchControl/Properties/Settings.settings
@@ -47,5 +47,8 @@
50
+
+ False
+
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5826feb
--- /dev/null
+++ b/README.md
@@ -0,0 +1,71 @@
+# CitraTouchControl #
+
+CitraTouchControl offers a multitouch able onscreen overlay for the [Nintendo 3DS Emulator Citra](https://github.com/citra-emu/citra).
+
+This is useful for remote play sessions with Citra from a mobile device or if you just don't wan't to sit in front of your PC to play.
+
+It will auto adjust to match Citra position and size, onscreen controls' keys can be modified and touch/controls can be toggled on and off.
+
+
+## Requirements ##
+* Citra-Qt build
+* Windows PC which runs Citra at decent (>30) framerates
+* Mobile device which can handle a massive input network-stream
+* fast (>75Mbps) network connection between the host-PC and your mobile device (ideally PC->LAN->Router->5G WLAN->Mobile device)
+
+
+## Usage ##
+1. Start Citra and load up your game.
+2. Connect to your host-PC via the Remote Desktop app on your mobile device.
+3. Start CitraTouchControl via remote control.
+4. Enjoy playing 3DS games on your tablet/phone.
+
+
+## Configuration ##
+1. [Enable RDP on your host PC](http://www.howtogeek.com/howto/windows-vista/turn-on-remote-desktop-in-windows-vista/)
+ * Make sure services "TermService" and "SessionEnv" are running if you encounter any problems
+2. Start Citra and CitraTouchControl, press the menu button on CitraTouchControl (bottom one) to bring up the configuration menu
+ 1. Press "Configurate Keys" to bring up the next window and set all your controls there
+3. Install [Microsoft Remote Desktop Beta](https://play.google.com/store/apps/details?id=com.microsoft.rdc.android.beta) on your mobile device
+4. Start it and add a new Desktop connection to your host PC
+ 1. Insert the local IP and Windows credentials of your host PC
+ 2. Enable "Custom display resolution" and set it to "788x1260"
+ 3. If you want sound then set Sound to "Play sound on device" (this will require additional bandwidth)
+
+
+
+## Options ##
+* Readjust Overlay: will get position and size of Citra again and place overlay accordingly. Use this if the overlay is off.
+* Enable Touch: will make the overlay "clickthrough" (buttons will still work)
+* Hide controls: will remove all controls except the menu button from the overlay. Use this in combinition with enabled touch for a stylus like experience.
+* Touch: Tap only: will make all onscreen buttons "static". If you press a button it will send KeyDown and KeyUp. Holding a button will not send multiple keystrokes anymore. Use this if you encounter involuntary KeyPresses (mostly due to lag).
+* Keypress: adjust the delay a Key keeps beeing pressed after you removed your finger from the button. Reduce this if you encounter involuntary KeyPresses (mostly due to lag) or increase it if buttons doesn't get recognised anymore.
+* Configurate Keys: set keys for all onscreen controls (only works with a physical keyboard, will not work over RDP from a mobile device)
+
+
+## Tested Remote Desktop Apps ##
+| Platorm | App | Working | Information |
+| ------------- | ------------- | ------------- | ------------- |
+| Android | [Microsoft Remote Desktop Beta](https://play.google.com/store/apps/details?id=com.microsoft.rdc.android.beta) | **_Yes_** |
+| Android | [Microsoft Remote Desktop](https://play.google.com/store/apps/details?id=com.microsoft.rdc.android) | no | no portrait mode |
+| Android | [TeamViewer](https://play.google.com/store/apps/details?id=com.teamviewer.teamviewer.market.mobile) | no | no portrait mode, distorted sound |
+| Android | [RDP Remote Desktop aFreeRDP](https://play.google.com/store/apps/details?id=com.freerdp.afreerdp) | no | no (multi-)touch |
+| Android | [aRDP Pro: Secure RDP Client](https://play.google.com/store/apps/details?id=com.iiordanov.aRDP) | no | no (multi-)touch |
+| Android | [Remote Desktop Manager](https://play.google.com/store/apps/details?id=com.devolutions.remotedesktopmanager) | no | no (multi-)touch |
+| Android | [Remotix VNC RDP Remote Desktop](https://play.google.com/store/apps/details?id=com.nulana.android.remotix) | no | no (multi-)touch, slow |
+| Android | [NoMachine](https://play.google.com/store/apps/details?id=com.nomachine.nxplayer) | no | slow, no portrait mode |
+| Android | [Remote Desktop Client](https://play.google.com/store/apps/details?id=com.xtralogic.android.rdpclient) | no | very slow |
+
+
+## Contributing ##
+If you find any Remote Desktop App which is fast enough for this kind of usage and supports portrait mode (also applies for other platforms like iOS or Windows Phone) then please let me know. Either send me a mail or create an issue here.
+
+
+## Limitations ##
+* Due to the fact that Microsofts RDP display driver does not support OpenGL 3.3 Citra will not work if you start it over a RDP-session. Start Citra and load your game **BEFORE** you connect via RDP.
+* Configurating keys only works with a physical keyboard, it will not work via Remote Desktop from a mobile device.
+* Overlay only checks for a proces called "citra-qt.exe". If your build of Citra is called something else then you'll have to rename it.
+* Overlay will not adjust itself after startup. Use the "Readjust Overlay" option in menu if you move/resize Citra later.
+* Overlay will not detect a restart of Citra. You'll have to manually restart CitraTouchControl if you close and reopen Citra.
+* If Citra runs in admin mode you'll have to start CitraTouchControl as admin too.
+* If your connection has lag >50ms CitraTouchControl will eventually hold KeyDown for too long which will result in recognition of multiple KeyPresses. Try the "Touch: Tap only" option in menu or reduce the KeyPress duration.