com.sun.glass.ui.monocle.EPDSettings Maven / Gradle / Ivy
/*
* 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);
}
}