1 /* 2 * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package com.sun.glass.ui.monocle; 26 27 import com.sun.glass.ui.monocle.EPDSystem.FbVarScreenInfo; 28 import com.sun.glass.ui.monocle.EPDSystem.IntStructure; 29 import com.sun.glass.ui.monocle.EPDSystem.MxcfbUpdateData; 30 import com.sun.glass.ui.monocle.EPDSystem.MxcfbWaveformModes; 31 import com.sun.javafx.logging.PlatformLogger; 32 import com.sun.javafx.logging.PlatformLogger.Level; 33 import com.sun.javafx.util.Logging; 34 import java.io.IOException; 35 import java.nio.ByteBuffer; 36 import java.text.MessageFormat; 37 38 /** 39 * Represents the standard Linux frame buffer device interface plus the custom 40 * extensions to that interface provided by the Electrophoretic Display 41 * Controller (EPDC) frame buffer driver. 42 * <p> 43 * The Linux frame buffer device interface is documented in <cite>The Frame 44 * Buffer Device API</cite> found in the Ubuntu package called <i>linux-doc</i> 45 * (see <i>/usr/share/doc/linux-doc/fb/api.txt.gz</i>).</p> 46 * <p> 47 * The EPDC frame buffer driver extensions are documented in the <cite>i.MX 48 * Linux Reference Manual</cite> available on the 49 * <a href="https://www.nxp.com/">NXP website</a> (registration required). On 50 * the NXP home page, click Products, ARM Processors, i.MX Application 51 * Processors, and then i.MX 6 Processors, for example. Select the i.MX6SLL 52 * Product in the chart; then click the Documentation tab. Look for a download 53 * with a label for Linux documents, like L4.1.15_2.1.0_LINUX_DOCS, under the 54 * Supporting Information section. After downloading and expanding the archive, 55 * the reference manual is found in the <i>doc</i> directory as the file 56 * <i>i.MX_Linux_Reference_Manual.pdf</i>.</p> 57 */ 58 class EPDFrameBuffer { 59 60 /** 61 * The arithmetic right shift value to convert a bit depth to a byte depth. 62 */ 63 private static final int BITS_TO_BYTES = 3; 64 65 /** 66 * The delay in milliseconds between the completion of all updates in the 67 * EPDC driver and when the driver powers down the EPDC and display power 68 * supplies. 69 */ 70 private static final int POWERDOWN_DELAY = 1_000; 71 72 /** 73 * Linux system error: ENOTTY 25 Inappropriate ioctl for device. 74 */ 75 private static final int ENOTTY = 25; 76 77 private final PlatformLogger logger = Logging.getJavaFXLogger(); 78 private final EPDSettings settings; 79 private final LinuxSystem system; 80 private final EPDSystem driver; 81 private final long fd; 82 83 private final int xres; 84 private final int yres; 85 private final int xresVirtual; 86 private final int yresVirtual; 87 private final int xoffset; 88 private final int yoffset; 89 private final int bitsPerPixel; 90 private final int bytesPerPixel; 91 private final int byteOffset; 92 private final MxcfbUpdateData updateData; 93 private final MxcfbUpdateData syncUpdate; 94 95 private int updateMarker; 96 private int lastMarker; 97 98 /** 99 * Creates a new {@code EPDFrameBuffer} for the given frame buffer device. 100 * The geometry of the Linux frame buffer is shown below for various color 101 * depths and rotations on a sample system, as printed by the <i>fbset</i> 102 * command. The first three are for landscape mode, while the last three are 103 * for portrait. 104 * <pre>{@code 105 * geometry 800 600 800 640 32 (line length: 3200) 106 * geometry 800 600 800 1280 16 (line length: 1600) 107 * geometry 800 600 800 1280 8 (line length: 800) 108 * 109 * geometry 600 800 608 896 32 (line length: 2432) 110 * geometry 600 800 608 1792 16 (line length: 1216) 111 * geometry 600 800 608 1792 8 (line length: 608) 112 * }</pre> 113 * 114 * @implNote {@code MonocleApplication} creates a {@code Screen} which 115 * requires that the width be set to {@link #xresVirtual} even though only 116 * the first {@link #xres} pixels of each row are visible. The EPDC driver 117 * supports panning only in the y-direction, so it is not possible to center 118 * the visible resolution horizontally when these values differ. The JavaFX 119 * application should be left-aligned in this case and ignore the few extra 120 * pixels on the right of its screen. 121 * 122 * @param fbPath the frame buffer device path, such as <i>/dev/fb0</i> 123 * @throws IOException if an error occurs when opening the frame buffer 124 * device or when getting or setting the frame buffer configuration 125 * @throws IllegalArgumentException if the EPD settings specify an 126 * unsupported color depth 127 */ 128 EPDFrameBuffer(String fbPath) throws IOException { 129 settings = EPDSettings.newInstance(); 130 system = LinuxSystem.getLinuxSystem(); 131 driver = EPDSystem.getEPDSystem(); 132 fd = system.open(fbPath, LinuxSystem.O_RDWR); 133 if (fd == -1) { 134 throw new IOException(system.getErrorMessage()); 135 } 136 137 /* 138 * Gets the current settings of the frame buffer device. 139 */ 140 var screen = new FbVarScreenInfo(); 141 getScreenInfo(screen); 142 143 /* 144 * Changes the settings of the frame buffer from the system properties. 145 * 146 * See the section, "Format configuration," in "The Frame Buffer Device 147 * API" for details. Note that xoffset is always zero, and yoffset can 148 * be modified only by panning in the y-direction with the IOCTL call to 149 * LinuxSystem.FBIOPAN_DISPLAY. 150 */ 151 screen.setBitsPerPixel(screen.p, settings.bitsPerPixel); 152 screen.setGrayscale(screen.p, settings.grayscale); 153 switch (settings.bitsPerPixel) { 154 case Byte.SIZE: 155 // rgba 8/0,8/0,8/0,0/0 (set by driver when grayscale > 0) 156 screen.setRed(screen.p, 0, 0); 157 screen.setGreen(screen.p, 0, 0); 158 screen.setBlue(screen.p, 0, 0); 159 screen.setTransp(screen.p, 0, 0); 160 break; 161 case Short.SIZE: 162 // rgba 5/11,6/5,5/0,0/0 163 screen.setRed(screen.p, 5, 11); 164 screen.setGreen(screen.p, 6, 5); 165 screen.setBlue(screen.p, 5, 0); 166 screen.setTransp(screen.p, 0, 0); 167 break; 168 case Integer.SIZE: 169 // rgba 8/16,8/8,8/0,8/24 170 screen.setRed(screen.p, 8, 16); 171 screen.setGreen(screen.p, 8, 8); 172 screen.setBlue(screen.p, 8, 0); 173 screen.setTransp(screen.p, 8, 24); 174 break; 175 default: 176 String msg = MessageFormat.format("Unsupported color depth: {0} bpp", settings.bitsPerPixel); 177 logger.severe(msg); 178 throw new IllegalArgumentException(msg); 179 } 180 screen.setActivate(screen.p, EPDSystem.FB_ACTIVATE_FORCE); 181 screen.setRotate(screen.p, settings.rotate); 182 setScreenInfo(screen); 183 184 /* 185 * Gets and logs the new settings of the frame buffer device. 186 */ 187 getScreenInfo(screen); 188 logScreenInfo(screen); 189 xres = screen.getXRes(screen.p); 190 yres = screen.getYRes(screen.p); 191 xresVirtual = screen.getXResVirtual(screen.p); 192 yresVirtual = screen.getYResVirtual(screen.p); 193 xoffset = screen.getOffsetX(screen.p); 194 yoffset = screen.getOffsetY(screen.p); 195 bitsPerPixel = screen.getBitsPerPixel(screen.p); 196 bytesPerPixel = bitsPerPixel >>> BITS_TO_BYTES; 197 byteOffset = (xoffset + yoffset * xresVirtual) * bytesPerPixel; 198 199 /* 200 * Allocates objects for reuse to avoid creating new direct byte buffers 201 * outside of the Java heap on each display update. 202 */ 203 updateData = new MxcfbUpdateData(); 204 syncUpdate = createDefaultUpdate(xres, yres); 205 } 206 207 /** 208 * Gets the variable screen information of the frame buffer. Run the 209 * <i>fbset</i> command as <i>root</i> to print the screen information. 210 * 211 * @param screen the object representing the variable screen information 212 * @throws IOException if an error occurs getting the information 213 */ 214 private void getScreenInfo(FbVarScreenInfo screen) throws IOException { 215 int rc = system.ioctl(fd, LinuxSystem.FBIOGET_VSCREENINFO, screen.p); 216 if (rc != 0) { 217 system.close(fd); 218 throw new IOException(system.getErrorMessage()); 219 } 220 } 221 222 /** 223 * Sets the variable screen information of the frame buffer. 224 * <p> 225 * "To ensure that the EPDC driver receives the initialization request, the 226 * {@code activate} field of the {@code fb_var_screeninfo} parameter should 227 * be set to {@code FB_ACTIVATE_FORCE}." [EPDC Panel Initialization, 228 * <cite>i.MX Linux Reference Manual</cite>]</p> 229 * <p> 230 * To request a change to 8-bit grayscale format, the bits per pixel must be 231 * set to 8 and the grayscale value must be set to one of the two valid 232 * grayscale format values: {@code GRAYSCALE_8BIT} or 233 * {@code GRAYSCALE_8BIT_INVERTED}. [Grayscale Framebuffer Selection, 234 * <cite>i.MX Linux Reference Manual</cite>]</p> 235 * 236 * @param screen the object representing the variable screen information 237 * @throws IOException if an error occurs setting the information 238 */ 239 private void setScreenInfo(FbVarScreenInfo screen) throws IOException { 240 int rc = system.ioctl(fd, LinuxSystem.FBIOPUT_VSCREENINFO, screen.p); 241 if (rc != 0) { 242 system.close(fd); 243 throw new IOException(system.getErrorMessage()); 244 } 245 } 246 247 /** 248 * Logs the variable screen information of the frame buffer, depending on 249 * the logging level. 250 * 251 * @param screen the object representing the variable screen information 252 */ 253 private void logScreenInfo(FbVarScreenInfo screen) { 254 if (logger.isLoggable(Level.FINE)) { 255 logger.fine("Frame buffer geometry: {0} {1} {2} {3} {4}", 256 screen.getXRes(screen.p), screen.getYRes(screen.p), 257 screen.getXResVirtual(screen.p), screen.getYResVirtual(screen.p), 258 screen.getBitsPerPixel(screen.p)); 259 logger.fine("Frame buffer rgba: {0}/{1},{2}/{3},{4}/{5},{6}/{7}", 260 screen.getRedLength(screen.p), screen.getRedOffset(screen.p), 261 screen.getGreenLength(screen.p), screen.getGreenOffset(screen.p), 262 screen.getBlueLength(screen.p), screen.getBlueOffset(screen.p), 263 screen.getTranspLength(screen.p), screen.getTranspOffset(screen.p)); 264 logger.fine("Frame buffer grayscale: {0}", screen.getGrayscale(screen.p)); 265 } 266 } 267 268 /** 269 * Creates the default update data with values from the EPD system 270 * properties, setting all fields except for the update marker. Reusing the 271 * update data object avoids creating a new one for each update request. 272 * 273 * @implNote An update mode of {@link EPDSystem#UPDATE_MODE_FULL} would make 274 * the {@link EPDSettings#NO_WAIT} system property useless by changing all 275 * non-colliding updates into colliding ones, so this method sets the 276 * default update mode to {@link EPDSystem#UPDATE_MODE_PARTIAL}. 277 * 278 * @param width the width of the update region 279 * @param height the height of the update region 280 * @return the default update data with all fields set but the update marker 281 */ 282 private MxcfbUpdateData createDefaultUpdate(int width, int height) { 283 var update = new MxcfbUpdateData(); 284 update.setUpdateRegion(update.p, 0, 0, width, height); 285 update.setWaveformMode(update.p, settings.waveformMode); 286 update.setUpdateMode(update.p, EPDSystem.UPDATE_MODE_PARTIAL); 287 update.setTemp(update.p, EPDSystem.TEMP_USE_AMBIENT); 288 update.setFlags(update.p, settings.flags); 289 return update; 290 } 291 292 /** 293 * Defines a mapping for common waveform modes. This mapping must be 294 * configured for the automatic waveform mode selection to function 295 * properly. Each of the parameters should be set to one of the following: 296 * <ul> 297 * <li>{@link EPDSystem#WAVEFORM_MODE_INIT}</li> 298 * <li>{@link EPDSystem#WAVEFORM_MODE_DU}</li> 299 * <li>{@link EPDSystem#WAVEFORM_MODE_GC16}</li> 300 * <li>{@link EPDSystem#WAVEFORM_MODE_GC4}</li> 301 * <li>{@link EPDSystem#WAVEFORM_MODE_A2}</li> 302 * </ul> 303 * 304 * @implNote This method fails on the Kobo Glo HD Model N437 with the error 305 * ENOTTY (25), "Inappropriate ioctl for device." The driver on that device 306 * uses an extended structure with four additional integers, changing its 307 * size and its corresponding request code. This method could use the 308 * extended structure, but the driver on the Kobo Glo HD ignores it and 309 * returns immediately, anyway. Furthermore, newer devices support both the 310 * current structure and the extended one, but define the extra fields in a 311 * different order. Therefore, simply use the current structure and ignore 312 * an error of ENOTTY, picking up the default values for any extra fields. 313 * 314 * @param init the initialization mode for clearing the screen to all white 315 * @param du the direct update mode for changing any gray values to either 316 * all black or all white 317 * @param gc4 the mode for 4-level (2-bit) grayscale images and text 318 * @param gc8 the mode for 8-level (3-bit) grayscale images and text 319 * @param gc16 the mode for 16-level (4-bit) grayscale images and text 320 * @param gc32 the mode for 32-level (5-bit) grayscale images and text 321 */ 322 private void setWaveformModes(int init, int du, int gc4, int gc8, int gc16, int gc32) { 323 var modes = new MxcfbWaveformModes(); 324 modes.setModes(modes.p, init, du, gc4, gc8, gc16, gc32); 325 int rc = system.ioctl(fd, driver.MXCFB_SET_WAVEFORM_MODES, modes.p); 326 if (rc != 0 && system.errno() != ENOTTY) { 327 logger.severe("Failed setting waveform modes: {0} ({1})", 328 system.getErrorMessage(), system.errno()); 329 } 330 } 331 332 /** 333 * Sets the temperature to be used by the EPDC driver in subsequent panel 334 * updates. Note that this temperature setting may be overridden by setting 335 * the temperature in a specific update to anything other than 336 * {@link EPDSystem#TEMP_USE_AMBIENT}. 337 * 338 * @param temp the temperature in degrees Celsius 339 */ 340 private void setTemperature(int temp) { 341 int rc = driver.ioctl(fd, driver.MXCFB_SET_TEMPERATURE, temp); 342 if (rc != 0) { 343 logger.severe("Failed setting temperature to {2} degrees Celsius: {0} ({1})", 344 system.getErrorMessage(), system.errno(), temp); 345 } 346 } 347 348 /** 349 * Selects between automatic and region update mode. In region update mode, 350 * updates must be submitted with an IOCTL call to 351 * {@link EPDSystem#MXCFB_SEND_UPDATE}. In automatic mode, updates are 352 * generated by the driver when it detects that pages in a frame buffer 353 * memory region have been modified. 354 * <p> 355 * Automatic mode is available only when it has been enabled in the Linux 356 * kernel by the option CONFIG_FB_MXC_EINK_AUTO_UPDATE_MODE. You can find 357 * the configuration options used to build the kernel in a file under 358 * <i>/proc</i> or <i>/boot</i>, such as <i>/proc/config.gz</i>.</p> 359 * 360 * @param mode the automatic update mode, one of: 361 * <ul> 362 * <li>{@link EPDSystem#AUTO_UPDATE_MODE_REGION_MODE}</li> 363 * <li>{@link EPDSystem#AUTO_UPDATE_MODE_AUTOMATIC_MODE}</li> 364 * </ul> 365 */ 366 private void setAutoUpdateMode(int mode) { 367 int rc = driver.ioctl(fd, driver.MXCFB_SET_AUTO_UPDATE_MODE, mode); 368 if (rc != 0) { 369 logger.severe("Failed setting auto-update mode to {2}: {0} ({1})", 370 system.getErrorMessage(), system.errno(), mode); 371 } 372 } 373 374 /** 375 * Requests the entire visible region of the frame buffer to be updated to 376 * the display. 377 * 378 * @param updateMode the update mode, one of: 379 * <ul> 380 * <li>{@link EPDSystem#UPDATE_MODE_PARTIAL}</li> 381 * <li>{@link EPDSystem#UPDATE_MODE_FULL}</li> 382 * </ul> 383 * @param waveformMode the waveform mode, one of: 384 * <ul> 385 * <li>{@link EPDSystem#WAVEFORM_MODE_INIT}</li> 386 * <li>{@link EPDSystem#WAVEFORM_MODE_DU}</li> 387 * <li>{@link EPDSystem#WAVEFORM_MODE_GC16}</li> 388 * <li>{@link EPDSystem#WAVEFORM_MODE_GC4}</li> 389 * <li>{@link EPDSystem#WAVEFORM_MODE_A2}</li> 390 * <li>{@link EPDSystem#WAVEFORM_MODE_AUTO}</li> 391 * </ul> 392 * @param flags a bit mask composed of the following flag values: 393 * <ul> 394 * <li>{@link EPDSystem#EPDC_FLAG_ENABLE_INVERSION}</li> 395 * <li>{@link EPDSystem#EPDC_FLAG_FORCE_MONOCHROME}</li> 396 * <li>{@link EPDSystem#EPDC_FLAG_USE_DITHERING_Y1}</li> 397 * <li>{@link EPDSystem#EPDC_FLAG_USE_DITHERING_Y4}</li> 398 * </ul> 399 * @return the marker to identify this update in a subsequence call to 400 * {@link #waitForUpdateComplete} 401 */ 402 private int sendUpdate(int updateMode, int waveformMode, int flags) { 403 updateData.setUpdateRegion(updateData.p, 0, 0, xres, yres); 404 updateData.setUpdateMode(updateData.p, updateMode); 405 updateData.setTemp(updateData.p, EPDSystem.TEMP_USE_AMBIENT); 406 updateData.setFlags(updateData.p, flags); 407 return sendUpdate(updateData, waveformMode); 408 } 409 410 /** 411 * Requests an update to the display, allowing for the reuse of the update 412 * data object. The waveform mode is reset because the update data could 413 * have been used in a previous update. In that case, the waveform mode may 414 * have been modified by the EPDC driver with the actual mode selected. The 415 * update marker is overwritten with the next sequential marker. 416 * 417 * @param update the data describing the update; the waveform mode and 418 * update marker are overwritten 419 * @param waveformMode the waveform mode for this update 420 * @return the marker to identify this update in a subsequence call to 421 * {@link #waitForUpdateComplete} 422 */ 423 private int sendUpdate(MxcfbUpdateData update, int waveformMode) { 424 /* 425 * The IOCTL call to MXCFB_WAIT_FOR_UPDATE_COMPLETE returns the error 426 * "Invalid argument (22)" when passed an update marker of zero. 427 */ 428 updateMarker++; 429 if (updateMarker == 0) { 430 updateMarker++; 431 } 432 update.setWaveformMode(update.p, waveformMode); 433 update.setUpdateMarker(update.p, updateMarker); 434 int rc = system.ioctl(fd, driver.MXCFB_SEND_UPDATE, update.p); 435 if (rc != 0) { 436 logger.severe("Failed sending update {2}: {0} ({1})", 437 system.getErrorMessage(), system.errno(), Integer.toUnsignedLong(updateMarker)); 438 } else if (logger.isLoggable(Level.FINER)) { 439 logger.finer("Sent update: {0} x {1}, waveform {2}, selected {3}, flags 0x{4}, marker {5}", 440 update.getUpdateRegionWidth(update.p), update.getUpdateRegionHeight(update.p), 441 waveformMode, update.getWaveformMode(update.p), 442 Integer.toHexString(update.getFlags(update.p)).toUpperCase(), 443 Integer.toUnsignedLong(updateMarker)); 444 } 445 return updateMarker; 446 } 447 448 /** 449 * Blocks and waits for a previous update request to complete. 450 * 451 * @param marker the marker to identify a particular update, returned by 452 * {@link #sendUpdate(MxcfbUpdateData, int)} 453 */ 454 private void waitForUpdateComplete(int marker) { 455 /* 456 * This IOCTL call returns: 0 if the marker was not found because the 457 * update already completed or failed, negative (-1) with the error 458 * "Connection timed out (110)" if the wait timed out after 5 seconds, 459 * or positive if the wait occurred and completed (see 460 * "wait_for_completion_timeout" in "kernel/sched/completion.c"). 461 */ 462 int rc = driver.ioctl(fd, driver.MXCFB_WAIT_FOR_UPDATE_COMPLETE, marker); 463 if (rc < 0) { 464 logger.severe("Failed waiting for update {2}: {0} ({1})", 465 system.getErrorMessage(), system.errno(), Integer.toUnsignedLong(marker)); 466 } else if (rc == 0 && logger.isLoggable(Level.FINER)) { 467 logger.finer("Update completed before wait: marker {0}", 468 Integer.toUnsignedLong(marker)); 469 } 470 } 471 472 /** 473 * Sets the delay between the completion of all updates in the driver and 474 * when the driver should power down the EPDC and display power supplies. To 475 * disable powering down entirely, use the delay value 476 * {@link EPDSystem#FB_POWERDOWN_DISABLE}. 477 * 478 * @param delay the delay in milliseconds 479 */ 480 private void setPowerdownDelay(int delay) { 481 int rc = driver.ioctl(fd, driver.MXCFB_SET_PWRDOWN_DELAY, delay); 482 if (rc != 0) { 483 logger.severe("Failed setting power-down delay to {2}: {0} ({1})", 484 system.getErrorMessage(), system.errno(), delay); 485 } 486 } 487 488 /** 489 * Gets the current power-down delay from the EPDC driver. 490 * 491 * @return the delay in milliseconds 492 */ 493 private int getPowerdownDelay() { 494 var integer = new IntStructure(); 495 int rc = system.ioctl(fd, driver.MXCFB_GET_PWRDOWN_DELAY, integer.p); 496 if (rc != 0) { 497 logger.severe("Failed getting power-down delay: {0} ({1})", 498 system.getErrorMessage(), system.errno()); 499 } 500 return integer.get(integer.p); 501 } 502 503 /** 504 * Selects a scheme for the flow of updates within the driver. 505 * 506 * @param scheme the update scheme, one of: 507 * <ul> 508 * <li>{@link EPDSystem#UPDATE_SCHEME_SNAPSHOT}</li> 509 * <li>{@link EPDSystem#UPDATE_SCHEME_QUEUE}</li> 510 * <li>{@link EPDSystem#UPDATE_SCHEME_QUEUE_AND_MERGE}</li> 511 * </ul> 512 */ 513 private void setUpdateScheme(int scheme) { 514 int rc = driver.ioctl(fd, driver.MXCFB_SET_UPDATE_SCHEME, scheme); 515 if (rc != 0) { 516 logger.severe("Failed setting update scheme to {2}: {0} ({1})", 517 system.getErrorMessage(), system.errno(), scheme); 518 } 519 } 520 521 /** 522 * Initializes the EPDC frame buffer device, setting the update scheme to 523 * {@link EPDSystem#UPDATE_SCHEME_SNAPSHOT}. 524 */ 525 void init() { 526 setWaveformModes(EPDSystem.WAVEFORM_MODE_INIT, EPDSystem.WAVEFORM_MODE_DU, 527 EPDSystem.WAVEFORM_MODE_GC4, EPDSystem.WAVEFORM_MODE_GC16, 528 EPDSystem.WAVEFORM_MODE_GC16, EPDSystem.WAVEFORM_MODE_GC16); 529 setTemperature(EPDSystem.TEMP_USE_AMBIENT); 530 setAutoUpdateMode(EPDSystem.AUTO_UPDATE_MODE_REGION_MODE); 531 setPowerdownDelay(POWERDOWN_DELAY); 532 setUpdateScheme(EPDSystem.UPDATE_SCHEME_SNAPSHOT); 533 } 534 535 /** 536 * Clears the display panel. The visible frame buffer should be cleared with 537 * zeros when called. This method sends two direct updates (all black 538 * followed by all white) to refresh the screen and clear any ghosting 539 * effects, and returns when both updates are complete. 540 * <p> 541 * <strong>This method is not thread safe</strong>, but it is invoked only 542 * once from the Event Thread during initialization.</p> 543 */ 544 void clear() { 545 lastMarker = sendUpdate(EPDSystem.UPDATE_MODE_FULL, 546 EPDSystem.WAVEFORM_MODE_DU, 0); 547 lastMarker = sendUpdate(EPDSystem.UPDATE_MODE_FULL, 548 EPDSystem.WAVEFORM_MODE_DU, EPDSystem.EPDC_FLAG_ENABLE_INVERSION); 549 waitForUpdateComplete(lastMarker); 550 } 551 552 /** 553 * Sends the updated contents of the Linux frame buffer to the EPDC driver, 554 * optionally synchronizing with the driver by first waiting for the 555 * previous update to complete. 556 * <p> 557 * <strong>This method is not thread safe</strong>, but it is invoked only 558 * from the JavaFX Application Thread.</p> 559 */ 560 void sync() { 561 if (!settings.noWait) { 562 waitForUpdateComplete(lastMarker); 563 } 564 lastMarker = sendUpdate(syncUpdate, settings.waveformMode); 565 } 566 567 /** 568 * Gets the number of bytes from the beginning of the frame buffer to the 569 * start of its visible resolution. 570 * 571 * @return the offset in bytes 572 */ 573 int getByteOffset() { 574 return byteOffset; 575 } 576 577 /** 578 * Creates an off-screen byte buffer equal in resolution to the virtual 579 * resolution of the frame buffer, but with 32 bits per pixel. 580 * 581 * @return a 32-bit pixel buffer matching the resolution of the frame buffer 582 */ 583 ByteBuffer getOffscreenBuffer() { 584 /* 585 * In this case, a direct byte buffer outside of the normal heap is 586 * faster than a non-direct byte buffer on the heap. The frame rate is 587 * roughly 10 to 40 percent faster for a framebuffer with 8 bits per 588 * pixel and 40 to 60 percent faster for a framebuffer with 16 bits per 589 * pixel, depending on the device processor and screen size. 590 */ 591 int size = xresVirtual * yres * Integer.BYTES; 592 return ByteBuffer.allocateDirect(size); 593 } 594 595 /** 596 * Creates a new mapping of the Linux frame buffer device into memory. 597 * 598 * @implNote The virtual y-resolution reported by the device driver can be 599 * wrong, as shown by the following example on the Kobo Glo HD Model N437 600 * which reports 2,304 pixels when the correct value is 1,152 pixels 601 * (6,782,976 / 5,888). Therefore, this method cannot use the frame buffer 602 * virtual resolution to calculate its size. 603 * 604 * <pre>{@code 605 * $ sudo fbset -i 606 * 607 * mode "1448x1072-46" 608 * # D: 80.000 MHz, H: 50.188 kHz, V: 46.385 Hz 609 * geometry 1448 1072 1472 2304 32 610 * timings 12500 16 102 4 4 28 2 611 * rgba 8/16,8/8,8/0,8/24 612 * endmode 613 * 614 * Frame buffer device information: 615 * Name : mxc_epdc_fb 616 * Address : 0x88000000 617 * Size : 6782976 618 * Type : PACKED PIXELS 619 * Visual : TRUECOLOR 620 * XPanStep : 1 621 * YPanStep : 1 622 * YWrapStep : 0 623 * LineLength : 5888 624 * Accelerator : No 625 * }</pre> 626 * 627 * @return a byte buffer containing the mapping of the Linux frame buffer 628 * device if successful; otherwise {@code null} 629 */ 630 ByteBuffer getMappedBuffer() { 631 ByteBuffer buffer = null; 632 int size = xresVirtual * yres * bytesPerPixel; 633 logger.fine("Mapping frame buffer: {0} bytes", size); 634 long addr = system.mmap(0l, size, LinuxSystem.PROT_WRITE, LinuxSystem.MAP_SHARED, fd, 0); 635 if (addr == LinuxSystem.MAP_FAILED) { 636 logger.severe("Failed mapping {2} bytes of frame buffer: {0} ({1})", 637 system.getErrorMessage(), system.errno(), size); 638 } else { 639 buffer = C.getC().NewDirectByteBuffer(addr, size); 640 } 641 return buffer; 642 } 643 644 /** 645 * Deletes the mapping of the Linux frame buffer device. 646 * 647 * @param buffer the byte buffer containing the mapping of the Linux frame 648 * buffer device 649 */ 650 void releaseMappedBuffer(ByteBuffer buffer) { 651 int size = buffer.capacity(); 652 logger.fine("Unmapping frame buffer: {0} bytes", size); 653 int rc = system.munmap(C.getC().GetDirectBufferAddress(buffer), size); 654 if (rc != 0) { 655 logger.severe("Failed unmapping {2} bytes of frame buffer: {0} ({1})", 656 system.getErrorMessage(), system.errno(), size); 657 } 658 } 659 660 /** 661 * Closes the Linux frame buffer device. 662 */ 663 void close() { 664 system.close(fd); 665 } 666 667 /** 668 * Gets the native handle to the Linux frame buffer device. 669 * 670 * @return the frame buffer device file descriptor 671 */ 672 long getNativeHandle() { 673 return fd; 674 } 675 676 /** 677 * Gets the frame buffer width in pixels. See the notes for the 678 * {@linkplain EPDFrameBuffer#EPDFrameBuffer constructor} above. 679 * 680 * @implNote When using an 8-bit, unrotated, and uninverted frame buffer in 681 * the Y8 pixel format, the Kobo Clara HD Model N249 works only when this 682 * method returns the visible x-resolution ({@code xres}) instead of the 683 * normal virtual x-resolution ({@code xresVirtual}). 684 * 685 * @return the width in pixels 686 */ 687 int getWidth() { 688 return settings.getWidthVisible ? xres : xresVirtual; 689 } 690 691 /** 692 * Gets the frame buffer height in pixels. 693 * 694 * @return the height in pixels 695 */ 696 int getHeight() { 697 return yres; 698 } 699 700 /** 701 * Gets the frame buffer color depth in bits per pixel. 702 * 703 * @return the color depth in bits per pixel 704 */ 705 int getBitDepth() { 706 return bitsPerPixel; 707 } 708 709 @Override 710 public String toString() { 711 return MessageFormat.format("{0}[width={1} height={2} bitDepth={3}]", 712 getClass().getName(), getWidth(), getHeight(), getBitDepth()); 713 } 714 }