2009-03-05 5 views
23

Tôi đang cố gắng tìm các tài liệu API (hoặc không có giấy tờ, nếu đó là lựa chọn duy nhất của tôi) trên OS X để truy vấn danh sách cửa sổ từ máy chủ cửa sổ và sau đó làm cho cửa sổ di chuyển và thay đổi kích thước. ai đó có thể chỉ cho tôi phương hướng đúng không? Tôi đoán tôi muốn bắt đầu với một cái gì đó như FindWindowEx và MoveWindow theo Win32.Di chuyển cửa sổ và thay đổi kích thước các API trong OS X

Lưu ý rằng tôi muốn thực hiện điều này từ quy trình bên ngoài - Tôi không hỏi cách kiểm soát kích thước và vị trí cửa sổ của ứng dụng của riêng tôi.

Trả lời

42

Sử dụng API trợ năng. Sử dụng API này, bạn có thể kết nối với một quá trình, có được một danh sách các cửa sổ (thực sự là một mảng), có được vị trí và kích cỡ của mỗi cửa sổ và cũng thay đổi thuộc tính cửa sổ nếu bạn muốn.

Tuy nhiên, một ứng dụng chỉ có thể sử dụng API này nếu người dùng đã bật quyền truy cập cho các thiết bị trợ giúp trong tùy chọn của mình (Tùy chọn hệ thống - Truy cập toàn cục), trong trường hợp đó tất cả ứng dụng có thể sử dụng API này hoặc nếu ứng dụng của bạn một ứng dụng đáng tin cậy (khi nó được tin cậy, nó có thể sử dụng API, ngay cả khi tùy chọn này không được chọn). API Accessibility cung cấp các chức năng cần thiết để làm cho ứng dụng của bạn tin cậy - về cơ bản bạn phải trở thành root (sử dụng các dịch vụ bảo mật để yêu cầu quyền root của người dùng) và sau đó đánh dấu quá trình của bạn là đáng tin cậy. Khi ứng dụng của bạn đã được đánh dấu là đáng tin cậy, ứng dụng phải được khởi động lại vì trạng thái tin cậy chỉ được chọn khi khởi động và không thể thay đổi trong khi ứng dụng đang chạy. Trạng thái tin cậy là vĩnh viễn, trừ khi người dùng di chuyển ứng dụng ở một nơi khác hoặc băm của nhị phân ứng dụng thay đổi (ví dụ: sau khi cập nhật). Nếu người dùng có các thiết bị trợ giúp được bật trong các prefs của mình, tất cả các ứng dụng sẽ được xử lý như thể chúng đã được tin cậy. Thông thường, ứng dụng của bạn sẽ kiểm tra xem tùy chọn này có được bật hay không, nếu có, hãy tiếp tục và thực hiện công cụ của bạn. Nếu không, nó sẽ kiểm tra nếu nó đã được tin cậy, nếu nó là, một lần nữa chỉ cần làm công cụ của bạn. Nếu không cố gắng làm cho chính nó đáng tin cậy và sau đó khởi động lại ứng dụng trừ khi người dùng từ chối ủy quyền root. API cung cấp tất cả các chức năng cần thiết để kiểm tra tất cả điều này.

Có các chức năng riêng để thực hiện tương tự bằng trình quản lý cửa sổ Mac OS, nhưng lợi thế duy nhất có thể mua cho bạn là bạn không cần phải là ứng dụng trợ năng đáng tin cậy (hoạt động một lần khi khởi chạy lần đầu tiên trong hầu hết các trường hợp). Những bất lợi là API này có thể thay đổi bất cứ lúc nào (nó đã thay đổi trong quá khứ), tất cả các tài liệu và chức năng chỉ được biết đến thông qua kỹ thuật đảo ngược. Tuy nhiên, khả năng truy cập là công khai, nó được ghi lại và nó không thay đổi nhiều kể từ phiên bản OS X đầu tiên giới thiệu nó (một số chức năng mới được thêm vào 10.4 và một lần nữa trong 10.5, nhưng không có nhiều thay đổi khác).

Dưới đây là ví dụ về mã. Nó sẽ đợi 5 giây, vì vậy bạn có thể chuyển sang một cửa sổ khác trước khi nó làm bất cứ điều gì khác (nếu không nó sẽ luôn làm việc với cửa sổ terminal, khá nhàm chán để thử nghiệm). Sau đó, nó sẽ có được quá trình đầu tiên nhất, cửa sổ phía trước nhất của quá trình này, in vị trí và kích thước của nó và cuối cùng di chuyển nó bằng 25 pixel bên phải. Bạn biên dịch nó trên dòng lệnh như vậy (giả sử nó được đặt tên test.c)

gcc -framework Carbon -o test test.c 

Xin lưu ý rằng tôi không thực hiện bất kỳ kiểm tra lỗi trong mã cho đơn giản (có những nơi khác nhau mà có thể gây ra các chương trình để sụp đổ nếu có điều gì đó sai và một số thứ có thể/có thể xảy ra sai). Dưới đây là các mã:

/* Carbon includes everything necessary for Accessibilty API */ 
#include <Carbon/Carbon.h> 

static bool amIAuthorized() 
{ 
    if (AXAPIEnabled() != 0) { 
     /* Yehaa, all apps are authorized */ 
     return true; 
    } 
    /* Bummer, it's not activated, maybe we are trusted */ 
    if (AXIsProcessTrusted() != 0) { 
     /* Good news, we are already trusted */ 
     return true; 
    } 
    /* Crap, we are not trusted... 
    * correct behavior would now be to become a root process using 
    * authorization services and then call AXMakeProcessTrusted() to make 
    * ourselves trusted, then restart... I'll skip this here for 
    * simplicity. 
    */ 
    return false; 
} 


static AXUIElementRef getFrontMostApp() 
{ 
    pid_t pid; 
    ProcessSerialNumber psn; 

    GetFrontProcess(&psn); 
    GetProcessPID(&psn, &pid); 
    return AXUIElementCreateApplication(pid); 
} 


int main (
    int argc, 
    char ** argv 
) { 
    int i; 
    AXValueRef temp; 
    CGSize windowSize; 
    CGPoint windowPosition; 
    CFStringRef windowTitle; 
    AXUIElementRef frontMostApp; 
    AXUIElementRef frontMostWindow; 

    if (!amIAuthorized()) { 
     printf("Can't use accessibility API!\n"); 
     return 1; 
    } 

    /* Give the user 5 seconds to switch to another window, otherwise 
    * only the terminal window will be used 
    */ 
    for (i = 0; i < 5; i++) { 
     sleep(1); 
     printf("%d", i + 1); 
     if (i < 4) { 
      printf("..."); 
      fflush(stdout); 
     } else { 
      printf("\n"); 
     } 
    } 

    /* Here we go. Find out which process is front-most */ 
    frontMostApp = getFrontMostApp(); 

    /* Get the front most window. We could also get an array of all windows 
    * of this process and ask each window if it is front most, but that is 
    * quite inefficient if we only need the front most window. 
    */ 
    AXUIElementCopyAttributeValue(
     frontMostApp, kAXFocusedWindowAttribute, (CFTypeRef *)&frontMostWindow 
    ); 

    /* Get the title of the window */ 
    AXUIElementCopyAttributeValue(
     frontMostWindow, kAXTitleAttribute, (CFTypeRef *)&windowTitle 
    ); 

    /* Get the window size and position */ 
    AXUIElementCopyAttributeValue(
     frontMostWindow, kAXSizeAttribute, (CFTypeRef *)&temp 
    ); 
    AXValueGetValue(temp, kAXValueCGSizeType, &windowSize); 
    CFRelease(temp); 

    AXUIElementCopyAttributeValue(
     frontMostWindow, kAXPositionAttribute, (CFTypeRef *)&temp 
    ); 
    AXValueGetValue(temp, kAXValueCGPointType, &windowPosition); 
    CFRelease(temp); 

    /* Print everything */ 
    printf("\n"); 
    CFShow(windowTitle); 
    printf(
     "Window is at (%f, %f) and has dimension of (%f, %f)\n", 
     windowPosition.x, 
     windowPosition.y, 
     windowSize.width, 
     windowSize.height 
    ); 

    /* Move the window to the right by 25 pixels */ 
    windowPosition.x += 25; 
    temp = AXValueCreate(kAXValueCGPointType, &windowPosition); 
    AXUIElementSetAttributeValue(frontMostWindow, kAXPositionAttribute, temp); 
    CFRelease(temp); 

    /* Clean up */ 
    CFRelease(frontMostWindow); 
    CFRelease(frontMostApp); 
    return 0; 
} 

Sine Bến hỏi làm thế nào bạn nhận được một danh sách tất cả các cửa sổ trong các ý kiến, đây là cách:

Thay vì "kAXFocusedWindowAttribute" bạn sử dụng "kAXWindowsAttribute" cho hàm AXUIElementCopyAttributeValue. Kết quả là sau đó không có AXUIElementRef, nhưng một CFArray của các phần tử AXUIElementRef, một cho mỗi cửa sổ của ứng dụng này.

+0

"Chúng tôi cũng có thể nhận được một loạt tất cả các cửa sổ của quá trình này" ... Tôi sẽ làm điều đó như thế nào? –

+1

@Ben: Thay vì "kAXFocusedWindowAttribute", bạn sử dụng "kAXWindowsAttribute" cho hàm AXUIElementCopyAttributeValue. Kết quả là sau đó không có AXUIElementRef, nhưng một CFArray của các phần tử AXUIElementRef, một cho mỗi cửa sổ của ứng dụng này. – Mecki

+0

Cảm ơn, chỉ là những gì tôi đang tìm kiếm. –

0

Tôi đồng ý rằng Trợ năng là cách tốt nhất để chuyển tiếp. Nhưng nếu bạn muốn nhanh chóng và dơ bẩn, AppleScript cũng sẽ hoạt động.