diff --git a/Switcheroo/AltTabHook.cs b/Switcheroo/AltTabHook.cs index 9f2bc025..0a921510 100644 --- a/Switcheroo/AltTabHook.cs +++ b/Switcheroo/AltTabHook.cs @@ -30,6 +30,7 @@ namespace Switcheroo public class AltTabHookEventArgs : EventArgs { + public bool CtrlDown { get; set; } public bool ShiftDown { get; set; } public bool Handled { get; set; } } @@ -37,8 +38,13 @@ public class AltTabHookEventArgs : EventArgs public class AltTabHook : IDisposable { public event AltTabHookEventHandler Pressed; - private const int AltDown = 32; + private const int AltKey = 32; + private const int CtrlKey = 11; private readonly KeyboardKey _shiftKey = new KeyboardKey(Keys.LShiftKey); + private readonly KeyboardKey _ctrlKey = new KeyboardKey(Keys.LControlKey); + private readonly KeyboardKey _altKey = new KeyboardKey(Keys.LMenu); + private readonly int WM_KEYDOWN = 0x0100; + private readonly int WM_SYSKEYDOWN = 0x0104; // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable private readonly LowLevelKeyboardHook _lowLevelKeyboardHook; @@ -58,28 +64,38 @@ private void OnMessageIntercepted(LowLevelMessage lowLevelMessage, ref bool hand return; } - if (!IsAltTabKeyCombination(keyboardMessage)) + if (!IsTabKeyDown(keyboardMessage)) { return; } - var shiftKeyDown = (_shiftKey.AsyncState & 32768) != 0; // is held down - Trace.WriteLine("Shiftkey: " + shiftKeyDown); + if (!IsKeyDown(_altKey)) + { + return; + } + + var shiftKeyDown = IsKeyDown(_shiftKey); + var ctrlKeyDown = IsKeyDown(_ctrlKey); - var eventArgs = OnPressed(shiftKeyDown); + var eventArgs = OnPressed(shiftKeyDown, ctrlKeyDown); handled = eventArgs.Handled; } - private static bool IsAltTabKeyCombination(LowLevelKeyboardMessage keyboardMessage) + private static bool IsKeyDown(KeyboardKey keyboardKey) + { + return (keyboardKey.AsyncState & 32768) != 0; + } + + private bool IsTabKeyDown(LowLevelKeyboardMessage keyboardMessage) { - return keyboardMessage.VirtualKeyCode == (int) Keys.Tab - && keyboardMessage.Flags == AltDown; + return keyboardMessage.VirtualKeyCode == (int) Keys.Tab && + (keyboardMessage.Message == WM_KEYDOWN || keyboardMessage.Message == WM_SYSKEYDOWN); } - private AltTabHookEventArgs OnPressed(bool shiftDown) + private AltTabHookEventArgs OnPressed(bool shiftDown, bool ctrlDown) { - var altTabHookEventArgs = new AltTabHookEventArgs { ShiftDown = shiftDown }; + var altTabHookEventArgs = new AltTabHookEventArgs { ShiftDown = shiftDown, CtrlDown = ctrlDown }; var handler = Pressed; if (handler != null) { diff --git a/Switcheroo/MainWindow.xaml.cs b/Switcheroo/MainWindow.xaml.cs index 1603bc6d..c6656544 100644 --- a/Switcheroo/MainWindow.xaml.cs +++ b/Switcheroo/MainWindow.xaml.cs @@ -60,6 +60,7 @@ public partial class MainWindow : Window private AboutWindow _aboutWindow; private AltTabHook _altTabHook; private SystemWindow _foregroundWindow; + private bool _altTabAutoSwitch; public MainWindow() { @@ -101,6 +102,13 @@ private void SetUpKeyBindings() { Opacity = 0; } + else if (args.SystemKey == Key.S && Keyboard.Modifiers.HasFlag(ModifierKeys.Alt)) + { + _altTabAutoSwitch = false; + tb.Text = ""; + tb.IsEnabled = true; + tb.Focus(); + } }; KeyUp += (sender, args) => @@ -114,7 +122,11 @@ private void SetUpKeyBindings() { HideWindow(); } - else if (args.SystemKey == Key.LeftAlt) + else if (args.SystemKey == Key.LeftAlt && !Keyboard.Modifiers.HasFlag(ModifierKeys.Control)) + { + Switch(); + } + else if (args.Key == Key.LeftAlt && _altTabAutoSwitch) { Switch(); } @@ -131,8 +143,7 @@ private void SetUpHotKey() _hotkey.HotkeyPressed += hotkey_HotkeyPressed; try { - // Use either custom shortcut or AltTab. - _hotkey.Enabled = !Settings.Default.AltTabHook; + _hotkey.Enabled = Settings.Default.EnableHotKey; } catch (HotkeyAlreadyInUseException) { @@ -337,6 +348,7 @@ private void Switch() var win = (AppWindowViewModel) (lb.SelectedItem ?? lb.Items[0]); win.AppWindow.SwitchToLastVisibleActivePopup(); } + HideWindow(); } @@ -348,7 +360,9 @@ private void HideWindow() _windowCloser = null; } - Hide(); + _altTabAutoSwitch = false; + Opacity = 0; + Dispatcher.BeginInvoke(new Action(Hide), DispatcherPriority.Input); } #endregion @@ -424,13 +438,15 @@ private void OnClose(object sender, System.ComponentModel.CancelEventArgs e) private void hotkey_HotkeyPressed(object sender, EventArgs e) { - if (Settings.Default.AltTabHook) + if (!Settings.Default.EnableHotKey) { return; } if (Visibility != Visibility.Visible) { + tb.IsEnabled = true; + _foregroundWindow = SystemWindow.ForegroundWindow; Show(); Activate(); @@ -456,6 +472,8 @@ private void AltTabPressed(object sender, AltTabHookEventArgs e) if (Visibility != Visibility.Visible) { + tb.IsEnabled = true; + _foregroundWindow = SystemWindow.ForegroundWindow; ActivateAndFocusMainWindow(); @@ -470,6 +488,13 @@ private void AltTabPressed(object sender, AltTabHookEventArgs e) LoadData(InitialFocus.NextItem); } + if (Settings.Default.AutoSwitch && !e.CtrlDown) + { + _altTabAutoSwitch = true; + tb.IsEnabled = false; + tb.Text = "Press Alt + S to search"; + } + Opacity = 1; } else @@ -520,6 +545,11 @@ private void ActivateAndFocusMainWindow() private void TextChanged(object sender, TextChangedEventArgs args) { + if (!tb.IsEnabled) + { + return; + } + var query = tb.Text; var context = new WindowFilterContext diff --git a/Switcheroo/OptionsWindow.xaml b/Switcheroo/OptionsWindow.xaml index cf363cc6..990cc95a 100644 --- a/Switcheroo/OptionsWindow.xaml +++ b/Switcheroo/OptionsWindow.xaml @@ -2,8 +2,8 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Switcheroo Options" ResizeMode="NoResize" SizeToContent="WidthAndHeight" - ShowInTaskbar="False" WindowStyle="ToolWindow" Background="{x:Static SystemColors.WindowBrush}" Height="200" - Width="250"> + ShowInTaskbar="False" WindowStyle="ToolWindow" Background="{x:Static SystemColors.WindowBrush}" Height="380" + MaxWidth="350"> @@ -11,15 +11,31 @@ - - Activate Switcheroo with this shortcut: + - Activate Switcheroo with Alt+Tab - - Run as Administrator on Startup - + LostFocus="HotkeyPreview_OnLostFocus" + Width="150" + HorizontalAlignment="Left"/> + Activate Switcheroo with Alt+Tab + + Use Switcheroo instead of the integrated task switcher in Windows. + + Automatically switch window when releasing Alt + Tab + + Faster and more native-like swiching between windows. + + Use Ctrl + Alt + Tab for displaying Switcheroo with search activated, or + press Alt + S when the Switcheroo overlay appears. + + + + This is needed if you want Alt + Tab to work when programs + running with higher privileges than your user account are in focus. + + * Requires a restart of Switcheroo + diff --git a/Switcheroo/OptionsWindow.xaml.cs b/Switcheroo/OptionsWindow.xaml.cs index 615bcc72..ccaf09df 100644 --- a/Switcheroo/OptionsWindow.xaml.cs +++ b/Switcheroo/OptionsWindow.xaml.cs @@ -1,225 +1,247 @@ -/* - * Switcheroo - The incremental-search task switcher for Windows. - * http://www.switcheroo.io/ - * Copyright 2009, 2010 James Sulak - * Copyright 2014 Regin Larsen - * - * Switcheroo is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Switcheroo is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Switcheroo. If not, see . - */ - -using System.Text; -using System.Windows; -using System.Windows.Forms; -using System.Windows.Input; -using ManagedWinapi; -using Switcheroo.Core; -using Switcheroo.Properties; -using Application = System.Windows.Application; -using KeyEventArgs = System.Windows.Input.KeyEventArgs; -using MessageBox = System.Windows.MessageBox; - -namespace Switcheroo -{ - public partial class OptionsWindow : Window - { - private readonly HotKey _hotkey; - private HotkeyViewModel _hotkeyViewModel; - - public OptionsWindow() - { - InitializeComponent(); - - // Show what's already selected - _hotkey = (HotKey) Application.Current.Properties["hotkey"]; - - try - { - _hotkey.LoadSettings(); - } - catch (HotkeyAlreadyInUseException) - { - } - - _hotkeyViewModel = new HotkeyViewModel - { - KeyCode = KeyInterop.KeyFromVirtualKey((int) _hotkey.KeyCode), - Alt = _hotkey.Alt, - Ctrl = _hotkey.Ctrl, - Windows = _hotkey.WindowsKey, - Shift = _hotkey.Shift - }; - - HotkeyPreview.Text = _hotkeyViewModel.ToString(); - AltTabCheckBox.IsChecked = Settings.Default.AltTabHook; - RunAsAdministrator.IsChecked = Settings.Default.RunAsAdmin; - } - - private void Cancel_Click(object sender, RoutedEventArgs e) - { - Close(); - } - - private void Ok_Click(object sender, RoutedEventArgs e) - { - var closeOptionsWindow = true; - - try - { - _hotkey.Enabled = false; - // Change the active hotkey - _hotkey.Alt = _hotkeyViewModel.Alt; - _hotkey.Shift = _hotkeyViewModel.Shift; - _hotkey.Ctrl = _hotkeyViewModel.Ctrl; - _hotkey.WindowsKey = _hotkeyViewModel.Windows; - _hotkey.KeyCode = (Keys) KeyInterop.VirtualKeyFromKey(_hotkeyViewModel.KeyCode); - _hotkey.Enabled = true; - _hotkey.SaveSettings(); - } - catch (HotkeyAlreadyInUseException) - { - var boxText = "Sorry! The selected shortcut for activating Switcheroo is in use by another program. " + - "Please choose another."; - MessageBox.Show(boxText, "Shortcut already in use", MessageBoxButton.OK, MessageBoxImage.Warning); - closeOptionsWindow = false; - } - - Settings.Default.AltTabHook = AltTabCheckBox.IsChecked.GetValueOrDefault(); - Settings.Default.RunAsAdmin = RunAsAdministrator.IsChecked.GetValueOrDefault(false); - Settings.Default.Save(); - - if (closeOptionsWindow) - { - Close(); - } - } - - private void HotkeyPreview_OnPreviewKeyDown(object sender, KeyEventArgs e) - { - // The text box grabs all input - e.Handled = true; - - // Fetch the actual shortcut key - var key = (e.Key == Key.System ? e.SystemKey : e.Key); - - // Ignore modifier keys - if (key == Key.LeftShift || key == Key.RightShift - || key == Key.LeftCtrl || key == Key.RightCtrl - || key == Key.LeftAlt || key == Key.RightAlt - || key == Key.LWin || key == Key.RWin) - { - return; - } - - var previewHotkeyModel = new HotkeyViewModel(); - previewHotkeyModel.Ctrl = (Keyboard.Modifiers & ModifierKeys.Control) != 0; - previewHotkeyModel.Shift = (Keyboard.Modifiers & ModifierKeys.Shift) != 0; - previewHotkeyModel.Alt = (Keyboard.Modifiers & ModifierKeys.Alt) != 0; - - var winLKey = new KeyboardKey(Keys.LWin); - var winRKey = new KeyboardKey(Keys.RWin); - previewHotkeyModel.Windows = (winLKey.State & 0x8000) == 0x8000 || (winRKey.State & 0x8000) == 0x8000; - previewHotkeyModel.KeyCode = key; - - var previewText = previewHotkeyModel.ToString(); - - // Jump to the next element if the user presses only the Tab key - if (previewText == "Tab") - { - ((UIElement) sender).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next)); - return; - } - - HotkeyPreview.Text = previewText; - _hotkeyViewModel = previewHotkeyModel; - } - - private class HotkeyViewModel - { - public Key KeyCode { get; set; } - public bool Shift { get; set; } - public bool Alt { get; set; } - public bool Ctrl { get; set; } - public bool Windows { get; set; } - - public override string ToString() - { - var shortcutText = new StringBuilder(); - - if (Ctrl) - { - shortcutText.Append("Ctrl + "); - } - - if (Shift) - { - shortcutText.Append("Shift + "); - } - - if (Alt) - { - shortcutText.Append("Alt + "); - } - - if (Windows) - { - shortcutText.Append("Win + "); - } - - var keyString = - KeyboardHelper.CodeToString((uint) KeyInterop.VirtualKeyFromKey(KeyCode)).ToUpper().Trim(); - if (keyString.Length == 0) - { - keyString = new KeysConverter().ConvertToString(KeyCode); - } - - // If the user presses "Escape" then show "Escape" :) - if (keyString == "\u001B") - { - keyString = "Escape"; - } - - shortcutText.Append(keyString); - return shortcutText.ToString(); - } - } - - private void HotkeyPreview_OnGotFocus(object sender, RoutedEventArgs e) - { - // Disable the current hotkey while the hotkey field is active - _hotkey.Enabled = false; - } - - private void HotkeyPreview_OnLostFocus(object sender, RoutedEventArgs e) - { - try - { - _hotkey.Enabled = true; - } - catch (HotkeyAlreadyInUseException) - { - // It is alright if the hotkey can't be reactivated - } - } - - private void AltTabCheckBox_OnChecked(object sender, RoutedEventArgs e) - { - HotkeyPreview.IsEnabled = false; - } - - private void AltTabCheckBox_OnUnchecked(object sender, RoutedEventArgs e) - { - HotkeyPreview.IsEnabled = true; +/* + * Switcheroo - The incremental-search task switcher for Windows. + * http://www.switcheroo.io/ + * Copyright 2009, 2010 James Sulak + * Copyright 2014 Regin Larsen + * + * Switcheroo is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Switcheroo is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Switcheroo. If not, see . + */ + +using System.Text; +using System.Windows; +using System.Windows.Forms; +using System.Windows.Input; +using ManagedWinapi; +using Switcheroo.Core; +using Switcheroo.Properties; +using Application = System.Windows.Application; +using KeyEventArgs = System.Windows.Input.KeyEventArgs; +using MessageBox = System.Windows.MessageBox; + +namespace Switcheroo +{ + public partial class OptionsWindow : Window + { + private readonly HotKey _hotkey; + private HotkeyViewModel _hotkeyViewModel; + + public OptionsWindow() + { + InitializeComponent(); + + // Show what's already selected + _hotkey = (HotKey) Application.Current.Properties["hotkey"]; + + try + { + _hotkey.LoadSettings(); + } + catch (HotkeyAlreadyInUseException) + { + } + + _hotkeyViewModel = new HotkeyViewModel + { + KeyCode = KeyInterop.KeyFromVirtualKey((int) _hotkey.KeyCode), + Alt = _hotkey.Alt, + Ctrl = _hotkey.Ctrl, + Windows = _hotkey.WindowsKey, + Shift = _hotkey.Shift + }; + + HotKeyCheckBox.IsChecked = Settings.Default.EnableHotKey; + HotkeyPreview.Text = _hotkeyViewModel.ToString(); + HotkeyPreview.IsEnabled = Settings.Default.EnableHotKey; + AltTabCheckBox.IsChecked = Settings.Default.AltTabHook; + AutoSwitch.IsChecked = Settings.Default.AutoSwitch; + AutoSwitch.IsEnabled = Settings.Default.AltTabHook; + RunAsAdministrator.IsChecked = Settings.Default.RunAsAdmin; } - } + + private void Cancel_Click(object sender, RoutedEventArgs e) + { + Close(); + } + + private void Ok_Click(object sender, RoutedEventArgs e) + { + var closeOptionsWindow = true; + + try + { + _hotkey.Enabled = false; + + if (Settings.Default.EnableHotKey) + { + // Change the active hotkey + _hotkey.Alt = _hotkeyViewModel.Alt; + _hotkey.Shift = _hotkeyViewModel.Shift; + _hotkey.Ctrl = _hotkeyViewModel.Ctrl; + _hotkey.WindowsKey = _hotkeyViewModel.Windows; + _hotkey.KeyCode = (Keys) KeyInterop.VirtualKeyFromKey(_hotkeyViewModel.KeyCode); + _hotkey.Enabled = true; + } + + _hotkey.SaveSettings(); + } + catch (HotkeyAlreadyInUseException) + { + var boxText = "Sorry! The selected shortcut for activating Switcheroo is in use by another program. " + + "Please choose another."; + MessageBox.Show(boxText, "Shortcut already in use", MessageBoxButton.OK, MessageBoxImage.Warning); + closeOptionsWindow = false; + } + + Settings.Default.EnableHotKey = HotKeyCheckBox.IsChecked.GetValueOrDefault(); + Settings.Default.AltTabHook = AltTabCheckBox.IsChecked.GetValueOrDefault(); + Settings.Default.AutoSwitch = AutoSwitch.IsChecked.GetValueOrDefault(); + Settings.Default.RunAsAdmin = RunAsAdministrator.IsChecked.GetValueOrDefault(); + Settings.Default.Save(); + + if (closeOptionsWindow) + { + Close(); + } + } + + private void HotkeyPreview_OnPreviewKeyDown(object sender, KeyEventArgs e) + { + // The text box grabs all input + e.Handled = true; + + // Fetch the actual shortcut key + var key = (e.Key == Key.System ? e.SystemKey : e.Key); + + // Ignore modifier keys + if (key == Key.LeftShift || key == Key.RightShift + || key == Key.LeftCtrl || key == Key.RightCtrl + || key == Key.LeftAlt || key == Key.RightAlt + || key == Key.LWin || key == Key.RWin) + { + return; + } + + var previewHotkeyModel = new HotkeyViewModel(); + previewHotkeyModel.Ctrl = (Keyboard.Modifiers & ModifierKeys.Control) != 0; + previewHotkeyModel.Shift = (Keyboard.Modifiers & ModifierKeys.Shift) != 0; + previewHotkeyModel.Alt = (Keyboard.Modifiers & ModifierKeys.Alt) != 0; + + var winLKey = new KeyboardKey(Keys.LWin); + var winRKey = new KeyboardKey(Keys.RWin); + previewHotkeyModel.Windows = (winLKey.State & 0x8000) == 0x8000 || (winRKey.State & 0x8000) == 0x8000; + previewHotkeyModel.KeyCode = key; + + var previewText = previewHotkeyModel.ToString(); + + // Jump to the next element if the user presses only the Tab key + if (previewText == "Tab") + { + ((UIElement) sender).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next)); + return; + } + + HotkeyPreview.Text = previewText; + _hotkeyViewModel = previewHotkeyModel; + } + + private class HotkeyViewModel + { + public Key KeyCode { get; set; } + public bool Shift { get; set; } + public bool Alt { get; set; } + public bool Ctrl { get; set; } + public bool Windows { get; set; } + + public override string ToString() + { + var shortcutText = new StringBuilder(); + + if (Ctrl) + { + shortcutText.Append("Ctrl + "); + } + + if (Shift) + { + shortcutText.Append("Shift + "); + } + + if (Alt) + { + shortcutText.Append("Alt + "); + } + + if (Windows) + { + shortcutText.Append("Win + "); + } + + var keyString = + KeyboardHelper.CodeToString((uint) KeyInterop.VirtualKeyFromKey(KeyCode)).ToUpper().Trim(); + if (keyString.Length == 0) + { + keyString = new KeysConverter().ConvertToString(KeyCode); + } + + // If the user presses "Escape" then show "Escape" :) + if (keyString == "\u001B") + { + keyString = "Escape"; + } + + shortcutText.Append(keyString); + return shortcutText.ToString(); + } + } + + private void HotkeyPreview_OnGotFocus(object sender, RoutedEventArgs e) + { + // Disable the current hotkey while the hotkey field is active + _hotkey.Enabled = false; + } + + private void HotkeyPreview_OnLostFocus(object sender, RoutedEventArgs e) + { + try + { + _hotkey.Enabled = true; + } + catch (HotkeyAlreadyInUseException) + { + // It is alright if the hotkey can't be reactivated + } + } + + private void AltTabCheckBox_OnChecked(object sender, RoutedEventArgs e) + { + AutoSwitch.IsEnabled = true; + } + + private void AltTabCheckBox_OnUnchecked(object sender, RoutedEventArgs e) + { + AutoSwitch.IsEnabled = false; + AutoSwitch.IsChecked = false; + } + + private void HotKeyCheckBox_Checked(object sender, RoutedEventArgs e) + { + HotkeyPreview.IsEnabled = true; + } + + private void HotKeyCheckBox_OnUnchecked(object sender, RoutedEventArgs e) + { + HotkeyPreview.IsEnabled = false; + } + } } \ No newline at end of file diff --git a/Switcheroo/Properties/Settings.Designer.cs b/Switcheroo/Properties/Settings.Designer.cs index 60c4da70..39739261 100644 --- a/Switcheroo/Properties/Settings.Designer.cs +++ b/Switcheroo/Properties/Settings.Designer.cs @@ -118,5 +118,29 @@ public bool RunAsAdmin { this["RunAsAdmin"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool AutoSwitch { + get { + return ((bool)(this["AutoSwitch"])); + } + set { + this["AutoSwitch"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool EnableHotKey { + get { + return ((bool)(this["EnableHotKey"])); + } + set { + this["EnableHotKey"] = value; + } + } } } diff --git a/Switcheroo/Properties/Settings.settings b/Switcheroo/Properties/Settings.settings index 493196f8..9a2684ea 100644 --- a/Switcheroo/Properties/Settings.settings +++ b/Switcheroo/Properties/Settings.settings @@ -26,5 +26,11 @@ False + + False + + + True + \ No newline at end of file diff --git a/Switcheroo/app.config b/Switcheroo/app.config index 48b0d393..35bd6400 100644 --- a/Switcheroo/app.config +++ b/Switcheroo/app.config @@ -35,6 +35,12 @@ False + + False + + + True +