@@ -69,6 +69,11 @@ class EPDFrameBuffer {
69
69
*/
70
70
private static final int POWERDOWN_DELAY = 1_000 ;
71
71
72
+ /**
73
+ * Linux system error: ENOTTY 25 Inappropriate ioctl for device.
74
+ */
75
+ private static final int ENOTTY = 25 ;
76
+
72
77
private final PlatformLogger logger = Logging .getJavaFXLogger ();
73
78
private final EPDSettings settings ;
74
79
private final LinuxSystem system ;
@@ -296,6 +301,16 @@ private MxcfbUpdateData createDefaultUpdate(int width, int height) {
296
301
* <li>{@link EPDSystem#WAVEFORM_MODE_A2}</li>
297
302
* </ul>
298
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
+ *
299
314
* @param init the initialization mode for clearing the screen to all white
300
315
* @param du the direct update mode for changing any gray values to either
301
316
* all black or all white
@@ -308,7 +323,7 @@ private void setWaveformModes(int init, int du, int gc4, int gc8, int gc16, int
308
323
var modes = new MxcfbWaveformModes ();
309
324
modes .setModes (modes .p , init , du , gc4 , gc8 , gc16 , gc32 );
310
325
int rc = system .ioctl (fd , driver .MXCFB_SET_WAVEFORM_MODES , modes .p );
311
- if (rc != 0 ) {
326
+ if (rc != 0 && system . errno () != ENOTTY ) {
312
327
logger .severe ("Failed setting waveform modes: {0} ({1})" ,
313
328
system .getErrorMessage (), system .errno ());
314
329
}
@@ -325,7 +340,7 @@ private void setWaveformModes(int init, int du, int gc4, int gc8, int gc16, int
325
340
private void setTemperature (int temp ) {
326
341
int rc = driver .ioctl (fd , driver .MXCFB_SET_TEMPERATURE , temp );
327
342
if (rc != 0 ) {
328
- logger .severe ("Failed setting temperature to {2} °C : {0} ({1})" ,
343
+ logger .severe ("Failed setting temperature to {2} degrees Celsius : {0} ({1})" ,
329
344
system .getErrorMessage (), system .errno (), temp );
330
345
}
331
346
}
@@ -421,7 +436,7 @@ private int sendUpdate(MxcfbUpdateData update, int waveformMode) {
421
436
logger .severe ("Failed sending update {2}: {0} ({1})" ,
422
437
system .getErrorMessage (), system .errno (), Integer .toUnsignedLong (updateMarker ));
423
438
} else if (logger .isLoggable (Level .FINER )) {
424
- logger .finer ("Sent update: {0} × {1}, waveform {2}, selected {3}, flags 0x{4}, marker {5}" ,
439
+ logger .finer ("Sent update: {0} x {1}, waveform {2}, selected {3}, flags 0x{4}, marker {5}" ,
425
440
update .getUpdateRegionWidth (update .p ), update .getUpdateRegionHeight (update .p ),
426
441
waveformMode , update .getWaveformMode (update .p ),
427
442
Integer .toHexString (update .getFlags (update .p )).toUpperCase (),
@@ -482,7 +497,7 @@ private int getPowerdownDelay() {
482
497
logger .severe ("Failed getting power-down delay: {0} ({1})" ,
483
498
system .getErrorMessage (), system .errno ());
484
499
}
485
- return integer .getInteger (integer .p );
500
+ return integer .get (integer .p );
486
501
}
487
502
488
503
/**
@@ -571,20 +586,57 @@ ByteBuffer getOffscreenBuffer() {
571
586
* "QuantumRenderer modifies buffer in use by JavaFX Application Thread"
572
587
* <https://bugs.openjdk.java.net/browse/JDK-8201567>.
573
588
*/
574
- int size = xresVirtual * yresVirtual * Integer .SIZE ;
589
+ int size = xresVirtual * yres * Integer .BYTES ;
575
590
return ByteBuffer .allocateDirect (size );
576
591
}
577
592
578
593
/**
579
594
* Creates a new mapping of the Linux frame buffer device into memory.
580
595
*
596
+ * @implNote The virtual y-resolution reported by the device driver can be
597
+ * wrong, as shown by the following example on the Kobo Glo HD Model N437
598
+ * which reports 2,304 pixels when the correct value is 1,152 pixels
599
+ * (6,782,976 / 5,888). Therefore, this method cannot use the frame buffer
600
+ * virtual resolution to calculate its size.
601
+ *
602
+ * <pre>{@code
603
+ * $ sudo fbset -i
604
+ *
605
+ * mode "1448x1072-46"
606
+ * # D: 80.000 MHz, H: 50.188 kHz, V: 46.385 Hz
607
+ * geometry 1448 1072 1472 2304 32
608
+ * timings 12500 16 102 4 4 28 2
609
+ * rgba 8/16,8/8,8/0,8/24
610
+ * endmode
611
+ *
612
+ * Frame buffer device information:
613
+ * Name : mxc_epdc_fb
614
+ * Address : 0x88000000
615
+ * Size : 6782976
616
+ * Type : PACKED PIXELS
617
+ * Visual : TRUECOLOR
618
+ * XPanStep : 1
619
+ * YPanStep : 1
620
+ * YWrapStep : 0
621
+ * LineLength : 5888
622
+ * Accelerator : No
623
+ * }</pre>
624
+ *
581
625
* @return a byte buffer containing the mapping of the Linux frame buffer
582
- * device
626
+ * device if successful; otherwise {@code null}
583
627
*/
584
628
ByteBuffer getMappedBuffer () {
585
- int size = xresVirtual * yresVirtual * bytesPerPixel ;
629
+ ByteBuffer buffer = null ;
630
+ int size = xresVirtual * yres * bytesPerPixel ;
631
+ logger .fine ("Mapping frame buffer: {0} bytes" , size );
586
632
long addr = system .mmap (0l , size , LinuxSystem .PROT_WRITE , LinuxSystem .MAP_SHARED , fd , 0 );
587
- return addr == LinuxSystem .MAP_FAILED ? null : C .getC ().NewDirectByteBuffer (addr , size );
633
+ if (addr == LinuxSystem .MAP_FAILED ) {
634
+ logger .severe ("Failed mapping {2} bytes of frame buffer: {0} ({1})" ,
635
+ system .getErrorMessage (), system .errno (), size );
636
+ } else {
637
+ buffer = C .getC ().NewDirectByteBuffer (addr , size );
638
+ }
639
+ return buffer ;
588
640
}
589
641
590
642
/**
@@ -594,7 +646,13 @@ ByteBuffer getMappedBuffer() {
594
646
* buffer device
595
647
*/
596
648
void releaseMappedBuffer (ByteBuffer buffer ) {
597
- system .munmap (C .getC ().GetDirectBufferAddress (buffer ), buffer .capacity ());
649
+ int size = buffer .capacity ();
650
+ logger .fine ("Unmapping frame buffer: {0} bytes" , size );
651
+ int rc = system .munmap (C .getC ().GetDirectBufferAddress (buffer ), size );
652
+ if (rc != 0 ) {
653
+ logger .severe ("Failed unmapping {2} bytes of frame buffer: {0} ({1})" ,
654
+ system .getErrorMessage (), system .errno (), size );
655
+ }
598
656
}
599
657
600
658
/**
@@ -614,26 +672,31 @@ long getNativeHandle() {
614
672
}
615
673
616
674
/**
617
- * Gets the virtual horizontal resolution of the frame buffer. See the notes
618
- * for the {@linkplain EPDFrameBuffer#EPDFrameBuffer constructor} above.
675
+ * Gets the frame buffer width in pixels. See the notes for the
676
+ * {@linkplain EPDFrameBuffer#EPDFrameBuffer constructor} above.
677
+ *
678
+ * @implNote When using an 8-bit, unrotated, and uninverted frame buffer in
679
+ * the Y8 pixel format, the Kobo Clara HD Model N249 works only when this
680
+ * method returns the visible x-resolution ({@code xres}) instead of the
681
+ * normal virtual x-resolution ({@code xresVirtual}).
619
682
*
620
- * @return the virtual width in pixels
683
+ * @return the width in pixels
621
684
*/
622
685
int getWidth () {
623
- return xresVirtual ;
686
+ return settings . getWidthVisible ? xres : xresVirtual ;
624
687
}
625
688
626
689
/**
627
- * Gets the visible vertical resolution of the frame buffer .
690
+ * Gets the frame buffer height in pixels .
628
691
*
629
- * @return the visible height in pixels
692
+ * @return the height in pixels
630
693
*/
631
694
int getHeight () {
632
695
return yres ;
633
696
}
634
697
635
698
/**
636
- * Gets the color depth of the frame buffer .
699
+ * Gets the frame buffer color depth in bits per pixel .
637
700
*
638
701
* @return the color depth in bits per pixel
639
702
*/
0 commit comments