make alt hotkeys more useful by preventing interference from windows menu system

This commit is contained in:
zeromus 2011-07-10 19:50:59 +00:00
parent 8e3302860f
commit f8f1906a3c
5 changed files with 158 additions and 19 deletions

View File

@ -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" />

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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)
{

View File

@ -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