Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2713 lines
93 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: focusact.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * History:
  7. * 11-08-90 DavidPe Created.
  8. * 02-11-91 JimA Multi-desktop support.
  9. * 02-13-91 mikeke Added Revalidation code.
  10. * 06-10-91 DavidPe Changed to desynchronized model.
  11. \***************************************************************************/
  12. #include "precomp.h"
  13. #pragma hdrstop
  14. BOOL RemoveEventMessage(PQ pq, DWORD dwQEvent, DWORD dwQEventStop);
  15. /***************************************************************************\
  16. * xxxDeactivate
  17. *
  18. * This routine does the processing for the event posted when the foreground
  19. * thread changes. Note the difference in order of assignment vs. message
  20. * sending in the focus and active windows. This is consistent with how
  21. * things are done in Win 3.1.
  22. *
  23. *
  24. * PTHREADINFO pti May not be ptiCurrent if SetForegroundWindow called from
  25. * minmax
  26. *
  27. * History:
  28. * 06-07-91 DavidPe Created.
  29. \***************************************************************************/
  30. void xxxDeactivate(
  31. PTHREADINFO pti, // May not be ptiCurrent
  32. DWORD tidSetForeground)
  33. {
  34. PWND pwndLose;
  35. AAS aas;
  36. TL tlpwndCapture;
  37. TL tlpwndChild;
  38. TL tlpwndLose;
  39. TL tlpti;
  40. TL tlptiLose;
  41. WPARAM wParam;
  42. PTHREADINFO ptiLose;
  43. PTHREADINFO ptiCurrent = PtiCurrent();
  44. BOOL fSetActivateAppBit = FALSE;
  45. /*
  46. * If we're not active, we have nothing to deactivate, so just return.
  47. * If we don't return, we'll send redundant WM_ACTIVATEAPP messages.
  48. * Micrografx Draw, for example, calls FreeProcInstance() twice when
  49. * this occurs, thereby crashing.
  50. */
  51. if (pti->pq->spwndActive == NULL)
  52. return;
  53. /*
  54. * If pti != ptiCurrent, thread lock pti because we may leave
  55. * the critical section.
  56. */
  57. if (pti != ptiCurrent)
  58. ThreadLockPti(ptiCurrent, pti, &tlpti);
  59. /*
  60. * Prevent an activating WM_ACTIVATEAPP from being sent
  61. * while we're processing this event.
  62. */
  63. if (!(pti->TIF_flags & TIF_INACTIVATEAPPMSG)) {
  64. pti->TIF_flags |= TIF_INACTIVATEAPPMSG;
  65. fSetActivateAppBit = TRUE;
  66. }
  67. /*
  68. * Cancel any modes like move/size and menu tracking.
  69. */
  70. if (pti->pq->spwndCapture != NULL) {
  71. ThreadLockAlwaysWithPti(ptiCurrent, pti->pq->spwndCapture, &tlpwndCapture);
  72. xxxSendMessage(pti->pq->spwndCapture, WM_CANCELMODE, 0, 0);
  73. ThreadUnlock(&tlpwndCapture);
  74. /*
  75. * Set QS_MOUSEMOVE so any sleeping modal loops,
  76. * like the move/size code, will wake up and figure
  77. * out that it should abort.
  78. */
  79. SetWakeBit(pti, QS_MOUSEMOVE);
  80. }
  81. /*
  82. * See the comments in xxxActivateThisWindow about Harvard Graphics.
  83. * WinWord's Equation editor does some games when it gets the WM_ACTIVATE
  84. * so we have to remember to send the WM_ACTIVATEAPP to ptiLose. 22510
  85. */
  86. if (pti->pq->spwndActive != NULL) {
  87. pwndLose = pti->pq->spwndActive;
  88. ptiLose = GETPTI(pwndLose);
  89. ThreadLockPti(ptiCurrent, ptiLose, &tlptiLose);
  90. ThreadLockAlwaysWithPti(ptiCurrent, pwndLose, &tlpwndLose);
  91. wParam = MAKELONG(WA_INACTIVE, TestWF(pwndLose, WFMINIMIZED));
  92. if (!xxxSendMessage(pwndLose, WM_NCACTIVATE, WA_INACTIVE, 0)) {
  93. ThreadUnlock(&tlpwndLose);
  94. ThreadUnlockPti(ptiCurrent, &tlptiLose);
  95. goto Exit;
  96. }
  97. xxxSendMessage(pwndLose, WM_ACTIVATE, wParam, 0);
  98. /*
  99. * Only update the queue's active windows if they weren't
  100. * changed while we were off calling SendMessage.
  101. */
  102. if (pti->pq->spwndActive == pwndLose) {
  103. Lock(&pti->pq->spwndActivePrev, pti->pq->spwndActive);
  104. Unlock(&pti->pq->spwndActive);
  105. }
  106. /*
  107. * The flag WFFRAMEON is cleared in the default processing of
  108. * WM_NCACTIVATE message.
  109. * We want to clear this flag again here since it might of been
  110. * set in xxxSendNCPaint.
  111. * Pbrush calls DrawMenuBar when it gets the WM_ACTIVATE message
  112. * sent above and this causes xxxSendNCPaint to get called and the
  113. * WFFRAMEON flag gets reset.
  114. */
  115. ClrWF(pwndLose, WFFRAMEON);
  116. ThreadUnlock(&tlpwndLose);
  117. /*
  118. * Revalidate ptiLose because the thread may have gone away
  119. * when the activation messages were sent above.
  120. */
  121. aas.ptiNotify = (ptiLose->TIF_flags & TIF_INCLEANUP) ? NULL : ptiLose;
  122. ThreadUnlockPti(ptiCurrent, &tlptiLose);
  123. } else {
  124. /*
  125. * Use a non-NULL special value for the test after
  126. * the xxxActivateApp calls.
  127. */
  128. pwndLose = (PWND)-1;
  129. aas.ptiNotify = pti;
  130. }
  131. if (aas.ptiNotify) {
  132. aas.tidActDeact = tidSetForeground;
  133. aas.fActivating = FALSE;
  134. aas.fQueueNotify = FALSE;
  135. ThreadLockWithPti(ptiCurrent,
  136. pti->rpdesk->pDeskInfo->spwnd->spwndChild, &tlpwndChild);
  137. xxxInternalEnumWindow(pti->rpdesk->pDeskInfo->spwnd->spwndChild,
  138. (WNDENUMPROC_PWND)xxxActivateApp, (LPARAM)&aas, BWL_ENUMLIST);
  139. ThreadUnlock(&tlpwndChild);
  140. }
  141. /*
  142. * If an app (i.e. Harvard Graphics/Windows Install) tries to
  143. * reactivate itself during a deactivating WM_ACTIVATEAPP
  144. * message, force deactivation.
  145. */
  146. if (pti->pq->spwndActive == pwndLose) {
  147. ThreadLockWithPti(ptiCurrent, pwndLose, &tlpwndLose);
  148. if (!xxxSendMessage(pwndLose, WM_NCACTIVATE, WA_INACTIVE, 0)) {
  149. ThreadUnlock(&tlpwndLose);
  150. goto Exit;
  151. }
  152. xxxSendMessage(pwndLose, WM_ACTIVATE, WA_INACTIVE, 0);
  153. ThreadUnlock(&tlpwndLose);
  154. /*
  155. * Only update the queue's active windows if they weren't
  156. * changed while we were off calling SendMessage.
  157. */
  158. if (pti->pq->spwndActive == pwndLose) {
  159. Lock(&pti->pq->spwndActivePrev, pti->pq->spwndActive);
  160. Unlock(&pti->pq->spwndActive);
  161. }
  162. }
  163. if (pti->pq->spwndFocus != NULL) {
  164. pwndLose = Unlock(&pti->pq->spwndFocus);
  165. if (pwndLose != NULL) {
  166. ThreadLockAlwaysWithPti(ptiCurrent, pwndLose, &tlpwndLose);
  167. xxxSendMessage(pwndLose, WM_KILLFOCUS, 0, 0);
  168. #ifdef FE_IME
  169. if (IS_IME_ENABLED()) {
  170. xxxFocusSetInputContext(pwndLose, FALSE, FALSE);
  171. }
  172. #endif
  173. ThreadUnlock(&tlpwndLose);
  174. }
  175. }
  176. Exit:
  177. if (fSetActivateAppBit) {
  178. pti->TIF_flags &= ~TIF_INACTIVATEAPPMSG;
  179. }
  180. if (pti != ptiCurrent)
  181. ThreadUnlockPti(ptiCurrent, &tlpti);
  182. }
  183. /***************************************************************************\
  184. * xxxSendFocusMessages
  185. *
  186. * Common routine for xxxSetFocus() and xxxActivateWindow() that sends the
  187. * WM_KILLFOCUS and WM_SETFOCUS messages to the windows losing and
  188. * receiving the focus. This function also sets the local pwndFocus
  189. * to the pwnd receiving the focus.
  190. *
  191. * History:
  192. * 11-08-90 DavidPe Ported.
  193. * 06-06-91 DavidPe Rewrote for local pwndFocus/pwndActive in THREADINFO.
  194. \***************************************************************************/
  195. void xxxSendFocusMessages(
  196. PTHREADINFO pti,
  197. PWND pwndReceive)
  198. {
  199. PWND pwndLose;
  200. TL tlpwndLose;
  201. CheckLock(pwndReceive);
  202. /*
  203. * Remember if this app set the focus to NULL on purpose after it was
  204. * activated (needed in ActivateThisWindow()).
  205. */
  206. pti->pq->QF_flags &= ~QF_FOCUSNULLSINCEACTIVE;
  207. if (pwndReceive == NULL && pti->pq->spwndActive != NULL)
  208. pti->pq->QF_flags |= QF_FOCUSNULLSINCEACTIVE;
  209. pwndLose = pti->pq->spwndFocus;
  210. ThreadLockWithPti(pti, pwndLose, &tlpwndLose);
  211. /*
  212. * We shouldn't be locking a valid pwnd from another queue.
  213. */
  214. UserAssert((pwndReceive == NULL)
  215. || TestWF(pwndReceive, WFDESTROYED)
  216. || (pti->pq == GETPTI(pwndReceive)->pq));
  217. Lock(&pti->pq->spwndFocus, pwndReceive);
  218. if (pwndReceive == NULL) {
  219. if (pwndLose != NULL) {
  220. /*
  221. * Tell the client that nobody is gaining focus.
  222. */
  223. xxxWindowEvent(EVENT_OBJECT_FOCUS, NULL, OBJID_CLIENT, INDEXID_OBJECT, 0);
  224. xxxSendMessage(pwndLose, WM_KILLFOCUS, 0, 0);
  225. #ifdef FE_IME
  226. if (IS_IME_ENABLED()) {
  227. xxxFocusSetInputContext(pwndLose, FALSE, FALSE);
  228. }
  229. #endif
  230. }
  231. } else {
  232. /*
  233. * Make this thread foreground so its base
  234. * priority get set higher.
  235. */
  236. if (pti->pq == gpqForeground)
  237. SetForegroundThread(GETPTI(pwndReceive));
  238. if (pwndLose != NULL) {
  239. xxxSendMessage(pwndLose, WM_KILLFOCUS, (WPARAM)HWq(pwndReceive), 0);
  240. #ifdef FE_IME
  241. if (IS_IME_ENABLED()) {
  242. xxxFocusSetInputContext(pwndLose, FALSE, FALSE);
  243. }
  244. #endif
  245. }
  246. /*
  247. * Send the WM_SETFOCUS message, but only if the window we're
  248. * setting the focus to still has the focus! This allows apps
  249. * to prevent themselves from losing the focus by catching
  250. * the WM_NCACTIVATE message and returning FALSE or by calling
  251. * SetFocus() inside their WM_KILLFOCUS handler.
  252. */
  253. if (pwndReceive == pti->pq->spwndFocus) {
  254. #ifdef FE_IME
  255. if (IS_IME_ENABLED()) {
  256. xxxFocusSetInputContext(pwndReceive, TRUE, FALSE);
  257. }
  258. #endif
  259. /*
  260. * We have to do this BEFORE sending the WM_SETFOCUS message.
  261. * The app, upon receiving it, very well may turn around and
  262. * SetFocus() to a child window.
  263. */
  264. xxxWindowEvent(EVENT_OBJECT_FOCUS, pwndReceive, OBJID_CLIENT, INDEXID_OBJECT, 0);
  265. xxxSendMessage(pwndReceive, WM_SETFOCUS, (WPARAM)HW(pwndLose), 0);
  266. }
  267. }
  268. ThreadUnlock(&tlpwndLose);
  269. }
  270. /***************************************************************************\
  271. * xxxActivateApp
  272. *
  273. * xxxEnumWindows call-back function to send the WM_ACTIVATEAPP
  274. * message to the appropriate windows.
  275. *
  276. * We search for windows whose pq == HIWORD(lParam). Once we find
  277. * one, we send a WM_ACTIVATEAPP message to that window. The wParam
  278. * of the message is FALSE if the app is losing the activation and
  279. * TRUE if the app is gaining the activation. The lParam is the
  280. * task handle of the app gaining the activation if wParam is FALSE
  281. * and the task handle of the app losing the activation if wParam
  282. * is TRUE.
  283. *
  284. * lParam = (HIWORD) : pq of app that we are searching for
  285. * (LOWORD) : pq of app that we notify about
  286. *
  287. * fDoActivate = TRUE : Send activate
  288. * FALSE : Send deactivate
  289. *
  290. * History:
  291. * 11-08-90 DavidPe Ported.
  292. * 06-26-91 DavidPe Changed for desync focus/activation.
  293. \***************************************************************************/
  294. BOOL xxxActivateApp(
  295. PWND pwnd,
  296. AAS *paas)
  297. {
  298. CheckLock(pwnd);
  299. if (GETPTI(pwnd) == paas->ptiNotify) {
  300. if (paas->fQueueNotify) {
  301. QueueNotifyMessage(pwnd, WM_ACTIVATEAPP, paas->fActivating,
  302. paas->tidActDeact);
  303. } else {
  304. xxxSendMessage(pwnd, WM_ACTIVATEAPP, paas->fActivating,
  305. paas->tidActDeact);
  306. }
  307. }
  308. return TRUE;
  309. }
  310. /***************************************************************************\
  311. * FBadWindow
  312. *
  313. *
  314. * History:
  315. * 11-08-90 DavidPe Ported.
  316. \***************************************************************************/
  317. BOOL FBadWindow(
  318. PWND pwnd)
  319. {
  320. return (pwnd == NULL
  321. || !TestWF(pwnd, WFVISIBLE)
  322. || TestWF(pwnd, WFDISABLED));
  323. }
  324. void xxxUpdateTray(PWND pwnd)
  325. {
  326. PWND pwndT;
  327. CheckLock(pwnd);
  328. if (!TestWF(pwnd, WFVISIBLE)) {
  329. return;
  330. }
  331. for (pwndT = pwnd; pwndT->spwndOwner; pwndT = pwndT->spwndOwner) {
  332. }
  333. // Notify the shell hook about this activation change
  334. if ( GETPTI(pwndT)->pq == gpqForeground &&
  335. FDoTray() &&
  336. (FCallHookTray() || FPostTray(pwndT->head.rpdesk)) &&
  337. FTopLevel(pwndT) &&
  338. TestWF(pwndT, WFVISIBLE))
  339. {
  340. BOOL fFirstTry;
  341. BOOL fTryAgain;
  342. PWND pwndArg;
  343. TL tlpwndArg;
  344. fFirstTry = TRUE;
  345. do {
  346. fTryAgain = FALSE;
  347. if (TestWF(pwndT, WFWIN40COMPAT)) {
  348. if (TestWF(pwnd, WFWIN40COMPAT) && IsTrayWindow(pwnd)) {
  349. pwndArg = pwnd;
  350. } else {
  351. pwndArg = IsTrayWindow(pwndT) ? pwndT : NULL;
  352. }
  353. } else {
  354. if (TestWF(pwndT, WEFTOOLWINDOW)) {
  355. pwndArg = NULL;
  356. } else if (FHas31TrayStyles(pwndT)) {
  357. pwndArg = Is31TrayWindow(pwndT) ? pwndT : NULL;
  358. } else if (fFirstTry && (pwndT = pwndT->spwndLastActive)) {
  359. fFirstTry = FALSE;
  360. fTryAgain = TRUE;
  361. } else {
  362. return;
  363. }
  364. }
  365. } while (fTryAgain);
  366. ThreadLock(pwndArg, &tlpwndArg);
  367. xxxSetTrayWindow(
  368. (pwndArg) ? pwndArg->head.rpdesk : pwndT->head.rpdesk,
  369. pwndArg,
  370. NULL);
  371. ThreadUnlock(&tlpwndArg);
  372. }
  373. }
  374. /***************************************************************************\
  375. * xxxActivateThisWindow
  376. *
  377. * This function is the workhorse for window activation. It will attempt to
  378. * activate the pwnd specified. The other parameters are defined as:
  379. *
  380. * fFlags This is a flag-mask which defines how the routine is called.
  381. * These flags are defined as follows:
  382. *
  383. * ATW_MOUSE This is set if activation is changing due to a
  384. * mouse click and not set if some other action
  385. * caused this window to be activated. This bit
  386. * determines the value of wParam on the
  387. * WM_ACTIVATE message.
  388. *
  389. * ATW_SETFOCUS This parameter is set if this routine should
  390. * set the focus to NULL. If we are called from
  391. * the xsxSetFocus() function this will not be
  392. * set indicating that we shouldn't mess with the
  393. * focus. Normally (if we are not called from
  394. * xxxSetFocus), we set the focus to NULL here
  395. * and either the app or xxxDefWindowProc() sets
  396. * the focus to the appropriate window. If the
  397. * bit is not set, we don't want to do anything
  398. * with focus. The app may still do a call to
  399. * xxxSetFocus() when the WM_ACTIVATE comes
  400. * through, but it will just be redundant on its
  401. * part.
  402. *
  403. * ATW_ASYNC This bit is set if we are processing this
  404. * routine from an asynchronous activate (i.e.
  405. * xxxProcessEventMessage()). In this case, we
  406. * make sure that we are the foreground queue
  407. * before determining if we bring the window to
  408. * top.
  409. *
  410. * History:
  411. * 11-08-90 DavidPe Ported.
  412. * 05-01-95 ChrisWil changed bool-flags to 1 ATW_ type.
  413. \***************************************************************************/
  414. BOOL xxxActivateThisWindow(
  415. PWND pwnd,
  416. DWORD tidLoseForeground,
  417. DWORD fFlags)
  418. {
  419. PTHREADINFO ptiCurrent = PtiCurrent();
  420. PWND pwndT, pwndActivePrev, pwndActiveSave;
  421. TL tlpwndActive;
  422. TL tlpwndChild;
  423. TL tlpwndActivePrev;
  424. WPARAM wParam;
  425. BOOL fSetActivateAppBit;
  426. BOOL fMouse = (BOOL)(fFlags & ATW_MOUSE);
  427. BOOL fSetFocus = (BOOL)(fFlags & ATW_SETFOCUS);
  428. BOOL fAsync = (BOOL)(fFlags & ATW_ASYNC);
  429. #if DBG
  430. PQ pqSave = ptiCurrent->pq;
  431. #endif
  432. CheckLock(pwnd);
  433. /*
  434. * If pwnd is NULL, then we can't do anything.
  435. */
  436. if ((pwnd == NULL) || (pwnd == PWNDDESKTOP(pwnd))) {
  437. return FALSE;
  438. }
  439. /*
  440. * Don't activate a window that has been destroyed.
  441. */
  442. if (HMIsMarkDestroy(pwnd))
  443. return FALSE;
  444. /*
  445. * We don't activate top-level windows of a different queue.
  446. */
  447. if (GETPTI(pwnd)->pq != ptiCurrent->pq) {
  448. return FALSE;
  449. }
  450. pwndActiveSave = ptiCurrent->pq->spwndActive;
  451. /*
  452. * Do the change-in-activation if the two-windows are different,
  453. * and if we're not recursing
  454. */
  455. if ((pwnd != pwndActiveSave) && !TestWF(pwnd, WFBEINGACTIVATED)) {
  456. /*
  457. * Ask the CBT hook whether it is OK to activate this window.
  458. */
  459. {
  460. CBTACTIVATESTRUCT CbtActivateParams;
  461. if (IsHooked(ptiCurrent, WHF_CBT)) {
  462. CbtActivateParams.fMouse = fMouse;
  463. CbtActivateParams.hWndActive = HW(pwndActiveSave);
  464. if (xxxCallHook(HCBT_ACTIVATE,
  465. (WPARAM)HWq(pwnd), (LPARAM)&CbtActivateParams, WH_CBT)) {
  466. return FALSE;
  467. }
  468. }
  469. }
  470. ptiCurrent->pq->QF_flags &= ~QF_EVENTDEACTIVATEREMOVED;
  471. /*
  472. * If the active window went away but somehow was left referenced
  473. * in the queue, then we do not want to do any deactivation of
  474. * that window.
  475. *
  476. * Don't thread lock this because the next thing we do with it
  477. * is just an equality check.
  478. *
  479. * A DBG check is placed in xxxDestroyWindow to attempt to
  480. * catch the situation where we return from the function with
  481. * the destroyed window set in the active (pq). If that situation
  482. * can be detected and solved, then this conditional might be
  483. * removed: ChrisWil - 08/22/95.
  484. */
  485. if (ptiCurrent->pq->spwndActive && TestWF(ptiCurrent->pq->spwndActive, WFDESTROYED)) {
  486. Lock(&ptiCurrent->pq->spwndActive, NULL);
  487. } else {
  488. Lock(&ptiCurrent->pq->spwndActivePrev, ptiCurrent->pq->spwndActive);
  489. }
  490. pwndActivePrev = ptiCurrent->pq->spwndActive;
  491. /*
  492. * If there was a previously active window,
  493. * and we're in the foreground then assign
  494. * gpqForegroundPrev to ourself.
  495. */
  496. if ((pwndActivePrev != NULL) && (ptiCurrent->pq == gpqForeground)) {
  497. gpqForegroundPrev = ptiCurrent->pq;
  498. }
  499. /*
  500. * Deactivate currently active window if possible.
  501. */
  502. if (pwndActivePrev != NULL) {
  503. ThreadLockWithPti(ptiCurrent, pwndActivePrev, &tlpwndActive);
  504. /*
  505. * The active window can prevent itself from losing the
  506. * activation by returning FALSE to this WM_NCACTIVATE message
  507. */
  508. wParam = MAKELONG(WA_INACTIVE, TestWF(pwndActivePrev, WFMINIMIZED));
  509. if (!xxxSendMessage(pwndActivePrev, WM_NCACTIVATE,
  510. wParam, (LPARAM)HWq(pwnd))) {
  511. ThreadUnlock(&tlpwndActive);
  512. return FALSE;
  513. }
  514. xxxSendMessage(pwndActivePrev, WM_ACTIVATE, wParam, (LPARAM)HWq(pwnd));
  515. ThreadUnlock(&tlpwndActive);
  516. }
  517. /*
  518. * If the activation changed while we were gone, we'd better
  519. * not send any more messages, since they'd go to the wrong window.
  520. * (and, they've already been sent anyhow)
  521. */
  522. if (ptiCurrent->pq->spwndActivePrev != ptiCurrent->pq->spwndActive ||
  523. pwndActiveSave != ptiCurrent->pq->spwndActive) {
  524. #if DBG
  525. if (ptiCurrent->pq->spwndActivePrev == ptiCurrent->pq->spwndActive) {
  526. RIPMSG0(RIP_WARNING, "xxxActivateThisWindow: ptiCurrent->pq->spwndActive changed in callbacks");
  527. }
  528. #endif
  529. return FALSE;
  530. }
  531. /*
  532. * If the window being activated has been destroyed, don't
  533. * do anything else. Making it the active window in this
  534. * case can cause console to hang during shutdown.
  535. */
  536. if (HMIsMarkDestroy(pwnd))
  537. return FALSE;
  538. /*
  539. * Before we lock the new pwndActivate, make sure we're still
  540. * on the same queue.
  541. */
  542. if (GETPTI(pwnd)->pq != ptiCurrent->pq) {
  543. RIPMSG1(RIP_WARNING, "xxxActivateThisWindow: Queue unattached:%#p", pqSave);
  544. return FALSE;
  545. }
  546. /*
  547. * This bit, which means the app set the focus to NULL after becoming
  548. * active, doesn't make sense if the app is just becoming active, so
  549. * clear it in this case. It is used below in this routine to
  550. * determine whether to send focus messages (read comment in this
  551. * routine).
  552. */
  553. if (ptiCurrent->pq->spwndActive == NULL)
  554. ptiCurrent->pq->QF_flags &= ~QF_FOCUSNULLSINCEACTIVE;
  555. Lock(&ptiCurrent->pq->spwndActive, pwnd);
  556. /*
  557. * Tp prevent recursion, set pwnd's WFBEINGACTIVATED bit.
  558. * Recursion can happen if we have an activation battle with other
  559. * threads which keep changing ptiCurrent->pq->spwndActive behind our
  560. * callbacks.
  561. * WARNING: Do NOT return from this routine without clearing this bit!
  562. */
  563. SetWF(pwnd, WFBEINGACTIVATED);
  564. xxxWindowEvent(EVENT_SYSTEM_FOREGROUND, pwnd, OBJID_WINDOW, INDEXID_OBJECT, WEF_USEPWNDTHREAD);
  565. /*
  566. * Remove all async activates up to the next async deactivate. We
  567. * do this so that any queued activates don't reset this synchronous
  568. * activation state we're now setting. Only remove up till the next
  569. * deactivate because active state is synchronized with reading
  570. * input from the input queue.
  571. *
  572. * For example, an activate event gets put in an apps queue. Before
  573. * processing it the app calls ActivateWindow(), which is synchronous.
  574. * You want the ActivateWindow() to win because it is newer
  575. * information.
  576. *
  577. * msmail32 demonstrates this. Minimize msmail. Alt-tab to it. It
  578. * brings up the password dialog, but it isn't active. It correctly
  579. * activates the password dialog but then processes an old activate
  580. * event activating the icon, so the password dialog is not active.
  581. */
  582. RemoveEventMessage(ptiCurrent->pq, QEVENT_ACTIVATE, QEVENT_DEACTIVATE);
  583. xxxMakeWindowForegroundWithState(NULL, 0);
  584. pwndActivePrev = ptiCurrent->pq->spwndActivePrev;
  585. ThreadLockWithPti(ptiCurrent, pwndActivePrev, &tlpwndActivePrev);
  586. if (TEST_PUSIF(PUSIF_PALETTEDISPLAY) && xxxSendMessage(pwnd, WM_QUERYNEWPALETTE, 0, 0)) {
  587. xxxSendNotifyMessage(PWND_BROADCAST, WM_PALETTEISCHANGING,
  588. (WPARAM)HWq(pwnd), 0);
  589. }
  590. /*
  591. * If the window becoming active is not already the top window in the
  592. * Z-order, then call xxxBringWindowToTop() to do so.
  593. */
  594. /*
  595. * If this isn't a child window, first check to see if the
  596. * window isn't already 'on top'. If not, then call
  597. * xxxBringWindowToTop().
  598. */
  599. if (!(fFlags & ATW_NOZORDER) && !TestWF(pwnd, WFCHILD)) {
  600. /*
  601. * Look for the first visible child of the desktop.
  602. * ScottLu changed this to start looking at the desktop
  603. * window. Since the desktop window was always visible,
  604. * BringWindowToTop was always called regardless of whether
  605. * it was needed or not. No one can remember why this
  606. * change was made, so I'll change it back to the way it
  607. * was in Windows 3.1. - JerrySh
  608. */
  609. pwndT = PWNDDESKTOP(pwnd)->spwndChild;
  610. while (pwndT && (!TestWF(pwndT, WFVISIBLE))) {
  611. pwndT = pwndT->spwndNext;
  612. }
  613. /*
  614. * If this activation came from an async call (i.e.
  615. * xxxProcessEventMessage), we need to check to see
  616. * if the thread is the foreground-queue. If not, then
  617. * we do not want to bring the window to the top. This
  618. * is because another window could have already been
  619. * place on top w/foreground. Bringing the window to
  620. * the top in this case would result in a top-level window
  621. * without activation. - ChrisWil
  622. *
  623. * Added a check to see if the previous-active window went
  624. * invisible during the deactivation time. This will ensure
  625. * that we bring the new window to the top. Otherwise, we
  626. * could end up skipping over the previous-window from the
  627. * above tests. Office95 apps demonstrate this behaviour by
  628. * turning their windows invisible during the painting of their
  629. * captionbars. By the time we use to get here, we failed to
  630. * bring the new window to top.
  631. */
  632. if ((pwnd != pwndT) || (pwndActivePrev && !IsVisible(pwndActivePrev))) {
  633. if (!(fAsync && (gpqForeground != ptiCurrent->pq))) {
  634. DWORD dwFlags;
  635. /*
  636. * Bring the window to the top. If we're already
  637. * activating the window, don't reactivate it.
  638. */
  639. dwFlags = SWP_NOSIZE | SWP_NOMOVE;
  640. if (pwnd == pwndT)
  641. dwFlags |= SWP_NOACTIVATE;
  642. xxxSetWindowPos(pwnd, PWND_TOP, 0, 0, 0, 0, dwFlags);
  643. }
  644. }
  645. }
  646. /*
  647. * If there was no previous active window, or if the
  648. * previously active window belonged to another thread
  649. * send the WM_ACTIVATEAPP messages. The fActivate == FALSE
  650. * case is handled in xxxDeactivate when 'hwndActivePrev == NULL'.
  651. *
  652. * Harvard Graphics/Windows setup calls SetActiveWindow when it
  653. * receives a deactivationg WM_ACTIVATEAPP. The TIF_INACTIVATEAPPMSG
  654. * prevents an activating WM_ACTIVATEAPP(TRUE) from being sent while
  655. * deactivation is occuring.
  656. */
  657. fSetActivateAppBit = FALSE;
  658. if (!(ptiCurrent->TIF_flags & TIF_INACTIVATEAPPMSG) &&
  659. ((pwndActivePrev == NULL) ||
  660. (GETPTI(pwndActivePrev) != GETPTI(pwnd)))) {
  661. AAS aas;
  662. /*
  663. * First send the deactivating WM_ACTIVATEAPP if there
  664. * was a previously active window of another thread in
  665. * the current queue.
  666. */
  667. if (pwndActivePrev != NULL) {
  668. PTHREADINFO ptiPrev = GETPTI(pwndActivePrev);
  669. TL tlptiPrev;
  670. /*
  671. * Ensure that the other thread can't recurse
  672. * and send more WM_ACTIVATEAPP msgs.
  673. */
  674. ptiPrev->TIF_flags |= TIF_INACTIVATEAPPMSG;
  675. aas.ptiNotify = ptiPrev;
  676. aas.tidActDeact = TIDq(ptiCurrent);
  677. aas.fActivating = FALSE;
  678. aas.fQueueNotify = FALSE;
  679. ThreadLockPti(ptiCurrent, ptiPrev, &tlptiPrev);
  680. ThreadLockWithPti(ptiCurrent, pwndActivePrev->head.rpdesk->pDeskInfo->spwnd->spwndChild, &tlpwndChild);
  681. xxxInternalEnumWindow(pwndActivePrev->head.rpdesk->pDeskInfo->spwnd->spwndChild,
  682. (WNDENUMPROC_PWND)xxxActivateApp, (LPARAM)&aas, BWL_ENUMLIST);
  683. ThreadUnlock(&tlpwndChild);
  684. ptiPrev->TIF_flags &= ~TIF_INACTIVATEAPPMSG;
  685. ThreadUnlockPti(ptiCurrent, &tlptiPrev);
  686. }
  687. /*
  688. * This will ensure that the current thread will not
  689. * send any more WM_ACTIVATEAPP messages until it
  690. * is done performing its activation.
  691. */
  692. ptiCurrent->TIF_flags |= TIF_INACTIVATEAPPMSG;
  693. fSetActivateAppBit = TRUE;
  694. aas.ptiNotify = GETPTI(pwnd);
  695. aas.tidActDeact = tidLoseForeground;
  696. aas.fActivating = TRUE;
  697. aas.fQueueNotify = FALSE;
  698. ThreadLockWithPti(ptiCurrent, ptiCurrent->rpdesk->pDeskInfo->spwnd->spwndChild, &tlpwndChild);
  699. xxxInternalEnumWindow(ptiCurrent->rpdesk->pDeskInfo->spwnd->spwndChild,
  700. (WNDENUMPROC_PWND)xxxActivateApp, (LPARAM)&aas, BWL_ENUMLIST);
  701. ThreadUnlock(&tlpwndChild);
  702. }
  703. /*
  704. * If this window has already been drawn as active, set the
  705. * flag so that we don't draw it again.
  706. */
  707. if (TestWF(pwnd, WFFRAMEON)) {
  708. SetWF(pwnd, WFNONCPAINT);
  709. }
  710. /*
  711. * If the window is marked for destruction, don't do
  712. * the lock because xxxFreeWindow has already been called
  713. * and a lock here will result in the window locking itself
  714. * and never being freed.
  715. */
  716. if (!HMIsMarkDestroy(pwnd)) {
  717. /*
  718. * Set most recently active window in owner/ownee list.
  719. */
  720. pwndT = pwnd;
  721. while (pwndT->spwndOwner != NULL) {
  722. pwndT = pwndT->spwndOwner;
  723. }
  724. Lock(&pwndT->spwndLastActive, pwnd);
  725. }
  726. xxxSendMessage(pwnd, WM_NCACTIVATE,
  727. MAKELONG(GETPTI(pwnd)->pq == gpqForeground,
  728. ptiCurrent->pq->spwndActive != NULL ?
  729. TestWF(ptiCurrent->pq->spwndActive, WFMINIMIZED) : 0),
  730. (LPARAM)HW(pwndActivePrev));
  731. if (ptiCurrent->pq->spwndActive != NULL) {
  732. xxxSendMessage(pwnd, WM_ACTIVATE,
  733. MAKELONG((fMouse ? WA_CLICKACTIVE : WA_ACTIVE),
  734. TestWF(ptiCurrent->pq->spwndActive, WFMINIMIZED)),
  735. (LPARAM)HW(pwndActivePrev));
  736. } else {
  737. xxxSendMessage(pwnd, WM_ACTIVATE,
  738. MAKELONG((fMouse ? WA_CLICKACTIVE : WA_ACTIVE), 0),
  739. (LPARAM)HW(pwndActivePrev));
  740. }
  741. xxxUpdateTray(pwnd);
  742. ThreadUnlock(&tlpwndActivePrev);
  743. ClrWF(pwnd, WFNONCPAINT);
  744. /*
  745. * If xxxActivateThisWindow() is called from xxxSetFocus() then
  746. * fSetFocus is FALSE. In this case, we don't set the focus since
  747. * xxxSetFocus() will do that for us. Otherwise, we set the focus
  748. * to the newly activated window if the window with the focus is
  749. * not the new active window or one of its children. Normally,
  750. * xxxDefWindowProc() will set the focus.
  751. */
  752. ThreadLockWithPti(ptiCurrent, ptiCurrent->pq->spwndActive, &tlpwndActive);
  753. /*
  754. * Win3.1 checks spwndFocus != NULL - we check QF_FOCUSNULLSINCEACTIVE,
  755. * which is the win32 equivalent. On win32, 32 bit apps each have their
  756. * own focus. If the app is not foreground, most of the time spwndFocus
  757. * is NULL when the window is being activated and brought to the
  758. * foreground. It wouldn't go through this code in this case. Win3.1 in
  759. * effect is checking if the previous active application had an
  760. * hwndFocus != NULL. Win32 effectively assumes the last window has a
  761. * non-NULL hwndFocus, so win32 instead checks to see if the focus has
  762. * been set to NULL since this application became active (meaning, did
  763. * it purposefully set the focus to NULL). If it did, don't go through
  764. * this codepath (like win3.1). If it didn't, go through this code path
  765. * because the previous application had an hwndFocus != NULL
  766. * (like win3.1). Effectively it is the same check as win3.1, but
  767. * updated to deal with async input.
  768. *
  769. * Case in point: bring up progman, hit f1 (to get win32 help). Click
  770. * history to get a popup (has the focus in a listbox in the client
  771. * area). Activate another app, now click on title bar only of history
  772. * popup. The focus should get set by going through this code path.
  773. *
  774. * Alternate case: Ventura Publisher brings up "Special Effects"
  775. * dialog. If "Bullet" from this dialog was clicked last time the
  776. * dialog was brought up, sending focus messages here when
  777. * hwndFocus == NULL, would reset the focus to "None" incorrectly
  778. * because Ventura does its state setting when it gets the focus
  779. * messages. The real focus messages it is depending on are the
  780. * ones that come from the SetFocus() call in DlgSetFocus() in
  781. * the dialog management code. (In this case, before the dialog
  782. * comes up, focus == active window. When the dialog comes up
  783. * and EnableWindow(hwndOwner, FALSE) is called, EnableWindow() calls
  784. * SetFocus(NULL) (because it is disabling the window that is also
  785. * the focus window). When the dialog comes up it gets activated via
  786. * SwpActivate(), but since the focus is NULL vpwin does not expect
  787. * to go through this code path.)
  788. *
  789. * - scottlu
  790. */
  791. #if 0
  792. // this is what win3.1 does - which won't work for win32
  793. if (fSetFocus && ptiCurrent->pq->spwndFocus != NULL && ptiCurrent->pq->spwndActive !=
  794. GetTopLevelWindow(ptiCurrent->pq->spwndFocus))
  795. #else
  796. if (fSetFocus && !(ptiCurrent->pq->QF_flags & QF_FOCUSNULLSINCEACTIVE) &&
  797. ptiCurrent->pq->spwndActive != GetTopLevelWindow(ptiCurrent->pq->spwndFocus)) {
  798. #endif
  799. xxxSendFocusMessages(ptiCurrent,
  800. (ptiCurrent->pq->spwndActive != NULL &&
  801. TestWF(ptiCurrent->pq->spwndActive, WFMINIMIZED)) ?
  802. NULL : ptiCurrent->pq->spwndActive);
  803. }
  804. ThreadUnlock(&tlpwndActive);
  805. /*
  806. * This flag is examined in the menu loop code so that we exit from
  807. * menu mode if another window was activated while we were tracking
  808. * menus.
  809. */
  810. ptiCurrent->pq->QF_flags |= QF_ACTIVATIONCHANGE;
  811. if (gppiScreenSaver == NULL) {
  812. /*
  813. * Activation has occurred, update our last idle time counter if
  814. * we're on the input desktop.
  815. */
  816. if ((ptiCurrent->rpdesk == grpdeskRitInput) && (!gbBlockSendInputResets)) {
  817. glinp.timeLastInputMessage = NtGetTickCount();
  818. }
  819. } else {
  820. if (GETPTI(pwnd)->ppi != gppiScreenSaver) {
  821. /*
  822. * Activation ocurred by an app other than the screen saver.
  823. * Update the idle time counter and mark our screen saver as
  824. * active (so it can quit).
  825. */
  826. #if 0
  827. // LATER
  828. if (ptiCurrent->rpdesk != gppiScreenSaver->rpdeskStartup) {
  829. /*
  830. * Activation is occurring on different desktops, let WinLogon decide
  831. * if it wants to switch.
  832. */
  833. }
  834. #endif
  835. glinp.timeLastInputMessage = NtGetTickCount();
  836. gppiScreenSaver->W32PF_Flags &= ~W32PF_IDLESCREENSAVER;
  837. SetForegroundPriorityProcess(gppiScreenSaver, gppiScreenSaver->ptiMainThread, TRUE);
  838. }
  839. }
  840. /*
  841. * If WM_ACTIVATEAPP messages were sent, it is now
  842. * safe to allow them to be sent again.
  843. */
  844. if (fSetActivateAppBit)
  845. ptiCurrent->TIF_flags &= ~TIF_INACTIVATEAPPMSG;
  846. } else {
  847. #if DBG
  848. if (TestWF(pwnd, WFBEINGACTIVATED)) {
  849. RIPMSG1(RIP_WARNING, "xxxActivateThisWindow recursing on pwnd %#p\n", pwnd);
  850. }
  851. #endif
  852. ptiCurrent->pq->QF_flags &= ~QF_EVENTDEACTIVATEREMOVED;
  853. if (TEST_PUSIF(PUSIF_PALETTEDISPLAY) && xxxSendMessage(pwnd, WM_QUERYNEWPALETTE, 0, 0)) {
  854. xxxSendNotifyMessage(PWND_BROADCAST, WM_PALETTEISCHANGING,
  855. (WPARAM)HWq(pwnd), 0);
  856. }
  857. }
  858. ClrWF(pwnd, WFBEINGACTIVATED);
  859. return ptiCurrent->pq->spwndActive == pwnd;
  860. }
  861. /***************************************************************************\
  862. * RemoveEventMessage
  863. *
  864. * Removes events dwQEvent until finding dwQEventStop. Used for removing
  865. * activate and deactivate events.
  866. *
  867. * 04-01-93 ScottLu Created.
  868. \***************************************************************************/
  869. BOOL RemoveEventMessage(
  870. PQ pq,
  871. DWORD dwQEvent,
  872. DWORD dwQEventStop)
  873. {
  874. PQMSG pqmsgT;
  875. PQMSG pqmsgPrev;
  876. BOOL bRemovedEvent = FALSE;
  877. /*
  878. * Remove all events dwQEvent until finding dwQEventStop.
  879. */
  880. for (pqmsgT = pq->mlInput.pqmsgWriteLast; pqmsgT != NULL; ) {
  881. if (pqmsgT->dwQEvent == dwQEventStop)
  882. return(bRemovedEvent);
  883. pqmsgPrev = pqmsgT->pqmsgPrev;
  884. /*
  885. * If the event is found and is not the one being peeked,
  886. * delete it.
  887. */
  888. if (pqmsgT->dwQEvent == dwQEvent &&
  889. pqmsgT != (PQMSG)pq->idSysPeek) {
  890. DelQEntry(&(pq->mlInput), pqmsgT);
  891. bRemovedEvent = TRUE;
  892. }
  893. pqmsgT = pqmsgPrev;
  894. }
  895. return(bRemovedEvent);
  896. }
  897. /***************************************************************************\
  898. * CanForceForeground
  899. *
  900. * A process can NOT force a new foreground when:
  901. * -There is a last input owner glinp.ptiLastWoken), and
  902. * -The process didn't get the last hot key, key or mouse click, and
  903. * -There is a thread with foreground priority gptiForeground), and
  904. * -The process doesn't own the foreground thread, and
  905. * -The process doesn't have foreground activation right, and
  906. * -The process was not the last one to do SendInput/JournalPlayBack
  907. * -There is a foreground queue, and
  908. * -The last input owner is not being debugged, and
  909. * -The foreground process is not being debugged, and
  910. * -The last input was not long ago
  911. *
  912. * History:
  913. * 05/12/97 GerardoB Extracted from xxxSetForegroundWindow
  914. \***************************************************************************/
  915. BOOL CanForceForeground(PPROCESSINFO ppi)
  916. {
  917. if ((glinp.ptiLastWoken != NULL)
  918. && (glinp.ptiLastWoken->ppi != ppi)
  919. && (gptiForeground != NULL)
  920. && (gptiForeground->ppi != ppi)
  921. && !(ppi->W32PF_Flags & (W32PF_ALLOWFOREGROUNDACTIVATE | W32PF_ALLOWSETFOREGROUND))
  922. && (ppi != gppiInputProvider)
  923. && (gpqForeground != NULL)
  924. &&
  925. #if DBG
  926. /*
  927. * When attaching the debugger to the foreground app, this function always
  928. * returns TRUE. In order to be able to debug anything related to this
  929. * function in such case, set this global to TRUE.
  930. */
  931. (gfDebugForegroundIgnoreDebugPort
  932. || (
  933. #endif
  934. (PsGetProcessDebugPort(glinp.ptiLastWoken->ppi->Process) == NULL)
  935. && (PsGetProcessDebugPort(gptiForeground->ppi->Process) == NULL)
  936. #if DBG
  937. ))
  938. #endif
  939. && !IsTimeFromLastRITEvent(UP(FOREGROUNDLOCKTIMEOUT))) {
  940. return FALSE;
  941. } else {
  942. return TRUE;
  943. }
  944. }
  945. /***************************************************************************\
  946. * AllowSetForegroundWindow (5.0 API)
  947. *
  948. * This API is meant to be called by the foreground process to allow another
  949. * process to take the foreground.
  950. * This is implemented by making a thread in dwProcessId the owner of the last
  951. * input event. This means that dwProcessId keeps the right to take the foreground
  952. * until the user generates new input (unless the input is direct to dwProcessId itself).
  953. *
  954. * History:
  955. * 01-28-98 GerardoB Created.
  956. \***************************************************************************/
  957. BOOL xxxAllowSetForegroundWindow(
  958. DWORD dwProcessId)
  959. {
  960. DWORD dwError;
  961. PEPROCESS pep;
  962. NTSTATUS Status;
  963. PPROCESSINFO ppi;
  964. /*
  965. * Get the ppi for dwProcessId
  966. * ASFW_ANY NULLs out the input owner so any process can take the foreground
  967. */
  968. if (dwProcessId != ASFW_ANY) {
  969. Status = LockProcessByClientId((HANDLE)LongToHandle( dwProcessId ), &pep);
  970. if (!NT_SUCCESS(Status)) {
  971. RIPERR0(ERROR_INVALID_PARAMETER, RIP_VERBOSE, "");
  972. return FALSE;
  973. }
  974. ppi = PpiFromProcess(pep);
  975. if (ppi == NULL) {
  976. dwError = ERROR_INVALID_PARAMETER;
  977. goto UnlockAndFail;
  978. }
  979. }
  980. /*
  981. * Do nothing if the current process cannot force a foreground change.
  982. * We could have checked this upfront but we didn't since we had to
  983. * leave the crit section and the state could have changed.
  984. */
  985. if (!CanForceForeground(PpiCurrent())) {
  986. dwError = ERROR_ACCESS_DENIED;
  987. goto UnlockAndFail;
  988. }
  989. /*
  990. * Let's make a thread (if any) of this process be the last input owner
  991. */
  992. if (dwProcessId != ASFW_ANY) {
  993. TAGMSG2(DBGTAG_FOREGROUND, "xxxAllowSetForegroundWindow by %#p to %#p", PpiCurrent(), ppi);
  994. glinp.ptiLastWoken = ppi->ptiList;
  995. UnlockProcess(pep);
  996. } else {
  997. TAGMSG1(DBGTAG_FOREGROUND, "xxxAllowSetForegroundWindow by %#p to ANY", PpiCurrent());
  998. glinp.ptiLastWoken = NULL;
  999. }
  1000. return TRUE;
  1001. UnlockAndFail:
  1002. if (dwProcessId != ASFW_ANY) {
  1003. UnlockProcess(pep);
  1004. }
  1005. RIPERR0(dwError, RIP_VERBOSE, "");
  1006. return FALSE;
  1007. }
  1008. /***************************************************************************\
  1009. * LockSetForegroundWindow (5.0 API)
  1010. *
  1011. * This API allows application to prevent any call to SetForegroundWindow.
  1012. * This is mainly intended for application implementing their own menus
  1013. * so they can block SFW just like we do for our own menus.
  1014. * Certain actions like hitting the ALT key or any foreground change (ie, by a click)
  1015. * will automatically unlock SFW (so apps cannot hose SFW)
  1016. *
  1017. * History:
  1018. * 07-04-98 GerardoB Created.
  1019. \***************************************************************************/
  1020. BOOL _LockSetForegroundWindow(
  1021. UINT uLockCode)
  1022. {
  1023. DWORD dwError;
  1024. PPROCESSINFO ppiCurrent = PpiCurrent();
  1025. switch (uLockCode) {
  1026. case LSFW_LOCK:
  1027. /*
  1028. * If the caller cannot lock it or already locked, fail the call
  1029. */
  1030. if (CanForceForeground(ppiCurrent) && (gppiLockSFW == NULL)) {
  1031. gppiLockSFW = ppiCurrent;
  1032. TAGMSG1(DBGTAG_FOREGROUND, "_LockSetForegroundWindow locked by %#p", ppiCurrent);
  1033. } else {
  1034. dwError = ERROR_ACCESS_DENIED;
  1035. goto FailIt;
  1036. }
  1037. break;
  1038. case LSFW_UNLOCK:
  1039. /*
  1040. * If the caller didn't lock it, fail the call
  1041. */
  1042. if (ppiCurrent == gppiLockSFW) {
  1043. gppiLockSFW = NULL;
  1044. TAGMSG0(DBGTAG_FOREGROUND, "_LockSetForegroundWindow UNLOCKED");
  1045. } else {
  1046. dwError = ERROR_ACCESS_DENIED;
  1047. goto FailIt;
  1048. }
  1049. break;
  1050. default:
  1051. dwError = ERROR_INVALID_PARAMETER;
  1052. goto FailIt;
  1053. }
  1054. return TRUE;
  1055. FailIt:
  1056. RIPERR0(dwError, RIP_VERBOSE, "");
  1057. return FALSE;
  1058. }
  1059. /***************************************************************************\
  1060. * CleanupDecSFWLockCount
  1061. *
  1062. * Wrapper to be passed to PushW32ThreadLock, which wants an actual function.
  1063. * History:
  1064. * 10/19/98 GerardoB Created.
  1065. \***************************************************************************/
  1066. void CleanupDecSFWLockCount(PVOID pIgnore)
  1067. {
  1068. DecSFWLockCount();
  1069. UNREFERENCED_PARAMETER(pIgnore);
  1070. }
  1071. /***************************************************************************\
  1072. * xxxSetForegroundWindow (API)
  1073. *
  1074. * History:
  1075. * 06-07-91 DavidPe Created.
  1076. \***************************************************************************/
  1077. BOOL xxxStubSetForegroundWindow(
  1078. PWND pwnd)
  1079. {
  1080. return xxxSetForegroundWindow(pwnd, TRUE);
  1081. }
  1082. BOOL xxxSetForegroundWindow(
  1083. PWND pwnd,
  1084. BOOL fFlash)
  1085. {
  1086. BOOL fNiceCall = TRUE;
  1087. BOOL fSyncActivate, fActive;
  1088. DWORD dwFlashFlags;
  1089. PTHREADINFO ptiCurrent = PtiCurrent();
  1090. PWND pwndFlash;
  1091. TL tlpwndFlash;
  1092. CheckLock(pwnd);
  1093. /*
  1094. * If we're trying to set a window on our own thread to the foreground,
  1095. * and we're already in the foreground, treat it just like a call to
  1096. * SetActiveWindow().
  1097. */
  1098. if ((pwnd != NULL) && (GETPTI(pwnd)->pq == gpqForeground)) {
  1099. fSyncActivate = (gpqForeground == ptiCurrent->pq);
  1100. if (fSyncActivate) {
  1101. gppiWantForegroundPriority = ptiCurrent->ppi;
  1102. } else {
  1103. gppiWantForegroundPriority = GETPTI(pwnd)->ppi;
  1104. }
  1105. goto JustActivateIt;
  1106. }
  1107. /*
  1108. * If the foregrond is not locked
  1109. * and this thread has the right to changethe foreground,
  1110. * then remove the activation right (it's a one-shot deal)
  1111. * and do it.
  1112. *
  1113. *
  1114. * Bug 247768 - joejo
  1115. * Add compatibility hack for foreground activation problems.
  1116. *
  1117. * To Fix Winstone99, ignore the foreground lock if the input
  1118. * provider is making this call. GerardoB.
  1119. *
  1120. * Windows Bug 88327 - jasonsch
  1121. * Screen savers can always come to the foreground.
  1122. */
  1123. if ((!IsForegroundLocked() || (ptiCurrent->ppi == gppiInputProvider))
  1124. && (ptiCurrent->TIF_flags & (TIF_ALLOWFOREGROUNDACTIVATE | TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD)
  1125. || CanForceForeground(ptiCurrent->ppi)
  1126. || GiveUpForeground()) || ptiCurrent->ppi == gppiScreenSaver) {
  1127. TAGMSG1(DBGTAG_FOREGROUND, "xxxSetForegroundWindow FRemoveForegroundActivate %#p", ptiCurrent);
  1128. FRemoveForegroundActivate(ptiCurrent);
  1129. return xxxSetForegroundWindow2(pwnd, ptiCurrent, 0);
  1130. }
  1131. fNiceCall = FALSE;
  1132. TAGMSG3(DBGTAG_FOREGROUND, "xxxSetForegroundWindow: rude call by %#p to %#p-%#p",
  1133. ptiCurrent, pwnd, (pwnd != NULL ? GETPTI(pwnd) : NULL));
  1134. if (pwnd == NULL) {
  1135. return FALSE;
  1136. }
  1137. /*
  1138. * Notify the user that this pwnd wants to come to the foreground.
  1139. * Try to flash a tray button only; otherwise, flash pwnd
  1140. */
  1141. if (fFlash) {
  1142. pwndFlash = DSW_GetTopLevelCreatorWindow(GetTopLevelWindow(pwnd));
  1143. if (IsTrayWindow(pwndFlash)) {
  1144. dwFlashFlags = FLASHW_TRAY;
  1145. } else {
  1146. pwndFlash = pwnd;
  1147. dwFlashFlags = FLASHW_ALL;
  1148. }
  1149. ThreadLockAlways(pwndFlash, &tlpwndFlash);
  1150. xxxFlashWindow(pwndFlash,
  1151. MAKELONG(dwFlashFlags | FLASHW_TIMERNOFG, UP(FOREGROUNDFLASHCOUNT)),
  1152. 0);
  1153. ThreadUnlock(&tlpwndFlash);
  1154. }
  1155. /*
  1156. * Activate the window.
  1157. */
  1158. fSyncActivate = (ptiCurrent->pq == GETPTI(pwnd)->pq);
  1159. JustActivateIt:
  1160. if (fSyncActivate) {
  1161. fActive = xxxActivateWindow(pwnd, AW_USE);
  1162. } else if (pwnd == GETPTI(pwnd)->pq->spwndActive) {
  1163. fActive = TRUE;
  1164. } else {
  1165. fActive = PostEventMessage(GETPTI(pwnd), GETPTI(pwnd)->pq,
  1166. QEVENT_ACTIVATE, NULL, 0,
  1167. 0, (LPARAM)HWq(pwnd)) ;
  1168. }
  1169. /*
  1170. * Return FALSE if we failed the set foreground request.
  1171. */
  1172. return fNiceCall && fActive;
  1173. }
  1174. /***************************************************************************\
  1175. * xxxSetForegroundWindow2
  1176. *
  1177. * History:
  1178. * 07-19-91 DavidPe Created.
  1179. \***************************************************************************/
  1180. BOOL xxxSetForegroundWindow2(
  1181. PWND pwnd,
  1182. PTHREADINFO pti,
  1183. DWORD fFlags)
  1184. {
  1185. PTHREADINFO ptiForegroundOld;
  1186. PTHREADINFO ptiForegroundNew;
  1187. PQ pqForegroundOld, pqForegroundNew, pqCurrent;
  1188. HWND hwnd;
  1189. PQMSG pqmsgDeactivate, pqmsgActivate;
  1190. BOOL bRemovedEvent;
  1191. PTHREADINFO ptiCurrent = PtiCurrent();
  1192. BOOL retval = TRUE;
  1193. UINT uMsg;
  1194. CheckLock(pwnd);
  1195. /*
  1196. * Queue pointers and threadinfo pointers can go away when calling xxx
  1197. * calls. Also, queues can get recalced via AttachThreadInput() during
  1198. * xxx calls - so we want to reference the application becoming foreground.
  1199. * PQs cannot be refcount locked (either thread locked or structure locked)
  1200. * so must (re)calculate them after returning from xxx calls.
  1201. *
  1202. * NOTE: gpqForeground and gpqForegroundPrev are always current and don't
  1203. * need special handling.
  1204. */
  1205. /*
  1206. * Don't allow the foreground to be set to a window that is not
  1207. * on the current desktop.
  1208. */
  1209. if (pwnd != NULL && (pwnd->head.rpdesk != grpdeskRitInput ||
  1210. HMIsMarkDestroy(pwnd))) {
  1211. return FALSE;
  1212. }
  1213. /*
  1214. * Unlock SetForegroundWindow (if someone had it locked)
  1215. */
  1216. gppiLockSFW = NULL;
  1217. TAGMSG3(DBGTAG_FOREGROUND, "xxxSetForegroundWindow2 by %#p to %#p-%#p",
  1218. ptiCurrent, pwnd, (pwnd != NULL ? GETPTI(pwnd) : NULL));
  1219. /*
  1220. * Calculate who is becoming foreground. Also, remember who we want
  1221. * foreground (for priority setting reasons).
  1222. */
  1223. if ((gptiForeground != NULL) && !(gptiForeground->TIF_flags & TIF_INCLEANUP)) {
  1224. ptiForegroundOld = gptiForeground;
  1225. } else {
  1226. ptiForegroundOld = NULL;
  1227. }
  1228. pqForegroundOld = NULL;
  1229. pqForegroundNew = NULL;
  1230. pqCurrent = NULL;
  1231. gpqForegroundPrev = gpqForeground;
  1232. if (pwnd != NULL) {
  1233. ptiForegroundNew = GETPTI(pwnd);
  1234. UserAssert(ptiForegroundNew->rpdesk == grpdeskRitInput);
  1235. gppiWantForegroundPriority = GETPTI(pwnd)->ppi;
  1236. gpqForeground = GETPTI(pwnd)->pq;
  1237. UserAssert(gpqForeground->cThreads != 0);
  1238. UserAssert(gpqForeground->ptiMouse->rpdesk == grpdeskRitInput);
  1239. // Assert to catch AV in xxxNextWindow doing Alt-Esc: If we have a non-NULL
  1240. // gpqForeground, its kbd input thread better have an rpdesk! -IanJa
  1241. UserAssert(!gpqForeground || (gpqForeground->ptiKeyboard && gpqForeground->ptiKeyboard->rpdesk));
  1242. SetForegroundThread(GETPTI(pwnd));
  1243. } else {
  1244. ptiForegroundNew = NULL;
  1245. gppiWantForegroundPriority = NULL;
  1246. gpqForeground = NULL;
  1247. SetForegroundThread(NULL);
  1248. }
  1249. /*
  1250. * Are we switching the foreground queue?
  1251. */
  1252. if (gpqForeground != gpqForegroundPrev) {
  1253. TL tlptiForegroundOld;
  1254. TL tlptiForegroundNew;
  1255. TL tlpti;
  1256. ThreadLockPti(ptiCurrent, ptiForegroundOld, &tlptiForegroundOld);
  1257. ThreadLockPti(ptiCurrent, ptiForegroundNew, &tlptiForegroundNew);
  1258. ThreadLockPti(ptiCurrent, pti, &tlpti);
  1259. /*
  1260. * If this call didn't come from the RIT, cancel tracking
  1261. * and other global states.
  1262. */
  1263. if (pti != NULL) {
  1264. /*
  1265. * Clear any visible tracking going on in system.
  1266. */
  1267. xxxCancelTracking();
  1268. /*
  1269. * Remove the clip cursor rectangle - it is a global mode that
  1270. * gets removed when switching. Also remove any LockWindowUpdate()
  1271. * that's still around.
  1272. */
  1273. zzzClipCursor(NULL);
  1274. LockWindowUpdate2(NULL, TRUE);
  1275. /*
  1276. * Make sure the desktop of the newly activated window is the
  1277. * foreground fullscreen window
  1278. */
  1279. xxxMakeWindowForegroundWithState(NULL, 0);
  1280. }
  1281. /*
  1282. * We've potentially done callbacks. Calculate pqForegroundOld
  1283. * based on our locked local variable ptiForegroundOld.
  1284. */
  1285. pqForegroundOld = NULL;
  1286. if (ptiForegroundOld && !(ptiForegroundOld->TIF_flags & TIF_INCLEANUP)) {
  1287. pqForegroundOld = ptiForegroundOld->pq;
  1288. }
  1289. pqCurrent = NULL;
  1290. if (pti != NULL)
  1291. pqCurrent = pti->pq;
  1292. /*
  1293. * Now allocate message for the deactivation
  1294. */
  1295. pqmsgDeactivate = pqmsgActivate = NULL;
  1296. if ((pqForegroundOld != NULL) && (pqForegroundOld != pqCurrent)) {
  1297. if ((pqmsgDeactivate = AllocQEntry(&pqForegroundOld->mlInput)) ==
  1298. NULL) {
  1299. retval = FALSE;
  1300. goto Exit;
  1301. }
  1302. }
  1303. /*
  1304. * Do any appropriate deactivation.
  1305. */
  1306. if (pqForegroundOld != NULL) {
  1307. /*
  1308. * If we're already on the foreground queue we'll call
  1309. * xxxDeactivate() directly later in this routine since
  1310. * it'll cause us to leave the critical section.
  1311. */
  1312. if (pqForegroundOld != pqCurrent) {
  1313. StoreQMessage(pqmsgDeactivate, NULL, 0,
  1314. gptiForeground != NULL ? (WPARAM)GETPTIID(gptiForeground) : 0,
  1315. 0, 0, QEVENT_DEACTIVATE, 0);
  1316. /*
  1317. * If there was an old foreground thread, make it perform
  1318. * the deactivation. Otherwise, any thread on the queue
  1319. * can perform the deactivation.
  1320. */
  1321. if (ptiForegroundOld != NULL) {
  1322. SetWakeBit(ptiForegroundOld, QS_EVENTSET);
  1323. StoreQMessagePti(pqmsgDeactivate, ptiForegroundOld);
  1324. }
  1325. if (pqForegroundOld->spwndActive != NULL) {
  1326. if (ptiForegroundOld != NULL && FHungApp(ptiForegroundOld, CMSHUNGAPPTIMEOUT)) {
  1327. TL tlpwnd;
  1328. ThreadLockAlwaysWithPti(ptiCurrent, pqForegroundOld->spwndActive, &tlpwnd);
  1329. xxxRedrawHungWindowFrame(pqForegroundOld->spwndActive, FALSE);
  1330. ThreadUnlock(&tlpwnd);
  1331. } else {
  1332. SetHungFlag(pqForegroundOld->spwndActive, WFREDRAWFRAMEIFHUNG);
  1333. }
  1334. }
  1335. }
  1336. }
  1337. /*
  1338. * We've potentially done callbacks. Calculate pqForegroundNew
  1339. * based on our locked local variable ptiForegroundNew.
  1340. */
  1341. pqForegroundNew = NULL;
  1342. if (ptiForegroundNew && !(ptiForegroundNew->TIF_flags & TIF_INCLEANUP)) {
  1343. pqForegroundNew = ptiForegroundNew->pq;
  1344. }
  1345. /*
  1346. * Update pqCurrent since we may have made an xxx call,
  1347. * and this variable may be invalid.
  1348. */
  1349. pqCurrent = NULL;
  1350. if (pti != NULL) {
  1351. pqCurrent = pti->pq;
  1352. }
  1353. if ((pqForegroundNew != NULL) && (pqForegroundNew != pqCurrent)) {
  1354. pqmsgActivate = AllocQEntry(&pqForegroundNew->mlInput);
  1355. if (pqmsgActivate == NULL) {
  1356. retval = FALSE;
  1357. goto Exit;
  1358. }
  1359. }
  1360. /*
  1361. * Do any appropriate activation.
  1362. */
  1363. if (pqForegroundNew != NULL) {
  1364. /*
  1365. * We're going to activate (synchronously or async with an activate
  1366. * event). We want to remove the last deactivate event if there is
  1367. * one because this is new state. If we don't, then 1> we could
  1368. * synchronously activate and then asynchronously deactivate,
  1369. * thereby processing these events out of order, or 2> we could
  1370. * pile up a chain of deactivate / activate events which would
  1371. * make the titlebar flash alot if the app wasn't responding to
  1372. * input for awhile (in this case, it doesn't matter if we
  1373. * put a redundant activate in the queue, since the app is already
  1374. * active. Remove all deactivate events because this app is
  1375. * setting a state that is not meant to be synchronized with
  1376. * existing queued input.
  1377. *
  1378. * Case: run setup, switch away (it gets deactivate event). setup
  1379. * is not reading messages so it hasn't go it yet. It finally
  1380. * comes up, calls SetForegroundWindow(). It's synchronous,
  1381. * it activates ok and sets foreground. Then the app calls
  1382. * GetMessage() and gets the deactivate. Now it isn't active.
  1383. */
  1384. bRemovedEvent = RemoveEventMessage(pqForegroundNew, QEVENT_DEACTIVATE, (DWORD)-1);
  1385. /*
  1386. * Now do any appropriate activation. See comment below
  1387. * for special cases. If we're already on the foreground
  1388. * queue we'll call xxxActivateThisWindow() directly.
  1389. */
  1390. if (pqForegroundNew != pqCurrent) {
  1391. /*
  1392. * We do the 'pqCurrent == NULL' test to see if we're being
  1393. * called from the RIT. In this case we pass NULL for the
  1394. * HWND which will check to see if there is already an active
  1395. * window for the thread and redraw its frame as truly active
  1396. * since it's in the foreground now. It will also cancel any
  1397. * global state like LockWindowUpdate() and ClipRect().
  1398. */
  1399. if ((pqCurrent == NULL) && (!(fFlags & SFW_SWITCH))) {
  1400. hwnd = NULL;
  1401. } else {
  1402. hwnd = HW(pwnd);
  1403. }
  1404. if (bRemovedEvent) {
  1405. pqForegroundNew->QF_flags |= QF_EVENTDEACTIVATEREMOVED;
  1406. }
  1407. /*
  1408. * MSMail relies on a specific order to how win3.1 does
  1409. * fast switch alt-tab activation. On win3.1, it essentially
  1410. * activates the window, then restores it. MsMail gets confused
  1411. * if it isn't active when it gets restored, so this logic
  1412. * will make sure msmail gets restore after it gets activated.
  1413. *
  1414. * Click on a message line in the in-box, minimize msmail,
  1415. * alt-tab to it. The same line should have the focus if msmail
  1416. * got restored after it got activated.
  1417. *
  1418. * This is the history behind SFW_ACTIVATERESTORE.
  1419. */
  1420. if (fFlags & SFW_ACTIVATERESTORE) {
  1421. uMsg = PEM_ACTIVATE_RESTORE;
  1422. } else {
  1423. uMsg = 0;
  1424. }
  1425. if (fFlags & SFW_NOZORDER) {
  1426. uMsg |= PEM_ACTIVATE_NOZORDER;
  1427. }
  1428. StoreQMessage(pqmsgActivate, NULL, uMsg,
  1429. (fFlags & SFW_STARTUP) ? 0 : (WPARAM)TID(ptiForegroundOld),
  1430. (LPARAM)hwnd, 0, QEVENT_ACTIVATE, 0);
  1431. /*
  1432. * Signal the window's thread to perform activation. We
  1433. * know that ptiForegroundNew is valid because pqForegroundNew
  1434. * is not NULL.
  1435. */
  1436. StoreQMessagePti(pqmsgActivate, ptiForegroundNew);
  1437. SetWakeBit(ptiForegroundNew, QS_EVENTSET);
  1438. if (pqForegroundNew->spwndActive != NULL) {
  1439. if (FHungApp(ptiForegroundNew, CMSHUNGAPPTIMEOUT)) {
  1440. TL tlpwnd;
  1441. ThreadLockAlwaysWithPti(ptiCurrent, pqForegroundNew->spwndActive, &tlpwnd);
  1442. xxxRedrawHungWindowFrame(pqForegroundNew->spwndActive, TRUE);
  1443. ThreadUnlock(&tlpwnd);
  1444. } else {
  1445. SetHungFlag(pqForegroundNew->spwndActive, WFREDRAWFRAMEIFHUNG);
  1446. }
  1447. }
  1448. } else {
  1449. if (pwnd != pqCurrent->spwndActive) {
  1450. if (!(fFlags & SFW_STARTUP)) {
  1451. retval = xxxActivateThisWindow(pwnd, TID(ptiForegroundOld),
  1452. ((fFlags & SFW_SETFOCUS) ? 0 : ATW_SETFOCUS));
  1453. /*
  1454. * Make sure the mouse is on this window.
  1455. */
  1456. if (retval && TestUP(ACTIVEWINDOWTRACKING)) {
  1457. zzzActiveCursorTracking(pwnd);
  1458. }
  1459. goto Exit;
  1460. }
  1461. } else {
  1462. /*
  1463. * If pwnd is already the active window, just make sure
  1464. * it's drawn active and on top (if requested).
  1465. */
  1466. xxxSendMessage(pwnd, WM_NCACTIVATE,
  1467. TRUE,
  1468. (LPARAM)HW(pwnd));
  1469. xxxUpdateTray(pwnd);
  1470. if (!(fFlags & SFW_NOZORDER)) {
  1471. xxxSetWindowPos(pwnd, PWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
  1472. }
  1473. }
  1474. }
  1475. } /* if (pqForegroundNew != NULL) */
  1476. /*
  1477. * First update pqForegroundOld and pqCurrent since we may have
  1478. * made an xxx call, and these variables may be invalid.
  1479. */
  1480. pqForegroundOld = NULL;
  1481. if (ptiForegroundOld && !(ptiForegroundOld->TIF_flags & TIF_INCLEANUP)) {
  1482. pqForegroundOld = ptiForegroundOld->pq;
  1483. }
  1484. pqCurrent = NULL;
  1485. if (pti != NULL)
  1486. pqCurrent = pti->pq;
  1487. /*
  1488. * Now check to see if we needed to do any 'local' deactivation.
  1489. * (ie. were we on the queue that is being deactivated by this
  1490. * SetForegroundWindow() call?)
  1491. */
  1492. if ((pqForegroundOld != NULL) && (pqForegroundOld == pqCurrent)) {
  1493. xxxDeactivate(pti, (pwnd != NULL) ? TIDq(GETPTI(pwnd)) : 0);
  1494. }
  1495. Exit:
  1496. ThreadUnlockPti(ptiCurrent, &tlpti);
  1497. ThreadUnlockPti(ptiCurrent, &tlptiForegroundNew);
  1498. ThreadUnlockPti(ptiCurrent, &tlptiForegroundOld);
  1499. }
  1500. return retval;
  1501. }
  1502. /***************************************************************************\
  1503. * FRemoveForegroundActivate
  1504. *
  1505. * Returns TRUE if the foreground activate right was removed.
  1506. *
  1507. * 05-12-97 GerardoB Extracted from FAllowForegroundActivate.
  1508. \***************************************************************************/
  1509. BOOL FRemoveForegroundActivate(PTHREADINFO pti)
  1510. {
  1511. BOOL fRemoved;
  1512. PPROCESSINFO ppi;
  1513. /*
  1514. * W32PF_APPSTARTING gets turned off the first activate this process does.
  1515. * We assume it's ready now for action.
  1516. */
  1517. ppi = pti->ppi;
  1518. if (ppi->W32PF_Flags & W32PF_APPSTARTING) {
  1519. ClearAppStarting(ppi);
  1520. }
  1521. /*
  1522. * Remove the right if present.
  1523. */
  1524. fRemoved = (pti->TIF_flags & TIF_ALLOWFOREGROUNDACTIVATE);
  1525. if (fRemoved) {
  1526. pti->TIF_flags &= ~TIF_ALLOWFOREGROUNDACTIVATE ;
  1527. TAGMSG1(DBGTAG_FOREGROUND, "FRemoveForegroundActivate clear TIF %#p", pti);
  1528. } else {
  1529. fRemoved = (ppi->W32PF_Flags & W32PF_ALLOWFOREGROUNDACTIVATE);
  1530. }
  1531. if (fRemoved) {
  1532. ppi->W32PF_Flags &= ~W32PF_ALLOWFOREGROUNDACTIVATE;
  1533. TAGMSG1(DBGTAG_FOREGROUND, "FRemoveForegroundActivate clear W32PF %#p", ppi);
  1534. }
  1535. return fRemoved;
  1536. }
  1537. /***************************************************************************\
  1538. * FAllowForegroundActivate
  1539. *
  1540. * Checks to see if we previously have allowed this process or thread to
  1541. * do a foreground activate - meaning, next time it becomes active, whether
  1542. * we'll allow it to come to the foreground. Sometimes processes are granted
  1543. * the right to foreground activate themselves, if they aren't foreground,
  1544. * like when starting up (there are other cases). Grant this if this process
  1545. * is allowed.
  1546. *
  1547. * 09-08-92 ScottLu Created.
  1548. \***************************************************************************/
  1549. BOOL FAllowForegroundActivate(
  1550. PQ pq,
  1551. PWND pwnd)
  1552. {
  1553. PTHREADINFO ptiCurrent = PtiCurrent();
  1554. UserAssert(pwnd != NULL);
  1555. /*
  1556. * Bail if this guy doesn't have the foreground activate right.
  1557. */
  1558. TAGMSG1(DBGTAG_FOREGROUND, "FAllowForegroundActivate FRemoveForegroundActivate %#p", ptiCurrent);
  1559. if (!FRemoveForegroundActivate(ptiCurrent)) {
  1560. return FALSE;
  1561. }
  1562. /*
  1563. * Don't try to foreground activate if:
  1564. * we're not on the right desktop.
  1565. * we're already in the foreground
  1566. * the foreground is locked
  1567. * It'll fail in SetForegroundWindow2() anyway. This way
  1568. * ActivateWindow() will still locally activate the window.
  1569. */
  1570. if ((ptiCurrent->rpdesk != grpdeskRitInput)
  1571. || (gpqForeground == pq)
  1572. || IsForegroundLocked()) {
  1573. TAGMSG0(DBGTAG_FOREGROUND, "FAllowForegroundActivate FALSE due to addtional checks");
  1574. return FALSE;
  1575. }
  1576. /*
  1577. * noactivate windows cannot take the foreground unless explicitly requested.
  1578. * Note that windows passed to this function are expected to be toplevel, which is
  1579. * where this style has meaning. This might not be the case if AW_SKIP picked an
  1580. * owner window which is not top level. Since noactivate doesn't apply to the owner
  1581. * chain, it's OK to ignore this.
  1582. */
  1583. #if DBG
  1584. if (TestwndChild(pwnd)) {
  1585. RIPMSG1(RIP_WARNING, "FAllowForegroundActivate pwnd %#p is not top level", pwnd);
  1586. }
  1587. #endif
  1588. if (TestWF(pwnd, WEFNOACTIVATE)) {
  1589. TAGMSG1(DBGTAG_FOREGROUND, "FAllowForegroundActivate noactivate window:%#p", pwnd);
  1590. return FALSE;
  1591. }
  1592. return TRUE;
  1593. }
  1594. /***************************************************************************\
  1595. * xxxSetFocus (API)
  1596. *
  1597. * History:
  1598. * 11-08-90 DavidPe Ported.
  1599. \***************************************************************************/
  1600. PWND xxxSetFocus(
  1601. PWND pwnd)
  1602. {
  1603. HWND hwndTemp;
  1604. PTHREADINFO ptiCurrent = PtiCurrent();
  1605. PTHREADINFO ptiActiveKL;
  1606. PWND pwndTemp = NULL;
  1607. TL tlpwndTemp;
  1608. CheckLock(pwnd);
  1609. /*
  1610. * Special case if we are setting the focus to a null window.
  1611. */
  1612. if (pwnd == NULL) {
  1613. if (IsHooked(ptiCurrent, WHF_CBT) && xxxCallHook(HCBT_SETFOCUS, 0,
  1614. (LPARAM)HW(ptiCurrent->pq->spwndFocus), WH_CBT)) {
  1615. return NULL;
  1616. }
  1617. /*
  1618. * Save old focus so that we can return it.
  1619. */
  1620. hwndTemp = HW(ptiCurrent->pq->spwndFocus);
  1621. xxxSendFocusMessages(ptiCurrent, pwnd);
  1622. return RevalidateHwnd(hwndTemp);
  1623. }
  1624. /*
  1625. * We no longer allow inter-thread set focuses.
  1626. */
  1627. if (GETPTI(pwnd)->pq != ptiCurrent->pq) {
  1628. return NULL;
  1629. }
  1630. /*
  1631. * If the window recieving the focus or any of its ancestors is either
  1632. * minimized or disabled, don't set the focus.
  1633. */
  1634. for (pwndTemp = pwnd; pwndTemp != NULL; pwndTemp = pwndTemp->spwndParent) {
  1635. if (TestWF(pwndTemp, WFMINIMIZED) || TestWF(pwndTemp, WFDISABLED)) {
  1636. /*
  1637. * Don't change the focus if going to a minimized or disabled
  1638. * window.
  1639. */
  1640. return NULL;
  1641. }
  1642. if (!TestwndChild(pwndTemp)) {
  1643. break;
  1644. }
  1645. }
  1646. UserAssert(pwndTemp != NULL);
  1647. /*
  1648. * pwndTemp should now be the top level ancestor of pwnd.
  1649. */
  1650. ThreadLockWithPti(ptiCurrent, pwndTemp, &tlpwndTemp);
  1651. if (pwnd != ptiCurrent->pq->spwndFocus) {
  1652. if (IsHooked(ptiCurrent, WHF_CBT) && xxxCallHook(HCBT_SETFOCUS, (WPARAM)HWq(pwnd),
  1653. (LPARAM)HW(ptiCurrent->pq->spwndFocus), WH_CBT)) {
  1654. ThreadUnlock(&tlpwndTemp);
  1655. return NULL;
  1656. }
  1657. /*
  1658. * Activation must follow the focus. That is, setting the focus to
  1659. * a particualr window means that the top-level parent of this window
  1660. * must be the active window (top-level parent is determined by
  1661. * following the parent chain until you hit a top-level guy). So,
  1662. * we must activate this top-level parent if it is different than
  1663. * the current active window.
  1664. *
  1665. * Only change activation if top-level parent is not the currently
  1666. * active window.
  1667. */
  1668. if (pwndTemp != ptiCurrent->pq->spwndActive) {
  1669. /*
  1670. * If this app is not in the foreground, see if foreground
  1671. * activation is allowed.
  1672. */
  1673. if (ptiCurrent->pq != gpqForeground && FAllowForegroundActivate(ptiCurrent->pq, pwndTemp)) {
  1674. /*
  1675. * If the process lost the foreground activation right by giving
  1676. * focus to a hidden window, then give it the right back. See
  1677. * bug #401932 for how this might affect an app
  1678. */
  1679. if (!TestWF(pwndTemp, WFVISIBLE)){
  1680. ptiCurrent->ppi->W32PF_Flags |= W32PF_ALLOWFOREGROUNDACTIVATE;
  1681. }
  1682. if (!xxxSetForegroundWindow2(pwndTemp, ptiCurrent, SFW_SETFOCUS)) {
  1683. ThreadUnlock(&tlpwndTemp);
  1684. return NULL;
  1685. }
  1686. }
  1687. /*
  1688. * This will return FALSE if something goes wrong.
  1689. */
  1690. if (pwndTemp != ptiCurrent->pq->spwndActive) {
  1691. if (!xxxActivateThisWindow(pwndTemp, 0, 0)) {
  1692. ThreadUnlock(&tlpwndTemp);
  1693. return NULL;
  1694. }
  1695. }
  1696. }
  1697. /*
  1698. * Save current pwndFocus since we must return this.
  1699. */
  1700. pwndTemp = ptiCurrent->pq->spwndFocus;
  1701. ThreadUnlock(&tlpwndTemp);
  1702. ThreadLockWithPti(ptiCurrent, pwndTemp, &tlpwndTemp);
  1703. /*
  1704. * Change the global pwndFocus and send the WM_{SET/KILL}FOCUS
  1705. * messages.
  1706. */
  1707. xxxSendFocusMessages(ptiCurrent, pwnd);
  1708. } else {
  1709. pwndTemp = ptiCurrent->pq->spwndFocus;
  1710. }
  1711. if (ptiCurrent->pq->spwndFocus) {
  1712. /*
  1713. * For the shell notification hook, we should use the pti->spkl
  1714. * of the window with the focus. This could be a different thread,
  1715. * (or even different process) when the queue is attached. The typical
  1716. * case would be OLE out-of-process server.
  1717. * #352877
  1718. */
  1719. ptiActiveKL = GETPTI(ptiCurrent->pq->spwndFocus);
  1720. } else {
  1721. /*
  1722. * Preserving the NT4 behavior, otherwise.
  1723. */
  1724. ptiActiveKL = ptiCurrent;
  1725. }
  1726. UserAssert(ptiActiveKL);
  1727. /*
  1728. * Update the keyboard icon on the tray if the layout changed during focus change.
  1729. * Before winlogon loads kbd layouts, pti->spkActive is NULL. #99321
  1730. */
  1731. if (ptiActiveKL->spklActive) {
  1732. HKL hklActive = ptiActiveKL->spklActive->hkl;
  1733. if ((gLCIDSentToShell != hklActive) && IsHooked(ptiCurrent, WHF_SHELL)) {
  1734. gLCIDSentToShell = hklActive;
  1735. xxxCallHook(HSHELL_LANGUAGE, (WPARAM)NULL, (LPARAM)hklActive, WH_SHELL);
  1736. }
  1737. }
  1738. hwndTemp = HW(pwndTemp);
  1739. ThreadUnlock(&tlpwndTemp);
  1740. /*
  1741. * Return the pwnd of the window that lost the focus.
  1742. * Return the validated hwndTemp: since we locked/unlocked pwndTemp,
  1743. * it may be gone.
  1744. */
  1745. return RevalidateHwnd(hwndTemp);
  1746. }
  1747. /***************************************************************************\
  1748. * xxxSetActiveWindow (API)
  1749. *
  1750. *
  1751. * History:
  1752. * 11-08-90 DavidPe Created.
  1753. \***************************************************************************/
  1754. PWND xxxSetActiveWindow(
  1755. PWND pwnd)
  1756. {
  1757. HWND hwndActiveOld;
  1758. PTHREADINFO pti;
  1759. CheckLock(pwnd);
  1760. pti = PtiCurrent();
  1761. /*
  1762. * 32 bit apps must call SetForegroundWindow (to be NT 3.1 compatible)
  1763. * but 16 bit apps that are foreground can make other apps foreground.
  1764. * xxxActivateWindow makes sure an app is foreground.
  1765. */
  1766. if (!(pti->TIF_flags & TIF_16BIT) && (pwnd != NULL) && (GETPTI(pwnd)->pq != pti->pq)) {
  1767. return NULL;
  1768. }
  1769. hwndActiveOld = HW(pti->pq->spwndActive);
  1770. xxxActivateWindow(pwnd, AW_USE);
  1771. return RevalidateHwnd(hwndActiveOld);
  1772. }
  1773. /***************************************************************************\
  1774. * xxxActivateWindow
  1775. *
  1776. * Changes the active window. Given the pwnd and cmd parameters, changes the
  1777. * activation according to the following rules:
  1778. *
  1779. * If cmd ==
  1780. * AW_USE Use the pwnd passed as the new active window. If this
  1781. * window cannot be activated, return FALSE.
  1782. *
  1783. * AW_TRY Try to use the pwnd passed as the new active window. If
  1784. * this window cannot be activated activate another window
  1785. * using the rules for AW_SKIP.
  1786. *
  1787. * AW_SKIP Activate any other window than pwnd passed. The order of
  1788. * searching for a candidate is as follows:
  1789. * - If pwnd is a popup, try its owner
  1790. * - else scan the top-level window list for the first
  1791. * window that is not pwnd that can be activated.
  1792. *
  1793. * AW_USE2 Same as AW_USE except that the wParam on the WM_ACTIVATE
  1794. * message will be set to 2 rather than the default of 1. This
  1795. * indicates the activation is being changed due to a mouse
  1796. * click.
  1797. *
  1798. * AW_TRY2 Same as AW_TRY except that the wParam on the WM_ACTIVATE
  1799. * message will be set to 2 rather than the default of 1. This
  1800. * indicates the activation is being changed due to a mouse
  1801. * click.
  1802. *
  1803. * AW_SKIP2 Same as AW_SKIP, but we skip the first check that AW_SKIP
  1804. * performes (the pwndOwner test). This is used when
  1805. * the pwnd parameter is NULL when this function is called.
  1806. *
  1807. * This function returns TRUE if the activation changed and FALSE if
  1808. * it did not change.
  1809. *
  1810. * This function calls xxxActivateThisWindow() to actually do the activation.
  1811. *
  1812. * History:
  1813. * 11-08-90 DavidPe Ported.
  1814. \***************************************************************************/
  1815. BOOL xxxActivateWindow(
  1816. PWND pwnd,
  1817. UINT cmd)
  1818. {
  1819. DWORD fFlags = ATW_SETFOCUS;
  1820. PTHREADINFO ptiCurrent = PtiCurrent();
  1821. TL tlpwnd;
  1822. BOOL fSuccess;
  1823. BOOL fAllowForeground, fSetForegroundRight;
  1824. CheckLock(pwnd);
  1825. if (pwnd != NULL) {
  1826. /*
  1827. * See if this window is OK to activate
  1828. * (Cannot activate child windows).
  1829. */
  1830. if (TestwndChild(pwnd))
  1831. return FALSE;
  1832. } else {
  1833. cmd = AW_SKIP2;
  1834. }
  1835. switch (cmd) {
  1836. case AW_TRY2:
  1837. fFlags |= ATW_MOUSE;
  1838. /*
  1839. *** FALL THRU **
  1840. */
  1841. case AW_TRY:
  1842. /*
  1843. * See if this window is OK to activate.
  1844. */
  1845. if (!FBadWindow(pwnd)) {
  1846. break;
  1847. }
  1848. /*
  1849. * If pwnd can not be activated, drop into the AW_SKIP case.
  1850. */
  1851. case AW_SKIP:
  1852. /*
  1853. * Try the owner of this popup.
  1854. */
  1855. if (TestwndPopup(pwnd) && !FBadWindow(pwnd->spwndOwner)) {
  1856. pwnd = pwnd->spwndOwner;
  1857. break;
  1858. }
  1859. /*
  1860. * fall through
  1861. */
  1862. case AW_SKIP2:
  1863. /*
  1864. * Try the previously active window but don't activate a shell window
  1865. */
  1866. if ((gpqForegroundPrev != NULL)
  1867. && !FBadWindow(gpqForegroundPrev->spwndActivePrev)
  1868. /*
  1869. * Bug 290129 - joejo
  1870. *
  1871. * Test for WFBOTTOMMOST as opposed to WEFTOOLWINDOW to fix
  1872. * issue with Office2000 assistant and balloon help.
  1873. */
  1874. && !TestWF(gpqForegroundPrev->spwndActivePrev, WFBOTTOMMOST)) {
  1875. pwnd = gpqForegroundPrev->spwndActivePrev;
  1876. break;
  1877. }
  1878. {
  1879. PWND pwndSave = pwnd;
  1880. DWORD flags = NTW_IGNORETOOLWINDOW;
  1881. TryAgain:
  1882. /*
  1883. * Find a new active window from the top-level window list,
  1884. * skip tool windows the first time through.
  1885. */
  1886. pwnd = NextTopWindow(ptiCurrent, pwndSave, (cmd == AW_SKIP ? pwndSave : NULL),
  1887. flags);
  1888. if (pwnd) {
  1889. if (!FBadWindow(pwnd->spwndLastActive))
  1890. pwnd = pwnd->spwndLastActive;
  1891. } else {
  1892. if (flags == NTW_IGNORETOOLWINDOW) {
  1893. flags = 0;
  1894. goto TryAgain;
  1895. }
  1896. }
  1897. }
  1898. case AW_USE:
  1899. break;
  1900. case AW_USE2:
  1901. fFlags |= ATW_MOUSE;
  1902. break;
  1903. default:
  1904. return FALSE;
  1905. }
  1906. if (pwnd == NULL)
  1907. return FALSE;
  1908. ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwnd);
  1909. if (GETPTI(pwnd)->pq == ptiCurrent->pq) {
  1910. /*
  1911. * Activation is within this queue. Usually this means just do
  1912. * all the normal message sending. But if this queue isn't the
  1913. * foreground queue, check to see if it is allowed to become
  1914. * foreground.
  1915. */
  1916. /*
  1917. * Sometimes processes are granted the right to foreground
  1918. * activate themselves, if they aren't foreground, like
  1919. * when starting up (there are other cases). Grant this if
  1920. * this process is allowed.
  1921. */
  1922. /*
  1923. * Removed the first clause from the following if statement
  1924. * if (pti->pq == gpqForeground || !FAllowForegroundActivate(pti->pq)) {
  1925. * This fixes the problem where foreground app A activates app B
  1926. * the user switches to app C, then B does something to activate A
  1927. * (like destroy an owned window). A now comes to the foreground
  1928. * unexpectedly. This clause is not in Win95 code and was added in
  1929. * 3.51 code to fix some test script hang (Bug 7461)
  1930. */
  1931. if (!FAllowForegroundActivate(ptiCurrent->pq, pwnd)) {
  1932. fSuccess = xxxActivateThisWindow(pwnd, 0, fFlags);
  1933. ThreadUnlock(&tlpwnd);
  1934. return fSuccess;
  1935. }
  1936. fAllowForeground = TRUE;
  1937. /*
  1938. * If this thread doesn't have any top-level non-minimized visible windows,
  1939. * let it keep the right since it's probably not done with activation yet.
  1940. * Bug 274383 - joejo
  1941. */
  1942. fSetForegroundRight = (ptiCurrent->cVisWindows == 0);
  1943. } else {
  1944. /*
  1945. * If the caller is in the foreground, it has the right to change
  1946. * the foreground itself.
  1947. */
  1948. fAllowForeground = (gpqForeground == ptiCurrent->pq)
  1949. || (gpqForeground == NULL);
  1950. /*
  1951. * Give the right to change the foreground to this thread only if it already
  1952. * has it, it has more visible windows or this is an explicit request to
  1953. * activate the given window.
  1954. * When an app destroys/hides the active (foreground) window, we choose a new
  1955. * active window and will probably hit this code. We don't want to give them the
  1956. * right to change the foreground in this case since it's us making the activation
  1957. * (See comments below). We let them keep the right so apps destroying their last
  1958. * visible window (ie a splash initialization window) can take the foreground again
  1959. * when they create another window (the main window).
  1960. */
  1961. if (fAllowForeground) {
  1962. fSetForegroundRight = ((ptiCurrent->TIF_flags & TIF_ALLOWFOREGROUNDACTIVATE)
  1963. || (ptiCurrent->cVisWindows != 0)
  1964. || (cmd == AW_USE));
  1965. } else {
  1966. fSetForegroundRight = FALSE;
  1967. }
  1968. }
  1969. fSuccess = FALSE;
  1970. if (fAllowForeground) {
  1971. /*
  1972. * Hack! Temporarily give this thread a foreground right to make sure
  1973. * this call succeds.
  1974. */
  1975. ptiCurrent->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
  1976. TAGMSG1(DBGTAG_FOREGROUND, "xxxActivateWindow temporarly set TIF %#p", ptiCurrent);
  1977. fSuccess = xxxSetForegroundWindow(pwnd, (cmd == AW_USE));
  1978. if (fSetForegroundRight) {
  1979. /*
  1980. * We activated some other app on purpose. If so that means this
  1981. * thread is probably controlling this window and will probably want
  1982. * to set itself active and foreground really soon again (for example,
  1983. * a setup program doing dde to progman). A real live case: wingz -
  1984. * bring up page setup..., options..., ok, ok. Under Win3.1 the
  1985. * activation goes somewhere strange and then wingz calls
  1986. * SetActiveWindow() to bring it back. This'll make sure that works.
  1987. *
  1988. * We used to set this before calling xxxSetForegeroundWindow above.
  1989. * This would cause callers doing an intra-queue activation to
  1990. * retain their foreground right eventhough it is supposed to be
  1991. * a one shot deal (that's why FAllowForeground clears the bits).
  1992. * In addtion, xxxSetForegroundWindow might clear the bits (it didnt'
  1993. * used to); so we do it here, and only if we did an inter-queue
  1994. * activation
  1995. */
  1996. ptiCurrent->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
  1997. TAGMSG1(DBGTAG_FOREGROUND, "xxxActivateWindow set TIF %#p", ptiCurrent);
  1998. } else {
  1999. /*
  2000. * Make sure to remove the temporary right.
  2001. */
  2002. ptiCurrent->TIF_flags &= ~TIF_ALLOWFOREGROUNDACTIVATE;
  2003. TAGMSG1(DBGTAG_FOREGROUND, "xxxActivateWindow clear TIF %#p", ptiCurrent);
  2004. }
  2005. }
  2006. ThreadUnlock(&tlpwnd);
  2007. return fSuccess;
  2008. }
  2009. /***************************************************************************\
  2010. * GNT_NextTopScan
  2011. *
  2012. * Starting at hwnd (or hwndDesktop->hwndChild if hwnd == NULL), find
  2013. * the next window owned by hwndOwner.
  2014. *
  2015. * History:
  2016. * 11-08-90 DavidPe Ported.
  2017. * 02-11-91 JimA Multi-desktop support.
  2018. \***************************************************************************/
  2019. PWND GNT_NextTopScan(
  2020. PTHREADINFO pti,
  2021. PWND pwnd,
  2022. PWND pwndOwner)
  2023. {
  2024. if (pwnd == NULL) {
  2025. UserAssert(pti->rpdesk != NULL &&
  2026. (pti->rpdesk->dwDTFlags & DF_DESKWNDDESTROYED) == 0);
  2027. pwnd = pti->rpdesk->pDeskInfo->spwnd->spwndChild;
  2028. } else {
  2029. pwnd = pwnd->spwndNext;
  2030. }
  2031. for (; pwnd != NULL; pwnd = pwnd->spwndNext) {
  2032. if (pwnd->spwndOwner == pwndOwner)
  2033. break;
  2034. }
  2035. return pwnd;
  2036. }
  2037. /***************************************************************************\
  2038. * NTW_GetNextTop
  2039. *
  2040. * <brief description>
  2041. *
  2042. * History:
  2043. * 11-08-90 DavidPe Ported.
  2044. * 02-11-91 JimA Multi-desktop support.
  2045. \***************************************************************************/
  2046. PWND NTW_GetNextTop(
  2047. PTHREADINFO pti,
  2048. PWND pwnd)
  2049. {
  2050. PWND pwndOwner;
  2051. if (pwnd == NULL) {
  2052. goto ReturnFirst;
  2053. }
  2054. /*
  2055. * First look for any windows owned by this window
  2056. * If that fails, then go up one level to our owner,
  2057. * and look for next window owned by his owner.
  2058. * This results in a depth-first ordering of the windows.
  2059. */
  2060. pwndOwner = pwnd;
  2061. pwnd = NULL;
  2062. do {
  2063. if ((pwnd = GNT_NextTopScan(pti, pwnd, pwndOwner)) != NULL) {
  2064. return pwnd;
  2065. }
  2066. pwnd = pwndOwner;
  2067. if (pwnd != NULL)
  2068. pwndOwner = pwnd->spwndOwner;
  2069. } while (pwnd != NULL);
  2070. ReturnFirst:
  2071. /*
  2072. * If no more windows to enumerate, return the first unowned window.
  2073. */
  2074. return GNT_NextTopScan(pti, NULL, NULL);
  2075. }
  2076. /***************************************************************************\
  2077. * NTW_GetPrevTop
  2078. *
  2079. * <brief description>
  2080. *
  2081. * History:
  2082. * 11-08-90 DavidPe Ported.
  2083. * 02-11-91 JimA Multi-desktop support.
  2084. \***************************************************************************/
  2085. PWND NTW_GetPrevTop(
  2086. PTHREADINFO pti,
  2087. PWND pwndCurrent)
  2088. {
  2089. PWND pwnd;
  2090. PWND pwndPrev;
  2091. /*
  2092. * Starting from beginning, loop thru the windows, saving the previous
  2093. * one, until we find the window we're currently at.
  2094. */
  2095. pwndPrev = NULL;
  2096. do {
  2097. pwnd = NTW_GetNextTop(pti, pwndPrev);
  2098. if (pwnd == pwndCurrent && pwndPrev != NULL) {
  2099. break;
  2100. }
  2101. } while ((pwndPrev = pwnd) != NULL);
  2102. return pwndPrev;
  2103. }
  2104. /***************************************************************************\
  2105. * NextTopWindow
  2106. *
  2107. * <brief description>
  2108. *
  2109. * History:
  2110. * 11-08-90 DavidPe Ported.
  2111. * 02-11-91 JimA Multi-desktop support.
  2112. \***************************************************************************/
  2113. PWND CheckTopLevelOnly(
  2114. PWND pwnd)
  2115. {
  2116. /*
  2117. * fnid == -1 means this is a desktop window - find the first child
  2118. * of this desktop, if it is one.
  2119. */
  2120. while (pwnd != NULL && GETFNID(pwnd) == FNID_DESKTOP) {
  2121. pwnd = pwnd->spwndChild;
  2122. }
  2123. return pwnd;
  2124. }
  2125. PWND NextTopWindow(
  2126. PTHREADINFO pti,
  2127. PWND pwnd,
  2128. PWND pwndSkip,
  2129. DWORD flags )
  2130. {
  2131. BOOL fFoundFirstUnowned;
  2132. PWND pwndPrev;
  2133. PWND pwndStart = pwnd;
  2134. PWND pwndFirstUnowned;
  2135. /*
  2136. * If the search gets to the first unowned window TWICE (See NTW_GetNextTop),
  2137. * we couldn't find a window
  2138. */
  2139. pwndFirstUnowned = GNT_NextTopScan(pti, NULL, NULL);
  2140. fFoundFirstUnowned = FALSE;
  2141. if (pwnd == NULL) {
  2142. pwnd = NTW_GetNextTop(pti, NULL);
  2143. /*
  2144. * Don't allow desktop windows.
  2145. */
  2146. pwnd = pwndStart = CheckTopLevelOnly(pwnd);
  2147. if (pwnd == NULL)
  2148. return NULL; // No more windows owned by the thread
  2149. goto Loop;
  2150. }
  2151. /*
  2152. * Don't allow desktop windows.
  2153. */
  2154. pwnd = pwndStart = CheckTopLevelOnly(pwnd);
  2155. if (pwnd == NULL)
  2156. return NULL; // No more windows owned by this thread
  2157. /*
  2158. * Don't allow desktop windows.
  2159. */
  2160. pwndSkip = CheckTopLevelOnly(pwndSkip);
  2161. while (TRUE) {
  2162. pwndPrev = pwnd;
  2163. pwnd = ((flags & NTW_PREVIOUS) ? NTW_GetPrevTop(pti, pwnd) : NTW_GetNextTop(pti, pwnd));
  2164. /*
  2165. * If we've cycled to where we started, couldn't find one: return NULL
  2166. */
  2167. if (pwnd == pwndStart)
  2168. break;
  2169. if (pwnd == pwndFirstUnowned) {
  2170. if (fFoundFirstUnowned) {
  2171. break;
  2172. } else {
  2173. fFoundFirstUnowned = TRUE;
  2174. }
  2175. }
  2176. if (pwnd == NULL)
  2177. break;
  2178. /*
  2179. * If we've cycled over desktops, then return NULL because we'll
  2180. * never hit pwndStart.
  2181. */
  2182. if (PWNDDESKTOP(pwndStart) != PWNDDESKTOP(pwnd))
  2183. break;
  2184. /*
  2185. * going nowhere is a bad sign.
  2186. */
  2187. if (pwndPrev == pwnd) {
  2188. /*
  2189. * This is a temporary fix chosen because its safe. This case
  2190. * was hit when a window failed the NCCREATE message and fell
  2191. * into xxxFreeWindow and left the critical section after being
  2192. * unlinked. The app then died and entered cleanup code and
  2193. * tried to destroy this window again.
  2194. */
  2195. break;
  2196. }
  2197. Loop:
  2198. if (pwnd == pwndSkip)
  2199. continue;
  2200. /*
  2201. * If it's visible, not disabled, not a noactivate window
  2202. * and either we're not ignoringtool windows or it's not a
  2203. * tool window, then we've got it.
  2204. */
  2205. if (TestWF(pwnd, WFVISIBLE) &&
  2206. !TestWF(pwnd, WFDISABLED) &&
  2207. !TestWF(pwnd, WEFNOACTIVATE) &&
  2208. (!(flags & NTW_IGNORETOOLWINDOW) || !TestWF(pwnd, WEFTOOLWINDOW))) {
  2209. return pwnd;
  2210. }
  2211. }
  2212. return NULL;
  2213. }
  2214. /***************************************************************************\
  2215. * xxxCheckFocus
  2216. *
  2217. *
  2218. * History:
  2219. * 11-08-90 DarrinM Ported.
  2220. \***************************************************************************/
  2221. void xxxCheckFocus(
  2222. PWND pwnd)
  2223. {
  2224. TL tlpwndParent;
  2225. PTHREADINFO pti;
  2226. CheckLock(pwnd);
  2227. pti = PtiCurrent();
  2228. if (pwnd == pti->pq->spwndFocus) {
  2229. /*
  2230. * Set focus to parent of child window.
  2231. */
  2232. if (TestwndChild(pwnd)) {
  2233. ThreadLockWithPti(pti, pwnd->spwndParent, &tlpwndParent);
  2234. xxxSetFocus(pwnd->spwndParent);
  2235. ThreadUnlock(&tlpwndParent);
  2236. } else {
  2237. xxxSetFocus(NULL);
  2238. }
  2239. }
  2240. if (pwnd == pti->pq->caret.spwnd) {
  2241. zzzDestroyCaret();
  2242. }
  2243. }
  2244. /***************************************************************************\
  2245. * SetForegroundThread
  2246. *
  2247. *
  2248. * History:
  2249. * 12-xx-91 MarkL Created.
  2250. * 02-12-92 DavidPe Rewrote as SetForegroundThread().
  2251. \***************************************************************************/
  2252. VOID SetForegroundThread(
  2253. PTHREADINFO pti)
  2254. {
  2255. PKL pklPrev;
  2256. if (pti == gptiForeground)
  2257. return;
  2258. /*
  2259. * The foregorund thread must be on the foreground queue.
  2260. * xxxSendFocusMessages obtains this pti from a window
  2261. * received as a parameter. If the owner of the window
  2262. * exited during a callback (in the caller), then the pti
  2263. * will be gptiRit,which might not be in the foreground queue
  2264. */
  2265. UserAssert((pti == NULL)
  2266. || (pti->pq == gpqForeground)
  2267. || (pti == gptiRit));
  2268. /*
  2269. * If we're changing gptiForeground to another process,
  2270. * change the base priorities of the two processes. We
  2271. * know that if either 'pti' or 'gptiForeground' is NULL
  2272. * that both aren't NULL due to the first test in this
  2273. * function.
  2274. */
  2275. if ((pti == NULL) || (gptiForeground == NULL) ||
  2276. (pti->ppi != gptiForeground->ppi)) {
  2277. if (gptiForeground != NULL) {
  2278. gptiForeground->ppi->W32PF_Flags &= ~W32PF_FORCEBACKGROUNDPRIORITY;
  2279. SetForegroundPriority(gptiForeground, FALSE);
  2280. }
  2281. if (pti != NULL) {
  2282. SetForegroundPriority(pti, TRUE);
  2283. }
  2284. }
  2285. if (gptiForeground) {
  2286. pklPrev = gptiForeground->spklActive;
  2287. } else {
  2288. pklPrev = NULL;
  2289. }
  2290. gptiForeground = pti;
  2291. if (gptiForeground && gptiForeground->spklActive) {
  2292. ChangeForegroundKeyboardTable(pklPrev, gptiForeground->spklActive);
  2293. }
  2294. /*
  2295. * Clear recent down information in the async key state to prevent
  2296. * spying by apps.
  2297. */
  2298. RtlZeroMemory(gafAsyncKeyStateRecentDown, CBKEYSTATERECENTDOWN);
  2299. /*
  2300. * Update the async key cache index.
  2301. */
  2302. gpsi->dwAsyncKeyCache++;
  2303. }
  2304. VOID SetForegroundPriorityProcess(
  2305. PPROCESSINFO ppi,
  2306. PTHREADINFO pti,
  2307. BOOL fSetForeground)
  2308. {
  2309. PEPROCESS Process;
  2310. UCHAR PriorityClassSave;
  2311. UserAssert(ppi != NULL);
  2312. Process = ppi->Process;
  2313. UserAssert(ppi->Process != NULL);
  2314. if (ppi->W32PF_Flags & W32PF_IDLESCREENSAVER) {
  2315. fSetForeground = FALSE;
  2316. PriorityClassSave = PsGetProcessPriorityClass(Process);
  2317. PsSetProcessPriorityClass(Process, PROCESS_PRIORITY_CLASS_IDLE);
  2318. }
  2319. /*
  2320. * If we previously delayed setting some process to the background
  2321. * because a screen saver was starting up, do it now.
  2322. */
  2323. if (gppiForegroundOld != NULL) {
  2324. if (gppiForegroundOld == ppi) {
  2325. gppiForegroundOld = NULL;
  2326. } else if (ppi != gppiScreenSaver) {
  2327. PsSetProcessPriorityByClass(gppiForegroundOld->Process, PsProcessPriorityBackground);
  2328. gppiForegroundOld = NULL;
  2329. }
  2330. }
  2331. /*
  2332. * If this app should be background, don't let it go foreground.
  2333. * Foreground apps run at a higher base priority.
  2334. */
  2335. if (ppi->W32PF_Flags & W32PF_FORCEBACKGROUNDPRIORITY) {
  2336. if (pti != NULL && !(pti->TIF_flags & TIF_GLOBALHOOKER)) {
  2337. PsSetProcessPriorityByClass(Process, PsProcessPrioritySpinning);
  2338. }
  2339. } else if (fSetForeground) {
  2340. PsSetProcessPriorityByClass(Process, PsProcessPriorityForeground);
  2341. } else if (pti != NULL && !(pti->TIF_flags & TIF_GLOBALHOOKER)) {
  2342. /*
  2343. * Don't adjust the priority of the current foreground process if
  2344. * the new foreground process is a screen saver.
  2345. */
  2346. if (gppiScreenSaver && gppiScreenSaver != ppi) {
  2347. gppiForegroundOld = ppi;
  2348. } else {
  2349. PsSetProcessPriorityByClass(Process, PsProcessPriorityBackground);
  2350. }
  2351. }
  2352. if (ppi->W32PF_Flags & W32PF_IDLESCREENSAVER) {
  2353. PsSetProcessPriorityClass(Process, PriorityClassSave);
  2354. }
  2355. }
  2356. VOID SetForegroundPriority(
  2357. PTHREADINFO pti,
  2358. BOOL fSetForeground)
  2359. {
  2360. UserAssert(pti != NULL);
  2361. /*
  2362. * We don't want to change the priority of system or console threads
  2363. */
  2364. if (pti->TIF_flags & (TIF_SYSTEMTHREAD | TIF_CSRSSTHREAD))
  2365. return;
  2366. SetForegroundPriorityProcess(pti->ppi, pti, fSetForeground);
  2367. }