This is the part of my code that is supposed to implement the mouse simulation:
SendMessage(winHandle, WM_MOUSEMOVE, 0, MAKELPARAM(0, 0));
SendMessage(winHandle, WM_LBUTTONDOWN, 0, 0);
SendMessage(winHandle, WM_LBUTTONUP, 0, 0);
As you can see, i'm trying to make the mouse's cursor move to a the point (0, 0) of the specified window and perform a single click. But for some reason, the cursor doesn't move at all, and it just clicks on wherever it's currently in.
How can i fix that?
WM_MOUSEMOVE doesn't actually move the cursor, it just notifies the window that a movement had occurred. To actually move the cursor, use SetCursorPos() or SendInput() instead. Use ClientToScreen() or MapWindowPoints() to convert the desired client coordinates into screen coordinates to then move to.
Related
I want to add multiple color themes to my Win32 application, this means that I have to manually handle all the control drawing manually by using the BS_OWNERDRAW style flag. I then handle all the drawing in the WM_DRAWITEM message through the LPDRAWITEMSTRUCT structure stored in the lParam. Here's the problem though, by introducing owner drawing I also have to handle click and hover events, which in and of itself isn't a problem but it becomes one because it turns out that the LPDRAWITEMSTRUCT has no message being sent to indicate whether or not a control is being hovered. The itemState member do have messages like ODS_SELECTED, ODS_FOCUS and so on, but no flag for hovering only.
That being said, there does exist a flag named ODS_HOTLIGHT which according to MSDN should do the job, the problem however is that this flag never occurs for buttons, only menu items. I've tried using the SetWindowSubclass function by giving each control it's separate WindowProc callback where you can track the mouse leaving and entering the control. This works, but that also means that I need to transition all drawing commands over to the subclass procedure, which seems rather stupid to me, since the WM_DRAWITEM is intended for custom drawing in the first place.
You could always send WM_DRAWITEM manually via SendMessage but that also means that I need to provide an LPDRAWITEMSTRUCT manually which hasn't worked out that great. In essence I do not know what the best approach is to react to these hover events and draw them accordingly, the LPDRAWITEMSTRUCT does not provide such a flag and thus I have no idea what other approach to use. How should I tackle this?
The way I'm currently handling the WM_DRAWITEM message (buttons only):
case WM_DRAWITEM: {
LPDRAWITEMSTRUCT pDIS = (LPDRAWITEMSTRUCT)lParam;
HPEN borderPen = CreatePen(PS_INSIDEFRAME, 0, RGB(0, 0, 0));
HGDIOBJ oldPen = SelectObject(pDIS->hDC, borderPen);
HGDIOBJ oldBrush;
if (pDIS->itemState & ODS_SELECTED) {
oldBrush = SelectObject(pDIS->hDC, buttonHoverBrush);
}
else {
oldBrush = SelectObject(pDIS->hDC, buttonDefaultBrush);
}
// Rounded button
RoundRect(pDIS->hDC, pDIS->rcItem.left, pDIS->rcItem.top, pDIS->rcItem.right, pDIS->rcItem.bottom, 5, 5);
//Clean up
SelectObject(pDIS->hDC, oldPen);
SelectObject(pDIS->hDC, oldBrush);
DeleteObject(borderPen);
// Calculate button dimensions
int buttonWidth = pDIS->rcItem.right - pDIS->rcItem.left;
int buttonHeight = pDIS->rcItem.bottom - pDIS->rcItem.top;
WCHAR staticText[128];
int len = SendMessage(pDIS->hwndItem, WM_GETTEXT, ARRAYSIZE(staticText), (LPARAM)staticText);
HFONT buttonFont = (HFONT)SendMessage(pDIS->hwndItem, WM_GETFONT, 0, 0);
SIZE buttonDim;
HFONT oldFont = (HFONT)SelectObject(pDIS->hDC, buttonFont);
GetTextExtentPoint32(pDIS->hDC, staticText, len, &buttonDim);
SetTextColor(pDIS->hDC, RGB(255, 255, 255));
SetBkMode(pDIS->hDC, TRANSPARENT);
TextOut(pDIS->hDC, buttonWidth / 2 - buttonDim.cx / 2, buttonHeight / 2 - buttonDim.cy / 2, staticText, len);
wasHandled = TRUE;
result = TRUE;
break;
}
The code listed above creates a button with a given background color and centers text within it. It also changes color to the hover rush when clicked. What I want to happen is that the button changes its color immediately upon hover and not upon click. Thank you in advance!
I've tried using the SetWindowSubclass function by giving each control it's separate WindowProc callback where you can track the mouse leaving and entering the control. This works, but that also means that I need to transition all drawing commands over to the subclass procedure, which seems rather stupid to me, since the WM_DRAWITEM is intended for custom drawing in the first place.
Unfortunately, that is precisely what you will likely need to do.
For instance, you can have the button's subclass procedure handle the WM_MOUSEMOVE message to detect when the mouse is over the button, and handle the WM_MOUSELEAVE message to detect when the mouse moves out of the button. The message handler can use WM_MOUSEMOVE to call TrackMouseEvent() to trigger WM_MOUSELEAVE and WM_MOUSEHOVER messages. It can then use WM_MOUSEHOVER to set a flag and invalidate the button to trigger a repaint, and it can use WM_MOUSELEAVE to clear the flag and invalidate the button to trigger a repaint.
Inside the button's normal draw handler (which DOES NOT need to be moved to the subclass procedure. BTW), if the flag is set then draw the button as hovered, otherwise draw it as non-hovered.
The only gotcha with this approach is if you have multiple buttons then you will need multiple flags, one per button. But you can use (Get|Set)WindowLongPtr(GWL_USERDATA) or (Get|Set)Prop() to store/retrieve each button's flag inside/from the button's HWND directly Or, you can just maintain your own std::map (or other lookup table) of HWND-to-flag associations.
Another option would be to have the subclass procedure use the WM_MOUSE(MOVE|HOVER|LEAVE) messages to send simulated WM_DRAWITEM messages to the button, specifying/omitting the ODS_HOTLIGHT style as needed so the drawing code can look for that style. You might need the subclass procedure to intercept WM_PAINT messages to make that work, though. Not sure about that.
Otherwise, you could simply have your WM_DRAWITEM handler grab the mouse's current coordinates and see if they fall within the button's client area, and then draw accordingly.
The problem that I have seems to be trivial, but I cannot find a way to solve it. Here it is. I have a window with some graphics in it.
For simplicity lets say it's a solid green rectangle which fills the entire client area of the window. I want this rectangle to be redrawn and to fill the entire window every time the window changes its size. What I did originally was this. I posted WM_PAINT message from WM_SIZE handler.
It works, but if I move mouse fast I see a bit of unpainted (white) area around the green rectangle (actually one or two sides only, close to where mouse is). My understanding of the problem is that system thread which handles user input (mouse) works faster than my handler of WM_PAINT message. It means that by the time I start drawing an updated rectangle (its size is taken from WM_SIZE), mouse actually moves a little bit and system draws a new window frame which is different from what I'm trying to fill with the green. This creates unfilled areas next to borders which move during resizing.
When I stop resizing, green eventually fills the entire window, but during resizing there is a bit of flickering happening close to borders which is annoying. In order to solve the problem I tried the following.
bool finishedPainting;
RECT windowRect;
case WM_PAINT :
// ..... painting here
finishedPainting = TRUE;
break;
case WM_SIZE :
// .... some actions
// posting WM_PAINT
InvalidateRect(hWnd, NULL, FALSE);
PostMessage(hWnd, WM_PAINT, 0, 0);
break;
case WM_SIZING :
// this supposedly should prevent the system from passing
// new window size to WM_SIZE
if (!finishedPainting) memcpy((void*)lParam, &windowRect, sizeof(windowRect));
else {
// remember current window size for later use
memcpy(&windowRect, (void*)lParam, sizeof(windowRect));
finishedPainting = FALSE;
}
return TRUE;
It doesnt' work. As a slight variation, I also tried this.
bool finishedPainting;
POINT cursorPos;
case WM_PAINT :
// ..... painting here
finishedPainting = TRUE;
break;
case WM_SIZE :
if (!finishedPainting) SetCursorPos(cursorPos.x, cursorPos.y);
else {
finishedPainting = FALSE;
GetCursorPos(&cursorPos);
// .... some actions
InvalidateRect(hWnd, NULL, FALSE);
PostMessage(hWnd, WM_PAINT, 0, 0);
}
break;
This also doesn't work. As far as I understand the solution to the problem lies in somehow slowing the mouse down so that it moves to the next position on the screen (dragging the corner or the side of the window with it) only after the painting is finished.
Any ideas how to achieve this? Or maybe there is something fundamentally wrong with the way I see the problem and solution lies somewhere else?
// ====================================================
Update
I did a few experiments and here is what I found
1) When resizing, the sequence of messages is WM_SIZING - WM_NCPAINT - WM_SIZE - WM_PAINT. This looks a bit strange to me. I would expect WM_SIZE to follow WM_SIZING without interruption by WM_NCPAINT
2) In each message handler I was checking the width of a window during resizing (for simplicity I was only changing width). Surprisingly, the width measured in WM_SIZE turned out to be different from the one in WM_SIZING, but the same as in WM_NCPAINT and WM_PAINT. This is not a problem as such, just a wierd fact.
3) I came to the conclusion that there are two major causes for flicker happening near the window borders. The first one is that WM_NCPAINT comes before WM_PAINT. Imagine that you are stretching your window. The new frame will appear first (WM_NCPAINT comes first), then WM_PAINT fills the client area. A human eye catches that short period of time when the new frame is already on the screen, but it is empty. Even if you specify that you don't want window background to be deleted before repainting, still newly added area is empty and you can see it for a split second. This reason for flicker is best demonstrated when you grab the right window edge and move it quickly to the right. The other reason for flickering effect is less obvious and best seen when you grab the left window edge and move it to the left. During this move you will see unfilled areas along the RIGHT edge. As far as I understand the effect is caused by this. When user is doing resize Windows does the following: A) it sends WM_NCPAINT to draw the new frame, B) it copies the content of the old client area into the new top left window corner (in our case it moved to the left), C) it sends WM_PAINT to fill the new client area. However during stage B for some reason Windows produces those unfilled areas along the right edge, although it seems like it shouldn't because the old content should just stay where it is until it gets repainted over during WM_PAINT.
Ok, the question remains - how to get rid of those artefacts during resizing. As far as I can see now it is impossible to do using standard techniques and functions, because they are caused by the sequence of steps Windows performs during resizing. Swapping WM_NCPAINT and WM_PAINT would probably help, but this seems to be beyond our control (unless there is a simple way to do that which I just don't know about).
You shouldn't post or send WM_PAINT messages yourself. Rather, use ::InvalidateRect to invalidate parts of your window and let Windows decide when to send the WM_PAINT messages.
Windows works this way on purpose. It's generally considered more important to be responsive to the user (i.e. the mouse) than to have a fully up-to-date painted window.
If you're always painting the entire window in your WM_PAINT handler, you can eliminate a lot of flicker by overriding the WM_ERASEBKGND handler and return without doing anything.
If you really insist on preferring window updates over mouse responsiveness, replace the InvalidateRect call with a RedrawWindow call using the RDW_UPDATENOW flag.
Edit: Your observations make sense. WM_SIZING comes before the window is resized, to give you a chance to modify the size and/or position. You might try painting the client area in the WM_NCPAINT handler, even before the border is drawn. After you paint it you can validate the client area to keep it from drawing again.
It's a bad idea to manually post WM_PAINT or WM_SIZE. One weird hacky crazy thing you can do though, is to record the new resized co-ordinates in a RECT in WM_SIZE, Use MoveWindow to change the size of the window back to it's previous one, then manually resize it again using MoveWindow in the WM_PAINT message after you've done the painting.
Another possible solution is to constantly fill your window with the color regardless of whether the screen is resized or not. i.e
// in the WinMain function
if (GetMessage(&msg,NULL,NULL,0))
{
TranslateMessage(&msg,NULL,NULL);
DispatchMessage(&msg,NULL,NULL);
}
else
{
// fill your window with the color in here
}
Or, of course, you can just set the background color of your window to green, instead of doing all the painting yourself:
// Before RegisterClass or RegisterClassEx
// wincl is a WNDCLASS or WNDCLASSEX
wincl.hbrBackground = CreateSolidBrush(RGB(50, 238, 50));
I have a WIN32 owner-drawn static control that draws a progress bar using two source images (filled and unfilled). Works great on the initial draw:
case WM_DRAWITEM:
{
DRAWITEMSTRUCT* draw = (DRAWITEMSTRUCT*)lparam;
// Manually draw the progress bar.
if( draw->hwndItem == hwndProgress )
{
// Progress bar is 526 pixels wide.
int left = progressPercent * 526 / 100;
// Paint sections of window with filled and unfilled bitmaps
// based on progress bar position.
HDC hdcMem = ::CreateCompatibleDC(draw->hDC);
::SelectObject(hdcMem, hBmpProgressFull);
::BitBlt(draw->hDC, 0, 0, left, 36, hdcMem, 0, 0, SRCCOPY);
::DeleteDC(hdcMem);
HDC hdcMem2 = ::CreateCompatibleDC(draw->hDC);
::SelectObject(hdcMem2, hBmpProgressEmpty);
::BitBlt(draw->hDC, left, 0, 526-left, 36, hdcMem2, left, 0, SRCCOPY);
::DeleteDC(hdcMem2);
return TRUE;
}
}
return 0;
However, I can’t seem to get the thing to erase and repaint properly. I’ve tried SendMessage with WM_PAINT and RedrawWindow and neither one has worked quite right:
bool SetLoginProgressBar(float value)
{
if( hwndProgress != NULL )
{
progressPercent = (int)(value * 100.0);
//::RedrawWindow(hwndProgress, NULL, NULL, RDW_INVALIDATE|RDW_INTERNALPAINT);
::SendMessage(hwndProgress, WM_PAINT, NULL, NULL);
}
return true;
}
Instead of redrawing the window with the new values, it just sits there with the initially drawn image and ignores further drawing commands. It draws the progress correctly for the initial value, whether it's 0%, 50%, etc, and I can verify that my WM_DRAWITEM message handler code is being called.
So, what is the correct way to tell this control to erase and redraw in WIN32?
Is is possible that I need to do something like BeginPaint/EndPaint, or delete the hDC in the DRAWITEMSTRUCT that I've been passed?
You aren't deselecting the bitmaps from the memory DC before you destroy the DC. Perhaps the bitmaps are in a state where Windows won't allow you to select them again, so the BitBlts are failing.
P.S. RedrawWindow is what I use in this situation. InvalidateRect works too, but only if your message loop is running. Which leads to another observation: if you're in the middle of a long running operation, you may not get back to the message loop and your app will appear to be hung, including updates to the progress window.
InvalidateRect() is the function you need to call.
You never send or post WM_PAINT messages—the Window manager does that for you when they are needed (e.g. windows dragged over your window). If the repaint is due to changes that the Window manager does not know about then you force a repaint cycle by calling InvalidateRect(). Pass NULL for lpRect and the entire client area will be repainted. Pass TRUE for bErase to force the background to be erased when the repaint cycle begins.
When you call InvalidateRect() what happens is that a WM_PAINT message is placed in your message queue and the InvalidateRect() function call returns. When you next clear your message queue you the process the WM_PAINT message.
I suggest that you get hold of a copy of Petzold's Programming Windows book and read all about it.
the question is how to send double click to object who is already focused by keyboard? For example let's say that my mouse is on bottom right position on screen, if i open explorer by pressing WIN+E, and than press SPACE -> i will get focus to disk (c:\ disk for example), so i want to know on what coordinate is that focus that can send double click to it. Is there any function in c++ that do it for us?
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
Sleep(10);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
Sleep(10);
// Click Two
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
Sleep(10);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
From google:
http://groups.google.com/group/borland.public.delphi.winapi/browse_thread/thread/f1380942baf5c1ae?pli=1
http://msdn.microsoft.com/en-us/library/ms646260(v=vs.85).aspx
I'm not sure that is possible - in your example, the Explorer window is highlighting the drive object - you need some way to obtain the coordinates of the object with in the explorer window (which you do not control, right?)
It's not hard to send a double-click to a window - but what is happening within the window is difficult to determine.
You can find currently focused window with GetFocus function. But there's in general no way to find where to send a double click event. You can easily find out the dimensions and the screen coordinates of any window with GetWindowRect function. You can send that event to the center of the window or to any of its corner or anywhere else, and in some cases that would suffice.
In your example you would need to figure out where the selected element is located on the screen and that element is not a window, its state is maintained by the parent window and generally there's no way to get that information.
Check out MSAA or UIAutomation on MSDN; these are APIs that allow you to access element information below the HWND level. They are designed for test tools and accessibility tools that need this information. For example, a screen magnifier can use these APIs to follow the keyboard focus, get the location of the current element, and determine the correct coordinates to zoom in on.
MSAA/UIA are supported widely within Windows - all the system controls (as used in explorer) support it, as does the content of IE, Firefox, and some other apps.
You can use the inspect.exe tool that's part of the Windows SDK to play with this functionality.
Note that before clicking on the target, you should check that the element at that point is the element you expect it to be: if there's some other dialog in the way, the click will go to that element instead.
ok i can draw ellipse the problem is this, im trying to draw one ellipse but change its x value to different one. like this i draw one ellipse and the x value is 1 after ten seconds i want it the x value to be 10 but it seems that im creating new ellipse with x value 10. here is my code
while(sd==1)//sd equal 1
{
sf++;//sf equals 1
onPaint(hdc);
InvalidateRect(hWnd,0,true);
}
//on paint function
VOID onPaint(HDC hdc)
{
Graphics graphics(hdc);
Pen pen(Color(255, 0, 0, 255));
graphics.DrawEllipse(&pen,sf , 0, 50, 50);
}
well i thought that invalidate rect will clear everything have been painted and repaint it but it didn't work
If you want to make an animation you are better setting a timer.
Using InvalidateRect as a way to generate WM_PAINTs seems overkill, it will do much more thant that. Instead you can draw directly in OnTimer call, since it is outside a WM_PAINT you will need to get a device context with GetDC.
For example if you can have the function DrawFrame(HDC hDC). OnTimer will update the current position and call DrawFrame, OnPaint will call DrawFrame but will not update the position (that way if you want to stop the animation you will have the last frame draw).
The DrawFrame will clear the background (probably with a FillRect), and draw the circle in the new position. If you have a large area this will flicker, to avoid it as Tom suggested you may use a memory DC and a HBITMAP for the double buffer.
You shouldn't try to draw multiple frames of an animation in one shot.
Save your variable sf somewhere, and in OnPaint(), increment sf, draw a single ellipse, and call Invalidate()
The Invalidate will trigger OnPaint() to be called again.
This should work, but will be very flickery :) You can fix the flicker by double-buffering.
InvalidateRect marks the window as "invalid", but that doesn't cause an erase and repaint to happen right away. The erasing and painting happen only when your message pump is running (e.g., the loop with GetMessage and DispatchMessage). When the message queue runs dry, GetMessage will synthesize WM_ERASEBKGND and WM_PAINT messages for the invalid windows. When those messages are dispatched to the window procedure, the window gets a chance to draw.
Your onPaint function only draws, it doesn't erase. And since your loop never exits, the message pump never gets to run.
For simple animations, the solution is to SetTimer. In your handler for the WM_TIMER messages, update your variables for single frame, call InvalidateRect, and return (which lets the message pump keep running). The erasing and painting message will happen, then the timer will fire again, and you'll get the next frame.