Go Back   Winamp & Shoutcast Forums > Developer Center > Winamp Development

Reply
Thread Tools Search this Thread Display Modes
Old 22nd June 2012, 06:59   #1
thinktink
Forum King
 
thinktink's Avatar
 
Join Date: May 2009
Location: No longer on the streets of Kings County, CA.
Posts: 3,089
Direct Mouse Wheel Support / WINAMP_WM_DIRECT_MOUSE_WHEEL Documentation

[edit - DrO 22/06/2012]
see post #2 for the code required to implement this in your own plug-ins



This post is mostly notes to myself for future reference in-case I forget. I think it would be a good idea for a mod to sticky this if possible until an official documentation is provided. I actually found this system by accident and have slowly worked up a working theory for its use and handling.



Windows normally (and quite unintuitively for end-users) sends all WM_MOUSEWHEEL messages to the currently focused window regardless of where the mouse cursor is pointed. However, for intuitive scroll wheel functionality, Winamp seems to have implemented a rather easy system for redirecting mouse wheel messages to the window procedure for the window that the mouse cursor is actually positioned over regardless of which window control has the active window input focus:

C/C++ Code
code:
UINT WINAMP_WM_DIRECT_MOUSE_WHEEL=RegisterWindowMessage(TEXT("WINAMP_WM_DIRECT_MOUSE_WHEEL"));


After experimenting with a dummy empty plugin that creates a GenEmbed window with various controls, I have found the following behavior of Winamp's mouse wheel message handling:

If the mouse cursor is over a window that does not have the active input focus but another window does, Winamp Send(Message)s the WINAMP_WM_DIRECT_MOUSE_WHEEL message to that window first before allowing the window with the actual input focus to receive the WM_MOUSEWHEEL message. The wParam and lParam fields from the original WM_MOUSEWHEEL message are directly copied as the fields of the WINAMP_WM_DIRECT_MOUSE_WHEEL message. If the window procedure that receives the sent WINAMP_WM_DIRECT_MOUSE_WHEEL message returns zero (or FALSE) then Winamp will allow the original mouse wheel message to enter the original window's procedure. If however, the result of the message is non-zero (or TRUE) then Winamp will block the original mouse wheel message from reaching the original window's procedure in-lieu of mouse wheel message processing for the new target window (the one that the mouse cursor is actually positioned over.)

If the mouse cursor is over the window that has the active input focus, then Winamp does not block the original WM_MOUSEWHEEL message from reaching it's originally intended target window procedure and does not generate a WINAMP_WM_DIRECT_MOUSE_WHEEL message (well, at least not anywhere that I could find.)


My recommendations for the handling of this message and the WM_MOUSEWEEL message:

If a window procedure processes the WINAMP_WM_DIRECT_MOUSE_WHEEL message in any way, it should always return non-zero (or TRUE) to prevent more than one window from scrolling at the same time. If processing from within a constructed framework, you should SendMessage (preferably not PostMessage) the WM_MOUSEWHEEL message to the same window handle as the one that received the WINAMP_WM_DIRECT_MOUSE_WHEEL message if you don't directly process the WINAMP_WM_DIRECT_MOUSE_WHEEL as a WM_MOUSEWHEEL message in your base class(es), copying the wParam and lParam parameters to the new WM_MOUSEWHEEL message. Do not generate a WINAMP_WM_DIRECT_MOUSE_WHEEL message in your code. Only respond to it appropriately. If processing the parameters of the WM_MOUSEWHEEL and WINAMP_WM_DIRECT_MOUSE_WHEEL messages, you probably do not need to check if the message's x=(short)LOWORD(lParam) and y=(short)HIWORD(lParam) cursor point location is within the control's bounding rectangle as Winamp seems to have already done this for you (except for instances where Winamp received a zero (or FALSE) response to the WINAMP_WM_DIRECT_MOUSE_WHEEL message or you want to be picky about it or you're coding a custom window class with multiple or confined scrolling regions needing special processing.)


My current theory for how this works:

The Winamp main UI thread message pump seems to have special handling for certain messages, including the WM_MOUSEWHEEL messages (among others.) It appears that when Winamp's UI message pump encounters a posted WM_MOUSEWHEEL message, before it reaches DispatchMessage, it checks to see if the cursor is over the window it's posted for and if not, sends the WINAMP_WM_DIRECT_MOUSE_WHEEL message to the window that is. If the window procedure responds non-zero (or TRUE) then Winamp doesn't call DispatchMessage. But if the window procedure responds zero (or FALSE) then Winamp will pass the original WM_MOUSEWHEEL message to DispatchMessage. This theory is based upon my observations to what various window message hooking applications report at which stage and whether or not my window procedures (in testing) actually received the original WM_MOUSEWHEEL message or not.

Wow, that took a while to write and proof-read.
thinktink is offline   Reply With Quote
Old 22nd June 2012, 10:39   #2
DrO
 
Join Date: Sep 2003
Posts: 27,873
sorry but tldr; any of the above, is just easier to have the code for accessing it in plug-ins.


at the end of the message proc for the dialog:
code:
if (FALSE != DirectMouseWheel_ProcessDialogMessage(hDlg, uMsg, wParam, lParam)) {
return TRUE;
}



the methods to do it all:
code:
BOOL IsDirectMouseWheelMessage(const UINT uMsg) {
static UINT WINAMP_WM_DIRECT_MOUSE_WHEEL = WM_NULL;

if (WM_NULL == WINAMP_WM_DIRECT_MOUSE_WHEEL) {
WINAMP_WM_DIRECT_MOUSE_WHEEL = RegisterWindowMessageW(L"WINAMP_WM_DIRECT_MOUSE_WHEEL");
if (WM_NULL == WINAMP_WM_DIRECT_MOUSE_WHEEL)
return FALSE;
}

return (WINAMP_WM_DIRECT_MOUSE_WHEEL == uMsg);
}

HWND ActiveChildWindowFromPoint(HWND hwnd, POINTS cursor_s, const int *controls, size_t controlsCount) {
POINT pt;
RECT controlRect;
HWND controlWindow;

POINTSTOPOINT(pt, cursor_s);

while (controlsCount--) {
controlWindow = GetDlgItem(hwnd, controls[controlsCount]);
if (NULL != controlWindow &&
FALSE != GetClientRect(controlWindow, &controlRect)) {
MapWindowPoints(controlWindow, HWND_DESKTOP, (POINT*)&controlRect, 2);
if (FALSE != PtInRect(&controlRect, pt)) {
unsigned long windowStyle;
windowStyle = (unsigned long)GetWindowLongPtrW(controlWindow, GWL_STYLE);
if (WS_VISIBLE == ((WS_VISIBLE | WS_DISABLED) & windowStyle))
return controlWindow;
break;
}
}
}
return NULL;
}

BOOL DirectMouseWheel_ProcessDialogMessage(HWND hwnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam) {
if (FALSE != IsDirectMouseWheelMessage(uMsg)) {
const int controls[] = {
IDC_XXX,
IDC_YYY,
IDC_ZZZ,

};
HWND targetWindow = ActiveChildWindowFromPoint(hwnd, MAKEPOINTS(lParam), controls, ARRAYSIZE(controls));
if (NULL != targetWindow) {
SendMessage(targetWindow, WM_MOUSEWHEEL, wParam, lParam);
SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (long)TRUE);
return TRUE;
}
}
return FALSE;
}


In DirectMouseWheel_ProcessDialogMessage(..) you just have to change IDC_XXX, IDC_YYY & IDC_ZZZ to be the id of the controls you want to allow the DirectMouseWheel handling on. And that should be it with no wasabi integration issues for those people using non-MSVC compilers, etc.

-daz
DrO is offline   Reply With Quote
Old 22nd June 2012, 19:41   #3
thinktink
Forum King
 
thinktink's Avatar
 
Join Date: May 2009
Location: No longer on the streets of Kings County, CA.
Posts: 3,089
Nice, thanks DrO, didn't know about the need to set the _MSGRESULT long property.
thinktink is offline   Reply With Quote
Old 23rd June 2012, 00:41   #4
DrO
 
Join Date: Sep 2003
Posts: 27,873
i can never remember the full thing behind how and when to use DWLP_MSGRESULT other than that is what was used in the code when it was implemented and since i've not seen any issues reported from its introduction (at least attributable to it) i have to assume it is correct in this instance.

-daz
DrO is offline   Reply With Quote
Old 23rd June 2012, 07:14   #5
thinktink
Forum King
 
thinktink's Avatar
 
Join Date: May 2009
Location: No longer on the streets of Kings County, CA.
Posts: 3,089
Ok, hangon, something's wrong. Probably something more to do with the custom framework I'm developing than anything else but when I set my framework's baseclass to always set _MSGRESULT long to true in response to the direct mouse wheel messages it kills the WC_LISTBOX controls. They don't hang, they just stop painting and responding completely. When I took that out and processed the message the same way as before the ListBox controls stopped malfunctioning.

What's _MSGRESULT for anyways? I've never messed with it myself before so I have no experience with it.
thinktink is offline   Reply With Quote
Old 24th June 2012, 20:33   #6
DrO
 
Join Date: Sep 2003
Posts: 27,873
all i know is DirectMouseWheel_ProcessDialogMessage(..) is done at the end of the dialog proceedure - if you're not using a dialog window (i.e. not created via DialogBox(..) or CreateDialog(..) then sending DWLP_MSGRESULT might cause issues. i can only say have a look on msdn for more details about it (is all i'd be doing anyway).

-daz
DrO is offline   Reply With Quote
Old 24th June 2012, 20:48   #7
thinktink
Forum King
 
thinktink's Avatar
 
Join Date: May 2009
Location: No longer on the streets of Kings County, CA.
Posts: 3,089
Ah, ok, no, I'm not using CreateDialog or DialogBox to construct the window from a resource or anything like that. It's dynamically created at runtime.
thinktink is offline   Reply With Quote
Old 24th June 2012, 21:23   #8
DrO
 
Join Date: Sep 2003
Posts: 27,873
then you don't need to send the dialog specific message

all of Winamp's usage is in dialog based windows which is why it's not an issue.

-daz
DrO is offline   Reply With Quote
Reply
Go Back   Winamp & Shoutcast Forums > Developer Center > Winamp Development

Tags
wm_mousewheel

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump