/// <summary> /// Attempts to update the current stylus and tablet devices for the latest WM_POINTER message. /// Will attempt retries if the tablet collection is invalid or does not contain the proper ids. /// </summary> /// <param name="deviceId">The id of the TabletDevice</param> /// <param name="cursorId">The id of the StylusDevice</param> /// <returns>True if successfully updated, false otherwise.</returns> private bool UpdateCurrentTabletAndStylus(IntPtr deviceId, uint cursorId) { PointerTabletDeviceCollection tablets = Tablet.TabletDevices?.As <PointerTabletDeviceCollection>(); // We have an invalid tablet collection, we should refresh to make sure // we have the latest. if (!tablets.IsValid) { tablets.Refresh(); // If the refresh fails, we need to skip input here, nothing we can do. // We'll try to pick up the proper state on the next WM_POINTER message. if (!tablets.IsValid) { return(false); } } _currentTabletDevice = tablets?.GetByDeviceId(deviceId); _currentStylusDevice = _currentTabletDevice?.GetStylusByCursorId(cursorId); // Something went wrong when querying the tablet or stylus, attempt a refresh if (_currentTabletDevice == null || _currentStylusDevice == null) { tablets.Refresh(); _currentTabletDevice = tablets?.GetByDeviceId(deviceId); _currentStylusDevice = _currentTabletDevice?.GetStylusByCursorId(cursorId); // Still can't get the proper devices, just wait for the next message if (_currentTabletDevice == null || _currentStylusDevice == null) { return(false); } } return(true); }
/// <summary> /// Creates raw stylus data from the raw WM_POINTER properties /// </summary> /// <param name="pointerData">The current pointer info</param> /// <param name="tabletDevice">The current TabletDevice</param> /// <returns>An array of raw pointer data</returns> private int[] GenerateRawStylusData(PointerData pointerData, PointerTabletDevice tabletDevice) { // Since we are copying raw pointer data, we want to use every property supported by this pointer. // We may never access some of the unknown (unsupported by WPF) properties, but they should be there // for consumption by the developer. int pointerPropertyCount = tabletDevice.DeviceInfo.SupportedPointerProperties.Length; // The data is as wide as the pointer properties and is per history point int[] rawPointerData = new int[pointerPropertyCount * pointerData.Info.historyCount]; int[] data = new int[0]; // Get the raw data formatted to our supported properties if (UnsafeNativeMethods.GetRawPointerDeviceData( pointerData.Info.pointerId, pointerData.Info.historyCount, (uint)pointerPropertyCount, tabletDevice.DeviceInfo.SupportedPointerProperties, rawPointerData)) { // Get the X and Y offsets to translate device coords to the origin of the hwnd int originOffsetX, originOffsetY; GetOriginOffsetsLogical(out originOffsetX, out originOffsetY); int numButtons = tabletDevice.DeviceInfo.SupportedPointerProperties.Length - tabletDevice.DeviceInfo.SupportedButtonPropertyIndex; int rawDataPointSize = (numButtons > 0) ? pointerPropertyCount - numButtons + 1 : pointerPropertyCount; // Instead of a single entry for each button we use one entry for all buttons so reflect that in the raw data size data = new int[rawDataPointSize * pointerData.Info.historyCount]; // Skip to the beginning of each stylus point in both the target WPF array and the pointer data array. // The pointer data is arranged from last point to first point in the history while WPF data is arranged // the reverse of this (in whole stylus points). Therefore we need to fill backward from pointer data // via stylus point strides. for (int i = 0, j = rawPointerData.Length - pointerPropertyCount; i < data.Length; i += rawDataPointSize, j -= pointerPropertyCount) { Array.Copy(rawPointerData, j, data, i, rawDataPointSize); // Apply offsets from the origin to raw pointer data here data[i + StylusPointDescription.RequiredXIndex] -= originOffsetX; data[i + StylusPointDescription.RequiredYIndex] -= originOffsetY; if (numButtons > 0) { int buttonIndex = i + rawDataPointSize - 1; // The last data point probably has garbage in it, so clear it to store button info data[buttonIndex] = 0; // Condense any leftover button properties into a single entry for (int k = tabletDevice.DeviceInfo.SupportedButtonPropertyIndex; k < pointerPropertyCount; k++) { int mask = rawPointerData[j + k] << (k - tabletDevice.DeviceInfo.SupportedButtonPropertyIndex); data[buttonIndex] |= mask; } } } } return(data); }