انقل النوافذ الأخرى على نظام التشغيل Mac OS X باستخدام Accessibility API
-
02-01-2020 - |
سؤال
أحاول استخدام Accessibility API لتغيير موضع نوافذ التطبيقات الأخرى. ما أرغب في فعله هو الحصول على جميع النوافذ التي تظهر على الشاشة من جميع التطبيقات، ثم نقلها جميعها بإزاحة معينة (على سبيل المثال 5 أو 10 أو أي قيمة).أواجه صعوبات في القيام بذلك لأن هذا هو اليوم الأول من البرمجة في Objective-C بالنسبة لي.
هذا ما أفعله الآن.أولاً، أجد قائمة النوافذ ومعرفات PID الخاصة بها التي تستخدمها CGWindowListCopyWindowInfo
.ثم لكل نافذة أستخدمها AXUIElementCreateApplication
للحصول على AXUIElementRef
من خلال النافذة.بعد ذلك، يجب أن أستخدم AXUIElementCopyAttributeValue
مع السمة kAXPositionAttribute
(والذي أفشل في الحصول على الموضع المناسب، دائما الحصول على الأصفار).أخيرًا، يجب أن أضيف الإزاحة المطلوبة إلى الموضع والاستخدام AXUIElementSetAttributeValue
مع السمة kAXPositionAttribute
ونقطة الموضع الجديدة (التي أحصل على أخطاء وقت التشغيل حتى لو قمت بتعيين قيم مطلقة مثل 0،0).
هل يمكن لأحد أن يساعدني بمقتطف للقيام بما وصفته أعلاه، حيث أنني جربت أشياء كثيرة دون أي حظ.أيضًا، لا ينبغي أن يكون الأمر تمامًا كما قررت تنفيذه أعلاه.إذا كانت هناك طريقة أفضل للقيام بذلك، فسأكون سعيدًا بتغييرها.
تحديث:كما هو مطلوب في التعليق، إليك مقتطف التعليمات البرمجية لإحدى المحاولات:
// Get all the windows
CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
NSArray* arr = CFBridgingRelease(windowList);
// Loop through the windows
for (NSMutableDictionary* entry in arr)
{
// Get window PID
pid_t pid = [[entry objectForKey:(id)kCGWindowOwnerPID] intValue];
// Get AXUIElement using PID
AXUIElementRef elementRef = AXUIElementCreateApplication(pid);
CFTypeRef position;
CGPoint point;
// Get the position attribute of the window (maybe something is wrong?)
AXUIElementCopyAttributeValue(elementRef, kAXPositionAttribute, (CFTypeRef *)&position);
AXValueGetValue(position, kAXValueCGPointType, &point);
// Debugging (always zeros?)
NSLog(@"point=%@", point);
// Create a point
NSPoint newPoint;
newPoint.x = 0;
newPoint.y = 0;
position = (CFTypeRef)(AXValueCreate(kAXValueCGPointType, (const void *)&newPoint));
// Set the position attribute of the window (runtime error over here)
AXUIElementSetAttributeValue(elementRef, kAXPositionAttribute, (CFTypeRef *)&position);
}
المحلول
استنادًا إلى نموذج التعليمات البرمجية الخاص بك (الذي تم تعديله قليلاً، نظرًا لأن ما نشرته لا يتم تجميعه وسيتعطل دون تعديل)، فقد أجريت بعض التجارب.
وهنا بعض التحذيرات:
- أنت تقوم باسترجاع طلب بواسطة PID، ولكن بعد ذلك يتم التصرف عليه كما لو كان نافذة.هذا هو جوهر مشكلتك، لكنه مجرد بداية الحل.
- ستحتاج إلى الاطلاع على قائمة النوافذ الخاصة بكائن تطبيق إمكانية الوصول للعثور على النوافذ القابلة لإعادة تحديد موقعها والتي يمكنك نقلها باستخدام Accessibility Framework.
CGWindowListCopyWindowInfo
سيعرض نوافذ "كل ما يظهر على الشاشة" عند سؤالك عن الطريقة التي تسميها بها، لكنه لا يضمن أن تكون هذه إما "نوافذ مستخدم" أو نوافذ ذات إمكانية الوصول.تحتوي معظم عناصر شريط القائمة على نافذة جذر "على الشاشة" ولا يمكن الوصول إلى معظمها (والتي تظهر عندما تحاول السير في شجرة إمكانية الوصول لمعرفات PID التي تسترجعها).- قد تجد أن اختبار AXRole مفيد، أو قد تجد سمات إمكانية الوصول الأخرى للنافذة أكثر فائدة في تحديد ما إذا كان سيتم نقل النوافذ أم لا.
لقد قمت بتضمين تعديلات على التعليمات البرمجية الخاصة بك هنا (سيتم تشغيل هذا دون تعطل)، والتي ستلتقط معلومات النافذة ذات الصلة من التطبيقات التي تستردها عبر PID ثم تحرك النوافذ.لدي بيان نوم حتى أتمكن من إيقاف التنفيذ، حيث كنت أختبر تأثير الحركة للتو:
#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>
#import <ApplicationServices/ApplicationServices.h>
int main(int argc, char *argv[]) {
@autoreleasepool {
// Get all the windows
CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
NSArray* arr = CFBridgingRelease(windowList);
// Loop through the windows
for (NSMutableDictionary* entry in arr)
{
// Get window PID
pid_t pid = [[entry objectForKey:(id)kCGWindowOwnerPID] intValue];
// Get AXUIElement using PID
AXUIElementRef appRef = AXUIElementCreateApplication(pid);
NSLog(@"Ref = %@",appRef);
// Get the windows
CFArrayRef windowList;
AXUIElementCopyAttributeValue(appRef, kAXWindowsAttribute, (CFTypeRef *)&windowList);
NSLog(@"WindowList = %@", windowList);
if ((!windowList) || CFArrayGetCount(windowList)<1)
continue;
// get just the first window for now
AXUIElementRef windowRef = (AXUIElementRef) CFArrayGetValueAtIndex( windowList, 0);
CFTypeRef role;
AXUIElementCopyAttributeValue(windowRef, kAXRoleAttribute, (CFTypeRef *)&role);
CFTypeRef position;
CGPoint point;
// Get the position attribute of the window (maybe something is wrong?)
AXUIElementCopyAttributeValue(windowRef, kAXPositionAttribute, (CFTypeRef *)&position);
AXValueGetValue(position, kAXValueCGPointType, &point);
// Debugging (always zeros?)
NSLog(@"point=%f,%f", point.x,point.y);
// Create a point
CGPoint newPoint;
newPoint.x = 0;
newPoint.y = 0;
NSLog(@"Create");
position = (CFTypeRef)(AXValueCreate(kAXValueCGPointType, (const void *)&newPoint));
// Set the position attribute of the window (runtime error over here)
NSLog(@"SetAttribute");
AXUIElementSetAttributeValue(windowRef, kAXPositionAttribute, position);
sleep(5);
}
}
}