1
2
3
4
5 package oshi.driver.unix;
6
7 import java.awt.Rectangle;
8 import java.util.ArrayList;
9 import java.util.HashMap;
10 import java.util.LinkedHashMap;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.Map.Entry;
14 import java.util.regex.Matcher;
15 import java.util.regex.Pattern;
16
17 import oshi.annotation.concurrent.ThreadSafe;
18 import oshi.software.os.OSDesktopWindow;
19 import oshi.util.ExecutingCommand;
20 import oshi.util.ParseUtil;
21 import oshi.util.Util;
22
23
24
25
26 @ThreadSafe
27 public final class Xwininfo {
28
29 private static final String[] NET_CLIENT_LIST_STACKING = ParseUtil.whitespaces
30 .split("xprop -root _NET_CLIENT_LIST_STACKING");
31 private static final String[] XWININFO_ROOT_TREE = ParseUtil.whitespaces.split("xwininfo -root -tree");
32 private static final String[] XPROP_NET_WM_PID_ID = ParseUtil.whitespaces.split("xprop _NET_WM_PID -id");
33
34 private Xwininfo() {
35 }
36
37
38
39
40
41
42
43 public static List<OSDesktopWindow> queryXWindows(boolean visibleOnly) {
44
45
46
47
48
49
50
51 Map<String, Integer> zOrderMap = new HashMap<>();
52 int z = 0;
53
54
55 List<String> stacking = ExecutingCommand.runNative(NET_CLIENT_LIST_STACKING, null);
56 if (!stacking.isEmpty()) {
57 String stack = stacking.get(0);
58 int bottom = stack.indexOf("0x");
59 if (bottom >= 0) {
60 for (String id : stack.substring(bottom).split(", ")) {
61 zOrderMap.put(id, ++z);
62 }
63 }
64 }
65
66 Pattern windowPattern = Pattern.compile(
67 "(0x\\S+) (?:\"(.+)\")?.*: \\((?:\"(.+)\" \".+\")?\\) (\\d+)x(\\d+)\\+.+ \\+(-?\\d+)\\+(-?\\d+)");
68 Map<String, String> windowNameMap = new HashMap<>();
69 Map<String, String> windowPathMap = new HashMap<>();
70
71 Map<String, Rectangle> windowMap = new LinkedHashMap<>();
72
73 for (String line : ExecutingCommand.runNative(XWININFO_ROOT_TREE, null)) {
74 Matcher m = windowPattern.matcher(line.trim());
75 if (m.matches()) {
76 String id = m.group(1);
77 if (!visibleOnly || zOrderMap.containsKey(id)) {
78 String windowName = m.group(2);
79 if (!Util.isBlank(windowName)) {
80 windowNameMap.put(id, windowName);
81 }
82 String windowPath = m.group(3);
83 if (!Util.isBlank(windowPath)) {
84 windowPathMap.put(id, windowPath);
85 }
86 windowMap.put(id, new Rectangle(ParseUtil.parseIntOrDefault(m.group(6), 0),
87 ParseUtil.parseIntOrDefault(m.group(7), 0), ParseUtil.parseIntOrDefault(m.group(4), 0),
88 ParseUtil.parseIntOrDefault(m.group(5), 0)));
89 }
90 }
91 }
92
93
94 List<OSDesktopWindow> windowList = new ArrayList<>();
95 for (Entry<String, Rectangle> e : windowMap.entrySet()) {
96 String id = e.getKey();
97 long pid = queryPidFromId(id);
98 boolean visible = zOrderMap.containsKey(id);
99 windowList.add(new OSDesktopWindow(ParseUtil.hexStringToLong(id, 0L), windowNameMap.getOrDefault(id, ""),
100 windowPathMap.getOrDefault(id, ""), e.getValue(), pid, zOrderMap.getOrDefault(id, 0), visible));
101 }
102 return windowList;
103 }
104
105 private static long queryPidFromId(String id) {
106
107 String[] cmd = new String[XPROP_NET_WM_PID_ID.length + 1];
108 System.arraycopy(XPROP_NET_WM_PID_ID, 0, cmd, 0, XPROP_NET_WM_PID_ID.length);
109 cmd[XPROP_NET_WM_PID_ID.length] = id;
110 List<String> pidStr = ExecutingCommand.runNative(cmd, null);
111 if (pidStr.isEmpty()) {
112 return 0;
113 }
114 return ParseUtil.getFirstIntValue(pidStr.get(0));
115 }
116
117 }