1 /* 2 * Copyright (c) 2003, 2004, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.awt; 27 28 import java.awt.Color; 29 30 import java.io.UnsupportedEncodingException; 31 32 import java.util.HashMap; 33 import java.util.Map; 34 35 36 /** 37 * Per-screen XSETTINGS. 38 */ 39 public class XSettings { 40 41 /** 42 */ 43 private long serial = -1; 44 45 46 /** 47 * Update these settings with {@code data} obtained from 48 * XSETTINGS manager. 49 * 50 * @param data settings data obtained from 51 * {@code _XSETTINGS_SETTINGS} window property of the 52 * settings manager. 53 * @return a {@code Map} of changed settings. 54 */ update(byte[] data)55 public Map<String, Object> update(byte[] data) { 56 return (new Update(data)).update(); 57 } 58 59 60 /** 61 * TBS ... 62 */ 63 class Update { 64 65 /* byte order mark */ 66 private static final int LITTLE_ENDIAN = 0; 67 private static final int BIG_ENDIAN = 1; 68 69 /* setting type */ 70 private static final int TYPE_INTEGER = 0; 71 private static final int TYPE_STRING = 1; 72 private static final int TYPE_COLOR = 2; 73 74 private byte[] data; 75 private int dlen; 76 private int idx; 77 private boolean isLittle; 78 private long serial = -1; 79 private int nsettings = 0; 80 private boolean isValid; 81 82 private HashMap<String, Object> updatedSettings; 83 84 85 /** 86 * Construct an Update object for the data read from 87 * {@code _XSETTINGS_SETTINGS} property of the XSETTINGS 88 * selection owner. 89 * 90 * @param data {@code _XSETTINGS_SETTINGS} contents. 91 */ Update(byte[] data)92 Update(byte[] data) { 93 this.data = data; 94 95 dlen = data.length; 96 if (dlen < 12) { 97 // XXX: debug trace? 98 return; 99 } 100 101 // first byte gives endianness of the data 102 // next 3 bytes are unused (pad to 32 bit) 103 idx = 0; 104 isLittle = (getCARD8() == LITTLE_ENDIAN); 105 106 idx = 4; 107 serial = getCARD32(); 108 109 // N_SETTINGS is actually CARD32 (i.e. unsigned), but 110 // since java doesn't have an unsigned int type, and 111 // N_SETTINGS cannot realistically exceed 2^31 (so we 112 // gonna use int anyway), just read it as INT32. 113 idx = 8; 114 nsettings = getINT32(); 115 116 updatedSettings = new HashMap<>(); 117 118 isValid = true; 119 } 120 121 needBytes(int n)122 private void needBytes(int n) 123 throws IndexOutOfBoundsException 124 { 125 if (idx + n <= dlen) { 126 return; 127 } 128 129 throw new IndexOutOfBoundsException("at " + idx 130 + " need " + n 131 + " length " + dlen); 132 } 133 134 getCARD8()135 private int getCARD8() 136 throws IndexOutOfBoundsException 137 { 138 needBytes(1); 139 140 int val = data[idx] & 0xff; 141 142 ++idx; 143 return val; 144 } 145 146 getCARD16()147 private int getCARD16() 148 throws IndexOutOfBoundsException 149 { 150 needBytes(2); 151 152 int val; 153 if (isLittle) { 154 val = ((data[idx + 0] & 0xff) ) 155 | ((data[idx + 1] & 0xff) << 8); 156 } else { 157 val = ((data[idx + 0] & 0xff) << 8) 158 | ((data[idx + 1] & 0xff) ); 159 } 160 161 idx += 2; 162 return val; 163 } 164 165 getINT32()166 private int getINT32() 167 throws IndexOutOfBoundsException 168 { 169 needBytes(4); 170 171 int val; 172 if (isLittle) { 173 val = ((data[idx + 0] & 0xff) ) 174 | ((data[idx + 1] & 0xff) << 8) 175 | ((data[idx + 2] & 0xff) << 16) 176 | ((data[idx + 3] & 0xff) << 24); 177 } else { 178 val = ((data[idx + 0] & 0xff) << 24) 179 | ((data[idx + 1] & 0xff) << 16) 180 | ((data[idx + 2] & 0xff) << 8) 181 | ((data[idx + 3] & 0xff) << 0); 182 } 183 184 idx += 4; 185 return val; 186 } 187 188 getCARD32()189 private long getCARD32() 190 throws IndexOutOfBoundsException 191 { 192 return getINT32() & 0x00000000ffffffffL; 193 } 194 195 getString(int len)196 private String getString(int len) 197 throws IndexOutOfBoundsException 198 { 199 needBytes(len); 200 201 String str = null; 202 try { 203 str = new String(data, idx, len, "UTF-8"); 204 } catch (UnsupportedEncodingException e) { 205 // XXX: cannot happen, "UTF-8" is always supported 206 } 207 208 idx = (idx + len + 3) & ~0x3; 209 return str; 210 } 211 212 213 /** 214 * Update settings. 215 */ update()216 public Map<String, Object> update() { 217 if (!isValid) { 218 return null; 219 } 220 221 synchronized (XSettings.this) { 222 long currentSerial = XSettings.this.serial; 223 224 if (this.serial <= currentSerial) { 225 return null; 226 } 227 228 for (int i = 0; i < nsettings && idx < dlen; ++i) { 229 updateOne(currentSerial); 230 } 231 232 XSettings.this.serial = this.serial; 233 } 234 235 return updatedSettings; 236 } 237 238 239 /** 240 * Parses a particular x setting. 241 * 242 * @exception IndexOutOfBoundsException if there isn't enough 243 * data for a setting. 244 */ updateOne(long currentSerial)245 private void updateOne(long currentSerial) 246 throws IndexOutOfBoundsException, 247 IllegalArgumentException 248 { 249 int type = getCARD8(); 250 ++idx; // pad to next CARD16 251 252 // save position of the property name, skip to serial 253 int nameLen = getCARD16(); 254 int nameIdx = idx; 255 256 // check if we should bother 257 idx = (idx + nameLen + 3) & ~0x3; // pad to 32 bit 258 long lastChanged = getCARD32(); 259 260 // Avoid constructing garbage for properties that has not 261 // changed, skip the data for this property. 262 if (lastChanged <= currentSerial) { // skip 263 if (type == TYPE_INTEGER) { 264 idx += 4; 265 } else if (type == TYPE_STRING) { 266 int len = getINT32(); 267 idx = (idx + len + 3) & ~0x3; 268 } else if (type == TYPE_COLOR) { 269 idx += 8; // 4 CARD16 270 } else { 271 throw new IllegalArgumentException("Unknown type: " 272 + type); 273 } 274 275 return; 276 } 277 278 idx = nameIdx; 279 String name = getString(nameLen); 280 idx += 4; // skip serial, parsed above 281 282 Object value = null; 283 if (type == TYPE_INTEGER) { 284 value = Integer.valueOf(getINT32()); 285 } 286 else if (type == TYPE_STRING) { 287 value = getString(getINT32()); 288 } 289 else if (type == TYPE_COLOR) { 290 int r = getCARD16(); 291 int g = getCARD16(); 292 int b = getCARD16(); 293 int a = getCARD16(); 294 295 value = new Color(r / 65535.0f, 296 g / 65535.0f, 297 b / 65535.0f, 298 a / 65535.0f); 299 } 300 else { 301 throw new IllegalArgumentException("Unknown type: " + type); 302 } 303 304 if (name == null) { 305 // dtrace??? 306 return; 307 } 308 309 updatedSettings.put(name, value); 310 } 311 312 } // class XSettings.Update 313 } 314