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</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 }