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