diff --git a/xalia/Interop/Win32.cs b/xalia/Interop/Win32.cs index 0693fe3..cebcd38 100644 --- a/xalia/Interop/Win32.cs +++ b/xalia/Interop/Win32.cs @@ -1,5 +1,4 @@ #if WINDOWS -using Interop.UIAutomationClient; using Microsoft.Win32.SafeHandles; using System; using System.Collections.Generic; @@ -350,7 +349,7 @@ public static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr public const int NAVDIR_FIRSTCHILD = 7; public const int NAVDIR_LASTCHILD = 8; - public static bool AccessibleNavigate(ref Accessibility.IAccessible acc, ref int child_id, int navdir) + public static bool AccessibleNavigate(ref IAccessible acc, ref int child_id, int navdir) { var result = acc.accNavigate(navdir, child_id); if (result is null) @@ -358,7 +357,7 @@ public static bool AccessibleNavigate(ref Accessibility.IAccessible acc, ref int if (result is int i) child_id = i; else - acc = (Accessibility.IAccessible)result; + acc = (IAccessible)result; return true; } @@ -369,7 +368,7 @@ public static extern int AccessibleObjectFromEvent(IntPtr hwnd, int dwId, int dw [DllImport(OLEACC_LIB, CallingConvention = CallingConvention.Winapi)] public static extern int ObjectFromLresult(IntPtr lResult, - [MarshalAs(UnmanagedType.LPStruct)] Guid riid, IntPtr wParam, out Accessibility.IAccessible ppvObject); + [MarshalAs(UnmanagedType.LPStruct)] Guid riid, IntPtr wParam, out IAccessible ppvObject); [ComImport, Guid("6d5140c1-7436-11ce-8034-00aa006009fa")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] @@ -397,6 +396,68 @@ public struct IA2Locale [MarshalAs(UnmanagedType.BStr)] public string variant; } + [ComImport, Guid("618736e0-3c3d-11cf-810c-00aa00389b71")] + [InterfaceType(ComInterfaceType.InterfaceIsDual)] + public interface IAccessible + { + // IAccessible methods + [return: MarshalAs(UnmanagedType.IDispatch)] + object get_accParent(); + + int get_accChildCount(); + + [return: MarshalAs(UnmanagedType.IDispatch)] + object get_accChild([MarshalAs(UnmanagedType.Struct)] object varChildId); + + [return: MarshalAs(UnmanagedType.BStr)] + string get_accName([MarshalAs(UnmanagedType.Struct)] object varID); + + [return: MarshalAs(UnmanagedType.BStr)] + string get_accValue([MarshalAs(UnmanagedType.Struct)] object varID); + + [return: MarshalAs(UnmanagedType.BStr)] + string get_accDescription([MarshalAs(UnmanagedType.Struct)] object varID); + + [return: MarshalAs(UnmanagedType.Struct)] + object get_accRole([MarshalAs(UnmanagedType.Struct)] object varID); + + [return: MarshalAs(UnmanagedType.Struct)] + object get_accState([MarshalAs(UnmanagedType.Struct)] object varID); + + [return: MarshalAs(UnmanagedType.BStr)] + string get_accHelp([MarshalAs(UnmanagedType.Struct)] object varID); + + long get_accHelpTopic([MarshalAs(UnmanagedType.BStr)] out string helpfile, [MarshalAs(UnmanagedType.Struct)] object varID); + + [return: MarshalAs(UnmanagedType.BStr)] + string get_accKeyboardShortcut([MarshalAs(UnmanagedType.Struct)] object varID); + + [return: MarshalAs(UnmanagedType.Struct)] + object get_accFocus(); + + [return: MarshalAs(UnmanagedType.Struct)] + object get_accSelection(); + + [return: MarshalAs(UnmanagedType.BStr)] + string get_accDefaultAction([MarshalAs(UnmanagedType.Struct)] object varID); + + void accSelect(long flagsSelect, [MarshalAs(UnmanagedType.Struct)] object varID); + + void accLocation(out int left, out int top, out int width, out int height, [MarshalAs(UnmanagedType.Struct)] object varID); + + [return: MarshalAs(UnmanagedType.Struct)] + object accNavigate(long dir, [MarshalAs(UnmanagedType.Struct)] object varStart); + + [return: MarshalAs(UnmanagedType.Struct)] + object accHitTest(long left, long top); + + void accDoDefaultAction([MarshalAs(UnmanagedType.Struct)] object varID); + + void set_accName([MarshalAs(UnmanagedType.Struct)] object varID, [MarshalAs(UnmanagedType.BStr)] string name); + + void set_accValue([MarshalAs(UnmanagedType.Struct)] object varID, [MarshalAs(UnmanagedType.BStr)] string value); + } + [ComImport, Guid("e89f726e-c4f4-4c19-bb19-b647d7fa8478")] [InterfaceType(ComInterfaceType.InterfaceIsDual)] public interface IAccessible2 diff --git a/xalia/MainClass.cs b/xalia/MainClass.cs index c2db6e8..c61fa63 100644 --- a/xalia/MainClass.cs +++ b/xalia/MainClass.cs @@ -11,9 +11,6 @@ using Xalia.Gudl; using Xalia.Ui; using Xalia.UiDom; -#if WINDOWS -using Xalia.Uia; -#endif using Xalia.Sdl; using static SDL2.SDL; @@ -58,23 +55,11 @@ static async Task Init(GudlStatement[] config) #if WINDOWS if (connection == null && - ((Environment.GetEnvironmentVariable("XALIA_EXPERIMENTAL_WIN32") ?? "0") != "0")) + ((Environment.GetEnvironmentVariable("XALIA_USE_WIN32") ?? (Utils.IsWindows() ? "1" : "0")) != "0")) { connection = new UiDomRoot(config, application); new Win32Connection(connection); } - - if (connection == null && - (Environment.GetEnvironmentVariable("XALIA_USE_UIA2") ?? "0") != "0") - { - connection = new UiaConnection(false, config, application); - } - - if (connection == null && - (Environment.GetEnvironmentVariable("XALIA_USE_UIA3") ?? (Utils.IsWindows() ? "1" : "0")) != "0") - { - connection = new UiaConnection(true, config, application); - } #endif if (connection == null) diff --git a/xalia/Uia/UiaAdjustScrollContainer.cs b/xalia/Uia/UiaAdjustScrollContainer.cs deleted file mode 100644 index 0f62e8f..0000000 --- a/xalia/Uia/UiaAdjustScrollContainer.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading.Tasks; -using Xalia.Input; -using Xalia.UiDom; - -namespace Xalia.Uia -{ - internal class UiaAdjustScrollContainer : UiDomRoutine - { - public UiaAdjustScrollContainer(UiaElement element) : base(element, "uia_adjust_scroll_container") - { - Element = element; - } - - private static readonly double xscale = 100.0 / 6 / 32767; - private static readonly double yscale = 100.0 / 6 / 32767; - private static readonly long delay_ticks = Stopwatch.Frequency / 120; - - public new UiaElement Element { get; } - - public override async Task ProcessInputQueue(InputQueue queue) - { - var stopwatch = new Stopwatch(); - InputState prev_state = new InputState(InputStateKind.Disconnected), state; - long last_repeat = 0; - double xremainder=0, yremainder=0; - while (true) - { - state = await queue.Dequeue(); - if (state.Kind == InputStateKind.Disconnected) - break; - if ((state.Kind == InputStateKind.Pulse || state.Kind == InputStateKind.Repeat) && - prev_state.Kind == InputStateKind.AnalogJoystick) - { - (xremainder, yremainder) = await DoAdjustment(prev_state, xscale, yscale, xremainder, yremainder); - if (stopwatch.IsRunning) - stopwatch.Reset(); - } - else if (state.Kind == InputStateKind.AnalogJoystick && state.Intensity >= 1000) - { - if (!stopwatch.IsRunning) - { - stopwatch.Start(); - last_repeat = 0; - (xremainder, yremainder) = await DoAdjustment(prev_state, xscale, yscale, xremainder, yremainder); - } - while (queue.IsEmpty) - { - var elapsed_ticks = stopwatch.ElapsedTicks - last_repeat; - if (elapsed_ticks < delay_ticks) - { - await Task.WhenAny(queue.WaitForInput(), Task.Delay(TimeSpan.FromSeconds((delay_ticks - elapsed_ticks) / (double)Stopwatch.Frequency))); - continue; - } - long num_steps = elapsed_ticks / delay_ticks; - - (xremainder, yremainder) = await DoAdjustment(prev_state, - Math.Min(num_steps, 60) * xscale, Math.Min(num_steps, 60) * yscale, - xremainder, yremainder); - last_repeat += delay_ticks * num_steps; - } - } - else if (state.Kind == InputStateKind.PixelDelta && state.Intensity > 0) - { - (xremainder, yremainder) = await DoAdjustment(state, 1, 1, xremainder, yremainder); - } - else - { - stopwatch.Reset(); - } - prev_state = state; - } - } - - private async Task<(double xremainder, double yremainder)> DoAdjustment(InputState state, double xmult, double ymult, double xremainder, double yremainder) - { - double xofs = state.XAxis * xmult + xremainder; - double yofs = state.YAxis * ymult + yremainder; - - double xadjustment = Math.Truncate(xofs); - double yadjustment = Math.Truncate(yofs); - - if (xadjustment == 0 && yadjustment == 0) - { - return (xofs, yofs); - } - - return await Element.Root.CommandThread.OnBackgroundThread(() => { - try - { - var scroll = Element.ElementWrapper.AutomationElement.Patterns.Scroll.Pattern; - - double xpercent = scroll.HorizontalScrollPercent; - if (xpercent != -1) // UIA_ScrollPatternNoScroll - { - xpercent += xadjustment / scroll.HorizontalViewSize * 100; - xpercent = Math.Min(Math.Max(xpercent, 0), 100); - } - - double ypercent = scroll.VerticalScrollPercent; - if (ypercent != -1) // UIA_ScrollPatternNoScroll - { - ypercent += yadjustment / scroll.VerticalViewSize * 100; - ypercent = Math.Min(Math.Max(ypercent, 0), 100); - } - - scroll.SetScrollPercent(xpercent, ypercent); - - return ( - xpercent == -1 ? 0 : xremainder, - ypercent == -1 ? 0 : yremainder); - } - catch (Exception e) - { - if (!UiaElement.IsExpectedException(e)) - throw; - return (0, 0); - } - }, Element.ElementWrapper.Pid+1); - } - } -} diff --git a/xalia/Uia/UiaCommandThread.cs b/xalia/Uia/UiaCommandThread.cs deleted file mode 100644 index 10f472f..0000000 --- a/xalia/Uia/UiaCommandThread.cs +++ /dev/null @@ -1,254 +0,0 @@ -using FlaUI.Core.AutomationElements; -using FlaUI.Core.Identifiers; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace Xalia.Uia -{ - public class UiaCommandThread - { - struct CommandThreadRequest - { - public Action Action; - } - - class CommandQueue - { - public int pid; - public ConcurrentQueue requests = new ConcurrentQueue(); - public long locked; - } - - Dictionary pid_queues = new Dictionary(); - - ConcurrentQueue overall_queue = new ConcurrentQueue(); - - AutoResetEvent tasks_available; - - long threads_waiting = 0; - - internal UiaCommandThread() - { - tasks_available = new AutoResetEvent(false); - } - - private void CreateNewThread(CommandQueue queue) - { - var thread = new Thread(ThreadProc); - thread.SetApartmentState(ApartmentState.MTA); - thread.Start(queue); - } - - private void QueueRequest(CommandThreadRequest request, int pid) - { - if (!pid_queues.TryGetValue(pid, out var queue)) - { - queue = new CommandQueue(); - queue.pid = pid; - pid_queues[pid] = queue; - } - - queue.requests.Enqueue(request); - - if (Interlocked.Read(ref queue.locked) == 0) - { - bool lock_taken = false; - if (Interlocked.Read(ref threads_waiting) == 0) - { - lock_taken = Interlocked.CompareExchange(ref queue.locked, 1, 0) == 0; - if (lock_taken) - CreateNewThread(queue); - } - if (!lock_taken) - { - overall_queue.Enqueue(queue); - tasks_available.Set(); - } - } - } - - public Task OnBackgroundThread(Func func, UiaElementWrapper element) - { - return OnBackgroundThread(func, element.Pid); - } - - public async Task OnBackgroundThread(Func func, int pid = 0) - { - var request = new CommandThreadRequest(); - var completion_source = new TaskCompletionSource(); - request.Action = () => - { - try - { - var result = func(); - completion_source.SetResult(result); - } - catch (Exception e) - { - completion_source.SetException(e); - } - }; - - QueueRequest(request, pid); - - return await completion_source.Task; - } - - public Task OnBackgroundThread(Action func, UiaElementWrapper element) - { - return OnBackgroundThread(func, element.Pid); - } - - public async Task OnBackgroundThread(Action func, int pid = 0) - { - var request = new CommandThreadRequest(); - var completion_source = new TaskCompletionSource(); - request.Action = () => - { - try - { - func(); - completion_source.SetResult(true); - } - catch (Exception e) - { - completion_source.SetException(e); - } - }; - - QueueRequest(request, pid); - - await completion_source.Task; - } - - public async Task GetPropertyValue(UiaElementWrapper element, PropertyId propid) - { - return await OnBackgroundThread(() => - { - try - { - return element.AutomationElement.FrameworkAutomationElement.GetPropertyValue(propid); - } - catch (Exception e) - { - if (UiaElement.IsExpectedException(e)) - return null; - throw; - } - }, element); - } - - public async Task GetChildren(UiaElementWrapper element) - { - return await OnBackgroundThread(() => - { - AutomationElement[] elements; - try - { - elements = element.AutomationElement.FindAllChildren(); - } - catch (Exception e) - { - if (UiaElement.IsExpectedException(e)) - return new UiaElementWrapper[0]; - throw; - } - - var result = new UiaElementWrapper[elements.Length]; - bool assume_unique = !element.Connection.HasNonIdChildren(element); - - for (var i = 0; i < elements.Length; i++) - { - result[i] = element.Connection.WrapElement(elements[i], element.UniqueId, assume_unique); - if (!result[i].IsValid) - return new UiaElementWrapper[0]; - } - - return result; - }, element); - } - - public async Task GetSupportedPatterns(UiaElementWrapper element) - { - return await OnBackgroundThread(() => - { - return element.AutomationElement.GetSupportedPatterns(); - }, element); - } - - private void ThreadProc(object initial_info) - { - var queue = (CommandQueue)initial_info; - // We start with the lock held - bool lock_held = true; - while (TryProcessQueue(queue, lock_held)) - { - // All work is done in TryProcessQueue - lock_held = false; - } - try - { - while (true) - { - while (overall_queue.TryDequeue(out queue)) - { - while (TryProcessQueue(queue, false)) - { - // All work is done in TryProcessQueue - } - } - Interlocked.Increment(ref threads_waiting); - tasks_available.WaitOne(); - Interlocked.Decrement(ref threads_waiting); - } - } - catch (Exception e) - { - Utils.OnError(e); - } - } - - private bool TryProcessQueue(CommandQueue queue, bool lock_held) - { - // It's important that we check this while the lock is not held. - // See the comment at the end for why this is necessary. - if (!lock_held && queue.requests.IsEmpty) - // Nothing to do. - return false; - - bool lock_taken = false; - - try - { - if (lock_held) - lock_taken = true; - else - lock_taken = Interlocked.CompareExchange(ref queue.locked, 1, 0) == 0; - - if (!lock_taken) - return false; - - while (queue.requests.TryDequeue(out var request)) - { - request.Action(); - } - } - finally - { - if (lock_taken) - Interlocked.Exchange(ref queue.locked, 0); - } - - /* It's possible that the main thread added a request to the queue after - * we called TryDequeue but before we unset locked. If this happened, - * the queue is no longer empty, but no other thread will be woken up to - * serve the request, and there is no new entry in requests. We - * must recheck the queue while the lock is not held to account for this case.*/ - return true; - } - } -} - diff --git a/xalia/Uia/UiaConnection.cs b/xalia/Uia/UiaConnection.cs deleted file mode 100644 index 9a6a2d1..0000000 --- a/xalia/Uia/UiaConnection.cs +++ /dev/null @@ -1,1228 +0,0 @@ -using FlaUI.Core; -using FlaUI.Core.AutomationElements; -using FlaUI.Core.Definitions; -using FlaUI.Core.Exceptions; -using FlaUI.Core.Identifiers; -using FlaUI.UIA3; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Xalia.Gudl; -using Xalia.UiDom; -using static Xalia.Interop.Win32; -using AccessibilityRole = FlaUI.Core.WindowsAPI.AccessibilityRole; -using IAccessible = Accessibility.IAccessible; -using IUIAutomationLegacyIAccessiblePattern = Interop.UIAutomationClient.IUIAutomationLegacyIAccessiblePattern; - -namespace Xalia.Uia -{ - public class UiaConnection : UiDomRoot - { - static int MonotonicElementId; // For cases where we can't get any unique id for an element - - static List event_proc_delegates = new List(); // to make sure delegates aren't GC'd while in use - - internal Dictionary names_to_property = new Dictionary(); - internal Dictionary properties_to_name = new Dictionary(); - internal Dictionary> msaa_event_properties = new Dictionary>(); - private string[] property_poll_names; - - bool polling_focus; - CancellationTokenSource focus_poll_token; - - // Dictionary mapping parent,role to a set of known child ids and elements - ConcurrentDictionary>> no_id_elements = - new ConcurrentDictionary>>(); - - private static string[] tracked_properties = { "poll_focus" }; - - public UiaConnection(bool use_uia3, GudlStatement[] rules, IUiDomApplication app) : base(rules, app) - { - MainContext = SynchronizationContext.Current; - CommandThread = new UiaCommandThread(); - Utils.RunTask(InitUia(use_uia3)); - RegisterTrackedProperties(tracked_properties); - } - - private async Task InitUia(bool use_uia3) - { - await CommandThread.OnBackgroundThread(() => - { - if (use_uia3) - Automation = new FlaUI.UIA3.UIA3Automation(); - else - Automation = new FlaUI.UIA2.UIA2Automation(); - }); - - // If a top-level window does not support WindowPattern, we don't get - // any notification from UIAutomation when it's created. - var eventprocdelegate = new WINEVENTPROC(OnMsaaEvent); - - event_proc_delegates.Add(eventprocdelegate); - - SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_OBJECT_NAMECHANGE, IntPtr.Zero, - eventprocdelegate, 0, 0, WINEVENT_OUTOFCONTEXT); - - RegisterPropertyMapping("uia_control_type", Automation.PropertyLibrary.Element.ControlType, EVENT_OBJECT_STATECHANGE); - RegisterPropertyMapping("uia_automation_id", Automation.PropertyLibrary.Element.AutomationId); - RegisterPropertyMapping("uia_class_name", Automation.PropertyLibrary.Element.ClassName); - RegisterPropertyMapping("uia_enabled", Automation.PropertyLibrary.Element.IsEnabled, EVENT_OBJECT_STATECHANGE); - RegisterPropertyMapping("uia_keyboard_focusable", Automation.PropertyLibrary.Element.IsKeyboardFocusable, EVENT_OBJECT_STATECHANGE); - RegisterPropertyMapping("uia_has_keyboard_focus", Automation.PropertyLibrary.Element.HasKeyboardFocus, EVENT_OBJECT_STATECHANGE); - RegisterPropertyMapping("uia_bounding_rectangle", Automation.PropertyLibrary.Element.BoundingRectangle, EVENT_OBJECT_LOCATIONCHANGE); - RegisterPropertyMapping("uia_name", Automation.PropertyLibrary.Element.Name, EVENT_OBJECT_NAMECHANGE); - RegisterPropertyMapping("uia_offscreen", Automation.PropertyLibrary.Element.IsOffscreen, EVENT_OBJECT_STATECHANGE); - RegisterPropertyMapping("uia_selected", Automation.PropertyLibrary.SelectionItem.IsSelected, EVENT_OBJECT_STATECHANGE); - RegisterPropertyMapping("uia_expand_collapse_state", Automation.PropertyLibrary.ExpandCollapse.ExpandCollapseState, EVENT_OBJECT_STATECHANGE); - RegisterPropertyMapping("uia_orientation", Automation.PropertyLibrary.Element.Orientation); - RegisterPropertyMapping("uia_framework_id", Automation.PropertyLibrary.Element.FrameworkId); - RegisterPropertyMapping("msaa_role", Automation.PropertyLibrary.LegacyIAccessible.Role); - RegisterPropertyMapping("aria_role", Automation.PropertyLibrary.Element.AriaRole); - - await CommandThread.OnBackgroundThread(() => - { - var DesktopElement = Automation.GetDesktop(); - - Automation.RegisterFocusChangedEvent(OnFocusChangedBackground); - - DesktopElement.RegisterStructureChangedEvent( - TreeScope.Element | TreeScope.Descendants, OnStructureChangedBackground); - - DesktopElement.RegisterPropertyChangedEvent( - TreeScope.Element | TreeScope.Descendants, OnPropertyChangedBackground, - properties_to_name.Keys.ToArray()); - - DesktopElement.RegisterAutomationEvent( - Automation.EventLibrary.Window.WindowOpenedEvent, TreeScope.Element | TreeScope.Descendants, - OnWindowOpenedBackground); - - DesktopElement.RegisterAutomationEvent( - Automation.EventLibrary.Window.WindowClosedEvent, TreeScope.Element | TreeScope.Descendants, - OnWindowClosedBackground); - - DesktopElement.RegisterAutomationEvent( - Automation.EventLibrary.Element.MenuModeStartEvent, TreeScope.Element | TreeScope.Descendants, - OnMenuModeStartBackground); - - DesktopElement.RegisterAutomationEvent( - Automation.EventLibrary.Element.MenuModeEndEvent, TreeScope.Element | TreeScope.Descendants, - OnMenuModeEndBackground); - - DesktopElement.RegisterAutomationEvent( - Automation.EventLibrary.Element.MenuOpenedEvent, TreeScope.Element | TreeScope.Descendants, - OnMenuOpenedBackground); - - DesktopElement.RegisterAutomationEvent( - Automation.EventLibrary.Element.MenuClosedEvent, TreeScope.Element | TreeScope.Descendants, - OnMenuClosedBackground); - - DesktopElement.RegisterAutomationEvent( - Automation.EventLibrary.SelectionItem.ElementSelectedEvent, TreeScope.Element | TreeScope.Descendants, - OnSelectedBackground); - - DesktopElement.RegisterAutomationEvent( - Automation.EventLibrary.SelectionItem.ElementAddedToSelectionEvent, TreeScope.Element | TreeScope.Descendants, - OnAddedToSelectionBackground); - - DesktopElement.RegisterAutomationEvent( - Automation.EventLibrary.SelectionItem.ElementRemovedFromSelectionEvent, TreeScope.Element | TreeScope.Descendants, - OnRemovedFromSelectionBackground); - }); - - Utils.RunTask(UpdateFocusedElement()); - - UpdateChildren(); // in case children changed before the events were registered - - Utils.RunTask(UpdateActiveWindow()); - } - - private void OnSelected(object s) - { - var wrapper = (UiaElementWrapper)s; - - var element = LookupAutomationElement(wrapper); - - if (element != null) - { - foreach (var sibling in element.Parent.Children) - { - ((UiaElement)sibling).OnPropertyChange("uia_selected", Automation.PropertyLibrary.SelectionItem.IsSelected, - sibling == element); - } - } - } - - private void OnSelectedBackground(AutomationElement arg1, EventId arg2) - { - var wrapper = WrapElement(arg1); - MainContext.Post(OnSelected, wrapper); - } - - private void OnAddedToSelection(object s) - { - var wrapper = (UiaElementWrapper)s; - - var element = LookupAutomationElement(wrapper); - - if (element != null) - { - element.OnPropertyChange("uia_selected", Automation.PropertyLibrary.SelectionItem.IsSelected, true); - } - } - - private void OnAddedToSelectionBackground(AutomationElement arg1, EventId arg2) - { - var wrapper = WrapElement(arg1); - MainContext.Post(OnAddedToSelection, wrapper); - } - - private void OnRemovedFromSelection(object s) - { - var wrapper = (UiaElementWrapper)s; - - var element = LookupAutomationElement(wrapper); - - if (element != null) - { - element.OnPropertyChange("uia_selected", Automation.PropertyLibrary.SelectionItem.IsSelected, false); - } - } - - private void OnRemovedFromSelectionBackground(AutomationElement arg1, EventId arg2) - { - var wrapper = WrapElement(arg1); - MainContext.Post(OnRemovedFromSelection, wrapper); - } - - internal List menu_elements = new List(); - - public UiaElementWrapper UiaOpenedMenu - { - get - { - return menu_elements.LastOrDefault(); - } - } - - public bool UiaInMenu - { - get - { - return menu_elements.Count >= 1; - } - } - - public bool UiaInSubmenu - { - get - { - return menu_elements.Count >= 2; - } - } - - private void OnMenuClosed(object state) - { - // A closing menu may no longer have an HWND or RuntimeId. - // Just assume it was the last menu to be opened. - if (menu_elements.Count == 0) - return; - - menu_elements.RemoveAt(menu_elements.Count - 1); - - PropertyChanged("uia_opened_menu"); - - if (menu_elements.Count == 0) - PropertyChanged("uia_in_menu"); - else if (menu_elements.Count == 1) - PropertyChanged("uia_in_submenu"); - } - - private void OnMenuClosedBackground(AutomationElement arg1, EventId arg2) - { - MainContext.Post(OnMenuClosed, null); - } - - private void OnMenuOpened(object state) - { - var wrapper = (UiaElementWrapper)state; - - menu_elements.Add(wrapper); - - PropertyChanged("uia_opened_menu"); - - if (menu_elements.Count == 1) - PropertyChanged("uia_in_menu"); - else if (menu_elements.Count == 2) - PropertyChanged("uia_in_submenu"); - } - - private void OnMenuOpenedBackground(AutomationElement arg1, EventId arg2) - { - var wrapper = WrapElement(arg1); - - MainContext.Post(OnMenuOpened, wrapper); - } - - public bool UiaMenuMode { get; private set; } = false; - - private void OnMenuModeEndBackground(AutomationElement arg1, EventId arg2) - { - MainContext.Post((object s) => - { - UiaMenuMode = false; - PropertyChanged("uia_menu_mode"); - }, null); - } - - private void OnMenuModeStartBackground(AutomationElement arg1, EventId arg2) - { - MainContext.Post((object s) => - { - UiaMenuMode = true; - PropertyChanged("uia_menu_mode"); - }, null); - } - - HashSet visible_toplevel_hwnds = new HashSet(); - - Dictionary toplevels_by_hwnd = new Dictionary(); - - internal bool IsUiaToplevel(IntPtr hwnd) - { - if (!WindowIsVisible(hwnd)) - return false; - - if (GetAncestor(hwnd, GA_PARENT) != GetDesktopWindow()) - return false; - - var window_class = RealGetWindowClass(hwnd); - - if (window_class == "#32770") - { - // Owned dialog windows show up as children of the owner in the UIA tree - var owner_window = GetWindow(hwnd, GW_OWNER); - - if (owner_window != IntPtr.Zero && WindowIsVisible(owner_window)) - return false; - } - - return true; - } - - internal void UpdateChildren() - { - var missing_hwnds = new HashSet(visible_toplevel_hwnds); - - foreach (var hwnd in EnumWindows()) - { - if (!IsUiaToplevel(hwnd)) - continue; - - if (missing_hwnds.Contains(hwnd)) - { - missing_hwnds.Remove(hwnd); - } - else - { - visible_toplevel_hwnds.Add(hwnd); - Utils.RunTask(AddToplevelHwnd(hwnd)); - } - } - - foreach (var hwnd in missing_hwnds) - { - visible_toplevel_hwnds.Remove(hwnd); - if (toplevels_by_hwnd.TryGetValue(hwnd, out var element)) - { - toplevels_by_hwnd.Remove(hwnd); - RemoveChild(Children.IndexOf(element)); - } - } - } - - internal Task WrapperFromHwnd(IntPtr hwnd) - { - if (elements_by_id.TryGetValue($"hwnd-{hwnd}", out var cached)) - return Task.FromResult(cached.ElementWrapper); - - if (hwnd == IntPtr.Zero || hwnd == GetDesktopWindow() || !WindowIsVisible(hwnd)) - return Task.FromResult(UiaElementWrapper.InvalidElement); - - GetWindowThreadProcessId(hwnd, out int pid); - - return CommandThread.OnBackgroundThread(() => - { - try - { - var element = Automation.FromHandle(hwnd); - - return WrapElement(element); - } - catch (COMException) - { - return UiaElementWrapper.InvalidElement; - } - catch (TimeoutException) - { - return UiaElementWrapper.InvalidElement; - } - catch (ElementNotAvailableException) - { - return UiaElementWrapper.InvalidElement; - } - }, pid); - } - internal Task WrapperFromHwnd(IntPtr hwnd, int objectId, int childId) - { - if (objectId == OBJID_WINDOW || objectId == OBJID_CLIENT) - { - if (childId == CHILDID_SELF) - return WrapperFromHwnd(hwnd); - if (elements_by_id.TryGetValue($"hwnd-{hwnd}-{childId}", out var cached)) - return Task.FromResult(cached.ElementWrapper); - } - - if (!(Automation is FlaUI.UIA3.UIA3Automation)) - { - // Hopefully we don't need to handle UIA2 because it translates the events for us. - return Task.FromResult(UiaElementWrapper.InvalidElement); - } - - if (hwnd == IntPtr.Zero || hwnd == GetDesktopWindow() || !WindowIsVisible(hwnd)) - return Task.FromResult(UiaElementWrapper.InvalidElement); - - GetWindowThreadProcessId(hwnd, out int pid); - - return CommandThread.OnBackgroundThread(() => - { - try - { - int hr = AccessibleObjectFromEvent(hwnd, objectId, childId, out var acc, out var res_childid); - - Marshal.ThrowExceptionForHR(hr); - - var au3 = (FlaUI.UIA3.UIA3Automation)Automation; - - var native = au3.NativeAutomation.ElementFromIAccessible(acc, (int)res_childid); - - var element = au3.WrapNativeElement(native); - - return WrapElement(element); - } - catch (COMException) - { - return UiaElementWrapper.InvalidElement; - } - catch (ArgumentException) - { - return UiaElementWrapper.InvalidElement; - } - catch (ElementNotAvailableException) - { - return UiaElementWrapper.InvalidElement; - } - }, pid); - } - - internal async Task AddToplevelHwnd(IntPtr hwnd) - { - UiaElementWrapper wrapper = await WrapperFromHwnd(hwnd); - - if (!visible_toplevel_hwnds.Contains(hwnd) || toplevels_by_hwnd.ContainsKey(hwnd) || - !wrapper.IsValid) - return; - - var element = new UiaElement(wrapper); - - AddChild(Children.Count, element); - toplevels_by_hwnd[hwnd] = element; - } - - private void OnPropertyChangedBackground(AutomationElement arg1, PropertyId arg2, object arg3) - { - var wrapper = WrapElement(arg1); - if (arg3 is null) - { - try - { - arg1.FrameworkAutomationElement.TryGetPropertyValue(arg2, out arg3); - } - catch (COMException) { } - catch (ElementNotAvailableException) { } - } - MainContext.Post((state) => - { - var element = LookupAutomationElement(wrapper); - if (!(element is null) && properties_to_name.TryGetValue(arg2, out var name)) - element.OnPropertyChange(name, arg2, arg3); - }, null); - } - - private void RegisterPropertyMapping(string name, PropertyId propid, uint msaa_change_event=0) - { - names_to_property[name] = propid; - properties_to_name[propid] = name; - if (msaa_change_event != 0) - { - if (!msaa_event_properties.TryGetValue(msaa_change_event, out var propids)) - { - propids = new List(); - msaa_event_properties[msaa_change_event] = propids; - } - propids.Add(propid); - } - } - - internal string[] PropertyPollNames - { - get - { - if (property_poll_names is null) - { - List result = new List(); - foreach (var propname in names_to_property.Keys) - { - result.Add("poll_" + propname); - } - property_poll_names = result.ToArray(); - } - return property_poll_names; - } - } - - private void OnStructureChangedBackground(AutomationElement arg1, StructureChangeType arg2, int[] arg3) - { - UiaElementWrapper wrapper; - try - { - if (arg2 == StructureChangeType.ChildAdded) - wrapper = WrapElement(arg1.Parent); - else - wrapper = WrapElement(arg1); - } - catch (COMException) - { - return; - } - MainContext.Post((state) => - { - var element = LookupAutomationElement(wrapper); - if (!(element is null)) - element.OnChildrenChanged(arg2, arg3); - }, null); - } - - private void OnFocusChangedBackground(AutomationElement obj) - { - var wrapper = WrapElement(obj); - MainContext.Post((state) => - { - OnFocusChanged(wrapper); - }, null); - } - - private async Task UpdateFocusedElement() - { - UiaElementWrapper wrapper; - try - { - wrapper = await CommandThread.OnBackgroundThread(() => - { - var result = Automation.FocusedElement(); - return WrapElement(result); - }); - } - catch (Exception e) - { - if (!UiaElement.IsExpectedException(e)) - throw; - wrapper = default; - } - - FocusedElement = wrapper; - } - - private async Task PollFocusedElement() - { - if (!polling_focus) - return; - - await UpdateFocusedElement(); - - focus_poll_token = new CancellationTokenSource(); - - try - { - await Task.Delay(200, focus_poll_token.Token); - } - catch (TaskCanceledException) - { - focus_poll_token = null; - return; - } - - focus_poll_token = null; - Utils.RunTask(PollFocusedElement()); - } - - protected override void TrackedPropertyChanged(string name, UiDomValue new_value) - { - if (name == "poll_focus" && new_value.ToBool() != polling_focus) - { - polling_focus = new_value.ToBool(); - if (polling_focus) - { - Utils.RunTask(PollFocusedElement()); - } - else if (!(focus_poll_token is null)) - { - focus_poll_token.Cancel(); - focus_poll_token = null; - } - } - base.TrackedPropertyChanged(name, new_value); - } - - private void OnWindowOpenedBackground(AutomationElement arg1, EventId arg2) - { - var wrapper = WrapElement(arg1.Parent); - - MainContext.Post((object state) => - { - OnWindowOpened(wrapper); - }, null); - } - - private void OnWindowClosedBackground(AutomationElement arg1, EventId arg2) - { - UiaElementWrapper wrapper; - try - { - wrapper = WrapElement(arg1.Parent); - } - catch (NullReferenceException) - { - // This can be thrown by AutomationElement.get_Parent - return; - } - - MainContext.Post((object state) => - { - OnWindowClosed(wrapper); - }, null); - } - - private void OnMsaaEvent(IntPtr hWinEventProc, uint eventId, IntPtr hwnd, int idObject, int idChild, int idEventThread, int dwmsEventTime) - { - switch (eventId) - { - case EVENT_SYSTEM_FOREGROUND: - case EVENT_OBJECT_CREATE: - case EVENT_OBJECT_DESTROY: - case EVENT_OBJECT_STATECHANGE: - case EVENT_OBJECT_LOCATIONCHANGE: - case EVENT_OBJECT_NAMECHANGE: - TranslateMsaaEvent(eventId, hwnd, idObject, idChild, idEventThread, dwmsEventTime); - break; - } - } - - private void TranslateMsaaEvent(uint eventId, IntPtr hwnd, int idObject, int idChild, int idEventThread, int dwmsEventTime) - { - Utils.RunTask(TranslateMsaaEventTask(eventId, hwnd, idObject, idChild, idEventThread, dwmsEventTime)); - } - - private async Task TranslateMsaaEventTask(uint eventId, IntPtr hwnd, int idObject, int idChild, int idEventThread, int dwmsEventTime) - { - if (eventId == EVENT_OBJECT_CREATE || eventId == EVENT_OBJECT_DESTROY) - { - // Can't retrieve the actual IAccessible for this event, get the parent instead. - - if (idChild != CHILDID_SELF) - idChild = CHILDID_SELF; - else if (idObject != OBJID_WINDOW && idObject != OBJID_CLIENT) - idObject = OBJID_WINDOW; - else - { - var parent = GetAncestor(hwnd, GA_PARENT); - if (parent == GetDesktopWindow()) - hwnd = GetWindow(hwnd, GW_OWNER); - else - hwnd = parent; - idObject = OBJID_CLIENT; - } - } - - var wrapper = await WrapperFromHwnd(hwnd, idObject, idChild); - - if (eventId == EVENT_SYSTEM_FOREGROUND) - { - // We may not know about the element yet, so just set the wrapper. - ForegroundElement = wrapper; - Utils.RunTask(UpdateActiveWindow()); - return; - } - - if (!wrapper.IsValid) - { - if ((eventId == EVENT_OBJECT_CREATE || eventId == EVENT_OBJECT_DESTROY) && - idChild == CHILDID_SELF && - (idObject == OBJID_WINDOW || idObject == OBJID_CLIENT)) - { - // Parent may be effectively the desktop window. - UpdateChildren(); - } - return; - } - - var element = LookupAutomationElement(wrapper); - - if (element is null) - return; - - if (msaa_event_properties.TryGetValue(eventId, out var properties)) - { - foreach (var propid in properties) - { - if (element.IsAlive) - await element.PropertyMaybeChanged(propid); - else - break; - } - } - - switch (eventId) - { - case EVENT_OBJECT_CREATE: - case EVENT_OBJECT_DESTROY: - element.UpdateChildren(); - break; - } - } - - private void OnFocusChanged(UiaElementWrapper obj) - { - FocusedElement = obj; - Utils.RunTask(UpdateActiveWindow()); - } - - private void OnWindowClosed(UiaElementWrapper parent) - { - var element = LookupAutomationElement(parent); - if (!(element is null)) - element.UpdateChildren(); - } - - private void OnWindowOpened(UiaElementWrapper parent) - { - var element = LookupAutomationElement(parent); - if (!(element is null)) - element.UpdateChildren(); - } - - public AutomationBase Automation { get; private set; } - public SynchronizationContext MainContext { get; } - - public UiaCommandThread CommandThread { get; } - - internal Dictionary elements_by_id = new Dictionary(); - - public UiaElement LookupAutomationElement(UiaElementWrapper ae) - { - if (!ae.IsValid) - return null; - elements_by_id.TryGetValue(ae.UniqueId, out var result); - return result; - } - - public UiaElementWrapper WrapElement(AutomationElement ae, string parent_id=null, bool assume_unique=false) - { - if (ae is null) - return UiaElementWrapper.InvalidElement; - try - { - return new UiaElementWrapper(ae, this, parent_id, assume_unique); - } - catch (Exception e) - { - if (UiaElement.IsExpectedException(e)) - return UiaElementWrapper.InvalidElement; - throw; - } - } - - UiaElementWrapper focused_element; - - public UiaElementWrapper FocusedElement - { - get { return focused_element; } - private set - { - var old_focused_element = focused_element; - - if (old_focused_element.Equals(value)) - { - return; - } - - if (MatchesDebugCondition()) - Utils.DebugWriteLine($"Focus changed to {value.UniqueId}"); - - focused_element = value; - - PropertyChanged("uia_focused_element"); - - if (LookupAutomationElement(old_focused_element) is UiaElement old) - { - old.PropertyChanged("uia_focused"); - } - - if (LookupAutomationElement(value) is UiaElement new_focused) - { - new_focused.PropertyChanged("uia_focused"); - } - } - } - - UiaElementWrapper foreground_element; - - public UiaElementWrapper ForegroundElement - { - get { return foreground_element; } - private set - { - var old_foreground_element = foreground_element; - - if (old_foreground_element.Equals(value)) - { - return; - } - - if (MatchesDebugCondition()) - Utils.DebugWriteLine($"Foreground window changed to {value.UniqueId}"); - - foreground_element = value; - - PropertyChanged("msaa_foreground_element"); - - if (LookupAutomationElement(old_foreground_element) is UiaElement old) - { - old.PropertyChanged("msaa_foreground"); - } - - if (LookupAutomationElement(value) is UiaElement new_focused) - { - new_focused.PropertyChanged("msaa_foreground"); - } - } - } - - UiaElementWrapper active_element; - - public UiaElementWrapper ActiveElement - { - get { return active_element; } - private set - { - var old_active_element = active_element; - - if (old_active_element.Equals(value)) - { - return; - } - - if (MatchesDebugCondition()) - Utils.DebugWriteLine($"Active window changed to {value.UniqueId}"); - - active_element = value; - - PropertyChanged("win32_active_element"); - - if (LookupAutomationElement(old_active_element) is UiaElement old) - { - old.PropertyChanged("win32_active"); - } - - if (LookupAutomationElement(value) is UiaElement new_active) - { - new_active.PropertyChanged("win32_active"); - } - } - } - - bool updating_active_window; - bool inflight_updating_active_window; - - internal async Task UpdateActiveWindow() - { - if (updating_active_window) - { - inflight_updating_active_window = true; - return; - } - - updating_active_window = true; - - GUITHREADINFO info = default; - info.cbSize = Marshal.SizeOf(); - GetGUIThreadInfo(0, ref info); - - try - { - ActiveElement = await WrapperFromHwnd(info.hwndActive); - } - catch (Exception e) - { - Utils.DebugWriteLine("Error getting element for active window:"); - Utils.DebugWriteLine(e); - } - - updating_active_window = false; - if (inflight_updating_active_window) - { - inflight_updating_active_window = false; - Utils.RunTask(UpdateActiveWindow()); - } - } - - protected override UiDomValue EvaluateIdentifierCore(string id, UiDomRoot root, [In, Out] HashSet<(UiDomElement, GudlExpression)> depends_on) - { - switch (id) - { - case "uia_focused_element": - case "focused_element": - depends_on.Add((Root, new IdentifierExpression("uia_focused_element"))); - return (UiDomValue)LookupAutomationElement(FocusedElement) ?? UiDomUndefined.Instance; - case "msaa_foreground_element": - case "foreground_element": - depends_on.Add((Root, new IdentifierExpression("msaa_foreground_element"))); - return (UiDomValue)LookupAutomationElement(ForegroundElement) ?? UiDomUndefined.Instance; - case "active_element": - case "win32_active_element": - depends_on.Add((Root, new IdentifierExpression("win32_active_element"))); - return (UiDomValue)LookupAutomationElement(ActiveElement) ?? UiDomUndefined.Instance; - case "menu_mode": - case "uia_menu_mode": - depends_on.Add((Root, new IdentifierExpression("uia_menu_mode"))); - return UiDomBoolean.FromBool(UiaMenuMode); - case "opened_menu": - case "uia_opened_menu": - depends_on.Add((Root, new IdentifierExpression("uia_opened_menu"))); - return (UiDomValue)LookupAutomationElement(UiaOpenedMenu) ?? UiDomUndefined.Instance; - case "in_menu": - case "uia_in_menu": - depends_on.Add((Root, new IdentifierExpression("uia_in_menu"))); - return UiDomBoolean.FromBool(UiaInMenu); - case "in_submenu": - case "uia_in_submenu": - depends_on.Add((Root, new IdentifierExpression("uia_in_submenu"))); - return UiDomBoolean.FromBool(UiaInSubmenu); - } - return base.EvaluateIdentifierCore(id, root, depends_on); - } - - protected override void DumpProperties() - { - var focused = LookupAutomationElement(focused_element); - if (!(focused is null)) - Utils.DebugWriteLine($" uia_focused_element: {focused}"); - var foreground = LookupAutomationElement(foreground_element); - if (!(foreground is null)) - Utils.DebugWriteLine($" msaa_foreground_element: {foreground}"); - var active = LookupAutomationElement(active_element); - if (!(active is null)) - Utils.DebugWriteLine($" win32_active_element: {active}"); - if (UiaMenuMode) - Utils.DebugWriteLine($" uia_menu_mode: true"); - var opened_menu = LookupAutomationElement(UiaOpenedMenu); - if (!(opened_menu is null)) - Utils.DebugWriteLine($" uia_opened_menu: {opened_menu}"); - if (UiaInMenu) - Utils.DebugWriteLine($" uia_in_menu: true"); - if (UiaInSubmenu) - Utils.DebugWriteLine($" uia_in_submenu: true"); - base.DumpProperties(); - } - - internal IAccessible GetIAccessibleBackground(AutomationElement element, out int child_id) - { - child_id = default; - - var fae = element.FrameworkAutomationElement as UIA3FrameworkAutomationElement; - if (fae is null) - return null; - - var nae = fae.NativeElement; - - try - { - var lia = nae.GetCurrentPattern(Automation.PatternLibrary.LegacyIAccessiblePattern.Id) as IUIAutomationLegacyIAccessiblePattern; - if (lia is null) - return null; - - child_id = lia.CurrentChildId; - - return (IAccessible)lia.GetIAccessible(); - } - catch (COMException e) - { - if (!UiaElement.IsExpectedException(e)) - Utils.OnError(e); - return null; - } - } - - internal bool HasNonIdChildren(UiaElementWrapper wrapper) - { - return no_id_elements.ContainsKey(wrapper.UniqueId); - } - - internal string BlockingGetElementId(AutomationElement element, out IntPtr hwnd, string parent_id=null, bool assume_unique=false) - { - // hwnd/childid pair - try - { - if (element.FrameworkAutomationElement.TryGetPropertyValue( - Automation.PropertyLibrary.Element.NativeWindowHandle, out hwnd)) - { - int childid = 0; - - try - { - if (element.FrameworkAutomationElement.TryGetPropertyValue( - Automation.PropertyLibrary.LegacyIAccessible.ChildId, out var cid)) - { - childid = cid; - } - } - catch (COMException) { } - - if (childid == 0) - { - return $"hwnd-{hwnd}"; - } - else - { - return $"hwnd-{hwnd}-{childid}"; - } - } - } - catch (COMException) - { - // Fall back on other methods - } - - hwnd = IntPtr.Zero; - - // UIAutomation runtimeid - try - { - if (element.FrameworkAutomationElement.TryGetPropertyValue( - Automation.PropertyLibrary.Element.RuntimeId, out var runtime_id)) - { - var sb = new StringBuilder(); - sb.Append("uia-runtime-id"); - foreach (var item in runtime_id) - sb.Append($"-{item}"); - return sb.ToString(); - } - } - catch (COMException) - { - // Fall back on other methods - } - - // TODO: If automationid is provided, use automationid + parent id - - var acc = GetIAccessibleBackground(element, out var child_id); - AutomationElement parent = null; - try - { - parent = element.Parent; - } - catch (Exception e) - { - if (!UiaElement.IsExpectedException(e)) - throw; - } - - if (!(acc is null) && !(parent is null)) - { - if (parent_id is null) - parent_id = BlockingGetElementId(parent, out var _unused); - - // Query for IAccessible2 directly, for old Qt versions - var acc2 = QueryIAccessible2(acc); - - if (!(acc2 is null)) - { - if (child_id != 0) - return $"{parent_id}-acc2-{acc2.uniqueID}-{child_id}"; - return $"{parent_id}-acc2-{acc2.uniqueID}"; - } - - // Use MSAA attributes and comparison to find elements - if (!no_id_elements.TryGetValue(parent_id, out var roles)) - { - // TryAdd may fail, in which case we use whatever was actually added - no_id_elements.TryAdd(parent_id, new ConcurrentDictionary>()); - roles = no_id_elements[parent_id]; - } - AccessibilityRole role = default; - try - { - role = element.Patterns.LegacyIAccessible.Pattern.Role.ValueOrDefault; - } - catch (Exception e2) - { - if (!UiaElement.IsExpectedException(e2)) - throw; - } - if (!roles.TryGetValue(role, out var values)) - { - // TryAdd may fail, in which case we use whatever was actually added - roles.TryAdd(role, new ConcurrentBag<(string, AutomationElement)>()); - values = roles[role]; - } - while (true) - { - // search for already-seen item - var values_list = values.ToList(); - if (!assume_unique && MsaaSiblingSearchList(element, values_list, out var item)) - return item.Item1; - lock (values) - { - if (!assume_unique && values.Count != values_list.Count) - { - // bag was modified during iteration, try again - continue; - } - // add item - var result = $"element-{Interlocked.Increment(ref MonotonicElementId)}"; - values.Add((result, element)); - return result; - } - } - } - - var id = Interlocked.Increment(ref MonotonicElementId); - return $"incomparable-element-{id}"; - } - - struct MsaaSiblingComparisonInfo - { - public int child_id; - public int left, top, width, height; - public int state; - public int index; - } - - private bool GetSiblingComparisonInfo(AutomationElement element, MsaaSiblingComparisonInfo? comparand, out MsaaSiblingComparisonInfo result) - { - result = default; - var acc = GetIAccessibleBackground(element, out var child_id); - if (acc is null) - return false; - result.child_id = child_id; - if (comparand.HasValue && comparand.Value.child_id != result.child_id) - return false; - try - { - acc.accLocation(out result.left, out result.top, out result.width, out result.height, - child_id); - } - catch (Exception e) - { - if (!UiaElement.IsExpectedException(e)) - throw; - } - if (comparand.HasValue && - (comparand.Value.left != result.left || - comparand.Value.top != result.top || - comparand.Value.width != result.width || - comparand.Value.height != result.height)) - return false; - try - { - result.state = (int)acc.accState[child_id]; - } - catch (InvalidCastException) { } - catch (Exception e) - { - if (!UiaElement.IsExpectedException(e)) - throw; - } - if (comparand.HasValue && comparand.Value.state != result.state) - return false; - if (child_id == 0) - { - try - { - int index = 0; - var nav_acc = acc; - int nav_child_id = child_id; - while (true) - { - if (!AccessibleNavigate(ref nav_acc, ref nav_child_id, NAVDIR_PREVIOUS)) - break; - index++; - if (comparand.HasValue && comparand.Value.index < index) - return false; - } - result.index = index; - if (comparand.HasValue && comparand.Value.index != result.index) - return false; - } - catch (Exception e) - { - if (!UiaElement.IsExpectedException(e)) - throw; - } - } - return true; - } - - private bool MsaaSiblingSearchList(AutomationElement item1, List<(string, AutomationElement)> list, - out (string, AutomationElement) result) - { - result = default; - - while (true) - { - if (!GetSiblingComparisonInfo(item1, null, out var info1)) - return false; - - bool found = false; - - foreach (var item2 in list) - { - if (GetSiblingComparisonInfo(item2.Item2, info1, out var _unused)) - { - found = true; - result = item2; - break; - } - } - - if (!GetSiblingComparisonInfo(item1, info1, out var _unused2)) - { - // info was modified during search - result = default; - return false; - } - - return found; - } - } - - internal void NotifyElementDefunct(UiaElement element) - { - no_id_elements.TryRemove(element.DebugId, out var _unused); - } - } -} diff --git a/xalia/Uia/UiaElement.cs b/xalia/Uia/UiaElement.cs deleted file mode 100644 index 1d2c02e..0000000 --- a/xalia/Uia/UiaElement.cs +++ /dev/null @@ -1,1579 +0,0 @@ -using FlaUI.Core.Definitions; -using FlaUI.Core.Exceptions; -using FlaUI.Core.Identifiers; -using FlaUI.Core.WindowsAPI; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using Xalia.Gudl; -using Xalia.Uia.Win32; -using Xalia.UiDom; -using static Xalia.Interop.Win32; -using IServiceProvider = Xalia.Interop.Win32.IServiceProvider; - -namespace Xalia.Uia -{ - public class UiaElement : UiDomElement - { - static string[] tracked_properties = { "recurse", "poll_children", - "win32_use_element", "win32_use_trackbar", "win32_use_tabcontrol", "win32_use_listview", - }; - - public UiaElement(UiaElementWrapper wrapper) : base(wrapper.UniqueId, wrapper.Connection) - { - ElementWrapper = wrapper; - RegisterTrackedProperties(tracked_properties); - RegisterTrackedProperties(Root.PropertyPollNames); - } - - public UiaElementWrapper ElementWrapper { get; } - - public string ElementIdentifier - { - get - { - return ElementWrapper.UniqueId; - } - } - - public new UiaConnection Root - { - get - { - return ElementWrapper.Connection; - } - } - - public string ProcessName { get; private set; } - - Dictionary fetching_property = new Dictionary(); - Dictionary property_known = new Dictionary(); - Dictionary property_raw_value = new Dictionary(); - Dictionary property_value = new Dictionary(); - - bool watching_children; - bool refreshing_children; - bool inflight_structure_changed; - - bool polling_children; - - PatternId[] supported_patterns; - bool fetching_supported_patterns; - - Dictionary polling_property = new Dictionary(0); - Dictionary property_poll_token = new Dictionary(0); - - private double offset_remainder; - - string application_name, application_version, toolkit_name, toolkit_version; - bool fetching_application_name, fetching_application_version, fetching_toolkit_name, fetching_toolkit_version; - internal static readonly string[] control_type_names = - { - "unknown", - "app_bar", - "button", - "calendar", - "check_box", - "combo_box", - "custom", - "data_grid", - "data_item", - "document", - "edit", - "group", - "header", - "header_item", - "hyperlink", - "image", - "list", - "list_item", - "menu_bar", - "menu", - "menu_item", - "pane", - "progress_bar", - "radio_button", - "scroll_bar", - "semantic_zoom", - "separator", - "slider", - "spinner", - "split_button", - "status_bar", - "tab", - "tab_item", - "table", - "text", - "thumb", - "title_bar", - "tool_bar", - "tool_tip", - "tree", - "tree_item", - "window", - }; - - internal static readonly string[] msaa_role_names = - { - "unknown", - "title_bar", - "menu_bar", - "scroll_bar", - "grip", - "sound", - "cursor", - "caret", - "alert", - "window", - "client", - "menu_popup", - "menu_item", - "tool_tip", - "application", - "document", - "pane", - "chart", - "dialog", - "border", - "grouping", - "separator", - "tool_bar", - "status_bar", - "table", - "column_header", - "row_header", - "column", - "row", - "cell", - "link", - "help_balloon", - "character", - "list", - "list_item", - "outline", - "outline_item", - "page_tab", - "property_page", - "indicator", - "graphic", - "static_text", - "text", - "push_button", - "check_button", - "radio_button", - "combo_box", - "drop_list", - "progress_bar", - "dial", - "hotkey_field", - "slider", - "spin_button", - "diagram", - "animation", - "equation", - "button_dropdown", - "button_menu", - "button_dropdown_grid", - "white_space", - "page_tab_list", - "clock", - "split_button", - "ip_address", - "outline_button", - }; - - private static readonly Dictionary name_to_control_type; - private static readonly UiDomEnum[] control_type_to_enum; - - private static readonly UiDomEnum[] msaa_role_to_enum; - - private static readonly Dictionary property_aliases; - - static UiaElement() - { - name_to_control_type = new Dictionary(); - control_type_to_enum = new UiDomEnum[control_type_names.Length]; - for (int i = 0; i < control_type_names.Length; i++) - { - string name = control_type_names[i]; - string[] names; - if (name == "button") - names = new[] { "button", "push_button", "pushbutton" }; - else if (name == "tab") - names = new[] { "tab", "page_tab_list", "pagetablist" }; - else if (name == "tab_item") - names = new[] { "tab_item", "tabitem", "page_tab", "pagetab" }; - else if (name == "edit") - names = new[] { "edit", "text_box", "textbox" }; - else if (name.Contains("_")) - names = new[] { name, name.Replace("_", "") }; - else - names = new[] { name }; - control_type_to_enum[i] = new UiDomEnum(names); - foreach (string rolename in names) - name_to_control_type[rolename] = (ControlType)i; - } - msaa_role_to_enum = new UiDomEnum[msaa_role_names.Length]; - for (int i = 0; i < msaa_role_names.Length; i++) - { - string name = msaa_role_names[i]; - string[] names; - if (name.Contains("_")) - names = new[] { name, name.Replace("_", "") }; - else - names = new[] { name }; - msaa_role_to_enum[i] = new UiDomEnum(names); - } - string[] aliases = { - "role", "uia_control_type", - "control_type", "uia_control_type", - "controltype", "uia_control_type", - "automation_id", "uia_automation_id", - "enabled", "uia_enabled", - "is_enabled", "uia_enabled", - "focusable", "uia_keyboard_focusable", - "keyboard_focusable", "uia_keyboard_focusable", - "is_keyboard_focusable", "uia_keyboard_focusable", - "offscreen", "uia_offscreen", - "is_offscreen", "uia_offscreen", - "visible", "uia_visible", - "selected", "uia_selected", - "is_selected", "uia_selected", - "bounds", "uia_bounding_rectangle", - "bounding_rectangle", "uia_bounding_rectangle", - "x", "uia_x", - "abs_x", "uia_x", - "y", "uia_y", - "abs_y", "uia_y", - "width", "uia_width", - "height", "uia_height", - "name", "uia_name", - "class_name", "uia_class_name", - "expand_collapse_state", "uia_expand_collapse_state", - "orientation", "uia_orientation", - "framework_id", "uia_framework_id", - "frameworkid", "uia_framework_id", - "focused", "uia_focused", - "foreground", "msaa_foreground", - "active", "win32_active", - "set_focus", "uia_set_focus", - "focused_element", "uia_focused_element", - "foreground_element", "uia_foreground_element", - "active_element", "win32_active_element", - "menu_mode", "uia_menu_mode", - "opened_menu", "uia_opened_menu", - "in_menu", "uia_in_menu", - "in_submenu", "uia_in_submenu", - "invoke", "ui_invoke", - "select", "uia_select", - "expand", "uia_expand", - "collapse", "uia_collapse", - "application_version", "acc2_application_version", - "toolkit_name", "acc2_toolkit_name", - "toolkit_version", "acc2_toolkit_version", - "set_foreground_window", "win32_set_foreground_window", - "application_name", "win32_process_name", - "process_name", "win32_process_name", - }; - property_aliases = new Dictionary(aliases.Length / 2); - for (int i=0; i seen_children = new HashSet(); - int i = 0; - while (i < children.Count) - { - if (!seen_children.Add(children[i].UniqueId)) - { - children.RemoveAt(i); - continue; - } - i++; - } - - // First remove any existing children that are missing or out of order - i = 0; - foreach (var new_child in children) - { - if (!Children.Exists((UiDomElement element) => ElementMatches(element, new_child.UniqueId))) - continue; - while (!ElementMatches(Children[i], new_child.UniqueId)) - { - RemoveChild(i); - } - i++; - } - - // Remove any remaining missing children - while (i < Children.Count && Children[i] is UiaElement) - RemoveChild(i); - - // Add any new children - i = 0; - foreach (var new_child in children) - { - if (Children.Count <= i || !ElementMatches(Children[i], new_child.UniqueId)) - { - if (!(Root.LookupAutomationElement(new_child) is null)) - { - // Child element is a duplicate of another element somewhere in the tree. - continue; - } - AddChild(i, new UiaElement(new_child)); - } - i += 1; - } - - refreshing_children = false; - - if (inflight_structure_changed) - { - inflight_structure_changed = false; - Utils.RunTask(RefreshChildren()); - } - } - - private bool ElementMatches(UiDomElement uidom, string element_id) - { - if (uidom is UiaElement uia) - return uia.ElementIdentifier == element_id; - return false; - } - - internal void UpdateChildren() - { - Utils.RunTask(RefreshChildren()); - } - - internal void OnChildrenChanged(StructureChangeType arg2, int[] arg3) - { - if (MatchesDebugCondition()) - Utils.DebugWriteLine($"OnChildrenChanged for {this}"); - UpdateChildren(); - } - - internal void WatchChildren() - { - if (watching_children) - return; - if (MatchesDebugCondition()) - Utils.DebugWriteLine($"WatchChildren for {this}"); - watching_children = true; - Utils.RunTask(RefreshChildren()); - if (polling_children) - PollProperty(new IdentifierExpression("children"), RefreshChildren, 2000); - } - - internal void UnwatchChildren() - { - if (!watching_children) - return; - if (MatchesDebugCondition()) - Utils.DebugWriteLine($"UnwatchChildren for {this}"); - watching_children = false; - for (int i = Children.Count - 1; i >= 0; i--) - { - if (Children[i] is UiaElement) - RemoveChild(i); - } - if (polling_children) - EndPollProperty(new IdentifierExpression("children")); - } - - private async Task PollProperty(string name, PropertyId propid) - { - if (!polling_property.TryGetValue(propid, out bool polling) || !polling) - return; - - await FetchPropertyAsync(name, propid); - - var poll_token = new CancellationTokenSource(); - property_poll_token[propid] = poll_token; - - try - { - await Task.Delay(2000, poll_token.Token); - } - catch (TaskCanceledException) - { - property_poll_token[propid] = null; - return; - } - - property_poll_token[propid] = null; - Utils.RunTask(PollProperty(name, propid)); - } - - protected override void TrackedPropertyChanged(string name, UiDomValue new_value) - { - switch (name) - { - case "recurse": - if (new_value.ToBool()) - WatchChildren(); - else - UnwatchChildren(); - break; - case "poll_children": - if (new_value.ToBool() != polling_children) - { - polling_children = new_value.ToBool(); - if (watching_children) - { - if (polling_children) - PollProperty(new IdentifierExpression("poll_children"), RefreshChildren, 2000); - else - EndPollProperty(new IdentifierExpression("poll_children")); - } - } - break; - case "win32_use_element": - UseElementPropertyChanged(new_value, - // have to do slow check because Win32Element has subclasses - (UiDomElement e) => { return e.GetType() == typeof(Win32Element); }, - () => { return new Win32Element("Win32Element", ElementWrapper.Hwnd, Root); }); - break; - case "win32_use_trackbar": - UseElementPropertyChanged(new_value, - (UiDomElement e) => { return e is Win32Trackbar; }, - () => { return new Win32Trackbar(ElementWrapper.Hwnd, Root); }); - break; - case "win32_use_tabcontrol": - UseElementPropertyChanged(new_value, - (UiDomElement e) => { return e is Win32TabControl; }, - () => { return new Win32TabControl(ElementWrapper.Hwnd, Root); }); - break; - case "win32_use_listview": - UseElementPropertyChanged(new_value, - (UiDomElement e) => { return e is Win32ListView; }, - () => { return new Win32ListView(ElementWrapper.Hwnd, Root); }); - break; - } - if (name.StartsWith("poll_") && Root.names_to_property.TryGetValue(name.Substring(5), out var propid)) - { - var prop_name = Root.properties_to_name[propid]; - - bool new_polling = new_value.ToBool(); - bool old_polling = polling_property.TryGetValue(propid, out bool polling) && polling; - - if (new_polling != old_polling) - { - polling_property[propid] = new_polling; - if (new_polling) - Utils.RunTask(PollProperty(prop_name, propid)); - else - { - if (property_poll_token.TryGetValue(propid, out var token) && token != null) - { - token.Cancel(); - property_poll_token[propid] = null; - } - } - } - } - base.TrackedPropertyChanged(name, new_value); - } - - private void UseElementPropertyChanged(UiDomValue new_value, Predicate predicate, Func ctor) - { - bool new_use_element = new_value.ToBool(); - int idx = Children.FindIndex(predicate); - bool old_use_element = (idx != -1); - - if (new_use_element == old_use_element) - return; - - if (new_use_element) - AddChild(Children.Count, ctor()); - else - RemoveChild(idx); - } - - private async Task FetchPropertyAsync(string name, PropertyId propid) - { - object current_value = await Root.CommandThread.GetPropertyValue(ElementWrapper, propid); - OnPropertyChange(name, propid, current_value); - } - - public void OnPropertyChange(string name, PropertyId propid, object value) - { - UiDomValue new_value; - - if (name == "uia_expand_collapse_state" && value is int ecsi) - { - value = (ExpandCollapseState)ecsi; - } - else if (name == "uia_orientation" && value is int ori) - { - value = (OrientationType)ori; - } - else if (name == "uia_control_type" && value is int cti) - { - value = (ControlType)cti; - } - else if (name == "msaa_role" && value is int rolei) - { - value = (AccessibilityRole)rolei; - } - - if (value is System.Drawing.Rectangle r) - { - new_value = new UiDomString(r.ToString()); - } - else if (value is System.Windows.Rect swr) - { - value = new System.Drawing.Rectangle((int)swr.Left, (int)swr.Top, (int)swr.Width, (int)swr.Height); - new_value = new UiDomString(value.ToString()); - } - else if (value is double[] dba && name == "uia_bounding_rectangle" && dba.Length == 4) - { - value = new System.Drawing.Rectangle((int)dba[0], (int)dba[1], (int)dba[2], (int)dba[3]); - new_value = new UiDomString(value.ToString()); - } - else if (value is ControlType ct && (int)ct < control_type_to_enum.Length) - { - new_value = control_type_to_enum[(int)ct]; - } - else if (value is AccessibilityRole role) - { - new_value = msaa_role_to_enum[(int)role]; - } - else if (value is bool b) - { - new_value = UiDomBoolean.FromBool(b); - } - else if (value is string s) - { - new_value = new UiDomString(s); - } - else if (value is ExpandCollapseState ecs) - { - switch (ecs) - { - case ExpandCollapseState.Collapsed: - new_value = new UiDomEnum(new[] { "collapsed" }); - break; - case ExpandCollapseState.Expanded: - new_value = new UiDomEnum(new[] { "expanded" }); - break; - case ExpandCollapseState.PartiallyExpanded: - new_value = new UiDomEnum(new[] { "partially_expanded" }); - break; - case ExpandCollapseState.LeafNode: - new_value = new UiDomEnum(new[] { "leaf_node" }); - break; - default: - new_value = UiDomUndefined.Instance; - break; - } - } - else if (value is OrientationType or) - { - switch (or) - { - case OrientationType.None: - new_value = new UiDomEnum(new[] { "none" }); - break; - case OrientationType.Horizontal: - new_value = new UiDomEnum(new[] { "horizontal" }); - break; - case OrientationType.Vertical: - new_value = new UiDomEnum(new[] { "vertical" }); - break; - default: - new_value = UiDomUndefined.Instance; - break; - } - } - else - { - if (!(value is null)) - Utils.DebugWriteLine($"Warning: value for {name} has unsupported type {value.GetType()}"); - new_value = UiDomUndefined.Instance; - } - - UiDomValue old_value; - if (!property_value.TryGetValue(propid, out old_value)) - old_value = UiDomUndefined.Instance; - - property_known[propid] = true; - if (!old_value.Equals(new_value)) - { - if (MatchesDebugCondition()) - Utils.DebugWriteLine($"{DebugId}.{name}: {new_value}"); - property_value[propid] = new_value; - property_raw_value[propid] = value; - PropertyChanged(name); - } - } - - protected override void WatchProperty(GudlExpression expression) - { - if (expression is IdentifierExpression id) - { - switch (id.Name) - { - case "uia_supported_patterns": - { - if (!fetching_supported_patterns) - { - fetching_supported_patterns = true; - Utils.RunTask(FetchSupportedPatterns()); - } - break; - } - case "acc2_application_name": - { - if (!fetching_application_name) - { - fetching_application_name = true; - Utils.RunTask(FetchApplicationName()); - } - break; - } - case "acc2_application_version": - { - if (!fetching_application_version) - { - fetching_application_version = true; - Utils.RunTask(FetchApplicationVersion()); - } - break; - } - case "acc2_toolkit_name": - { - if (!fetching_toolkit_name) - { - fetching_toolkit_name = true; - Utils.RunTask(FetchToolkitName()); - } - break; - } - case "acc2_toolkit_version": - { - if (!fetching_toolkit_version) - { - fetching_toolkit_version = true; - Utils.RunTask(FetchToolkitVersion()); - } - break; - } - } - - if (Root.names_to_property.TryGetValue(id.Name, out var propid) && - (!property_known.TryGetValue(propid, out var known) || !known) && - (!fetching_property.TryGetValue(propid, out var fetching) || !fetching)) - { - fetching_property[propid] = true; - Utils.RunTask(FetchPropertyAsync(id.Name, propid)); - } - } - - base.WatchProperty(expression); - } - - internal async Task PropertyMaybeChanged(PropertyId propid) - { - if ((property_known.TryGetValue(propid, out var known) && known) || - (fetching_property.TryGetValue(propid, out var fetching) && fetching)) - { - await FetchPropertyAsync(Root.properties_to_name[propid], propid); - } - } - - private async Task FetchSupportedPatterns() - { - try - { - supported_patterns = await Root.CommandThread.GetSupportedPatterns(ElementWrapper); - - PropertyChanged("uia_supported_patterns"); - } - catch (Exception e) - { - if (!IsExpectedException(e)) - throw; - } - } - - static bool DebugExceptions = Environment.GetEnvironmentVariable("XALIA_DEBUG_EXCEPTIONS") != "0"; - - internal static bool IsExpectedException(Exception e) - { -#if DEBUG - if (DebugExceptions) - { - Utils.DebugWriteLine("WARNING: Exception:"); - Utils.DebugWriteLine(e); - } -#endif - if (e is FlaUI.Core.Exceptions.NotSupportedException) - { - return true; - } - if (e is InvalidOperationException) - { - return true; - } - if (e is COMException com) - { - switch (com.ErrorCode) - { - case unchecked((int)0x80004005): // E_FAIL - case unchecked((int)0x80010012): // RPC_E_SERVER_DIED_DNE - case unchecked((int)0x80010108): // RPC_E_DISCONNECTED - case unchecked((int)0x80020003): // DISP_E_MEMBERNOTFOUND - case unchecked((int)0x800401FD): // CO_E_OBJNOTCONNECTED - case unchecked((int)0x80040201): // EVENT_E_ALL_SUBSCRIBERS_FAILED - case unchecked((int)0x800706B5): // RPC_S_UNKNOWN_IF - case unchecked((int)0x800706BA): // RPC_E_SERVER_UNAVAILABLE - case unchecked((int)0x800706BE): // RPC_S_CALL_FAILED - case unchecked((int)0x80131505): // UIA_E_TIMEOUT - return true; - } - } - if (e is UnauthorizedAccessException) - { - return true; - } -#if DEBUG - return false; -#else - if (DebugExceptions) - { - Utils.DebugWriteLine("WARNING: Exception ignored:"); - Utils.DebugWriteLine(e); - } - return true; -#endif - } - - private IAccessibleApplication QueryAccessibleApplicationBackground() - { - IntPtr pIAA; - - try - { - object acc = Root.GetIAccessibleBackground(ElementWrapper.AutomationElement, out var _unused); - - if (acc is null) - return null; - - IServiceProvider sp = (IServiceProvider)acc; - - if (sp is null) - { - // Unsure how this can happen - return null; - } - - Guid iid = IID_IAccessibleApplication; - - pIAA = sp.QueryService(ref iid, ref iid); - } - catch (InvalidCastException) // E_NOINTERFACE - { - return null; - } - catch (ArgumentException) - { - return null; - } - catch (Exception e) - { - if (IsExpectedException(e)) - return null; - throw; - } - - return (IAccessibleApplication)Marshal.GetTypedObjectForIUnknown(pIAA, typeof(IAccessibleApplication)); - } - - private async Task FetchApplicationName() - { - try - { - application_name = await Root.CommandThread.OnBackgroundThread(() => - { - return QueryAccessibleApplicationBackground()?.appName; - }, ElementWrapper); - } - catch (Exception e) - { - if (!IsExpectedException(e)) - throw; - } - } - - private async Task FetchApplicationVersion() - { - try - { - application_version = await Root.CommandThread.OnBackgroundThread(() => - { - return QueryAccessibleApplicationBackground()?.appVersion; - }, ElementWrapper); - } - catch (Exception e) - { - if (!IsExpectedException(e)) - throw; - } - } - - private async Task FetchToolkitName() - { - try - { - toolkit_name = await Root.CommandThread.OnBackgroundThread(() => - { - return QueryAccessibleApplicationBackground()?.toolkitName; - }, ElementWrapper); - } - catch (Exception e) - { - if (!IsExpectedException(e)) - throw; - } - } - - private async Task FetchToolkitVersion() - { - try - { - toolkit_version = await Root.CommandThread.OnBackgroundThread(() => - { - return QueryAccessibleApplicationBackground()?.toolkitVersion; - }, ElementWrapper); - } - catch (Exception e) - { - if (!IsExpectedException(e)) - throw; - } - } - - protected override void SetAlive(bool value) - { - if (value) - { - Root.elements_by_id[ElementIdentifier] = this; - if (ElementWrapper.Equals(Root.FocusedElement)) - { - Root.PropertyChanged("uia_focused_element"); - } - if (ElementWrapper.Equals(Root.ForegroundElement)) - { - Root.PropertyChanged("msaa_foreground_element"); - } - if (ElementWrapper.Equals(Root.ActiveElement)) - { - Root.PropertyChanged("win32_active_element"); - } - if (ElementWrapper.Equals(Root.UiaOpenedMenu)) - { - Root.PropertyChanged("uia_opened_menu"); - } - } - else - { - property_known.Clear(); - property_value.Clear(); - property_raw_value.Clear(); - Root.elements_by_id.Remove(ElementIdentifier); - polling_children = false; - while (property_poll_token.Count != 0) - { - var kvp = property_poll_token.First(); - if (!(kvp.Value is null)) - kvp.Value.Cancel(); - property_poll_token.Remove(kvp.Key); - } - property_poll_token.Clear(); - polling_property.Clear(); - Root.NotifyElementDefunct(this); - } - base.SetAlive(value); - } - - private UiDomValue GetProperty(string name, PropertyId id, [In, Out] HashSet<(UiDomElement, GudlExpression)> depends_on) - { - depends_on.Add((this, new IdentifierExpression(name))); - if (property_known.TryGetValue(id, out var known) && known) - { - if (property_value.TryGetValue(id, out var val)) - return val; - return UiDomUndefined.Instance; - } - return UiDomUndefined.Instance; - } - - private object GetRawProperty(string name, PropertyId id, [In, Out] HashSet<(UiDomElement, GudlExpression)> depends_on) - { - depends_on.Add((this, new IdentifierExpression(name))); - if (property_known.TryGetValue(id, out var known) && known) - { - if (property_raw_value.TryGetValue(id, out var val)) - return val; - return null; - } - return UiDomUndefined.Instance; - } - - protected override UiDomValue EvaluateIdentifierCore(string id, UiDomRoot root, [In, Out] HashSet<(UiDomElement, GudlExpression)> depends_on) - { - if (property_aliases.TryGetValue(id, out string aliased)) - { - var value = base.EvaluateIdentifierCore(id, root, depends_on); - if (!value.Equals(UiDomUndefined.Instance)) - return value; - id = aliased; - } - - - switch (id) - { - case "uia_controltype": - case "uia_control_type": - return GetProperty("uia_control_type", Root.Automation.PropertyLibrary.Element.ControlType, depends_on); - case "uia_automation_id": - return GetProperty("uia_automation_id", Root.Automation.PropertyLibrary.Element.AutomationId, depends_on); - case "uia_enabled": - case "uia_is_enabled": - return GetProperty("uia_enabled", Root.Automation.PropertyLibrary.Element.IsEnabled, depends_on); - case "uia_is_keyboard_focusable": - case "uia_keyboard_focusable": - return GetProperty("uia_keyboard_focusable", Root.Automation.PropertyLibrary.Element.IsKeyboardFocusable, depends_on); - case "uia_has_keyboard_focus": - return GetProperty("uia_has_keyboard_focus", Root.Automation.PropertyLibrary.Element.HasKeyboardFocus, depends_on); - case "uia_offscreen": - case "uia_is_offscreen": - return GetProperty("uia_offscreen", Root.Automation.PropertyLibrary.Element.IsOffscreen, depends_on); - case "uia_visible": - return UiDomBoolean.FromBool(!GetProperty("uia_offscreen", Root.Automation.PropertyLibrary.Element.IsOffscreen, depends_on).ToBool()); - case "uia_selected": - case "uia_is_selected": - return GetProperty("uia_selected", Root.Automation.PropertyLibrary.SelectionItem.IsSelected, depends_on); - case "uia_bounding_rectangle": - return GetProperty("uia_bounding_rectangle", Root.Automation.PropertyLibrary.Element.BoundingRectangle, depends_on); - case "uia_x": - case "uia_abs_x": - { - if (GetRawProperty("uia_bounding_rectangle", Root.Automation.PropertyLibrary.Element.BoundingRectangle, depends_on) is System.Drawing.Rectangle r) - { - return new UiDomInt(r.Left); - } - return UiDomUndefined.Instance; - } - case "uia_y": - case "uia_abs_y": - { - if (GetRawProperty("uia_bounding_rectangle", Root.Automation.PropertyLibrary.Element.BoundingRectangle, depends_on) is System.Drawing.Rectangle r) - { - return new UiDomInt(r.Top); - } - return UiDomUndefined.Instance; - } - case "uia_width": - { - if (GetRawProperty("uia_bounding_rectangle", Root.Automation.PropertyLibrary.Element.BoundingRectangle, depends_on) is System.Drawing.Rectangle r) - { - return new UiDomInt(r.Width); - } - return UiDomUndefined.Instance; - } - case "uia_height": - { - if (GetRawProperty("uia_bounding_rectangle", Root.Automation.PropertyLibrary.Element.BoundingRectangle, depends_on) is System.Drawing.Rectangle r) - { - return new UiDomInt(r.Height); - } - return UiDomUndefined.Instance; - } - case "uia_name": - return GetProperty("uia_name", Root.Automation.PropertyLibrary.Element.Name, depends_on); - case "uia_class_name": - return GetProperty("uia_class_name", Root.Automation.PropertyLibrary.Element.ClassName, depends_on); - case "uia_expand_collapse_state": - return GetProperty("uia_expand_collapse_state", Root.Automation.PropertyLibrary.ExpandCollapse.ExpandCollapseState, depends_on); - case "collapsed": - { - var value = base.EvaluateIdentifierCore(id, root, depends_on); - if (!value.Equals(UiDomUndefined.Instance)) - return value; - var st = GetRawProperty("uia_expand_collapse_state", Root.Automation.PropertyLibrary.ExpandCollapse.ExpandCollapseState, depends_on); - if (st is ExpandCollapseState ecs) - { - return UiDomBoolean.FromBool(ecs == ExpandCollapseState.Collapsed); - } - return UiDomUndefined.Instance; - } - case "expanded": - { - var value = base.EvaluateIdentifierCore(id, root, depends_on); - if (!value.Equals(UiDomUndefined.Instance)) - return value; - var st = GetRawProperty("uia_expand_collapse_state", Root.Automation.PropertyLibrary.ExpandCollapse.ExpandCollapseState, depends_on); - if (st is ExpandCollapseState ecs) - { - return UiDomBoolean.FromBool(ecs == ExpandCollapseState.Expanded); - } - return UiDomUndefined.Instance; - } - case "uia_orientation": - return GetProperty("uia_orientation", Root.Automation.PropertyLibrary.Element.Orientation, depends_on); - case "horizontal": - { - var value = base.EvaluateIdentifierCore(id, root, depends_on); - if (!value.Equals(UiDomUndefined.Instance)) - return value; - var orientation = GetRawProperty("uia_orientation", Root.Automation.PropertyLibrary.Element.Orientation, depends_on); - if (orientation is OrientationType ore) - { - return UiDomBoolean.FromBool(ore == OrientationType.Horizontal); - } - return UiDomUndefined.Instance; - } - case "vertical": - { - var value = base.EvaluateIdentifierCore(id, root, depends_on); - if (!value.Equals(UiDomUndefined.Instance)) - return value; - var orientation = GetRawProperty("uia_orientation", Root.Automation.PropertyLibrary.Element.Orientation, depends_on); - if (orientation is OrientationType ore) - { - return UiDomBoolean.FromBool(ore == OrientationType.Vertical); - } - return UiDomUndefined.Instance; - } - case "uia_framework_id": - return GetProperty("uia_framework_id", Root.Automation.PropertyLibrary.Element.FrameworkId, depends_on); - case "msaa_role": - return GetProperty("msaa_role", Root.Automation.PropertyLibrary.LegacyIAccessible.Role, depends_on); - case "aria_role": - return GetProperty("aria_role", Root.Automation.PropertyLibrary.Element.AriaRole, depends_on); - case "uia_focused": - depends_on.Add((this, new IdentifierExpression("uia_focused"))); - return UiDomBoolean.FromBool(ElementWrapper.Equals(Root.FocusedElement)); - case "msaa_foreground": - depends_on.Add((this, new IdentifierExpression("msaa_foreground"))); - return UiDomBoolean.FromBool(ElementWrapper.Equals(Root.ForegroundElement)); - case "win32_active": - depends_on.Add((this, new IdentifierExpression("win32_active"))); - return UiDomBoolean.FromBool(ElementWrapper.Equals(Root.ActiveElement)); - case "uia_set_focus": - { - return new UiDomRoutineAsync(this, "uia_set_focus", DoSetFocus); - } - case "uia_focused_element": - depends_on.Add((Root, new IdentifierExpression("uia_focused_element"))); - return (UiDomValue)Root.LookupAutomationElement(Root.FocusedElement) ?? UiDomUndefined.Instance; - case "msaa_foreground_element": - depends_on.Add((Root, new IdentifierExpression("msaa_foreground_element"))); - return (UiDomValue)Root.LookupAutomationElement(Root.ForegroundElement) ?? UiDomUndefined.Instance; - case "win32_active_element": - depends_on.Add((Root, new IdentifierExpression("win32_active_element"))); - return (UiDomValue)Root.LookupAutomationElement(Root.ActiveElement) ?? UiDomUndefined.Instance; - case "uia_menu_mode": - depends_on.Add((Root, new IdentifierExpression("uia_menu_mode"))); - return UiDomBoolean.FromBool(Root.UiaMenuMode); - case "uia_opened_menu": - return Root.EvaluateIdentifier(id, Root, depends_on); - case "uia_in_menu": - return Root.EvaluateIdentifier(id, Root, depends_on); - case "uia_in_submenu": - return Root.EvaluateIdentifier(id, Root, depends_on); - case "is_uia_element": - return UiDomBoolean.True; - case "is_spi_element": - case "is_atspi_element": - case "is_at_spi_element": - return UiDomBoolean.False; - case "uia_invoke": - depends_on.Add((this, new IdentifierExpression("uia_supported_patterns"))); - if (!(supported_patterns is null) && supported_patterns.Contains(Root.Automation.PatternLibrary.InvokePattern)) - { - return new UiDomRoutineAsync(this, "uia_invoke", Invoke); - } - return UiDomUndefined.Instance; - case "msaa_do_default_action": - depends_on.Add((this, new IdentifierExpression("uia_supported_patterns"))); - if (!(supported_patterns is null) && supported_patterns.Contains(Root.Automation.PatternLibrary.LegacyIAccessiblePattern)) - { - return new UiDomRoutineAsync(this, "msaa_do_default_action", MsaaDefaultAction); - } - return UiDomUndefined.Instance; - case "uia_select": - depends_on.Add((this, new IdentifierExpression("uia_supported_patterns"))); - if (!(supported_patterns is null) && supported_patterns.Contains(Root.Automation.PatternLibrary.SelectionItemPattern)) - { - return new UiDomRoutineAsync(this, "uia_select", Select); - } - return UiDomUndefined.Instance; - case "uia_expand": - depends_on.Add((this, new IdentifierExpression("uia_supported_patterns"))); - if (!(supported_patterns is null) && supported_patterns.Contains(Root.Automation.PatternLibrary.ExpandCollapsePattern)) - { - return new UiDomRoutineAsync(this, "uia_expand", Expand); - } - return UiDomUndefined.Instance; - case "uia_collapse": - depends_on.Add((this, new IdentifierExpression("uia_supported_patterns"))); - if (!(supported_patterns is null) && supported_patterns.Contains(Root.Automation.PatternLibrary.ExpandCollapsePattern)) - { - return new UiDomRoutineAsync(this, "uia_collapse", Collapse); - } - return UiDomUndefined.Instance; - case "uia_adjust_scroll_container": - depends_on.Add((this, new IdentifierExpression("uia_supported_patterns"))); - if (!(supported_patterns is null) && supported_patterns.Contains(Root.Automation.PatternLibrary.ScrollPattern)) - { - return new UiaAdjustScrollContainer(this); - } - return UiDomUndefined.Instance; - case "acc2_application_name": - depends_on.Add((this, new IdentifierExpression(id))); - if (application_name is null) - return UiDomUndefined.Instance; - return new UiDomString(application_name); - case "acc2_application_version": - depends_on.Add((this, new IdentifierExpression(id))); - if (application_version is null) - return UiDomUndefined.Instance; - return new UiDomString(application_version); - case "acc2_toolkit_name": - depends_on.Add((this, new IdentifierExpression(id))); - if (toolkit_name is null) - return UiDomUndefined.Instance; - return new UiDomString(toolkit_name); - case "acc2_toolkit_version": - depends_on.Add((this, new IdentifierExpression(id))); - if (toolkit_version is null) - return UiDomUndefined.Instance; - return new UiDomString(toolkit_version); - case "win32_set_foreground_window": - if (ElementWrapper.Hwnd != IntPtr.Zero) - { - return new UiDomRoutineSync(this, "win32_set_foreground_window", Win32SetForegroundWindow); - } - return UiDomUndefined.Instance; - case "win32_process_name": - if (ElementWrapper.Pid != 0) - { - if (ProcessName is null) - { - using (var process = Process.GetProcessById(ElementWrapper.Pid)) - ProcessName = process.ProcessName; - } - return new UiDomString(ProcessName); - } - return UiDomUndefined.Instance; - } - - { - var value = base.EvaluateIdentifierCore(id, root, depends_on); - if (!value.Equals(UiDomUndefined.Instance)) - return value; - } - - switch (id) - { - case "toolkit": - { - var acc2_toolkit = EvaluateIdentifierCore("acc2_toolkit_name", root, depends_on); - if (!acc2_toolkit.Equals(UiDomUndefined.Instance)) - { - return acc2_toolkit; - } - return EvaluateIdentifierCore("uia_framework_id", root, depends_on); - } - } - - if (name_to_control_type.ContainsKey(id)) - { - return Evaluate(new BinaryExpression( - new IdentifierExpression("uia_control_type"), - new IdentifierExpression(id), - GudlToken.Dot), depends_on); - } - - return UiDomUndefined.Instance; - } - - protected override void DumpProperties() - { - foreach (var kvp in property_known) - { - if (kvp.Value && property_value.TryGetValue(kvp.Key, out var val)) - { - Utils.DebugWriteLine($" {Root.properties_to_name[kvp.Key]}: {val}"); - } - } - if (ElementWrapper.Equals(Root.FocusedElement)) - Utils.DebugWriteLine($" uia_focused: true"); - if (ElementWrapper.Equals(Root.ForegroundElement)) - Utils.DebugWriteLine($" msaa_foreground: true"); - if (ElementWrapper.Equals(Root.ActiveElement)) - Utils.DebugWriteLine($" win32_active: true"); - if (!(supported_patterns is null)) - foreach (var patternid in supported_patterns) - Utils.DebugWriteLine($" supported pattern: {patternid.Name}"); - if (!(application_name is null)) - Utils.DebugWriteLine($" acc2_application_name: {application_name}"); - if (!(application_version is null)) - Utils.DebugWriteLine($" acc2_application_verion: {application_version}"); - if (!(toolkit_name is null)) - Utils.DebugWriteLine($" acc2_toolkit_name: {toolkit_name}"); - if (!(toolkit_version is null)) - Utils.DebugWriteLine($" acc2_toolkit_version: {toolkit_version}"); - if (!(ProcessName is null)) - Utils.DebugWriteLine($" win32_process_name: {ProcessName}"); - base.DumpProperties(); - } - - private void Win32SetForegroundWindow(UiDomRoutineSync obj) - { - SetForegroundWindow(ElementWrapper.Hwnd); - } - - public override async Task<(bool, int, int)> GetClickablePoint() - { - var result = await base.GetClickablePoint(); - if (result.Item1) - return result; - - var rc = await Root.CommandThread.GetPropertyValue(ElementWrapper, Root.Automation.PropertyLibrary.Element.BoundingRectangle); - if (!(rc is null) && rc is System.Drawing.Rectangle r) - { - int x = r.Left + r.Width / 2; - int y = r.Right + r.Height / 2; - return (true, x, y); - } - - try - { - var clickable = await Root.CommandThread.OnBackgroundThread(() => - { - return ElementWrapper.AutomationElement.GetClickablePoint(); - }, ElementWrapper.Pid+1); - - int x = clickable.X; - int y = clickable.Y; - return (true, x, y); - } - catch (NoClickablePointException) { } - catch (Exception e) - { - if (!IsExpectedException(e)) - throw; - } - - return (false, 0, 0); - } - - private async Task MsaaDefaultAction(UiDomRoutineAsync obj) - { - var supported = await Root.CommandThread.OnBackgroundThread(() => - { - try - { - ElementWrapper.AutomationElement.Patterns.LegacyIAccessible.Pattern.DoDefaultAction(); - } - catch (Exception e) - { - if (!IsExpectedException(e)) - throw; - return false; - } - - return true; - }, ElementWrapper.Pid+1); - - if (!supported) - { - Utils.DebugWriteLine($"WARNING: msaa_do_default_action not supported on {this}"); - } - } - - private Task DoSetFocus(UiDomRoutineAsync obj) - { - return Root.CommandThread.OnBackgroundThread(() => - { - ElementWrapper.AutomationElement.Focus(); - }, ElementWrapper.Pid+1); - } - - private async Task Invoke(UiDomRoutineAsync obj) - { - var supported = await Root.CommandThread.OnBackgroundThread(() => - { - try - { - ElementWrapper.AutomationElement.Patterns.Invoke.Pattern.Invoke(); - } - catch (Exception e) - { - if (!IsExpectedException(e)) - throw; - } - - return true; - }, ElementWrapper.Pid+1); - - if (!supported) - { - Utils.DebugWriteLine($"WARNING: uia_invoke not supported on {this}"); - } - } - - private Task Select(UiDomRoutineAsync obj) - { - return Root.CommandThread.OnBackgroundThread(() => - { - try - { - ElementWrapper.AutomationElement.Patterns.SelectionItem.Pattern.Select(); - } - catch (Exception e) - { - if (!IsExpectedException(e)) - throw; - } - }, ElementWrapper.Pid+1); - } - - private Task Expand(UiDomRoutineAsync obj) - { - return Root.CommandThread.OnBackgroundThread(() => - { - try - { - ElementWrapper.AutomationElement.Patterns.ExpandCollapse.Pattern.Expand(); - } - catch (Exception e) - { - if (!IsExpectedException(e)) - throw; - } - }, ElementWrapper.Pid+1); - } - - private Task Collapse(UiDomRoutineAsync obj) - { - return Root.CommandThread.OnBackgroundThread(() => - { - try - { - ElementWrapper.AutomationElement.Patterns.ExpandCollapse.Pattern.Collapse(); - } - catch (Exception e) - { - if (!IsExpectedException(e)) - throw; - } - }, ElementWrapper.Pid+1); - } - - private bool GetScrollInfoBackground(int flags, out IntPtr hwnd, out int which, out SCROLLINFO info) - { - hwnd = default; - which = default; - info = default; - if (ElementWrapper.Hwnd != IntPtr.Zero) - { - hwnd = ElementWrapper.Hwnd; - which = SB_CTL; - } - else if (Parent is UiaElement pue && pue.ElementWrapper.Hwnd != IntPtr.Zero) - { - hwnd = pue.ElementWrapper.Hwnd; - string automation_id; - try - { - automation_id = ElementWrapper.AutomationElement.AutomationId; - } - catch (Exception e) - { - if (!IsExpectedException(e)) - throw; - return false; - } - switch (automation_id) - { - case "NonClientHorizontalScrollBar": - which = SB_HORZ; - break; - case "NonClientVerticalScrollBar": - which = SB_VERT; - break; - default: - return false; - } - } - else - { - return false; - } - - info.cbSize = Marshal.SizeOf(); - info.fMask = flags; - return GetScrollInfo(hwnd, which, ref info); - } - - public override async Task GetMinimumIncrement() - { - try - { - var result = await Root.CommandThread.OnBackgroundThread(() => - { - // Try Win32 first - - if (GetScrollInfoBackground(SIF_PAGE, out var unused, out var unused2, out var info)) - { - return Math.Max(1.0, info.nPage / 10.0); - } - - var range = ElementWrapper.AutomationElement.Patterns.RangeValue.Pattern; - return Math.Max(range.SmallChange, range.LargeChange / 10); - }, ElementWrapper.Pid+1); - if (result != 0) - return result; - } - catch (Exception e) - { - if (!IsExpectedException(e)) - throw; - } - return await base.GetMinimumIncrement(); - } - - public override async Task OffsetValue(double ofs) - { - try - { - await Root.CommandThread.OnBackgroundThread(() => - { - // Try Win32 first - if (GetScrollInfoBackground(SIF_POS|SIF_PAGE|SIF_RANGE, out var hwnd, out var which, out var info)) - { - var scroll_current = info.nPos; - - var scroll_new = scroll_current + ofs + offset_remainder; - - int max; - if (info.nPage == 0) - max = info.nMax; - else - max = info.nMax - info.nPage + 1; - - if (scroll_new > max) - scroll_new = max; - else if (scroll_new < info.nMin) - scroll_new = info.nMin; - - int scroll_new_int = (int)Math.Round(scroll_new); - - if (scroll_new_int != scroll_current) - { - info.fMask = SIF_POS; - info.nPos = scroll_new_int; - SetScrollInfo(hwnd, which, ref info, true); - - // We have to also send a WM_HSCROLL or WM_VSCROLL for the app to notice - int msg; - IntPtr ctrl_hwnd, msg_hwnd; - switch (which) - { - case SB_CTL: - msg = ((int)GetWindowLong(hwnd, GWL_STYLE) & SBS_VERT) == SBS_VERT ? WM_VSCROLL : WM_HSCROLL; - ctrl_hwnd = hwnd; - msg_hwnd = GetAncestor(ctrl_hwnd, GA_PARENT); - break; - case SB_HORZ: - msg = WM_HSCROLL; - ctrl_hwnd = IntPtr.Zero; - msg_hwnd = hwnd; - break; - case SB_VERT: - msg = WM_VSCROLL; - ctrl_hwnd = IntPtr.Zero; - msg_hwnd = hwnd; - break; - default: - throw new InvalidOperationException(); - } - - SendMessageW(msg_hwnd, msg, MAKEWPARAM(SB_THUMBTRACK, (ushort)scroll_new_int), ctrl_hwnd); - SendMessageW(msg_hwnd, msg, MAKEWPARAM(SB_THUMBPOSITION, (ushort)scroll_new_int), ctrl_hwnd); - SendMessageW(msg_hwnd, msg, MAKEWPARAM(SB_ENDSCROLL, 0), ctrl_hwnd); - } - - offset_remainder = scroll_new - scroll_new_int; - return; - } - - var range = ElementWrapper.AutomationElement.Patterns.RangeValue.Pattern; - - var current_value = range.Value; - - var new_value = current_value + ofs; - - if (ofs > 0) - { - var maximum_value = range.Maximum; - - if (new_value > maximum_value) - new_value = maximum_value; - } - else - { - var minimum_value = range.Minimum; - - if (new_value < minimum_value) - new_value = minimum_value; - } - - if (new_value != current_value) - range.SetValue(new_value); - }, ElementWrapper.Pid+1); - } - catch (Exception e) - { - if (!IsExpectedException(e)) - throw; - } - } - } -} diff --git a/xalia/Uia/UiaElementWrapper.cs b/xalia/Uia/UiaElementWrapper.cs deleted file mode 100644 index 900f564..0000000 --- a/xalia/Uia/UiaElementWrapper.cs +++ /dev/null @@ -1,74 +0,0 @@ -using FlaUI.Core.AutomationElements; -using System; -using System.Runtime.InteropServices; - -namespace Xalia.Uia -{ - public struct UiaElementWrapper - { - // We need to store more information than the AutomationElement itself to use it. - - // The constructor should generally only be used on background threads because it can block. - - internal UiaElementWrapper(AutomationElement ae, UiaConnection connection, string parent_id = null, bool assume_unique = false) - { - AutomationElement = ae; - Connection = connection; - UniqueId = connection.BlockingGetElementId(ae, out var hwnd, parent_id, assume_unique); - Hwnd = hwnd; - try - { - if (ae.FrameworkAutomationElement.TryGetPropertyValue( - Connection.Automation.PropertyLibrary.Element.ProcessId, out var pid) && - pid is int i) - { - Pid = i; - } - else - { - Pid = 0; - } - } - catch (COMException) - { - Pid = 0; - } - } - - public AutomationElement AutomationElement { get; } - - public UiaConnection Connection { get; } - - public string UniqueId { get; } - - public int Pid { get; } - - public IntPtr Hwnd { get; } - - public bool IsValid - { - get - { - return !(AutomationElement is null); - } - } - - public static UiaElementWrapper InvalidElement - { - get - { - return default(UiaElementWrapper); - } - } - - public bool Equals(UiaElementWrapper other) - { - return UniqueId == other.UniqueId && Connection == other.Connection; - } - - public UiaElement LookupElement() - { - return Connection.LookupAutomationElement(this); - } - } -} diff --git a/xalia/Uia/Win32/Win32Element.cs b/xalia/Uia/Win32/Win32Element.cs deleted file mode 100644 index a8eb5d4..0000000 --- a/xalia/Uia/Win32/Win32Element.cs +++ /dev/null @@ -1,355 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using Xalia.Gudl; -using Xalia.UiDom; - -using static Xalia.Interop.Win32; - -namespace Xalia.Uia.Win32 -{ - public class Win32Element : UiDomElement - { - static Win32Element() - { - string[] aliases = { - "x", "win32_x", - "y", "win32_y", - "width", "win32_width", - "height", "win32_height", - "style", "win32_style", - "enabled", "win32_enabled", - "visible", "win32_visible", - "set_foreground_window", "win32_set_foreground_window", - "application_name", "win32_process_name", - "process_name", "win32_process_name", - }; - property_aliases = new Dictionary(aliases.Length / 2); - for (int i=0; i property_aliases; - private string ProcessName; - - private int _pid; - public int Pid - { - get - { - if (_pid == 0) - { - GetWindowThreadProcessId(Hwnd, out _pid); - } - return _pid; - } - } - - public IntPtr Hwnd { get; } - - internal RECT WindowRect { get; private set; } - public bool WindowRectKnown { get; private set; } - public int X => WindowRect.left; - public int Y => WindowRect.top; - public int Width => WindowRect.right - WindowRect.left; - public int Height => WindowRect.bottom - WindowRect.top; - - public int WindowStyle { get; private set; } - public bool WindowStyleKnown { get; private set; } - - public override bool Equals(object obj) - { - if (obj is Win32Element win32) - { - return DebugId == win32.DebugId; - } - return false; - } - - public override int GetHashCode() - { - return Hwnd.GetHashCode() ^ GetType().GetHashCode(); - } - - protected override void SetAlive(bool value) - { - if (!value) - { - use_virtual_scroll = false; - } - base.SetAlive(value); - } - - protected override UiDomValue EvaluateIdentifierCore(string id, UiDomRoot root, [In, Out] HashSet<(UiDomElement, GudlExpression)> depends_on) - { - if (property_aliases.TryGetValue(id, out string aliased)) - { - var value = base.EvaluateIdentifierCore(id, root, depends_on); - if (!value.Equals(UiDomUndefined.Instance)) - return value; - id = aliased; - } - - switch (id) - { - case "is_win32_element": - return UiDomBoolean.True; - case "win32_set_foreground_window": - return new UiDomRoutineSync(this, "win32_set_foreground_window", Win32SetForegroundWindow); - case "win32_process_name": - try - { - if (ProcessName is null) - { - GetWindowThreadProcessId(Hwnd, out var pid); - using (var process = Process.GetProcessById(pid)) - ProcessName = process.ProcessName; - } - } - catch (ArgumentException) - { - return UiDomUndefined.Instance; - } - return new UiDomString(ProcessName); - case "win32_x": - depends_on.Add((this, new IdentifierExpression("win32_rect"))); - if (WindowRectKnown) - return new UiDomInt(X); - return UiDomUndefined.Instance; - case "win32_y": - depends_on.Add((this, new IdentifierExpression("win32_rect"))); - if (WindowRectKnown) - return new UiDomInt(Y); - return UiDomUndefined.Instance; - case "win32_width": - depends_on.Add((this, new IdentifierExpression("win32_rect"))); - if (WindowRectKnown) - return new UiDomInt(Width); - return UiDomUndefined.Instance; - case "win32_height": - depends_on.Add((this, new IdentifierExpression("win32_rect"))); - if (WindowRectKnown) - return new UiDomInt(Height); - return UiDomUndefined.Instance; - case "win32_style": - depends_on.Add((this, new IdentifierExpression("win32_style"))); - if (WindowStyleKnown) - return new UiDomInt(WindowStyle); - return UiDomUndefined.Instance; - case "win32_visible": - depends_on.Add((this, new IdentifierExpression("win32_style"))); - if (WindowStyleKnown) - return UiDomBoolean.FromBool((WindowStyle & WS_VISIBLE) == WS_VISIBLE); - return UiDomUndefined.Instance; - case "win32_enabled": - depends_on.Add((this, new IdentifierExpression("win32_style"))); - if (WindowStyleKnown) - return UiDomBoolean.FromBool((WindowStyle & WS_DISABLED) == 0); - return UiDomUndefined.Instance; - } - return base.EvaluateIdentifierCore(id, root, depends_on); - } - - protected override void DumpProperties() - { - if (!(ProcessName is null)) - Utils.DebugWriteLine($" win32_process_name: {ProcessName}"); - if (WindowRectKnown) - { - Utils.DebugWriteLine($" win32_x: {X}"); - Utils.DebugWriteLine($" win32_y: {Y}"); - Utils.DebugWriteLine($" win32_width: {Width}"); - Utils.DebugWriteLine($" win32_height: {Height}"); - } - if (WindowStyleKnown) - { - Utils.DebugWriteLine($" win32_style: 0x{WindowStyle:X}"); - Utils.DebugWriteLine($" win32_visible: {(WindowStyle & WS_VISIBLE) == WS_VISIBLE}"); - Utils.DebugWriteLine($" win32_enabled: {(WindowStyle & WS_DISABLED) == 0}"); - } - base.DumpProperties(); - } - - private void Win32SetForegroundWindow(UiDomRoutineSync obj) - { - SetForegroundWindow(Hwnd); - } - - public Task RefreshWindowRect() - { - bool new_rect_known; - RECT new_rect; - new_rect_known = GetWindowRect(Hwnd, out new_rect); - - if (new_rect_known != WindowRectKnown || - (new_rect_known && !new_rect.Equals(WindowRect))) - { - WindowRectKnown = new_rect_known; - WindowRect = new_rect; - - if (MatchesDebugCondition()) - { - if (WindowRectKnown) - Utils.DebugWriteLine($"{DebugId}.win32_rect: {X},{Y} {Width}x{Height}"); - else - Utils.DebugWriteLine($"{DebugId}.win32_rect: undefined"); - } - - PropertyChanged("win32_rect"); - } - return Task.CompletedTask; - } - - private Task RefreshWindowStyle() - { - int new_style = GetWindowLong(Hwnd, GWL_STYLE).ToInt32(); - - if (new_style != WindowStyle || !WindowStyleKnown) - { - int styles_changed = new_style ^ WindowStyle; - WindowStyle = new_style; - WindowStyleKnown = true; - - if (MatchesDebugCondition()) - Utils.DebugWriteLine($"{DebugId}.win32_style: {new_style:x}"); - - PropertyChanged("win32_style"); - } - return Task.CompletedTask; - } - - protected override void WatchProperty(GudlExpression expression) - { - if (expression is IdentifierExpression id) - { - switch (id.Name) - { - case "win32_rect": - PollProperty(expression, RefreshWindowRect, 200); - break; - case "win32_style": - PollProperty(expression, RefreshWindowStyle, 200); - break; - } - } - base.WatchProperty(expression); - } - - protected override void UnwatchProperty(GudlExpression expression) - { - if (expression is IdentifierExpression id) - { - switch (id.Name) - { - case "win32_rect": - EndPollProperty(expression); - WindowRectKnown = false; - break; - case "win32_style": - EndPollProperty(expression); - WindowStyleKnown = false; - break; - } - } - base.UnwatchProperty(expression); - } - - protected override void PropertiesChanged(HashSet changed_properties) - { - if (use_virtual_scroll && changed_properties.Contains(new IdentifierExpression("win32_style"))) - { - UpdateScrollbars(); - } - base.PropertiesChanged(changed_properties); - } - - private bool use_virtual_scroll; - private bool has_hscroll; - private bool has_vscroll; - - public bool UseVirtualScrollBars - { - get => use_virtual_scroll; - protected set - { - if (value != use_virtual_scroll) - { - use_virtual_scroll = value; - if (value) - { - UpdateScrollbars(); - } - else - { - has_hscroll = false; - has_vscroll = false; - - int idx = Children.FindIndex(e => e is Win32VirtualScrollbar); - if (idx != -1) - { - RemoveChild(idx); - idx = Children.FindIndex(idx, e => e is Win32VirtualScrollbar); - if (idx != -1) - RemoveChild(idx); - } - } - } - } - } - - private void UpdateScrollbars() - { - bool hscroll = WindowStyleKnown && (WindowStyle & WS_HSCROLL) == WS_HSCROLL; - bool vscroll = WindowStyleKnown && (WindowStyle & WS_VSCROLL) == WS_VSCROLL; - - if (hscroll != has_hscroll) - { - has_hscroll = hscroll; - if (has_hscroll) - AddChild(Children.Count, new Win32VirtualScrollbar(this, false)); - else - RemoveChild(Children.FindIndex(e => e is Win32VirtualScrollbar scroll && !scroll.Vertical)); - } - if (vscroll != has_vscroll) - { - has_vscroll = vscroll; - if (has_vscroll) - AddChild(Children.Count, new Win32VirtualScrollbar(this, true)); - else - RemoveChild(Children.FindIndex(e => e is Win32VirtualScrollbar scroll && scroll.Vertical)); - } - } - - public virtual Task GetHScrollMinimumIncrement() - { - return Task.FromResult(25.0); - } - - public virtual Task GetVScrollMinimumIncrement() - { - return Task.FromResult(25.0); - } - - public virtual Task OffsetHScroll(double ofs) - { - return Task.CompletedTask; - } - - public virtual Task OffsetVScroll(double ofs) - { - return Task.CompletedTask; - } - } -} diff --git a/xalia/Uia/Win32/Win32ListView.cs b/xalia/Uia/Win32/Win32ListView.cs deleted file mode 100644 index 3edc4a3..0000000 --- a/xalia/Uia/Win32/Win32ListView.cs +++ /dev/null @@ -1,625 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; -using Xalia.Gudl; -using Xalia.Interop; -using Xalia.UiDom; -using static Xalia.Interop.Win32; - -namespace Xalia.Uia.Win32 -{ - internal class Win32ListView : Win32Element - { - public Win32ListView(IntPtr hwnd, UiaConnection root) : base("Win32ListView", hwnd, root) - { - } - - static Win32ListView() - { - string[] aliases = { - "view", "win32_view", - "top_index", "win32_top_index", - "item_count", "win32_item_count", - "count_per_page", "win32_count_per_page", - "header", "win32_header", - "header_hwnd", "win32_header_hwnd", - }; - property_aliases = new Dictionary(aliases.Length / 2); - for (int i = 0; i < aliases.Length; i += 2) - { - property_aliases[aliases[i]] = aliases[i + 1]; - } - } - - static Dictionary property_aliases; - - private Win32RemoteProcessMemory remote_process_memory; - - bool CheckingComCtl6; - bool IsComCtl6; - bool IsComCtl6Known; - - int ViewInt; - bool ViewKnown; - - bool watching_children; - private IDisposable HeaderHwndWatcher; - private IDisposable ChildItemStartWatcher; - private IDisposable ChildItemCountWatcher; - bool HeaderHwndKnown; - bool CheckingHeaderHwnd; - IntPtr HeaderHwnd; - Win32Element Header; - private bool refreshing_children; - private bool TopIndexKnown; - private int TopIndex; - private bool ItemCountKnown; - private int ItemCount; - private bool CountPerPageKnown; - private int CountPerPage; - private int child_item_start; - private int child_item_count; - - protected override void SetAlive(bool value) - { - if (!value) - { - if (!(HeaderHwndWatcher is null)) - { - HeaderHwndWatcher.Dispose(); - HeaderHwndWatcher = null; - } - if (!(ChildItemStartWatcher is null)) - { - ChildItemStartWatcher.Dispose(); - ChildItemStartWatcher = null; - } - if (!(ChildItemCountWatcher is null)) - { - ChildItemCountWatcher.Dispose(); - ChildItemCountWatcher = null; - } - if (!(remote_process_memory is null)) - { - remote_process_memory.Unref(); - remote_process_memory = null; - } - } - base.SetAlive(value); - } - - private static UiDomValue ViewFromInt(int view) - { - switch (view) - { - case LV_VIEW_ICON: - return new UiDomEnum(new string[] { "icon" }); - case LV_VIEW_DETAILS: - return new UiDomEnum(new string[] { "report", "details", "table" }); - case LV_VIEW_SMALLICON: - return new UiDomEnum(new string[] { "small_icon", "smallicon" }); - case LV_VIEW_LIST: - return new UiDomEnum(new string[] { "list" }); - case LV_VIEW_TILE: - return new UiDomEnum(new string[] { "tile" }); - } - return UiDomUndefined.Instance; - } - - private static UiDomValue ViewFromStyle(int style) - { - return ViewFromInt(style & LVS_TYPEMASK); - } - - protected override UiDomValue EvaluateIdentifierCore(string id, UiDomRoot root, [In, Out] HashSet<(UiDomElement, GudlExpression)> depends_on) - { - if (property_aliases.TryGetValue(id, out string aliased)) - { - var value = base.EvaluateIdentifierCore(id, root, depends_on); - if (!value.Equals(UiDomUndefined.Instance)) - return value; - id = aliased; - } - - switch (id) - { - case "is_win32_list_view": - case "is_win32_listview": - return UiDomBoolean.True; - case "icon": - case "report": - case "details": - case "table": - case "smallicon": - case "small_icon": - case "list": - case "tile": - return EvaluateIdentifierCore("win32_view", Root, depends_on).EvaluateIdentifier(id, Root, depends_on); - case "win32_is_comctl6": - if (IsComCtl6Known) - return UiDomBoolean.FromBool(IsComCtl6); - return UiDomUndefined.Instance; - case "role": - case "control_type": - case "win32_view": - if (!IsComCtl6Known) - { - depends_on.Add((this, new IdentifierExpression("win32_is_comctl6"))); - return UiDomUndefined.Instance; - } - if (!IsComCtl6) - { - depends_on.Add((this, new IdentifierExpression("win32_style"))); - if (WindowStyleKnown) - { - return ViewFromStyle(WindowStyle); - } - return UiDomUndefined.Instance; - } - depends_on.Add((this, new IdentifierExpression("win32_view"))); - if (ViewKnown) - { - return ViewFromInt(ViewInt); - } - return UiDomUndefined.Instance; - case "win32_header_hwnd": - EvaluateIdentifierCore("win32_view", root, depends_on); - if (HasHeader()) - { - if (HeaderHwndKnown) - return new UiDomInt(HeaderHwnd.ToInt32()); - depends_on.Add((this, new IdentifierExpression("win32_header_hwnd"))); - } - if (ViewAsInt == -1) - { - return UiDomUndefined.Instance; - } - return UiDomBoolean.False; - case "win32_header": - depends_on.Add((this, new IdentifierExpression("win32_header"))); - if (!(Header is null)) - return Header; - return UiDomUndefined.Instance; - case "win32_top_index": - depends_on.Add((this, new IdentifierExpression("win32_view"))); - if (!HasTopIndex()) - return UiDomUndefined.Instance; - depends_on.Add((this, new IdentifierExpression("win32_top_index"))); - if (TopIndexKnown) - return new UiDomInt(TopIndex); - return UiDomUndefined.Instance; - case "win32_item_count": - depends_on.Add((this, new IdentifierExpression("win32_item_count"))); - if (ItemCountKnown) - return new UiDomInt(ItemCount); - return UiDomUndefined.Instance; - case "win32_count_per_page": - depends_on.Add((this, new IdentifierExpression("win32_count_per_page"))); - if (CountPerPageKnown) - return new UiDomInt(CountPerPage); - return UiDomUndefined.Instance; - } - - return base.EvaluateIdentifierCore(id, root, depends_on); - } - - private bool HasHeader() - { - return ViewAsInt == LV_VIEW_DETAILS; - } - - private bool HasTopIndex() - { - switch (ViewAsInt) - { - case LV_VIEW_ICON: - case LV_VIEW_SMALLICON: - case -1: - return false; - default: - return true; - } - } - - protected override void DumpProperties() - { - if (IsComCtl6Known) - { - Utils.DebugWriteLine($" win32_is_comctl6: {IsComCtl6}"); - if (IsComCtl6) - { - if (ViewKnown) - Utils.DebugWriteLine($" win32_view: {ViewFromInt(ViewInt)}"); - } - else - { - if (WindowStyleKnown) - Utils.DebugWriteLine($" win32_view: {ViewFromStyle(WindowStyle)}"); - } - } - if (HasHeader()) - { - if (HeaderHwndKnown) - Utils.DebugWriteLine($" win32_header_hwnd: {HeaderHwnd}"); - if (!(Header is null)) - Utils.DebugWriteLine($" win32_header: {Header}"); - } - if (HasTopIndex() && TopIndexKnown) - Utils.DebugWriteLine($" win32_top_index: {TopIndex}"); - if (ItemCountKnown) - Utils.DebugWriteLine($" win32_item_count: {ItemCount}"); - if (CountPerPageKnown) - Utils.DebugWriteLine($" win32_count_per_page: {CountPerPage}"); - base.DumpProperties(); - } - - protected override void WatchProperty(GudlExpression expression) - { - if (expression is IdentifierExpression id) - { - switch (id.Name) - { - case "win32_is_comctl6": - if (!CheckingComCtl6) - { - Utils.RunTask(CheckComCtl6()); - CheckingComCtl6 = true; - } - break; - case "win32_header_hwnd": - if (!CheckingHeaderHwnd) - { - Utils.RunTask(CheckHeaderHwnd()); - CheckingHeaderHwnd = true; - } - break; - case "win32_view": - PollProperty(expression, RefreshView, 200); - break; - case "win32_top_index": - PollProperty(expression, RefreshTopIndex, 200); - break; - case "win32_item_count": - PollProperty(expression, RefreshItemCount, 200); - break; - case "win32_count_per_page": - PollProperty(expression, RefreshCountPerPage, 200); - break; - } - } - base.WatchProperty(expression); - } - - private async Task CheckHeaderHwnd() - { - var result = await SendMessageAsync(Hwnd, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero); - - if (GetAncestor(result, GA_PARENT) != Hwnd) - result = IntPtr.Zero; - - CheckingHeaderHwnd = false; - HeaderHwndKnown = true; - HeaderHwnd = result; - - PropertyChanged("win32_header_hwnd"); - } - - protected override void UnwatchProperty(GudlExpression expression) - { - if (expression is IdentifierExpression id) - { - switch (id.Name) - { - case "win32_view": - EndPollProperty(expression); - ViewKnown = false; - break; - case "win32_top_index": - EndPollProperty(expression); - TopIndexKnown = false; - break; - case "win32_item_count": - EndPollProperty(expression); - ItemCountKnown = false; - break; - case "win32_count_per_page": - EndPollProperty(expression); - CountPerPageKnown = false; - break; - } - } - base.UnwatchProperty(expression); - } - - private async Task CheckComCtl6() - { - // comctl6 will return -1 to indicate error, earlier versions should not recognize the message and return 0 - var result = await SendMessageAsync(Hwnd, LVM_SETVIEW, new IntPtr(-1), IntPtr.Zero); - - if (result == IntPtr.Zero) - { - IsComCtl6 = false; - IsComCtl6Known = true; - PropertyChanged("win32_is_comctl6", "false"); - } - else if (result == new IntPtr(-1)) - { - IsComCtl6 = true; - IsComCtl6Known = true; - PropertyChanged("win32_is_comctl6", "true"); - } - } - - private async Task RefreshView() - { - var result = await SendMessageAsync(Hwnd, LVM_GETVIEW, IntPtr.Zero, IntPtr.Zero); - - if (!ViewKnown || ViewInt != result.ToInt32()) - { - ViewKnown = true; - ViewInt = result.ToInt32(); - PropertyChanged("win32_view", ViewFromInt(ViewInt)); - } - } - - private async Task RefreshTopIndex() - { - var result = await SendMessageAsync(Hwnd, LVM_GETTOPINDEX, IntPtr.Zero, IntPtr.Zero); - - if (!TopIndexKnown || TopIndex != result.ToInt32()) - { - TopIndexKnown = true; - TopIndex = result.ToInt32(); - PropertyChanged("win32_top_index", TopIndex); - } - } - - private async Task RefreshItemCount() - { - var result = await SendMessageAsync(Hwnd, LVM_GETITEMCOUNT, IntPtr.Zero, IntPtr.Zero); - - if (!ItemCountKnown || ItemCount != result.ToInt32()) - { - ItemCountKnown = true; - ItemCount = result.ToInt32(); - PropertyChanged("win32_item_count", ItemCount); - } - } - - private async Task RefreshCountPerPage() - { - var result = await SendMessageAsync(Hwnd, LVM_GETCOUNTPERPAGE, IntPtr.Zero, IntPtr.Zero); - - if (!CountPerPageKnown || CountPerPage != result.ToInt32()) - { - CountPerPageKnown = true; - CountPerPage = result.ToInt32(); - PropertyChanged("win32_count_per_page", CountPerPage); - } - } - - protected override void PropertiesChanged(HashSet changed_properties) - { - if (changed_properties.Contains(new IdentifierExpression("recurse")) || - changed_properties.Contains(new IdentifierExpression("win32_header_hwnd")) || - changed_properties.Contains(new IdentifierExpression("win32_top_index"))) - { - QueueRefreshChildren(this, new IdentifierExpression("recurse")); - } - base.PropertiesChanged(changed_properties); - } - - public int ViewAsInt - { - get - { - if (IsComCtl6Known) - { - if (IsComCtl6) - { - if (ViewKnown) - return ViewInt; - } - else - { - if (WindowStyleKnown) - return WindowStyle & LVS_TYPEMASK; - } - } - return -1; - } - } - - private void RefreshChildren() - { - if (GetDeclaration("recurse").ToBool()) - { - if (!watching_children) - { - watching_children = true; - HeaderHwndWatcher = NotifyPropertyChanged(new IdentifierExpression("win32_header_hwnd"), QueueRefreshChildren); - ChildItemStartWatcher = NotifyPropertyChanged(new IdentifierExpression("win32_child_item_start"), QueueRefreshChildren); - ChildItemCountWatcher = NotifyPropertyChanged(new IdentifierExpression("win32_child_item_count"), QueueRefreshChildren); - UseVirtualScrollBars = true; - } - if (!HasHeader() || HeaderHwnd == IntPtr.Zero) - { - if (!(Header is null)) - { - RemoveChild(Children.IndexOf(Header)); - Header = null; - PropertyChanged("win32_header", "undefined"); - } - } - else if (Header is null || HeaderHwnd != Header.Hwnd) - { - if (!(Header is null)) - RemoveChild(Children.IndexOf(Header)); - Header = new Win32Element("Win32Element", HeaderHwnd, Root); - AddChild(Children.Count, Header); - PropertyChanged("win32_header", Header); - } - if (GetDeclaration("win32_child_item_start").TryToInt(out int start) && - GetDeclaration("win32_child_item_count").TryToInt(out int count) && - (start != child_item_start || count != child_item_count)) - { - int old_start = child_item_start; - int old_count = child_item_count; - int old_end = child_item_start + child_item_count; - child_item_start = start; - child_item_count = count; - int child_item_end = child_item_start + child_item_count; - - if (child_item_start >= old_end || old_start >= child_item_end) - { - // Disjoint sets - RemoveChildRange(0, old_count); - AddChildItemRange(0, child_item_count, child_item_start); - } - else - { - if (child_item_end > old_end) - { - AddChildItemRange(old_count, child_item_end - old_end, old_end); - } - else if (child_item_end < old_end) - { - RemoveChildRange(child_item_end - old_start, old_end - child_item_end); - } - if (child_item_start > old_start) - { - RemoveChildRange(0, child_item_start - old_start); - } - else if (child_item_start < old_start) - { - AddChildItemRange(0, old_start - child_item_start, child_item_start); - } - } - } - } - else - { - if (watching_children) - { - watching_children = false; - UseVirtualScrollBars = false; - if (!(Header is null)) - { - RemoveChild(Children.IndexOf(Header)); - Header = null; - PropertyChanged("win32_header", "undefined"); - } - if (!(HeaderHwndWatcher is null)) - { - HeaderHwndWatcher.Dispose(); - HeaderHwndWatcher = null; - } - if (!(ChildItemStartWatcher is null)) - { - ChildItemStartWatcher.Dispose(); - ChildItemStartWatcher = null; - } - if (!(ChildItemCountWatcher is null)) - { - ChildItemCountWatcher.Dispose(); - ChildItemCountWatcher = null; - } - while (Children.Count != 0) - { - RemoveChild(Children.Count - 1); - } - child_item_start = 0; - child_item_count = 0; - } - } - refreshing_children = false; - } - - private void RemoveChildRange(int child_start, int child_count) - { - for (int i = child_count - 1; i >= 0; i--) - { - RemoveChild(child_start + i); - } - } - - private void AddChildItemRange(int child_start, int child_count, int item_start) - { - for (int i = 0; i < child_count; i++) - { - AddChild(child_start + i, new Win32ListViewItem(this, item_start + i)); - } - } - - private void QueueRefreshChildren(UiDomElement element, GudlExpression property) - { - if (!refreshing_children) - { - refreshing_children = true; - Utils.RunIdle(RefreshChildren); - } - } - - double yremainder; - - public override Task GetVScrollMinimumIncrement() - { - return Task.FromResult(1.0); - } - - public override async Task OffsetVScroll(double ofs) - { - switch (ViewAsInt) - { - case LV_VIEW_DETAILS: - { - int pos = (int)await SendMessageAsync(Hwnd, LVM_GETTOPINDEX, IntPtr.Zero, IntPtr.Zero); - - int total_items = (int)await SendMessageAsync(Hwnd, LVM_GETITEMCOUNT, IntPtr.Zero, IntPtr.Zero); - - int count_per_page = (int)await SendMessageAsync(Hwnd, LVM_GETCOUNTPERPAGE, IntPtr.Zero, IntPtr.Zero); - - int max_pos = total_items - count_per_page; - - double new_pos = pos + yremainder + ofs; - - int pos_ofs = (int)Math.Truncate(new_pos - pos); - - int new_pos_int = pos + pos_ofs; - - if (new_pos_int != pos) - { - if (new_pos_int < 0) - new_pos = new_pos_int = 0; - else if (new_pos_int > max_pos) - new_pos = new_pos_int = max_pos; - } - - if (new_pos_int != pos) - { - if (remote_process_memory is null) - remote_process_memory = Win32RemoteProcessMemory.FromPid(Pid); - RECT rc = new RECT(); - rc.left = LVIR_SELECTBOUNDS; - IntPtr result; - using (var memory = remote_process_memory.WriteAlloc(rc)) - { - result = await SendMessageAsync(Hwnd, LVM_GETITEMRECT, (IntPtr)pos, new IntPtr((long)memory.Address)); - rc = memory.Read(); - } - - await SendMessageAsync(Hwnd, LVM_SCROLL, IntPtr.Zero, new IntPtr((rc.bottom - rc.top) * pos_ofs)); - } - - yremainder = new_pos - new_pos_int; - } - break; - default: - // FIXME - break; - } - } - } -} diff --git a/xalia/Uia/Win32/Win32ListViewItem.cs b/xalia/Uia/Win32/Win32ListViewItem.cs deleted file mode 100644 index 8201f9f..0000000 --- a/xalia/Uia/Win32/Win32ListViewItem.cs +++ /dev/null @@ -1,235 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using Xalia.Gudl; -using Xalia.Interop; -using Xalia.UiDom; -using static Xalia.Interop.Win32; - -namespace Xalia.Uia.Win32 -{ - internal class Win32ListViewItem : UiDomElement - { - struct rect_info - { - public bool Known; - public int X; - public int Y; - public int Width; - public int Height; - } - - private rect_info[] rect_infos = new rect_info[4]; - - private Win32RemoteProcessMemory remote_process_memory; - - public Win32ListViewItem(Win32ListView parent, int index) : base($"Win32ListViewItem-{parent.Hwnd}-{index}", parent.Root) - - { - Parent = parent; - Hwnd = parent.Hwnd; - Index = index; - } - - protected override void SetAlive(bool value) - { - if (!value) - { - if (!(remote_process_memory is null)) - { - remote_process_memory.Unref(); - remote_process_memory = null; - } - } - base.SetAlive(value); - } - - public new Win32ListView Parent { get; } - public IntPtr Hwnd { get; } - public int Index { get; } - - protected override UiDomValue EvaluateIdentifierCore(string id, UiDomRoot root, [In, Out] HashSet<(UiDomElement, GudlExpression)> depends_on) - { - switch (id) - { - case "is_win32_subelement": - case "is_win32_listview_item": - case "item": - case "visible": - case "enabled": - return UiDomBoolean.True; - case "list_item": - return UiDomBoolean.FromBool(!Parent.EvaluateIdentifier("details", root, depends_on).ToBool()); - case "table_row": - return UiDomBoolean.FromBool(Parent.EvaluateIdentifier("details", root, depends_on).ToBool()); - case "win32_bounds_x": - case "win32_bounds_y": - case "win32_bounds_width": - case "win32_bounds_height": - case "win32_icon_x": - case "win32_icon_y": - case "win32_icon_width": - case "win32_icon_height": - case "win32_label_x": - case "win32_label_y": - case "win32_label_width": - case "win32_label_height": - case "win32_selectbounds_x": - case "win32_selectbounds_y": - case "win32_selectbounds_width": - case "win32_selectbounds_height": - string bounds_name = id.Substring(0, id.LastIndexOf('_')); - int bounds_type = BoundsTypeFromName(bounds_name); - string component_name = id.Substring(id.LastIndexOf('_') + 1); - depends_on.Add((this, new IdentifierExpression(bounds_name))); - if (rect_infos[bounds_type].Known) - { - switch (component_name) - { - case "x": - return new UiDomInt(rect_infos[bounds_type].X); - case "y": - return new UiDomInt(rect_infos[bounds_type].Y); - case "width": - return new UiDomInt(rect_infos[bounds_type].Width); - case "height": - return new UiDomInt(rect_infos[bounds_type].Height); - } - } - return UiDomUndefined.Instance; - } - - return base.EvaluateIdentifierCore(id, root, depends_on); - } - - private int BoundsTypeFromName(string bounds_name) - { - switch (bounds_name) - { - case "win32_bounds": - return LVIR_BOUNDS; - case "win32_icon": - return LVIR_ICON; - case "win32_label": - return LVIR_LABEL; - case "win32_selectbounds": - return LVIR_SELECTBOUNDS; - } - return - 1; - } - - protected override void DumpProperties() - { - foreach (string bounds_name in new string[] { "win32_bounds", "win32_icon", "win32_label", "win32_selectbounds" }) - { - int bounds_type = BoundsTypeFromName(bounds_name); - if (rect_infos[bounds_type].Known) - { - Utils.DebugWriteLine($" {this}.{bounds_name}_x: {rect_infos[bounds_type].X}"); - Utils.DebugWriteLine($" {this}.{bounds_name}_y: {rect_infos[bounds_type].Y}"); - Utils.DebugWriteLine($" {this}.{bounds_name}_width: {rect_infos[bounds_type].Width}"); - Utils.DebugWriteLine($" {this}.{bounds_name}_height: {rect_infos[bounds_type].Height}"); - } - } - base.DumpProperties(); - } - - protected override void WatchProperty(GudlExpression expression) - { - if (expression is IdentifierExpression id) - { - switch (id.Name) - { - case "win32_bounds": - PollProperty(expression, RefreshBounds, 2000); - break; - case "win32_icon": - PollProperty(expression, RefreshIconRect, 2000); - break; - case "win32_label": - PollProperty(expression, RefreshLabelRect, 2000); - break; - case "win32_selectbounds": - PollProperty(expression, RefreshSelectBounds, 2000); - break; - } - } - base.WatchProperty(expression); - } - - protected override void UnwatchProperty(GudlExpression expression) - { - if (expression is IdentifierExpression id) - { - int bounds_type = BoundsTypeFromName(id.Name); - if (bounds_type != -1) - { - EndPollProperty(expression); - rect_infos[bounds_type].Known = false; - } - } - base.UnwatchProperty(expression); - } - - private async Task RefreshRect(int bounds_type, string prop_name) - { - if (remote_process_memory is null) - { - GetWindowThreadProcessId(Hwnd, out var pid); - remote_process_memory = Win32RemoteProcessMemory.FromPid(pid); - } - RECT rc = new RECT(); - rc.left = bounds_type; - IntPtr result; - using (var memory = remote_process_memory.WriteAlloc(rc)) - { - result = await SendMessageAsync(Hwnd, LVM_GETITEMRECT, (IntPtr)Index, new IntPtr((long)memory.Address)); - rc = memory.Read(); - } - if (result == IntPtr.Zero) - { - if (rect_infos[bounds_type].Known) - { - rect_infos[bounds_type].Known = false; - PropertyChanged(prop_name, "undefined"); - } - } - else - { - - if (!rect_infos[bounds_type].Known || rc.left != rect_infos[bounds_type].X || - rc.top != rect_infos[bounds_type].Y || - rc.right - rc.left != rect_infos[bounds_type].Width || rc.bottom - rc.top != rect_infos[bounds_type].Height) - { - rect_infos[bounds_type].Known = true; - rect_infos[bounds_type].X = rc.left; - rect_infos[bounds_type].Y = rc.top; - rect_infos[bounds_type].Width = rc.right - rc.left; - rect_infos[bounds_type].Height = rc.bottom - rc.top; - PropertyChanged(prop_name, $"{rect_infos[bounds_type].X},{rect_infos[bounds_type].Y} {rect_infos[bounds_type].Width}x{rect_infos[bounds_type].Height}"); - } - } - } - - private Task RefreshBounds() - { - return RefreshRect(LVIR_BOUNDS, "win32_bounds"); - } - - private Task RefreshIconRect() - { - return RefreshRect(LVIR_ICON, "win32_icon"); - } - - private Task RefreshLabelRect() - { - return RefreshRect(LVIR_LABEL, "win32_label"); - } - - private Task RefreshSelectBounds() - { - return RefreshRect(LVIR_SELECTBOUNDS, "win32_selectbounds"); - } - } -} diff --git a/xalia/Uia/Win32/Win32TabControl.cs b/xalia/Uia/Win32/Win32TabControl.cs deleted file mode 100644 index 5b980a6..0000000 --- a/xalia/Uia/Win32/Win32TabControl.cs +++ /dev/null @@ -1,261 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using Xalia.Gudl; -using Xalia.Interop; -using Xalia.UiDom; -using static Xalia.Interop.Win32; - -namespace Xalia.Uia.Win32 -{ - internal class Win32TabControl : Win32Element - { - public Win32TabControl(IntPtr hwnd, UiaConnection root) : base("Win32TabControl", hwnd, root) - { - } - - static Win32TabControl() - { - string[] aliases = { - "selection_index", "win32_selection_index", - "item_count", "win32_item_count", - }; - property_aliases = new Dictionary(aliases.Length / 2); - for (int i = 0; i < aliases.Length; i += 2) - { - property_aliases[aliases[i]] = aliases[i + 1]; - } - } - - static Dictionary property_aliases; - private static readonly UiDomValue role = new UiDomEnum(new[] { "tab", "page_tab_list", "pagetablist" }); - - private Win32RemoteProcessMemory remote_process_memory; - public bool SelectionIndexKnown; - public int SelectionIndex; - private bool ItemCountKnown; - private int ItemCount; - private bool refreshing_children; - private bool watching_children; - private IDisposable ItemCountWatcher; - private int num_child_items; - - protected override void SetAlive(bool value) - { - if (!value) - { - if (!(ItemCountWatcher is null)) - { - ItemCountWatcher.Dispose(); - ItemCountWatcher = null; - } - if (!(remote_process_memory is null)) - { - remote_process_memory.Unref(); - remote_process_memory = null; - } - } - base.SetAlive(value); - } - - protected override UiDomValue EvaluateIdentifierCore(string id, UiDomRoot root, [In, Out] HashSet<(UiDomElement, GudlExpression)> depends_on) - { - if (property_aliases.TryGetValue(id, out string aliased)) - { - var value = base.EvaluateIdentifierCore(id, root, depends_on); - if (!value.Equals(UiDomUndefined.Instance)) - return value; - id = aliased; - } - - switch (id) - { - case "is_win32_tab_control": - case "is_win32_tabcontrol": - case "tab": - case "page_tab_list": - case "pagetablist": - return UiDomBoolean.True; - case "role": - case "control_type": - return role; - case "win32_selection_index": - depends_on.Add((this, new IdentifierExpression("win32_selection_index"))); - if (SelectionIndexKnown) - return new UiDomInt(SelectionIndex); - return UiDomUndefined.Instance; - case "win32_item_count": - depends_on.Add((this, new IdentifierExpression("win32_item_count"))); - if (ItemCountKnown) - return new UiDomInt(ItemCount); - return UiDomUndefined.Instance; - default: - break; - } - - return base.EvaluateIdentifierCore(id, root, depends_on); - } - - protected override void DumpProperties() - { - if (SelectionIndexKnown) - Utils.DebugWriteLine($" win32_selection_index: {SelectionIndex}"); - if (ItemCountKnown) - Utils.DebugWriteLine($" win32_item_count: {ItemCount}"); - base.DumpProperties(); - } - - protected override void WatchProperty(GudlExpression expression) - { - if (expression is IdentifierExpression id) - { - switch (id.Name) - { - case "win32_selection_index": - PollProperty(expression, RefreshSelectionIndex, 200); - break; - case "win32_item_count": - PollProperty(expression, RefreshItemCount, 200); - break; - } - } - base.WatchProperty(expression); - } - - protected override void UnwatchProperty(GudlExpression expression) - { - if (expression is IdentifierExpression id) - { - switch (id.Name) - { - case "win32_selection_index": - EndPollProperty(expression); - SelectionIndexKnown = false; - break; - case "win32_item_count": - EndPollProperty(expression); - ItemCountKnown = false; - break; - } - } - base.UnwatchProperty(expression); - } - - private async Task RefreshSelectionIndex() - { - IntPtr index = await SendMessageAsync(Hwnd, TCM_GETCURSEL, IntPtr.Zero, IntPtr.Zero); - int i = index.ToInt32(); - - bool known = i >= 0; - - if (known != SelectionIndexKnown || i != SelectionIndex) - { - if (known) - { - SelectionIndexKnown = true; - SelectionIndex = i; - PropertyChanged("win32_selection_index", i); - } - else - { - SelectionIndexKnown = false; - PropertyChanged("win32_selection_index", "undefined"); - } - } - } - - private async Task RefreshItemCount() - { - IntPtr index = await SendMessageAsync(Hwnd, TCM_GETITEMCOUNT, IntPtr.Zero, IntPtr.Zero); - int i = index.ToInt32(); - - bool known = i >= 0; - - if (known != ItemCountKnown || i != ItemCount) - { - if (known) - { - ItemCountKnown = true; - ItemCount = i; - PropertyChanged("win32_item_count", i); - } - else - { - ItemCountKnown = false; - PropertyChanged("win32_item_count", "undefined"); - } - } - } - - protected override void PropertiesChanged(HashSet changed_properties) - { - if (changed_properties.Contains(new IdentifierExpression("recurse")) || - changed_properties.Contains(new IdentifierExpression("win32_item_count"))) - { - QueueRefreshChildren(this, new IdentifierExpression("recurse")); - } - base.PropertiesChanged(changed_properties); - } - - private void QueueRefreshChildren(UiDomElement element, GudlExpression identifierExpression) - { - if (!refreshing_children) - { - refreshing_children = true; - Utils.RunIdle(RefreshChildren); - } - } - - private void RefreshChildren() - { - if (GetDeclaration("recurse").ToBool()) - { - if (!watching_children) - { - watching_children = true; - ItemCountWatcher = NotifyPropertyChanged(new IdentifierExpression("win32_item_count"), QueueRefreshChildren); - } - if (ItemCountKnown && num_child_items != ItemCount) - { - if (ItemCount > num_child_items) - AddChildRange(num_child_items, ItemCount); - else - RemoveChildRange(ItemCount, num_child_items); - num_child_items = ItemCount; - } - } - else - { - if (watching_children) - { - watching_children = false; - if (!(ItemCountWatcher is null)) - { - ItemCountWatcher.Dispose(); - ItemCountWatcher = null; - } - RemoveChildRange(0, num_child_items); - num_child_items = 0; - } - } - refreshing_children = false; - } - - private void AddChildRange(int start_index, int end_index) - { - for (int i=start_index; i=start_index; i--) - { - RemoveChild(i); - } - } - } -} diff --git a/xalia/Uia/Win32/Win32TabControlItem.cs b/xalia/Uia/Win32/Win32TabControlItem.cs deleted file mode 100644 index 071d01c..0000000 --- a/xalia/Uia/Win32/Win32TabControlItem.cs +++ /dev/null @@ -1,174 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using Xalia.Gudl; -using Xalia.Interop; -using Xalia.UiDom; -using static Xalia.Interop.Win32; - -namespace Xalia.Uia.Win32 -{ - internal class Win32TabControlItem : UiDomElement - { - public Win32TabControlItem(Win32TabControl parent, int index) : base($"Win32TabControlItem-{parent.Hwnd}-{index}", parent.Root) - { - Parent = parent; - Hwnd = parent.Hwnd; - Index = index; - } - - protected override void SetAlive(bool value) - { - if (!value) - { - if (!(remote_process_memory is null)) - { - remote_process_memory.Unref(); - remote_process_memory = null; - } - } - base.SetAlive(value); - } - - private static readonly UiDomValue role = new UiDomEnum(new[] { "tab_item", "tabitem", "page_tab", "pagetab" }); - - private Win32RemoteProcessMemory remote_process_memory; - private bool BoundsKnown; - private int X; - private int Y; - private int Width; - private int Height; - - public new Win32TabControl Parent { get; } - public IntPtr Hwnd { get; } - public int Index { get; } - - protected override UiDomValue EvaluateIdentifierCore(string id, UiDomRoot root, [In, Out] HashSet<(UiDomElement, GudlExpression)> depends_on) - { - switch (id) - { - case "is_win32_subelement": - case "is_win32_tabcontrol_item": - case "tab_item": - case "tabitem": - case "page_tab": - case "pagetab": - case "visible": - case "enabled": - return UiDomBoolean.True; - case "role": - case "control_type": - return role; - case "selected": - depends_on.Add((Parent, new IdentifierExpression("win32_selection_index"))); - if (Parent.SelectionIndexKnown) - return UiDomBoolean.FromBool(Index == Parent.SelectionIndex); - return UiDomUndefined.Instance; - case "win32_x": - case "win32_y": - case "win32_width": - case "win32_height": - depends_on.Add((this, new IdentifierExpression("win32_bounds"))); - if (BoundsKnown) - { - switch (id) - { - case "win32_x": - return new UiDomInt(X); - case "win32_y": - return new UiDomInt(Y); - case "win32_width": - return new UiDomInt(Width); - case "win32_height": - return new UiDomInt(Height); - } - } - return UiDomUndefined.Instance; - } - return base.EvaluateIdentifierCore(id, root, depends_on); - } - - protected override void DumpProperties() - { - if (Parent.SelectionIndexKnown) - Utils.DebugWriteLine($" selected: {Index == Parent.SelectionIndex}"); - if (BoundsKnown) - { - Utils.DebugWriteLine($" win32_x: {X}"); - Utils.DebugWriteLine($" win32_y: {Y}"); - Utils.DebugWriteLine($" win32_width: {Width}"); - Utils.DebugWriteLine($" win32_height: {Height}"); - } - base.DumpProperties(); - } - - protected override void WatchProperty(GudlExpression expression) - { - if (expression is IdentifierExpression id) - { - switch (id.Name) - { - case "win32_bounds": - PollProperty(expression, RefreshBounds, 2000); - break; - } - } - base.WatchProperty(expression); - } - - protected override void UnwatchProperty(GudlExpression expression) - { - if (expression is IdentifierExpression id) - { - switch (id.Name) - { - case "win32_bounds": - EndPollProperty(expression); - BoundsKnown = false; - break; - } - } - base.UnwatchProperty(expression); - } - - private async Task RefreshBounds() - { - if (remote_process_memory is null) - { - GetWindowThreadProcessId(Hwnd, out var pid); - remote_process_memory = Win32RemoteProcessMemory.FromPid(pid); - } - - RECT rc; - bool known; - using (var memory = remote_process_memory.Alloc()) - { - IntPtr result = await SendMessageAsync(Hwnd, TCM_GETITEMRECT, new IntPtr(Index), new IntPtr((long)memory.Address)); - known = result != IntPtr.Zero; - rc = memory.Read(); - } - - if (known != BoundsKnown || X != rc.left || Y != rc.top || - Width != rc.right - rc.left || Height != rc.bottom - rc.top) - { - BoundsKnown = known; - if (known) - { - X = rc.left; - Y = rc.top; - Width = rc.right - rc.left; - Height = rc.bottom - rc.top; - - if (MatchesDebugCondition()) - Utils.DebugWriteLine($"{this}.win32_bounds: {X},{Y} {Width}x{Height}"); - PropertyChanged("win32_bounds"); - } - else - { - PropertyChanged("win32_bounds", "undefined"); - } - } - } - } -} diff --git a/xalia/Uia/Win32/Win32Trackbar.cs b/xalia/Uia/Win32/Win32Trackbar.cs deleted file mode 100644 index 8e8638d..0000000 --- a/xalia/Uia/Win32/Win32Trackbar.cs +++ /dev/null @@ -1,183 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using Xalia.Gudl; -using Xalia.UiDom; - -using static Xalia.Interop.Win32; - -namespace Xalia.Uia.Win32 -{ - internal class Win32Trackbar : Win32Element - { - public Win32Trackbar(IntPtr hwnd, UiaConnection root) : base("Win32Trackbar", hwnd, root) { } - - static UiDomEnum role = new UiDomEnum(new string[] { "slider" }); - static Win32Trackbar() - { - string[] aliases = { - "vertical", "win32_vertical", - "horizontal", "win32_horizontal", - "minimum_increment", "win32_line_size", - "line_size", "win32_line_size", - }; - property_aliases = new Dictionary(aliases.Length / 2); - for (int i=0; i property_aliases; - - private bool LineSizeKnown; - private int LineSize; - - protected override UiDomValue EvaluateIdentifierCore(string id, UiDomRoot root, [In, Out] HashSet<(UiDomElement, GudlExpression)> depends_on) - { - if (property_aliases.TryGetValue(id, out string aliased)) - { - var value = base.EvaluateIdentifierCore(id, root, depends_on); - if (!value.Equals(UiDomUndefined.Instance)) - return value; - id = aliased; - } - - switch (id) - { - case "is_win32_trackbar": - case "slider": - return UiDomBoolean.True; - case "role": - case "control_type": - return role; - case "win32_vertical": - depends_on.Add((this, new IdentifierExpression("win32_style"))); - if (WindowStyleKnown) - return UiDomBoolean.FromBool((WindowStyle & TBS_VERT) == TBS_VERT); - return UiDomUndefined.Instance; - case "win32_horizontal": - depends_on.Add((this, new IdentifierExpression("win32_style"))); - if (WindowStyleKnown) - return UiDomBoolean.FromBool((WindowStyle & TBS_VERT) == 0); - return UiDomUndefined.Instance; - case "win32_line_size": - depends_on.Add((this, new IdentifierExpression("win32_line_size"))); - if (LineSizeKnown) - return new UiDomInt(LineSize); - return UiDomUndefined.Instance; - } - return base.EvaluateIdentifierCore(id, root, depends_on); - } - - protected override void DumpProperties() - { - if (WindowStyleKnown) - { - Utils.DebugWriteLine($" win32_vertical: {(WindowStyle & TBS_VERT) == TBS_VERT}"); - Utils.DebugWriteLine($" win32_horizontal: {(WindowStyle & TBS_VERT) == 0}"); - } - base.DumpProperties(); - } - - protected override void WatchProperty(GudlExpression expression) - { - if (expression is IdentifierExpression id) - { - switch (id.Name) - { - case "win32_line_size": - PollProperty(expression, RefreshLineSize, 2000); - break; - } - } - base.WatchProperty(expression); - } - - protected override void UnwatchProperty(GudlExpression expression) - { - if (expression is IdentifierExpression id) - { - switch (id.Name) - { - case "win32_line_size": - EndPollProperty(expression); - break; - } - } - base.UnwatchProperty(expression); - } - - private async Task RefreshLineSize() - { - var result = (int)await SendMessageAsync(Hwnd, TBM_GETLINESIZE, IntPtr.Zero, IntPtr.Zero); - if (result == 0) - result = 1; - if (!LineSizeKnown || LineSize != result) - { - LineSizeKnown = true; - LineSize = result; - - if (MatchesDebugCondition()) - Utils.DebugWriteLine($"{this}.win32_line_size: {LineSize}"); - - PropertyChanged("win32_line_size"); - } - } - - public override async Task GetMinimumIncrement() - { - var result = (int)await SendMessageAsync(Hwnd, TBM_GETLINESIZE, IntPtr.Zero, IntPtr.Zero); - if (result == 0) - result = 1; - return result; - } - - private double remainder; - - public override async Task OffsetValue(double ofs) - { - int current_pos = (int)await SendMessageAsync(Hwnd, TBM_GETPOS, IntPtr.Zero, IntPtr.Zero); - - double new_pos = current_pos + remainder + ofs; - - int pos_ofs = (int)Math.Truncate(new_pos - current_pos); - - int new_pos_int = current_pos + pos_ofs; - - if (new_pos_int != current_pos) - { - if (pos_ofs < 0) - { - int min = (int)await SendMessageAsync(Hwnd, TBM_GETRANGEMIN, IntPtr.Zero, IntPtr.Zero); - - if (new_pos_int < min) - new_pos = new_pos_int = min; - } - else - { - int max = (int)await SendMessageAsync(Hwnd, TBM_GETRANGEMAX, IntPtr.Zero, IntPtr.Zero); - - if (new_pos_int > max) - new_pos = new_pos_int = max; - } - } - - if (new_pos_int != current_pos) - { - await SendMessageAsync(Hwnd, TBM_SETPOS, new IntPtr(1), new IntPtr(new_pos_int)); - IntPtr parent = GetAncestor(Hwnd, GA_PARENT); - bool vertical = ((int)GetWindowLong(Hwnd, GWL_STYLE) & TBS_VERT) == TBS_VERT; - await SendMessageAsync(parent, vertical ? WM_VSCROLL : WM_HSCROLL, - MAKEWPARAM(TB_THUMBTRACK, (ushort)new_pos_int), Hwnd); - await SendMessageAsync(parent, vertical ? WM_VSCROLL : WM_HSCROLL, - MAKEWPARAM(TB_THUMBPOSITION, (ushort)new_pos_int), Hwnd); - await SendMessageAsync(parent, vertical ? WM_VSCROLL : WM_HSCROLL, - MAKEWPARAM(TB_ENDTRACK, 0), Hwnd); - } - - remainder = new_pos - new_pos_int; - } - } -} diff --git a/xalia/Uia/Win32/Win32VirtualScrollbar.cs b/xalia/Uia/Win32/Win32VirtualScrollbar.cs deleted file mode 100644 index c04be39..0000000 --- a/xalia/Uia/Win32/Win32VirtualScrollbar.cs +++ /dev/null @@ -1,151 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using Xalia.Gudl; -using Xalia.UiDom; -using static Xalia.Interop.Win32; - -namespace Xalia.Uia.Win32 -{ - public class Win32VirtualScrollbar : UiDomElement - { - public Win32VirtualScrollbar(Win32Element parent, bool vertical) : - base($"Win32VirtualScrollbar-{parent.Hwnd}-{(vertical ? 'V' : 'H')}", parent.Root) - { - Parent = parent; - Vertical = vertical; - Hwnd = parent.Hwnd; - } - - static UiDomEnum role = new UiDomEnum(new string[] { "scroll_bar", "scrollbar" }); - private bool MinimumIncrementKnown; - private double MinimumIncrement; - - static Win32VirtualScrollbar() - { - string[] aliases = { - "vertical", "win32_vertical", - "horizontal", "win32_horizontal", - "cxvscroll", "win32_cxvscroll", - "cyhscroll", "win32_cyhscroll", - "minimum_increment", "win32_minimum_increment", - }; - property_aliases = new Dictionary(aliases.Length / 2); - for (int i=0; i property_aliases; - - public new Win32Element Parent { get; } - public bool Vertical { get; } - public IntPtr Hwnd { get; } - - protected override UiDomValue EvaluateIdentifierCore(string id, UiDomRoot root, [In, Out] HashSet<(UiDomElement, GudlExpression)> depends_on) - { - if (property_aliases.TryGetValue(id, out string aliased)) - { - var value = base.EvaluateIdentifierCore(id, root, depends_on); - if (!value.Equals(UiDomUndefined.Instance)) - return value; - id = aliased; - } - - switch (id) - { - case "is_win32_subelement": - case "is_win32_virtual_scrollbar": - case "scroll_bar": - case "scrollbar": - case "visible": - case "enabled": - return UiDomBoolean.True; - case "role": - case "control_type": - return role; - case "win32_vertical": - return UiDomBoolean.FromBool(Vertical); - case "win32_horizontal": - return UiDomBoolean.FromBool(!Vertical); - case "win32_cxvscroll": - return new UiDomInt(GetSystemMetrics(SM_CXVSCROLL)); - case "win32_cyhscroll": - return new UiDomInt(GetSystemMetrics(SM_CYHSCROLL)); - case "win32_minimum_increment": - depends_on.Add((this, new IdentifierExpression("win32_minimum_increment"))); - if (MinimumIncrementKnown) - return new UiDomDouble(MinimumIncrement); - return UiDomUndefined.Instance; - } - - return base.EvaluateIdentifierCore(id, root, depends_on); - } - - protected override void DumpProperties() - { - if (MinimumIncrementKnown) - Utils.DebugWriteLine($" win32_minimum_increment: {MinimumIncrement}"); - base.DumpProperties(); - } - - protected override void WatchProperty(GudlExpression expression) - { - if (expression is IdentifierExpression id) - { - switch (id.Name) - { - case "win32_minimum_increment": - PollProperty(expression, RefreshMinimumIncrement, 200); - break; - } - } - base.WatchProperty(expression); - } - - protected override void UnwatchProperty(GudlExpression expression) - { - if (expression is IdentifierExpression id) - { - switch (id.Name) - { - case "win32_minimum_increment": - EndPollProperty(expression); - MinimumIncrementKnown = false; - break; - } - } - base.UnwatchProperty(expression); - } - - private async Task RefreshMinimumIncrement() - { - var minimum_increment = await GetMinimumIncrement(); - - if (!MinimumIncrementKnown || minimum_increment != MinimumIncrement) - { - MinimumIncrementKnown = true; - MinimumIncrement = minimum_increment; - PropertyChanged("win32_minimum_increment", minimum_increment); - } - } - - public override Task GetMinimumIncrement() - { - if (Vertical) - return Parent.GetVScrollMinimumIncrement(); - else - return Parent.GetHScrollMinimumIncrement(); - } - - public override Task OffsetValue(double ofs) - { - if (Vertical) - return Parent.OffsetVScroll(ofs); - else - return Parent.OffsetHScroll(ofs); - } - } -} diff --git a/xalia/Win32/AccessibleProvider.cs b/xalia/Win32/AccessibleProvider.cs index f19c281..b07c335 100644 --- a/xalia/Win32/AccessibleProvider.cs +++ b/xalia/Win32/AccessibleProvider.cs @@ -1,5 +1,4 @@ -using Accessibility; -using System; +using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; @@ -495,7 +494,7 @@ private async Task FetchDefaultAction() { if (old_change_count != _defaultActionChangeCount) return null; - return IAccessible.accDefaultAction[ChildId]; + return IAccessible.get_accDefaultAction(ChildId); }, Tid+1); } catch (Exception e) @@ -529,7 +528,7 @@ private async Task FetchState(bool polling) { if (old_change_count != _stateChangeCount) return null; - return IAccessible.accState[ChildId] as int?; + return IAccessible.get_accState(ChildId) as int?; }, polling ? Tid + 2 : Tid + 1); } catch (Exception e) @@ -646,7 +645,7 @@ private async Task PollChildren() List result; var count = 0; if (_recurseMethod != RecurseMethod.None && _recurseMethod != RecurseMethod.accNavigate) - count = IAccessible.accChildCount; + count = IAccessible.get_accChildCount(); switch (_recurseMethod) { case RecurseMethod.None: @@ -755,7 +754,7 @@ private List GetChildrenAccChildBackground(int count) var seen = new HashSet(count); for (int i = 0; i < count; i++) { - var v = IAccessible.accChild[i + 1]; + var v = IAccessible.get_accChild(i + 1); if (v is int vi && vi == CHILDID_SELF) continue; if (!seen.Add(v)) @@ -773,7 +772,7 @@ private ElementIdentifier ElementIdFromVariantBackground(object variant, IAccess IAccessible acc; if (variant is int childid) { - var child = childid == CHILDID_SELF ? base_acc : base_acc.accChild[childid]; + var child = childid == CHILDID_SELF ? base_acc : base_acc.get_accChild(childid); if (child is null) { // Child without its own IAccessible @@ -892,7 +891,7 @@ private async Task FetchRole() { role_obj = await Connection.CommandThread.OnBackgroundThread(() => { - return IAccessible.accRole[ChildId]; + return IAccessible.get_accRole(ChildId); }, RootHwnd.Tid + 1); } catch (Exception e) diff --git a/xalia/Win32/ElementIdentifier.cs b/xalia/Win32/ElementIdentifier.cs index 4fee989..55c31f7 100644 --- a/xalia/Win32/ElementIdentifier.cs +++ b/xalia/Win32/ElementIdentifier.cs @@ -1,5 +1,4 @@ -using Accessibility; -using System; +using System; using static Xalia.Interop.Win32; namespace Xalia.Win32 diff --git a/xalia/Win32/HwndProvider.cs b/xalia/Win32/HwndProvider.cs index 9323804..0b78010 100644 --- a/xalia/Win32/HwndProvider.cs +++ b/xalia/Win32/HwndProvider.cs @@ -1,5 +1,4 @@ -using Accessibility; -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; diff --git a/xalia/xalia-netcore.csproj b/xalia/xalia-netcore.csproj index 71899a1..8b32721 100644 --- a/xalia/xalia-netcore.csproj +++ b/xalia/xalia-netcore.csproj @@ -36,7 +36,6 @@ - @@ -45,12 +44,6 @@ net6.0-windows - - - - - - @@ -106,7 +99,6 @@ - diff --git a/xalia/xalia.csproj b/xalia/xalia.csproj index 493cf46..eb1fc8d 100644 --- a/xalia/xalia.csproj +++ b/xalia/xalia.csproj @@ -68,16 +68,8 @@ - - - - - - - - @@ -110,13 +102,6 @@ - - - - - - - @@ -149,11 +134,6 @@ - - - - - @@ -222,15 +202,6 @@ - - 4.0.0 - - - 4.0.0 - - - 4.0.0 - 3.0.0