1
2
3
4
5 package oshi.driver.mac;
6
7 import static oshi.jna.platform.mac.CoreGraphics.kCGNullWindowID;
8 import static oshi.jna.platform.mac.CoreGraphics.kCGWindowListExcludeDesktopElements;
9 import static oshi.jna.platform.mac.CoreGraphics.kCGWindowListOptionAll;
10 import static oshi.jna.platform.mac.CoreGraphics.kCGWindowListOptionOnScreenOnly;
11
12 import java.awt.Rectangle;
13 import java.util.ArrayList;
14 import java.util.List;
15
16 import com.sun.jna.Pointer;
17 import com.sun.jna.platform.mac.CoreFoundation.CFArrayRef;
18 import com.sun.jna.platform.mac.CoreFoundation.CFBooleanRef;
19 import com.sun.jna.platform.mac.CoreFoundation.CFDictionaryRef;
20 import com.sun.jna.platform.mac.CoreFoundation.CFNumberRef;
21 import com.sun.jna.platform.mac.CoreFoundation.CFStringRef;
22
23 import oshi.annotation.concurrent.ThreadSafe;
24 import oshi.jna.platform.mac.CoreGraphics;
25 import oshi.jna.platform.mac.CoreGraphics.CGRect;
26 import oshi.software.os.OSDesktopWindow;
27 import oshi.util.FormatUtil;
28 import oshi.util.platform.mac.CFUtil;
29
30
31
32
33 @ThreadSafe
34 public final class WindowInfo {
35
36 private WindowInfo() {
37 }
38
39
40
41
42
43
44
45 public static List<OSDesktopWindow> queryDesktopWindows(boolean visibleOnly) {
46 CFArrayRef windowInfo = CoreGraphics.INSTANCE.CGWindowListCopyWindowInfo(
47 visibleOnly ? kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements
48 : kCGWindowListOptionAll,
49 kCGNullWindowID);
50 int numWindows = windowInfo.getCount();
51
52 List<OSDesktopWindow> windowList = new ArrayList<>();
53
54 CFStringRef kCGWindowIsOnscreen = CFStringRef.createCFString("kCGWindowIsOnscreen");
55 CFStringRef kCGWindowNumber = CFStringRef.createCFString("kCGWindowNumber");
56 CFStringRef kCGWindowOwnerPID = CFStringRef.createCFString("kCGWindowOwnerPID");
57 CFStringRef kCGWindowLayer = CFStringRef.createCFString("kCGWindowLayer");
58 CFStringRef kCGWindowBounds = CFStringRef.createCFString("kCGWindowBounds");
59 CFStringRef kCGWindowName = CFStringRef.createCFString("kCGWindowName");
60 CFStringRef kCGWindowOwnerName = CFStringRef.createCFString("kCGWindowOwnerName");
61 try {
62
63 for (int i = 0; i < numWindows; i++) {
64
65 Pointer result = windowInfo.getValueAtIndex(i);
66 CFDictionaryRef windowRef = new CFDictionaryRef(result);
67
68 result = windowRef.getValue(kCGWindowIsOnscreen);
69 boolean visible = result == null || new CFBooleanRef(result).booleanValue();
70 if (!visibleOnly || visible) {
71 result = windowRef.getValue(kCGWindowNumber);
72 long windowNumber = new CFNumberRef(result).longValue();
73
74 result = windowRef.getValue(kCGWindowOwnerPID);
75 long windowOwnerPID = new CFNumberRef(result).longValue();
76
77 result = windowRef.getValue(kCGWindowLayer);
78 int windowLayer = new CFNumberRef(result).intValue();
79
80 result = windowRef.getValue(kCGWindowBounds);
81 try (CGRect rect = new CGRect()) {
82 CoreGraphics.INSTANCE.CGRectMakeWithDictionaryRepresentation(new CFDictionaryRef(result), rect);
83 Rectangle windowBounds = new Rectangle(FormatUtil.roundToInt(rect.origin.x),
84 FormatUtil.roundToInt(rect.origin.y), FormatUtil.roundToInt(rect.size.width),
85 FormatUtil.roundToInt(rect.size.height));
86
87 result = windowRef.getValue(kCGWindowName);
88 String windowName = CFUtil.cfPointerToString(result, false);
89
90
91 result = windowRef.getValue(kCGWindowOwnerName);
92 String windowOwnerName = CFUtil.cfPointerToString(result, false);
93 if (windowName.isEmpty()) {
94 windowName = windowOwnerName;
95 } else {
96 windowName = windowName + "(" + windowOwnerName + ")";
97 }
98
99 windowList.add(new OSDesktopWindow(windowNumber, windowName, windowOwnerName, windowBounds,
100 windowOwnerPID, windowLayer, visible));
101 }
102 }
103 }
104 } finally {
105
106 kCGWindowIsOnscreen.release();
107 kCGWindowNumber.release();
108 kCGWindowOwnerPID.release();
109 kCGWindowLayer.release();
110 kCGWindowBounds.release();
111 kCGWindowName.release();
112 kCGWindowOwnerName.release();
113 windowInfo.release();
114 }
115
116 return windowList;
117 }
118 }