Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

ScreenHopper

4.96/5 (7 votes)
3 Jul 2013CPOL6 min read 23.5K   431  
A CodeProject mash-up providing a Hotkey to jump the cursor from one screen to the next on multi-monitor systems
Image of ScreenHopper tool

What's It For

When you get tired of dragging the mouse back and forth between two or more big screens and you really don't care to Alt-Tab your way around umpteen applications, you may find this tool an easier way to cover vast cursor distances in a single bound. The default "Last Position" mode will move the cursor to the last position it was at when jumping back to a screen when a hotkey is hit. The "Same Position" mode moves the cursor on the current screen to the relative same position, or near the same position, on the next monitor screen to the right or left when the hotkey is hit.

"Jump Right" and "Jump Left" only show a difference in behavior when you have a desktop extended across more than two monitors. Since I am just a poor man and don't have freakish amounts of hardware, you can be the first to tell us whether this tool works on a 3 or more monitor systems. I think it should work.

Due Diligence

PLEASE tell me if I've wasted my time. I think I'm a fair Googler and I haven't found any Windows trick or free tool to do this. Although, I did uncover a few shortcuts I hadn't seen before:

  • Desktop publishing forum: Windows Short Cuts & Hot Keys by: Dohadwala June 5, 2007.
  • Win+B: Move focus to systray icons. You can use Left and Right arrow keys to move between icons. 'Enter' for left-click menu.
  • Ctl+Alt+Down arrow: Rotate screen 180, Ctl+Alt+Up arrow: Rotate screen back. Really?!! Are these a gag? What is the use of these? You can tell I'm not a tablet user (remember - no freakish amounts of hardware). Operates on the screen with the cursor.
  • Useful New Windows 7 HOTKEYS-Keyboard Shortcuts January 29, 2009.
  • Here are a few notable shortcuts:
    • Win+Left : Snap to left, Win+Right : Snap to right. Kind of neat. Works great on Win8 but this feature doesn't work on my Win7 system. Aero Flip 3D (Ctl+Win+TAB) works on my Win7 machine but I can't get Aero Snaps to work for anything.
    • Win+Shift+Left : Jump to left monitor, Win+Shift+Right : Jump to right monitor. Nnnngh. Almost. Now just leave the active window behind and move the cursor instead and you have what I want. Works sensibly when the active window spans screens. Left of leftmost cycles back to rightmost and vice versa.
    • Ctl+Esc : Go to Win8 Start screen. My current favorite shortcut. If you are on the Start screen, Ctl+Esc will return you to whatever you were viewing before. This will probably remain useful even in Windows 8.1, wherein Microsoft admits it was a mistake to eliminate the desktop Start ball.
    • Win+Page Up, Win+Page Down : Switch Metro to other screens. I am still trying to find how to extend Metro across screens. I am not sure I really want to, but I would probably be scrolling the mousewheel Up(aka Left) and Down(aka Right) a lot less.

Parts and Pieces

I started this tool by finding this link. I don't want a Form, Window, or Page hanging around. I wanted the smallest desktop manifestation possible that would allow controlling a hot key to use in jumping the cursor from screen to screen, allow control of what "screen to screen" means, and would quietly go away (Exit) when I want. This application's ProcessIcon class plus ContextMenus class is pretty much all that minus the hotkey. What is a little unusual is the initial steps in this project:

  1. Create a new Windows Forms Application.
  2. Delete Form1.cs from the project.
  3. Open Program.cs - remove the line that reads Application.Run(new Form1());.

In place of running a Form, the Program.cs module displays a ProcessIcon's NotifyIcon (system tray icon) and starts a message pump to handle context menu selections (Application.Run() with no parameter). Mark Merrens uses a using statement to create the ProcessIcon instance and, since this class implements IDisposable, to clean up resources when the application exits. Here is the main part of Program.cs in that project:

C#
// Show the system tray icon.
using (ProcessIcon pi = new ProcessIcon())
{
	pi.Display();

	// Make sure the application runs!
	Application.Run();
}

In my case, I detect during the ProcessIcon constructor whether the system has at least two monitor screens. If not, public member terminate is set to true. For one monitor or more than one, the NotifyIcon is instantiated since we know Dispose() will always be called on exit. Here is the same section in ScreenHopper's Program.cs:

C#
// Show the system tray icon.
using (ProcessIcon pi = new ProcessIcon())
{
	if (pi.terminate)
	{
		return;
	}
	pi.Display();

	// Make sure the application runs!
	Application.Run();
}

The other main part of ScreenHopper is the hotkey used to jump the cursor. I wanted something that didn't require taking your hand off the mouse, but I couldn't use plain mouse clicks since they are claimed by most applications and regular desktop use. Most right-handed people can locate the Left Control key without looking so I chose Control as the default modifier. The other choices of modifier are Shift and Alt or any combination of these you choose except the "no modifier" case (Right Click the ScreenHopper systray icon, choose the Hotkey menu item). The Win key can also be a modifier but I didn't want to tempt fate. Win key up is definitely more than a modifier.

The hotkey trigger can be any "modified" mouse button. I chose the mousewheel (middle mouse button) as default since most mice have a wheel switch now and its use is otherwise less common.

The hotkey library I found is well-described here. This library is perfect since it handles both global keyboard and global mouse events (vs clicks and presses for an active, focused, non-systray app).

The most recent version (Version 3) is actually hosted on CodePlex here, but I am calling it a CodeProject project since it appeared here first (How could you leave us, George Mamaladze? Weren't we good for you?)

A Pressing Matter

From examining the library, you would think you could detect mouse key presses in a KeyboardHookManager. Something like:

C#
private void HookManager_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.LButton || e.KeyCode == Keys.MButton || e.KeyCode == Keys.RButton)
        DoSomething();
}

Alas, I couldn't get this to detect mouse presses. From what I can tell, keyboard hooks and mouse hooks are completely separate. You can't detect mouse buttons in KeyEventArgs and you can't detect Ctl/Alt/Shift/ modifiers in MouseEventArgs. What to do? Well, obviously you handle Control key, Alt key, and Shift key KeyDown and KeyUp events to set a few global variables and then use their setting when processing MouseEventArgs. Below is my HookManager_KeyUp handler and my HookManager_MouseDown handler. HookManager_KeyDown is the same as HookManager_KeyUp except for setting the three globals: shiftDown, controlDown, and altDown. The assignment statement for modifiersDown is the same in both keyboard hook handlers. We don't need a HookManager_MouseUp handler since we take the jump only on detecting mouse button down events. I tried to keep processing as light as possible since these are global for all keys and mouse button events.

C#
// Handles all Key Up events.  Filter modifier changes.  Evaluate modifiersDown for mouse clicks
private void HookManager_KeyUp(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.LShiftKey || e.KeyCode == Keys.RShiftKey)
        shiftDown = false;
    else if (e.KeyCode == Keys.LControlKey || e.KeyCode == Keys.RControlKey)
        controlDown = false;
    else if (e.KeyCode == Keys.LMenu || e.KeyCode == Keys.RMenu) //for Menu read Alt
        altDown = false;
    else
        return;

    modifiersDown = (shiftDown == shiftChecked) &&
    (controlDown == controlChecked) && (altDown == altChecked);
}

private void HookManager_MouseDown(object sender, MouseEventArgs e)
{
    if (!modifiersDown)
        return;
    if ((e.Button == MouseButtons.Left && mouseLeftSelected) ||
        (e.Button == MouseButtons.Middle && mouseWheelSelected) ||
        (e.Button == MouseButtons.Right && mouseRightSelected))
    {
        JumpCursor();
    }
}

Summary

I would go over the JumpCursor method, but it's really just a bunch of if-then-else's. This project at least shows how to handle combining mouse and keyboard events to effect mouse clicks with modifiers. I've found the "Last Position" mode more natural to me. If you prefer the "Same Position" mode as the default, change the initial assignment of lastPosition to false.

You can start more than one ScreenHopper. Nothing bad happens but they make confusing hops or no hop (Same same). ScreenHopper works when one monitor is desktop and one is Metro.

History

  • First submission to CodeProject, June 2013
  • Update submitted Jun 21, 2013.  Failed to imagine negative screen bounds. Fixed for primary screen on right.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)