I'm wondering how to get true mouse displacement with windows.
For example, I could save the position of a previous mouse position, and request the new position, and subtract the latter from the previous. But this would not give me true mouse displacement.
Imagine the previous cursor position would be the maximum x coordinate of your screen resolution, and the user is moving his mouse to the right. Is there still a way to capture the true mouse displacement then?
Thanks
Although it might be possible to actually read sensor data (after all the mouse itself only reports movement, not location), I'm not aware of how this could be done. I think at the very low levels of windows, that displacement information gets translated into cursor position on the screen and from then on, you will always be limited by your screen resolution.
In whatever you are trying to do, is the mouse cursor still visible?
A little while ago, I wrote a WPF numeric edit box control that mimicked the way those controls work in Expression Blend. The ones where you can drag the mouse from the edit box itself and it'll change the value. I ran into exactly same issue that you found and my solution was to hide the mouse cursor, detect displacement on every tick and reset the cursor to the center of the screen. Then when the user lets go of the button to stop dragging, I would put the cursor back to where I found it before the drag. This worked out really well and Expression Blend also behaves this way in hiding the cursor.
As far as I know DirectX has its own APIs to interact with peripherals, that is recommended for game developers. You should look into it - try for example DirectX 8 and the Mouse, more detailed documentation you can find on MSDN.
The most reliable way is to use window hooks. Here is a minimalist sample using C(++)
LRESULT CALLBACK LowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam){
MSLLHOOKSTRUCT* hookStruct = (MSLLHOOKSTRUCT*)lParam;
int ScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
int ScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
int x = hookStruct->pt.x * ScreenWidth / 65536);
int y = hookStruct->pt.y * ScreenHeight / 65536);
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
HHOOK LowLevelMouseProcHook = NULL;
void hook_window_procedure()
{
if (LowLevelMouseProcHook == NULL)
LowLevelMouseProcHook = SetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)LowLevelMouseProc, (HINSTANCE)NULL, NULL);
}
void unhook_window_procedure()
{
if (LowLevelMouseProcHook != NULL)
UnhookWindowsHookEx(LowLevelMouseProcHook);
}
You can hook the mouse calling hook_window_procedure(); and unhook it by calling unhook_window_procedure();
You will receive calls to void LowLevelMouseProc(...) whenever the mouse moves. lParam contains a pointer to MSLLHOOKSTRUCT and you find all needed mouse information in its structs. Most interesting here is POINT pt; that is in relative screen coordinates assuming 1/65536th of the screen. Transform them to real pixels by multiplying with int ScreenWidth or int ScreenHeight and dividing by 65536
If you struggle being clamped by the screen borders, you can make a workaround setting the mouse back to the center of your window each call. This is often seen in older games. If you are not forced to use only the Windows API, I would try using Direct Input.
Another way would be to evaluate WM_INPUT directly in your WindowProc for example. This reads data directly from the Human Interface Device (HID) stack and should have raw Mouse-Data in it. Here is a link to the MS-Documentation
The method on windows is as such:
// you can #include <hidusage.h> for these defines
#ifndef HID_USAGE_PAGE_GENERIC
#define HID_USAGE_PAGE_GENERIC ((USHORT) 0x01)
#endif
#ifndef HID_USAGE_GENERIC_MOUSE
#define HID_USAGE_GENERIC_MOUSE ((USHORT) 0x02)
#endif
RAWINPUTDEVICE Rid[1];
Rid[0].usUsagePage = HID_USAGE_PAGE_GENERIC;
Rid[0].usUsage = HID_USAGE_GENERIC_MOUSE;
Rid[0].dwFlags = RIDEV_INPUTSINK;
Rid[0].hwndTarget = hWnd;
RegisterRawInputDevices(Rid, 1, sizeof(Rid[0]));
then in message loop:
case WM_INPUT:
{
UINT dwSize = sizeof(RAWINPUT);
static BYTE lpb[sizeof(RAWINPUT)];
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER));
RAWINPUT* raw = (RAWINPUT*)lpb;
if (raw->header.dwType == RIM_TYPEMOUSE)
{
int xPosRelative = raw->data.mouse.lLastX;
int yPosRelative = raw->data.mouse.lLastY;
}
break;
}
Installing hooks will trigger sensitive antiviruses.
MSDN doc to refer
This sort of displacement ? : http://en.wikipedia.org/wiki/Differential_calculus
Try pitch/yaw : How could simply calling Pitch() and Yaw() cause the camera to eventually Roll()?
FPS style : http://cboard.cprogramming.com/game-programming/94944-mouse-input-aiming-fps-style-using-glut.html
and this : Jittering when moving mouse
pseudo code:
int mouseX, mouseY;
int oldMouseX, oldMouseY;
while(game_is_Running)
{
oldMouseX = mouseX;
oldMouseY = mouseY;
mouseX = get_new_mouse_from_windows_X();
mouseY = get_new_mouse_from_windows_Y();
if ((mouseX - oldMouseX) > 0)
{
// mouse moved to the right
}
else if ((mouseX - oldMouseX) < 0)
{
// mouse moved to the left
}
if ((mouseY - oldMouseY) < 0)
{
// mouse moved down
}
else if ((mouseY - oldMouseY) > 0)
{
// mouse moved up
}
}
A common method for this (in games):
Every frame of the game, get the position of the mouse and then recenter the mouse in the middle of the window using operating system functions. For every frame other than the first, this should give you an accurate displacement information. The key is to just be re-centering the mouse every frame so it never reaches the blocking outer bounds of the screen.
EDIT: Woops, didn't realize that DXM had already said this.
It is not possible to track physical mouse displacement with the Windows API. However, DirectInput provides features to keep track of it. You can also 'fake' it using only the Windows API, using the neat little trick in the answer of SO user DXM
Related
I have a classic Win32-API (C++) application and need to detect if the window is docked to the left/right half of the screen.
Background of the question is that the window only sizes in grid steps, let's say 32 pixel. In full screen the program detects that state, allow size to match the full screen and pad the excess space. With Windows 8 and later I would like to do the same instead of currently leaving borders (because the size snaps to a multiple of 32 pixel).
With the function GetWindowPlacement() you can retrieve the normal window rectangle, using the member rcNormalPosition of WINDOWPLACEMENT. Then compare the normal rectangle to the actual window rectangle. If they don't match the window is most likely in a docked state.
Example:
bool IsDockedToMonitor(HWND hWnd)
{
WINDOWPLACEMENT placement = {sizeof(WINDOWPLACEMENT)};
GetWindowPlacement(hWnd, &placement);
RECT rc;
GetWindowRect(hWnd, &rc);
return placement.showCmd == SW_SHOWNORMAL
&& (rc.left != placement.rcNormalPosition.left ||
rc.top != placement.rcNormalPosition.top ||
rc.right != placement.rcNormalPosition.right ||
rc.bottom != placement.rcNormalPosition.bottom);
}
Note that this solution is not reliable 100% of the time. There is a slim chance that the normal rectangle and current window rectangle could match even when the window is docked to the side of the monitor.
The Aero Snap feature is built into the Shell, not the window manager. As such, there is no particular window style or flag that indicates the docked state. The Shell simply repositions windows in response to certain actions (and internally records the state). It does so in a way that is indistinguishable from manually repositioning a window with the mouse or keyboard.
You cannot reliably determine, whether a window is docked to the left or right of the screen. There is no particular message sent by the Shell, nor is a window's size and position relative to the working area a sufficient property.
What you are trying to accomplish isn't possible. You will have to implement a solution, that doesn't require the information that isn't available. One such implementation would be to always use padding for window sizes, that don't allow the entire client area to be used. Another solution would be to implement the opposite: Allow window resizing to any size, unless you know the user is manually resizing the window. You can determine the latter by handling the WM_SIZING message.
In addition to what IInspectable has already mentioned, there is another way to determine this information and act accordingly.
Wait for a WM_WINDOWPOSCHANGED message and read its x, y, cx, and cy values from the WINDOWPOS pointer stored in lParam.
Get a handle to the current monitor on which the window is placed by calling MonitorFromWindow.
Create a MONITORINFO variable and set its cbSize field to sizeof(MONITORINFO).
Use the monitor handle and the address of your MONITORINFO variable to call GetMonitorInfo.
Read the rcWork value from your MONITORINFO variable.
rcWork.top == WINDOWPOS.y && rcWork.bottom == (WINDOWPOS.y + WINDOWPOS.cx) && rcWork.left == WINDOWPOS.x - the window is "docked" to the left
rcWork.top == WINDOWPOS.y && rcWork.bottom == (WINDOWPOS.y + WINDOWPOS.cx) && rcwork.right == (WINDOWPOS.x + WINDOWPOS.cx) - the window is "docked" to the right
rcWork.top == WINDOWPOS.y && rcWork.left == WINDOWPOS.x && rcWork.right == (WINDOWPOS.x + WINDOWPOS.cx) - the window is "docked" to the top
rcWork.top == (WINDOWPOS.y + WINDOWPOS.cy) && rcWork.left == WINDOWPOS.x && rcWork.right == (WINDOWPOS.x + WINDOWPOS.cx) - the window is "docked" to the bottom
You say you already have logic to determine if the window is fullscreen (do you mean fullscreen or maximized?), but effective maximization can be determined if left == x && top == y && right == x + cx && bottom == y + cy.
Here is an MSDN example of something similar.
Note that it may be more desirable to cache the MONITORINFO values so you don't need to call it every time the window is repositioned.
If you only want this to apply when a user does NOT manually resize the window, here is a contrived example of a possible way to do so:
LRESULT CALLBACK windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
static bool userSizing = false;
switch (msg)
{
// could also catch WM_ENTERSIZEMOVE here, but this will trigger on
// moves as well as sizes
case WM_SIZING:
userSizing = true;
break;
case WM_EXITSIZEMOVE:
userSizing = false;
break;
case WM_WINDOWPOSCHANGED:
if (userSizing)
{
break;
}
// do logic to check to see if the window is sized in a "docked"
// manner here
break;
// handle other window messages ...
}
}
I'm trying to draw sine wave using win32 api. I done this.
hDC = GetDC(hWnd);
while (TRUE)
{
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
DispatchMessage(&msg);
}
if (msg.message == WM_QUIT)
break;
wavefunc(hWnd, hDC);
}
void wavefunc(HWND hWnd, HDC hDC)
{
double full = 2 * pi * _freq;
static double _x = 0;
short int _y = 0;
short _y = (short)(sin(_x / _freq)*_amp) + 300;
if (_x >= full)
_x -= full;
SetPixel(hDC, 600, _y, blue);
ScrollWindow(hWnd, -1, 0, NULL, NULL);
Sleep(_sTime);
_x++;
}
Now i'm trying to figure out how to draw cartesian system. But with no result.
I have a window which is scrolled at each sin value.
I try to draw to lines on hdc. When window is scrolled there is no way to stop hdc to not scroll.
Then i create another hDc from windows but no succes.
How to do this? The problem is to have window scrolling but with some point fixes..
The way to do this is to paint everything all at once: clear the window, draw the grid, and then draw the entire waveform (not just the next point) over the grid. To make the waveform scroll, you keep redrawing with a different offset for the waveform.
To avoid flicker, you will likely need to do some double-buffering.
Typical WinAPI programs paint the window when handling the WM_PAINT command. The WM_PAINT message is generated when any or all of the window is invalid and needs to be repainted. Each time you scroll the window, you're creating an invalid region, but since you're ignoring the WM_PAINT message, nothing happens.
Here is how I would animate the sin() function:
Create a bitmap (the size of your window) and draw your static parts on it (axis, title, labels, etc.).
Create another bitmap (the height of your sin() and the width of your window + the width of the sin()’s period at your scale), and draw sin() on it.
In the wavefunc(), get the precise time and calculate the offset for your sin bitmap.
Use BitBlt() function to first blit the static part onto your window’s DC, then use TransparentBlt() to paint the sin, starting at pre-calculated offset.
I'm using winapi's raw input devices for mouse input and it seems I'm not getting the correct values in my RAWMOUSE struct.
When I get the RAWMOUSE struct I get the values of lLastX and lLastY as I'd expect but for some reason the ulRawButtons is constantly 0. The msdn documentation says that ulRawButtons is "The raw state of the mouse buttons" so I take it this should change when I press a mouse button.
This is the code I create the raw input device with (I removed error checking for readability):
RAWINPUTDEVICE rid;
rid.usUsagePage = 0x01;
rid.usUsage = 0x02;
rid.dwFlags = RIDEV_INPUTSINK;
rid.hwndTarget = myWindowHandle;
RegisterRawInputDevices(&rid, 1, sizeof(rid));
After the WM_INPUT message I call:
void handleRawInput(HWND window, UINT, WPARAM wParam, LPARAM lParam)
{
RAWINPUT input;
UINT szData = sizeof(input), szHeader = sizeof(RAWINPUTHEADER);
HRAWINPUT handle = reinterpret_cast<HRAWINPUT>(lParam);
GetRawInputData(handle, RID_INPUT, &input, &szData, szHeader);
if (input.header.dwType == RIM_TYPEMOUSE)
{
// Here input.data.mouse.ulRawButtons is 0 at all times.
}
}
I'm using mingw32 (4.7) and I've defined WIN32_LEAN_AND_MEAN, WINVER=0x0501 and WIN32_WINNT=0x0501.
Any ideas why I'm not getting the correct values for ulRawButtons?
Windows populates the RAWMOUSE struct with what the mouse driver tells it via the MOUSE_INPUT_DATA struct.
You'll notice that the MSDN page for MOUSE_INPUT_DATA states the following:
RawButtons
Specifies the raw state of the mouse buttons. The Win32 subsystem does not use this member.
What the MSDN page doesn't say is that not only does Win32 not use RawButtons/ulRawButtons but the mouse drivers it ships do not populate them. So, unless you have a mouse driver from a 3rd party vendor, you are never going to get this field set.
I think what you are looking to use is: input.data.mouse.usButtonFlags for mouse clicks and input.data.mouse.usButtonData for scroll wheel delta.
I want to create an irregularily shaped/skinned window (just with rounded, alpha blended corners for now). Upon creating the top-level window i am processing the WM_CREATE message and do the following:
Create a compatible memory DC
Create a DIB section compatible with the window DC
Select DIB into memory DC
Do the drawing of my backdrop
Apply alpha channel and premultiply RGB values
Call UpdateLayeredWindow()
Later on I am planning on rounding of the edges by setting the alpha channel and premultiplying the bits in question to make that happen.
Now, if I create for instance a button in my window, it will not render itself. I know why, and I am trying to come up with an elegant solution to making my custom controls (child windows) here.
My initial approach was to ditch using child windows in the first place and just let the top level window do all the drawing and also input handling, hit testing, and so on. I found this to be way to tedious and instead I want to let Windows handle all this for me.
Now, I know if I create a child window, it of course behaves normally (e.g. reacting to user input), and I want to leverage this. I am planning on creating the child windows (custom controls) normally using CreateWindowEx() so they get a window handle, and recieve window messages without me having to worry about passing them manually.
Somehow I need to get these windows painted, and as I see it, the only possible way to do this is from the routine that paints the whole top level window. I need to invent some kind of logic to get the top level window's paint routine to paint my child windows whenever necessary. As far as I understand the UpdateLayeredWindow() function need to redraw the whole window.
Is it sensible to for instance have the child controls render an image of themselves that are sent to the top level window's paint routine? Like for instance the child window sending a user WM to the top level window passing pointer to its rendered bitmap as a WPARAM and pointer to a structure defining its position and size as a LPARAM.
Any better ideas? Does this make any sense at all?
Thanks,
Eirik
I was trying to do a very similar thing.
From reading this and other searching web. It seams the recommended mechanism for drawing a CWnd (or HWND) and it's children onto your own CDC (or HDC) is to use the printing API.
CWnd has methods Print and PrintClient and which send WM_PRINT correctly. There is also the Win32 methods: PrintWindow.
I had trouble getting this to work at first but I eventually got the right method and parameters. The code that worked for me was:
void Vg2pImageHeaderRibbon::Update() {
// Get dimensions
CRect window_rect;
GetWindowRect(&window_rect);
// Make mem DC + mem bitmap
CDC* screen_dc = GetDC(); // Get DC for the hwnd
CDC dc;
dc.CreateCompatibleDC(screen_dc);
CBitmap dc_buffer;
dc_buffer.CreateCompatibleBitmap(screen_dc, window_rect.Width(), window_rect.Height());
auto hBmpOld = dc.SelectObject(dc_buffer);
// Create a buffer for manipulating the raw bitmap pixels (per-pixel alpha).
// Used by ClearBackgroundAndPrepForPerPixelTransparency and CorrectPerPixelAlpha.
BITMAP raw_bitmap;
dc_buffer.GetBitmap(&raw_bitmap);
int bytes = raw_bitmap.bmWidthBytes * raw_bitmap.bmHeight;
std::unique_ptr<char> bits(new char[bytes]);
// Clears the background black (I want semi-transparent black background).
ClearBackgroundAndPrepForPerPixelTransparency(dc, raw_bitmap, bytes, bits.get(), dc_buffer);
// To get the window and it's children to draw using print command
Print(&dc, PRF_CLIENT | PRF_CHILDREN | PRF_OWNED);
CorrectPerPixelAlpha(dc, raw_bitmap, bytes, bits.get(), dc_buffer);
// Call UpdateLayeredWindow
BLENDFUNCTION blend = {0};
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
CPoint ptSrc;
UpdateLayeredWindow(
screen_dc,
&window_rect.TopLeft(),
&window_rect.Size(),
&dc,
&ptSrc,
0,
&blend,
ULW_ALPHA
);
SelectObject(dc, hBmpOld);
DeleteObject(dc_buffer);
ReleaseDC(screen_dc);
}
This worked for me just as is. But incase you window or children don't support WM_PRINT I looked at how it was implemented for CView class I discovered that this class provides a virtual method called OnDraw(CDC* dc) that is provided with a DC to draw with. WM_PAINT is implemented something like this:
CPaintDC dc(this);
OnDraw(&dc);
And the WM_PAINT is implemented:
CDC* dc = CDC::FromHandle((HDC)wParam);
OnDraw(dc);
So the WM_PAINT and WM_PRINT results an OnDraw(), and the drawing code implemented once.
You can basically add this same logic your own CWnd derived class. This may not be possible using visual studio's class wizards. I had to add the following to message map block:
BEGIN_MESSAGE_MAP(MyButton, CButton)
...other messages
ON_MESSAGE(WM_PRINT, OnPrint)
END_MESSAGE_MAP()
And my handler:
LRESULT MyButton::OnPrint(WPARAM wParam, LPARAM lParam) {
CDC* dc = CDC::FromHandle((HDC)wParam);
OnDraw(dc);
return 0;
}
NOTE: If you add a custom WM_PRINT handler on a class that already supports this automatically then you loose the default implementation. There isn't a CWnd method for OnPrint so you have to use the Default() method to invoke the default handler.
I have't tried the following but I expect it works:
LRESULT MyCWnd::OnPrint(WPARAM wParam, LPARAM lParam) {
CDC* dc = CDC::FromHandle((HDC)wParam);
// Do my own drawing using custom drawing
OnDraw(dc);
// And get the default handler to draw children
return Default();
}
Above I defined some strange methods: ClearBackgroundAndPrepForPerPixelTransparency and CorrectPerPixelAlpha. These allow me to set the background of my dialog be semi-transparent when having the child controls be full opaque (this is my per-pixel transparency).
// This method is not very efficient but the CBitmap class doens't
// give me a choice I have to copy all the pixel data out, process it and set it back again.
// For performance I recommend using another bitmap class
//
// This method makes every pixel have an opacity of 255 (fully opaque).
void Vg2pImageHeaderRibbon::ClearBackgroundAndPrepForPerPixelTransparency(
CDC& dc, const BITMAP& raw_bitmap, int bytes, char* bits, CBitmap& dc_buffer
) {
CRect rect;
GetClientRect(&rect);
dc.FillSolidRect(0, 0, rect.Width(), rect.Height(), RGB(0,0,0));
dc_buffer.GetBitmapBits(bytes, bits);
UINT* pixels = reinterpret_cast<UINT*>(bits);
for (int c = 0; c < raw_bitmap.bmWidth * raw_bitmap.bmHeight; c++ ){
pixels[c] |= 0xff000000;
}
dc_buffer.SetBitmapBits(bytes, bits);
}
// This method is not very efficient but the CBitmap class doens't
// give me a choice I have to copy all the pixel data out, process it and set it back again.
// For performance I recommend using another bitmap class
//
// This method modifies the opacity value because we know GDI drawing always sets
// the opacity to 0 we find all pixels that have been drawn on since we called
// For performance I recommend using another bitmap class such as the IcfMfcRasterImage
// ClearBackgroundAndPrepForPerPixelTransparency. Anything that has been drawn on will get an
// opacity of 255 and all untouched pixels will get an opacity of 100.
void Vg2pImageHeaderRibbon::CorrectPerPixelAlpha(
CDC& dc, const BITMAP& raw_bitmap, int bytes, char* bits, CBitmap& dc_buffer
) {
const unsigned char AlphaForBackground = 100; // (0 - 255)
const int AlphaForBackgroundWord = AlphaForBackground << 24;
dc_buffer.GetBitmapBits(bytes, bits);
UINT* pixels = reinterpret_cast<UINT*>(bits);
for (int c = 0; c < raw_bitmap.bmWidth * raw_bitmap.bmHeight; c++ ){
if ((pixels[c] & 0xff000000) == 0) {
pixels[c] |= 0xff000000;
} else {
pixels[c] = (pixels[c] & 0x00ffffff) | AlphaForBackgroundWord;
}
}
dc_buffer.SetBitmapBits(bytes, bits);
}
Here is a screen shot of my test application. When the user hovers the mouse over the "more buttons" button the dialog box is created with a semi-transparent background. The buttons "B1" to "B16" are child controls derived from CButton and are being drawn using the Print() call show above. You can see the semi-transparent background at the right hand edge of the view and between the buttons.
I think I'm going to go for this solution:
Top level window
The top level window maintains two bitmaps. One which is the displayed window and one without any of the child controls rendered. The latter one will only need redrawing when the window changes size. The window will have a message handler that renders a child control on the displayed bitmap. The message handler will expect a pointer to either a DIB containing the child control, or to the actual bits (not sure which is best at the moment), as the WPARAM, and a pointer to a structure containing the rectangle that the child shall be drawn into as the LPARAM. A call to BitBlt() will be made to clear out the underlying surface (this is where the other bitmap comes in) prior to an AlphaBlend() call for rendering the child control bitmap onto the displayed bitmap surface.
The parent window will call the EnumChildWindows whenever it is resized or for some reason need to redraw its children. There could of course be some kind of invalidation regime enforced here to reduce unnecessary rendering of the child controls. Not sure if the speed increase is worth the effort, though.
Child windows
Upon creation of the child control instance, an internal bitmap compatible with that of the top-level window is created. The child renders itself into this internal bitmap and whenever it needs redrawing it notifies its parent window via the SendMessage() function, passing a pointer to its bitmap as the WPARAM, and a RECT as the LPARAM defining its position and dimensions. If the parent needs redrawing, it issues a message down to all its child windows requesting their bitmap. Childs will then respond with the same message that they normally would send when they decide they need redrawing themselves.
Eirik
To quote the MSDN page for WM_PAINT:
The WM_PAINT message is generated by the system and should not be sent by an application. To force a window to draw into a specific device context, use the WM_PRINT or WM_PRINTCLIENT message. Note that this requires the target window to support the WM_PRINTCLIENT message. Most common controls support the WM_PRINTCLIENT message.
So it looks like you can iterate through all the child windows with EnumChildWindows and send them WM_PRINT with your memory DC between steps 5 and 6.
It will look something like this:
static BOOL CALLBACK MyPaintCallback(HWND hChild,LPARAM lParam) {
SendMessage(hChild,WM_PRINT,(WPARAM)lParam,(LPARAM)(PRF_CHECKVISIBLE|PRF_CHILDREN|PRF_CLIENT));
return TRUE;
}
void MyPaintMethod(HWND hWnd) {
//steps 1-5
EnumChildWindows(hWnd,MyPaintCallback,MyMemoryDC);
//step 6
}
I am developing an application for pocket PC which should run in landscape mode.
I wrote the function SetScreenOrientation(int angle), which rotates the screen. This function is called on application start and on application close. I want to change the screen orientation when I minimize/maximize orientation as well. To do this I edited the following function:
void CMainFrame::OnSize(UINT nType, int cx, int cy)
{
RECT r;
GetWindowRect(&r);
RECT rstatus;
rstatus.left = 0;
rstatus.top = 0;
rstatus.right = r.right;
rstatus.bottom = TOOLBAR_HEIGHT;
m_wndStatus.MoveWindow(&rstatus, TRUE);
RECT rcamera;
rcamera.left = 0;
rcamera.top = 0;
rcamera.right = r.right;
rcamera.bottom = r.bottom - TOOLBAR_HEIGHT;
m_wndCameraView.MoveWindow(&rcamera, TRUE);
if(nType == SIZE_MAXIMIZED)
{
CScreenOrientation::SetScreenOrientation(270);
}
if(nType == SIZE_MINIMIZED)
{
CScreenOrientation::SetScreenOrientation(0);
}
}
The problem is that when I minimize the application the function is executed more than once so the screen first rotates back to 0 degrees and then it rotates to 270 degrees.
While debugging I can see that the second time the function is executed the following piece of wincore code is executed:
BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
...
switch (lpEntry->nSig)
{
...
case AfxSig_v_u_ii:
(this->*mmf.pfn_v_u_i_i)(static_cast<UINT>(wParam), LOWORD(lParam), HIWORD(lParam));
break;
...
}
}
Does anyone know any other way to set the screen orientation on application minimize/maximize or any trick that could prevent multiple function execution?
For one thing, it seems likely that SetScreenOrientation is going to give you another OnSize notification, so you want to detect recursive calls and do nothing when that happens.
More importantly, how do you know what orientation the user really wants? When your application starts up you can check the orientation and save that. But if the user changed the orientation while you happened to be running, they won't be happy when you change it back. Maybe you can check notifications of system settings changes and detect if the user changed the orientation themselves.