兩種方法操作其它mac應用的窗口


(圖文基本無關)
如果單純說簡單方便,其使用AppleScript更好,特別是現在有了JS的加入,比如:

(*

This Apple script will resize any program window to an exact size and the window is then moved to the center of your screen.
Specify the program name, height and width below and run the script.

Written by Amit Agarwal on December 10, 2013

*)

set theApp to "Google Chrome"
set appHeight to 1080
set appWidth to 1920

tell application "Finder"
	set screenResolution to bounds of window of desktop
end tell

set screenWidth to item 3 of screenResolution
set screenHeight to item 4 of screenResolution

tell application theApp
	activate
	reopen
	set yAxis to (screenHeight - appHeight) / 2 as integer
	set xAxis to (screenWidth - appWidth) / 2 as integer
	set the bounds of the first window to {xAxis, yAxis, appWidth + xAxis, appHeight + yAxis}
end tell

覺得增加腳本會讓你的工程比較繁瑣的話,還可以把腳本寫入到object-c用對象調用的方法完成,比如:

NSApplescript * as = [[NSApplescript alloc] initWithSource:@"tell application \"TheApplication\"\nclose every window\nend tell"];
NSDictionary * errInfo;
NSAppleEventDescriptor * res = [as executeAndReturnError:&err];
if( !res ){
    // An error occurred. Inspect errInfo and perform necessary actions
}

[as release];

但是如果真的開發一個產品,使用純的c/object-c還是更規范、可控一些,因此上面這個兩個腳本的方法不算在內。閑話不說,直接貼代碼:

#include <stdio.h>

#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>
#import <ApplicationServices/ApplicationServices.h>
#import <CoreGraphics/CoreGraphics.h>

typedef int CGSConnection;
extern CGSConnection _CGSDefaultConnection(void);
extern CGError CGSCopyWindowProperty(const CGSConnection cid, NSInteger wid, CFStringRef key, CFStringRef *output);

void getTitleList1(){
	CFStringRef titleValue;
	CGSConnection connection = CGSDefaultConnectionForThread();
	NSInteger windowCount, *windows;
	char cTitle[256] = {0};
	
	NSCountWindows(&windowCount);
	windows = (NSInteger*) malloc(windowCount * sizeof(NSInteger));
	if (windows) {
	    NSWindowList(windowCount, windows);
	    for (int i = 0; i < windowCount; ++i)
	    {
	        CGSCopyWindowProperty(connection, windows[i], CFSTR("kCGSWindowTitle"), &titleValue);
	        if(!titleValue) //Not every window has a title
	            continue;

			//CFStringGetCString(titleValue,cTitle,127,kCFStringEncodingMacRoman);
			CFStringGetCString(titleValue,cTitle,256,kCFStringEncodingUTF8);
			printf("title: %s\n",cTitle);
	    }
	    free(windows);
	}
}

void getTitleList2(){
	@autoreleasepool {
	// Get all the windows
	CFArrayRef windowListAll = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
	NSArray* arr = CFBridgingRelease(windowListAll);
	NSUInteger count = [arr count]; //CFArrayGetCount(arr);
	// Loop through the windows
	for (NSMutableDictionary* entry in arr){
		if (entry == nil){
			break;
		}
		NSLog(@"enter:%@",entry);
		NSString *wndName=[entry objectForKey:(id)kCGWindowName];
		NSInteger wndNumber=[[entry objectForKey:(id)kCGWindowNumber] intValue];
		NSLog(@"wndName:%@ number:%ld",wndName,wndNumber);
		if (![wndName isEqualToString: @"~/test.txt"]){
			//不是自己想要的窗口繼續下一個循環
			continue;
		}
		//下面這個方法是手冊中最先查到的,但僅對屬於自己app的窗口有效,其它app的窗口無效,所以不能采用
		//NSWindow * wind=[NSApp windowWithWindowNumber: wndNumber];
		//NSLog(@"wnd:%@",wind);
		CGRect bounds;
	    CGRectMakeWithDictionaryRepresentation((CFDictionaryRef)[entry objectForKey:@"kCGWindowBounds"], &bounds);
	    NSLog(@"bounds: %@",NSStringFromRect(bounds));   
		//根據pid獲取窗口所屬的app
        pid_t pid = [[entry objectForKey:(id)kCGWindowOwnerPID] intValue];
        AXUIElementRef appRef = AXUIElementCreateApplication(pid);
        NSLog(@"Ref = %@",appRef);
		//獲取app所有的窗口
        CFArrayRef windowList;
        AXUIElementCopyAttributeValue(appRef, kAXWindowsAttribute, (CFTypeRef *)&windowList);
        //NSLog(@"WindowList = %@", windowList);
		CFRelease(appRef);
		if (!windowList){
			//NSLog(@"windowList is nil");
			continue;
		}
		for (int i=0;i<CFArrayGetCount(windowList);i++){
			//遍歷app所有窗口,查找跟全局遍歷所獲得窗口的實體
			AXUIElementRef windowRef = (AXUIElementRef) CFArrayGetValueAtIndex( windowList, i);
			NSLog(@"windowRef:%@",windowRef);
			CGWindowID application_window_id = 0;
			_AXUIElementGetWindow(windowRef, &application_window_id);
			if (application_window_id == wndNumber){
				//找到
				NSLog(@"Found a wnd that number is:%u",application_window_id);
				//根據需要來操作窗口的位置,僅用作示例,這里可以修改成其它操作
	            CFTypeRef position;
	            CGPoint newPoint;
	            newPoint.x = 0;
	            newPoint.y = 0;
	            NSLog(@"Create new position");
	            position = (CFTypeRef)(AXValueCreate(kAXValueCGPointType, (const void *)&newPoint));
				//setting new position
				AXUIElementSetAttributeValue(windowRef, kAXPositionAttribute, position);
			}
			CFRelease(windowRef);
		}
	 NSLog(@"end a loop ----------------------------");
	} //for windowListAll
	} //autorelease
}

int main(int argc, char **argv){
	getTitleList1();	//第一種方法,重點在遍歷
	getTitleList2();	//第二種方法,重點在獲取窗口后可以進一步控制

	return 0;	
}

重點的內容直接看注釋,其中的第二種方法可控性要好很多,不過程序也復雜一些。大概流程是先遍歷所有屏幕的窗口->然后根據窗口獲取該窗口所屬的應用->再次獲取應用所屬的所有窗口->在這些窗口中找到自己想要的->控制,第二步的確做的會有大量重復遍歷,不過從提供的api上看,目前只有這個辦法才能夠控制窗口。代碼中有大量的日志信息,正式使用的話調試完成可以刪掉。
這段代碼使用object-c和c混編,后綴為.m,因為只是測試代碼,沒有建立xcode項目,是在命令行編譯的,編譯方法可能有的人不太熟悉,也貼出來:

clang -fobjc-arc  -o wndjob wndjob.m -framework CoreGraphics -framework AppKit

參考鏈接:
https://www.labnol.org/software/resize-mac-windows-to-specific-size/28345/
https://stackoverflow.com/questions/8898430/close-all-windows-of-another-app-using-accessibility-api
https://stackoverflow.com/questions/21069066/move-other-windows-on-mac-os-x-using-accessibility-api


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM