1 /* 2 * Copyright (c) 2011, 2014, 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.lwawt.macosx; 27 28 import java.awt.*; 29 import java.awt.image.BufferedImage; 30 31 @SuppressWarnings("serial") // JDK implementation class 32 public class CCustomCursor extends Cursor { 33 static Dimension sMaxCursorSize; 34 static Dimension getMaxCursorSize() { 35 if (sMaxCursorSize != null) return sMaxCursorSize; 36 final Rectangle bounds = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().getBounds(); 37 return sMaxCursorSize = new Dimension(bounds.width / 2, bounds.height / 2); 38 } 39 40 Image fImage; 41 Point fHotspot; 42 int fWidth; 43 int fHeight; 44 45 public CCustomCursor(final Image cursor, final Point hotSpot, final String name) throws IndexOutOfBoundsException, HeadlessException { 46 super(name); 47 fImage = cursor; 48 fHotspot = hotSpot; 49 50 // This chunk of code is copied from sun.awt.CustomCursor 51 final Toolkit toolkit = Toolkit.getDefaultToolkit(); 52 53 // Make sure image is fully loaded. 54 final Component c = new Canvas(); // for its imageUpdate method 55 final MediaTracker tracker = new MediaTracker(c); 56 // MediaTracker loads resolution variants from MultiResolution Toolkit image 57 tracker.addImage(fImage, 0); 58 try { 59 tracker.waitForAll(); 60 } catch (final InterruptedException e) {} 61 62 int width = fImage.getWidth(c); 63 int height = fImage.getHeight(c); 64 65 // Fix for bug 4212593 The Toolkit.createCustomCursor does not 66 // check absence of the image of cursor 67 // If the image is invalid, the cursor will be hidden (made completely 68 // transparent). 69 if (tracker.isErrorAny() || width < 0 || height < 0) { 70 fHotspot.x = fHotspot.y = 0; 71 width = height = 1; 72 fImage = createTransparentImage(width, height); 73 } else { 74 // Get the nearest supported cursor size 75 final Dimension nativeSize = toolkit.getBestCursorSize(width, height); 76 width = nativeSize.width; 77 height = nativeSize.height; 78 } 79 80 fWidth = width; 81 fHeight = height; 82 83 // NOTE: this was removed for 3169146, but in 1.5 the JCK tests for an exception and fails if one isn't thrown. 84 // See what JBuilder does. 85 // Verify that the hotspot is within cursor bounds. 86 if (fHotspot.x >= width || fHotspot.y >= height || fHotspot.x < 0 || fHotspot.y < 0) { 87 throw new IndexOutOfBoundsException("invalid hotSpot"); 88 } 89 90 // Must normalize the hotspot 91 if (fHotspot.x >= width) { 92 fHotspot.x = width - 1; // it is zero based. 93 } else if (fHotspot.x < 0) { 94 fHotspot.x = 0; 95 } 96 if (fHotspot.y >= height) { 97 fHotspot.y = height - 1; // it is zero based. 98 } else if (fHotspot.y < 0) { 99 fHotspot.y = 0; 100 } 101 } 102 103 private static BufferedImage createTransparentImage(int w, int h) { 104 GraphicsEnvironment ge = 105 GraphicsEnvironment.getLocalGraphicsEnvironment(); 106 GraphicsDevice gs = ge.getDefaultScreenDevice(); 107 GraphicsConfiguration gc = gs.getDefaultConfiguration(); 108 109 BufferedImage img = gc.createCompatibleImage(w, h, Transparency.BITMASK); 110 Graphics2D g = (Graphics2D)img.getGraphics(); 111 g.setBackground(new Color(0, 0, 0, 0)); 112 g.clearRect(0, 0, w, h); 113 g.dispose(); 114 115 return img; 116 } 117 118 public static Dimension getBestCursorSize(final int preferredWidth, final int preferredHeight) { 119 // With Panther, cursors have no limit on their size. So give the client their 120 // preferred size, but no larger than half the dimensions of the main screen 121 // This will allow large cursors, but not cursors so large that they cover the 122 // screen. Since solaris nor windows allow cursors this big, this shouldn't be 123 // a limitation. 124 // JCK triggers an overflow in the int -- if we get a bizarre value normalize it. 125 final Dimension maxCursorSize = getMaxCursorSize(); 126 final Dimension d = new Dimension(Math.max(1, Math.abs(preferredWidth)), Math.max(1, Math.abs(preferredHeight))); 127 return new Dimension(Math.min(d.width, maxCursorSize.width), Math.min(d.height, maxCursorSize.height)); 128 } 129 130 // Called from native when the cursor is set 131 CImage fCImage; 132 long getImageData() { 133 if (fCImage != null) { 134 return fCImage.ptr; 135 } 136 137 try { 138 fCImage = CImage.getCreator().createFromImage(fImage); 139 if (fCImage == null) { 140 // Something unexpected happened: CCustomCursor constructor 141 // takes care of invalid cursor images, yet createFromImage() 142 // failed to do its job. Return null to keep the cursor unchanged. 143 return 0L; 144 } else { 145 fCImage.resizeRepresentations(fWidth, fHeight); 146 return fCImage.ptr; 147 } 148 } catch (IllegalArgumentException iae) { 149 // see comment above 150 return 0L; 151 } 152 } 153 154 Point getHotSpot() { 155 return fHotspot; 156 } 157 }