internal TabletDevice(TabletDeviceInfo tabletInfo, PenThread penThread) { _tabletInfo = tabletInfo; _penThread = penThread; int count = tabletInfo.StylusDevicesInfo.Length; StylusDevice[] styluses = new StylusDevice[count]; for ( int i = 0; i < count; i++ ) { StylusDeviceInfo cursorInfo = tabletInfo.StylusDevicesInfo[i]; styluses[i] = new StylusDevice( this, cursorInfo.CursorName, cursorInfo.CursorId, cursorInfo.CursorInverted, cursorInfo.ButtonCollection); } _stylusDeviceCollection = new StylusDeviceCollection(styluses); if (_tabletInfo.DeviceType == TabletDeviceType.Touch) { // _multiTouchSystemGestureLogic = new MultiTouchSystemGestureLogic(); } }
internal TabletDevice(TabletDeviceInfo tabletInfo, PenThread penThread) { _tabletInfo = tabletInfo; _penThread = penThread; int count = tabletInfo.StylusDevicesInfo.Length; StylusDevice[] styluses = new StylusDevice[count]; for (int i = 0; i < count; i++) { StylusDeviceInfo cursorInfo = tabletInfo.StylusDevicesInfo[i]; styluses[i] = new StylusDevice( this, cursorInfo.CursorName, cursorInfo.CursorId, cursorInfo.CursorInverted, cursorInfo.ButtonCollection); } _stylusDeviceCollection = new StylusDeviceCollection(styluses); if (_tabletInfo.DeviceType == TabletDeviceType.Touch) { // _multiTouchSystemGestureLogic = new MultiTouchSystemGestureLogic(); } }
internal WispTabletDevice(TabletDeviceInfo tabletInfo, PenThread penThread) : base(tabletInfo) { _penThread = penThread; int count = tabletInfo.StylusDevicesInfo.Length; WispStylusDevice[] styluses = new WispStylusDevice[count]; for (int i = 0; i < count; i++) { StylusDeviceInfo cursorInfo = tabletInfo.StylusDevicesInfo[i]; styluses[i] = new WispStylusDevice( this, cursorInfo.CursorName, cursorInfo.CursorId, cursorInfo.CursorInverted, cursorInfo.ButtonCollection); } _stylusDeviceCollection = new StylusDeviceCollection(styluses); // We only create a TabletDevice when one is connected (physically or virtually). // So we can log the connection in the constructor. StylusTraceLogger.LogDeviceConnect( new StylusTraceLogger.StylusDeviceInfo( Id, Name, ProductId, TabletHardwareCapabilities, TabletSize, ScreenSize, _tabletInfo.DeviceType, StylusDevices.Count)); }
private PenThread GetPenThreadForPenContextHelper(PenContext penContext) { bool needCleanup = false; PenThread penThread = null; int i; // Scan existing penthreads to see if we have an available slot for context. for (i = 0; i < _penThreadWeakRefList.Count; i++) { PenThread penThreadFound = _penThreadWeakRefList[i].Target as PenThread; if (penThreadFound == null) { needCleanup = true; } else { // See if we can use this one if (penContext == null || penThreadFound.AddPenContext(penContext)) { // We can use this one. penThread = penThreadFound; break; } } } if (needCleanup) { // prune invalid refs for (i = _penThreadWeakRefList.Count - 1; i >= 0; i--) { if (_penThreadWeakRefList[i].Target == null) { _penThreadWeakRefList.RemoveAt(i); } } } if (penThread == null) { penThread = new PenThread(); // Make sure we add this context to the penthread if (penContext != null) { penThread.AddPenContext(penContext); } _penThreadWeakRefList.Add(new WeakReference(penThread)); } return(penThread); }
internal void DisposeOrDeferDisposal() { // Only dispose when no input events are left in the queue if (CanDispose) { // Make sure this device is not the current one. if (Tablet.CurrentTabletDevice == this.TabletDevice) { StylusLogic.GetCurrentStylusLogicAs <WispLogic>().SelectStylusDevice(null, null, true); } // A disconnect will be logged in the dispose as WPF will have gotten rid of the tablet. StylusTraceLogger.LogDeviceDisconnect(_tabletInfo.Id); // DDVSO:174153 // Force tablets to clean up as soon as they are disposed. This helps to reduce // COM references that might be waiting for RCWs to finalize. IPimcTablet2 tablet = _tabletInfo.PimcTablet?.Value; _tabletInfo.PimcTablet = null; if (tablet != null) { // DDVSO:514949 // Balance calls in PenThreadWorker.GetTabletInfoHelper and CPimcTablet::Init. PenThread.WorkerReleaseTabletLocks(tablet, _tabletInfo.WispTabletKey); Marshal.ReleaseComObject(tablet); } StylusDeviceCollection styluses = _stylusDeviceCollection; _stylusDeviceCollection = null; if (styluses != null) { styluses.Dispose(); } _penThread = null; _isDisposalPending = false; // DDVSO:614343 // Ensure that we are marked disposed and no longer attempt to finalize. _disposed = true; GC.SuppressFinalize(this); } else { _isDisposalPending = true; } }
///////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// internal WispTabletDevice(TabletDeviceInfo tabletInfo, PenThread penThread) : base(tabletInfo) { _penThread = penThread; // Constructing a WispTabletDevice means we will actually use this tablet for input purposes. // Lock the tablet and underlying WISP tablet at this point. // This is balanced in DisposeOrDeferDisposal. _penThread.WorkerAcquireTabletLocks(tabletInfo.PimcTablet.Value, tabletInfo.WispTabletKey); int count = tabletInfo.StylusDevicesInfo.Length; WispStylusDevice[] styluses = new WispStylusDevice[count]; for (int i = 0; i < count; i++) { StylusDeviceInfo cursorInfo = tabletInfo.StylusDevicesInfo[i]; styluses[i] = new WispStylusDevice( this, cursorInfo.CursorName, cursorInfo.CursorId, cursorInfo.CursorInverted, cursorInfo.ButtonCollection); } _stylusDeviceCollection = new StylusDeviceCollection(styluses); // We only create a TabletDevice when one is connected (physically or virtually). // So we can log the connection in the constructor. StylusTraceLogger.LogDeviceConnect( new StylusTraceLogger.StylusDeviceInfo( Id, Name, ProductId, TabletHardwareCapabilities, TabletSize, ScreenSize, _tabletInfo.DeviceType, StylusDevices.Count)); }
/// <summary> /// DevDiv:1192272 /// /// This function has been changed to avoid re-entrancy issues. Previously, the /// PenThread selection depended on calls to AddPenContext in the selection mechanism /// or when creating a new PenThread. Since AddPenContext will wait on operations done /// on a PenThread, this would allow re-entrant calls to occur. These calls had the /// potential to generate redundant PenThreads that could cause performance and functional /// issues in touch-enabled applications. /// /// By removing calls to AddPenContext from the selection loops, we can be certain that there is /// no re-entrancy possible during this part of the code. The call to AddPenContext is now done /// post thread selection/creation. While this is still re-entrant, we handle the possible issues /// from that case by retrying thread selection for any failed AddPenContext calls, ignoring the /// specific thread that failed. After MAX_PENTHREAD_RETRIES, we exit and log an error. /// </summary> private PenThread GetPenThreadForPenContextHelper(PenContext penContext) { // A list of full PenThreads that we should ignore when attempting to select a thread // for this context. List <PenThread> ignoredThreads = new List <PenThread>(); PenThread selectedPenThread = null; // We have gone over the max retries, something is forcing a huge amount // of re-entrancy. In this case, break the loop and exit even if we might // have some issues with missing touch contexts and bad touch behavior. while (ignoredThreads.Count < MAX_PENTHREAD_RETRIES) { // Scan existing penthreads to find one to add the context to // We scan back to front to enable list cleanup. for (int i = _penThreadWeakRefList.Count - 1; i >= 0; i--) { PenThread candidatePenThread = null; // Select a thread if it's a valid WeakReference and we're not ignoring it // Allow selection to happen multiple times so we get the first valid candidate // in forward order. if (_penThreadWeakRefList[i].TryGetTarget(out candidatePenThread) && !ignoredThreads.Contains(candidatePenThread)) { selectedPenThread = candidatePenThread; } // This is an invalid WeakReference and should be removed else if (candidatePenThread == null) { _penThreadWeakRefList.RemoveAt(i); } } // If no valid thread was found, create a new one and add to the pool if (selectedPenThread == null) { selectedPenThread = new PenThread(); _penThreadWeakRefList.Add(new WeakReference <PenThread>(selectedPenThread)); } // If we have no context or we can successfully add to it, then end with this thread if (penContext == null || selectedPenThread.AddPenContext(penContext)) { break; } // If the add wasn't successful, this thread is full, so try again and ignore it else { ignoredThreads.Add(selectedPenThread); selectedPenThread = null; // Log re-entrant calls StylusTraceLogger.LogReentrancy(); } } // If we're here due to max retries, log errors appropriately if (selectedPenThread == null) { StylusTraceLogger.LogReentrancyRetryLimitReached(); Debug.Assert(false, "Retry limit reached when acquiring PenThread"); } return(selectedPenThread); }
internal bool HandleTabletAdded(uint wisptisIndex, ref uint tabletIndexChanged) { if (_tablets == null) { throw new ObjectDisposedException("TabletDeviceCollection"); } tabletIndexChanged = UInt32.MaxValue; // REENTRANCY NOTE: Let a PenThread do this work to avoid reentrancy! // On return you get the tablet info needed to // create a tablet devices (and stylus device info gets // cached in penimc too which avoids calls to wisptis.exe). // Use existing penthread if we have one otherwise grab an available one. PenThread penThread = _tablets.Length > 0 ? _tablets[0].PenThread : PenThreadPool.GetPenThreadForPenContext(null); TabletDeviceInfo tabletInfo = penThread.WorkerGetTabletInfo(wisptisIndex); // If we failed due to a COM exception on the pen thread then return if (tabletInfo.PimcTablet == null) { return(true); // make sure we rebuild our tablet collection. (return true + MaxValue). } // if mouse tabletdevice then ignore it. if (tabletInfo.DeviceType == (TabletDeviceType)(-1)) { _indexMouseTablet = wisptisIndex; // update index. return(false); // TabletDevices did not change. } // Now see if this is a duplicate add call we want to filter out (ie - already added to tablet collection). uint indexFound = UInt32.MaxValue; for (uint i = 0; i < _tablets.Length; i++) { // If it is the mouse tablet device we want to ignore it. if (_tablets[i].Id == tabletInfo.Id) { indexFound = i; break; } } // We only want to add this if it is not currently in the collection. Wisptis will send // us duplicate adds at times so this is a work around for that issue. uint tabletIndex = UInt32.MaxValue; if (indexFound == UInt32.MaxValue) { tabletIndex = wisptisIndex; if (tabletIndex > _indexMouseTablet) { tabletIndex--; } else { _indexMouseTablet++; } // if index is out of range then ignore it. Return of MaxValue causes a rebuild of the devices. if (tabletIndex <= _tablets.Length) { try { // Add new tablet at end of collection AddTablet(tabletIndex, new TabletDevice(tabletInfo, penThread)); } catch (InvalidOperationException ex) { // This is caused by the Stylus ID not being unique when trying // to register it in the StylusLogic.__stylusDeviceMap. If we // come across a dup then we should rebuild the tablet device collection. // There seems to be an issue in wisptis where different tablet IDs get // duplicate Stylus Ids when installing the VHID test devices. if (ex.Data.Contains("System.Windows.Input.StylusLogic")) { return(true); // trigger the tabletdevices to be rebuilt. } else { throw; // not an expected exception, rethrow it. } } tabletIndexChanged = tabletIndex; return(true); } else { return(true); // bogus index. Return true so that the caller can rebuild the collection. } } else { return(false); // We found this tablet device already. Don't do anything. } }
void UpdateTabletsImpl() { // REENTRANCY NOTE: Let a PenThread do this work to avoid reentrancy! // On return you get entire list of tablet and info needed to // create all the tablet devices (and stylus device info gets // cached too in penimc which avoids calls to wisptis.exe). // Use existing penthread if we have one otherwise grab an available one. PenThread penThread = _tablets.Length > 0 ? _tablets[0].PenThread : PenThreadPool.GetPenThreadForPenContext(null); TabletDeviceInfo [] tabletdevices = penThread.WorkerGetTabletsInfo(); // First find out the index of the mouse device (usually the first at index 0) uint indexMouseTablet = UInt32.MaxValue; for (uint i = 0; i < tabletdevices.Length; i++) { // See if this is a bogus entry first. if (tabletdevices[i].PimcTablet == null) { continue; } // If it is the mouse tablet device we want to ignore it. if (tabletdevices[i].DeviceType == (TabletDeviceType)(-1)) { indexMouseTablet = i; tabletdevices[i].PimcTablet = null; // ignore this one! } } // Now figure out count of valid tablet devices left uint count = 0; for (uint k = 0; k < tabletdevices.Length; k++) { if (tabletdevices[k].PimcTablet != null) { count++; } } TabletDevice[] tablets = new TabletDevice[count]; uint tabletsIndex = 0; uint unchangedTabletCount = 0; for (uint iTablet = 0; iTablet < tabletdevices.Length; iTablet++) { if (tabletdevices[iTablet].PimcTablet == null) { continue; // Skip looking at this index (mouse and bogus tablets are ignored). } int id = tabletdevices[iTablet].Id; // First see if same index has not changed (typical case) if (tabletsIndex < _tablets.Length && _tablets[tabletsIndex] != null && _tablets[tabletsIndex].Id == id) { tablets[tabletsIndex] = _tablets[tabletsIndex]; _tablets[tabletsIndex] = null; // clear to ignore on cleanup pass. unchangedTabletCount++; } else { // Look up and see if we have this tabletdevice created already... TabletDevice tablet = null; for (uint i = 0; i < _tablets.Length; i++) { if (_tablets[i] != null && _tablets[i].Id == id) { tablet = _tablets[i]; _tablets[i] = null; // clear it so we don't dispose it. break; } } // Not found so create it. if (tablet == null) { try { tablet = new TabletDevice(tabletdevices[iTablet], penThread); } catch (InvalidOperationException ex) { // This is caused by the Stylus ID not being unique when trying // to register it in the StylusLogic.__stylusDeviceMap. If we // come across a dup then just ignore registering this tablet device. // There seems to be an issue in wisptis where different tablet IDs get // duplicate Stylus Ids when installing the VHID test devices. if (ex.Data.Contains("System.Windows.Input.StylusLogic")) { continue; // Just go to next without adding this one. } else { throw; // not an expected exception, rethrow it. } } } tablets[tabletsIndex] = tablet; } tabletsIndex++; } if (unchangedTabletCount == _tablets.Length && unchangedTabletCount == tabletsIndex && tabletsIndex == count) { // Keep the _tablet reference when nothing changes. // The reason is that if this method gets called from within // CreateContexts while looping on _tablets, it could result in // a null ref exception since the original _tablets array has // been purged to nulls. // NOTE: There is still the case of changing the ref (else case below) // when tablet devices actually change. But such a case is rare // (if not improbable) from within CreateContexts. Array.Copy(tablets, 0, _tablets, 0, count); _indexMouseTablet = indexMouseTablet; } else { // See if we need to re alloc the array due to invalid tabletdevice being seen. if (tabletsIndex != count) { TabletDevice[] updatedTablets = new TabletDevice[tabletsIndex]; Array.Copy(tablets, 0, updatedTablets, 0, tabletsIndex); tablets = updatedTablets; } DisposeTablets(); // Clean up any non null TabletDevice entries on old array. _tablets = tablets; // set updated tabletdevice array _indexMouseTablet = indexMouseTablet; } // DevDiv:1078091 // Any deferred tablet should be properly disposed of when applicable and // removed from the list of deferred tablets. DisposeDeferredTablets(); }
private PenThread GetPenThreadForPenContextHelper(PenContext penContext) { bool needCleanup = false; PenThread penThread = null; int i; // Scan existing penthreads to see if we have an available slot for context. for (i=0; i < _penThreadWeakRefList.Count; i++) { PenThread penThreadFound = _penThreadWeakRefList[i].Target as PenThread; if (penThreadFound == null) { needCleanup = true; } else { // See if we can use this one if (penContext == null || penThreadFound.AddPenContext(penContext)) { // We can use this one. penThread = penThreadFound; break; } } } if (needCleanup) { // prune invalid refs for (i=_penThreadWeakRefList.Count - 1; i >= 0; i--) { if (_penThreadWeakRefList[i].Target == null) { _penThreadWeakRefList.RemoveAt(i); } } } if (penThread == null) { penThread = new PenThread(); // Make sure we add this context to the penthread if (penContext != null) { penThread.AddPenContext(penContext); } _penThreadWeakRefList.Add(new WeakReference(penThread)); } return penThread; }