make alt hotkeys more useful by preventing interference from windows menu system
This commit is contained in:
parent
8e3302860f
commit
f8f1906a3c
|
@ -560,7 +560,6 @@
|
|||
<None Include="images\textdoc.png" />
|
||||
<None Include="images\calculator.png" />
|
||||
<None Include="images\NESControllerIcon.png" />
|
||||
<Content Include="corphawk.ico" />
|
||||
<None Include="images\FastForward.png" />
|
||||
<None Include="images\ReadOnly.png" />
|
||||
<None Include="images\Back.png" />
|
||||
|
|
|
@ -80,30 +80,50 @@ namespace BizHawk.MultiClient
|
|||
public string Button;
|
||||
public ModifierKey Modifiers;
|
||||
|
||||
public bool Alt { get { return ((Modifiers & ModifierKey.Alt) != 0); } }
|
||||
public bool Control { get { return ((Modifiers & ModifierKey.Control) != 0); } }
|
||||
public bool Shift { get { return ((Modifiers & ModifierKey.Shift) != 0); } }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string ret = "";
|
||||
if ((Modifiers & ModifierKey.Control) != 0) ret += "Ctrl+";
|
||||
if ((Modifiers & ModifierKey.Alt) != 0) ret += "Alt+";
|
||||
if ((Modifiers & ModifierKey.Shift) != 0) ret += "Shift+";
|
||||
if (Control) ret += "Ctrl+";
|
||||
if (Alt) ret += "Alt+";
|
||||
if (Shift) ret += "Shift+";
|
||||
ret += Button;
|
||||
return ret;
|
||||
}
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
var other = (LogicalButton)obj;
|
||||
return other == this;
|
||||
}
|
||||
public static bool operator==(LogicalButton lhs, LogicalButton rhs)
|
||||
{
|
||||
return lhs.Button == rhs.Button && lhs.Modifiers == rhs.Modifiers;
|
||||
}
|
||||
public static bool operator !=(LogicalButton lhs, LogicalButton rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
}
|
||||
public class InputEvent
|
||||
{
|
||||
public LogicalButton LogicalButton;
|
||||
public InputEventType EventType;
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0}:{1}", EventType.ToString(), LogicalButton.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
WorkingDictionary<string, object> ModifierState = new WorkingDictionary<string, object>();
|
||||
WorkingDictionary<string, bool> LastState = new WorkingDictionary<string, bool>();
|
||||
|
||||
|
||||
HashSet<string> Ignore = new HashSet<string>(new[] { "LeftShift", "RightShift", "LeftControl", "RightControl", "LeftAlt", "RightAlt" });
|
||||
HashSet<string> IgnoreKeys = new HashSet<string>(new[] { "LeftShift", "RightShift", "LeftControl", "RightControl", "LeftAlt", "RightAlt" });
|
||||
void HandleButton(string button, bool newState)
|
||||
{
|
||||
if (Ignore.Contains(button)) return;
|
||||
if (EnableIgnoreModifiers && IgnoreKeys.Contains(button)) return;
|
||||
if (LastState[button] && newState) return;
|
||||
if (!LastState[button] && !newState) return;
|
||||
|
||||
|
@ -112,6 +132,29 @@ namespace BizHawk.MultiClient
|
|||
ie.LogicalButton = new LogicalButton(button, _Modifiers);
|
||||
_NewEvents.Add(ie);
|
||||
LastState[button] = newState;
|
||||
|
||||
////track the pressed events with modifiers that we send so that we can send corresponding unpresses with modifiers
|
||||
////this is an interesting idea, which we may need later, but not yet.
|
||||
////for example, you may see this series of events: press:ctrl+c, release:ctrl, release:c
|
||||
////but you might would rather have press:ctr+c, release:ctrl+c
|
||||
////this code relates the releases to the original presses.
|
||||
//if (newState)
|
||||
//{
|
||||
// ModifierState[button] = ie.LogicalButton;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// if (ModifierState[button] != null)
|
||||
// {
|
||||
// LogicalButton alreadyReleased = ie.LogicalButton;
|
||||
// ie = new InputEvent();
|
||||
// ie.LogicalButton = (LogicalButton)ModifierState[button];
|
||||
// ie.EventType = InputEventType.Release;
|
||||
// if(ie.LogicalButton != alreadyReleased)
|
||||
// _NewEvents.Add(ie);
|
||||
// }
|
||||
// ModifierState[button] = null;
|
||||
//}
|
||||
}
|
||||
|
||||
ModifierKey _Modifiers;
|
||||
|
@ -131,12 +174,14 @@ namespace BizHawk.MultiClient
|
|||
void EnqueueEvent(InputEvent ie)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
InputEvents.Enqueue(ie);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateThreadProc()
|
||||
{
|
||||
for(;;)
|
||||
for (; ; )
|
||||
{
|
||||
KeyInput.Update();
|
||||
GamePad.UpdateAll();
|
||||
|
@ -147,7 +192,7 @@ namespace BizHawk.MultiClient
|
|||
//analyze keys
|
||||
foreach (var key in KeyInput.State.PressedKeys) HandleButton(key.ToString(), true);
|
||||
foreach (var key in KeyInput.State.ReleasedKeys) HandleButton(key.ToString(), false);
|
||||
|
||||
|
||||
//analyze joysticks
|
||||
for (int i = 0; i < GamePad.Devices.Count; i++)
|
||||
{
|
||||
|
@ -193,5 +238,11 @@ namespace BizHawk.MultiClient
|
|||
if (ie.EventType == InputEventType.Release) return null;
|
||||
return ie.LogicalButton.ToString();
|
||||
}
|
||||
|
||||
//controls whether modifier keys will be ignored as key press events
|
||||
//this should be used by hotkey binders, but we may want modifier key events
|
||||
//to get triggered in the main form
|
||||
public bool EnableIgnoreModifiers = false;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -997,9 +997,6 @@ namespace BizHawk.MultiClient
|
|||
InputLog.StopMovie();
|
||||
}
|
||||
|
||||
[System.Security.SuppressUnmanagedCodeSecurity, DllImport("User32.dll", CharSet = CharSet.Auto)]
|
||||
public static extern bool PeekMessage(out Message msg, IntPtr hWnd, UInt32 msgFilterMin, UInt32 msgFilterMax, UInt32 flags);
|
||||
|
||||
void OnSelectSlot(int num)
|
||||
{
|
||||
SaveSlot = num;
|
||||
|
@ -1015,19 +1012,47 @@ namespace BizHawk.MultiClient
|
|||
var ie = Input.Instance.DequeueEvent();
|
||||
if(ie == null) break;
|
||||
|
||||
//useful debugging:
|
||||
//Console.WriteLine(ie);
|
||||
|
||||
//TODO - wonder what happens if we pop up something interactive as a response to one of these hotkeys? may need to purge further processing
|
||||
|
||||
//look for client control bindings for this key
|
||||
var triggers = Global.ClientControls.SearchBindings(ie.LogicalButton.ToString());
|
||||
if (triggers.Count == 0)
|
||||
{
|
||||
//no hotkeys or player inputs bound this, so mutate it to an unmodified key and assign it for use as a game controller input
|
||||
bool sys_hotkey = false;
|
||||
|
||||
//maybe it is a system alt-key which hasnt been overridden
|
||||
if (ie.EventType == Input.InputEventType.Press)
|
||||
{
|
||||
if (ie.LogicalButton.Alt && ie.LogicalButton.Button.Length == 1)
|
||||
{
|
||||
char c = ie.LogicalButton.Button.ToLower()[0];
|
||||
if (c >= 'a' && c <= 'z' || c==' ')
|
||||
{
|
||||
SendAltKeyChar(c);
|
||||
sys_hotkey = true;
|
||||
}
|
||||
}
|
||||
if (ie.LogicalButton.Alt && ie.LogicalButton.Button == "Space")
|
||||
{
|
||||
SendPlainAltKey(32);
|
||||
sys_hotkey = true;
|
||||
}
|
||||
}
|
||||
//ordinarily, an alt release with nothing else would move focus to the menubar. but that is sort of useless, and hard to implement exactly right.
|
||||
|
||||
//no hotkeys or system keys bound this, so mutate it to an unmodified key and assign it for use as a game controller input
|
||||
//(we have a rule that says: modified events may be used for game controller inputs but not hotkeys)
|
||||
var mutated_ie = new Input.InputEvent();
|
||||
mutated_ie.EventType = ie.EventType;
|
||||
mutated_ie.LogicalButton = ie.LogicalButton;
|
||||
mutated_ie.LogicalButton.Modifiers = Input.ModifierKey.None;
|
||||
Global.ControllerInputCoalescer.Receive(mutated_ie);
|
||||
if (!sys_hotkey)
|
||||
{
|
||||
var mutated_ie = new Input.InputEvent();
|
||||
mutated_ie.EventType = ie.EventType;
|
||||
mutated_ie.LogicalButton = ie.LogicalButton;
|
||||
mutated_ie.LogicalButton.Modifiers = Input.ModifierKey.None;
|
||||
Global.ControllerInputCoalescer.Receive(mutated_ie);
|
||||
}
|
||||
}
|
||||
|
||||
bool handled = false;
|
||||
|
@ -1990,6 +2015,43 @@ namespace BizHawk.MultiClient
|
|||
switchToFullscreenToolStripMenuItem.ShortcutKeyDisplayString = Global.Config.ToggleFullscreenBinding;
|
||||
}
|
||||
|
||||
//--alt key hacks
|
||||
|
||||
protected override void WndProc(ref Message m)
|
||||
{
|
||||
//this is necessary to trap plain alt keypresses so that only our hotkey system gets them
|
||||
if (m.Msg == 0x0112) //WM_SYSCOMMAND
|
||||
if (m.WParam.ToInt32() == 0xF100) //SC_KEYMENU
|
||||
return;
|
||||
base.WndProc(ref m);
|
||||
}
|
||||
|
||||
protected override bool ProcessDialogChar(char charCode)
|
||||
{
|
||||
//this is necessary to trap alt+char combinations so that only our hotkey system gets them
|
||||
if ((Control.ModifierKeys & Keys.Alt) != 0)
|
||||
return true;
|
||||
else return base.ProcessDialogChar(charCode);
|
||||
}
|
||||
|
||||
//sends a simulation of a plain alt key keystroke
|
||||
void SendPlainAltKey(int lparam)
|
||||
{
|
||||
Message m = new Message();
|
||||
m.WParam = new IntPtr(0xF100); //SC_KEYMENU
|
||||
m.LParam = new IntPtr(lparam);
|
||||
m.Msg = 0x0112; //WM_SYSCOMMAND
|
||||
m.HWnd = Handle;
|
||||
base.WndProc(ref m);
|
||||
}
|
||||
|
||||
//sends an alt+mnemonic combination
|
||||
void SendAltKeyChar(char c)
|
||||
{
|
||||
typeof(ToolStrip).InvokeMember("ProcessMnemonicInternal", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Instance, null, menuStrip1, new object[] { c });
|
||||
}
|
||||
|
||||
|
||||
private void menuStrip1_MenuActivate(object sender, EventArgs e)
|
||||
{
|
||||
HandlePlatformMenus();
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace BizHawk.MultiClient.tools
|
|||
public HotkeyWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
IDW_FRAMEADVANCE.SetBindings(Global.Config.FrameAdvanceBinding);
|
||||
IDW_PAUSE.SetBindings(Global.Config.EmulatorPauseBinding);
|
||||
IDW_HARDRESET.SetBindings(Global.Config.HardResetBinding);
|
||||
|
@ -94,6 +95,18 @@ namespace BizHawk.MultiClient.tools
|
|||
this.Close();
|
||||
}
|
||||
|
||||
protected override void OnShown(EventArgs e)
|
||||
{
|
||||
Input.Instance.EnableIgnoreModifiers = true;
|
||||
base.OnShown(e);
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
base.OnClosed(e);
|
||||
Input.Instance.EnableIgnoreModifiers = false;
|
||||
}
|
||||
|
||||
private void IDB_SAVE_Click(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
|
|
|
@ -39,6 +39,20 @@ namespace BizHawk.MultiClient
|
|||
Changed = false;
|
||||
}
|
||||
|
||||
|
||||
protected override void OnShown(EventArgs e)
|
||||
{
|
||||
Input.Instance.EnableIgnoreModifiers = true;
|
||||
base.OnShown(e);
|
||||
}
|
||||
|
||||
protected override void OnClosed(EventArgs e)
|
||||
{
|
||||
base.OnClosed(e);
|
||||
Input.Instance.EnableIgnoreModifiers = false;
|
||||
}
|
||||
|
||||
|
||||
private string AppendButtonMapping(string button, string oldmap)
|
||||
{
|
||||
//adelikat: Another relic, remove this
|
||||
|
|
Loading…
Reference in New Issue