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