两种方法操作其它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
两种方法操作其它mac应用的窗口的更多相关文章
- Linux 下操作GPIO(两种方法,驱动和mmap)(转载)
目前我所知道的在Linux下操作GPIO有两种方法: 1.编写驱动,这当然要熟悉Linux下驱动的编写方法和技巧,在驱动里可以使用ioremap函数获得GPIO物理基地址指针,然后使用这个指针根据io ...
- Linux 下操作gpio(两种方法,驱动和mmap)
目前我所知道的在linux下操作GPIO有两种方法: 1. 编写驱动,这当然要熟悉linux下驱动的编写方法和技巧,在驱动里可以使用ioremap函数获得GPIO物理基地址指针,然后使用这个指针根据 ...
- 安卓ListView操作的两种方法
举例做一个微信的中间部分(好友消息等信息通知) 第一种:BaseAdapter() package com.example.wx; import java.util.ArrayList;import ...
- Android开发12——Andorid中操作数据库的insert的两种方法以及nullColumnHack
一.发现问题 先看两种方法插入数据 public void save(Person p){ SQLiteDatabase db = dbHelper.getWritableDatabase(); db ...
- spark-sql将Rdd转换为DataFrame进行操作的两种方法
SparkConf sparkConf = new SparkConf() .setMaster("local").setAppName("ClzMap"); ...
- 代码操作Word时,目录自动更新的两种方法
最近的项目中有一个功能点为:根据分析数据库并生成报告.不过不是大数据.数据挖掘之类,报告的内容.组织方式都是事先固定下来的.实现的方式为,在普通word文档中插入书签制成模板,然后程序使用OpenXM ...
- MySQL命令执行sql文件的两种方法
MySQL命令执行sql文件的两种方法 摘要:和其他数据库一样,MySQL也提供了命令执行sql脚本文件,方便地进行数据库.表以及数据等各种操作.下面笔者讲解MySQL执行sql文件命令的两种方法,希 ...
- C# 调用WCF服务的两种方法
项目简介 之前领导布置一个做单点登录的功能给我,实际上就是医院想做一个统一的平台来实现在这个统一的平台登录后不需要在His.Emr.Lis等系统一个个登录,直接可以登录到对应的系统,然后进行相应的操作 ...
- windows下获取IP地址的两种方法
windows下获取IP地址的两种方法: 一种可以获取IPv4和IPv6,但是需要WSAStartup: 一种只能取到IPv4,但是不需要WSAStartup: 如下: 方法一:(可以获取IPv4和I ...
随机推荐
- MySql由于编码问题,存储过程执行出错。
在存储过程后面加了一段话.红色部分 DELIMITER $$ USE `mysql_wispeed01-test1`$$ DROP PROCEDURE IF EXISTS `sp_crebillno` ...
- AGC001F - Wide Swap
Description 给你一个长度为$n$的排列,每次可以交换$|i-j|\geq K$并且$|a_i-a_j|=1$的数对,问你经过若干次变换后最小字典序的排列是啥 Solution 对$a$做一 ...
- 分布式、集群、微服务、SOA 之间的区别
分布式:不同模块部署在不同服务器上 作用:分布式解决网站高并发带来问题 集群:多台服务器部署相同应用构成一个集群 作用:通过负载均衡设备共同对外提供服务 SOA:Service Oriented Ar ...
- 图解Raft之领导者选举
图解Raft领导者选举,这里通过五张图来解答Raft选举的全过程: Raft集群各个节点之间是通过RPC通讯传递消息的,每个节点都包含一个RPC服务端与客户端,初始时启动RPC服务端.状态设置为Fol ...
- git教程——安装配置
Git(读音为/gɪt/.)是一个开源的分布式版本控制系统,可以有效.高速的处理从很小到非常大的项目版本管理. Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个 ...
- 微信小程序----没有 DOM 对象,一切基于组件化 ---- mpvue
封装好用的 类库 和 组件,复用且灵活度高 抽取相同的部分放在函数内部(组件内部) 抽取不同的部分放在形参(组件 props 传参,或者插槽) new Promise 运行时 初始化实例对象的状态为 ...
- 关于height、offsetheight、clientheight、scrollheight、innerheight、outerheight的区别
二.也是平时经常用到的offsetheight 它返回的高度是内容高+padding+边框,但是注意哦,木有加margin哦,当然一般也木有啥需要把margin加进去的,以上代码为例,结果显示上图h2 ...
- javaEE学习路线与目标
1.Java基础知识(15-30天) 2.了解html+css+js+jq+bootstrap(7天) 3.mysql+jdbc(重点)(3天) 4.xml(1天) 5.http协议+tomcat(1 ...
- SQA计划和验收测试规程设计
一.SQA(软件质量保证)的定义 软件质量保证(SQA-Software Quality Assurance)是建立一套有计划,有系统的方法,来向管理层保证拟定出的标准.步骤.实践和方法能够正确地被所 ...
- VSTS 执行git pull报错问题修复
VSTS中进行双向同步配置的git pull指令如下: 运行时报错,Log如下图所示: 原因说的很清楚了,需要提前执行以下两条git config指令: git config --global use ...