As you can see from the code snippet below, I've implemented a way to drag the Windows command prompt by its client area.
The problem with this code is that if the user follows these steps:
Unfocus console window
Focus console window by clicking and dragging it (without releasing)
Drag the window so that the cursor escapes window area (this can mean moving the cursor too quickly, or out of the set bounds (second monitor), or over the taskbar/other always-on-top window)
The console window will stop following the cursor, until it's moved to be inside the window again.
The fact that this does not happen when the console window is already in-focus at step 1, is what's really weird to me. I've tried debugging this for so many hours now, I just can't do it anymore. I'd appreciate any help regarding this.
// Continuously read input
while(ReadConsoleInput(hIn, &ir, 1, &nr))
{
switch(ir.EventType)
{
// Left mouse button down that either focuses or unfocuses console window
case FOCUS_EVENT:
// Left mouse button down that focuses console window
if(ir.Event.FocusEvent.bSetFocus)
{
GetCursorPos(&firstPos);
ScreenToClient(hWnd, &firstPos);
}
break;
case MOUSE_EVENT:
// Mouse did something inside console window
switch(ir.Event.MouseEvent.dwButtonState)
{
// Left mouse button down or up
case FROM_LEFT_1ST_BUTTON_PRESSED:
// Left mouse down or up, no drag
if(!ir.Event.MouseEvent.dwEventFlags)
{
GetCursorPos(&firstPos);
ScreenToClient(hWnd, &firstPos);
}
// Left button down, and mouse move. -> drag
if(ir.Event.MouseEvent.dwEventFlags == MOUSE_MOVED)
{
GetCursorPos(¤tRelativeToScreen);
// Calculate window position while dragging
// |
// v
if(currentRelativeToScreen.x - firstPos.x > scrnSz.right - ca.right)
wndPos.X = scrnSz.right - ca.right;
else if(currentRelativeToScreen.x - firstPos.x < 0)
wndPos.X = 0;
else
wndPos.X = currentRelativeToScreen.x - firstPos.x;
if(currentRelativeToScreen.y - firstPos.y > scrnSz.bottom - ca.bottom)
wndPos.Y = scrnSz.bottom - ca.bottom;
else if(currentRelativeToScreen.y - firstPos.y < 0)
wndPos.Y = 0;
else
wndPos.Y = currentRelativeToScreen.y - firstPos.y;
// End window position calculations
SetWindowPos(hWnd, 0, wndPos.X, wndPos.Y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
break;
default:
break;
}
break;
default:
break;
}
Drag the window so that the cursor escapes window area [...] The console window will stop following the cursor, until it's moved to be inside the window again.
This is the expected behavior, as documented at https://msdn.microsoft.com/en-us/library/windows/desktop/ms684239.aspx (emphasis added).
Mouse events are generated whenever the user moves the mouse, or presses or releases one of the mouse buttons. Mouse events are placed in a console's input buffer only when the console group has the keyboard focus and the cursor is within the borders of the console's window.
[ EDIT] To answer the followup comment about this.
this does not happen when the console window is already in-focus at step 1
Though not obvious from the official documentation, the console window appears to capture the mouse when clicked (and thus track it even when moved outside the console window) only if it already had the focus at the time of the click. For a console window out of focus, the 1st click gives it the focus (without globally capturing the mouse, so it only receives MOUSE_MOVE notifications while the cursor is within its client area), while the 2nd click enters exclusive capture mode (and receives all MOUSE_MOVE notifications regardless of cursor position).
This can be verified with a standard console window set to quick-edit mode. If the window has the input focus, then click-and-drag selects text in the console, even if the mouse is moved outside the window. However, if the window does not have the focus. the 1st click just gives it the focus, but does not enter any capture mode and dragging does not select any text.
on mouse move do:
if(button==leftButton){
ReleaseCapture();
SenMessage(hWnd,WM_NCLBUTTON_DOWN,HTCAPTION,lParam);
}
Related
I have a ListView Control with a LVS_REPORT style set. The thing I want is that when I click the row of an item, doesn't matter where it is, even if it's above a subitem, the item gets selected. Currently, the ListView only detects clicks over the first column (item) to make the selection.
To do this, I tried to handle the WM_LBUTTONUP message, so when the user releases the left mouse button on the row rectangle, it selects the corresponding item. It could work, but currently it needs a double click to do the selection.
This is the code that goes in the subclass window procedure:
case WM_LBUTTONUP:
{
long x = GET_X_LPARAM(lParam);
long y = GET_Y_LPARAM(lParam);
RECT rc{};
Message("btn up"); // message box that shows when the left button is released
for (auto& item : lst->m_items)
{
ListView_GetItemRect(lst->handle(), item->index, &rc, LVIR_BOUNDS);
POINTS p;
if (PtInRect(&rc, { x, y })) // checks if clicked point is inside the item rect
{
ListView_SetItemState(lst->m_hwnd, item->index, LVIS_SELECTED, 7);
break;
}
}
return 0;
}
The same code (except the loop part) I put inside an Edit subclass procedure and the Main Window subclass procedure. Both work as intended, that is, when a hit a single click, it shows a message.
Is there a workaround for this? Couldn't find any style to achieve this objective, either. What could be causing this issue? I thought, for some reason, that either some other window could be above the ListView and that could be stopping it to receive new clicks or also it could be sending the click first to the parent window. These are not the case: 1) the control is not behind any other window; 2) if this was the case, the program would show a MessageBox with the message specified in the procedure, which doesn't happen.
Thanks in advance.
A message box created with
MessageBox (NULL, "Text", "Title", MB_ICONINFORMATION | MB_SYSTEMMODAL);
stays on top of other windows, but it loses keyboard focus when the user clicks another window.
How could I create a message box (or an Edit box or a dialog box) that never loses keyboard focus, so if I try to switch to another window using [ALT-TAB] or mouse or any other method, or if another application running in a background opens its own Edit box, the keyboard focus would jump back to my message / Edit box?
The standard MessageBox function doesn't have such an option, so I tried a custom Edit box. I experimented with WM_SETFOCUS, WM_KILLFOCUS, WM_NCACTIVATE, SetForegroundWindow, SetFocus, but had no luck so far. When I open another window, the keyboard focus stubbornly goes to that window.
RegisterHotKey(0,1,MOD_ALT,VK_TAB); //disables alt+tab until message box returns
SetTimer(hwnd,1,100,0); //0.1 sec is enough for message box window creation
MessageBox(0,"Text","Title",MB_ICONINFORMATION|MB_SYSTEMMODAL); //since it's system modal, main message loop will wait for it to return
ClipCursor(0); //frees cursor
UnregisterHotKey(0,1); //enables alt+tab again
Here we use a timer to clip cursor so that user won't be able to click outside of the message box. I left title part of the messagebox outside of the clip area because clicking there negates cursor clipping.
case WM_TIMER:
{
KillTimer(hwnd,1);
RECT rc;
GetWindowRect(FindWindow(0,"Title"),&rc);
rc.top+=22; //title area left out of clip area
ClipCursor(&rc); //user cannot move their mouse outside of the message box
break;
}
This however can't block Ctrl + Alt + Del but does what you ask.
I know the mouse click position using API GetCursorPos and the handle of application (HWND).
How can I check mouse click position is on this application?
My view:
Get the bounding box of application from its handle. (GetWindowRect(hWnd, &rect);)
Check cursor position lies in this bounding box. (PtInRect(&rect, p))
This is not working if windows are overlapping.
As we know the handle of targeted screen handle and click cursor position:
// hWnd : Already known windows handle
GetCursorPos(&p);
HWND hWndFromPoint = WindowFromPoint(p);
// If the handle got from click point is child of the desire window means it is clicked on the desire window itself.
if (IsChild(hWnd, hWndFromPoint))
{
// Do something on Mouse click
}
I'm reading about the GetCapture() function , which is part of the mfc.
I'm still unclear as to what it does, as well as what it means to capture the mouse , as it says here:
http://msdn.microsoft.com/en-us/library/dxa5eaaa(v=vs.80).aspx
in my book it's used in this way:
void CSketcherView::OnLButtonUp(UINY nFlags, CPoint point)
{
if(this == GetCapture())
ReleaseCapture(); // Stop capturing mouse messages
// ... add information to document
}
So what does GetCapture() return? and what does "capturing" the mouse mean?
As a concrete example for the purpose of mouse capture:
Take a window with two pushbuttons. Click on one of them and keep the mouse button held down. Now drag the mouse cursor over to the second pushbutton and release the mouse button. The first pushbutton will receive a WM_LBUTTONUP message, but the second won't, even though the mouse cursor is on top of it.
When that first pushbutton received the WM_LBUTTONDOWN message, it captured the mouse. While a window is capturing the mouse, it guarantees that it will receive all subsequent mouse events (particularly the WM_LBUTTONUP message), even if the mouse has been dragged outside of its window bounds. This is important so that it can match button-down to button-up messages and maintain proper state. It's also important for usability (if you click on one button and accidentally move away to another button, you neither want to trigger the first nor the second button).
Capturing the mouse usually means you will stil get Mouse events even when the mouse cursor position is outside the bounds of your window
The GetCapture function simply returns the current window that has the mouse capture.
Using C++ in VS2008, I'm at the beginnings of a DirectX program which can so far display a token image and flip between fullscreen and windowed mode with no problems.
It has a menu bar, which is invisible as expected in fullscreen mode. The ALT key could still activate this menu in fullscreen, but it was very clumsy since its items weren't visible until highlighted, and even then it only responded to about 1 in 5 presses of the ALT key.
So I decided to implement a popup menu instead, that responds to the right mouse button. My problem is that this popup menu is also temperamental in fullscreen mode. It also plays with the custom cursor I am using.
The wrong behaviour is defined as follows:
1· A right click anywhere will active the popup menu, but also deactive the custom cursor and replace it with the system default.
2· Once the menu is visible, RIGHT-clicking outside of the menu will deactive it, but this leaves the default cursor on. However, right-clicking again will activate the menu again, and this continues perfectly if not for the missing custom cursor.
3· Once the menu is visible, LEFT-clicking outside of the menu will deactivate it, and this displays the custom cursor again. Now it gets a bit more messy because now:
3a· If I right-click, it will return to step 1. Menu on + cursor off.
3b· If I left-click a second time (as if I am gaining focus or something), then right-click, will will activate the default cursor but NOT active the menu (as if I half did and half didn't regain focus or something). Menu off + cursor off.
3c· If I move the mouse and then right-click, it will do one of the above. Step 1 or step 3b. Menu on + cursor off, or menu off + cursor off. Frustrating.
4· Most obvious, if step 3b occurs (menu off + cursor off), the user can flip the custom cursor on and off forever. Left click on, right click off. Not only does the menu never appear, but this is simply not what I want a user to be seeing in my program... the cursor changing for no reason.
I imagine the cursor changing is to do with the menu itself taking focus or something like that. I would like that to be avoided if possible, but the real problem is why is the menu activation temperamental in the first place?
My code for the popup menu is:
case WM_RBUTTONDOWN:
cout << "Right button in fullscreen" << endl;
TrackPopupMenu(GetSubMenu(g_menu,0), TPM_LEFTALIGN|TPM_HORPOSANIMATION,
30, 30, 0, g_parentWindow, NULL);
// SetActiveWindow(g_parentWindow); // Makes no difference.
// SetForegroundWindow(g_parentWindow); // Makes no difference.
break;
One more thing. If I use F3 to active the menu instead, absolutely nothing happens.
Problem 1: Can I make the menu activate reliably?
Problem 2: Can I stop the cursor from changing?
Problem 3: Why does a key command not work?
Thanks for the read. Any ideas?
you can use this little function to set the cursor back to your customize cursor SetCursor(CURSOR_ID); you can add this in WM_RBUTTONDOWN