Exemplo n.º 1
0
        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));
        }
Exemplo n.º 2
0
            internal override void OnShutDown(object target, object sender, EventArgs e)
            {
                StylusLogic stylusLogic = (StylusLogic)target;

                StylusTraceLogger.LogStatistics(stylusLogic.Statistics);

                StylusTraceLogger.LogShutdown();
            }
        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;
            }
        }
Exemplo n.º 4
0
        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)
                {
                    Marshal.ReleaseComObject(tablet);
                }

                StylusDeviceCollection styluses = _stylusDeviceCollection;
                _stylusDeviceCollection = null;

                if (styluses != null)
                {
                    styluses.Dispose();
                }

                _penThread         = null;
                _isDisposalPending = false;
            }
            else
            {
                _isDisposalPending = true;
            }
        }
Exemplo n.º 5
0
        /////////////////////////////////////////////////////////////////////////

        /////////////////////////////////////////////////////////////////////
        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));
        }
Exemplo n.º 6
0
        /// <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);
        }
Exemplo n.º 7
0
        /// <summary>
        /// Retrieves the latest device information from connected touch devices.
        /// </summary>
        internal void Refresh()
        {
            try
            {
                // Keep track of old tablets so that we can properly log connects/disconnects
                Dictionary <IntPtr, PointerTabletDevice> oldTablets = _tabletDeviceMap;

                _tabletDeviceMap = new Dictionary <IntPtr, PointerTabletDevice>();
                TabletDevices.Clear();

                uint deviceCount = 0;

                // Pattern is to first get the count, then declare an array of that size
                // which is then marshaled via the second call with the proper data.
                IsValid = UnsafeNativeMethods.GetPointerDevices(ref deviceCount, null);

                if (IsValid)
                {
                    UnsafeNativeMethods.POINTER_DEVICE_INFO[] deviceInfos
                        = new UnsafeNativeMethods.POINTER_DEVICE_INFO[deviceCount];

                    IsValid = UnsafeNativeMethods.GetPointerDevices(ref deviceCount, deviceInfos);

                    if (IsValid)
                    {
                        foreach (var deviceInfo in deviceInfos)
                        {
                            // Old PenIMC code gets this id via a straight cast from COM pointer address
                            // into an int32.  This does a very similar thing semantically using the pointer
                            // to the tablet from the WM_POINTER stack.  While it may have similar issues
                            // (chopping the upper bits, duplicate ids) we don't use this id internally
                            // and have never received complaints about this in the WISP stack.
                            int id = MS.Win32.NativeMethods.IntPtrToInt32(deviceInfo.device);

                            PointerTabletDeviceInfo ptdi = new PointerTabletDeviceInfo(id, deviceInfo);


                            // Don't add a device that fails initialization.  This means we will try a refresh
                            // next time around if we receive stylus input and the device is not available.
                            // <see cref="HwndPointerInputProvider.UpdateCurrentTabletAndStylus">
                            if (ptdi.TryInitialize())
                            {
                                PointerTabletDevice tablet = new PointerTabletDevice(ptdi);

                                if (!oldTablets.Remove(tablet.Device))
                                {
                                    // We only create a TabletDevice when one is connected (physically or virtually).
                                    // As such we have to log when there is no corresponding old tablet being refreshed.
                                    StylusTraceLogger.LogDeviceConnect(
                                        new StylusTraceLogger.StylusDeviceInfo(
                                            tablet.Id,
                                            tablet.Name,
                                            tablet.ProductId,
                                            tablet.TabletHardwareCapabilities,
                                            tablet.TabletSize,
                                            tablet.ScreenSize,
                                            tablet.Type,
                                            tablet.StylusDevices.Count));
                                }

                                _tabletDeviceMap[tablet.Device] = tablet;
                                TabletDevices.Add(tablet.TabletDevice);
                            }
                        }
                    }

                    // Any tablet leftover here was not refreshed from the previous set of tablets
                    // and should be logged as disconnected.
                    foreach (var oldTablet in oldTablets.Values)
                    {
                        StylusTraceLogger.LogDeviceDisconnect(oldTablet.Id);
                    }
                }
            }
            catch (Win32Exception)
            {
                IsValid = false;
            }
        }
Exemplo n.º 8
0
        internal void UpdateTablets()
        {
            if (_tablets == null)
            {
                throw new ObjectDisposedException("TabletDeviceCollection");
            }

            // This method can be re-entered in a way that can cause deadlock
            // (Dev11 960656).  This can happen if multiple WM_DEVICECHANGE
            // messages are pending and we have not yet built the tablet collection.
            // Here's how:
            // 1. First WM_DEVICECHANGE message enters here, starts to launch pen thread
            // 2.   Penimc.UnsafeNativeMethods used for the first time, start its
            //              static constructor.
            // 3.     The static cctor starts to create a PimcManager via COM CLSID.
            // 4.       COM pumps message, a second WM_DEVICECHANGE re-enters here
            //              and starts to launch another pen thread.
            // 5.         The static cctor is skipped (although step 3 hasn't finished),
            //                  and the pen thread is started
            // 6.           The PenThreadWorker (on the UI thread) waits for the
            //                  PenThread to respond.
            // 7. Meanwhile the pen thread's code refers to Pimc.UnsafeNativeMethods.
            //      It's on a separate thread, so it waits for the static cctor to finish.
            // Deadlock:  UI thread is waiting for PenThread, but holding the CLR's
            //  lock on the static cctor.  The Pen thread is waiting for the static cctor.
            //
            // Multiple WM_DEVICECHANGE messages also cause re-entrancy even if the
            // static cctor has already finished.  There's no deadlock in that case,
            // but we end up with multiple pen threads (steps 1 and 4).  Besides being
            // redundant, that can cause problems when the threads collide with each
            // other or do work twice.
            //
            // In any case, the re-entrancy is harmful.  So we avoid it in the usual
            // way - setting a flag on entry and early-exit if the flag is set.
            //
            // Usually the outermost call will leave the tablet collection in the
            // right state, but there's a small chance that the OS or pen-input service
            // will change something while the outermost call is in progress, so that
            // the inner (re-entrant) call really would have picked up new information.
            // To handle that case, we'll simply re-run the outermost call if any
            // re-entrancy is detected.  Usually that will have no real effect, as
            // the code here detects when no change is made to the tablet collection.

            if (_inUpdateTablets)
            {
                // Log re-entrant calls
                StylusTraceLogger.LogReentrancy();

                // this is a re-entrant call.  Note that it happened, but do no work.
                _hasUpdateTabletsBeenCalledReentrantly = true;
                return;
            }

            try
            {
                _inUpdateTablets = true;
                do
                {
                    _hasUpdateTabletsBeenCalledReentrantly = false;

                    // do the real work
                    UpdateTabletsImpl();

                    // if re-entrancy happened, start over
                    // This could loop forever, but only if we get an unbounded
                    // number of re-entrant events;  that would have looped
                    // forever even without this re-entrancy logic.
                } while (_hasUpdateTabletsBeenCalledReentrantly);
            }
            finally
            {
                // when we're done (either normally or via exception)
                // reset the re-entrancy state
                _inUpdateTablets = false;
                _hasUpdateTabletsBeenCalledReentrantly = false;
            }
        }