1/*
2 * Copyright (c) 2003, 2013, 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
26package sun.awt.X11;
27
28import java.util.*;
29import jdk.internal.misc.Unsafe;
30
31public class WindowPropertyGetter {
32    private static Unsafe unsafe = XlibWrapper.unsafe;
33    private final long actual_type = unsafe.allocateMemory(8);
34    private final long actual_format = unsafe.allocateMemory(4);
35    private final long nitems_ptr = unsafe.allocateMemory(8);
36    private final long bytes_after = unsafe.allocateMemory(8);
37    private final long data = unsafe.allocateMemory(8);
38    private final long window;
39    private final XAtom property;
40    private final long offset;
41    private final long length;
42    private final boolean auto_delete;
43    private final long type;
44    private boolean executed = false;
45    public WindowPropertyGetter(long window, XAtom property, long offset,
46                                long length, boolean auto_delete, long type)
47    {
48        if (property.getAtom() == 0) {
49            throw new IllegalArgumentException("Property ATOM should be initialized first:" + property);
50        }
51        // Zero is AnyPropertyType.
52        // if (type == 0) {
53        //     throw new IllegalArgumentException("Type ATOM shouldn't be zero");
54        // }
55        if (window == 0) {
56            throw new IllegalArgumentException("Window must not be zero");
57        }
58        this.window = window;
59        this.property = property;
60        this.offset = offset;
61        this.length = length;
62        this.auto_delete = auto_delete;
63        this.type = type;
64
65        Native.putLong(data, 0);
66        sun.java2d.Disposer.addRecord(this, disposer = new UnsafeXDisposerRecord("WindowPropertyGetter", new long[] {actual_type,
67                                                                                 actual_format, nitems_ptr, bytes_after}, new long[] {data}));
68    }
69    UnsafeXDisposerRecord disposer;
70    public WindowPropertyGetter(long window, XAtom property, long offset,
71                                long length, boolean auto_delete, XAtom type)
72    {
73        this(window, property, offset, length, auto_delete, type.getAtom());
74    }
75    public int execute() {
76        return execute(null);
77    }
78    public int execute(XErrorHandler errorHandler) {
79
80        XToolkit.awtLock();
81        try {
82            if (isDisposed()) {
83                throw new IllegalStateException("Disposed");
84            }
85            if (executed) {
86                throw new IllegalStateException("Already executed");
87            }
88            executed = true;
89
90            if (isCachingSupported() && isCached()) {
91                readFromCache();
92                return XConstants.Success;
93            }
94
95            // Fix for performance problem - IgnodeBadWindowHandler is
96            // used too much without reason, just ignore it
97            if (errorHandler instanceof XErrorHandler.IgnoreBadWindowHandler) {
98                errorHandler = null;
99            }
100
101            if (errorHandler != null) {
102                XErrorHandlerUtil.WITH_XERROR_HANDLER(errorHandler);
103            }
104            Native.putLong(data, 0);
105            int status = XlibWrapper.XGetWindowProperty(XToolkit.getDisplay(), window, property.getAtom(),
106                                                        offset, length, (auto_delete?1:0), type,
107                                                        actual_type, actual_format, nitems_ptr,
108                                                        bytes_after, data);
109            if (isCachingSupported() &&  status == XConstants.Success && getData() != 0 && isCacheableProperty(property)) {
110                // Property has some data, we cache them
111                cacheProperty();
112            }
113
114            if (errorHandler != null) {
115                XErrorHandlerUtil.RESTORE_XERROR_HANDLER();
116            }
117            return status;
118        } finally {
119            XToolkit.awtUnlock();
120        }
121    }
122
123    public boolean isExecuted() {
124        return executed;
125    }
126
127    public boolean isDisposed() {
128        return disposer.disposed;
129    }
130
131    public int getActualFormat() {
132        if (isDisposed()) {
133            throw new IllegalStateException("Disposed");
134        }
135        if (!executed) {
136            throw new IllegalStateException("Not executed");
137        }
138        return unsafe.getInt(actual_format);
139    }
140    public long getActualType() {
141        if (isDisposed()) {
142            throw new IllegalStateException("Disposed");
143        }
144        if (!executed) {
145            throw new IllegalStateException("Not executed");
146        }
147        return XAtom.getAtom(actual_type);
148    }
149    public int getNumberOfItems() {
150        if (isDisposed()) {
151            throw new IllegalStateException("Disposed");
152        }
153        if (!executed) {
154            throw new IllegalStateException("Not executed");
155        }
156        return (int)Native.getLong(nitems_ptr);
157    }
158    public long getData() {
159        if (isDisposed()) {
160            throw new IllegalStateException("Disposed");
161        }
162        return Native.getLong(data);
163    }
164    public long getBytesAfter() {
165        if (isDisposed()) {
166            throw new IllegalStateException("Disposed");
167        }
168        if (!executed) {
169            throw new IllegalStateException("Not executed");
170        }
171        return Native.getLong(bytes_after);
172    }
173    public void dispose() {
174        XToolkit.awtLock();
175        try {
176            if (isDisposed()) {
177                return;
178            }
179            disposer.dispose();
180        } finally {
181            XToolkit.awtUnlock();
182        }
183    }
184
185    static boolean isCachingSupported() {
186        return XPropertyCache.isCachingSupported();
187    }
188
189    static Set<XAtom> cacheableProperties = new HashSet<XAtom>(Arrays.asList(new XAtom[] {
190            XAtom.get("_NET_WM_STATE"), XAtom.get("WM_STATE"), XAtom.get("_MOTIF_WM_HINTS")}));
191
192    static boolean isCacheableProperty(XAtom property) {
193        return cacheableProperties.contains(property);
194    }
195
196    boolean isCached() {
197        return XPropertyCache.isCached(window, property);
198    }
199
200    int getDataLength() {
201        return getActualFormat() / 8 * getNumberOfItems();
202    }
203
204    void readFromCache() {
205        property.putAtom(actual_type);
206        XPropertyCache.PropertyCacheEntry entry = XPropertyCache.getCacheEntry(window, property);
207        Native.putInt(actual_format, entry.getFormat());
208        Native.putLong(nitems_ptr, entry.getNumberOfItems());
209        Native.putLong(bytes_after, entry.getBytesAfter());
210        Native.putLong(data, unsafe.allocateMemory(getDataLength()));
211        XlibWrapper.memcpy(getData(), entry.getData(), getDataLength());
212    }
213
214    void cacheProperty() {
215        XPropertyCache.storeCache(
216            new XPropertyCache.PropertyCacheEntry(getActualFormat(),
217                                                  getNumberOfItems(),
218                                                  getBytesAfter(),
219                                                  getData(),
220                                                  getDataLength()),
221            window,
222            property);
223    }
224
225}
226