2011-09-14 39 views
26

Tôi đang cố vá an application thay đổi kích thước cửa sổ bằng API trợ năng.Nhận dạng duy nhất cửa sổ đang hoạt động trên OS X

Tôi cần duy trì từ điển có kích thước cửa sổ trước đó. Chìa khóa cần xác định cửa sổ hiện đang hoạt động. Tại thời điểm này, cửa sổ hiện hoạt này được truy xuất qua NSAccessibilityFocusedWindowAttribute khi nhấn phím nóng.

Tuy nhiên, mỗi lần phương thức này được gọi, số trả lại AXUIElementRef xác định cửa sổ khác nhau! Điều này tất nhiên có nghĩa là tôi không thể sử dụng nó làm khóa từ điển - từ điển sẽ không tìm thấy mục nhập tương ứng.

Các mã sau tái tạo các vấn đề:

-(IBAction)testWindowIdentification:(id)sender{ 
    AXUIElementRef focusedApp; 
    AXUIElementRef focusedWindow; 

    AXUIElementCopyAttributeValue(_systemWideElement, 
            (CFStringRef) kAXFocusedApplicationAttribute, 
            (CFTypeRef*) &focusedApp); 
    AXUIElementCopyAttributeValue((AXUIElementRef) focusedApp, 
            (CFStringRef) NSAccessibilityFocusedWindowAttribute, 
            (CFTypeRef*) &focusedWindow); 
    CFShow(focusedWindow); 
} 

_systemWideElement đã được khởi tạo trong phương pháp init sử dụng một cuộc gọi đến AXUIElementCreateSystemWide().

Tuyên bố CFShow rõ ràng hiển thị ID khác nhau mỗi khi phương pháp này được gọi là (mặc dù cùng một cửa sổ đang hoạt động), mà là vô ích cho tôi:

<AXUIElement 0x47e850> {pid=42463} 
<AXUIElement 0x47e890> {pid=42463} 
<AXUIElement 0x47e2c0> {pid=42463} 
… 

Các documentation on AXUIElement cho thấy không có phương pháp để lấy một thuộc tính duy nhất cho phần tử giao diện người dùng và không phải là that of the NSAccessibility protocol. PID duy nhất là không phải đủ cho tôi, vì quá trình có thể có nhiều cửa sổ.

Làm cách nào tôi có thể truy xuất một số số nhận dạng duy nhất của cửa sổ hiện hoạt trong Cocoa?

(Bằng cách này, các mã thực là kiểm tra mã trở lại trong các cuộc gọi trên, không có lỗi, các cuộc gọi thành công.)

+1

@JeremyBanks Người trả lời ban đầu có ý tưởng đúng ở đây. Bạn thực sự có thể sử dụng Quartz để có được một 'CGWindowID' một khi bạn đã xác định được cửa sổ tập trung, nếu [câu trả lời này] (http://stackoverflow.com/a/312099/517815) là để được tin tưởng. _should_ này cung cấp cho bạn mã định danh cửa sổ duy nhất mà bạn đang hy vọng, mà bạn có thể vượt qua xung quanh với khả năng miễn dịch trong ngữ cảnh của ứng dụng hiện tại của bạn. Hãy cho tôi biết nếu bạn muốn có một phiên bản mạch lạc và đầy đủ hơn về điều này như một câu trả lời thực tế. – MrGomez

+0

@MrGomez Chắc chắn, một câu trả lời như thế sẽ rất tuyệt. :) –

+0

@JeremyBanks Sẽ làm. Hôm nay tôi hơi quá tải, nhưng tôi sẽ cố gắng trả lời câu hỏi này vào tối nay (PST). :) – MrGomez

Trả lời

16

Rob Keniger có chiến lược đúng với his answer here. Điều duy nhất còn thiếu trong câu trả lời này (và thực sự, lý do cho vị trí tiền thưởng) là một thực thi khả thi có cửa sổ hoạt động hiện tại và dịch nó thành một khóa duy nhất phù hợp để lập chỉ mục trong ngữ cảnh của ứng dụng hiện tại.

Giải pháp của Rob phác thảo điều này thông qua việc sử dụng CGWindowID được đưa ra trong bối cảnh của Dịch vụ Cửa sổ Quartz. Đó là, tất nhiên, ngụ ý mạnh mẽ rằng tham chiếu cửa sổ này là only useful for your current application.

Việc tham chiếu cửa sổ này rất phức tạp, vì không có đảm bảo mạnh mẽ nào tồn tại giữa API trợ năng và Dịch vụ cửa sổ thạch anh. Tuy nhiên, bạn có thể làm việc xung quanh này theo các cách sau:

  1. Sử dụng extern "C" AXError _AXUIElementGetWindow(AXUIElementRef, CGWindowID* out);, như documented here.Đây không phải là đảm bảo hoạt động, nhưng nó hoạt động như một bài kiểm tra ở tầng trệt để bắt đầu mọi thứ nếu nó hoạt động trong phiên bản OSX của bạn.

  2. Nhận trực tiếp số CGWindowID, sử dụng, ví dụ: HIWindowGetCGWindowID(). Bạn có thể tìm thêm chi tiết về cách chọn cửa sổ hiện hoạt và giải mã ID trong the reference manual for the Carbon Window Manager (cảnh báo: PDF lớn).

  3. Danh mục CGWindowID đặt bằng cách sử dụng một cái gì đó như CGWindowListCreateDescriptionFromArray, chính xác như Rob đề xuất. Mục đích ở đây là tìm một số lược đồ để bắc cầu API trợ năng và Quartz, nhưng điều này có thể hiểu được bằng cách sử dụng, ví dụ, một cuộc gọi lại được liên kết với ngữ cảnh của cửa sổ hiện tại đang hoạt động của bạn. Tôi thành thật không biết một ví dụ tối ưu về điều này là đó là đúng cách trong tương lai được kiểm soát trong tương lai.

Trong số lựa chọn, tôi khuyên bạn nên đi với 2. cho nhu cầu hiện tại của bạn, nếu bạn không thể tạo ra một số trang trí khác cho các cửa sổ của bạn để nhận diện chúng. Nó hiện được định nghĩa trong cơ sở mã kế thừa, nhưng nó sẽ làm những gì bạn mong muốn.

Chúc bạn may mắn với ứng dụng của mình.

+0

Làm cách nào để phát hiện thấy không có cửa sổ nào được chọn hoặc tất cả các cửa sổ không hoạt động (tức là điều khiển trên máy tính để bàn)? –

+0

Xin lỗi, cách sử dụng "extern" C "AXError _AXUIElementGetWindow (AXUIElementRef, CGWindowID * out);" trong Swift? – allenlinli

8

Tôi nghĩ rằng bạn có thể có thể sử dụng các chức năng Quartz Window Dịch vụ , cụ thể là CGWindowListCreateDescriptionFromArray để liệt kê các cửa sổ hiện đang hoạt động trong một ứng dụng cụ thể.

Cuộc gọi này thấp hơn AppKit và sẽ không cho bạn biết đó là cửa sổ đang hoạt động, nhưng nó sẽ cung cấp cho bạn ID cửa sổ duy nhất cho phiên người dùng hiện tại. Đây không phải là giải pháp tuyệt vời, nhưng bạn có thể so sánh thông tin giới hạn cửa sổ với những gì bạn nhận được từ API trợ năng để liên kết các cửa sổ với ID thực của họ.

Các vấn đề liên quan