J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2003-2004 Sun Microsystems, Inc. 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. Sun designates this |
| 8 | * particular file as subject to the "Classpath" exception as provided |
| 9 | * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| 22 | * CA 95054 USA or visit www.sun.com if you need additional information or |
| 23 | * have any 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</code> obtained from |
| 48 | * XSETTINGS manager. |
| 49 | * |
| 50 | * @param data settings data obtained from |
| 51 | * <code>_XSETTINGS_SETTINGS</code> window property of the |
| 52 | * settings manager. |
| 53 | * @return a <code>Map</code> of changed settings. |
| 54 | */ |
| 55 | public Map 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 updatedSettings; |
| 83 | |
| 84 | |
| 85 | /** |
| 86 | * Construct an Update object for the data read from |
| 87 | * <code>_XSETTINGS_SETTINGS</code> property of the XSETTINGS |
| 88 | * selection owner. |
| 89 | * |
| 90 | * @param data <code>_XSETTINGS_SETTINGS</code> contents. |
| 91 | */ |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 189 | private long getCARD32() |
| 190 | throws IndexOutOfBoundsException |
| 191 | { |
| 192 | return getINT32() & 0x00000000ffffffffL; |
| 193 | } |
| 194 | |
| 195 | |
| 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 | */ |
| 216 | public Map 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 | */ |
| 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 | } |