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