Introduction
In my mind by far the coolest feature that a desktop manager can have is proper multiple (virtual) desktopping (shallow, I know). So, Windows XP doesn't have this feature, ah well there's a PowerTool that does it. Except - you can't change desktop just by mousing off the edge, and more seriously there doesn't appear to be any mechanism to transfer windows between desktops - which is very annoying, as things never open where you want them to.
Concept
I thought that the easiest way to do this is simply to show and hide windows in such a way as to give the impression of more than one desktop. About halfway through writing this I discovered SwitchDesktop, which looked quite complicated so I didn't bother trying it that way.
Technical Details
The program is an MFC Dialog App.
I found out how to list all the windows on the system and show and hide them from the tbarsort article. I ask Windows for handles to every window on the system using EnumWindows and filter out the irrelevant ones.
bool ValidWindow(HWND hwnd)
{
if (!IsWindowVisible(hwnd)) return false;
if (GetWindowTextLength(hwnd) == 0) return false;
if (GetAsyncKeyState(VK_LBUTTON) && hwnd==GetForegroundWindow()) return false;
char str[100];
GetWindowText(hwnd,str,99);
if(strncmp(str,"Desktop",100)==0) return false;
if(strncmp(str,"Program Manager",100)==0) return false;
return true;
}
BOOL CALLBACK AddToList(HWND hwnd,LPARAM ret)
{
std::vector<HWND>* vecret=(std::vector<HWND>*)ret;
if(ValidWindow(hwnd)) vecret->push_back(hwnd);
return TRUE;
}
std::vector<HWND> WindowList()
{
std::vector<HWND> windows;
EnumWindows(AddToList,LPARAM(&windows));
return windows;
}
Desktops can be switched by pressing the relevant button or with a keyboard shortcut, implemented with RegisterHotKey, for example Win+1 is registered by
RegisterHotKey(GetSafeHwnd(),1,MOD_WIN,'1');
For some reason the MFC ClassWizard doesn't know about the WM_HOTKEY message, so it has to be added manually to the message map:
ON_MESSAGE(WM_HOTKEY,OnHotKey)
By far the hardest thing to do was to detect when the mouse touched the edge of the screen (which is when the desktops change). Eventually I found the MouseHookDlg project (although I can't currently find a reference for it), which shows how to install a global mouse hook. All I have to do is package the dll with my app and call the simple function API_SetHooks to register a mouse callback, and put a handler in the message map:
ON_REGISTERED_MESSAGE(UWM_MOUSEMOVE, OnHookMouseMove)
having first registered this mesage by
UWM_MOUSEMOVE = RegisterWindowMessage(UWM_SHELL_HOOK_MSG);
Finally, I wanted it to be possible to drag windows off the side of the screen and have them come with you to the new desktop. I failed to find an actual way of checking if any windows are being dragged (I suspect it can be done with another global hook), instead if the left mouse button is down as the desktops are switched then the foreground window is transfered, hence the line
if (GetAsyncKeyState(VK_LBUTTON) && hwnd==GetForegroundWindow()) return false;
in the ValidWindow above, this works well but occasionally leads to accidental window transfers.
Conclusion
The program works fine, with one or two minor bugs (not bad for only a few hours' work). Occasionally windows get transfered when not intended, and much worse occasionally they get permanently hidden (don't do any absolutely vital work whilst using this program, there is a small chance that this will happen and you will be unable to save). I would be glad of suggestions for bug fixes especially for improving the dragging detection, extra features are unlikely to get added.
Blatant plug: visit my site for mostly various games written using OpenGL with MFC or GLUT.