1 /*
   2  * Copyright (c) 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 import java.awt.BorderLayout;
  24 import java.awt.Color;
  25 import java.awt.Cursor;
  26 import java.awt.Dialog;
  27 import java.awt.Frame;
  28 import java.awt.Graphics;
  29 import java.awt.Graphics2D;
  30 import java.awt.Image;
  31 import java.awt.Label;
  32 import java.awt.Point;
  33 import java.awt.TextArea;
  34 import java.awt.Toolkit;
  35 import java.awt.geom.AffineTransform;
  36 import java.awt.image.BufferedImage;
  37 import java.util.LinkedList;
  38 import java.util.List;
  39 import javax.swing.JApplet;
  40 import sun.awt.OSInfo;
  41 import java.awt.image.MultiResolutionImage;
  42 import sun.awt.image.ImageResolutionHelper;
  43 import sun.awt.image.ImageResolutionHelperType;
  44 import static sun.awt.image.ImageResolutionHelperType.DPI_AWARE;
  45 import static sun.awt.image.ImageResolutionHelperType.TRANSFORM_AWARE;
  46 
  47 /**
  48  * @test
  49  * @bug 8028212
  50  * @summary [macosx] Custom Cursor HiDPI support
  51  * @author Alexander Scherbatiy
  52  * @run applet/manual=yesno MultiResolutionCursorTest.html
  53  */
  54 public class MultiResolutionCursorTest extends JApplet {
  55     //Declare things used in the test, like buttons and labels here
  56 
  57     static final int sizes[] = {16, 32, 128};
  58     static final Color colors[] = {Color.WHITE, Color.RED, Color.GREEN, Color.BLUE};
  59 
  60     public void init() {
  61         //Create instructions for the user here, as well as set up
  62         // the environment -- set the layout manager, add buttons,
  63         // etc.
  64         this.setLayout(new BorderLayout());
  65 
  66         if (OSInfo.getOSType().equals(OSInfo.OSType.MACOSX)) {
  67             String[] instructions = {
  68                 "Verify that high resolution custom cursor is used"
  69                 + " on HiDPI displays.",
  70                 "1) Run the test on Retina display or enable the Quartz Debug"
  71                 + " and select the screen resolution with (HiDPI) label",
  72                 "2) Move the cursor to the Test Frame",
  73                 "3) Check that cursor has red, green or blue color",
  74                 "If so, press PASS, else press FAIL."
  75             };
  76             Sysout.createDialogWithInstructions(instructions);
  77 
  78         } else {
  79             String[] instructions = {
  80                 "This test is not applicable to the current platform. Press PASS."
  81             };
  82             Sysout.createDialogWithInstructions(instructions);
  83         }
  84     }//End  init()
  85 
  86     public void start() {
  87         //Get things going.  Request focus, set size, et cetera
  88         setSize(200, 200);
  89         setVisible(true);
  90         validate();
  91 
  92         final Image image = new MultiResolutionCursor();
  93 
  94         int center = sizes[0] / 2;
  95         Cursor cursor = Toolkit.getDefaultToolkit().createCustomCursor(
  96                 image, new Point(center, center), "multi-resolution cursor");
  97 
  98         Frame frame = new Frame("Test Frame");
  99         frame.setSize(300, 300);
 100         frame.setLocation(300, 50);
 101         frame.add(new Label("Move cursor here"));
 102         frame.setCursor(cursor);
 103         frame.setVisible(true);
 104     }// start()
 105 
 106 
 107     static class MultiResolutionCursor extends BufferedImage implements MultiResolutionImage {
 108 
 109         List<Image> highResolutionImages;
 110 
 111         public MultiResolutionCursor() {
 112             super(sizes[0], sizes[0], BufferedImage.TYPE_INT_RGB);
 113 
 114             draw(getGraphics(), 0);
 115             highResolutionImages = new LinkedList<>();
 116             highResolutionImages.add(this);
 117 
 118             for (int i = 1; i < sizes.length; i++) {
 119                 BufferedImage highResolutionImage =
 120                         new BufferedImage(sizes[i], sizes[i], BufferedImage.TYPE_INT_RGB);
 121                 draw(highResolutionImage.getGraphics(), i);
 122                 highResolutionImages.add(highResolutionImage);
 123             }
 124         }
 125 
 126         @Override
 127         public Image getResolutionVariant(float logicalDPIX, float logicalDPIY,
 128                 float baseWidth, float baseHeight, float destWidth, float destHeight) {
 129             ImageResolutionHelper helper = getPlatformHelper();
 130             float w = helper.getHiDPISize(logicalDPIX, baseWidth, destWidth);
 131             float h = helper.getHiDPISize(logicalDPIY, baseHeight, destHeight);
 132             return getResolutionVariant(w, h);
 133         }
 134 
 135         private Image getResolutionVariant(float width, float height) {
 136 
 137             for (int i = 0; i < sizes.length; i++) {
 138                 Image image = highResolutionImages.get(i);
 139                 int w = image.getWidth(null);
 140                 int h = image.getHeight(null);
 141 
 142                 if (width <= w && height <= h) {
 143                     return image;
 144                 }
 145             }
 146 
 147             return highResolutionImages.get(highResolutionImages.size() - 1);
 148         }
 149 
 150         void draw(Graphics graphics, int index) {
 151             Graphics2D g2 = (Graphics2D) graphics;
 152             Color color = colors[index];
 153             g2.setColor(color);
 154             g2.fillRect(0, 0, sizes[index], sizes[index]);
 155         }
 156 
 157         @Override
 158         public List<Image> getResolutionVariants() {
 159             return highResolutionImages;
 160         }
 161 
 162         static ImageResolutionHelper getPlatformHelper() {
 163             return OSInfo.OSType.WINDOWS.equals(OSInfo.getOSType())
 164                     ? DPI_AWARE : TRANSFORM_AWARE;
 165         }
 166 
 167     }
 168 }// class BlockedWindowTest
 169 
 170 /* Place other classes related to the test after this line */
 171 /**
 172  * **************************************************
 173  * Standard Test Machinery DO NOT modify anything below -- it's a standard chunk
 174  * of code whose purpose is to make user interaction uniform, and thereby make
 175  * it simpler to read and understand someone else's test.
 176  * **************************************************
 177  */
 178 /**
 179  * This is part of the standard test machinery. It creates a dialog (with the
 180  * instructions), and is the interface for sending text messages to the user. To
 181  * print the instructions, send an array of strings to Sysout.createDialog
 182  * WithInstructions method. Put one line of instructions per array entry. To
 183  * display a message for the tester to see, simply call Sysout.println with the
 184  * string to be displayed. This mimics System.out.println but works within the
 185  * test harness as well as standalone.
 186  */
 187 class Sysout {
 188 
 189     private static TestDialog dialog;
 190 
 191     public static void createDialogWithInstructions(String[] instructions) {
 192         dialog = new TestDialog(new Frame(), "Instructions");
 193         dialog.printInstructions(instructions);
 194         dialog.setVisible(true);
 195         println("Any messages for the tester will display here.");
 196     }
 197 
 198     public static void createDialog() {
 199         dialog = new TestDialog(new Frame(), "Instructions");
 200         String[] defInstr = {"Instructions will appear here. ", ""};
 201         dialog.printInstructions(defInstr);
 202         dialog.setVisible(true);
 203         println("Any messages for the tester will display here.");
 204     }
 205 
 206     public static void printInstructions(String[] instructions) {
 207         dialog.printInstructions(instructions);
 208     }
 209 
 210     public static void println(String messageIn) {
 211         dialog.displayMessage(messageIn);
 212     }
 213 }// Sysout  class
 214 
 215 /**
 216  * This is part of the standard test machinery. It provides a place for the test
 217  * instructions to be displayed, and a place for interactive messages to the
 218  * user to be displayed. To have the test instructions displayed, see Sysout. To
 219  * have a message to the user be displayed, see Sysout. Do not call anything in
 220  * this dialog directly.
 221  */
 222 class TestDialog extends Dialog {
 223 
 224     TextArea instructionsText;
 225     TextArea messageText;
 226     int maxStringLength = 80;
 227 
 228     //DO NOT call this directly, go through Sysout
 229     public TestDialog(Frame frame, String name) {
 230         super(frame, name);
 231         int scrollBoth = TextArea.SCROLLBARS_BOTH;
 232         instructionsText = new TextArea("", 15, maxStringLength, scrollBoth);
 233         add("North", instructionsText);
 234 
 235         messageText = new TextArea("", 5, maxStringLength, scrollBoth);
 236         add("Center", messageText);
 237 
 238         pack();
 239 
 240         setVisible(true);
 241     }// TestDialog()
 242 
 243     //DO NOT call this directly, go through Sysout
 244     public void printInstructions(String[] instructions) {
 245         //Clear out any current instructions
 246         instructionsText.setText("");
 247 
 248         //Go down array of instruction strings
 249 
 250         String printStr, remainingStr;
 251         for (int i = 0; i < instructions.length; i++) {
 252             //chop up each into pieces maxSringLength long
 253             remainingStr = instructions[ i];
 254             while (remainingStr.length() > 0) {
 255                 //if longer than max then chop off first max chars to print
 256                 if (remainingStr.length() >= maxStringLength) {
 257                     //Try to chop on a word boundary
 258                     int posOfSpace = remainingStr.lastIndexOf(' ', maxStringLength - 1);
 259 
 260                     if (posOfSpace <= 0) {
 261                         posOfSpace = maxStringLength - 1;
 262                     }
 263 
 264                     printStr = remainingStr.substring(0, posOfSpace + 1);
 265                     remainingStr = remainingStr.substring(posOfSpace + 1);
 266                 } //else just print
 267                 else {
 268                     printStr = remainingStr;
 269                     remainingStr = "";
 270                 }
 271 
 272                 instructionsText.append(printStr + "\n");
 273 
 274             }// while
 275 
 276         }// for
 277 
 278     }//printInstructions()
 279 
 280     //DO NOT call this directly, go through Sysout
 281     public void displayMessage(String messageIn) {
 282         messageText.append(messageIn + "\n");
 283         System.out.println(messageIn);
 284     }
 285 }// Te