1 /*
   2  * Copyright (c) 2010, 2015, 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 package com.sun.glass.ui.win;
  26 
  27 import com.sun.glass.ui.*;
  28 import com.sun.glass.ui.CommonDialogs.ExtensionFilter;
  29 import com.sun.glass.ui.CommonDialogs.FileChooserResult;
  30 import com.sun.glass.utils.NativeLibLoader;
  31 import com.sun.prism.impl.PrismSettings;
  32 
  33 import java.io.File;
  34 import java.nio.ByteBuffer;
  35 import java.nio.IntBuffer;
  36 import java.security.AccessController;
  37 import java.security.PrivilegedAction;
  38 
  39 final class WinApplication extends Application implements InvokeLaterDispatcher.InvokeLaterSubmitter {
  40     static float   overrideUIScale;
  41     static float   overrideRenderScale;
  42     static float   minDPIScale;
  43     static boolean forceIntegerRenderScale;
  44 
  45     private static boolean getBoolean(String propname, boolean defval, String description) {
  46         String str = System.getProperty(propname);
  47         if (str == null) {
  48             str = System.getenv(propname);
  49         }
  50         if (str == null) {
  51             return defval;
  52         }
  53         Boolean ret = Boolean.parseBoolean(str);
  54         if (PrismSettings.verbose) {
  55             System.out.println((ret ? "" : "not ")+description);
  56         }
  57         return ret;
  58     }
  59 
  60     private static float getFloat(String propname, float defval, String description) {
  61         String str = System.getProperty(propname);
  62         if (str == null) {
  63             str = System.getenv(propname);
  64         }
  65         if (str == null) {
  66             return defval;
  67         }
  68         str = str.trim();
  69         float val;
  70         if (str.endsWith("%")) {
  71             val = Integer.parseInt(str.substring(0, str.length()-1)) / 100.0f;
  72         } else if (str.endsWith("DPI") || str.endsWith("dpi")) {
  73             val = Integer.parseInt(str.substring(0, str.length()-3)) / 96.0f;
  74         } else {
  75             val = Float.parseFloat(str);
  76         }
  77         if (PrismSettings.verbose) {
  78             System.out.println(description+val);
  79         }
  80         return val;
  81     }
  82 
  83     private static native void initIDs(float overrideUIScale,
  84                                        float overrideRenderScale,
  85                                        float minDPIScale,
  86                                        boolean forceIntegerRenderScale);
  87     static {
  88         // This loading of msvcr120.dll and msvcp120.dll (VS2013) is required when run with Java 8
  89         // since it was build with VS2010 and doesn't include msvcr120.dll in its JRE.
  90         // Note: See README-builds.html on MSVC requirement: VS2013 is required.
  91         AccessController.doPrivileged(new PrivilegedAction<Void>() {
  92             public Void run() {
  93                 verbose = Boolean.getBoolean("javafx.verbose");
  94                 if (PrismSettings.allowHiDPIScaling) {
  95                     overrideUIScale = getFloat("glass.win.uiScale", -1.0f, "Forcing UI scaling factor: ");
  96                     overrideRenderScale = getFloat("glass.win.renderScale", -1.0f, "Forcing Rendering scaling factor: ");
  97                     minDPIScale = getFloat("glass.win.minHiDPI", 1.5f, "Threshold to enable UI scaling factor: ");
  98                     forceIntegerRenderScale = getBoolean("glass.win.forceIntegerRenderScale", true, "forcing integer rendering scale");
  99                 } else {
 100                     overrideUIScale = overrideRenderScale = 1.0f;
 101                     minDPIScale = Float.MAX_VALUE;
 102                     forceIntegerRenderScale = false;
 103                 }
 104                 try {
 105                     NativeLibLoader.loadLibrary("msvcr120");
 106                 } catch (Throwable t) {
 107                     if (verbose) {
 108                         System.err.println("Error: failed to load msvcr120.dll : " + t);
 109                     }
 110                 }
 111                 try {
 112                     NativeLibLoader.loadLibrary("msvcp120");
 113                 } catch (Throwable t) {
 114                     if (verbose) {
 115                         System.err.println("Error: failed to load msvcp120.dll : " + t);
 116                     }
 117                 }
 118                 Application.loadNativeLibrary();
 119                 return null;
 120             }
 121         });
 122         initIDs(overrideUIScale, overrideRenderScale, minDPIScale, forceIntegerRenderScale);
 123     }
 124 
 125     private final InvokeLaterDispatcher invokeLaterDispatcher;
 126     WinApplication() {
 127         // Embedded in SWT, with shared event thread
 128         boolean isEventThread = AccessController
 129                 .doPrivileged((PrivilegedAction<Boolean>) () -> Boolean.getBoolean("javafx.embed.isEventThread"));
 130         if (!isEventThread) {
 131             invokeLaterDispatcher = new InvokeLaterDispatcher(this);
 132             invokeLaterDispatcher.start();
 133         } else {
 134             invokeLaterDispatcher = null;
 135         }
 136     }
 137 
 138     private static boolean verbose;
 139 
 140     // returng toolkit window HWND
 141     private native long _init(int awarenessRequested);
 142     private native void _setClassLoader(ClassLoader classLoader);
 143     private native void _runLoop(Runnable launchable);
 144     private native void _terminateLoop();
 145 
 146     private static final int Process_DPI_Unaware            = 0;
 147     private static final int Process_System_DPI_Aware       = 1;
 148     private static final int Process_Per_Monitor_DPI_Aware  = 2;
 149 
 150     private static int getDesiredAwarenesslevel() {
 151         if (!PrismSettings.allowHiDPIScaling) {
 152             return Process_DPI_Unaware;
 153         }
 154         String awareRequested = AccessController
 155             .doPrivileged((PrivilegedAction<String>) () ->
 156                           System.getProperty("javafx.glass.winDPIawareness"));
 157         if (awareRequested != null) {
 158             awareRequested = awareRequested.toLowerCase();
 159             if (awareRequested.equals("aware")) {
 160                 return Process_System_DPI_Aware;
 161             } else if (awareRequested.equals("permonitor")) {
 162                 return Process_Per_Monitor_DPI_Aware;
 163             } else {
 164                 if (!awareRequested.equals("unaware")) {
 165                     System.err.println("unrecognized DPI awareness request, defaulting to unaware: "+awareRequested);
 166                 }
 167                 return Process_DPI_Unaware;
 168             }
 169         }
 170         return Process_Per_Monitor_DPI_Aware;
 171     }
 172 
 173     @Override
 174     protected void runLoop(final Runnable launchable) {
 175         boolean isEventThread = AccessController
 176             .doPrivileged((PrivilegedAction<Boolean>) () -> Boolean.getBoolean("javafx.embed.isEventThread"));
 177         int awareness = getDesiredAwarenesslevel();
 178 
 179         ClassLoader classLoader = WinApplication.class.getClassLoader();
 180         _setClassLoader(classLoader);
 181 
 182         if (isEventThread) {
 183             _init(awareness);
 184             setEventThread(Thread.currentThread());
 185             launchable.run();
 186             return;
 187         }
 188         final Thread toolkitThread =
 189             AccessController.doPrivileged((PrivilegedAction<Thread>) () -> new Thread(() -> {
 190                 _init(awareness);
 191                 _runLoop(launchable);
 192             }, "WindowsNativeRunloopThread"));
 193         setEventThread(toolkitThread);
 194         toolkitThread.start();
 195     }
 196 
 197     @Override protected void finishTerminating() {
 198         final Thread toolkitThread = getEventThread();
 199         if (toolkitThread != null) {
 200             _terminateLoop();
 201             setEventThread(null);
 202         }
 203         super.finishTerminating();
 204     }
 205 
 206     @Override public boolean shouldUpdateWindow() {
 207         return true;
 208     }
 209 
 210     native private Object _enterNestedEventLoopImpl();
 211     native private void _leaveNestedEventLoopImpl(Object retValue);
 212 
 213     @Override protected Object _enterNestedEventLoop() {
 214         if (invokeLaterDispatcher != null) {
 215             invokeLaterDispatcher.notifyEnteringNestedEventLoop();
 216         }
 217         try {
 218             return _enterNestedEventLoopImpl();
 219         } finally {
 220             if (invokeLaterDispatcher != null) {
 221                 invokeLaterDispatcher.notifyLeftNestedEventLoop();
 222             }
 223         }
 224     }
 225 
 226     @Override protected void _leaveNestedEventLoop(Object retValue) {
 227         if (invokeLaterDispatcher != null) {
 228             invokeLaterDispatcher.notifyLeavingNestedEventLoop();
 229         }
 230         _leaveNestedEventLoopImpl(retValue);
 231     }
 232 
 233     // FACTORY METHODS
 234 
 235     @Override public Window createWindow(Window owner, Screen screen, int styleMask) {
 236         return new WinWindow(owner, screen, styleMask);
 237     }
 238 
 239     @Override public Window createWindow(long parent) {
 240         return new WinChildWindow(parent);
 241     }
 242 
 243     @Override public View createView() {
 244         return new WinView();
 245     }
 246 
 247     @Override public Cursor createCursor(int type) {
 248         return new WinCursor(type);
 249     }
 250 
 251     @Override public Cursor createCursor(int x, int y, Pixels pixels) {
 252         return new WinCursor(x, y, pixels);
 253     }
 254 
 255     @Override protected void staticCursor_setVisible(boolean visible) {
 256         WinCursor.setVisible_impl(visible);
 257     }
 258 
 259     @Override protected Size staticCursor_getBestSize(int width, int height) {
 260         return WinCursor.getBestSize_impl(width, height);
 261     }
 262 
 263     @Override public Pixels createPixels(int width, int height, ByteBuffer data) {
 264         return new WinPixels(width, height, data);
 265     }
 266 
 267     @Override public Pixels createPixels(int width, int height, IntBuffer data) {
 268         return new WinPixels(width, height, data);
 269     }
 270 
 271     @Override
 272     public Pixels createPixels(int width, int height, IntBuffer data, float scale) {
 273         return new WinPixels(width, height, data, scale);
 274     }
 275 
 276     @Override protected int staticPixels_getNativeFormat() {
 277         return WinPixels.getNativeFormat_impl();
 278     }
 279 
 280     @Override public Robot createRobot() {
 281         return new WinRobot();
 282     }
 283 
 284     @Override protected double staticScreen_getVideoRefreshPeriod() {
 285         return 0.0;     // indicate millisecond resolution
 286     }
 287 
 288     @Override native protected Screen[] staticScreen_getScreens();
 289 
 290     @Override public Timer createTimer(Runnable runnable) {
 291         return new WinTimer(runnable);
 292     }
 293 
 294     @Override protected int staticTimer_getMinPeriod() {
 295         return WinTimer.getMinPeriod_impl();
 296     }
 297 
 298     @Override protected int staticTimer_getMaxPeriod() {
 299         return WinTimer.getMaxPeriod_impl();
 300     }
 301 
 302     @Override public Accessible createAccessible() {
 303         return new WinAccessible();
 304     }
 305 
 306     @Override protected FileChooserResult staticCommonDialogs_showFileChooser(Window owner, String folder, String filename, String title, int type,
 307                                              boolean multipleMode, ExtensionFilter[] extensionFilters, int defaultFilterIndex) {
 308         if (invokeLaterDispatcher != null) {
 309             invokeLaterDispatcher.notifyEnteringNestedEventLoop();
 310         }
 311         return WinCommonDialogs.showFileChooser_impl(owner, folder, filename, title, type, multipleMode, extensionFilters, defaultFilterIndex);
 312     }
 313 
 314     @Override protected File staticCommonDialogs_showFolderChooser(Window owner, String folder, String title) {
 315         if (invokeLaterDispatcher != null) {
 316             invokeLaterDispatcher.notifyEnteringNestedEventLoop();
 317         }
 318         return WinCommonDialogs.showFolderChooser_impl(owner, folder, title);
 319     }
 320 
 321     @Override protected long staticView_getMultiClickTime() {
 322         return WinView.getMultiClickTime_impl();
 323     }
 324 
 325     @Override protected int staticView_getMultiClickMaxX() {
 326         return WinView.getMultiClickMaxX_impl();
 327     }
 328 
 329     @Override protected int staticView_getMultiClickMaxY() {
 330         return WinView.getMultiClickMaxY_impl();
 331     }
 332 
 333     @Override native protected void _invokeAndWait(Runnable runnable);
 334 
 335     native private void _submitForLaterInvocation(Runnable r);
 336     // InvokeLaterDispatcher.InvokeLaterSubmitter
 337     @Override public void submitForLaterInvocation(Runnable r) {
 338         _submitForLaterInvocation(r);
 339     }
 340 
 341     @Override protected void _invokeLater(Runnable runnable) {
 342         if (invokeLaterDispatcher != null) {
 343             invokeLaterDispatcher.invokeLater(runnable);
 344         } else {
 345             submitForLaterInvocation(runnable);
 346         }
 347     }
 348 
 349     private native String _getHighContrastTheme();
 350     @Override public String getHighContrastTheme() {
 351         checkEventThread();
 352         return _getHighContrastTheme();
 353     }
 354 
 355     @Override
 356     protected boolean _supportsInputMethods() {
 357         return true;
 358     }
 359 
 360     @Override
 361     protected boolean _supportsTransparentWindows() {
 362         return true;
 363     }
 364 
 365     @Override native protected boolean _supportsUnifiedWindows();
 366 
 367     public String getDataDirectory() {
 368         checkEventThread();
 369         String baseDirectory = AccessController.doPrivileged((PrivilegedAction<String>) () -> System.getenv("APPDATA"));
 370         if (baseDirectory == null || baseDirectory.length() == 0) {
 371             return super.getDataDirectory();
 372         }
 373         return baseDirectory + File.separator + name + File.separator;
 374     }
 375 
 376     @Override
 377     protected native int _getKeyCodeForChar(char c);
 378 }