diff --git a/.github/workflows/entry.yml b/.github/workflows/entry.yml index 4cd5cf6..87ccbc0 100644 --- a/.github/workflows/entry.yml +++ b/.github/workflows/entry.yml @@ -1,14 +1,10 @@ --- -name: TestFX 4 CI +name: Monocle CI on: workflow_dispatch: pull_request: - branches: - - 'master' push: - branches: - - 'master' permissions: actions: read @@ -34,6 +30,6 @@ jobs: uses: ./.github/workflows/build.yml with: os: ubuntu-22.04 - jdk: 11 - openjfx: 11 + jdk: 21.0.2 + openjfx: 21 secrets: inherit diff --git a/build.gradle b/build.gradle index d2d3ced..0e51e0a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,83 +1,102 @@ -buildscript { - repositories { - jcenter() - maven { - url "https://plugins.gradle.org/m2/" - } - } - - dependencies { - classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4" - classpath 'org.openjfx:javafx-plugin:0.0.7' - } +//buildscript { +// repositories { +// mavenCentral() +// maven { +// url "https://plugins.gradle.org/m2/" +// } +// } +//} + +plugins { + id "java" + id "maven-publish" } -apply plugin: 'org.openjfx.javafxplugin' - // print welcome message. -apply from: rootProject.file("gradle/welcome.gradle") +//apply from: rootProject.file("gradle/welcome.gradle") +// +//wrapper { +// gradleVersion = '6.5.1' +// distributionUrl = "https://services.gradle.org/distributions/" + +// "gradle-${gradleVersion}-bin.zip" +//} +// +//// task to print gradle and groovy versions. +//task("versions", group: "help").doLast { +// println "Java version: ${System.properties["java.version"]}" +// println "Gradle version: ${gradle.gradleVersion}" +// println "Groovy version: ${GroovySystem.version}" +//} +// +//// task to create main and test source directories. +//task("initSourceDirs", group: "build setup").doLast { +// // ignore source directories for projects without source sets. +// if (!project.hasProperty("sourceSets")) { return } +// +// // list all source directories. +// def sourceSets = project.sourceSets as SourceSetContainer +// def sourceDirs = sourceSets*.allSource.srcDirs.flatten() as List +// +// // create source directories, for those who not exists. +// for (sourceDir in sourceDirs) { sourceDir.mkdirs() } +//} -wrapper { - gradleVersion = '5.4.1' - distributionUrl = "https://services.gradle.org/distributions/" + - "gradle-${gradleVersion}-bin.zip" +repositories { + mavenCentral() } -// task to print gradle and groovy versions. -task("versions", group: "help").doLast { - println "Java version: ${System.properties["java.version"]}" - println "Gradle version: ${gradle.gradleVersion}" - println "Groovy version: ${GroovySystem.version}" +static def getOSName() { + final String osName = System.getProperty("os.name").toLowerCase() + if (osName.contains("linux")) { + return ("linux") + } else if (osName.contains("mac os x") || osName.contains("darwin") || osName.contains("osx")) { + return ("mac") + } else if (osName.contains("windows")) { + return ("win") + } + return "" } -// task to create main and test source directories. -task("initSourceDirs", group: "build setup").doLast { - // ignore source directories for projects without source sets. - if (!project.hasProperty("sourceSets")) { return } - - // list all source directories. - def sourceSets = project.sourceSets as SourceSetContainer - def sourceDirs = sourceSets*.allSource.srcDirs.flatten() as List +ext { + def buildTimeAndDate = new Date() + buildDate = buildTimeAndDate.format("yyyy-MM-dd") + buildTime = buildTimeAndDate.format("HH:mm:ss.SSSZ") - // create source directories, for those who not exists. - for (sourceDir in sourceDirs) { sourceDir.mkdirs() } + javaVersion = System.properties["java.version"] + javaVendor = System.properties["java.vendor"] + javaVmVersion = System.properties["java.vm.version"] + platform = getOSName() } -// provide java tasks. -apply plugin: "java" - -repositories { - jcenter() -} +// java language level. +sourceCompatibility = "21" +targetCompatibility = "21" -javafx { - modules = [ 'javafx.controls', 'javafx.graphics' ] - version = '12.0.1' +dependencies { + implementation "org.openjfx:javafx-base:21.0.2:${platform}" + implementation "org.openjfx:javafx-graphics:21.0.2:${platform}" } -// java language level. -sourceCompatibility = "9" -targetCompatibility = "9" - // configure publish tasks. -apply from: rootProject.file("gradle/publish-bintray.gradle") +apply from: rootProject.file("gradle/publish-pom.gradle") // task to create jar with source code. task("sourceJar", type: Jar) { - group "Build" - description "An archive of the source code" - classifier "sources" + group = "Build" + description = "An archive of the source code" + archiveClassifier = "sources" from sourceSets.main.allJava } // task to create jar with javadocs. task("javadocJar", type: Jar) { - group "Build" - description "An archive of the javadoc" - classifier "javadoc" + group = "Build" + description = "An archive of the javadoc" + archiveClassifier = "javadoc" from javadoc } +jar.finalizedBy generatePomFileForMavenPublication jar.finalizedBy sourceJar jar.finalizedBy javadocJar @@ -85,29 +104,15 @@ artifacts { archives jar archives sourceJar archives javadocJar + generatePomFileForMavenPublication } compileJava { javadoc { options.addStringOption("Xdoclint:none", "-quiet") - options.addMultilineStringsOption('-add-exports').setValue(['javafx.graphics/com.sun.glass.ui=ALL-UNNAMED', - 'javafx.graphics/com.sun.glass.events=ALL-UNNAMED', - 'javafx.graphics/com.sun.glass.ui.delegate=ALL-UNNAMED', - 'javafx.graphics/com.sun.glass.utils=ALL-UNNAMED', - 'javafx.graphics/com.sun.javafx.tk=ALL-UNNAMED',]) } } -ext { - def buildTimeAndDate = new Date() - buildDate = buildTimeAndDate.format("yyyy-MM-dd") - buildTime = buildTimeAndDate.format("HH:mm:ss.SSSZ") - - javaVersion = System.properties["java.version"] - javaVendor = System.properties["java.vendor"] - javaVmVersion = System.properties["java.vm.version"] -} - jar { manifest.attributes( "Created-By": project.javaVersion + diff --git a/gradle.properties b/gradle.properties index 690f811..975acef 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,9 +1,13 @@ group = org.testfx -version = jdk-12.0.1+2 +artifact = monocle +version = 21.0.2 -description = Headless graphics driver for JavaFX. +description = Headless graphics driver for JavaFX vendor = The OpenJDK Community +pomName = OpenJFX Monocle +pomDependencyVersion = 21.0.2 + url = https://github.com/TestFX/Monocle sourceUrl = https://github.com/TestFX/Monocle issuesUrl = https://github.com/TestFX/Monocle/issues diff --git a/gradle/publish-pom.gradle b/gradle/publish-pom.gradle new file mode 100644 index 0000000..7738f2d --- /dev/null +++ b/gradle/publish-pom.gradle @@ -0,0 +1,72 @@ +/* + * Copyright 2013-2014 SmartBear Software + * Copyright 2014-2023 The TestFX Contributors + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by the + * European Commission - subsequent versions of the EUPL (the "Licence"); You may + * not use this work except in compliance with the Licence. + * + * You may obtain a copy of the Licence at: + * http://ec.europa.eu/idabc/eupl + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the Licence for the + * specific language governing permissions and limitations under the Licence. + */ + +publishing { + publications { + maven(MavenPublication) { + pom { + name = "${project.pomName}" + description = "${project.description}" + url = "https://openjfx.io/" + + licenses { + license { + name = "GNU General Public License, version 2, with the Classpath Exception" + url = "https://github.com/TestFX/Monocle/blob/main/LICENSE" + distribution = "repo" + } + } + + scm { + url = "https://github.com/TestFX/Monocle" + } + + developers { + developer { + name = "The OpenJDK Community" + } + } + + withXml { + asNode().appendNode('dependencies') + .appendNode('dependency') + .appendNode('groupId', 'org.openjfx') + .parent() + .appendNode('artifactId', 'javafx-base') + .parent() + .appendNode('version', "${project.pomDependencyVersion}") + .parent() + .appendNode('scope', 'provided') + .parent() + .parent() + .appendNode('dependency') + .appendNode('groupId', 'org.openjfx') + .parent() + .appendNode('artifactId', 'javafx-graphics') + .parent() + .appendNode('version', "${project.pomDependencyVersion}") + .parent() + .appendNode('scope', 'provided') + } + } + } + } +} + +tasks.named("generatePomFileForMavenPublication") { + destination = "$buildDir/libs/${project.name}-${project.version}.pom" +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f4d7b2b..17655d0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/scripts/bundle.sh b/scripts/bundle.sh new file mode 100755 index 0000000..705f678 --- /dev/null +++ b/scripts/bundle.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash + +# This script was derived from instructions found at https://central.sonatype.org/publish/publish-manual/#bundle-creation + +set -ue -o pipefail + +function cleanup { + EXIT_CODE=$? + set +e # disable termination on error +# if [[ $EXIT_CODE != 0 ]]; then +# # Logic to clean up from script +# fi + exit $EXIT_CODE +} +trap cleanup EXIT INT TERM # Are all three of these the right choice? + +# Usage info +show_help() { +cat << EOF +Usage: ${0##*/} [-h] +Helper for deploying a signed maven bundle of TestFX to Maven Central. + +Prerequisites: +- gpg - PGP encryption and signing tool + +Requires a Maven key for the TestFX repository. + +Options: + -h / --help display this help and exit +EOF +} + +# Show the help information +if [[ $# -gt 0 ]]; then + if [ "$1" == "-h" ] || [ "$1" == "--help" ]; then + show_help + exit 0 + else + show_help + exit 1 + fi +fi + +# Check if the user is at the root level of the project +if [[ ! $(git rev-parse --show-toplevel 2>/dev/null) = "$PWD" ]]; then + echo "You are not currently at the root of the TestFX git repository." + exit +fi + +# Check if gpg is installed +if ! [ -x "$(command -v gpg)" ]; then + echo 'Error: gpg is not installed.' >&2 + exit 1 +fi + +# Determine which gpg key to use +gpgKey=$(gpg --list-keys --with-colon | grep '^uid' | grep '(TestFX)' | cut -d':' -f10) +if [[ -z "$gpgKey" ]]; then + echo "Could not find a GPG key with (TestFX) in its' name." + echo "See: https://github.com/TestFX/TestFX/wiki/Issuing-a-Release#create-a-testfx-gpg-key" + exit 1 +fi + +sign() { + libsPath=build/libs + + # Remove prior files + rm -f "$libsPath"/*.asc + + # Determine which gpg key to use + gpgKey=$(gpg --list-keys --with-colon | grep '^uid' | grep '(TestFX)' | cut -d':' -f10) + + # Sign each file in the libs folder + for file in $libsPath/*; do + echo Sign $file + gpg -ab --batch -u "$gpgKey" $file + done +} + +bundle() { + libsPath=build/libs + bundle="$1-bundle.jar" + target="build/bundles/" + + mkdir -p "$target" + + echo Create bundle + cd $libsPath || exit + rm -f bundle* + jar -cvf "$bundle" ./* + cd - || exit + + cp "$libsPath/$bundle" "$target" + + echo Bundle created "$target/$bundle" +} + +sign monocle +bundle monocle + diff --git a/src/main/java/com/sun/glass/ui/monocle/AcceleratedScreen.java b/src/main/java/com/sun/glass/ui/monocle/AcceleratedScreen.java index a96a81b..d47489d 100644 --- a/src/main/java/com/sun/glass/ui/monocle/AcceleratedScreen.java +++ b/src/main/java/com/sun/glass/ui/monocle/AcceleratedScreen.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,10 +33,10 @@ public class AcceleratedScreen { private static long glesLibraryHandle; private static long eglLibraryHandle; private static boolean initialized = false; - private long eglSurface; - private long eglContext; - private long eglDisplay; - private long nativeWindow; + long eglSurface; + long eglContext; + long eglDisplay; + long nativeWindow; protected static final LinuxSystem ls = LinuxSystem.getLinuxSystem(); private EGL egl; long eglConfigs[] = {0}; @@ -55,6 +55,14 @@ protected long platformGetNativeWindow() { return 0L; } + /** + * Create and initialize an AcceleratedScreen. Subclasses should override + * this constructor in case the {@link #AcceleratedScreen(int[]) AcceleratedScreen(int[])} + * constructor is not sufficient. + */ + AcceleratedScreen() { + } + /** * Perform basic egl intialization - open the display, create the drawing * surface, and create a GL context to that drawing surface. diff --git a/src/main/java/com/sun/glass/ui/monocle/AndroidAcceleratedScreen.java b/src/main/java/com/sun/glass/ui/monocle/AndroidAcceleratedScreen.java index a04a781..8018621 100644 --- a/src/main/java/com/sun/glass/ui/monocle/AndroidAcceleratedScreen.java +++ b/src/main/java/com/sun/glass/ui/monocle/AndroidAcceleratedScreen.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,9 +25,6 @@ package com.sun.glass.ui.monocle; -import java.security.AccessController; -import java.security.PrivilegedAction; - /** * Provide Android implementation of AcceleratedScreen * @@ -39,6 +36,7 @@ class AndroidAcceleratedScreen extends AcceleratedScreen { super(attributes); } + @Override boolean initPlatformLibraries() { return super.initPlatformLibraries(); } diff --git a/src/main/java/com/sun/glass/ui/monocle/AndroidInputDevice.java b/src/main/java/com/sun/glass/ui/monocle/AndroidInputDevice.java index 59664b2..0ac7a65 100644 --- a/src/main/java/com/sun/glass/ui/monocle/AndroidInputDevice.java +++ b/src/main/java/com/sun/glass/ui/monocle/AndroidInputDevice.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,6 @@ package com.sun.glass.ui.monocle; -import java.io.IOException; - /** * */ diff --git a/src/main/java/com/sun/glass/ui/monocle/AndroidInputDeviceRegistry.java b/src/main/java/com/sun/glass/ui/monocle/AndroidInputDeviceRegistry.java index 216746e..133034b 100644 --- a/src/main/java/com/sun/glass/ui/monocle/AndroidInputDeviceRegistry.java +++ b/src/main/java/com/sun/glass/ui/monocle/AndroidInputDeviceRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,7 +59,7 @@ public static void gotTouchEventFromNative(int count, int[] actions, int[] ids, touchState.addPoint(p); } } - instance.gotTouchEvent(touchState); + Platform.runLater(() -> instance.gotTouchEvent(touchState)); } private void gotTouchEvent(TouchState touchState) { @@ -77,6 +77,14 @@ private void gotTouchEvent(TouchState touchState) { } + public static void dispatchKeyEventFromNative(int type, int key, char[] chars, int modifiers) { + instance.processor.dispatchKeyEvent(type, key, chars, modifiers); + } + + public static void dispatchMenuEventFromNative(int x, int y, int xAbs, int yAbs, boolean isKeyboardTrigger) { + instance.processor.dispatchMenuEvent(x, y, xAbs, yAbs, isKeyboardTrigger); + } + public static void gotKeyEventFromNative(int action, int linuxKey) { instance.gotKeyEvent (action, linuxKey); } @@ -124,6 +132,7 @@ private AndroidInputDevice addDeviceInternal(AndroidInputDevice device, String n } void removeDevice(AndroidInputDevice device) { + @SuppressWarnings("removal") SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission(new AllPermission()); diff --git a/src/main/java/com/sun/glass/ui/monocle/AndroidInputProcessor.java b/src/main/java/com/sun/glass/ui/monocle/AndroidInputProcessor.java index 31e2c91..d20167f 100644 --- a/src/main/java/com/sun/glass/ui/monocle/AndroidInputProcessor.java +++ b/src/main/java/com/sun/glass/ui/monocle/AndroidInputProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,6 +24,8 @@ */ package com.sun.glass.ui.monocle; +import javafx.application.Platform; + class AndroidInputProcessor { private final AndroidInputDevice device; @@ -54,4 +56,32 @@ synchronized void pushKeyEvent(KeyState keyState) { keyInput.setState(keyState); } + synchronized void dispatchKeyEvent(int type, int key, char[] chars, int modifiers) { + Platform.runLater( () -> { + MonocleWindow window = MonocleWindowManager.getInstance().getFocusedWindow(); + if (window == null) { + return; + } + MonocleView view = (MonocleView) window.getView(); + if (view == null) { + return; + } + RunnableProcessor.runLater( () -> view.notifyKey(type, key, chars, modifiers)); + }); + } + + synchronized void dispatchMenuEvent(int x, int y, int xAbs, int yAbs, boolean isKeyboardTrigger) { + Platform.runLater(() -> { + MonocleWindow window = MonocleWindowManager.getInstance().getFocusedWindow(); + if (window == null) { + return; + } + MonocleView view = (MonocleView) window.getView(); + if (view == null) { + return; + } + RunnableProcessor.runLater(() -> view.notifyMenu(x, y, xAbs, yAbs, isKeyboardTrigger)); + }); + } + } diff --git a/src/main/java/com/sun/glass/ui/monocle/AndroidPlatform.java b/src/main/java/com/sun/glass/ui/monocle/AndroidPlatform.java index d19c9dc..7a22f85 100644 --- a/src/main/java/com/sun/glass/ui/monocle/AndroidPlatform.java +++ b/src/main/java/com/sun/glass/ui/monocle/AndroidPlatform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,7 +39,7 @@ protected InputDeviceRegistry createInputDeviceRegistry() { @Override protected NativeCursor createCursor() { - return new NullCursor(); + return logSelectedCursor(new NullCursor()); } @Override diff --git a/src/main/java/com/sun/glass/ui/monocle/AndroidPlatformFactory.java b/src/main/java/com/sun/glass/ui/monocle/AndroidPlatformFactory.java index 44b2ee3..e6cc87a 100644 --- a/src/main/java/com/sun/glass/ui/monocle/AndroidPlatformFactory.java +++ b/src/main/java/com/sun/glass/ui/monocle/AndroidPlatformFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ class AndroidPlatformFactory extends NativePlatformFactory { @Override protected boolean matches() { + @SuppressWarnings("removal") String platform = AccessController.doPrivileged( (PrivilegedAction) () -> System.getProperty("javafx.platform")); return platform != null && platform.equals("android"); diff --git a/src/main/java/com/sun/glass/ui/monocle/AndroidScreen.java b/src/main/java/com/sun/glass/ui/monocle/AndroidScreen.java index 75ac1bb..0cc8c6c 100644 --- a/src/main/java/com/sun/glass/ui/monocle/AndroidScreen.java +++ b/src/main/java/com/sun/glass/ui/monocle/AndroidScreen.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ public class AndroidScreen implements NativeScreen { private float density = -1; + @Override public int getDepth() { return 24; } @@ -41,6 +42,7 @@ public int getDepth() { * Returns the native format of the screen, as a constant from the Pixels * class. */ + @Override public int getNativeFormat() { return Pixels.Format.BYTE_ARGB; } @@ -48,6 +50,7 @@ public int getNativeFormat() { /** * Returns the pixel width of the screen. */ + @Override public int getWidth() { int answer = (int)(_getWidth()/getScale()); return answer; @@ -56,6 +59,7 @@ public int getWidth() { /** * Returns the pixel height of the screen. */ + @Override public int getHeight() { return (int)(_getHeight()/getScale()); } @@ -63,6 +67,7 @@ public int getHeight() { /** * Returns the number of pixels per inch in the screen. */ + @Override public int getDPI() { return 100; } @@ -78,6 +83,7 @@ public float getScale () { /** * Returns a native handle for the screen. The handle is platform-specific. */ + @Override public long getNativeHandle() { long answer = _getNativeHandle(); return answer; @@ -86,6 +92,7 @@ public long getNativeHandle() { /** * Called during JavaFX shutdown to release the screen. Called only once. */ + @Override public void shutdown() { _shutdown(); } @@ -101,6 +108,7 @@ public void shutdown() { * @param alpha The alpha level to use to compose the data over existing * pixels */ + @Override public void uploadPixels(Buffer b, int x, int y, int width, int height, float alpha) { _uploadPixels (b, x, y, width, height, alpha); @@ -110,6 +118,7 @@ public void uploadPixels(Buffer b, * Called on the JavaFX application thread when pixel data for all windows * has been uploaded. */ + @Override public void swapBuffers() { _swapBuffers(); } @@ -118,6 +127,7 @@ public void swapBuffers() { * Returns a read-only ByteBuffer in the native pixel format containing the screen contents. * @return ByteBuffer a read-only ByteBuffer containing the screen contents */ + @Override public ByteBuffer getScreenCapture() { return _getScreenCapture(); } diff --git a/src/main/java/com/sun/glass/ui/monocle/C.java b/src/main/java/com/sun/glass/ui/monocle/C.java index b119b21..ef37cf6 100644 --- a/src/main/java/com/sun/glass/ui/monocle/C.java +++ b/src/main/java/com/sun/glass/ui/monocle/C.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,6 +51,7 @@ static C getC() { } private static void checkPermissions() { + @SuppressWarnings("removal") SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission(permission); diff --git a/src/main/java/com/sun/glass/ui/monocle/DispmanAcceleratedScreen.java b/src/main/java/com/sun/glass/ui/monocle/DispmanAcceleratedScreen.java index 499924c..045b218 100644 --- a/src/main/java/com/sun/glass/ui/monocle/DispmanAcceleratedScreen.java +++ b/src/main/java/com/sun/glass/ui/monocle/DispmanAcceleratedScreen.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,9 +38,11 @@ class DispmanAcceleratedScreen extends AcceleratedScreen { @Override protected long platformGetNativeWindow() { + @SuppressWarnings("removal") int displayID = AccessController.doPrivileged( (PrivilegedAction) () -> Integer.getInteger("dispman.display", 0 /* LCD */)); + @SuppressWarnings("removal") int layerID = AccessController.doPrivileged( (PrivilegedAction) () -> Integer.getInteger("dispman.layer", 1)); diff --git a/src/main/java/com/sun/glass/ui/monocle/DispmanPlatform.java b/src/main/java/com/sun/glass/ui/monocle/DispmanPlatform.java index e82c3b3..cec41a7 100644 --- a/src/main/java/com/sun/glass/ui/monocle/DispmanPlatform.java +++ b/src/main/java/com/sun/glass/ui/monocle/DispmanPlatform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,8 @@ class DispmanPlatform extends LinuxPlatform { @Override protected NativeCursor createCursor() { - return new DispmanCursor(); + final NativeCursor c = useCursor ? new DispmanCursor() : new NullCursor(); + return logSelectedCursor(c); } @Override diff --git a/src/main/java/com/sun/glass/ui/monocle/EGL.java b/src/main/java/com/sun/glass/ui/monocle/EGL.java index 489808c..739adf9 100644 --- a/src/main/java/com/sun/glass/ui/monocle/EGL.java +++ b/src/main/java/com/sun/glass/ui/monocle/EGL.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -165,6 +165,7 @@ static EGL getEGL() { } private static void checkPermissions() { + @SuppressWarnings("removal") SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission(permission); diff --git a/src/main/java/com/sun/glass/ui/monocle/EGLAcceleratedScreen.java b/src/main/java/com/sun/glass/ui/monocle/EGLAcceleratedScreen.java new file mode 100644 index 0000000..3eea128 --- /dev/null +++ b/src/main/java/com/sun/glass/ui/monocle/EGLAcceleratedScreen.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.glass.ui.monocle; + +/** + * The EGLAcceleratedScreen manages the link to the hardware-accelerated + * component, using EGL. This class is not directly using EGL commands, + * as the order and meaning of parameters might vary between implementations. + * Also, implementation-specific logic may be applied before, in between, or + * after the EGL commands. + */ +public class EGLAcceleratedScreen extends AcceleratedScreen { + + private long eglWindowHandle = -1; + + /** + * Create a new EGLAcceleratedScreen with a set of attributes. + * This will create an EGL Context that can be used by the + * Prism component. + * @param attributes an array of attributes that will be used by the underlying + * implementation to get the best matching configuration. + */ + EGLAcceleratedScreen(int[] attributes) { + eglWindowHandle = platformGetNativeWindow(); + eglDisplay = nGetEglDisplayHandle(); + nEglInitialize(eglDisplay); + nEglBindApi(EGL.EGL_OPENGL_ES_API); + long eglConfig = nEglChooseConfig(eglDisplay, attributes); + if (eglConfig == -1) { + throw new IllegalArgumentException("Could not create an EGLChooseConfig"); + } + eglSurface = nEglCreateWindowSurface(eglDisplay, eglConfig, eglWindowHandle); + eglContext = nEglCreateContext(eglDisplay, eglConfig); + } + + @Override + protected long platformGetNativeWindow() { + String displayID = System.getProperty("egl.displayid", "/dev/dri/card1" ); + return nPlatformGetNativeWindow(displayID); + } + + @Override + public void enableRendering(boolean flag) { + if (flag) { + nEglMakeCurrent(eglDisplay, eglSurface, eglSurface, + eglContext); + } else { + nEglMakeCurrent(eglDisplay, 0, 0, eglContext); + } + } + + @Override + public boolean swapBuffers() { + boolean result = false; + synchronized (NativeScreen.framebufferSwapLock) { + result = nEglSwapBuffers(eglDisplay, eglSurface); + } + return result; + } + + private native long nPlatformGetNativeWindow(String displayID); + private native long nGetEglDisplayHandle(); + private native boolean nEglInitialize(long handle); + private native boolean nEglBindApi(int v); + private native long nEglChooseConfig(long eglDisplay, int[] attribs); + private native boolean nEglMakeCurrent(long eglDisplay, long eglDrawSurface, long eglReadSurface, long eglContext); + private native long nEglCreateWindowSurface(long eglDisplay, long eglConfig, long nativeWindow); + private native long nEglCreateContext(long eglDisplay, long eglConfig); + private native boolean nEglSwapBuffers(long eglDisplay, long eglSurface); +} diff --git a/src/main/java/com/sun/glass/ui/monocle/EGLCursor.java b/src/main/java/com/sun/glass/ui/monocle/EGLCursor.java new file mode 100644 index 0000000..9ec20fe --- /dev/null +++ b/src/main/java/com/sun/glass/ui/monocle/EGLCursor.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.glass.ui.monocle; + +import com.sun.glass.ui.Size; + +class EGLCursor extends NativeCursor { + + private static final int CURSOR_WIDTH = 16; + private static final int CURSOR_HEIGHT = 16; + + + private native void _initEGLCursor(int cursorWidth, int cursorHeight); + private native void _setVisible(boolean visible); + private native void _setLocation(int x, int y); + private native void _setImage(byte[] cursorImage); + + EGLCursor() { + _initEGLCursor(CURSOR_WIDTH, CURSOR_HEIGHT); + } + + @Override + Size getBestSize() { + return new Size(CURSOR_WIDTH, CURSOR_HEIGHT); + } + + @Override + void setVisibility(boolean visibility) { + isVisible = visibility; + _setVisible(visibility); + } + + private void updateImage(boolean always) { + System.out.println("EGLCursor.updateImage: not implemented"); + } + + @Override + void setImage(byte[] cursorImage) { + _setImage(cursorImage); + } + + @Override + void setLocation(int x, int y) { + _setLocation(x, y); + } + + @Override + void setHotSpot(int hotspotX, int hotspotY) { + } + + @Override + void shutdown() { + } +} diff --git a/src/main/java/com/sun/glass/ui/monocle/EGLPlatform.java b/src/main/java/com/sun/glass/ui/monocle/EGLPlatform.java new file mode 100644 index 0000000..66223ec --- /dev/null +++ b/src/main/java/com/sun/glass/ui/monocle/EGLPlatform.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.glass.ui.monocle; + +import java.util.ArrayList; +import java.util.List; + +public class EGLPlatform extends LinuxPlatform { + + private List screens; + + /** + * Create an EGLPlatform. If a library with specific native code is needed for this platform, + * it will be downloaded now. The system property monocle.egl.lib can be used to define the + * name of the library that should be loaded. + */ + public EGLPlatform() { + String lib = System.getProperty("monocle.egl.lib"); + if (lib != null) { + long handle = LinuxSystem.getLinuxSystem().dlopen(lib, LinuxSystem.RTLD_LAZY | LinuxSystem.RTLD_GLOBAL); + if (handle == 0) { + throw new UnsatisfiedLinkError("EGLPlatform failed to load the requested library " + lib); + } + } + } + + @Override + protected NativeCursor createCursor() { + // By default, hardware cursor will be used + // Fallback to software cursor will be used in case monocle.egl.swcursor is set to true + boolean swcursor = Boolean.getBoolean("monocle.egl.swcursor"); + final NativeCursor c = useCursor ? (swcursor ? new SoftwareCursor() : new EGLCursor()) : new NullCursor(); + return logSelectedCursor(c); + } + + + @Override + protected NativeScreen createScreen() { + return new EGLScreen(0); + } + + @Override + protected synchronized List createScreens() { + if (screens == null) { + int numScreens = nGetNumberOfScreens(); + screens = new ArrayList<>(numScreens); + for (int i = 0; i < numScreens; i++) { + screens.add(new EGLScreen(i)); + } + } + return screens; + } + + @Override + public synchronized AcceleratedScreen getAcceleratedScreen(int[] attributes) throws GLException { + if (accScreen == null) { + accScreen = new EGLAcceleratedScreen(attributes); + } + return accScreen; + + } + + private native int nGetNumberOfScreens(); + +} diff --git a/src/main/java/com/sun/glass/ui/monocle/EGLPlatformFactory.java b/src/main/java/com/sun/glass/ui/monocle/EGLPlatformFactory.java new file mode 100644 index 0000000..8d4057a --- /dev/null +++ b/src/main/java/com/sun/glass/ui/monocle/EGLPlatformFactory.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.glass.ui.monocle; + +public class EGLPlatformFactory extends NativePlatformFactory { + + @Override + protected boolean matches() { + return true; + } + + @Override + protected int getMajorVersion() { + return 1; + } + + @Override + protected int getMinorVersion() { + return 0; + } + + @Override + protected NativePlatform createNativePlatform() { + return new EGLPlatform(); + } + + +} diff --git a/src/main/java/com/sun/glass/ui/monocle/EGLScreen.java b/src/main/java/com/sun/glass/ui/monocle/EGLScreen.java new file mode 100644 index 0000000..2723500 --- /dev/null +++ b/src/main/java/com/sun/glass/ui/monocle/EGLScreen.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.glass.ui.monocle; + +import java.nio.Buffer; +import java.nio.ByteBuffer; + +public class EGLScreen implements NativeScreen { + + final int depth; + final int nativeFormat; + final int width, height; + final int offsetX, offsetY; + final int dpi; + final long handle; + final float scale; + + public EGLScreen(int idx) { + this.handle = nGetHandle(idx); + this.depth = nGetDepth(idx); + this.nativeFormat = nGetNativeFormat(idx); + this.width = nGetWidth(idx); + this.height = nGetHeight(idx); + this.offsetX = nGetOffsetX(idx); + this.offsetY = nGetOffsetY(idx); + this.dpi = nGetDpi(idx); + this.scale = nGetScale(idx); + } + + @Override + public int getDepth() { + return this.depth; + } + + @Override + public int getNativeFormat() { + return this.nativeFormat; + } + + @Override + public int getWidth() { + return this.width; + } + + @Override + public int getHeight() { + return this.height; + } + + @Override + public int getOffsetX() { + return this.offsetX; + } + + @Override + public int getOffsetY() { + return this.offsetY; + } + + @Override + public int getDPI() { + return this.dpi; + } + + @Override + public long getNativeHandle() { + return handle; + } + + @Override + public void shutdown() { + } + + @Override + public void uploadPixels(Buffer b, int x, int y, int width, int height, float alpha) { + } + + @Override + public void swapBuffers() { + } + + @Override + public ByteBuffer getScreenCapture() { + throw new UnsupportedOperationException("No screencapture on EGL platforms"); + } + + @Override + public float getScale() { + return this.scale; + } + + private native long nGetHandle(int idx); + private native int nGetDepth(int idx); + private native int nGetWidth(int idx); + private native int nGetHeight(int idx); + private native int nGetOffsetX(int idx); + private native int nGetOffsetY(int idx); + private native int nGetDpi(int idx); + private native int nGetNativeFormat(int idx); + private native float nGetScale(int idx); + +} diff --git a/src/main/java/com/sun/glass/ui/monocle/EPDFrameBuffer.java b/src/main/java/com/sun/glass/ui/monocle/EPDFrameBuffer.java new file mode 100644 index 0000000..92334c6 --- /dev/null +++ b/src/main/java/com/sun/glass/ui/monocle/EPDFrameBuffer.java @@ -0,0 +1,714 @@ +/* + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.glass.ui.monocle; + +import com.sun.glass.ui.monocle.EPDSystem.FbVarScreenInfo; +import com.sun.glass.ui.monocle.EPDSystem.IntStructure; +import com.sun.glass.ui.monocle.EPDSystem.MxcfbUpdateData; +import com.sun.glass.ui.monocle.EPDSystem.MxcfbWaveformModes; +import com.sun.javafx.logging.PlatformLogger; +import com.sun.javafx.logging.PlatformLogger.Level; +import com.sun.javafx.util.Logging; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.text.MessageFormat; + +/** + * Represents the standard Linux frame buffer device interface plus the custom + * extensions to that interface provided by the Electrophoretic Display + * Controller (EPDC) frame buffer driver. + *

+ * The Linux frame buffer device interface is documented in The Frame + * Buffer Device API found in the Ubuntu package called linux-doc + * (see /usr/share/doc/linux-doc/fb/api.txt.gz).

+ *

+ * The EPDC frame buffer driver extensions are documented in the i.MX + * Linux Reference Manual available on the + * NXP website (registration required). On + * the NXP home page, click Products, ARM Processors, i.MX Application + * Processors, and then i.MX 6 Processors, for example. Select the i.MX6SLL + * Product in the chart; then click the Documentation tab. Look for a download + * with a label for Linux documents, like L4.1.15_2.1.0_LINUX_DOCS, under the + * Supporting Information section. After downloading and expanding the archive, + * the reference manual is found in the doc directory as the file + * i.MX_Linux_Reference_Manual.pdf.

+ */ +class EPDFrameBuffer { + + /** + * The arithmetic right shift value to convert a bit depth to a byte depth. + */ + private static final int BITS_TO_BYTES = 3; + + /** + * The delay in milliseconds between the completion of all updates in the + * EPDC driver and when the driver powers down the EPDC and display power + * supplies. + */ + private static final int POWERDOWN_DELAY = 1_000; + + /** + * Linux system error: ENOTTY 25 Inappropriate ioctl for device. + */ + private static final int ENOTTY = 25; + + private final PlatformLogger logger = Logging.getJavaFXLogger(); + private final EPDSettings settings; + private final LinuxSystem system; + private final EPDSystem driver; + private final long fd; + + private final int xres; + private final int yres; + private final int xresVirtual; + private final int yresVirtual; + private final int xoffset; + private final int yoffset; + private final int bitsPerPixel; + private final int bytesPerPixel; + private final int byteOffset; + private final MxcfbUpdateData updateData; + private final MxcfbUpdateData syncUpdate; + + private int updateMarker; + private int lastMarker; + + /** + * Creates a new {@code EPDFrameBuffer} for the given frame buffer device. + * The geometry of the Linux frame buffer is shown below for various color + * depths and rotations on a sample system, as printed by the fbset + * command. The first three are for landscape mode, while the last three are + * for portrait. + *
{@code
+     * geometry 800 600 800 640 32 (line length: 3200)
+     * geometry 800 600 800 1280 16 (line length: 1600)
+     * geometry 800 600 800 1280 8 (line length: 800)
+     *
+     * geometry 600 800 608 896 32 (line length: 2432)
+     * geometry 600 800 608 1792 16 (line length: 1216)
+     * geometry 600 800 608 1792 8 (line length: 608)
+     * }
+ * + * @implNote {@code MonocleApplication} creates a {@code Screen} which + * requires that the width be set to {@link #xresVirtual} even though only + * the first {@link #xres} pixels of each row are visible. The EPDC driver + * supports panning only in the y-direction, so it is not possible to center + * the visible resolution horizontally when these values differ. The JavaFX + * application should be left-aligned in this case and ignore the few extra + * pixels on the right of its screen. + * + * @param fbPath the frame buffer device path, such as /dev/fb0 + * @throws IOException if an error occurs when opening the frame buffer + * device or when getting or setting the frame buffer configuration + * @throws IllegalArgumentException if the EPD settings specify an + * unsupported color depth + */ + EPDFrameBuffer(String fbPath) throws IOException { + settings = EPDSettings.newInstance(); + system = LinuxSystem.getLinuxSystem(); + driver = EPDSystem.getEPDSystem(); + fd = system.open(fbPath, LinuxSystem.O_RDWR); + if (fd == -1) { + throw new IOException(system.getErrorMessage()); + } + + /* + * Gets the current settings of the frame buffer device. + */ + var screen = new FbVarScreenInfo(); + getScreenInfo(screen); + + /* + * Changes the settings of the frame buffer from the system properties. + * + * See the section, "Format configuration," in "The Frame Buffer Device + * API" for details. Note that xoffset is always zero, and yoffset can + * be modified only by panning in the y-direction with the IOCTL call to + * LinuxSystem.FBIOPAN_DISPLAY. + */ + screen.setBitsPerPixel(screen.p, settings.bitsPerPixel); + screen.setGrayscale(screen.p, settings.grayscale); + switch (settings.bitsPerPixel) { + case Byte.SIZE: + // rgba 8/0,8/0,8/0,0/0 (set by driver when grayscale > 0) + screen.setRed(screen.p, 0, 0); + screen.setGreen(screen.p, 0, 0); + screen.setBlue(screen.p, 0, 0); + screen.setTransp(screen.p, 0, 0); + break; + case Short.SIZE: + // rgba 5/11,6/5,5/0,0/0 + screen.setRed(screen.p, 5, 11); + screen.setGreen(screen.p, 6, 5); + screen.setBlue(screen.p, 5, 0); + screen.setTransp(screen.p, 0, 0); + break; + case Integer.SIZE: + // rgba 8/16,8/8,8/0,8/24 + screen.setRed(screen.p, 8, 16); + screen.setGreen(screen.p, 8, 8); + screen.setBlue(screen.p, 8, 0); + screen.setTransp(screen.p, 8, 24); + break; + default: + String msg = MessageFormat.format("Unsupported color depth: {0} bpp", settings.bitsPerPixel); + logger.severe(msg); + throw new IllegalArgumentException(msg); + } + screen.setActivate(screen.p, EPDSystem.FB_ACTIVATE_FORCE); + screen.setRotate(screen.p, settings.rotate); + setScreenInfo(screen); + + /* + * Gets and logs the new settings of the frame buffer device. + */ + getScreenInfo(screen); + logScreenInfo(screen); + xres = screen.getXRes(screen.p); + yres = screen.getYRes(screen.p); + xresVirtual = screen.getXResVirtual(screen.p); + yresVirtual = screen.getYResVirtual(screen.p); + xoffset = screen.getOffsetX(screen.p); + yoffset = screen.getOffsetY(screen.p); + bitsPerPixel = screen.getBitsPerPixel(screen.p); + bytesPerPixel = bitsPerPixel >>> BITS_TO_BYTES; + byteOffset = (xoffset + yoffset * xresVirtual) * bytesPerPixel; + + /* + * Allocates objects for reuse to avoid creating new direct byte buffers + * outside of the Java heap on each display update. + */ + updateData = new MxcfbUpdateData(); + syncUpdate = createDefaultUpdate(xres, yres); + } + + /** + * Gets the variable screen information of the frame buffer. Run the + * fbset command as root to print the screen information. + * + * @param screen the object representing the variable screen information + * @throws IOException if an error occurs getting the information + */ + private void getScreenInfo(FbVarScreenInfo screen) throws IOException { + int rc = system.ioctl(fd, LinuxSystem.FBIOGET_VSCREENINFO, screen.p); + if (rc != 0) { + system.close(fd); + throw new IOException(system.getErrorMessage()); + } + } + + /** + * Sets the variable screen information of the frame buffer. + *

+ * "To ensure that the EPDC driver receives the initialization request, the + * {@code activate} field of the {@code fb_var_screeninfo} parameter should + * be set to {@code FB_ACTIVATE_FORCE}." [EPDC Panel Initialization, + * i.MX Linux Reference Manual]

+ *

+ * To request a change to 8-bit grayscale format, the bits per pixel must be + * set to 8 and the grayscale value must be set to one of the two valid + * grayscale format values: {@code GRAYSCALE_8BIT} or + * {@code GRAYSCALE_8BIT_INVERTED}. [Grayscale Framebuffer Selection, + * i.MX Linux Reference Manual]

+ * + * @param screen the object representing the variable screen information + * @throws IOException if an error occurs setting the information + */ + private void setScreenInfo(FbVarScreenInfo screen) throws IOException { + int rc = system.ioctl(fd, LinuxSystem.FBIOPUT_VSCREENINFO, screen.p); + if (rc != 0) { + system.close(fd); + throw new IOException(system.getErrorMessage()); + } + } + + /** + * Logs the variable screen information of the frame buffer, depending on + * the logging level. + * + * @param screen the object representing the variable screen information + */ + private void logScreenInfo(FbVarScreenInfo screen) { + if (logger.isLoggable(Level.FINE)) { + logger.fine("Frame buffer geometry: {0} {1} {2} {3} {4}", + screen.getXRes(screen.p), screen.getYRes(screen.p), + screen.getXResVirtual(screen.p), screen.getYResVirtual(screen.p), + screen.getBitsPerPixel(screen.p)); + logger.fine("Frame buffer rgba: {0}/{1},{2}/{3},{4}/{5},{6}/{7}", + screen.getRedLength(screen.p), screen.getRedOffset(screen.p), + screen.getGreenLength(screen.p), screen.getGreenOffset(screen.p), + screen.getBlueLength(screen.p), screen.getBlueOffset(screen.p), + screen.getTranspLength(screen.p), screen.getTranspOffset(screen.p)); + logger.fine("Frame buffer grayscale: {0}", screen.getGrayscale(screen.p)); + } + } + + /** + * Creates the default update data with values from the EPD system + * properties, setting all fields except for the update marker. Reusing the + * update data object avoids creating a new one for each update request. + * + * @implNote An update mode of {@link EPDSystem#UPDATE_MODE_FULL} would make + * the {@link EPDSettings#NO_WAIT} system property useless by changing all + * non-colliding updates into colliding ones, so this method sets the + * default update mode to {@link EPDSystem#UPDATE_MODE_PARTIAL}. + * + * @param width the width of the update region + * @param height the height of the update region + * @return the default update data with all fields set but the update marker + */ + private MxcfbUpdateData createDefaultUpdate(int width, int height) { + var update = new MxcfbUpdateData(); + update.setUpdateRegion(update.p, 0, 0, width, height); + update.setWaveformMode(update.p, settings.waveformMode); + update.setUpdateMode(update.p, EPDSystem.UPDATE_MODE_PARTIAL); + update.setTemp(update.p, EPDSystem.TEMP_USE_AMBIENT); + update.setFlags(update.p, settings.flags); + return update; + } + + /** + * Defines a mapping for common waveform modes. This mapping must be + * configured for the automatic waveform mode selection to function + * properly. Each of the parameters should be set to one of the following: + *
    + *
  • {@link EPDSystem#WAVEFORM_MODE_INIT}
  • + *
  • {@link EPDSystem#WAVEFORM_MODE_DU}
  • + *
  • {@link EPDSystem#WAVEFORM_MODE_GC16}
  • + *
  • {@link EPDSystem#WAVEFORM_MODE_GC4}
  • + *
  • {@link EPDSystem#WAVEFORM_MODE_A2}
  • + *
+ * + * @implNote This method fails on the Kobo Glo HD Model N437 with the error + * ENOTTY (25), "Inappropriate ioctl for device." The driver on that device + * uses an extended structure with four additional integers, changing its + * size and its corresponding request code. This method could use the + * extended structure, but the driver on the Kobo Glo HD ignores it and + * returns immediately, anyway. Furthermore, newer devices support both the + * current structure and the extended one, but define the extra fields in a + * different order. Therefore, simply use the current structure and ignore + * an error of ENOTTY, picking up the default values for any extra fields. + * + * @param init the initialization mode for clearing the screen to all white + * @param du the direct update mode for changing any gray values to either + * all black or all white + * @param gc4 the mode for 4-level (2-bit) grayscale images and text + * @param gc8 the mode for 8-level (3-bit) grayscale images and text + * @param gc16 the mode for 16-level (4-bit) grayscale images and text + * @param gc32 the mode for 32-level (5-bit) grayscale images and text + */ + private void setWaveformModes(int init, int du, int gc4, int gc8, int gc16, int gc32) { + var modes = new MxcfbWaveformModes(); + modes.setModes(modes.p, init, du, gc4, gc8, gc16, gc32); + int rc = system.ioctl(fd, driver.MXCFB_SET_WAVEFORM_MODES, modes.p); + if (rc != 0 && system.errno() != ENOTTY) { + logger.severe("Failed setting waveform modes: {0} ({1})", + system.getErrorMessage(), system.errno()); + } + } + + /** + * Sets the temperature to be used by the EPDC driver in subsequent panel + * updates. Note that this temperature setting may be overridden by setting + * the temperature in a specific update to anything other than + * {@link EPDSystem#TEMP_USE_AMBIENT}. + * + * @param temp the temperature in degrees Celsius + */ + private void setTemperature(int temp) { + int rc = driver.ioctl(fd, driver.MXCFB_SET_TEMPERATURE, temp); + if (rc != 0) { + logger.severe("Failed setting temperature to {2} degrees Celsius: {0} ({1})", + system.getErrorMessage(), system.errno(), temp); + } + } + + /** + * Selects between automatic and region update mode. In region update mode, + * updates must be submitted with an IOCTL call to + * {@link EPDSystem#MXCFB_SEND_UPDATE}. In automatic mode, updates are + * generated by the driver when it detects that pages in a frame buffer + * memory region have been modified. + *

+ * Automatic mode is available only when it has been enabled in the Linux + * kernel by the option CONFIG_FB_MXC_EINK_AUTO_UPDATE_MODE. You can find + * the configuration options used to build the kernel in a file under + * /proc or /boot, such as /proc/config.gz.

+ * + * @param mode the automatic update mode, one of: + *
    + *
  • {@link EPDSystem#AUTO_UPDATE_MODE_REGION_MODE}
  • + *
  • {@link EPDSystem#AUTO_UPDATE_MODE_AUTOMATIC_MODE}
  • + *
+ */ + private void setAutoUpdateMode(int mode) { + int rc = driver.ioctl(fd, driver.MXCFB_SET_AUTO_UPDATE_MODE, mode); + if (rc != 0) { + logger.severe("Failed setting auto-update mode to {2}: {0} ({1})", + system.getErrorMessage(), system.errno(), mode); + } + } + + /** + * Requests the entire visible region of the frame buffer to be updated to + * the display. + * + * @param updateMode the update mode, one of: + *
    + *
  • {@link EPDSystem#UPDATE_MODE_PARTIAL}
  • + *
  • {@link EPDSystem#UPDATE_MODE_FULL}
  • + *
+ * @param waveformMode the waveform mode, one of: + *
    + *
  • {@link EPDSystem#WAVEFORM_MODE_INIT}
  • + *
  • {@link EPDSystem#WAVEFORM_MODE_DU}
  • + *
  • {@link EPDSystem#WAVEFORM_MODE_GC16}
  • + *
  • {@link EPDSystem#WAVEFORM_MODE_GC4}
  • + *
  • {@link EPDSystem#WAVEFORM_MODE_A2}
  • + *
  • {@link EPDSystem#WAVEFORM_MODE_AUTO}
  • + *
+ * @param flags a bit mask composed of the following flag values: + *
    + *
  • {@link EPDSystem#EPDC_FLAG_ENABLE_INVERSION}
  • + *
  • {@link EPDSystem#EPDC_FLAG_FORCE_MONOCHROME}
  • + *
  • {@link EPDSystem#EPDC_FLAG_USE_DITHERING_Y1}
  • + *
  • {@link EPDSystem#EPDC_FLAG_USE_DITHERING_Y4}
  • + *
+ * @return the marker to identify this update in a subsequence call to + * {@link #waitForUpdateComplete} + */ + private int sendUpdate(int updateMode, int waveformMode, int flags) { + updateData.setUpdateRegion(updateData.p, 0, 0, xres, yres); + updateData.setUpdateMode(updateData.p, updateMode); + updateData.setTemp(updateData.p, EPDSystem.TEMP_USE_AMBIENT); + updateData.setFlags(updateData.p, flags); + return sendUpdate(updateData, waveformMode); + } + + /** + * Requests an update to the display, allowing for the reuse of the update + * data object. The waveform mode is reset because the update data could + * have been used in a previous update. In that case, the waveform mode may + * have been modified by the EPDC driver with the actual mode selected. The + * update marker is overwritten with the next sequential marker. + * + * @param update the data describing the update; the waveform mode and + * update marker are overwritten + * @param waveformMode the waveform mode for this update + * @return the marker to identify this update in a subsequence call to + * {@link #waitForUpdateComplete} + */ + private int sendUpdate(MxcfbUpdateData update, int waveformMode) { + /* + * The IOCTL call to MXCFB_WAIT_FOR_UPDATE_COMPLETE returns the error + * "Invalid argument (22)" when passed an update marker of zero. + */ + updateMarker++; + if (updateMarker == 0) { + updateMarker++; + } + update.setWaveformMode(update.p, waveformMode); + update.setUpdateMarker(update.p, updateMarker); + int rc = system.ioctl(fd, driver.MXCFB_SEND_UPDATE, update.p); + if (rc != 0) { + logger.severe("Failed sending update {2}: {0} ({1})", + system.getErrorMessage(), system.errno(), Integer.toUnsignedLong(updateMarker)); + } else if (logger.isLoggable(Level.FINER)) { + logger.finer("Sent update: {0} x {1}, waveform {2}, selected {3}, flags 0x{4}, marker {5}", + update.getUpdateRegionWidth(update.p), update.getUpdateRegionHeight(update.p), + waveformMode, update.getWaveformMode(update.p), + Integer.toHexString(update.getFlags(update.p)).toUpperCase(), + Integer.toUnsignedLong(updateMarker)); + } + return updateMarker; + } + + /** + * Blocks and waits for a previous update request to complete. + * + * @param marker the marker to identify a particular update, returned by + * {@link #sendUpdate(MxcfbUpdateData, int)} + */ + private void waitForUpdateComplete(int marker) { + /* + * This IOCTL call returns: 0 if the marker was not found because the + * update already completed or failed, negative (-1) with the error + * "Connection timed out (110)" if the wait timed out after 5 seconds, + * or positive if the wait occurred and completed (see + * "wait_for_completion_timeout" in "kernel/sched/completion.c"). + */ + int rc = driver.ioctl(fd, driver.MXCFB_WAIT_FOR_UPDATE_COMPLETE, marker); + if (rc < 0) { + logger.severe("Failed waiting for update {2}: {0} ({1})", + system.getErrorMessage(), system.errno(), Integer.toUnsignedLong(marker)); + } else if (rc == 0 && logger.isLoggable(Level.FINER)) { + logger.finer("Update completed before wait: marker {0}", + Integer.toUnsignedLong(marker)); + } + } + + /** + * Sets the delay between the completion of all updates in the driver and + * when the driver should power down the EPDC and display power supplies. To + * disable powering down entirely, use the delay value + * {@link EPDSystem#FB_POWERDOWN_DISABLE}. + * + * @param delay the delay in milliseconds + */ + private void setPowerdownDelay(int delay) { + int rc = driver.ioctl(fd, driver.MXCFB_SET_PWRDOWN_DELAY, delay); + if (rc != 0) { + logger.severe("Failed setting power-down delay to {2}: {0} ({1})", + system.getErrorMessage(), system.errno(), delay); + } + } + + /** + * Gets the current power-down delay from the EPDC driver. + * + * @return the delay in milliseconds + */ + private int getPowerdownDelay() { + var integer = new IntStructure(); + int rc = system.ioctl(fd, driver.MXCFB_GET_PWRDOWN_DELAY, integer.p); + if (rc != 0) { + logger.severe("Failed getting power-down delay: {0} ({1})", + system.getErrorMessage(), system.errno()); + } + return integer.get(integer.p); + } + + /** + * Selects a scheme for the flow of updates within the driver. + * + * @param scheme the update scheme, one of: + *
    + *
  • {@link EPDSystem#UPDATE_SCHEME_SNAPSHOT}
  • + *
  • {@link EPDSystem#UPDATE_SCHEME_QUEUE}
  • + *
  • {@link EPDSystem#UPDATE_SCHEME_QUEUE_AND_MERGE}
  • + *
+ */ + private void setUpdateScheme(int scheme) { + int rc = driver.ioctl(fd, driver.MXCFB_SET_UPDATE_SCHEME, scheme); + if (rc != 0) { + logger.severe("Failed setting update scheme to {2}: {0} ({1})", + system.getErrorMessage(), system.errno(), scheme); + } + } + + /** + * Initializes the EPDC frame buffer device, setting the update scheme to + * {@link EPDSystem#UPDATE_SCHEME_SNAPSHOT}. + */ + void init() { + setWaveformModes(EPDSystem.WAVEFORM_MODE_INIT, EPDSystem.WAVEFORM_MODE_DU, + EPDSystem.WAVEFORM_MODE_GC4, EPDSystem.WAVEFORM_MODE_GC16, + EPDSystem.WAVEFORM_MODE_GC16, EPDSystem.WAVEFORM_MODE_GC16); + setTemperature(EPDSystem.TEMP_USE_AMBIENT); + setAutoUpdateMode(EPDSystem.AUTO_UPDATE_MODE_REGION_MODE); + setPowerdownDelay(POWERDOWN_DELAY); + setUpdateScheme(EPDSystem.UPDATE_SCHEME_SNAPSHOT); + } + + /** + * Clears the display panel. The visible frame buffer should be cleared with + * zeros when called. This method sends two direct updates (all black + * followed by all white) to refresh the screen and clear any ghosting + * effects, and returns when both updates are complete. + *

+ * This method is not thread safe, but it is invoked only + * once from the Event Thread during initialization.

+ */ + void clear() { + lastMarker = sendUpdate(EPDSystem.UPDATE_MODE_FULL, + EPDSystem.WAVEFORM_MODE_DU, 0); + lastMarker = sendUpdate(EPDSystem.UPDATE_MODE_FULL, + EPDSystem.WAVEFORM_MODE_DU, EPDSystem.EPDC_FLAG_ENABLE_INVERSION); + waitForUpdateComplete(lastMarker); + } + + /** + * Sends the updated contents of the Linux frame buffer to the EPDC driver, + * optionally synchronizing with the driver by first waiting for the + * previous update to complete. + *

+ * This method is not thread safe, but it is invoked only + * from the JavaFX Application Thread.

+ */ + void sync() { + if (!settings.noWait) { + waitForUpdateComplete(lastMarker); + } + lastMarker = sendUpdate(syncUpdate, settings.waveformMode); + } + + /** + * Gets the number of bytes from the beginning of the frame buffer to the + * start of its visible resolution. + * + * @return the offset in bytes + */ + int getByteOffset() { + return byteOffset; + } + + /** + * Creates an off-screen byte buffer equal in resolution to the virtual + * resolution of the frame buffer, but with 32 bits per pixel. + * + * @return a 32-bit pixel buffer matching the resolution of the frame buffer + */ + ByteBuffer getOffscreenBuffer() { + /* + * In this case, a direct byte buffer outside of the normal heap is + * faster than a non-direct byte buffer on the heap. The frame rate is + * roughly 10 to 40 percent faster for a framebuffer with 8 bits per + * pixel and 40 to 60 percent faster for a framebuffer with 16 bits per + * pixel, depending on the device processor and screen size. + */ + int size = xresVirtual * yres * Integer.BYTES; + return ByteBuffer.allocateDirect(size); + } + + /** + * Creates a new mapping of the Linux frame buffer device into memory. + * + * @implNote The virtual y-resolution reported by the device driver can be + * wrong, as shown by the following example on the Kobo Glo HD Model N437 + * which reports 2,304 pixels when the correct value is 1,152 pixels + * (6,782,976 / 5,888). Therefore, this method cannot use the frame buffer + * virtual resolution to calculate its size. + * + *
{@code
+     * $ sudo fbset -i
+     *
+     * mode "1448x1072-46"
+     * # D: 80.000 MHz, H: 50.188 kHz, V: 46.385 Hz
+     * geometry 1448 1072 1472 2304 32
+     * timings 12500 16 102 4 4 28 2
+     * rgba 8/16,8/8,8/0,8/24
+     * endmode
+     *
+     * Frame buffer device information:
+     * Name        : mxc_epdc_fb
+     * Address     : 0x88000000
+     * Size        : 6782976
+     * Type        : PACKED PIXELS
+     * Visual      : TRUECOLOR
+     * XPanStep    : 1
+     * YPanStep    : 1
+     * YWrapStep   : 0
+     * LineLength  : 5888
+     * Accelerator : No
+     * }
+ * + * @return a byte buffer containing the mapping of the Linux frame buffer + * device if successful; otherwise {@code null} + */ + ByteBuffer getMappedBuffer() { + ByteBuffer buffer = null; + int size = xresVirtual * yres * bytesPerPixel; + logger.fine("Mapping frame buffer: {0} bytes", size); + long addr = system.mmap(0l, size, LinuxSystem.PROT_WRITE, LinuxSystem.MAP_SHARED, fd, 0); + if (addr == LinuxSystem.MAP_FAILED) { + logger.severe("Failed mapping {2} bytes of frame buffer: {0} ({1})", + system.getErrorMessage(), system.errno(), size); + } else { + buffer = C.getC().NewDirectByteBuffer(addr, size); + } + return buffer; + } + + /** + * Deletes the mapping of the Linux frame buffer device. + * + * @param buffer the byte buffer containing the mapping of the Linux frame + * buffer device + */ + void releaseMappedBuffer(ByteBuffer buffer) { + int size = buffer.capacity(); + logger.fine("Unmapping frame buffer: {0} bytes", size); + int rc = system.munmap(C.getC().GetDirectBufferAddress(buffer), size); + if (rc != 0) { + logger.severe("Failed unmapping {2} bytes of frame buffer: {0} ({1})", + system.getErrorMessage(), system.errno(), size); + } + } + + /** + * Closes the Linux frame buffer device. + */ + void close() { + system.close(fd); + } + + /** + * Gets the native handle to the Linux frame buffer device. + * + * @return the frame buffer device file descriptor + */ + long getNativeHandle() { + return fd; + } + + /** + * Gets the frame buffer width in pixels. See the notes for the + * {@linkplain EPDFrameBuffer#EPDFrameBuffer constructor} above. + * + * @implNote When using an 8-bit, unrotated, and uninverted frame buffer in + * the Y8 pixel format, the Kobo Clara HD Model N249 works only when this + * method returns the visible x-resolution ({@code xres}) instead of the + * normal virtual x-resolution ({@code xresVirtual}). + * + * @return the width in pixels + */ + int getWidth() { + return settings.getWidthVisible ? xres : xresVirtual; + } + + /** + * Gets the frame buffer height in pixels. + * + * @return the height in pixels + */ + int getHeight() { + return yres; + } + + /** + * Gets the frame buffer color depth in bits per pixel. + * + * @return the color depth in bits per pixel + */ + int getBitDepth() { + return bitsPerPixel; + } + + @Override + public String toString() { + return MessageFormat.format("{0}[width={1} height={2} bitDepth={3}]", + getClass().getName(), getWidth(), getHeight(), getBitDepth()); + } +} diff --git a/src/main/java/com/sun/glass/ui/monocle/EPDInputDeviceRegistry.java b/src/main/java/com/sun/glass/ui/monocle/EPDInputDeviceRegistry.java new file mode 100644 index 0000000..bee92e1 --- /dev/null +++ b/src/main/java/com/sun/glass/ui/monocle/EPDInputDeviceRegistry.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.glass.ui.monocle; + +import java.io.File; +import java.io.IOException; +import java.text.MessageFormat; +import java.util.HashMap; +import java.util.Map; + +/** + * Maintains an observable set of input devices. This class is responsible for + * detecting the attached input devices and generating their input events. Run + * the following commands as root to list the properties of the keypad + * (event0) and touch screen (event1) input devices on the system: + *
{@code
+ * # udevadm info -q all -n /dev/input/event0
+ * # udevadm info -q all -n /dev/input/event1
+ * }
+ * + * @implNote {@code EPDPlatform} creates an instance of this class instead of + * {@code LinuxInputDeviceRegistry} because this class replaces two of its + * methods. + *

+ * It replaces the {@link #createDevice} method to work around bug JDK-8201568 + * by opening the device before creating its {@code LinuxInputDevice}. It also + * replaces the {@link #addDeviceInternal} method to work around older versions + * of udev, such as version 142, which do not provide the + * ID_INPUT_TOUCHSCREEN=1 property for the touch screen device. + * {@link LinuxInputDevice#isTouch} requires that property and value; otherwise + * the method returns {@code false}, and the touch screen is mistakenly assigned + * a keyboard input processor. Newer versions of udev, such as version + * 204, provide the correct property and value.

+ *

+ * Therefore, once JDK-8201568 is fixed and the old version of udev is no + * longer in use, this entire class can be removed and replaced by + * {@code LinuxInputDeviceRegistry}.

+ */ +class EPDInputDeviceRegistry extends InputDeviceRegistry { + + /** + * The file name of the keypad input device. + */ + private static final String KEYPAD_FILENAME = "event0"; + + /** + * The file name of the touch screen input device. + */ + private static final String TOUCH_FILENAME = "event1"; + + /** + * Creates a new observable set of input devices. + * + * @implNote This is a verbatim copy of the {@link LinuxInputDeviceRegistry} + * constructor. + * + * @param headless {@code true} if this environment cannot support a + * display, keyboard, and mouse; otherwise {@code false} + */ + EPDInputDeviceRegistry(boolean headless) { + if (headless) { + // Keep the registry but do not bind it to udev. + return; + } + Map deviceMap = new HashMap<>(); + UdevListener udevListener = (action, event) -> { + String subsystem = event.get("SUBSYSTEM"); + String devPath = event.get("DEVPATH"); + String devName = event.get("DEVNAME"); + if (subsystem != null && subsystem.equals("input") + && devPath != null && devName != null) { + try { + File sysPath = new File("/sys", devPath); + if (action.equals("add") + || (action.equals("change") + && !deviceMap.containsKey(sysPath))) { + File devNode = new File(devName); + LinuxInputDevice device = createDevice( + devNode, sysPath, event); + if (device != null) { + deviceMap.put(sysPath, device); + } + } else if (action.equals("remove")) { + LinuxInputDevice device = deviceMap.get(sysPath); + deviceMap.remove(sysPath); + if (device != null) { + devices.remove(device); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + }; + Udev.getInstance().addListener(udevListener); + // Request updates for existing devices + SysFS.triggerUdevNotification("input"); + } + + /** + * Creates a Linux input device with the given properties. + * + * @implNote Works around bug + * JDK-8201568, + * "zForce touchscreen input device fails when closed and immediately + * reopened," by opening the device before creating its + * {@code LinuxInputDevice}. + * + * @param devNode the file representing the device name, such as + * /dev/input/event1 + * @param sysPath the system path to the device, such as + * /sys/devices/virtual/input/input1/event1 + * @param udevManifest the set of properties for the device + * @return the new Linux input device, or {@code null} if no processor is + * found for the device + * @throws IOException if an error occurs opening the device + */ + private LinuxInputDevice createDevice(File devNode, File sysPath, + Map udevManifest) throws IOException { + LinuxSystem system = LinuxSystem.getLinuxSystem(); + system.open(devNode.getPath(), LinuxSystem.O_RDONLY); + + var device = new LinuxInputDevice(devNode, sysPath, udevManifest); + return addDeviceInternal(device, "Linux input: " + devNode.toString()); + } + + /** + * Creates an input processor for the device which runs on a new daemon + * background thread. Run the following commands as root to display + * the events generated by the keypad (0) and touch screen (1) input devices + * when you press buttons or touch the screen: + *
{@code
+     * # input-events 0
+     * # input-events 1
+     * }
+ * + * @implNote The "mxckpd" keypad device driver does not generate EV_SYN + * events, yet the {@link LinuxInputDevice#run} method schedules an event + * for processing only after receiving the EV_SYN event terminator (see the + * {@link LinuxEventBuffer#put} method). The events from this device, + * therefore, are never delivered to the JavaFX application. The "gpio-keys" + * keypad device driver on more recent systems, though, correctly generates + * the EV_SYN event terminator. + * + * @param device the Linux input device + * @param name the device name, such as /dev/input/event0 + * @return the Linux input device, or {@code null} if no input processor is + * found for the device + */ + private LinuxInputDevice addDeviceInternal(LinuxInputDevice device, String name) { + LinuxInputProcessor processor = null; + if (name.endsWith(KEYPAD_FILENAME)) { + processor = new LinuxKeyProcessor(); + } else if (name.endsWith(TOUCH_FILENAME)) { + processor = new LinuxSimpleTouchProcessor(device); + } + if (processor == null) { + return null; + } else { + device.setInputProcessor(processor); + var thread = new Thread(device); + thread.setName(name); + thread.setDaemon(true); + thread.start(); + devices.add(device); + return device; + } + } + + @Override + public String toString() { + return MessageFormat.format("{0}[devices={1}]", getClass().getName(), devices); + } +} diff --git a/src/main/java/com/sun/glass/ui/monocle/EPDPlatform.java b/src/main/java/com/sun/glass/ui/monocle/EPDPlatform.java new file mode 100644 index 0000000..946ecbb --- /dev/null +++ b/src/main/java/com/sun/glass/ui/monocle/EPDPlatform.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.glass.ui.monocle; + +/** + * A native platform for a Linux system with an electrophoretic display, also + * called an e-paper display. + */ +class EPDPlatform extends LinuxPlatform { + + /** + * Creates a new Monocle EPD Platform. + */ + EPDPlatform() { + EPDSystem.getEPDSystem().loadLibrary(); + } + + @Override + protected InputDeviceRegistry createInputDeviceRegistry() { + return new EPDInputDeviceRegistry(false); + } + + @Override + protected NativeScreen createScreen() { + try { + return new EPDScreen(); + } catch (RuntimeException e) { + return new HeadlessScreen(); + } + } +} diff --git a/src/main/java/com/sun/glass/ui/monocle/EPDPlatformFactory.java b/src/main/java/com/sun/glass/ui/monocle/EPDPlatformFactory.java new file mode 100644 index 0000000..9740c37 --- /dev/null +++ b/src/main/java/com/sun/glass/ui/monocle/EPDPlatformFactory.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.glass.ui.monocle; + +import com.sun.javafx.logging.PlatformLogger; +import com.sun.javafx.util.Logging; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.text.MessageFormat; + +/** + * A factory object for creating the native platform on a Linux system with an + * electrophoretic display, also called an e-paper display, found on e-readers + * such as the Amazon Kindle and Rakuten Kobo. + */ +class EPDPlatformFactory extends NativePlatformFactory { + + /** + * The major version number of this platform factory. + */ + private static final int MAJOR_VERSION = 1; + + /** + * The minor version number of this platform factory. + */ + private static final int MINOR_VERSION = 0; + + /** + * The file that contains the name of the frame buffer device when CONFIG_FB + * is defined during kernel compilation. + */ + private static final String FB_FILE = "/proc/fb"; + + /** + * The name of the Mobile Extreme Convergence Electrophoretic Display + * Controller Frame Buffer device. + */ + private static final String FB_NAME = "mxc_epdc_fb"; + + private final PlatformLogger logger = Logging.getJavaFXLogger(); + + /** + * Creates a new factory object for the Monocle EPD Platform. + */ + EPDPlatformFactory() { + } + + @Override + protected boolean matches() { + @SuppressWarnings("removal") + String fbinfo = AccessController.doPrivileged((PrivilegedAction) () -> { + String line = null; + try (var reader = new BufferedReader(new FileReader(FB_FILE))) { + line = reader.readLine(); + } catch (IOException e) { + logger.severe("Failed reading " + FB_FILE, e); + } + return line; + }); + return fbinfo != null && fbinfo.contains(FB_NAME); + } + + @Override + protected NativePlatform createNativePlatform() { + return new EPDPlatform(); + } + + @Override + protected int getMajorVersion() { + return MAJOR_VERSION; + } + + @Override + protected int getMinorVersion() { + return MINOR_VERSION; + } + + @Override + public String toString() { + return MessageFormat.format("{0}[majorVersion={1} minorVersion={2} matches=\"{3} in {4}\"]", + getClass().getName(), getMajorVersion(), getMinorVersion(), FB_NAME, FB_FILE); + } +} diff --git a/src/main/java/com/sun/glass/ui/monocle/EPDScreen.java b/src/main/java/com/sun/glass/ui/monocle/EPDScreen.java new file mode 100644 index 0000000..7c924e7 --- /dev/null +++ b/src/main/java/com/sun/glass/ui/monocle/EPDScreen.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.glass.ui.monocle; + +import com.sun.glass.ui.Pixels; +import com.sun.javafx.logging.PlatformLogger; +import com.sun.javafx.util.Logging; +import java.io.IOException; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.FileChannel; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.text.MessageFormat; + +/** + * A native screen for an electrophoretic display, also called an e-paper + * display. This class uploads pixels directly into the Linux frame buffer if it + * is configured with a color depth of 32 bits per pixel. Otherwise, this class + * uploads pixels into a 32-bit off-screen composition buffer and converts the + * pixels to the correct format when writing them to the Linux frame buffer. + */ +class EPDScreen implements NativeScreen { + + /** + * The system property for setting the frame buffer device path. + */ + private static final String FB_PATH_KEY = "monocle.screen.fb"; + + /** + * The default value for the frame buffer device path. + */ + private static final String FB_PATH_DEFAULT = "/dev/fb0"; + + /** + * The density of this screen in pixels per inch. For now, the value is + * hard-coded to the density of a 6-inch display panel with 800 x 600 px at + * 167 ppi. + */ + private static final int DPI = 167; + + /** + * The ratio of physical pixels to logical pixels on this screen. For now, + * the value is hard-coded to a ratio of 1.0. + */ + private static final float SCALE = 1.0f; + + private final PlatformLogger logger = Logging.getJavaFXLogger(); + + private final String fbPath; + private final EPDFrameBuffer fbDevice; + private final ByteBuffer fbMapping; + private final FileChannel fbChannel; + private final Framebuffer pixels; + private final int width; + private final int height; + private final int bitDepth; + + private boolean isShutdown; + + /** + * Creates a native screen for the electrophoretic display. + * + * @throws IllegalStateException if an error occurs opening the frame buffer + */ + EPDScreen() { + @SuppressWarnings("removal") + String tmp = AccessController.doPrivileged((PrivilegedAction) () + -> System.getProperty(FB_PATH_KEY, FB_PATH_DEFAULT)); + fbPath = tmp; + try { + fbDevice = new EPDFrameBuffer(fbPath); + fbDevice.init(); + + width = fbDevice.getWidth(); + height = fbDevice.getHeight(); + bitDepth = fbDevice.getBitDepth(); + logger.fine("Native screen geometry: {0} px x {1} px x {2} bpp", + width, height, bitDepth); + + /* + * If the Linux frame buffer is configured for 32-bit color, compose + * the pixels directly into it. Otherwise, compose the pixels into + * an off-screen buffer and write them to the frame buffer when + * swapping buffers. + * + * With an LCD display, there must be space for two full screens to + * be able to write directly into the frame buffer, displaying one + * while updating the other. The Snapshot update mode of an e-paper + * display, though, allows us to reuse the same frame buffer region + * immediately after sending an update. + */ + ByteBuffer mapping = null; + if (bitDepth == Integer.SIZE) { + mapping = fbDevice.getMappedBuffer(); + } + if (mapping != null) { + fbMapping = mapping; + fbChannel = null; + } else { + Path path = FileSystems.getDefault().getPath(fbPath); + fbChannel = FileChannel.open(path, StandardOpenOption.WRITE); + fbMapping = null; + } + } catch (IOException e) { + String msg = MessageFormat.format("Failed opening frame buffer: {0}", fbPath); + logger.severe(msg, e); + throw new IllegalStateException(msg, e); + } + + /* + * Note that pixels.clearBufferContents() throws a NullPointerException + * if the last parameter of its constructor ("clear") is false. + */ + ByteBuffer buffer = fbMapping != null ? fbMapping : fbDevice.getOffscreenBuffer(); + buffer.order(ByteOrder.nativeOrder()); + pixels = new FramebufferY8(buffer, width, height, bitDepth, true); + clearScreen(); + } + + /** + * Closes the Linux frame buffer device and related resources. Called only + * from the {@link #shutdown} method, which is called only once. + */ + private void close() { + try { + if (fbChannel != null) { + fbChannel.close(); + } + } catch (IOException e) { + logger.severe("Failed closing frame buffer channel", e); + } finally { + if (fbMapping != null) { + fbDevice.releaseMappedBuffer(fbMapping); + } + fbDevice.close(); + } + } + + /** + * Writes the content of the off-screen buffer to the Linux frame buffer, if + * necessary. If the frame buffer is mapped, the content to display is + * already there, and this method does nothing. + */ + private void writeBuffer() { + if (fbChannel != null) { + try { + fbChannel.position(fbDevice.getByteOffset()); + pixels.write(fbChannel); + } catch (IOException e) { + logger.severe("Failed writing to frame buffer channel", e); + } + } + } + + /** + * Clears the screen. + */ + private void clearScreen() { + pixels.clearBufferContents(); + writeBuffer(); + fbDevice.clear(); + } + + @Override + public int getDepth() { + return bitDepth; + } + + @Override + public int getNativeFormat() { + /* + * The native pixel format must be one of either + * Pixels.Format.BYTE_BGRA_PRE when the system byte order is + * ByteOrder.LITTLE_ENDIAN, or Pixels.Format.BYTE_ARGB when the system + * byte order is ByteOrder.BIG_ENDIAN. The ARMv7-A architecture is + * little endian by default. + */ + return Pixels.Format.BYTE_BGRA_PRE; + } + + @Override + public int getWidth() { + return width; + } + + @Override + public int getHeight() { + return height; + } + + @Override + public int getDPI() { + return DPI; + } + + @Override + public long getNativeHandle() { + return fbDevice.getNativeHandle(); + } + + @Override + public synchronized void shutdown() { + close(); + isShutdown = true; + } + + @Override + public synchronized void uploadPixels(Buffer b, int x, int y, int width, int height, float alpha) { + pixels.composePixels(b, x, y, width, height, alpha); + } + + @Override + public synchronized void swapBuffers() { + if (!isShutdown && pixels.hasReceivedData()) { + writeBuffer(); + fbDevice.sync(); + pixels.reset(); + } + } + + @Override + public synchronized ByteBuffer getScreenCapture() { + return pixels.getBuffer().asReadOnlyBuffer(); + } + + @Override + public float getScale() { + return SCALE; + } + + @Override + public String toString() { + return MessageFormat.format("{0}[width={1} height={2} depth={3} DPI={4} scale={5,number,0.0#}]", + getClass().getName(), getWidth(), getHeight(), getDepth(), getDPI(), getScale()); + } +} diff --git a/src/main/java/com/sun/glass/ui/monocle/EPDSettings.java b/src/main/java/com/sun/glass/ui/monocle/EPDSettings.java new file mode 100644 index 0000000..5b162d0 --- /dev/null +++ b/src/main/java/com/sun/glass/ui/monocle/EPDSettings.java @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.glass.ui.monocle; + +import com.sun.javafx.logging.PlatformLogger; +import com.sun.javafx.logging.PlatformLogger.Level; +import com.sun.javafx.util.Logging; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.HashMap; + +/** + * Provides the values of the EPD system properties. + */ +class EPDSettings { + + /** + * Sets the frame buffer color depth and pixel format: 8 for 8-bit grayscale + * in the Y8 pixel format, 16 for 16-bit color in the RGB565 pixel format, + * or 32 for 32-bit color in the ARGB32 pixel format. The default is 32. + *

+ * Using the 32-bit format allows JavaFX to render directly into the Linux + * frame buffer and avoid the step of copying and converting each pixel from + * an off-screen composition buffer.

+ * + * @implNote Corresponds to the {@code bits_per_pixel} field of + * {@code fb_var_screeninfo} in linux/fb.h. + */ + private static final String BITS_PER_PIXEL = "monocle.epd.bitsPerPixel"; + + /** + * Sets the frame buffer rotation: 0 for unrotated (UR), 1 for 90 degrees + * clockwise (CW), 2 for 180 degrees upside-down (UD), and 3 for 90 degrees + * counter-clockwise (CCW). The default is 0. + *

+ * The unrotated and upside-down settings are in landscape mode, while the + * clockwise and counter-clockwise settings are in portrait.

+ * + * @implNote Corresponds to the {@code rotate} field of + * {@code fb_var_screeninfo} in linux/fb.h. + */ + private static final String ROTATE = "monocle.epd.rotate"; + + /** + * Sets an indicator for the frame buffer grayscale value: {@code true} to + * invert the pixels of all updates when using 8-bit grayscale in the Y8 + * pixel format; otherwise {@code false}. The default is {@code false}. + *

+ * The value is ignored when the frame buffer is not set to 8-bit grayscale + * in the Y8 pixel format.

+ * + * @implNote Corresponds to the {@code GRAYSCALE_8BIT_INVERTED} constant in + * linux/mxcfb.h. + */ + private static final String Y8_INVERTED = "monocle.epd.Y8Inverted"; + + /** + * Indicates whether to wait for the previous update to complete before + * sending the next update: {@code true} to avoid waiting and send updates + * as quickly as possible; otherwise {@code false}. The default is + * {@code false}. + *

+ * The number of outstanding updates is limited by the device controller to + * either 16 or 64 concurrent non-colliding updates, depending on the model. + * A value of {@code true} may result in errors if the maximum number of + * concurrent non-colliding updates is exceeded.

+ * + * @implNote Corresponds to the IOCTL call constant + * {@code MXCFB_WAIT_FOR_UPDATE_COMPLETE} in linux/mxcfb.h. + */ + private static final String NO_WAIT = "monocle.epd.noWait"; + + /** + * Sets the waveform mode used for updates: 1 for black-and-white direct + * update (DU), 2 for 16 levels of gray (GC16), 3 for 4 levels of gray + * (GC4), 4 for pure black-and-white animation (A2), and 257 for the + * automatic selection of waveform mode based on the number of gray levels + * in the update (AUTO). The default is 257. + *

+ * Automatic selection chooses one of 1 (DU), 2 (GC16), or 3 (GC4). If the + * waveform mode is set to 2 (GC16), it may be upgraded to a compatible but + * optimized mode internal to the driver, if available.

+ * + * @implNote Corresponds to the {@code waveform_mode} field of + * {@code mxcfb_update_data} in linux/mxcfb.h. + */ + private static final String WAVEFORM_MODE = "monocle.epd.waveformMode"; + + /** + * Sets the update flag for pixel inversion: {@code true} to invert the + * pixels of each update; otherwise {@code false}. The default is + * {@code false}. + * + * @implNote Corresponds to the {@code EPDC_FLAG_ENABLE_INVERSION} constant + * in linux/mxcfb.h. + */ + private static final String FLAG_ENABLE_INVERSION = "monocle.epd.enableInversion"; + + /** + * Sets the update flag for monochrome conversion: {@code true} to convert + * the pixels of each update to pure black and white using a 50-percent + * threshold; otherwise {@code false}. The default is {@code false}. + * + * @implNote Corresponds to the {@code EPDC_FLAG_FORCE_MONOCHROME} constant + * in linux/mxcfb.h. + */ + private static final String FLAG_FORCE_MONOCHROME = "monocle.epd.forceMonochrome"; + + /** + * Sets the update flag for 1-bit dithering: {@code true} to dither each + * update in an 8-bit Y8 frame buffer to 1-bit black and white, if + * available; otherwise {@code false}. The default is {@code false}. + * + * @implNote Corresponds to the {@code EPDC_FLAG_USE_DITHERING_Y1} constant + * in linux/mxcfb.h. + */ + private static final String FLAG_USE_DITHERING_Y1 = "monocle.epd.useDitheringY1"; + + /** + * Sets the update flag for 4-bit dithering: {@code true} to dither each + * update in an 8-bit Y8 frame buffer to 4-bit grayscale, if available; + * otherwise {@code false}. The default is {@code false}. + * + * @implNote Corresponds to the {@code EPDC_FLAG_USE_DITHERING_Y4} constant + * in linux/mxcfb.h. + */ + private static final String FLAG_USE_DITHERING_Y4 = "monocle.epd.useDitheringY4"; + + /** + * Indicates whether to work around the bug found on devices, such as the + * Kobo Clara HD Model N249, which require a screen width equal to the + * visible x-resolution, instead of the normal virtual x-resolution, when + * using an 8-bit, unrotated, and uninverted frame buffer in the Y8 pixel + * format: {@code true} to work around the bug; otherwise {@code false}. The + * default is {@code false}. + */ + private static final String FIX_WIDTH_Y8UR = "monocle.epd.fixWidthY8UR"; + + private static final String[] EPD_PROPERTIES = { + BITS_PER_PIXEL, + ROTATE, + Y8_INVERTED, + NO_WAIT, + WAVEFORM_MODE, + FLAG_ENABLE_INVERSION, + FLAG_FORCE_MONOCHROME, + FLAG_USE_DITHERING_Y1, + FLAG_USE_DITHERING_Y4, + FIX_WIDTH_Y8UR + }; + + private static final int BITS_PER_PIXEL_DEFAULT = Integer.SIZE; + private static final int ROTATE_DEFAULT = EPDSystem.FB_ROTATE_UR; + private static final int WAVEFORM_MODE_DEFAULT = EPDSystem.WAVEFORM_MODE_AUTO; + + private static final int[] BITS_PER_PIXEL_PERMITTED = { + Byte.SIZE, + Short.SIZE, + Integer.SIZE + }; + + private static final int[] ROTATIONS_PERMITTED = { + EPDSystem.FB_ROTATE_UR, + EPDSystem.FB_ROTATE_CW, + EPDSystem.FB_ROTATE_UD, + EPDSystem.FB_ROTATE_CCW + }; + + private static final int[] WAVEFORM_MODES_PERMITTED = { + EPDSystem.WAVEFORM_MODE_DU, + EPDSystem.WAVEFORM_MODE_GC16, + EPDSystem.WAVEFORM_MODE_GC4, + EPDSystem.WAVEFORM_MODE_A2, + EPDSystem.WAVEFORM_MODE_AUTO + }; + + /** + * Obtains a new instance of this class with the current values of the EPD + * system properties. + * + * @return a new {@code EPDSettings} instance + */ + @SuppressWarnings("removal") + static EPDSettings newInstance() { + return AccessController.doPrivileged( + (PrivilegedAction) () -> new EPDSettings()); + } + + private final PlatformLogger logger = Logging.getJavaFXLogger(); + + private final boolean y8inverted; + private final boolean flagEnableInversion; + private final boolean flagForceMonochrome; + private final boolean flagUseDitheringY1; + private final boolean flagUseDitheringY4; + private final boolean fixWidthY8UR; + + final int bitsPerPixel; + final int rotate; + final boolean noWait; + final int waveformMode; + final int grayscale; + final int flags; + final boolean getWidthVisible; + + /** + * Creates a new EPDSettings, capturing the current values of the EPD system + * properties. + */ + private EPDSettings() { + if (logger.isLoggable(Level.FINE)) { + HashMap map = new HashMap<>(); + for (String key : EPD_PROPERTIES) { + String value = System.getProperty(key); + if (value != null) { + map.put(key, value); + } + } + logger.fine("EPD system properties: {0}", map); + } + + bitsPerPixel = getInteger(BITS_PER_PIXEL, BITS_PER_PIXEL_DEFAULT, BITS_PER_PIXEL_PERMITTED); + rotate = getInteger(ROTATE, ROTATE_DEFAULT, ROTATIONS_PERMITTED); + noWait = Boolean.getBoolean(NO_WAIT); + waveformMode = getInteger(WAVEFORM_MODE, WAVEFORM_MODE_DEFAULT, WAVEFORM_MODES_PERMITTED); + + y8inverted = Boolean.getBoolean(Y8_INVERTED); + if (bitsPerPixel == Byte.SIZE) { + if (y8inverted) { + grayscale = EPDSystem.GRAYSCALE_8BIT_INVERTED; + } else { + grayscale = EPDSystem.GRAYSCALE_8BIT; + } + } else { + grayscale = 0; + } + + flagEnableInversion = Boolean.getBoolean(FLAG_ENABLE_INVERSION); + flagForceMonochrome = Boolean.getBoolean(FLAG_FORCE_MONOCHROME); + flagUseDitheringY1 = Boolean.getBoolean(FLAG_USE_DITHERING_Y1); + flagUseDitheringY4 = Boolean.getBoolean(FLAG_USE_DITHERING_Y4); + flags = (flagEnableInversion ? EPDSystem.EPDC_FLAG_ENABLE_INVERSION : 0) + | (flagForceMonochrome ? EPDSystem.EPDC_FLAG_FORCE_MONOCHROME : 0) + | (flagUseDitheringY1 ? EPDSystem.EPDC_FLAG_USE_DITHERING_Y1 : 0) + | (flagUseDitheringY4 ? EPDSystem.EPDC_FLAG_USE_DITHERING_Y4 : 0); + + fixWidthY8UR = Boolean.getBoolean(FIX_WIDTH_Y8UR); + getWidthVisible = fixWidthY8UR && grayscale == EPDSystem.GRAYSCALE_8BIT + && rotate == EPDSystem.FB_ROTATE_UR; + } + + /** + * Gets an integer system property. + * + * @param key the property name + * @param def the default value + * @param list a list of the permitted values for the property + * @return the value provided for the property if it is equal to one of the + * permitted values; otherwise, the default value + */ + private int getInteger(String key, int def, int... list) { + int value = Integer.getInteger(key, def); + boolean found = false; + for (int i = 0; i < list.length && !found; i++) { + found = value == list[i]; + } + if (!found) { + logger.severe("Value of {0}={1} not in {2}; using default ({3})", + key, value, Arrays.toString(list), def); + value = def; + } + return value; + } + + @Override + public String toString() { + return MessageFormat.format("{0}[bitsPerPixel={1} rotate={2} " + + "noWait={3} waveformMode={4} grayscale={5} flags=0x{6} " + + "getWidthVisible={7}]", + getClass().getName(), bitsPerPixel, rotate, + noWait, waveformMode, grayscale, Integer.toHexString(flags), + getWidthVisible); + } +} diff --git a/src/main/java/com/sun/glass/ui/monocle/EPDSystem.java b/src/main/java/com/sun/glass/ui/monocle/EPDSystem.java new file mode 100644 index 0000000..cd5455d --- /dev/null +++ b/src/main/java/com/sun/glass/ui/monocle/EPDSystem.java @@ -0,0 +1,752 @@ +/* + * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.glass.ui.monocle; + +import com.sun.glass.utils.NativeLibLoader; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.security.Permission; +import java.text.MessageFormat; + +/** + * A Java-language interface to the device API of the Electrophoretic Display + * Controller (EPDC) frame buffer driver. {@code EPDSystem} is a singleton. Its + * instance is obtained by calling the {@link EPDSystem#getEPDSystem} method. + * This class also extends {@link LinuxSystem.FbVarScreenInfo} to provide all of + * the fields in {@code fb_var_screeninfo}, defined in linux/fb.h. + */ +class EPDSystem { + + /** + * The value for {@link FbVarScreenInfo#setActivate} to ensure that the EPDC + * driver receives the initialization request. + */ + static final int FB_ACTIVATE_FORCE = 128; + + /** + * The value for {@link FbVarScreenInfo#setRotate} to set the frame buffer + * rotation to un-rotated (upright landscape mode). + */ + static final int FB_ROTATE_UR = 0; + + /** + * The value for {@link FbVarScreenInfo#setRotate} to set the frame buffer + * rotation to 90-degrees clockwise (upside-down portrait mode). + */ + static final int FB_ROTATE_CW = 1; + + /** + * The value for {@link FbVarScreenInfo#setRotate} to set the frame buffer + * rotation to 180-degrees upside-down (upside-down landscape mode). + */ + static final int FB_ROTATE_UD = 2; + + /** + * The value for {@link FbVarScreenInfo#setRotate} to set the frame buffer + * rotation to 90-degrees counter-clockwise (upright portrait mode). + */ + static final int FB_ROTATE_CCW = 3; + + /** + * The value for {@link FbVarScreenInfo#setGrayscale} to set the frame + * buffer to an 8-bit grayscale pixel format. + */ + static final int GRAYSCALE_8BIT = 0x1; + + /** + * The value for {@link FbVarScreenInfo#setGrayscale} to set the frame + * buffer to an inverted 8-bit grayscale pixel format. + */ + static final int GRAYSCALE_8BIT_INVERTED = 0x2; + + /** + * Region update mode, in which updates to the display must be submitted + * with an IOCTL call to {@link #MXCFB_SEND_UPDATE}. + */ + static final int AUTO_UPDATE_MODE_REGION_MODE = 0; + + /** + * Automatic mode, in which updates are generated automatically by the + * driver when it detects that pages in a frame buffer memory region have + * been modified. + */ + static final int AUTO_UPDATE_MODE_AUTOMATIC_MODE = 1; + + /** + * Snapshot update scheme, which processes the contents of the frame buffer + * immediately and stores the update in a memory buffer internal to the + * driver. When the IOCTL call to {@link #MXCFB_SEND_UPDATE} returns, the + * frame buffer region is free and can be modified without affecting the + * update. + */ + static final int UPDATE_SCHEME_SNAPSHOT = 0; + + /** + * Queue update scheme, which uses a work queue to handle the processing of + * updates asynchronously. When updates are submitted with an IOCTL call to + * {@link #MXCFB_SEND_UPDATE}, they are added to the queue and processed in + * order as the EPDC hardware resources become available. The frame buffer + * contents processed and displayed, therefore, may not reflect what was + * present in the frame buffer when the update was submitted. + */ + static final int UPDATE_SCHEME_QUEUE = 1; + + /** + * Queue and Merge update scheme, which adds a merging step to the Queue + * update scheme. Before an update is added to the work queue, it is + * compared with other pending updates. If a pending update matches the mode + * and flags of the current update and also overlaps the update region, it + * will be merged with the current update. After all such merges, the final + * merged update is submitted to the queue. + */ + static final int UPDATE_SCHEME_QUEUE_AND_MERGE = 2; + + /** + * Partial update mode, which applies the waveform to only the pixels that + * change in a given region. + */ + static final int UPDATE_MODE_PARTIAL = 0x0; + + /** + * Full update mode, which applies the waveform to all pixels in a given + * region. + */ + static final int UPDATE_MODE_FULL = 0x1; + + /** + * Auto waveform mode, which requests the driver to select the actual + * waveform mode automatically based on the contents of the updated region. + */ + static final int WAVEFORM_MODE_AUTO = 257; + + /** + * The temperature value that requests the driver to use the ambient + * temperature of the device. + */ + static final int TEMP_USE_AMBIENT = 0x1000; + + /** + * An update flag to enable inversion of all pixels in the updated region. + */ + static final int EPDC_FLAG_ENABLE_INVERSION = 0x01; + + /** + * An update flag to enable black-and-white posterization of all pixels in + * the updated region. + */ + static final int EPDC_FLAG_FORCE_MONOCHROME = 0x02; + + /** + * An update flag to enable dithering of an 8-bit grayscale frame buffer to + * 1-bit black and white, if supported by the driver or hardware. + */ + static final int EPDC_FLAG_USE_DITHERING_Y1 = 0x2000; + + /** + * An update flag to enable dithering of an 8-bit grayscale frame buffer to + * 4-bit grayscale, if supported by the driver or hardware. + */ + static final int EPDC_FLAG_USE_DITHERING_Y4 = 0x4000; + + /** + * The power-down delay value to disable the powering down of the EPDC and + * display power supplies. + */ + static final int FB_POWERDOWN_DISABLE = -1; + + /** + * Initialization waveform (0x0...0xF to 0xF in ~4000 ms). Clears the screen + * to all white. + *

+ * "A first exemplary drive scheme provides waveforms that may be used to + * change the display state of a pixel from any initial display state to a + * new display state of white. The first drive scheme may be referred to as + * an initialization or 'INIT' drive scheme." [United States Patent + * 9,280,955]

+ */ + static final int WAVEFORM_MODE_INIT = 0; + + /** + * Direct update waveform (0x0...0xF to 0x0 or 0xF in ~260 ms). Changes gray + * pixels to black or white. + *

+ * "A second exemplary drive scheme provides waveforms that may be used to + * change the display state of a pixel from any initial display state to a + * new display state of either white or black. The second drive scheme may + * be referred to as a 'DU' drive scheme." [United States Patent + * 9,280,955]

+ */ + static final int WAVEFORM_MODE_DU = 1; + + /** + * Gray 4-level waveform (0x0...0xF to 0x0, 0x5, 0xA, or 0xF in ~500 ms). + * Supports 2-bit grayscale images and text with lower quality. + *

+ * "A third exemplary drive scheme provides waveforms that may be used to + * change the display state of a pixel from any initial display state to a + * new display state. The initial state may be any four-bit (16 gray states) + * value. The new display state may be any two-bit (4 gray states) value. + * The third drive scheme may be referred to as a 'GC4' drive scheme." + * [United States Patent 9,280,955]

+ */ + static final int WAVEFORM_MODE_GC4 = 3; + + /** + * Gray 16-level waveform (0x0...0xF to 0x0...0xF in ~760 ms). Supports + * 4-bit grayscale images and text with high quality. + *

+ * "A fourth exemplary drive scheme provides waveforms that may be used to + * change the display state of a pixel from any initial display state to a + * new display state. The initial state may be any four-bit (16 gray states) + * value. The new display state may be any four-bit (16 gray states) value. + * The fourth drive scheme may be referred to as a 'GC16' drive scheme." + * [United States Patent 9,280,955]

+ */ + static final int WAVEFORM_MODE_GC16 = 2; + + /** + * Animation waveform (0x0 or 0xF to 0x0 or 0xF in ~120 ms). Provides a fast + * 1-bit black-and-white animation mode of up to eight frames per second. + *

+ * "A fifth exemplary drive scheme provides waveforms that may be used to + * change the display state of a pixel from an initial display state to a + * new display state. The initial state must be white or black. The new + * display state may be black or white. The fifth drive scheme may be + * referred to as an 'A2' drive scheme. An advantage of A2 waveforms is that + * they have generally short waveform periods, providing rapid display + * updates. A disadvantage of A2 waveforms is that there use may result in + * ghosting artifacts." [United States Patent 9,280,955]

+ */ + static final int WAVEFORM_MODE_A2 = 4; + + private static final Permission PERMISSION = new RuntimePermission("loadLibrary.*"); + private static final EPDSystem INSTANCE = new EPDSystem(); + + /** + * Checks for permission to load native libraries if running under a + * security manager. + */ + private static void checkPermissions() { + @SuppressWarnings("removal") + SecurityManager security = System.getSecurityManager(); + if (security != null) { + security.checkPermission(PERMISSION); + } + } + + /** + * Obtains the single instance of {@code EPDSystem}. Calling this method + * requires the "loadLibrary.*" {@code RuntimePermission}. The + * {@link #loadLibrary} method must be called on the EPDSystem instance + * before any system calls can be made using it. + * + * @return the {@code EPDSystem} instance + */ + static EPDSystem getEPDSystem() { + checkPermissions(); + return INSTANCE; + } + + /** + * The IOCTL request code to define a mapping for common waveform modes. + */ + final int MXCFB_SET_WAVEFORM_MODES; + + /** + * The IOCTL request code to set the temperature used by the EPDC driver in + * subsequent panel updates. + */ + final int MXCFB_SET_TEMPERATURE; + + /** + * The IOCTL request code to select between automatic and region update + * mode. + */ + final int MXCFB_SET_AUTO_UPDATE_MODE; + + /** + * The IOCTL request code to update a region of the frame buffer to the + * display. + */ + final int MXCFB_SEND_UPDATE; + + /** + * The IOCTL request code to block and wait for a previous update to + * complete. + */ + final int MXCFB_WAIT_FOR_UPDATE_COMPLETE; + + /** + * The IOCTL request code to set the delay between the completion of all + * updates in the driver and when the driver should power down the EPDC and + * display power supplies. + */ + final int MXCFB_SET_PWRDOWN_DELAY; + + /** + * The IOCTL request code to get the current power-down delay value from the + * driver. + */ + final int MXCFB_GET_PWRDOWN_DELAY; + + /** + * The IOCTL request code to select a scheme for the flow of updates within + * the driver. + */ + final int MXCFB_SET_UPDATE_SCHEME; + + private final LinuxSystem system; + + /** + * Creates the single instance of {@code EPDSystem}. + */ + private EPDSystem() { + system = LinuxSystem.getLinuxSystem(); + + MXCFB_SET_WAVEFORM_MODES = system.IOW('F', 0x2B, MxcfbWaveformModes.BYTES); + MXCFB_SET_TEMPERATURE = system.IOW('F', 0x2C, Integer.BYTES); + MXCFB_SET_AUTO_UPDATE_MODE = system.IOW('F', 0x2D, Integer.BYTES); + MXCFB_SEND_UPDATE = system.IOW('F', 0x2E, MxcfbUpdateData.BYTES); + MXCFB_WAIT_FOR_UPDATE_COMPLETE = system.IOW('F', 0x2F, Integer.BYTES); + MXCFB_SET_PWRDOWN_DELAY = system.IOW('F', 0x30, Integer.BYTES); + MXCFB_GET_PWRDOWN_DELAY = system.IOR('F', 0x31, IntStructure.BYTES); + MXCFB_SET_UPDATE_SCHEME = system.IOW('F', 0x32, Integer.BYTES); + } + + /** + * Loads the native libraries required to make system calls using this + * {@code EPDSystem} instance. This method must be called before any other + * instance methods of {@code EPDSystem}. If this method is called multiple + * times, it has no effect after the first call. + */ + void loadLibrary() { + NativeLibLoader.loadLibrary("glass_monocle_epd"); + } + + /** + * Calls the {@code ioctl} system function, passing a write integer + * parameter. This method is more convenient than passing the pointer to an + * {@code IntStructure} with {@link LinuxSystem#ioctl} and can be used when + * the request code is created by {@link LinuxSystem#IOW} for setting an + * integer value. + * + * @param fd an open file descriptor + * @param request a device-dependent request code + * @param value the integer value + * @return 0 if successful; otherwise -1 with {@code errno} set + * appropriately + */ + native int ioctl(long fd, int request, int value); + + /** + * A structure for passing the pointer to an integer in an IOCTL call. + */ + static class IntStructure extends C.Structure { + + private static final int VALUE = 0; + + private static final int NUM_INTS = 1; + private static final int BYTES = NUM_INTS * Integer.BYTES; + + private final IntBuffer data; + + IntStructure() { + b.order(ByteOrder.nativeOrder()); + data = b.asIntBuffer(); + } + + @Override + int sizeof() { + return BYTES; + } + + int get(long p) { + return data.get(VALUE); + } + + void set(long p, int value) { + data.put(VALUE, value); + } + } + + /** + * Wraps the C structure {@code mxcfb_waveform_modes}, defined in + * mxcfb.h. + */ + static class MxcfbWaveformModes extends C.Structure { + + private static final int MODE_INIT = 0; + private static final int MODE_DU = 1; + private static final int MODE_GC4 = 2; + private static final int MODE_GC8 = 3; + private static final int MODE_GC16 = 4; + private static final int MODE_GC32 = 5; + + private static final int NUM_INTS = 6; + private static final int BYTES = NUM_INTS * Integer.BYTES; + + private final IntBuffer data; + + MxcfbWaveformModes() { + b.order(ByteOrder.nativeOrder()); + data = b.asIntBuffer(); + } + + @Override + int sizeof() { + return BYTES; + } + + int getModeInit(long p) { + return data.get(MODE_INIT); + } + + int getModeDu(long p) { + return data.get(MODE_DU); + } + + int getModeGc4(long p) { + return data.get(MODE_GC4); + } + + int getModeGc8(long p) { + return data.get(MODE_GC8); + } + + int getModeGc16(long p) { + return data.get(MODE_GC16); + } + + int getModeGc32(long p) { + return data.get(MODE_GC32); + } + + void setModes(long p, int init, int du, int gc4, int gc8, int gc16, int gc32) { + data.put(MODE_INIT, init); + data.put(MODE_DU, du); + data.put(MODE_GC4, gc4); + data.put(MODE_GC8, gc8); + data.put(MODE_GC16, gc16); + data.put(MODE_GC32, gc32); + } + + @Override + public String toString() { + return MessageFormat.format( + "{0}[mode_init={1} mode_du={2} mode_gc4={3} mode_gc8={4} mode_gc16={5} mode_gc32={6}]", + getClass().getName(), getModeInit(p), getModeDu(p), getModeGc4(p), + getModeGc8(p), getModeGc16(p), getModeGc32(p)); + } + } + + /** + * Wraps the C structure {@code mxcfb_update_data}, defined in + * mxcfb.h. + */ + static class MxcfbUpdateData extends C.Structure { + + private static final int UPDATE_REGION_TOP = 0; + private static final int UPDATE_REGION_LEFT = 1; + private static final int UPDATE_REGION_WIDTH = 2; + private static final int UPDATE_REGION_HEIGHT = 3; + + private static final int WAVEFORM_MODE = 4; + private static final int UPDATE_MODE = 5; + private static final int UPDATE_MARKER = 6; + private static final int TEMP = 7; + private static final int FLAGS = 8; + + private static final int ALT_BUFFER_DATA_VIRT_ADDR = 9; + private static final int ALT_BUFFER_DATA_PHYS_ADDR = 10; + private static final int ALT_BUFFER_DATA_WIDTH = 11; + private static final int ALT_BUFFER_DATA_HEIGHT = 12; + + private static final int ALT_BUFFER_DATA_ALT_UPDATE_REGION_TOP = 13; + private static final int ALT_BUFFER_DATA_ALT_UPDATE_REGION_LEFT = 14; + private static final int ALT_BUFFER_DATA_ALT_UPDATE_REGION_WIDTH = 15; + private static final int ALT_BUFFER_DATA_ALT_UPDATE_REGION_HEIGHT = 16; + + private static final int NUM_INTS = 17; + private static final int BYTES = NUM_INTS * Integer.BYTES; + + private final IntBuffer data; + + MxcfbUpdateData() { + b.order(ByteOrder.nativeOrder()); + data = b.asIntBuffer(); + } + + @Override + int sizeof() { + return BYTES; + } + + int getUpdateRegionTop(long p) { + return data.get(UPDATE_REGION_TOP); + } + + int getUpdateRegionLeft(long p) { + return data.get(UPDATE_REGION_LEFT); + } + + int getUpdateRegionWidth(long p) { + return data.get(UPDATE_REGION_WIDTH); + } + + int getUpdateRegionHeight(long p) { + return data.get(UPDATE_REGION_HEIGHT); + } + + int getWaveformMode(long p) { + return data.get(WAVEFORM_MODE); + } + + int getUpdateMode(long p) { + return data.get(UPDATE_MODE); + } + + int getUpdateMarker(long p) { + return data.get(UPDATE_MARKER); + } + + int getTemp(long p) { + return data.get(TEMP); + } + + int getFlags(long p) { + return data.get(FLAGS); + } + + long getAltBufferDataVirtAddr(long p) { + return data.get(ALT_BUFFER_DATA_VIRT_ADDR); + } + + long getAltBufferDataPhysAddr(long p) { + return data.get(ALT_BUFFER_DATA_PHYS_ADDR); + } + + int getAltBufferDataWidth(long p) { + return data.get(ALT_BUFFER_DATA_WIDTH); + } + + int getAltBufferDataHeight(long p) { + return data.get(ALT_BUFFER_DATA_HEIGHT); + } + + int getAltBufferDataAltUpdateRegionTop(long p) { + return data.get(ALT_BUFFER_DATA_ALT_UPDATE_REGION_TOP); + } + + int getAltBufferDataAltUpdateRegionLeft(long p) { + return data.get(ALT_BUFFER_DATA_ALT_UPDATE_REGION_LEFT); + } + + int getAltBufferDataAltUpdateRegionWidth(long p) { + return data.get(ALT_BUFFER_DATA_ALT_UPDATE_REGION_WIDTH); + } + + int getAltBufferDataAltUpdateRegionHeight(long p) { + return data.get(ALT_BUFFER_DATA_ALT_UPDATE_REGION_HEIGHT); + } + + void setUpdateRegion(long p, int top, int left, int width, int height) { + data.put(UPDATE_REGION_TOP, top); + data.put(UPDATE_REGION_LEFT, left); + data.put(UPDATE_REGION_WIDTH, width); + data.put(UPDATE_REGION_HEIGHT, height); + } + + void setWaveformMode(long p, int mode) { + data.put(WAVEFORM_MODE, mode); + } + + void setUpdateMode(long p, int mode) { + data.put(UPDATE_MODE, mode); + } + + void setUpdateMarker(long p, int marker) { + data.put(UPDATE_MARKER, marker); + } + + void setTemp(long p, int temp) { + data.put(TEMP, temp); + } + + void setFlags(long p, int flags) { + data.put(FLAGS, flags); + } + + void setAltBufferData(long p, long virtAddr, long physAddr, int width, int height, + int altUpdateRegionTop, int altUpdateRegionLeft, int altUpdateRegionWidth, int altUpdateRegionHeight) { + data.put(ALT_BUFFER_DATA_VIRT_ADDR, (int) virtAddr); + data.put(ALT_BUFFER_DATA_PHYS_ADDR, (int) physAddr); + data.put(ALT_BUFFER_DATA_WIDTH, width); + data.put(ALT_BUFFER_DATA_HEIGHT, height); + data.put(ALT_BUFFER_DATA_ALT_UPDATE_REGION_TOP, altUpdateRegionTop); + data.put(ALT_BUFFER_DATA_ALT_UPDATE_REGION_LEFT, altUpdateRegionLeft); + data.put(ALT_BUFFER_DATA_ALT_UPDATE_REGION_WIDTH, altUpdateRegionWidth); + data.put(ALT_BUFFER_DATA_ALT_UPDATE_REGION_HEIGHT, altUpdateRegionHeight); + } + + @Override + public String toString() { + return MessageFormat.format( + "{0}[update_region.top={1} update_region.left={2} update_region.width={3} update_region.height={4}" + + " waveform_mode={5} update_mode={6} update_marker={7} temp={8} flags=0x{9}" + + " alt_buffer_data.virt_addr=0x{10} alt_buffer_data.phys_addr=0x{11}" + + " alt_buffer_data.width={12} alt_buffer_data.height={13}" + + " alt_buffer_data.alt_update_region.top={14} alt_buffer_data.alt_update_region.left={15}" + + " alt_buffer_data.alt_update_region.width={16} alt_buffer_data.alt_update_region.height={17}]", + getClass().getName(), + Integer.toUnsignedLong(getUpdateRegionTop(p)), + Integer.toUnsignedLong(getUpdateRegionLeft(p)), + Integer.toUnsignedLong(getUpdateRegionWidth(p)), + Integer.toUnsignedLong(getUpdateRegionHeight(p)), + Integer.toUnsignedLong(getWaveformMode(p)), + Integer.toUnsignedLong(getUpdateMode(p)), + Integer.toUnsignedLong(getUpdateMarker(p)), + getTemp(p), + Integer.toHexString(getFlags(p)), + Long.toHexString(getAltBufferDataVirtAddr(p)), + Long.toHexString(getAltBufferDataPhysAddr(p)), + Integer.toUnsignedLong(getAltBufferDataWidth(p)), + Integer.toUnsignedLong(getAltBufferDataHeight(p)), + Integer.toUnsignedLong(getAltBufferDataAltUpdateRegionTop(p)), + Integer.toUnsignedLong(getAltBufferDataAltUpdateRegionLeft(p)), + Integer.toUnsignedLong(getAltBufferDataAltUpdateRegionWidth(p)), + Integer.toUnsignedLong(getAltBufferDataAltUpdateRegionHeight(p))); + } + } + + /** + * Wraps the entire C structure {@code fb_var_screeninfo}, defined in + * linux/fb.h. + */ + static class FbVarScreenInfo extends LinuxSystem.FbVarScreenInfo { + + native int getGrayscale(long p); + + native int getRedOffset(long p); + + native int getRedLength(long p); + + native int getRedMsbRight(long p); + + native int getGreenOffset(long p); + + native int getGreenLength(long p); + + native int getGreenMsbRight(long p); + + native int getBlueOffset(long p); + + native int getBlueLength(long p); + + native int getBlueMsbRight(long p); + + native int getTranspOffset(long p); + + native int getTranspLength(long p); + + native int getTranspMsbRight(long p); + + native int getNonstd(long p); + + native int getActivate(long p); + + native int getHeight(long p); + + native int getWidth(long p); + + native int getAccelFlags(long p); + + native int getPixclock(long p); + + native int getLeftMargin(long p); + + native int getRightMargin(long p); + + native int getUpperMargin(long p); + + native int getLowerMargin(long p); + + native int getHsyncLen(long p); + + native int getVsyncLen(long p); + + native int getSync(long p); + + native int getVmode(long p); + + native int getRotate(long p); + + native void setGrayscale(long p, int grayscale); + + native void setNonstd(long p, int nonstd); + + native void setHeight(long p, int height); + + native void setWidth(long p, int width); + + native void setAccelFlags(long p, int accelFlags); + + native void setPixclock(long p, int pixclock); + + native void setLeftMargin(long p, int leftMargin); + + native void setRightMargin(long p, int rightMargin); + + native void setUpperMargin(long p, int upperMargin); + + native void setLowerMargin(long p, int lowerMargin); + + native void setHsyncLen(long p, int hsyncLen); + + native void setVsyncLen(long p, int vsyncLen); + + native void setSync(long p, int sync); + + native void setVmode(long p, int vmode); + + native void setRotate(long p, int rotate); + } + + @Override + public String toString() { + return MessageFormat.format("{0}[MXCFB_SET_WAVEFORM_MODES=0x{1} MXCFB_SET_TEMPERATURE=0x{2} " + + "MXCFB_SET_AUTO_UPDATE_MODE=0x{3} MXCFB_SEND_UPDATE=0x{4} MXCFB_WAIT_FOR_UPDATE_COMPLETE=0x{5} " + + "MXCFB_SET_PWRDOWN_DELAY=0x{6} MXCFB_GET_PWRDOWN_DELAY=0x{7} MXCFB_SET_UPDATE_SCHEME=0x{8}]", + getClass().getName(), + Integer.toHexString(MXCFB_SET_WAVEFORM_MODES), + Integer.toHexString(MXCFB_SET_TEMPERATURE), + Integer.toHexString(MXCFB_SET_AUTO_UPDATE_MODE), + Integer.toHexString(MXCFB_SEND_UPDATE), + Integer.toHexString(MXCFB_WAIT_FOR_UPDATE_COMPLETE), + Integer.toHexString(MXCFB_SET_PWRDOWN_DELAY), + Integer.toHexString(MXCFB_GET_PWRDOWN_DELAY), + Integer.toHexString(MXCFB_SET_UPDATE_SCHEME) + ); + } +} diff --git a/src/main/java/com/sun/glass/ui/monocle/FBDevScreen.java b/src/main/java/com/sun/glass/ui/monocle/FBDevScreen.java index 36a8162..d4951dd 100644 --- a/src/main/java/com/sun/glass/ui/monocle/FBDevScreen.java +++ b/src/main/java/com/sun/glass/ui/monocle/FBDevScreen.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,9 +55,11 @@ class FBDevScreen implements NativeScreen { private final String fbDevPath; FBDevScreen() { - fbDevPath = AccessController.doPrivileged( + @SuppressWarnings("removal") + String tmp = AccessController.doPrivileged( (PrivilegedAction) () -> System.getProperty("monocle.screen.fb", "/dev/fb0")); + fbDevPath = tmp; try { linuxFB = new LinuxFrameBuffer(fbDevPath); nativeHandle = 1l; diff --git a/src/main/java/com/sun/glass/ui/monocle/FramebufferY8.java b/src/main/java/com/sun/glass/ui/monocle/FramebufferY8.java new file mode 100644 index 0000000..ef7b4dc --- /dev/null +++ b/src/main/java/com/sun/glass/ui/monocle/FramebufferY8.java @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.glass.ui.monocle; + +import com.sun.javafx.logging.PlatformLogger; +import com.sun.javafx.util.Logging; +import java.io.IOException; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; +import java.nio.channels.WritableByteChannel; +import java.text.MessageFormat; + +/** + * Provides a buffer for composing JavaFX scenes. This class is given a 32-bit + * composition buffer that is either the Linux frame buffer itself or an + * off-screen byte buffer. It can write the contents of this buffer to a target + * channel, or copy them to a buffer, in one of three pixel formats: 32-bit + * ARGB32 color, 16-bit RGB565 color, or 8-bit Y8 grayscale. + */ +class FramebufferY8 extends Framebuffer { + + /** + * The arithmetic right shift value to convert a bit depth to a byte depth. + */ + private static final int BITS_TO_BYTES = 3; + + private final PlatformLogger logger = Logging.getJavaFXLogger(); + private final ByteBuffer bb; + private final int width; + private final int height; + private final int bitDepth; + private final int byteDepth; + + private ByteBuffer lineByteBuffer; + private Buffer linePixelBuffer; + + /** + * Creates a new {@code FramebufferY8} with the given 32-bit composition + * buffer and target color depth. + * + * @param bb the 32-bit composition buffer + * @param width the width of the composition buffer in pixels + * @param height the height of the composition buffer in pixels + * @param depth the color depth of the target channel or buffer in bits per + * pixel + * @param clear {@code true} to clear the composition buffer on the first + * upload of each frame unless that upload already overwrites the entire + * buffer; otherwise {@code false} + */ + FramebufferY8(ByteBuffer bb, int width, int height, int depth, boolean clear) { + super(bb, width, height, depth, clear); + this.bb = bb; + this.width = width; + this.height = height; + this.bitDepth = depth; + this.byteDepth = depth >>> BITS_TO_BYTES; + if (byteDepth != Integer.BYTES && byteDepth != Short.BYTES && byteDepth != Byte.BYTES) { + String msg = MessageFormat.format("Unsupported color depth: {0} bpp", bitDepth); + logger.severe(msg); + throw new IllegalArgumentException(msg); + } + } + + /** + * Copies the next 32-bit ARGB32 pixel to a byte buffer with 8-bit Y8 + * pixels. Luma Y' can be calculated from gamma-corrected R'G'B' using the + * following coefficients. This method uses the coefficients from Rec. 709, + * which defines the same primaries and white point as the sRGB color space. + *
{@code
+     * Simple average:  Y' = (R' + G' + B') / 3
+     * Rec. 601 (SDTV): Y' = 0.299  * R' + 0.587  * G' + 0.114  * B'
+     * Rec. 709 (HDTV): Y' = 0.2126 * R' + 0.7152 * G' + 0.0722 * B'
+     * Rec. 2100 (HDR): Y' = 0.2627 * R' + 0.6780 * G' + 0.0593 * B'
+     * }
+ * + * @implNote Java rounds toward zero when converting a {@code float} to an + * {@code int}. The calculation of luma could be rounded to the nearest + * integer by adding 0.5 before the type conversion, but the extra operation + * seems unnecessary for a display with only 16 levels of gray. + * + * @param source the source integer buffer in ARGB32 format + * @param target the target byte buffer in Y8 format + */ + private void copyNextPixel(IntBuffer source, ByteBuffer target) { + int pixel32 = source.get(); + int r = (pixel32 >> 16) & 0xFF; + int g = (pixel32 >> 8) & 0xFF; + int b = pixel32 & 0xFF; + int y = (int) (0.2126f * r + 0.7152f * g + 0.0722f * b); + target.put((byte) y); + } + + /** + * Copies the next 32-bit ARGB32 pixel to a short buffer with 16-bit RGB565 + * pixels. This method truncates the low-order bits of each color component. + * + * @param source the source integer buffer in ARGB32 format + * @param target the target short buffer in RGB565 format + */ + private void copyNextPixel(IntBuffer source, ShortBuffer target) { + int pixel32 = source.get(); + int r = (pixel32 >> 8) & 0xF800; + int g = (pixel32 >> 5) & 0x07E0; + int b = (pixel32 >> 3) & 0x001F; + int pixel16 = r | g | b; + target.put((short) pixel16); + } + + /** + * Writes the contents of the composition buffer to the output channel, + * converting the pixel format as necessary. + * + * @param out the output channel + * @throws IOException if an error occurs writing to the channel + * @throws IllegalArgumentException if the channel has an unsupported color + * depth + */ + @Override + void write(WritableByteChannel out) throws IOException { + bb.clear(); + switch (byteDepth) { + case Byte.BYTES: { + if (lineByteBuffer == null) { + lineByteBuffer = ByteBuffer.allocate(width * Byte.BYTES); + lineByteBuffer.order(ByteOrder.nativeOrder()); + linePixelBuffer = lineByteBuffer.duplicate(); + } + IntBuffer srcPixels = bb.asIntBuffer(); + ByteBuffer byteBuffer = (ByteBuffer) linePixelBuffer; + for (int y = 0; y < height; y++) { + byteBuffer.clear(); + for (int x = 0; x < width; x++) { + copyNextPixel(srcPixels, byteBuffer); + } + lineByteBuffer.clear(); + out.write(lineByteBuffer); + } + break; + } + case Short.BYTES: { + if (lineByteBuffer == null) { + lineByteBuffer = ByteBuffer.allocate(width * Short.BYTES); + lineByteBuffer.order(ByteOrder.nativeOrder()); + linePixelBuffer = lineByteBuffer.asShortBuffer(); + } + IntBuffer srcPixels = bb.asIntBuffer(); + ShortBuffer shortBuffer = (ShortBuffer) linePixelBuffer; + for (int y = 0; y < height; y++) { + shortBuffer.clear(); + for (int x = 0; x < width; x++) { + copyNextPixel(srcPixels, shortBuffer); + } + lineByteBuffer.clear(); + out.write(lineByteBuffer); + } + break; + } + case Integer.BYTES: { + out.write(bb); + break; + } + default: + String msg = MessageFormat.format("byteDepth={0}", byteDepth); + logger.severe(msg); + throw new IllegalStateException(msg); + } + } + + /** + * Copies the contents of the composition buffer to the output buffer, + * converting the pixel format as necessary. + * + * @param out the output buffer + * @throws IllegalArgumentException if the buffer has an unsupported color + * depth + */ + @Override + void copyToBuffer(ByteBuffer out) { + bb.clear(); + switch (byteDepth) { + case Byte.BYTES: { + if (lineByteBuffer == null) { + lineByteBuffer = ByteBuffer.allocate(width * Byte.BYTES); + lineByteBuffer.order(ByteOrder.nativeOrder()); + linePixelBuffer = lineByteBuffer.duplicate(); + } + IntBuffer srcPixels = bb.asIntBuffer(); + ByteBuffer byteBuffer = (ByteBuffer) linePixelBuffer; + for (int y = 0; y < height; y++) { + byteBuffer.clear(); + for (int x = 0; x < width; x++) { + copyNextPixel(srcPixels, byteBuffer); + } + lineByteBuffer.clear(); + out.put(lineByteBuffer); + } + break; + } + case Short.BYTES: { + if (lineByteBuffer == null) { + lineByteBuffer = ByteBuffer.allocate(width * Short.BYTES); + lineByteBuffer.order(ByteOrder.nativeOrder()); + linePixelBuffer = lineByteBuffer.asShortBuffer(); + } + IntBuffer srcPixels = bb.asIntBuffer(); + ShortBuffer shortBuffer = (ShortBuffer) linePixelBuffer; + for (int y = 0; y < height; y++) { + shortBuffer.clear(); + for (int x = 0; x < width; x++) { + copyNextPixel(srcPixels, shortBuffer); + } + lineByteBuffer.clear(); + out.put(lineByteBuffer); + } + break; + } + case Integer.BYTES: { + out.put(bb); + break; + } + default: + String msg = MessageFormat.format("byteDepth={0}", byteDepth); + logger.severe(msg); + throw new IllegalStateException(msg); + } + } + + @Override + public String toString() { + return MessageFormat.format("{0}[width={1} height={2} depth={3} bb={4}]", + getClass().getName(), width, height, bitDepth, bb); + } +} diff --git a/src/main/java/com/sun/glass/ui/monocle/GetEvent.java b/src/main/java/com/sun/glass/ui/monocle/GetEvent.java index 85f83ee..368344f 100644 --- a/src/main/java/com/sun/glass/ui/monocle/GetEvent.java +++ b/src/main/java/com/sun/glass/ui/monocle/GetEvent.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -101,7 +101,7 @@ class GetEvent { } devices.add(sysPath); } else if (action.equals("remove")) { - devices.remove(devPath); + devices.remove(sysPath); } } catch (IOException | RuntimeException e) { e.printStackTrace(); diff --git a/src/main/java/com/sun/glass/ui/monocle/HeadlessPlatform.java b/src/main/java/com/sun/glass/ui/monocle/HeadlessPlatform.java index 0ff77b9..d1ff87f 100644 --- a/src/main/java/com/sun/glass/ui/monocle/HeadlessPlatform.java +++ b/src/main/java/com/sun/glass/ui/monocle/HeadlessPlatform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,7 +36,7 @@ protected InputDeviceRegistry createInputDeviceRegistry() { @Override protected NativeCursor createCursor() { - return new NullCursor(); + return logSelectedCursor(new NullCursor()); } @Override diff --git a/src/main/java/com/sun/glass/ui/monocle/HeadlessScreen.java b/src/main/java/com/sun/glass/ui/monocle/HeadlessScreen.java index d055bc9..a4d4d74 100644 --- a/src/main/java/com/sun/glass/ui/monocle/HeadlessScreen.java +++ b/src/main/java/com/sun/glass/ui/monocle/HeadlessScreen.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,6 +50,7 @@ protected HeadlessScreen(int defaultWidth, this.width = defaultWidth; this.height = defaultHeight; this.depth = defaultDepth; + @SuppressWarnings("removal") String geometry = AccessController.doPrivileged((PrivilegedAction) () -> System.getProperty("headless.geometry")); if (geometry != null && geometry.indexOf('x') > 0) { try { diff --git a/src/main/java/com/sun/glass/ui/monocle/IntSet.java b/src/main/java/com/sun/glass/ui/monocle/IntSet.java index f108b67..794f1c4 100644 --- a/src/main/java/com/sun/glass/ui/monocle/IntSet.java +++ b/src/main/java/com/sun/glass/ui/monocle/IntSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -144,6 +144,7 @@ public boolean equals(IntSet set) { } } + @Override public boolean equals(Object o) { if (o instanceof IntSet) { return equals((IntSet) o); @@ -152,6 +153,16 @@ public boolean equals(Object o) { } } + @Override + public int hashCode() { + int h = 1; + for (int i = 0; i < size; i++) { + h = 31 * h + elements[i]; + } + return h; + } + + @Override public String toString() { StringBuffer sb = new StringBuffer("IntSet["); for (int i = 0; i < size; i++) { diff --git a/src/main/java/com/sun/glass/ui/monocle/KeyInput.java b/src/main/java/com/sun/glass/ui/monocle/KeyInput.java index e66422b..3742ef9 100644 --- a/src/main/java/com/sun/glass/ui/monocle/KeyInput.java +++ b/src/main/java/com/sun/glass/ui/monocle/KeyInput.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -85,7 +85,8 @@ void setState(KeyState newState) { } else if (key == KeyEvent.VK_NUM_LOCK) { numLock = !numLock; } else if (key == KeyEvent.VK_C && newState.isControlPressed()) { - AccessController.doPrivileged((PrivilegedAction) () -> { + @SuppressWarnings("removal") + var dummy = AccessController.doPrivileged((PrivilegedAction) () -> { if ("1".equals(System.getenv("JAVAFX_DEBUG"))) { System.exit(0); } diff --git a/src/main/java/com/sun/glass/ui/monocle/KeyState.java b/src/main/java/com/sun/glass/ui/monocle/KeyState.java index e976555..3745f83 100644 --- a/src/main/java/com/sun/glass/ui/monocle/KeyState.java +++ b/src/main/java/com/sun/glass/ui/monocle/KeyState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -77,8 +77,7 @@ IntSet getKeysPressed() { /** Returns the Glass window on which this event state is located . */ MonocleWindow getWindow(boolean recalculateCache) { if (window == null || recalculateCache) { - window = (MonocleWindow) - MonocleWindowManager.getInstance().getFocusedWindow(); + window = MonocleWindowManager.getInstance().getFocusedWindow(); } return window; } @@ -111,6 +110,7 @@ boolean isControlPressed() { return (modifiers & KeyEvent.MODIFIER_CONTROL) != 0; } + @Override public String toString() { return "KeyState[modifiers=" + modifiers + ",keys=" + keysPressed + "]"; } diff --git a/src/main/java/com/sun/glass/ui/monocle/LinuxAbsoluteInputCapabilities.java b/src/main/java/com/sun/glass/ui/monocle/LinuxAbsoluteInputCapabilities.java index ee0449b..27e1e5a 100644 --- a/src/main/java/com/sun/glass/ui/monocle/LinuxAbsoluteInputCapabilities.java +++ b/src/main/java/com/sun/glass/ui/monocle/LinuxAbsoluteInputCapabilities.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,7 +54,7 @@ class LinuxAbsoluteInputCapabilities { private LinuxAbsoluteInputCapabilities(LinuxSystem system, LinuxSystem.InputAbsInfo info, - long fd, int axis) throws IOException { + long fd, int axis) { system.ioctl(fd, system.EVIOCGABS(axis), info.p); value = LinuxSystem.InputAbsInfo.getValue(info.p); minimum = LinuxSystem.InputAbsInfo.getMinimum(info.p); @@ -81,8 +81,7 @@ static Map getCapabilities( if (fd == -1) { throw new IOException(system.getErrorMessage()); } - Map caps = - new HashMap(); + Map caps = new HashMap<>(); for (int i = 0; (i = axes.nextSetBit(i)) != -1; i++) { caps.put(i, new LinuxAbsoluteInputCapabilities(system, info, fd, i)); } diff --git a/src/main/java/com/sun/glass/ui/monocle/LinuxArch.java b/src/main/java/com/sun/glass/ui/monocle/LinuxArch.java index 13c70d1..820e0c1 100644 --- a/src/main/java/com/sun/glass/ui/monocle/LinuxArch.java +++ b/src/main/java/com/sun/glass/ui/monocle/LinuxArch.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,14 +30,11 @@ public class LinuxArch { - private static final int bits; - - static { - bits = AccessController.doPrivileged((PrivilegedAction) () -> { - LinuxSystem system = LinuxSystem.getLinuxSystem(); - return (int) system.sysconf(LinuxSystem._SC_LONG_BIT); - }); - } + @SuppressWarnings("removal") + private static final int bits = AccessController.doPrivileged((PrivilegedAction) () -> { + LinuxSystem system = LinuxSystem.getLinuxSystem(); + return (int) system.sysconf(LinuxSystem._SC_LONG_BIT); + }); static boolean is64Bit() { return bits == 64; diff --git a/src/main/java/com/sun/glass/ui/monocle/LinuxEventBuffer.java b/src/main/java/com/sun/glass/ui/monocle/LinuxEventBuffer.java index 6a2471a..a1bf9f6 100644 --- a/src/main/java/com/sun/glass/ui/monocle/LinuxEventBuffer.java +++ b/src/main/java/com/sun/glass/ui/monocle/LinuxEventBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,16 +41,24 @@ interface EventStruct { } class EventStruct32Bit implements EventStruct { + @Override public int getTypeIndex() { return 8; } + @Override public int getCodeIndex() { return 10; } + @Override public int getValueIndex() { return 12; } + @Override public int getSize() { return 16; } } class EventStruct64Bit implements EventStruct { + @Override public int getTypeIndex() { return 16; } + @Override public int getCodeIndex() { return 18; } + @Override public int getValueIndex() { return 20; } + @Override public int getSize() { return 24; } } @@ -90,7 +98,7 @@ int getEventSize() { */ synchronized boolean put(ByteBuffer event) throws InterruptedException { - boolean isSync = event.getInt(eventStruct.getTypeIndex()) == 0 + boolean isSync = event.getShort(eventStruct.getTypeIndex()) == 0 && event.getInt(eventStruct.getValueIndex()) == 0; while (bb.limit() - bb.position() < event.limit()) { // Block if bb is full. This should be the diff --git a/src/main/java/com/sun/glass/ui/monocle/LinuxInputDevice.java b/src/main/java/com/sun/glass/ui/monocle/LinuxInputDevice.java index ce89628..ebbedf4 100644 --- a/src/main/java/com/sun/glass/ui/monocle/LinuxInputDevice.java +++ b/src/main/java/com/sun/glass/ui/monocle/LinuxInputDevice.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -83,12 +83,12 @@ class LinuxInputDevice implements Runnable, InputDevice { this.sysPath = sysPath; this.udevManifest = udevManifest; this.capabilities = SysFS.readCapabilities(sysPath); - this.absCaps = LinuxAbsoluteInputCapabilities.getCapabilities( - devNode, capabilities.get("abs")); fd = system.open(devNode.getPath(), LinuxSystem.O_RDONLY); if (fd == -1) { throw new IOException(system.getErrorMessage() + " on " + devNode); } + this.absCaps = LinuxAbsoluteInputCapabilities.getCapabilities( + devNode, capabilities.get("abs")); // attempt to grab the device. If the grab fails, keep going. int EVIOCGRAB = system.IOW('E', 0x90, 4); system.ioctl(fd, EVIOCGRAB, 1); @@ -173,6 +173,7 @@ public void run() { class EventProcessor implements Runnable { boolean scheduled; + @Override public void run() { buffer.startIteration(); // Do not lock the buffer while processing events. We still want to be @@ -211,6 +212,7 @@ boolean isQuiet() { /** * @return a string describing this input device */ + @Override public String toString() { return devNode == null ? "Robot" : devNode.toString(); } diff --git a/src/main/java/com/sun/glass/ui/monocle/LinuxInputDeviceRegistry.java b/src/main/java/com/sun/glass/ui/monocle/LinuxInputDeviceRegistry.java index 7810e02..bad3ec3 100644 --- a/src/main/java/com/sun/glass/ui/monocle/LinuxInputDeviceRegistry.java +++ b/src/main/java/com/sun/glass/ui/monocle/LinuxInputDeviceRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -84,6 +84,7 @@ private LinuxInputDevice createDevice(File devNode, } LinuxInputDevice addDevice(LinuxInputDevice device, String name) { + @SuppressWarnings("removal") SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission(new AllPermission()); @@ -107,6 +108,7 @@ private LinuxInputDevice addDeviceInternal(LinuxInputDevice device, String name) } void removeDevice(LinuxInputDevice device) { + @SuppressWarnings("removal") SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission(new AllPermission()); diff --git a/src/main/java/com/sun/glass/ui/monocle/LinuxPlatform.java b/src/main/java/com/sun/glass/ui/monocle/LinuxPlatform.java index cc3e065..0c5ad17 100644 --- a/src/main/java/com/sun/glass/ui/monocle/LinuxPlatform.java +++ b/src/main/java/com/sun/glass/ui/monocle/LinuxPlatform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,7 +39,8 @@ protected InputDeviceRegistry createInputDeviceRegistry() { @Override protected NativeCursor createCursor() { - return new SoftwareCursor(); + final NativeCursor c = useCursor ? new SoftwareCursor() : new NullCursor(); + return logSelectedCursor(c); } @Override diff --git a/src/main/java/com/sun/glass/ui/monocle/LinuxPlatformFactory.java b/src/main/java/com/sun/glass/ui/monocle/LinuxPlatformFactory.java index 54456fa..ec47c5f 100644 --- a/src/main/java/com/sun/glass/ui/monocle/LinuxPlatformFactory.java +++ b/src/main/java/com/sun/glass/ui/monocle/LinuxPlatformFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,16 +25,13 @@ package com.sun.glass.ui.monocle; -import java.security.AccessController; -import java.security.PrivilegedAction; +import com.sun.javafx.PlatformUtil; class LinuxPlatformFactory extends NativePlatformFactory { @Override protected boolean matches() { - String os = AccessController.doPrivileged( - (PrivilegedAction) () -> System.getProperty("os.name")); - return os != null && os.equals("Linux"); + return PlatformUtil.isLinux(); } @Override diff --git a/src/main/java/com/sun/glass/ui/monocle/LinuxStatefulMultiTouchProcessor.java b/src/main/java/com/sun/glass/ui/monocle/LinuxStatefulMultiTouchProcessor.java index 5ce5eb5..a835e37 100644 --- a/src/main/java/com/sun/glass/ui/monocle/LinuxStatefulMultiTouchProcessor.java +++ b/src/main/java/com/sun/glass/ui/monocle/LinuxStatefulMultiTouchProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,8 +38,7 @@ class LinuxStatefulMultiTouchProcessor extends LinuxTouchProcessor { private int currentID = ID_UNASSIGNED; private int currentSlot = 0; - private final Map slotToIDMap = - new HashMap(); + private final Map slotToIDMap = new HashMap<>(); LinuxStatefulMultiTouchProcessor(LinuxInputDevice device) { super(device); diff --git a/src/main/java/com/sun/glass/ui/monocle/LinuxSystem.java b/src/main/java/com/sun/glass/ui/monocle/LinuxSystem.java index fc72b51..b28805c 100644 --- a/src/main/java/com/sun/glass/ui/monocle/LinuxSystem.java +++ b/src/main/java/com/sun/glass/ui/monocle/LinuxSystem.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -57,6 +57,7 @@ static LinuxSystem getLinuxSystem() { } private static void checkPermissions() { + @SuppressWarnings("removal") SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission(permission); diff --git a/src/main/java/com/sun/glass/ui/monocle/LinuxTouchProcessor.java b/src/main/java/com/sun/glass/ui/monocle/LinuxTouchProcessor.java index 06531ab..eb6f021 100644 --- a/src/main/java/com/sun/glass/ui/monocle/LinuxTouchProcessor.java +++ b/src/main/java/com/sun/glass/ui/monocle/LinuxTouchProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ abstract class LinuxTouchProcessor implements LinuxInputProcessor { final TouchPipeline pipeline; final LinuxTouchTransform transform; + @SuppressWarnings("removal") LinuxTouchProcessor(LinuxInputDevice device) { transform = new LinuxTouchTransform(device); PrivilegedAction getFilterProperty = diff --git a/src/main/java/com/sun/glass/ui/monocle/LinuxTouchTransform.java b/src/main/java/com/sun/glass/ui/monocle/LinuxTouchTransform.java index 1a2633d..66bfbf4 100644 --- a/src/main/java/com/sun/glass/ui/monocle/LinuxTouchTransform.java +++ b/src/main/java/com/sun/glass/ui/monocle/LinuxTouchTransform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -52,6 +52,7 @@ class LinuxTouchTransform { private int[] mins = new int[2]; private int[] maxs = new int[2]; + @SuppressWarnings("removal") LinuxTouchTransform(LinuxInputDevice device) { this.device = device; Arrays.fill(axes, -1); @@ -138,7 +139,8 @@ private void initTransform(int axis, int index) { } LinuxAbsoluteInputCapabilities caps = device.getAbsoluteInputCapabilities(axis); String product = device.getProduct(); - AccessController.doPrivileged((PrivilegedAction) () -> { + @SuppressWarnings("removal") + var dummy = AccessController.doPrivileged((PrivilegedAction) () -> { int minimum = Integer.getInteger( "monocle.input." + product + ".min" + axisName, caps.getMinimum()); @@ -146,7 +148,7 @@ private void initTransform(int axis, int index) { "monocle.input." + product + ".max" + axisName, caps.getMaximum()); translates[index] = -minimum; - scalars[index] = ((double) (range)) / (maximum - minimum); + scalars[index] = range / (maximum - minimum); return null; }); } diff --git a/src/main/java/com/sun/glass/ui/monocle/MX6Platform.java b/src/main/java/com/sun/glass/ui/monocle/MX6Platform.java index 82ea536..1d6da9f 100644 --- a/src/main/java/com/sun/glass/ui/monocle/MX6Platform.java +++ b/src/main/java/com/sun/glass/ui/monocle/MX6Platform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,8 @@ class MX6Platform extends LinuxPlatform { @Override protected NativeCursor createCursor() { - return new MX6Cursor(); + final NativeCursor c = useCursor ? new MX6Cursor() : new NullCursor(); + return logSelectedCursor(c); } @Override diff --git a/src/main/java/com/sun/glass/ui/monocle/MonocleApplication.java b/src/main/java/com/sun/glass/ui/monocle/MonocleApplication.java index b45422f..9f7a33d 100644 --- a/src/main/java/com/sun/glass/ui/monocle/MonocleApplication.java +++ b/src/main/java/com/sun/glass/ui/monocle/MonocleApplication.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,6 +42,7 @@ import java.nio.IntBuffer; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.List; public final class MonocleApplication extends Application { @@ -110,6 +111,7 @@ private void updateDeviceFlags(InputDevice device, boolean added) { @Override protected void runLoop(Runnable launchable) { runnableProcessor.invokeLater(launchable); + @SuppressWarnings("removal") long stackSize = AccessController.doPrivileged( (PrivilegedAction) () -> Long.getLong("monocle.stackSize", 0)); @@ -153,11 +155,6 @@ public Window createWindow(Window owner, Screen screen, int styleMask) { return new MonocleWindow(owner, screen, styleMask); } - @Override - public Window createWindow(long parent) { - return new MonocleWindow(parent); - } - @Override public View createView() { return new MonocleView(); @@ -190,15 +187,18 @@ public Pixels createPixels(int width, int height, ByteBuffer data) { return new MonoclePixels(width, height, data); } + @Override + public Pixels createPixels(int width, int height, ByteBuffer data, float scalex, float scaley) { + return new MonoclePixels(width, height, data, scalex, scaley); + } + @Override public Pixels createPixels(int width, int height, IntBuffer data) { return new MonoclePixels(width, height, data); } @Override - public Pixels createPixels(int width, int height, IntBuffer data, - float scalex, float scaley) - { + public Pixels createPixels(int width, int height, IntBuffer data, float scalex, float scaley) { return new MonoclePixels(width, height, data, scalex, scaley); } @@ -219,21 +219,29 @@ protected double staticScreen_getVideoRefreshPeriod() { @Override protected Screen[] staticScreen_getScreens() { - NativeScreen ns = platform.getScreen(); - Screen screen = new Screen(1l, // dummy native pointer; - ns.getDepth(), - 0, 0, ns.getWidth(), ns.getHeight(), - 0, 0, ns.getWidth(), ns.getHeight(), - 0, 0, ns.getWidth(), ns.getHeight(), - ns.getDPI(), ns.getDPI(), - ns.getScale(), ns.getScale(), - ns.getScale(), ns.getScale()); - // Move the cursor to the middle of the screen - MouseState mouseState = new MouseState(); - mouseState.setX(ns.getWidth() / 2); - mouseState.setY(ns.getHeight() / 2); - MouseInput.getInstance().setState(mouseState, false); - return new Screen[] { screen }; + List screens = platform.getScreens(); + Screen[] answer = new Screen[screens.size()]; + int cnt = 0; + for (NativeScreen ns: screens) { + Screen screen = new Screen(ns.getNativeHandle(), + ns.getDepth(), + ns.getOffsetX(), ns.getOffsetY(), ns.getWidth(), ns.getHeight(), + ns.getOffsetX(), ns.getOffsetY(), ns.getWidth(), ns.getHeight(), + ns.getOffsetX(), ns.getOffsetY(), ns.getWidth(), ns.getHeight(), + ns.getDPI(), ns.getDPI(), + 1.f, 1.f, ns.getScale(), ns.getScale()); + answer[cnt] = screen; + // The first screen is the primaryscreen, we set the cursor to that one. + if (cnt == 0) { + // Move the cursor to the middle of the screen + MouseState mouseState = new MouseState(); + mouseState.setX(ns.getWidth() / 2); + mouseState.setY(ns.getHeight() / 2); + MouseInput.getInstance().setState(mouseState, false); + } + cnt++; + } + return answer; } @Override @@ -251,6 +259,7 @@ protected int staticTimer_getMaxPeriod() { return MonocleTimer.getMaxPeriod_impl(); } + @Override public boolean hasWindowManager() { return false; } diff --git a/src/main/java/com/sun/glass/ui/monocle/MonoclePixels.java b/src/main/java/com/sun/glass/ui/monocle/MonoclePixels.java index bbb17ce..57448d7 100644 --- a/src/main/java/com/sun/glass/ui/monocle/MonoclePixels.java +++ b/src/main/java/com/sun/glass/ui/monocle/MonoclePixels.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,10 @@ final class MonoclePixels extends Pixels { super(width, height, data); } + MonoclePixels(int width, int height, ByteBuffer data, float scalex, float scaley) { + super(width, height, data, scalex, scaley); + } + MonoclePixels(int width, int height, IntBuffer data) { super(width, height, data); } diff --git a/src/main/java/com/sun/glass/ui/monocle/MonocleSettings.java b/src/main/java/com/sun/glass/ui/monocle/MonocleSettings.java index bced18f..6567638 100644 --- a/src/main/java/com/sun/glass/ui/monocle/MonocleSettings.java +++ b/src/main/java/com/sun/glass/ui/monocle/MonocleSettings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ class MonocleSettings { + @SuppressWarnings("removal") static final MonocleSettings settings = AccessController.doPrivileged( (PrivilegedAction) () -> new MonocleSettings()); diff --git a/src/main/java/com/sun/glass/ui/monocle/MonocleSystemClipboard.java b/src/main/java/com/sun/glass/ui/monocle/MonocleSystemClipboard.java index d0e5839..d8a50ab 100644 --- a/src/main/java/com/sun/glass/ui/monocle/MonocleSystemClipboard.java +++ b/src/main/java/com/sun/glass/ui/monocle/MonocleSystemClipboard.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,24 +39,30 @@ final class MonocleSystemClipboard extends SystemClipboard { super(Clipboard.SYSTEM); } + @Override protected boolean isOwner() { return true; } + @Override protected void pushToSystem(HashMap cacheData, int supportedActions) { } + @Override protected void pushTargetActionToSystem(int actionDone) { } + @Override protected Object popFromSystem(String mimeType) { return null; } + @Override protected int supportedSourceActionsFromSystem() { return Clipboard.ACTION_NONE; } + @Override protected String[] mimesFromSystem() { return new String[0]; } diff --git a/src/main/java/com/sun/glass/ui/monocle/MonocleTimer.java b/src/main/java/com/sun/glass/ui/monocle/MonocleTimer.java index 344ba8b..f55cc41 100644 --- a/src/main/java/com/sun/glass/ui/monocle/MonocleTimer.java +++ b/src/main/java/com/sun/glass/ui/monocle/MonocleTimer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,13 +26,18 @@ package com.sun.glass.ui.monocle; import com.sun.glass.ui.Timer; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; /** * Monocle implementation class for Timer. */ final class MonocleTimer extends Timer { - private static java.util.Timer timer; - private java.util.TimerTask task; + private static final String THREAD_NAME = "Monocle Timer"; + + private static ScheduledThreadPoolExecutor scheduler; + private ScheduledFuture task; MonocleTimer(final Runnable runnable) { super(runnable); @@ -47,19 +52,15 @@ static int getMaxPeriod_impl() { } @Override protected long _start(final Runnable runnable, int period) { - if (timer == null) { - timer = new java.util.Timer(true); + if (scheduler == null) { + scheduler = new ScheduledThreadPoolExecutor(1, target -> { + Thread thread = new Thread(target, THREAD_NAME); + thread.setDaemon(true); + return thread; + }); } - task = new java.util.TimerTask() { - - @Override - public void run() { - runnable.run(); - } - }; - - timer.schedule(task, 0, (long)period); + task = scheduler.scheduleAtFixedRate(runnable, 0, period, TimeUnit.MILLISECONDS); return 1; // need something non-zero to denote success. } @@ -69,7 +70,7 @@ public void run() { @Override protected void _stop(long timer) { if (task != null) { - task.cancel(); + task.cancel(false); task = null; } } diff --git a/src/main/java/com/sun/glass/ui/monocle/MonocleView.java b/src/main/java/com/sun/glass/ui/monocle/MonocleView.java index 9281db7..84df91f 100644 --- a/src/main/java/com/sun/glass/ui/monocle/MonocleView.java +++ b/src/main/java/com/sun/glass/ui/monocle/MonocleView.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -201,7 +201,9 @@ protected boolean _enterFullscreen(long ptr, boolean animate, boolean hideCursor) { MonocleWindowManager wm = MonocleWindowManager.getInstance(); MonocleWindow focusedWindow = wm.getFocusedWindow(); - focusedWindow.setFullScreen(true); + if (focusedWindow != null) { + focusedWindow.setFullScreen(true); + } if (hideCursor) { resetCursorVisibility = true; NativeCursor nativeCursor = @@ -216,7 +218,9 @@ protected boolean _enterFullscreen(long ptr, boolean animate, protected void _exitFullscreen(long ptr, boolean animate) { MonocleWindowManager wm = MonocleWindowManager.getInstance(); MonocleWindow focusedWindow = wm.getFocusedWindow(); - focusedWindow.setFullScreen(false); + if (focusedWindow != null) { + focusedWindow.setFullScreen(false); + } if (resetCursorVisibility) { resetCursorVisibility = false; NativeCursor nativeCursor = diff --git a/src/main/java/com/sun/glass/ui/monocle/MonocleWindow.java b/src/main/java/com/sun/glass/ui/monocle/MonocleWindow.java index f064f39..fba8a19 100644 --- a/src/main/java/com/sun/glass/ui/monocle/MonocleWindow.java +++ b/src/main/java/com/sun/glass/ui/monocle/MonocleWindow.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,10 +55,6 @@ final class MonocleWindow extends Window { super(owner, screen, styleMask); } - MonocleWindow(long parent) { - super(parent); - } - @Override protected void _toFront(long ptr) { MonocleWindowManager.getInstance().toFront(this); @@ -167,11 +163,6 @@ protected long _createWindow(long NativeWindow, long NativeScreen, return id; } - @Override - protected long _createChildWindow(long parent) { - throw new UnsupportedOperationException(); - } - @Override protected boolean _close(long nativeWindowPointer) { return MonocleWindowManager.getInstance().closeWindow(this); @@ -188,6 +179,10 @@ protected boolean _setView(long nativeWindowPointer, View view) { return result; } + // empty - not needed by this implementation + @Override + protected void _updateViewSize(long ptr) {} + /** * Returns the handle used to create a rendering context in Prism */ @@ -273,6 +268,11 @@ protected boolean _maximize(long nativeWindowPointer, boolean maximize, return true; } + @Override + protected void notifyMoveToAnotherScreen(Screen screen) { + super.notifyMoveToAnotherScreen(screen); + } + void setFullScreen(boolean fullscreen) { NativeScreen screen = NativePlatformFactory.getNativePlatform().getScreen(); int x = getX(); @@ -445,13 +445,6 @@ void _notifyFocusDisabled() { ((MonocleCursor) cursor).applyCursor(); } - @Override protected int _getEmbeddedX(long ptr) { - return 0; - } - @Override protected int _getEmbeddedY(long ptr) { - return 0; - } - @Override protected void _requestInput(long ptr, String text, int type, double width, double height, double Mxx, double Mxy, double Mxz, double Mxt, diff --git a/src/main/java/com/sun/glass/ui/monocle/MonocleWindowManager.java b/src/main/java/com/sun/glass/ui/monocle/MonocleWindowManager.java index 256c824..ff63867 100644 --- a/src/main/java/com/sun/glass/ui/monocle/MonocleWindowManager.java +++ b/src/main/java/com/sun/glass/ui/monocle/MonocleWindowManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -97,7 +97,7 @@ boolean closeWindow(MonocleWindow window) { windows.length - index - 1); windows = Arrays.copyOf(windows, windows.length - 1); } - List windowsToNotify = new ArrayList(); + List windowsToNotify = new ArrayList<>(); for (MonocleWindow otherWindow : windows) { if (otherWindow.getOwner() == window) { windowsToNotify.add(otherWindow); @@ -107,6 +107,9 @@ boolean closeWindow(MonocleWindow window) { windowsToNotify.get(i).notifyClose(); } window.notifyDestroy(); + if (focusedWindow == window) { + focusedWindow = null; + } return true; } @@ -170,16 +173,18 @@ void repaintAll() { } } - static void repaintFromNative () { - Platform.runLater(new Runnable () { - - @Override - public void run() { - Screen.notifySettingsChanged(); - instance.getFocusedWindow().setFullScreen(true); - instance.repaintAll(); - Toolkit.getToolkit().requestNextPulse(); + static void repaintFromNative(Screen screen) { + Platform.runLater(() -> { + Screen.notifySettingsChanged(); + MonocleWindow focusedWindow = instance.getFocusedWindow(); + if (focusedWindow != null) { + if (screen != null && screen.getNativeScreen() != focusedWindow.getScreen().getNativeScreen()) { + focusedWindow.notifyMoveToAnotherScreen(screen); + } + focusedWindow.setFullScreen(true); } + instance.repaintAll(); + Toolkit.getToolkit().requestNextPulse(); }); } diff --git a/src/main/java/com/sun/glass/ui/monocle/MouseInput.java b/src/main/java/com/sun/glass/ui/monocle/MouseInput.java index 6a173e8..bd3e48b 100644 --- a/src/main/java/com/sun/glass/ui/monocle/MouseInput.java +++ b/src/main/java/com/sun/glass/ui/monocle/MouseInput.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -89,9 +89,9 @@ void setState(MouseState newState, boolean synthesized) { newState.setY(y); // Get the cached window for the old state and compute the window for // the new state - MonocleWindow oldWindow = state.getWindow(false); + MonocleWindow oldWindow = state.getWindow(false, null); boolean recalculateWindow = state.getButtonsPressed().isEmpty(); - MonocleWindow window = newState.getWindow(recalculateWindow); + MonocleWindow window = newState.getWindow(recalculateWindow, null); MonocleView view = (window == null) ? null : (MonocleView) window.getView(); // send exit event if (oldWindow != window && oldWindow != null) { @@ -105,7 +105,9 @@ void setState(MouseState newState, boolean synthesized) { MonocleView oldView = (MonocleView) oldWindow.getView(); if (oldView != null) { // send exit event - int modifiers = state.getModifiers(); // TODO: include key modifiers + KeyState keyState = new KeyState(); + KeyInput.getInstance().getState(keyState); + int modifiers = state.getModifiers() | keyState.getModifiers(); int button = state.getButton(); boolean isPopupTrigger = false; // TODO int oldX = state.getX(); @@ -145,7 +147,9 @@ void setState(MouseState newState, boolean synthesized) { int relY = y - window.getY(); // send enter event if (oldWindow != window && view != null) { - int modifiers = state.getModifiers(); // TODO: include key modifiers + KeyState keyState = new KeyState(); + KeyInput.getInstance().getState(keyState); + int modifiers = state.getModifiers() | keyState.getModifiers(); int button = state.getButton(); boolean isPopupTrigger = false; // TODO postMouseEvent(view, MouseEvent.ENTER, button, @@ -156,7 +160,9 @@ void setState(MouseState newState, boolean synthesized) { if (oldWindow != window | newAbsoluteLocation) { boolean isDrag = !state.getButtonsPressed().isEmpty(); int eventType = isDrag ? MouseEvent.DRAG : MouseEvent.MOVE; - int modifiers = state.getModifiers(); // TODO: include key modifiers + KeyState keyState = new KeyState(); + KeyInput.getInstance().getState(keyState); + int modifiers = state.getModifiers() | keyState.getModifiers(); int button = state.getButton(); boolean isPopupTrigger = false; // TODO postMouseEvent(view, eventType, button, @@ -172,11 +178,14 @@ void setState(MouseState newState, boolean synthesized) { for (int i = 0; i < buttons.size(); i++) { int button = buttons.get(i); pressState.pressButton(button); + KeyState keyState = new KeyState(); + KeyInput.getInstance().getState(keyState); + int modifiers = pressState.getModifiers() | keyState.getModifiers(); // send press event boolean isPopupTrigger = false; // TODO postMouseEvent(view, MouseEvent.DOWN, button, relX, relY, x, y, - pressState.getModifiers(), isPopupTrigger, + modifiers, isPopupTrigger, synthesized); } } @@ -190,11 +199,14 @@ void setState(MouseState newState, boolean synthesized) { for (int i = 0; i < buttons.size(); i++) { int button = buttons.get(i); releaseState.releaseButton(button); + KeyState keyState = new KeyState(); + KeyInput.getInstance().getState(keyState); + int modifiers = releaseState.getModifiers() | keyState.getModifiers(); // send release event boolean isPopupTrigger = false; // TODO postMouseEvent(view, MouseEvent.UP, button, relX, relY, x, y, - releaseState.getModifiers(), isPopupTrigger, + modifiers, isPopupTrigger, synthesized); } } @@ -208,7 +220,9 @@ void setState(MouseState newState, boolean synthesized) { default: dY = 0.0; break; } if (dY != 0.0) { - int modifiers = newState.getModifiers(); + KeyState keyState = new KeyState(); + KeyInput.getInstance().getState(keyState); + int modifiers = newState.getModifiers() | keyState.getModifiers(); RunnableProcessor.runLater(() -> { view.notifyScroll(relX, relY, x, y, 0.0, dY, modifiers, 1, 0, 0, 0, 1.0, 1.0); @@ -321,4 +335,4 @@ void notifyDragStart() { dragInProgress = true; } -} \ No newline at end of file +} diff --git a/src/main/java/com/sun/glass/ui/monocle/MouseState.java b/src/main/java/com/sun/glass/ui/monocle/MouseState.java index 9ff3591..3af7d36 100644 --- a/src/main/java/com/sun/glass/ui/monocle/MouseState.java +++ b/src/main/java/com/sun/glass/ui/monocle/MouseState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -78,18 +78,24 @@ void releaseButton(int button) { buttonsPressed.removeInt(button); } - /** Returns the Glass window on which the coordinates of this state are located. + /** + * Returns the Glass window on which the coordinates of this state are located. * @param recalculateCache true if the cached value for the target window * should be recalculated; false if the cached * value should be used to determine the result * of this method. + * @param fallback if the original window is null, or if no window can + * be found, return the fallback window * @return the MonocleWindow at the top of the stack at the coordinates - * described by this state object. + * described by this state object, or the fallback window in case the + * current window is null or no window can be found for the supplied coordinates. */ - MonocleWindow getWindow(boolean recalculateCache) { - if (window == null || recalculateCache) { - window = (MonocleWindow) - MonocleWindowManager.getInstance().getWindowForLocation(x, y); + MonocleWindow getWindow(boolean recalculateCache, MonocleWindow fallback) { + if (recalculateCache) { + window = MonocleWindowManager.getInstance().getWindowForLocation(x, y); + } + if (window == null) { + window = fallback; } return window; } @@ -142,6 +148,7 @@ IntSet getButtonsPressed() { return buttonsPressed; } + @Override public String toString() { return "MouseState[x=" + x + ",y=" + y diff --git a/src/main/java/com/sun/glass/ui/monocle/NativePlatform.java b/src/main/java/com/sun/glass/ui/monocle/NativePlatform.java index 16a7cc8..122bd21 100644 --- a/src/main/java/com/sun/glass/ui/monocle/NativePlatform.java +++ b/src/main/java/com/sun/glass/ui/monocle/NativePlatform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,15 +25,37 @@ package com.sun.glass.ui.monocle; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import java.util.ArrayList; +import java.util.List; +import com.sun.javafx.logging.PlatformLogger; +import com.sun.javafx.logging.PlatformLogger.Level; +import com.sun.javafx.util.Logging; + /** Abstract of a platform on which JavaFX can run. */ public abstract class NativePlatform { private static InputDeviceRegistry inputDeviceRegistry; private final RunnableProcessor runnableProcessor; + private final PlatformLogger logger = Logging.getJavaFXLogger(); + private NativeCursor cursor; - private NativeScreen screen; + protected List screens; protected AcceleratedScreen accScreen; + + @SuppressWarnings("removal") + protected static final boolean useCursor = + AccessController.doPrivileged((PrivilegedAction) () -> { + final String str = + System.getProperty("monocle.cursor.enabled", "true"); + return "true".equalsIgnoreCase(str); + }); + + + protected NativePlatform() { runnableProcessor = new RunnableProcessor(); } @@ -46,8 +68,10 @@ void shutdown() { if (cursor != null) { cursor.shutdown(); } - if (screen != null) { - screen.shutdown(); + if (screens != null) { + for (NativeScreen screen: screens) { + screen.shutdown(); + } } } @@ -102,15 +126,49 @@ synchronized NativeCursor getCursor() { protected abstract NativeScreen createScreen(); /** - * Obtains the singleton NativeScreen + * Create NativeScreen instances for all + * available native screens. The default implementation of + * this method will invoke the createScreen + * method and return a list with a single screen only. + * Subclasses can override this and return a list + * with all screens. The approach to get this list is + * specific to the subclass and can be done in Java or + * in native code.

+ * The first element in the returned list is considered + * to be the primary screen. + * + * @return a list with all native screens. This list is + * never null, but it can be empty. + */ + protected synchronized List createScreens() { + if (screens == null) { + screens = new ArrayList<>(1); + screens.add(createScreen()); + } + return screens; + } + + /** + * Obtains the NativeScreen instances. This method will + * create NativeScreen instances for all native screens, + * and stores them internally. The main screen (primaryscreen) + * is returned. * - * @return the NativeScreen + * @return the primaryscreen, or null if no + * screen is found. */ synchronized NativeScreen getScreen() { - if (screen == null) { - screen = createScreen(); + if (screens == null) { + screens = createScreens(); + } + return (screens.size() == 0 ? null : screens.get(0)); + } + + synchronized List getScreens() { + if (screens == null) { + screens = createScreens(); } - return screen; + return screens; } /** @@ -129,4 +187,19 @@ public synchronized AcceleratedScreen getAcceleratedScreen(int[] attributes) return accScreen; } + + /** + * Log the name of the supplied native cursor class if required. + * + * @param cursor the native cursor in use, null is permitted + * @return the passed in cursor + */ + protected NativeCursor logSelectedCursor(final NativeCursor cursor) { + if (logger.isLoggable(Level.FINE)) { + final String name = cursor == null ? null : cursor.getClass().getSimpleName(); + logger.fine("Using native cursor: {0}", name); + } + return cursor; + } + } diff --git a/src/main/java/com/sun/glass/ui/monocle/NativePlatformFactory.java b/src/main/java/com/sun/glass/ui/monocle/NativePlatformFactory.java index be4d905..a2ac6d8 100644 --- a/src/main/java/com/sun/glass/ui/monocle/NativePlatformFactory.java +++ b/src/main/java/com/sun/glass/ui/monocle/NativePlatformFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -81,6 +81,7 @@ public abstract class NativePlatformFactory { */ public static synchronized NativePlatform getNativePlatform() { if (platform == null) { + @SuppressWarnings("removal") String platformFactoryProperty = AccessController.doPrivileged((PrivilegedAction) () -> System.getProperty("monocle.platform", "MX6,OMAP,Dispman,Android,X11,Linux,Headless")); @@ -105,7 +106,7 @@ public static synchronized NativePlatform getNativePlatform() { throw new IllegalArgumentException("Unrecognized Monocle platform: " + factoryClassName); } - NativePlatformFactory npf = (NativePlatformFactory) clazz.newInstance(); + NativePlatformFactory npf = (NativePlatformFactory) clazz.getDeclaredConstructor().newInstance(); if (npf.matches() && npf.getMajorVersion() == majorVersion && npf.getMinorVersion() == minorVersion) { diff --git a/src/main/java/com/sun/glass/ui/monocle/NativeScreen.java b/src/main/java/com/sun/glass/ui/monocle/NativeScreen.java index 0ca6853..d5f5305 100644 --- a/src/main/java/com/sun/glass/ui/monocle/NativeScreen.java +++ b/src/main/java/com/sun/glass/ui/monocle/NativeScreen.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -52,6 +52,22 @@ public interface NativeScreen { */ int getHeight(); + /** + * Returns the horizontal start position of this screen relative to the total + * combined screen size. + */ + default int getOffsetX() { + return 0; + } + + /** + * Returns the vertical start position of this screen relative to the total + * combined screen size. + */ + default int getOffsetY() { + return 0; + } + /** * Returns the number of pixels per inch in the screen. */ diff --git a/src/main/java/com/sun/glass/ui/monocle/OMAPPlatform.java b/src/main/java/com/sun/glass/ui/monocle/OMAPPlatform.java index f99fc3e..6f464f4 100644 --- a/src/main/java/com/sun/glass/ui/monocle/OMAPPlatform.java +++ b/src/main/java/com/sun/glass/ui/monocle/OMAPPlatform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,7 +29,8 @@ class OMAPPlatform extends LinuxPlatform { @Override protected NativeCursor createCursor() { - return new OMAPCursor(); + final NativeCursor c = useCursor ? new OMAPCursor() : new NullCursor(); + return logSelectedCursor(c); } @Override diff --git a/src/main/java/com/sun/glass/ui/monocle/RunnableProcessor.java b/src/main/java/com/sun/glass/ui/monocle/RunnableProcessor.java index 1298083..92154ae 100644 --- a/src/main/java/com/sun/glass/ui/monocle/RunnableProcessor.java +++ b/src/main/java/com/sun/glass/ui/monocle/RunnableProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,7 +44,7 @@ private static class RunLoopControl { } // our stack of nested run loops - private LinkedList activeRunLoops = new LinkedList(); + private LinkedList activeRunLoops = new LinkedList<>(); @Override public void run() { diff --git a/src/main/java/com/sun/glass/ui/monocle/SysFS.java b/src/main/java/com/sun/glass/ui/monocle/SysFS.java index 24f2acb..c6026bd 100644 --- a/src/main/java/com/sun/glass/ui/monocle/SysFS.java +++ b/src/main/java/com/sun/glass/ui/monocle/SysFS.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,8 +32,6 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.nio.IntBuffer; -import java.nio.LongBuffer; import java.util.BitSet; import java.util.HashMap; import java.util.Map; @@ -45,7 +43,7 @@ class SysFS { /** Read input device capability data from sysfs */ static Map readCapabilities(File sysPath) { - Map capsMap = new HashMap(); + Map capsMap = new HashMap<>(); File[] capsFiles = new File(sysPath, "device/capabilities").listFiles(); if (capsFiles == null) { return capsMap; diff --git a/src/main/java/com/sun/glass/ui/monocle/TouchInput.java b/src/main/java/com/sun/glass/ui/monocle/TouchInput.java index 653db66..0540b63 100644 --- a/src/main/java/com/sun/glass/ui/monocle/TouchInput.java +++ b/src/main/java/com/sun/glass/ui/monocle/TouchInput.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,6 +46,7 @@ class TouchInput { * events with a delta smaller then the value of this property will be * filtered out.The value of the property is in pixels. */ + @SuppressWarnings("removal") private final int touchRadius = AccessController.doPrivileged( (PrivilegedAction) () -> Integer.getInteger( "monocle.input.touchRadius", 20) @@ -70,6 +71,7 @@ private TouchInput() { TouchPipeline getBasePipeline() { if (basePipeline == null) { basePipeline = new TouchPipeline(); + @SuppressWarnings("removal") String[] touchFilterNames = AccessController.doPrivileged( (PrivilegedAction) () -> System.getProperty( "monocle.input.touchFilters", diff --git a/src/main/java/com/sun/glass/ui/monocle/TouchPipeline.java b/src/main/java/com/sun/glass/ui/monocle/TouchPipeline.java index 6b3da8d..584007b 100644 --- a/src/main/java/com/sun/glass/ui/monocle/TouchPipeline.java +++ b/src/main/java/com/sun/glass/ui/monocle/TouchPipeline.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,7 +30,7 @@ class TouchPipeline { private TouchInput touch = TouchInput.getInstance(); - private ArrayList filters = new ArrayList(); + private ArrayList filters = new ArrayList<>(); private TouchState flushState = new TouchState(); /** @@ -73,7 +73,7 @@ void addNamedFilter(String filterName) { + filterName + "TouchFilter"; } ClassLoader loader = Thread.currentThread().getContextClassLoader(); - addFilter((TouchFilter) loader.loadClass(filterName).newInstance()); + addFilter((TouchFilter) loader.loadClass(filterName).getDeclaredConstructor().newInstance()); } } catch (Exception e) { System.err.println( diff --git a/src/main/java/com/sun/glass/ui/monocle/TouchState.java b/src/main/java/com/sun/glass/ui/monocle/TouchState.java index 2e104d0..81200bb 100644 --- a/src/main/java/com/sun/glass/ui/monocle/TouchState.java +++ b/src/main/java/com/sun/glass/ui/monocle/TouchState.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -86,9 +86,7 @@ MonocleWindow getWindow(boolean recalculateCache, MonocleWindow fallback) { if (primaryID >= 0) { Point p = getPointForID(primaryID); if (p != null) { - window = (MonocleWindow) - MonocleWindowManager.getInstance() - .getWindowForLocation(p.x, p.y); + window = MonocleWindowManager.getInstance().getWindowForLocation(p.x, p.y); } } } diff --git a/src/main/java/com/sun/glass/ui/monocle/Udev.java b/src/main/java/com/sun/glass/ui/monocle/Udev.java index 0200bf9..1165ab5 100644 --- a/src/main/java/com/sun/glass/ui/monocle/Udev.java +++ b/src/main/java/com/sun/glass/ui/monocle/Udev.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -94,6 +94,7 @@ public void run() { while (true) { Map event = readEvent(); runnableProcessor.invokeLater(new Runnable() { + @Override public void run() { String action = event.get("ACTION"); if (action != null) { diff --git a/src/main/java/com/sun/glass/ui/monocle/VNCScreen.java b/src/main/java/com/sun/glass/ui/monocle/VNCScreen.java index 3555110..c0ac6a6 100644 --- a/src/main/java/com/sun/glass/ui/monocle/VNCScreen.java +++ b/src/main/java/com/sun/glass/ui/monocle/VNCScreen.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,12 +48,13 @@ class VNCScreen extends HeadlessScreen { private ServerSocketChannel server; - private Set clients = new HashSet(); + private Set clients = new HashSet<>(); VNCScreen() { super(1024, 600, 32); try { server = ServerSocketChannel.open(); + @SuppressWarnings("removal") int vncPort = AccessController.doPrivileged( (PrivilegedAction) () -> Integer.getInteger("vnc.port", 5901)); diff --git a/src/main/java/com/sun/glass/ui/monocle/X.java b/src/main/java/com/sun/glass/ui/monocle/X.java index 6eda9f6..62fc822 100644 --- a/src/main/java/com/sun/glass/ui/monocle/X.java +++ b/src/main/java/com/sun/glass/ui/monocle/X.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,6 +56,7 @@ static X getX() { } private static void checkPermissions() { + @SuppressWarnings("removal") SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkPermission(permission); @@ -175,6 +176,7 @@ static class XDisplay extends C.Structure { XDisplay(long p) { super(p); } + @Override native int sizeof(); } diff --git a/src/main/java/com/sun/glass/ui/monocle/X11AcceleratedScreen.java b/src/main/java/com/sun/glass/ui/monocle/X11AcceleratedScreen.java index 47a0253..1c92f0e 100644 --- a/src/main/java/com/sun/glass/ui/monocle/X11AcceleratedScreen.java +++ b/src/main/java/com/sun/glass/ui/monocle/X11AcceleratedScreen.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -51,6 +51,7 @@ protected long platformGetNativeDisplay() { * This workaround can be removed when the bug in the drivers is fixed. */ if (nativeDisplay == null) { + @SuppressWarnings("removal") boolean doMaliWorkaround = AccessController.doPrivileged( (PrivilegedAction) () -> diff --git a/src/main/java/com/sun/glass/ui/monocle/X11Cursor.java b/src/main/java/com/sun/glass/ui/monocle/X11Cursor.java index 4c02d58..78d6d43 100644 --- a/src/main/java/com/sun/glass/ui/monocle/X11Cursor.java +++ b/src/main/java/com/sun/glass/ui/monocle/X11Cursor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,6 @@ import com.sun.glass.ui.Size; -import java.io.IOException; import java.nio.ByteBuffer; /** Provides a native cursor for use with X11. Since X11 doesn't provide any diff --git a/src/main/java/com/sun/glass/ui/monocle/X11Platform.java b/src/main/java/com/sun/glass/ui/monocle/X11Platform.java index 336fcb8..1c69e88 100644 --- a/src/main/java/com/sun/glass/ui/monocle/X11Platform.java +++ b/src/main/java/com/sun/glass/ui/monocle/X11Platform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,7 @@ class X11Platform extends NativePlatform { private final boolean x11Input; + @SuppressWarnings("removal") X11Platform() { LinuxSystem.getLinuxSystem().loadLibrary(); x11Input = AccessController.doPrivileged((PrivilegedAction) @@ -61,10 +62,11 @@ protected InputDeviceRegistry createInputDeviceRegistry() { */ @Override protected NativeCursor createCursor() { - if (x11Input) { - return new X11Cursor(); + if (useCursor) { + final NativeCursor c = x11Input ? new X11Cursor() : new X11WarpingCursor(); + return logSelectedCursor(c); } else { - return new X11WarpingCursor(); + return logSelectedCursor(new NullCursor()); } } diff --git a/src/main/java/com/sun/glass/ui/monocle/X11PlatformFactory.java b/src/main/java/com/sun/glass/ui/monocle/X11PlatformFactory.java index 4fd3786..1c56612 100644 --- a/src/main/java/com/sun/glass/ui/monocle/X11PlatformFactory.java +++ b/src/main/java/com/sun/glass/ui/monocle/X11PlatformFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,7 @@ class X11PlatformFactory extends NativePlatformFactory { @Override protected boolean matches() { + @SuppressWarnings("removal") String display = AccessController.doPrivileged( (PrivilegedAction) () -> System.getenv("DISPLAY")); return display != null; diff --git a/src/main/java/com/sun/glass/ui/monocle/X11Screen.java b/src/main/java/com/sun/glass/ui/monocle/X11Screen.java index 5010136..91eb984 100644 --- a/src/main/java/com/sun/glass/ui/monocle/X11Screen.java +++ b/src/main/java/com/sun/glass/ui/monocle/X11Screen.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -74,6 +74,7 @@ class X11Screen implements NativeScreen { int w = xLib.WidthOfScreen(screen); int h = xLib.HeightOfScreen(screen); boolean fullScreen = true; + @SuppressWarnings("removal") String geometry = AccessController.doPrivileged((PrivilegedAction) () -> System.getProperty("x11.geometry"));