001package jmri.util; 002 003import java.awt.GraphicsConfiguration; 004import java.awt.GraphicsDevice; 005import java.awt.GraphicsEnvironment; 006import java.awt.HeadlessException; 007import java.awt.Insets; 008import java.awt.Toolkit; 009import java.io.BufferedReader; 010import java.io.File; 011import java.io.FileReader; 012import java.io.IOException; 013import java.io.InputStreamReader; 014import java.util.Arrays; 015import org.slf4j.Logger; 016import org.slf4j.LoggerFactory; 017 018/** 019 * This class attempts to retrieve the screen insets for all operating systems. 020 * <p> 021 * The standard insets command fails on Linux - this class attempts to rectify 022 * that. 023 * <p> 024 * Borrows heavily from the <a href="https://community.oracle.com/thread/1368416?start=29"> 025 * Linsets class created by: A. Tres Finocchiaro 026 * </a> 027 * 028 * @author Matt Harris 029 */ 030public class JmriInsets { 031 032 private static final String DESKTOP_ENVIRONMENTS = "kdesktop|gnome-panel|xfce|darwin|icewm"; // NOI18N 033 034 private static final String GNOME_CONFIG = "%gconf.xml"; // NOI18N 035 private static final String GNOME_PANEL = "_panel_screen"; // NOI18N 036 private static final String GNOME_ROOT = System.getProperty("user.home") + "/.gconf/apps/panel/toplevels/"; // NOI18N 037 038 private static final String KDE_CONFIG = System.getProperty("user.home") + "/.kde/share/config/kickerrc"; // NOI18N 039 040 //private static final String XFCE_CONFIG = System.getProperty("user.home") + "/.config/xfce4/mcs_settings/panel.xml"; 041 private static final String OS_NAME = SystemType.getOSName(); 042 043 // Set this to -2 initially (out of the normal range) 044 // which can then be used to determine if we need to 045 // determine the window manager in use. 046 private static int linuxWM = -2; 047 048 /** 049 * Creates a new instance of JmriInsets 050 * 051 * @return the new instance 052 */ 053 public static Insets getInsets() { 054 055 if (linuxWM == -2) { 056 linuxWM = getLinuxWindowManager(); 057 } 058 059 switch (linuxWM) { 060 case 0: 061 return getKDEInsets(); 062 case 1: 063 return getGnomeInsets(); 064 case 2: 065 return getXfceInsets(); 066 case 3: 067 return getDarwinInsets(); 068 case 4: 069 return getIcewmInsets(); 070 default: 071 return getDefaultInsets(); 072 } 073 074 } 075 076 /* 077 * Determine the current Linux Window Manager 078 */ 079 private static int getLinuxWindowManager() { 080 if (!SystemType.isWindows() 081 && !OS_NAME.toLowerCase().startsWith("mac")) { // NOI18N 082 try { 083 Process p = Runtime.getRuntime().exec(new String[]{"ps","ax"}); // NOI18N 084 BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream())); 085 086 try { 087 java.util.List<String> desktopList = Arrays.asList(DESKTOP_ENVIRONMENTS.split("\\|")); // NOI18N 088 089 String line = r.readLine(); 090 while (line != null) { 091 for (int i = 0; i < desktopList.size(); i++) { 092 String s = desktopList.get(i); 093 if (line.contains(s) && !line.contains("grep")) // NOI18N 094 { 095 return desktopList.indexOf(s); 096 } 097 } 098 line = r.readLine(); 099 } 100 } catch (java.io.IOException e1) { 101 log.error("error reading file", e1); 102 throw e1; 103 } finally { 104 try { 105 r.close(); 106 } catch (java.io.IOException e2) { 107 log.error("Exception closing file", e2); 108 } 109 } 110 111 } catch (IOException e) { 112 log.error("IO Exception"); 113 } 114 } 115 return -1; 116 } 117 118 /* 119 * Get insets for KDE 3.5+ 120 */ 121 private static Insets getKDEInsets() { 122 /* 123 * KDE: 2 Top | JAVA: 0 Top 124 * 0 Left 1 Right | 1 Left 3 Right 125 * 3 Bottom | 2 Bottom 126 */ 127 int[] sizes = {24, 30, 46, 58, 0}; // xSmall, Small, Medium, Large, xLarge, Null 128 int[] i = {0, 0, 0, 0, 0}; // Left, Right, Top, Bottom, Null 129 130 /* Needs to be fixed. Doesn't know the difference between CustomSize and Size */ 131 int iniCustomSize = getKdeINI("General", "CustomSize"); // NOI18N 132 int iniSize = getKdeINI("General", "Size"); // NOI18N 133 int iniPosition = getKdeINI("General", "Position"); // NOI18N 134 int position = iniPosition == -1 ? 3 : iniPosition; 135 int size = (iniCustomSize == -1 || iniSize != 4) ? iniSize : iniCustomSize; 136 size = size < 24 ? sizes[size] : size; 137 i[position] = size; 138 return new Insets(i[2], i[0], i[3], i[1]); 139 } 140 141 /* 142 * Get insets for Gnome 143 */ 144 private static Insets getGnomeInsets() { 145 File gnomeRoot = new File(GNOME_ROOT); 146 147 int n = 0; 148 int s = 0; 149 int e = 0; 150 int w = 0; 151 File[] files = gnomeRoot.listFiles(); 152 if (files != null) { 153 for (File f : files) { 154 String folder = f.getName(); 155 if (f.isDirectory() && folder.contains(GNOME_PANEL)) { 156 int val = getGnomeXML(new File(GNOME_ROOT + "/" + folder + "/" + GNOME_CONFIG)); 157 if (val == -1) { 158 // Skip 159 } else if (folder.startsWith("top" + GNOME_PANEL)) { // NOI18N 160 n = Math.max(val, n); 161 } else if (folder.startsWith("bottom" + GNOME_PANEL)) { // NOI18N 162 s = Math.max(val, s); 163 } else if (folder.startsWith("right" + GNOME_PANEL)) { // NOI18N 164 e = Math.max(val, e); 165 } else if (folder.startsWith("left" + GNOME_PANEL)) { // NOI18N 166 w = Math.max(val, w); 167 } 168 } 169 } 170 } 171 return new Insets(n, w, s, e); 172 } 173 174 /* 175 * Get insets for Xfce 176 */ 177 private static Insets getXfceInsets() { 178 return getDefaultInsets(false); 179 } 180 181 /* 182 * Get insets for Darwin (Mac OS X) 183 */ 184 private static Insets getDarwinInsets() { 185 return getDefaultInsets(false); 186 } 187 188 /* 189 * Get insets for IceWM 190 */ 191 private static Insets getIcewmInsets() { 192 // OK, this is being a bit lazy but the vast majority of 193 // IceWM themes do not seem to modify the taskbar height 194 // from the default 25 pixels nor do they change the 195 // position of being along the bottom 196 return new Insets(0, 0, 25, 0); 197 } 198 199 /* 200 * Default insets (Java standard) 201 * Write log entry for any OS that we don't yet now how to handle. 202 */ 203 private static Insets getDefaultInsets() { 204 if (!OS_NAME.toLowerCase().startsWith("windows") // NOI18N 205 && !OS_NAME.toLowerCase().startsWith("mac")) { // NOI18N 206 // MS Windows & Mac OS will always end-up here, so no need to log. 207 return getDefaultInsets(false); 208 } else { 209 // any other OS ends up here 210 return getDefaultInsets(true); 211 } 212 } 213 214 private static Insets getDefaultInsets(boolean logOS) { 215 if (logOS) { 216 log.trace("Trying default insets for {}", OS_NAME); 217 } 218 try { 219 GraphicsDevice gs[] = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices(); 220 for (GraphicsDevice g : gs) { 221 GraphicsConfiguration[] gc = g.getConfigurations(); 222 for (GraphicsConfiguration element : gc) { 223 return (Toolkit.getDefaultToolkit().getScreenInsets(element)); 224 } 225 } 226 } catch (HeadlessException h) { 227 log.debug("Warning: Headless error - no GUI available"); 228 } 229 return new Insets(0, 0, 0, 0); 230 } 231 232 /* 233 * Some additional routines required for specific window managers 234 */ 235 /* 236 * Parse XML files for some sizes in Gnome 237 */ 238 private static int getGnomeXML(File xmlFile) { 239 try { 240 boolean found = false; 241 String temp; 242 try (FileReader reader = new FileReader(xmlFile); BufferedReader buffer = new BufferedReader(reader)) { 243 temp = buffer.readLine(); 244 while (temp != null) { 245 if (temp.contains("<entry name=\"size\"")) { // NOI18N 246 found = true; 247 break; 248 } 249 temp = buffer.readLine(); 250 } 251 } 252 if (temp == null) { 253 return -1; 254 } 255 if (found) { 256 temp = temp.substring(temp.indexOf("value=\"") + 7); 257 return Integer.parseInt(temp.substring(0, temp.indexOf('"'))); 258 } 259 } catch (IOException e) { 260 log.error("Error parsing Gnome XML: {}", e.getMessage()); 261 } 262 return -1; 263 } 264 265 private static int getKdeINI(String category, String component) { 266 try { 267 File f = new File(KDE_CONFIG); 268 if (!f.exists() || category == null || component == null) { 269 return -1; 270 } 271 272 boolean found = false; 273 String value = null; 274 FileReader reader = new FileReader(f); 275 BufferedReader buffer = new BufferedReader(reader); 276 try { 277 String temp = buffer.readLine(); 278 while (temp != null) { 279 if (temp.trim().equals("[" + category + "]")) { 280 temp = buffer.readLine(); 281 while (temp != null) { 282 if (temp.trim().startsWith("[")) { 283 return -1; 284 } else if (temp.startsWith(component + "=")) { 285 value = temp.substring(component.length() + 1); 286 found = true; 287 break; 288 } 289 temp = buffer.readLine(); 290 } 291 } 292 if (found == true) { 293 break; 294 } 295 temp = buffer.readLine(); 296 } 297 } catch (java.io.IOException e1) { 298 log.error("error reading file", e1); 299 throw e1; 300 } finally { 301 try { 302 buffer.close(); 303 } catch (java.io.IOException e2) { 304 log.error("Exception closing file", e2); 305 } 306 try { 307 reader.close(); 308 } catch (java.io.IOException e2) { 309 log.error("Exception closing file", e2); 310 } 311 } 312 313 if (found) { 314 return Integer.parseInt(value); 315 } 316 } catch (IOException e) { 317 log.error("Error parsing KDI_CONFIG: {}", e.getMessage()); 318 } 319 return -1; 320 } 321 322 private final static Logger log = LoggerFactory.getLogger(JmriInsets.class); 323}