TNB Library
TnbRawMouseWatcher.h
[詳解]
1#pragma once
12#include "TnbDummyWindow.h"
13#include "TnbSimpleVector.h"
14#include "TnbStr.h"
15#include "TnbVector.h"
16#include "TnbWindowsVersion.h"
17#include <dbt.h>
18
19
20
21//TNB Library
22namespace TNB
23{
24
25
26
27#ifndef _TnbDOXYGEN //Document作成用シンボル
28_SELECTANY HHOOK s_hTnbRmwLowMouse = NULL;
29#endif
30
31
32
86{
87 DEFSUPER(IMultiMouseWatcher);
88public:
89
92 {
93 Refresh();
94 }
95
101 {
102 m_in.m_pListener = P;
103 }
104
109 virtual size_t GetMouseCount(void) const
110 {
111 return m_in.m_devsList.GetSize();
112 }
113
119 virtual bool Start(void)
120 {
121 Stop();
122 if ( m_win.Create(&m_in) )
123 {
124 RAWINPUTDEVICE rid;
125 rid.usUsagePage = 1/*HID_USAGE_PAGE_GENERIC*/;
126 rid.usUsage = 2/*HID_USAGE_GENERIC_MOUSE*/;
127 rid.dwFlags = RIDEV_INPUTSINK;
128 rid.hwndTarget = m_win;
129 if ( ::RegisterRawInputDevices(&rid, 1, sizeof(rid)) )
130 {
131 return true;
132 }
133 }
134 Stop();
135 return false;
136 }
137
141 virtual void Stop(void)
142 {
143 m_win.Destroy();
144 }
145
153 bool GetMouseSpecified(RID_DEVICE_INFO_MOUSE& _spec, INDEX index) const
154 {
155 if ( GetMouseCount() > index )
156 {
157 _spec = m_in.m_devsList[index].specified;
158 return true;
159 }
160 return false;
161 }
162
168 CStr GetMouseName(INDEX index) const
169 {
170 if ( GetMouseCount() > index )
171 {
172 return m_in.m_devsList[index].name;
173 }
174 return _T("");
175 }
176
184 bool Refresh(void)
185 {
186 return m_in.Refresh();
187 }
188
199 static bool EnableMouseCursor(bool isEnable)
200 {
201 if ( ! isEnable )
202 {
203 //無効へ
204 if ( s_hTnbRmwLowMouse == NULL )
205 {
206 s_hTnbRmwLowMouse = ::SetWindowsHookEx(WH_MOUSE_LL, ms_OnLowLevelMouseProc, GetInstanceHandleByTnb(), 0);
207 }
208 return s_hTnbRmwLowMouse != NULL;
209 }
210 // 有効へ
211 if ( s_hTnbRmwLowMouse != NULL )
212 {
213 ::UnhookWindowsHookEx(s_hTnbRmwLowMouse);
214 s_hTnbRmwLowMouse = NULL;
215 }
216 return s_hTnbRmwLowMouse == NULL;
217 }
218
230 static void MouseEvent(DWORD flags, DWORD dx = 0,DWORD dy = 0, DWORD data = 0)
231 {
232 ::mouse_event(flags, dx, dy, data, COOKIE);
233 }
234
244 static void HorizontalWheelEvent(int delta)
245 {
246 //CWindowsVersion wv; @todo VISTA でも HWHEEL メッセージに対応していないウィンドウが多い・・・。
247 //if ( wv.GetMajorVersion() >= 6 )
248 //{
249 // MouseEvent(0x1000/*MOUSEEVENTF_HWHEEL*/, 0, 0, delta);
250 // return;
251 //}
252 // 現在のフォアグラウンドのウィンドウ取得
253 HWND hWnd = ::GetForegroundWindow();
254 TTRACE1("GetForegroundWindow = %08X\n", hWnd);
255 // アクティブなウインドウのスレッドIDを取得する
256 DWORD dwCurrentThreadId = ::GetCurrentThreadId();
257 DWORD dwActiveProcessId;
258 DWORD dwActiveThreadId = ::GetWindowThreadProcessId(hWnd, &dwActiveProcessId);
259 if ( ::AttachThreadInput(dwCurrentThreadId, dwActiveThreadId, TRUE) )
260 {
261 hWnd = ::GetFocus();
262 if ( hWnd != NULL )
263 {
264 LONG style = ::GetWindowLong(hWnd, GWL_STYLE);
265 bool hasScrollBar = (style & WS_HSCROLL) != 0;
266 #ifdef _DEBUG
267 CStr s;
268 ::GetClassName(hWnd, s.GetBuffer(100), 100);
269 s.ReleaseBuffer();
270 TTRACE3(" GetFocus = %08X %d, [%s]\n", hWnd, hasScrollBar, s);
271 #endif
272 bool isDisp = false;
273 if ( ! hasScrollBar )
274 {
275 // memo; WORD は兄弟に ScrollBar がある。
276 isDisp = ms_SubWheel(hWnd, delta);
277 if ( ! isDisp )
278 {
279 // memo; EXCEL は一個下に ScrollBar がある。
280 HWND h = ::GetWindow(hWnd, GW_CHILD);
281 isDisp = ms_SubWheel(h, delta);
282 }
283 }
284 if ( ! isDisp )
285 {
286 ms_PostWheel(hWnd, NULL, delta);
287 }
288 }
289 // 開放する
290 ::AttachThreadInput(dwCurrentThreadId, dwActiveThreadId, FALSE);
291 }
292 }
293
294private:
295
296 enum
297 {
298 COOKIE = 'TbMm'
299 };
300
302 struct TInfo : TNB::IComparableT<TInfo>
303 {
304 HANDLE handle;
305 CStr name;
306 RID_DEVICE_INFO_MOUSE specified;
307 TInfo(void) : handle(INVALID_HANDLE_VALUE)
308 {
309 }
310 virtual INT_PTR Compare(const TInfo& t) const
311 {
312 return name.Compare(t.name);
313 }
314 };
316 class CInner : public CDummyWindow::IListener
317 {
318 DEFPARENTLISTENER(IMultiMouseWatcher, IParentListener);
319 public:
321 CInner(void) : m_pListener(NULL), m_lastHandle(INVALID_HANDLE_VALUE)
322 {
323 }
329 virtual bool OnWindowMessage(LRESULT& _result, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
330 {
331 if ( m_pListener != NULL )
332 {
333 if ( message == WM_DEVICECHANGE && wParam == DBT_DEVNODES_CHANGED )
334 {
335 CVectorT<TInfo> newInf;
336 if ( Collect(newInf) )
337 {
338 loop ( i, m_devsList )
339 {
340 INDEX idx = newInf.Find(m_devsList[i]);
341 if ( idx != INVALID_INDEX )
342 {
343 // 再発見された
344 if ( m_devsList[i].handle == INVALID_HANDLE_VALUE )
345 {
346 m_devsList[i] = newInf[idx];
347 m_pListener->OnMultiMouseDevice(i, _super::IListener::Device_Reconnect);
348 }
349 }
350 else
351 {
352 // 無くなった
353 if ( m_devsList[i].handle != INVALID_HANDLE_VALUE )
354 {
355 m_devsList[i].handle = INVALID_HANDLE_VALUE;
356 m_pListener->OnMultiMouseDevice(i, _super::IListener::Device_Remove);
357 }
358 }
359 }
360 loop ( i, newInf )
361 {
362 INDEX idx = m_devsList.Find(newInf[i]);
363 if ( idx == INVALID_INDEX )
364 {
365 // 新規発見
366 int x = m_devsList.Add(newInf[i]);
367 m_pListener->OnMultiMouseDevice(x, _super::IListener::Device_Connect);
368 }
369 }
370 }
371 }
372 else if ( message == WM_INPUT )
373 {
374 UINT size = 40;
375 CWorkMem w(size);
376 ::GetRawInputData(reinterpret_cast<HRAWINPUT>(lParam), RID_INPUT, w, &size, sizeof(RAWINPUTHEADER));
377 RAWINPUT* P = reinterpret_cast<RAWINPUT*>(w.Ref());
378 if ( P->header.dwType == RIM_TYPEMOUSE )
379 {
380 INDEX idx = INVALID_INDEX;
381 if ( m_lastHandle == P->header.hDevice )
382 {
383 idx = m_lastIndex;
384 }
385 else
386 {
387 loop ( i, m_devsList.GetSize() )
388 {
389 if ( m_devsList[i].handle == P->header.hDevice )
390 {
391 idx = i;
392 m_lastHandle = P->header.hDevice;
393 m_lastIndex = idx;
394 break;
395 }
396 }
397 }
398 if ( idx != INVALID_INDEX )
399 {
400 m_pListener->OnMultiMouseEvent(idx, P->data.mouse);
401 }
402 }
403 }
404 }
405 return false;
406 }
408 bool Refresh(void)
409 {
410 m_lastHandle = INVALID_HANDLE_VALUE;
411 return Collect(m_devsList);
412 }
414 bool Collect(CVectorT<TInfo>& _info)
415 {
416 _info.RemoveAll();
417 UINT count = 0;
418 UINT rr = ::GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST));
419 if ( rr != 0 )
420 {
421 return false;
422 }
423 UINT d1 = count;
425 UINT d2 = ::GetRawInputDeviceList(devs, &count, sizeof(RAWINPUTDEVICELIST));
426 if ( d1 != d2 )
427 {
428 return false;
429 }
430 loop ( i, devs.GetSize() )
431 {
432 if ( devs[i].dwType != RIM_TYPEMOUSE )
433 {
434 continue;
435 }
436 TInfo info;
437 info.handle = devs[i].hDevice;
438 // スペック取得
439 RID_DEVICE_INFO dev;
440 UINT size = sizeof(RID_DEVICE_INFO);
441 if ( ::GetRawInputDeviceInfo(info.handle, RIDI_DEVICEINFO, &dev, &size) > 0 )
442 {
443 info.specified = dev.mouse;
444 // デバイス名の長さ取得
445 if ( ::GetRawInputDeviceInfo(info.handle, RIDI_DEVICENAME, NULL, &size) == 0 )
446 {
447 // デバイス名取得
448 CWorkMemT<TCHAR> name(size + 1);
449 int r = ::GetRawInputDeviceInfo(info.handle, RIDI_DEVICENAME, name, &size);
450 if ( r > 0 )
451 {
452 info.name = name.Ref();
453 }
454 }
455 _info.Add(info);
456 }
457 }
458 return true;
459 }
460 IParentListener* m_pListener;
461 CVectorT<TInfo> m_devsList;
462 mutable HANDLE m_lastHandle;
463 mutable INDEX m_lastIndex;
464 };
469 static LRESULT CALLBACK ms_OnLowLevelMouseProc(int nCode, WPARAM wParam, LPARAM lParam)
470 {
471 if ( nCode == HC_ACTION )
472 {
473 MSLLHOOKSTRUCT* P = reinterpret_cast<MSLLHOOKSTRUCT*>(lParam);
474 TTRACE3("OnLowLevelMouseProc msg=0x%X, x=%d, y=%d\n", wParam, P->pt.x, P->pt.y);
475 TTRACE3(" data=%d, flags=0x%X, ex-info=%d\n", P->mouseData, P->flags, P->dwExtraInfo);
476 if ( P->dwExtraInfo != COOKIE )
477 {
478 return 1;
479 }
480 }
481 return ::CallNextHookEx(s_hTnbRmwLowMouse, nCode, wParam, lParam);
482 }
483 // CLASS 名チェック
484 static bool ms_IsScrollBar(LPCTSTR P)
485 {
486 const TCHAR src[] = _T("ScrollBar");
487 loop ( i, sizeof(src) )
488 {
489 if ( src[i] != P[i] )
490 {
491 return false;
492 }
493 }
494 return true;
495 }
496 // 送信
497 static void ms_PostWheel(HWND hTargetWnd, HWND hCtrlWnd, int delta)
498 {
499 UINT msg = WM_HSCROLL;
500 WPARAM wp = (delta < 0) ? SB_LINELEFT : SB_LINERIGHT;
501 LPARAM lp = reinterpret_cast<LPARAM>(hCtrlWnd);
502 int l = ((delta < 0) ? -delta : delta) / WHEEL_DELTA;
503 loop ( i, l )
504 {
505 ::PostMessage(hTargetWnd, msg, wp, lp);
506 }
507 ::PostMessage(hTargetWnd, msg, SB_ENDSCROLL, lp);
508 }
509 // サブ
510 static bool ms_SubWheel(HWND hWnd, int delta)
511 {
512 CWorkMemT<TCHAR> buf(100);
513 HWND h = ::GetWindow(hWnd, GW_HWNDNEXT);
514 while ( h != NULL )
515 {
516 ::GetClassName(h, buf, 100);
517 LONG style = ::GetWindowLong(h, GWL_STYLE);
518 bool isVisible = (style & WS_VISIBLE) != 0;
519 bool isHorz = (style & (SBS_VERT | SBS_SIZEBOX)) == 0;
520 if ( ms_IsScrollBar(buf) && isVisible && isHorz )
521 {
522 TTRACE2(" Found ScrollBar = %08X, [%s]\n", h, buf.Ref());
523 HWND h2 = ::GetParent(h);
524 if ( h2 != NULL )
525 {
526 // 親に通知
527 ms_PostWheel(h2, h, delta);
528 return true;
529 }
530 }
531 h = ::GetWindow(h, GW_HWNDNEXT);
532 }
533 return false;
534 }
535 CInner m_in;
536 CDummyWindow m_win;
537};
538
539
540
541}; //TNB
542
543
544
#define loop(VAR, CNT)
loop構文.
Definition: TnbDef.h:343
ダミーウィンドウ関係のヘッダ
マルチマウス管理関係のヘッダ
簡易配列型情報管理関係のヘッダ
文字列管理関係のヘッダ
配列型情報管理関係のヘッダ
ウィンドウズバージョン関係のヘッダ
ダミーウィンドウクラス
void Destroy(void)
[操作] ウィンドウの破棄.
bool Create(CDummyWindow::IListener *I, LPCTSTR lpszTitle=NULL, LPCTSTR lpszClassName=NULL, DWORD dwStyle=WS_OVERLAPPEDWINDOW, HMENU hMenu=NULL, HWND hParent=NULL)
[操作] ウィンドウの作成.
生マウス監視クラス
virtual void Stop(void)
[設定] 監視停止.
static bool EnableMouseCursor(bool isEnable)
[設定] システムマウス有効無効設定.
bool GetMouseSpecified(RID_DEVICE_INFO_MOUSE &_spec, INDEX index) const
[取得] マウススペック取得.
virtual void SetListener(IMultiMouseWatcher::IListener *P)
[登録] リスナー登録.
static void HorizontalWheelEvent(int delta)
[設定] システム水平ホイールイベント.
bool Refresh(void)
[設定] 再検索.
CRawMouseWatcher(void)
コンストラクタ
static void MouseEvent(DWORD flags, DWORD dx=0, DWORD dy=0, DWORD data=0)
[設定] システムマウスイベント.
CStr GetMouseName(INDEX index) const
[取得] マウスデバイス名取得.
virtual size_t GetMouseCount(void) const
[取得] マウス数取得.
virtual bool Start(void)
[設定] 監視開始.
int Compare(const TYP *lpszSubject) const
[確認] 文字列比較
Definition: TnbStr.h:658
void ReleaseBuffer(void)
[操作] 割り当てたバッファを開放.
Definition: TnbStr.h:954
TYP * GetBuffer(size_t iLength=0)
[操作] 書き込みバッファ要求.
Definition: TnbStr.h:914
virtual bool RemoveAll(void)
[削除] 空化
Definition: TnbVector.h:565
virtual INDEX Add(const TYP &t)
[追加] 要素一つ追加.
Definition: TnbVector.h:383
int Compare(LPCSTR P1, LPCSTR P2, INT_PTR len=-1, DWORD dwCmpFlags=0)
[比較] 文字列比較(ASCII/SJIS用)
Definition: TnbStrLib.h:135
HINSTANCE GetInstanceHandleByTnb(EInstanceType type=EI_Process)
[取得] インスタンスハンドル取得.
Definition: TnbDef.h:1341
TNB Library
Definition: TnbDoxyTitle.txt:2
ダミーウィンドウクラスのリスナーインターフェース
比較機能インターフェース.
Definition: TnbComparable.h:54
INDEX Find(const IChecker &checker, INDEX startIndex=0, bool boIsReverse=false) const
[検索] 条件一致要素の検索.
マルチマウス監視のリスナー
マルチマウス監視インターフェース.