@@ -96,6 +96,8 @@ typedef LONG NTSTATUS;
9696// x; y
9797#define DESKFLOW_MSG_FAKE_TOUCH DESKFLOW_HOOK_LAST_MSG + 13
9898
99+ #define TOUCH_CLICK_TIMER_ID 1
100+
99101static void send_keyboard_input (WORD wVk, WORD wScan, DWORD dwFlags)
100102{
101103 INPUT inp;
@@ -451,6 +453,9 @@ LRESULT CALLBACK MSWindowsDesks::secondaryDeskProc(HWND hwnd, UINT msg, WPARAM w
451453 SInt32 x = GET_X_LPARAM (lParam);
452454 SInt32 y = GET_Y_LPARAM (lParam);
453455 LOG ((CLOG_DEBUG " secondary WM_POINTERDOWN touch at %d,%d" , x, y));
456+ self->m_pendingTouchUp = true ;
457+ self->m_pendingTouchX = x;
458+ self->m_pendingTouchY = y;
454459 PostThreadMessage (self->m_threadID , DESKFLOW_MSG_TOUCH,
455460 static_cast <WPARAM>(x), static_cast <LPARAM>(y));
456461 return 0 ;
@@ -459,6 +464,47 @@ LRESULT CALLBACK MSWindowsDesks::secondaryDeskProc(HWND hwnd, UINT msg, WPARAM w
459464 break ;
460465 }
461466
467+ case WM_POINTERUP: {
468+ MSWindowsDesks *self = reinterpret_cast <MSWindowsDesks *>(
469+ GetWindowLongPtr (hwnd, GWLP_USERDATA));
470+ if (self && self->m_pendingTouchUp ) {
471+ SInt32 x = self->m_pendingTouchX ;
472+ SInt32 y = self->m_pendingTouchY ;
473+ if (self->m_isOnScreen ) {
474+ self->m_pendingTouchUp = false ;
475+ SetWindowPos (hwnd, HWND_BOTTOM, 0 , 0 , 0 , 0 ,
476+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_HIDEWINDOW);
477+ ULONGLONG elapsed = GetTickCount64 () - self->m_touchHideTime ;
478+ LOG ((CLOG_DEBUG " touch: finger up, hider hidden, %llu ms since shrink, injecting at %d,%d" ,
479+ elapsed, x, y));
480+ PostThreadMessage (GetCurrentThreadId (), DESKFLOW_MSG_FAKE_TOUCH,
481+ static_cast <WPARAM>(x), static_cast <LPARAM>(y));
482+ } else {
483+ self->m_touchLifted = true ;
484+ LOG ((CLOG_DEBUG " touch: finger up before enter, will fire on deskEnter at %d,%d" , x, y));
485+ }
486+ }
487+ return 0 ;
488+ }
489+
490+ case WM_TIMER: {
491+ if (wParam == TOUCH_CLICK_TIMER_ID) {
492+ KillTimer (hwnd, TOUCH_CLICK_TIMER_ID);
493+ MSWindowsDesks *self = reinterpret_cast <MSWindowsDesks *>(
494+ GetWindowLongPtr (hwnd, GWLP_USERDATA));
495+ if (self) {
496+ ULONGLONG elapsed = GetTickCount64 () - self->m_touchHideTime ;
497+ LOG ((CLOG_DEBUG " touch: timer fired, %llu ms after hide, injecting at %d,%d" ,
498+ elapsed, self->m_pendingTouchX , self->m_pendingTouchY ));
499+ PostThreadMessage (GetCurrentThreadId (), DESKFLOW_MSG_FAKE_TOUCH,
500+ static_cast <WPARAM>(self->m_pendingTouchX ),
501+ static_cast <LPARAM>(self->m_pendingTouchY ));
502+ }
503+ return 0 ;
504+ }
505+ break ;
506+ }
507+
462508 case WM_MOUSEMOVE: {
463509 LPARAM extraInfo = GetMessageExtraInfo ();
464510 if ((extraInfo & TOUCH_SIGNATURE_MASK) == TOUCH_SIGNATURE) {
@@ -502,61 +548,112 @@ void MSWindowsDesks::deskMouseMove(SInt32 x, SInt32 y) const
502548
503549void MSWindowsDesks::deskFakeTouchClick (SInt32 x, SInt32 y) const
504550{
505- static bool s_touchReady = false ;
506-
507- if (!s_touchReady) {
508- if (!InitializeTouchInjection (1 , TOUCH_FEEDBACK_DEFAULT)) {
509- DWORD err = GetLastError ();
510- LOG ((CLOG_WARN " touch: InitializeTouchInjection failed, err=%d" , err));
511- deskMouseMove (x, y);
512- send_mouse_input (MOUSEEVENTF_LEFTDOWN, 0 , 0 , 0 );
513- send_mouse_input (MOUSEEVENTF_LEFTUP, 0 , 0 , 0 );
514- return ;
515- }
516- LOG ((CLOG_DEBUG1 " touch: InitializeTouchInjection succeeded" ));
517- s_touchReady = true ;
551+ ULONGLONG elapsed = GetTickCount64 () - m_touchHideTime;
552+
553+ POINT pt = {x, y};
554+ HWND target = WindowFromPoint (pt);
555+ HWND fg = GetForegroundWindow ();
556+
557+ char targetClass[128 ] = {0 };
558+ char targetTitle[128 ] = {0 };
559+ char fgClass[128 ] = {0 };
560+ char fgTitle[128 ] = {0 };
561+ if (target) {
562+ GetClassNameA (target, targetClass, sizeof (targetClass));
563+ GetWindowTextA (target, targetTitle, sizeof (targetTitle));
564+ }
565+ if (fg) {
566+ GetClassNameA (fg, fgClass, sizeof (fgClass));
567+ GetWindowTextA (fg, fgTitle, sizeof (fgTitle));
518568 }
519569
520- POINTER_TOUCH_INFO contact;
521- memset (&contact, 0 , sizeof (POINTER_TOUCH_INFO ));
570+ LOG ((CLOG_DEBUG " touch: injecting at %d,%d %llu ms after hide target= \" %s \" [%s] fg= \" %s \" [%s] " ,
571+ x, y, elapsed, targetTitle, targetClass, fgTitle, fgClass ));
522572
523- contact.pointerInfo .pointerType = PT_TOUCH;
524- contact.pointerInfo .pointerId = 0 ;
525- contact.pointerInfo .ptPixelLocation .x = x;
526- contact.pointerInfo .ptPixelLocation .y = y;
573+ if (target) {
574+ HWND root = GetAncestor (target, GA_ROOT);
575+ if (root && root != fg) {
576+ DWORD targetThread = GetWindowThreadProcessId (root, NULL );
577+ DWORD curThread = GetCurrentThreadId ();
527578
528- contact.touchFlags = TOUCH_FLAG_NONE;
529- contact.touchMask = TOUCH_MASK_CONTACTAREA | TOUCH_MASK_ORIENTATION | TOUCH_MASK_PRESSURE;
530- contact.orientation = 90 ;
531- contact.pressure = 32000 ;
532- contact.rcContact .left = x - 2 ;
533- contact.rcContact .right = x + 2 ;
534- contact.rcContact .top = y - 2 ;
535- contact.rcContact .bottom = y + 2 ;
579+ BOOL attached = FALSE ;
580+ if (targetThread != 0 && targetThread != curThread) {
581+ attached = AttachThreadInput (targetThread, curThread, TRUE );
582+ }
536583
537- contact. pointerInfo . pointerFlags =
538- POINTER_FLAG_DOWN | POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT ;
584+ // bypass foreground lock
585+ mouse_event (MOUSEEVENTF_MOVE, 0 , 0 , 0 , 0 ) ;
539586
540- LOG ((CLOG_DEBUG1 " touch: injecting DOWN at %d,%d" , x, y));
587+ BOOL fgOk = SetForegroundWindow (root);
588+ BringWindowToTop (root);
541589
542- bool injected = false ;
543- if (InjectTouchInput (1 , &contact)) {
544- Sleep (30 );
545- contact.pointerInfo .pointerFlags = POINTER_FLAG_UP;
546- InjectTouchInput (1 , &contact);
547- injected = true ;
548- LOG ((CLOG_DEBUG1 " touch: injected touch at %d,%d" , x, y));
549- } else {
550- DWORD err = GetLastError ();
551- LOG ((CLOG_WARN " touch: InjectTouchInput failed at %d,%d, err=%d" , x, y, err));
590+ char rootTitle[128 ] = {0 };
591+ GetWindowTextA (root, rootTitle, sizeof (rootTitle));
592+
593+ int waited = 0 ;
594+ while (GetForegroundWindow () != root && waited < 200 ) {
595+ Sleep (10 );
596+ waited += 10 ;
597+ }
598+ HWND newFg = GetForegroundWindow ();
599+
600+ if (attached) {
601+ AttachThreadInput (targetThread, curThread, FALSE );
602+ }
603+
604+ LOG ((CLOG_DEBUG " touch: SetForegroundWindow(\" %s\" )=%d, fg=0x%p (root=0x%p) waited=%dms" ,
605+ rootTitle, fgOk, newFg, root, waited));
606+ }
607+ }
608+
609+ static bool touchInitialized = false ;
610+ if (!touchInitialized) {
611+ touchInitialized = InitializeTouchInjection (1 , TOUCH_FEEDBACK_NONE) != 0 ;
612+ LOG ((CLOG_DEBUG " touch: InitializeTouchInjection %s (err=%d)" ,
613+ touchInitialized ? " ok" : " FAILED" , GetLastError ()));
552614 }
553615
554- Sleep (50 );
616+ if (touchInitialized) {
617+ POINTER_TOUCH_INFO contact = {};
618+ contact.pointerInfo .pointerType = PT_TOUCH;
619+ contact.pointerInfo .pointerId = 0 ;
620+ contact.pointerInfo .ptPixelLocation .x = x;
621+ contact.pointerInfo .ptPixelLocation .y = y;
622+ contact.pointerInfo .pointerFlags =
623+ POINTER_FLAG_DOWN | POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT;
624+ contact.touchFlags = TOUCH_FLAG_NONE;
625+ contact.touchMask = TOUCH_MASK_CONTACTAREA | TOUCH_MASK_ORIENTATION |
626+ TOUCH_MASK_PRESSURE;
627+ contact.orientation = 90 ;
628+ contact.pressure = 32000 ;
629+ contact.rcContact .left = x - 2 ;
630+ contact.rcContact .right = x + 2 ;
631+ contact.rcContact .top = y - 2 ;
632+ contact.rcContact .bottom = y + 2 ;
633+
634+ BOOL downOk = InjectTouchInput (1 , &contact);
635+ DWORD downErr = GetLastError ();
636+
637+ Sleep (20 );
638+
639+ contact.pointerInfo .pointerFlags =
640+ POINTER_FLAG_UPDATE | POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT;
641+ BOOL updOk = InjectTouchInput (1 , &contact);
642+
643+ Sleep (20 );
644+
645+ contact.pointerInfo .pointerFlags = POINTER_FLAG_UP;
646+ BOOL upOk = InjectTouchInput (1 , &contact);
647+ DWORD upErr = GetLastError ();
648+
649+ LOG ((CLOG_DEBUG " touch: InjectTouchInput down=%d(err=%d) upd=%d up=%d(err=%d) at %d,%d" ,
650+ downOk, downErr, updOk, upOk, upErr, x, y));
651+ }
555652
556653 deskMouseMove (x, y);
557654 send_mouse_input (MOUSEEVENTF_LEFTDOWN, 0 , 0 , 0 );
558655 send_mouse_input (MOUSEEVENTF_LEFTUP, 0 , 0 , 0 );
559- LOG ((CLOG_DEBUG1 " touch: sent mouse click at %d,%d (touch injected=%d) " , x, y, injected ));
656+ LOG ((CLOG_DEBUG " touch: SendInput mouse click at %d,%d" , x, y));
560657}
561658
562659void MSWindowsDesks::deskMouseRelativeMove (SInt32 dx, SInt32 dy) const
@@ -634,31 +731,54 @@ void MSWindowsDesks::deskEnter(Desk *desk)
634731 ReleaseCapture ();
635732
636733 LONG_PTR exStyle = GetWindowLongPtr (desk->m_window , GWL_EXSTYLE);
637- // let hit-testing pass through to windows underneath
638734 SetWindowLongPtr (desk->m_window , GWL_EXSTYLE, exStyle | WS_EX_TRANSPARENT);
639735 } else {
640736 registerTouchRawInput (desk->m_window , false );
641737 }
642738
643- SetWindowPos (desk->m_window , HWND_BOTTOM, 0 , 0 , 0 , 0 , SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_HIDEWINDOW);
739+ bool touchEnter = false ;
740+ if (m_pendingTouchUp && m_touchLifted) {
741+ touchEnter = true ;
742+ m_pendingTouchUp = false ;
743+ m_touchLifted = false ;
744+ SetWindowPos (desk->m_window , HWND_BOTTOM, 0 , 0 , 0 , 0 ,
745+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_HIDEWINDOW);
746+ m_touchHideTime = GetTickCount64 ();
747+ SetTimer (desk->m_window , TOUCH_CLICK_TIMER_ID, 150 , NULL );
748+ LOG ((CLOG_DEBUG " touch: deskEnter path, hider hidden, timer started at %d,%d" ,
749+ m_pendingTouchX, m_pendingTouchY));
750+ } else if (m_pendingTouchUp) {
751+ touchEnter = true ;
752+ SetWindowPos (desk->m_window , HWND_BOTTOM, m_xCenter, m_yCenter, 1 , 1 ,
753+ SWP_NOACTIVATE);
754+ m_touchHideTime = GetTickCount64 ();
755+ LOG ((CLOG_DEBUG " touch: deskEnter, finger still down, hider shrunk to 1x1" ));
756+ } else {
757+ SetWindowPos (desk->m_window , HWND_BOTTOM, 0 , 0 , 0 , 0 ,
758+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_HIDEWINDOW);
759+ }
644760
645761 setCursorVisibility (true );
646762
647763 HCURSOR arrow = LoadCursor (NULL , IDC_ARROW);
648764 SetClassLongPtr (desk->m_window , GCLP_HCURSOR, reinterpret_cast <LONG_PTR>(arrow));
649765 SetCursor (arrow);
650766
651- // restore the foreground window
652- // XXX -- this raises the window to the top of the Z-order. we
653- // want it to stay wherever it was to properly support X-mouse
654- // (mouse over activation) but i've no idea how to do that.
655- // the obvious workaround of using SetWindowPos() to move it back
656- // after being raised doesn't work.
657- DWORD thisThread = GetWindowThreadProcessId (desk->m_window , NULL );
658- DWORD thatThread = GetWindowThreadProcessId (desk->m_foregroundWindow , NULL );
659- AttachThreadInput (thatThread, thisThread, TRUE );
660- SetForegroundWindow (desk->m_foregroundWindow );
661- AttachThreadInput (thatThread, thisThread, FALSE );
767+ if (touchEnter) {
768+ LOG ((CLOG_DEBUG " touch: skipping foreground restore to preserve fullscreen app focus" ));
769+ } else {
770+ // restore the foreground window
771+ // XXX -- this raises the window to the top of the Z-order. we
772+ // want it to stay wherever it was to properly support X-mouse
773+ // (mouse over activation) but i've no idea how to do that.
774+ // the obvious workaround of using SetWindowPos() to move it back
775+ // after being raised doesn't work.
776+ DWORD thisThread = GetWindowThreadProcessId (desk->m_window , NULL );
777+ DWORD thatThread = GetWindowThreadProcessId (desk->m_foregroundWindow , NULL );
778+ AttachThreadInput (thatThread, thisThread, TRUE );
779+ SetForegroundWindow (desk->m_foregroundWindow );
780+ AttachThreadInput (thatThread, thisThread, FALSE );
781+ }
662782 EnableWindow (desk->m_window , desk->m_lowLevel ? FALSE : TRUE );
663783 desk->m_foregroundWindow = NULL ;
664784}
@@ -864,8 +984,13 @@ bool MSWindowsDesks::parseHidTouch(const RAWINPUT *raw, const HidTouchDevice &de
864984 return false ;
865985 }
866986
867- outX = m_x + static_cast <SInt32>(rawX * m_w / dev.logicalMaxX );
868- outY = m_y + static_cast <SInt32>(rawY * m_h / dev.logicalMaxY );
987+ // Touch digitizer maps to the primary monitor, not the virtual desktop
988+ SInt32 pw = GetSystemMetrics (SM_CXSCREEN);
989+ SInt32 ph = GetSystemMetrics (SM_CYSCREEN);
990+ outX = static_cast <SInt32>(rawX * pw / dev.logicalMaxX );
991+ outY = static_cast <SInt32>(rawY * ph / dev.logicalMaxY );
992+ LOG ((CLOG_DEBUG1 " touch HID: raw=%lu,%lu logMax=%lu,%lu primary=%dx%d -> %d,%d" ,
993+ rawX, rawY, dev.logicalMaxX , dev.logicalMaxY , pw, ph, outX, outY));
869994 return true ;
870995}
871996
0 commit comments