001: /*
002: * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.awt;
027:
028: import java.awt.Color;
029:
030: import java.io.UnsupportedEncodingException;
031:
032: import java.util.HashMap;
033: import java.util.Map;
034:
035: /**
036: * Per-screen XSETTINGS.
037: */
038: public class XSettings {
039:
040: /**
041: */
042: private long serial = -1;
043:
044: /**
045: * Update these settings with <code>data</code> obtained from
046: * XSETTINGS manager.
047: *
048: * @param data settings data obtained from
049: * <code>_XSETTINGS_SETTINGS</code> window property of the
050: * settings manager.
051: * @return a <code>Map</code> of changed settings.
052: */
053: public Map update(byte[] data) {
054: return (new Update(data)).update();
055: }
056:
057: /**
058: * TBS ...
059: */
060: class Update {
061:
062: /* byte order mark */
063: private static final int LITTLE_ENDIAN = 0;
064: private static final int BIG_ENDIAN = 1;
065:
066: /* setting type */
067: private static final int TYPE_INTEGER = 0;
068: private static final int TYPE_STRING = 1;
069: private static final int TYPE_COLOR = 2;
070:
071: private byte[] data;
072: private int dlen;
073: private int idx;
074: private boolean isLittle;
075: private long serial = -1;
076: private int nsettings = 0;
077: private boolean isValid;
078:
079: private HashMap updatedSettings;
080:
081: /**
082: * Construct an Update object for the data read from
083: * <code>_XSETTINGS_SETTINGS</code> property of the XSETTINGS
084: * selection owner.
085: *
086: * @param data <code>_XSETTINGS_SETTINGS</code> contents.
087: */
088: Update(byte[] data) {
089: this .data = data;
090:
091: dlen = data.length;
092: if (dlen < 12) {
093: // XXX: debug trace?
094: return;
095: }
096:
097: // first byte gives endianness of the data
098: // next 3 bytes are unused (pad to 32 bit)
099: idx = 0;
100: isLittle = (getCARD8() == LITTLE_ENDIAN);
101:
102: idx = 4;
103: serial = getCARD32();
104:
105: // N_SETTINGS is actually CARD32 (i.e. unsigned), but
106: // since java doesn't have an unsigned int type, and
107: // N_SETTINGS cannot realistically exceed 2^31 (so we
108: // gonna use int anyway), just read it as INT32.
109: idx = 8;
110: nsettings = getINT32();
111:
112: updatedSettings = new HashMap();
113:
114: isValid = true;
115: }
116:
117: private void needBytes(int n) throws IndexOutOfBoundsException {
118: if (idx + n <= dlen) {
119: return;
120: }
121:
122: throw new IndexOutOfBoundsException("at " + idx + " need "
123: + n + " length " + dlen);
124: }
125:
126: private int getCARD8() throws IndexOutOfBoundsException {
127: needBytes(1);
128:
129: int val = data[idx] & 0xff;
130:
131: ++idx;
132: return val;
133: }
134:
135: private int getCARD16() throws IndexOutOfBoundsException {
136: needBytes(2);
137:
138: int val;
139: if (isLittle) {
140: val = ((data[idx + 0] & 0xff))
141: | ((data[idx + 1] & 0xff) << 8);
142: } else {
143: val = ((data[idx + 0] & 0xff) << 8)
144: | ((data[idx + 1] & 0xff));
145: }
146:
147: idx += 2;
148: return val;
149: }
150:
151: private int getINT32() throws IndexOutOfBoundsException {
152: needBytes(4);
153:
154: int val;
155: if (isLittle) {
156: val = ((data[idx + 0] & 0xff))
157: | ((data[idx + 1] & 0xff) << 8)
158: | ((data[idx + 2] & 0xff) << 16)
159: | ((data[idx + 3] & 0xff) << 24);
160: } else {
161: val = ((data[idx + 0] & 0xff) << 24)
162: | ((data[idx + 1] & 0xff) << 16)
163: | ((data[idx + 2] & 0xff) << 8)
164: | ((data[idx + 3] & 0xff) << 0);
165: }
166:
167: idx += 4;
168: return val;
169: }
170:
171: private long getCARD32() throws IndexOutOfBoundsException {
172: return getINT32() & 0x00000000ffffffffL;
173: }
174:
175: private String getString(int len)
176: throws IndexOutOfBoundsException {
177: needBytes(len);
178:
179: String str = null;
180: try {
181: str = new String(data, idx, len, "UTF-8");
182: } catch (UnsupportedEncodingException e) {
183: // XXX: cannot happen, "UTF-8" is always supported
184: }
185:
186: idx = (idx + len + 3) & ~0x3;
187: return str;
188: }
189:
190: /**
191: * Update settings.
192: */
193: public Map update() {
194: if (!isValid) {
195: return null;
196: }
197:
198: synchronized (XSettings.this ) {
199: long currentSerial = XSettings.this .serial;
200:
201: if (this .serial <= currentSerial) {
202: return null;
203: }
204:
205: for (int i = 0; i < nsettings && idx < dlen; ++i) {
206: updateOne(currentSerial);
207: }
208:
209: XSettings.this .serial = this .serial;
210: }
211:
212: return updatedSettings;
213: }
214:
215: /**
216: * Parses a particular x setting.
217: *
218: * @exception IndexOutOfBoundsException if there isn't enough
219: * data for a setting.
220: */
221: private void updateOne(long currentSerial)
222: throws IndexOutOfBoundsException,
223: IllegalArgumentException {
224: int type = getCARD8();
225: ++idx; // pad to next CARD16
226:
227: // save position of the property name, skip to serial
228: int nameLen = getCARD16();
229: int nameIdx = idx;
230:
231: // check if we should bother
232: idx = (idx + nameLen + 3) & ~0x3; // pad to 32 bit
233: long lastChanged = getCARD32();
234:
235: // Avoid constructing garbage for properties that has not
236: // changed, skip the data for this property.
237: if (lastChanged <= currentSerial) { // skip
238: if (type == TYPE_INTEGER) {
239: idx += 4;
240: } else if (type == TYPE_STRING) {
241: int len = getINT32();
242: idx = (idx + len + 3) & ~0x3;
243: } else if (type == TYPE_COLOR) {
244: idx += 8; // 4 CARD16
245: } else {
246: throw new IllegalArgumentException("Unknown type: "
247: + type);
248: }
249:
250: return;
251: }
252:
253: idx = nameIdx;
254: String name = getString(nameLen);
255: idx += 4; // skip serial, parsed above
256:
257: Object value = null;
258: if (type == TYPE_INTEGER) {
259: value = Integer.valueOf(getINT32());
260: } else if (type == TYPE_STRING) {
261: value = getString(getINT32());
262: } else if (type == TYPE_COLOR) {
263: int r = getCARD16();
264: int g = getCARD16();
265: int b = getCARD16();
266: int a = getCARD16();
267:
268: value = new Color(r / 65535.0f, g / 65535.0f,
269: b / 65535.0f, a / 65535.0f);
270: } else {
271: throw new IllegalArgumentException("Unknown type: "
272: + type);
273: }
274:
275: if (name == null) {
276: // dtrace???
277: return;
278: }
279:
280: updatedSettings.put(name, value);
281: }
282:
283: } // class XSettings.Update
284: }
|