-
Notifications
You must be signed in to change notification settings - Fork 24
/
fbink.h
1798 lines (1692 loc) · 108 KB
/
fbink.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
FBInk: FrameBuffer eInker, a library to print text & images to an eInk Linux framebuffer
Copyright (C) 2018-2024 NiLuJe <ninuje@gmail.com>
SPDX-License-Identifier: GPL-3.0-or-later
----
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __FBINK_H
#define __FBINK_H
// Because we're pretty much Linux-bound ;).
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <linux/fb.h>
// Be friendly with C++ compilers (both GCC & Clang support __restrict__).
#ifdef __cplusplus
# define restrict __restrict__
extern "C" {
#endif
// Symbol visibility shenanigans...
// c.f., https://gcc.gnu.org/wiki/Visibility
#if __GNUC__ >= 4
# define DLL_PUBLIC __attribute__((visibility("default")))
# define DLL_LOCAL __attribute__((visibility("hidden")))
#else
# define DLL_PUBLIC
# define DLL_LOCAL
#endif
// Are we actually building the shared lib?
#ifdef FBINK_SHAREDLIB
# define FBINK_API DLL_PUBLIC
# define FBINK_LOCAL DLL_LOCAL
#else
# define FBINK_API
# define FBINK_LOCAL
#endif
//
////
//
// Magic number for automatic fbfd handling
#define FBFD_AUTO -1
// As 0 is an invalid marker value, we can coopt it to try to retrieve our own last sent marker
#define LAST_MARKER 0U
// NOTE: There's a dirty bit of trickery involved here to coerce enums into a specific data type (instead of an int):
// * The packed attribute, which does locally what GCC's -fshort-enums does globally,
// ensuring the enum's data type is only as wide as the actual values require.
// * A tail element with a value corresponding to the MAX of the target data type,
// mainly used to enforce an unsigned data type.
// BUT, while this "works" in C, this is essentially non-standard, and would break most automatic bindings,
// which happily assume we're not breaking the C standard, and as such expect an enum to be int-sized...
// So, in addition to that, instead of using the enum's typedef directly, we go through an explicit typedef.
// This obviously still works in C because enums are dumb, they're basically unscoped constants,
// and this makes bindings happy because they'll have an explicit typedef to rely on.
// Fun fact: this is essentially what the Go bindings were already doing ;).
//
// Supported targets
typedef enum
{
FBINK_TARGET_LINUX = 0U,
FBINK_TARGET_KOBO,
FBINK_TARGET_KINDLE,
FBINK_TARGET_KINDLE_LEGACY,
FBINK_TARGET_CERVANTES,
FBINK_TARGET_REMARKABLE,
FBINK_TARGET_POCKETBOOK,
FBINK_TARGET_MAX = UINT8_MAX,
} __attribute__((packed)) FBINK_TARGET_E;
typedef uint8_t FBINK_TARGET_T;
// Supported feature flags
#define FBINK_FEATURE_MINIMAL 0
#define FBINK_FEATURE_DRAW (1 << 0) // Basic draw primitives
#define FBINK_FEATURE_BITMAP (1 << 1) // Fixed-cell font rendering, plus the base IBM font
#define FBINK_FEATURE_FONTS (1 << 2) // The full set of fixed-cell fonts
#define FBINK_FEATURE_UNIFONT (1 << 3) // Unifont for the fixed-cell font rendering
#define FBINK_FEATURE_OPENTYPE (1 << 4) // TrueType/OpenType font rendering
#define FBINK_FEATURE_IMAGE (1 << 5) // Image support
#define FBINK_FEATURE_BUTTON_SCAN (1 << 6) // Button scan support (Kobo only, deprecated)
#define FBINK_FEATURE_INPUT (1 << 7) // Input utilities (e.g., fbink_input_scan)
#define FBINK_FEATURE_FULL \
(FBINK_FEATURE_DRAW | FBINK_FEATURE_BITMAP | FBINK_FEATURE_FONTS | FBINK_FEATURE_OPENTYPE | \
FBINK_FEATURE_IMAGE | FBINK_FEATURE_INPUT)
// List of available fonts
typedef enum
{
IBM = 0U, // font8x8
UNSCII, // unscii-8
UNSCII_ALT, // unscii-8-alt
UNSCII_THIN, // unscii-8-thin
UNSCII_FANTASY, // unscii-8-fantasy
UNSCII_MCR, // unscii-8-mcr
UNSCII_TALL, // unscii-16
BLOCK, // block
LEGGIE, // leggie (regular)
VEGGIE, // leggie EGA/VGA/FB
KATES, // kates (nexus)
FKP, // fkp
CTRLD, // ctrld
ORP, // orp (regular)
ORPB, // orp (bold)
ORPI, // orp (italic)
SCIENTIFICA, // scientifica (regular)
SCIENTIFICAB, // scientifica (bold)
SCIENTIFICAI, // scientifica (italic)
TERMINUS, // terminus (regular)
TERMINUSB, // terminus (bold)
FATTY, // fatty
SPLEEN, // spleen
TEWI, // tewi (medium)
TEWIB, // tewi (bold)
TOPAZ, // Topaz+ A1200
MICROKNIGHT, // MicroKnight+
VGA, // IBM VGA 8x16
UNIFONT, // Unifont (single-wide glyphs only)
UNIFONTDW, // Unifont (double-wide glyphs only)
COZETTE, // Cozette
FONT_MAX = UINT8_MAX, // uint8_t
} __attribute__((packed)) FONT_INDEX_E;
typedef uint8_t FONT_INDEX_T;
// List of supported font styles
typedef enum
{
FNT_REGULAR = 0U,
FNT_ITALIC,
FNT_BOLD,
FNT_BOLD_ITALIC,
} FONT_STYLE_E;
// NOTE: This one is not packed to avoid breaking ABI w/ fbink_add_ot_font
typedef int FONT_STYLE_T;
// List of available halign/valign values
typedef enum
{
NONE = 0U, // i.e., LEFT for halign, TOP for valign
CENTER, //
EDGE, // i.e., RIGHT for halign, BOTTOM for valign
ALIGN_MAX = UINT8_MAX, // uint8_t
} __attribute__((packed)) ALIGN_INDEX_E;
typedef uint8_t ALIGN_INDEX_T;
// List of available padding values
typedef enum
{
NO_PADDING = 0U,
HORI_PADDING,
VERT_PADDING,
FULL_PADDING,
MAX_PADDING = UINT8_MAX, // uint8_t
} __attribute__((packed)) PADDING_INDEX_E;
typedef uint8_t PADDING_INDEX_T;
// List of available colors in the eInk color map
// NOTE: This is split in FG & BG to ensure that the default values lead to a sane result (i.e., black on white)
typedef enum
{
FG_BLACK = 0U, // 0x00
FG_GRAY1, // 0x11
FG_GRAY2, // 0x22
FG_GRAY3, // 0x33
FG_GRAY4, // 0x44
FG_GRAY5, // 0x55
FG_GRAY6, // 0x66
FG_GRAY7, // 0x77
FG_GRAY8, // 0x88
FG_GRAY9, // 0x99
FG_GRAYA, // 0xAA
FG_GRAYB, // 0xBB
FG_GRAYC, // 0xCC
FG_GRAYD, // 0xDD
FG_GRAYE, // 0xEE
FG_WHITE, // 0xFF
FG_MAX = UINT8_MAX, // uint8_t
} __attribute__((packed)) FG_COLOR_INDEX_E;
typedef uint8_t FG_COLOR_INDEX_T;
typedef enum
{
BG_WHITE = 0U,
BG_GRAYE,
BG_GRAYD,
BG_GRAYC,
BG_GRAYB,
BG_GRAYA,
BG_GRAY9,
BG_GRAY8,
BG_GRAY7,
BG_GRAY6,
BG_GRAY5,
BG_GRAY4,
BG_GRAY3,
BG_GRAY2,
BG_GRAY1,
BG_BLACK,
BG_MAX = UINT8_MAX, // uint8_t
} __attribute__((packed)) BG_COLOR_INDEX_E;
typedef uint8_t BG_COLOR_INDEX_T;
// List of Cervantes device IDs (HWConfig PCB index)
typedef enum
{
DEVICE_CERVANTES_TOUCH = 22U,
DEVICE_CERVANTES_TOUCHLIGHT = 23U,
DEVICE_CERVANTES_2013 = 33U,
DEVICE_CERVANTES_3 = 51U,
DEVICE_CERVANTES_4 = 68U,
DEVICE_CERVANTES_MAX = UINT16_MAX, // uint16_t
} __attribute__((packed)) CERVANTES_DEVICE_ID_E;
// List of Kobo device IDs
typedef enum
{
DEVICE_KOBO_TOUCH_A = 300U, // Not an actual Kobo ID, Nickel bundles it with the B
DEVICE_KOBO_TOUCH_B = 310U,
DEVICE_KOBO_TOUCH_C = 320U,
DEVICE_KOBO_MINI = 340U,
DEVICE_KOBO_GLO = 330U,
DEVICE_KOBO_GLO_HD = 371U,
DEVICE_TOLINO_SHINE_2HD = DEVICE_KOBO_GLO_HD + 300U,
DEVICE_KOBO_TOUCH_2 = 372U,
DEVICE_KOBO_AURA = 360U,
DEVICE_KOBO_AURA_HD = 350U,
DEVICE_KOBO_AURA_H2O = 370U,
DEVICE_KOBO_AURA_H2O_2 = 374U,
DEVICE_KOBO_AURA_H2O_2_R2 = 378U,
DEVICE_KOBO_AURA_ONE = 373U,
DEVICE_KOBO_AURA_ONE_LE = 381U,
DEVICE_KOBO_AURA_SE = 375U,
DEVICE_TOLINO_VISION = DEVICE_KOBO_AURA_SE + 300U,
DEVICE_KOBO_AURA_SE_R2 = 379U,
DEVICE_KOBO_CLARA_HD = 376U,
DEVICE_TOLINO_SHINE_3 = DEVICE_KOBO_CLARA_HD + 300U,
DEVICE_KOBO_FORMA = 377U,
DEVICE_TOLINO_EPOS_2 = DEVICE_KOBO_FORMA + 300U,
DEVICE_KOBO_FORMA_32GB = 380U,
DEVICE_KOBO_LIBRA_H2O = 384U,
DEVICE_TOLINO_VISION_5 = DEVICE_KOBO_LIBRA_H2O + 300U,
DEVICE_KOBO_NIA = 382U,
DEVICE_KOBO_ELIPSA = 387U,
DEVICE_KOBO_LIBRA_2 = 388U,
DEVICE_KOBO_SAGE = 383U,
DEVICE_TOLINO_EPOS_3 = DEVICE_KOBO_SAGE + 300U,
DEVICE_KOBO_CLARA_2E = 386U,
DEVICE_KOBO_ELIPSA_2E = 389U,
DEVICE_KOBO_LIBRA_COLOUR = 390U,
DEVICE_TOLINO_VISION_COLOR = DEVICE_KOBO_LIBRA_COLOUR + 300U,
DEVICE_KOBO_CLARA_BW = 391U,
DEVICE_TOLINO_SHINE_BW = DEVICE_KOBO_CLARA_BW + 300U,
DEVICE_KOBO_CLARA_COLOUR = 393U,
DEVICE_TOLINO_SHINE_COLOR = DEVICE_KOBO_CLARA_COLOUR + 300U,
DEVICE_KOBO_MAX = UINT16_MAX, // uint16_t
} __attribute__((packed)) KOBO_DEVICE_ID_E;
// List of device IDs for mainline kernels
// c.f., https://github.com/NiLuJe/FBInk/issues/70#issuecomment-1242274710 for Tolinos
typedef enum
{
DEVICE_MAINLINE_TOLINO_SHINE_2HD = ('T' << 8U) | ('o' << 8U) | ('l' << 8U) | DEVICE_KOBO_GLO_HD,
DEVICE_MAINLINE_TOLINO_SHINE_3 = ('T' << 8U) | ('o' << 8U) | ('l' << 8U) | DEVICE_KOBO_CLARA_HD,
DEVICE_MAINLINE_TOLINO_VISION = ('T' << 8U) | ('o' << 8U) | ('l' << 8U) | DEVICE_KOBO_AURA_SE,
DEVICE_MAINLINE_TOLINO_VISION_5 = ('T' << 8U) | ('o' << 8U) | ('l' << 8U) | DEVICE_KOBO_LIBRA_H2O,
DEVICE_MAINLINE_GENERIC_IMX5 = ('i' << 8U) | ('.' << 8U) | 'M' | 'X' | '5',
DEVICE_MAINLINE_GENERIC_IMX6 = ('i' << 8U) | ('.' << 8U) | 'M' | 'X' | '6',
DEVICE_MAINLINE_GENERIC_SUNXI_B300 = ('A' << 8U) | ('W' << 8U) | 'B' | '3' | '0' | '0',
DEVICE_MAINLINE_MAX = UINT16_MAX, // uint16_t
} __attribute__((packed)) MAINLINE_DEVICE_ID_E;
// List of reMarkable device IDs
typedef enum
{
DEVICE_REMARKABLE_1 = 1U,
DEVICE_REMARKABLE_2 = 2U,
DEVICE_REMARKABLE_MAX = UINT16_MAX, // uint16_t
} __attribute__((packed)) REMARKABLE_DEVICE_ID_E;
// List of PocketBook device IDs
typedef enum
{
DEVICE_POCKETBOOK_MINI = 515U,
DEVICE_POCKETBOOK_606 = 606U,
DEVICE_POCKETBOOK_611 = 611U,
DEVICE_POCKETBOOK_613 = 613U,
DEVICE_POCKETBOOK_614 = 614U,
DEVICE_POCKETBOOK_615 = 615U,
DEVICE_POCKETBOOK_616 = 616U,
DEVICE_POCKETBOOK_617 = 617U,
DEVICE_POCKETBOOK_618 = 618U,
DEVICE_POCKETBOOK_TOUCH = 622U,
DEVICE_POCKETBOOK_LUX = 623U,
DEVICE_POCKETBOOK_BASIC_TOUCH = 624U,
DEVICE_POCKETBOOK_BASIC_TOUCH_2 = 625U,
DEVICE_POCKETBOOK_LUX_3 = 626U,
DEVICE_POCKETBOOK_LUX_4 = 627U,
DEVICE_POCKETBOOK_LUX_5 = 628U,
DEVICE_POCKETBOOK_VERSE = 629U,
DEVICE_POCKETBOOK_SENSE = 630U,
DEVICE_POCKETBOOK_TOUCH_HD = 631U,
DEVICE_POCKETBOOK_TOUCH_HD_PLUS = 632U,
DEVICE_POCKETBOOK_COLOR = 633U,
DEVICE_POCKETBOOK_VERSE_PRO = 634U,
DEVICE_POCKETBOOK_VERSE_PRO_COLOR = 634U | 'K',
DEVICE_POCKETBOOK_AQUA = 640U,
DEVICE_POCKETBOOK_AQUA2 = 641U,
DEVICE_POCKETBOOK_ULTRA = 650U,
DEVICE_POCKETBOOK_ERA = 700U,
DEVICE_POCKETBOOK_ERA_COLOR = 700U | 'K',
DEVICE_POCKETBOOK_INKPAD_3 = 740U,
DEVICE_POCKETBOOK_INKPAD_3_PRO = 742U,
DEVICE_POCKETBOOK_INKPAD_COLOR = 741U,
DEVICE_POCKETBOOK_INKPAD_COLOR_2 = 743U | 'C',
DEVICE_POCKETBOOK_INKPAD_COLOR_3 = 743U | 'K',
DEVICE_POCKETBOOK_INKPAD = 840U,
DEVICE_POCKETBOOK_INKPAD_X = 1040U,
DEVICE_POCKETBOOK_INKPAD_4 = 743U | 'G',
DEVICE_POCKETBOOK_COLOR_LUX =
('C' << 8U) | ('o' << 8U) | ('l' << 8U) | ('o' << 8U) | ('r' << 8U) | 'L' | 'u' | 'x',
DEVICE_POCKETBOOK_INKPAD_LITE = 970U,
DEVICE_POCKETBOOK_MAX = UINT16_MAX, // uint16_t
} __attribute__((packed)) POCKETBOOK_DEVICE_ID_E;
// If device detection failed...
#define DEVICE_UNKNOWN 0U
// NOTE: There's no enum for Kindles, because there are an insane number of device IDs per model,
// so it doesn't really fit into this model. Use the deviceName instead.
typedef uint16_t DEVICE_ID_T;
// List of *potentially* available waveform modes.
// NOTE: On mxcfb EPDC v1 (as well as all Kindle) & most MTK devices, REAGL & REAGLD generally expect to *always* be flashing.
// The same is also true for Kaleido-specific modes (GCC16 & GLRC16).
// This is currently left at your own discretion, though.
// c.f., https://github.com/NiLuJe/FBInk/commit/32acece78f7cc92b06faa4a668feead260b8ce24
// See also the comments around the relevant refresh_* functions in fbink.c
// NOTE: On very old devices (e.g., Kobo Mk. 3 & 4; possibly early PB), only AUTO, DU & GC16 may be relied on.
// GC4 will probably behave, but A2 & GL16 are not a given at all:
// e.g., GL16 is actively broken on Kobo <= Mk. 4: c.f., https://github.com/baskerville/plato/issues/158#issuecomment-787520759.
// If a waveform mode produces unexpected/broken results, and/or if you start to hit unexpected EPDC timeouts (or even an OOPS),
// that's usually a strong hint that you're trying to use something you shouldn't ;).
// NOTE: See the various mxcfb headers in the eink folder for more details about what's available on your platform.
// Platform-specific quirks, if any, are also commented upon in the relevant refresh_* functions in fbink.c
// NOTE: If you're curious about how to deal with all this stuff in practice for real world use-cases,
// I'd recommend looking at my various comments in the KOReader backends for some more context,
// c.f., https://github.com/koreader/koreader-base/blob/master/ffi/framebuffer_mxcfb.lua
typedef enum
{
WFM_AUTO = 0U, // Let the EPDC choose, via histogram analysis of the refresh region.
// May *not* always (or ever) opt to use REAGL on devices where it is otherwise available.
// This is the default.
// If you request a flashing update w/ AUTO, FBInk automatically uses GC16 instead.
// NOTE: On sunxi SoCs, this analysis is done on CPU, instead of by the PxP.
// As such, it's going to be slower. Prefer explicitly choosing a mode instead.
// (When in doubt, GL16 is usually a good middle ground).
// Common
WFM_DU, // From any to B&W, fast (~260ms), some light ghosting.
// On-screen pixels will be left as-is for new content that is *not* B&W.
// Great for UI highlights, or tracing touch/pen input.
// Will never flash.
// DU stands for "Direct Update".
WFM_GC16, // From any to any, ~450ms, high fidelity (i.e., lowest risk of ghosting).
// Ideal for image content.
// If flashing, will flash and update the full region.
// If not, only changed pixels will update.
// GC stands for "Grayscale Clearing"
WFM_GC4, // From any to B/W/GRAYA/GRAY5, (~290ms), some ghosting. (may be implemented as DU4 on some devices).
// Will *probably* never flash, especially if the device doesn't implement any other 4 color modes.
// Limited use-cases in practice.
WFM_A2, // From B&W to B&W, fast (~120ms), some ghosting.
// On-screen pixels will be left as-is for new content that is *not* B&W.
// FBInk will ask the EPDC to enforce quantization to B&W to honor the "to" requirement,
// (via EPDC_FLAG_FORCE_MONOCHROME).
// Will never flash.
// Consider bracketing a series of A2 refreshes between white screens to transition in and out of A2,
// so as to honor the "from" requirement,
// (especially given that FORCE_MONOCHROME may not be reliably able to do so, c.f., refresh_kobo_mk7):
// non-flashing GC16 for the in transition, A2 or GC16 for the out transition.
// A stands for "Animation"
WFM_GL16, // From white to any, ~450ms, some ghosting.
// Typically optimized for text on a white background.
// Newer generation devices only
WFM_REAGL, // From white to any, ~450ms, with ghosting and flashing reduction.
// When available, best option for text (in place of GL16).
// May enforce timing constraints if in collision with another waveform mode, e.g.,
// it may, to some extent, wait for completion of previous updates to have access to HW resources.
// Marketing term for the feature is "Regal". Technically called 5-bit waveform modes.
WFM_REAGLD, // From white to any, ~450ms, with more ghosting reduction, but less flashing reduction.
// Should only be used when flashing, which should yield a less noticeable flash than GC16.
// Rarely used in practice, because still optimized for text or lightly mixed content,
// not pure image content.
// (Mostly) Kindle only
WFM_GC16_FAST, // Better latency at the expense of lower fidelity than GC16.
WFM_GL16_FAST, // Better latency at the expense of lower fidelity than GL16.
WFM_DU4, // From any to B/W/GRAYA/GRAY5. (e.g., GC4. Will never flash. Also available on Kobo Mk. 9).
WFM_GL4, // From white to B/W/GRAYA/GRAY5.
WFM_GL16_INV, // From black to any. Optimized for text on a black background (e.g., nightmode).
// "Nightmode" waveform modes (dubbed "eclipse" in Kobo-land).
// Only available on some devices (Zelda on Kindle, Mk. 8+ on Kobo).
// If you need to check at runtime whether it's actually supported, on an i.MX board,
// check if /sys/class/graphics/fb0/waveform_mode_gck16 exists ;).
// Otherwise, refer to the hasEclipseWfm deviceQuirks.
WFM_GCK16, // From black to any. Goes hand-in-hand with GLKW16, should only be used when flashing.
WFM_GLKW16, // From black to any. Newer variant of GL16_INV. (On Kobo, Mk. 9, 11 & 12 only. It's GLK16 on sunxi).
// For documentation purposes
WFM_INIT, // May flash several times to end up with a white screen, slow (~2000ms).
WFM_UNKNOWN,
// reMarkable only
WFM_INIT2,
// PocketBook only
WFM_A2IN,
WFM_A2OUT,
WFM_GC16HQ, // Only available on i.MX SoCs. Alias for REAGL, or REAGLD when flashing.
WFM_GS16, // Only available on B288 SoCs. Fidelity supposedly somewhere between GL16 and GC16.
// Kobo Sunxi only
WFM_GU16, // GL16, but honoring the in-kernel DISP_EINK_SET_GC_CNT
//WFM_GCK16, // GC16, but for white-on-black.
WFM_GLK16, // GL16, but for white-on-black.
WFM_CLEAR, // GC16 local (NOTE: Appears to crash the EPDC... [Elipsa on FW 4.28.17826])
WFM_GC4L, // GC4 local (NOTE: Appears to crash the EPDC... [Elipsa on FW 4.28.17826])
// Kobo Sunxi & MTK only
WFM_GCC16, // GCC16, for color image content on Kaleido panels.
WFM_GLRC16, // GLRC16, for color highlights on text on Kaleido panels.
// Kindle MTK only
WFM_GC16_PARTIAL, // Internal use only, GC16 + PARTIAL
WFM_GCK16_PARTIAL, // Internal use only, GCK16 + PARTIAL
WFM_DUNM, // DU, but for white-on-black.
WFM_P2SW, // Internal use only, used by the swipe animation.
WFM_MAX = UINT8_MAX, // uint8_t
} __attribute__((packed)) WFM_MODE_INDEX_E;
typedef uint8_t WFM_MODE_INDEX_T;
// List of *potentially* available HW dithering modes
typedef enum
{
HWD_PASSTHROUGH = 0U,
HWD_FLOYD_STEINBERG,
HWD_ATKINSON,
HWD_ORDERED, // Generally the only supported HW variant on EPDC v2
HWD_QUANT_ONLY,
HWD_LEGACY = UINT8_MAX, // Use legacy EPDC v1 dithering instead (if available).
// Note that it is *not* offloaded to the PxP, it's purely software, in-kernel.
// Usually based on Atkinson's algo. The most useful one being the Y8->Y1 one,
// which we request with A2/DU refreshes.
} __attribute__((packed)) HW_DITHER_INDEX_E;
typedef uint8_t HW_DITHER_INDEX_T;
// List of *potentially* available CFA post-process modes
typedef enum
{
CFA_DEFAULT = 0U,
CFA_AIE_S4,
CFA_AIE_S7,
CFA_AIE_S9,
CFA_G0,
CFA_G1,
CFA_G2,
CFA_NTX,
CFA_NTX_SF,
CFA_SKIP,
CFA_MAX = UINT8_MAX,
} __attribute__((packed)) CFA_MODE_INDEX_E;
typedef uint8_t CFA_MODE_INDEX_T;
// List of NTX rotation quirk types (c.f., mxc_epdc_fb_check_var @ drivers/video/fbdev/mxc/mxc_epdc_v2_fb.c)...
typedef enum
{
NTX_ROTA_STRAIGHT = 0U, // No shenanigans (at least as far as ioctls are concerned)
NTX_ROTA_ALL_INVERTED, // Every rotation is inverted by the kernel
NTX_ROTA_ODD_INVERTED, // Only Landscape (odd) rotations are inverted by the kernel
// NOTE: Everything below this line should be considered mostly deprecated, as those were attempts to piggyback on this flag
// to handle input translations. That was a mistake, we've since switched to dedicated flags for touch input quirks.
// As the comment above implies, this is only meant to deal with the kernel screwing with us when setting the fb rotation.
NTX_ROTA_SANE, // NTX_ROTA_STRAIGHT, and ntxBootRota is the native Portrait orientation.
// Optionally, bonus points if that's actually UR, and the panel is natively mounted UR,
// like on the Kobo Libra.
// Triple whammy if the touch layer rotation matches!
NTX_ROTA_SUNXI, // The rotate flag is technically meaningless, but *may* be set by third-party code (we don't).
NTX_ROTA_CW_TOUCH, // No kernel shenanigans, and Touch panel mounted in the invert of the usual rotation.
NTX_ROTA_CCW_TOUCH, // No kernel shenanigans, and Touch panel mounted in the usual rotation.
NTX_ROTA_MAX = UINT8_MAX, // uint8_t
} __attribute__((packed)) NTX_ROTA_INDEX_E;
typedef uint8_t NTX_ROTA_INDEX_T;
// Available states for fbink_sunxi_ntx_enforce_rota
typedef enum
{
FORCE_ROTA_NOTSUP = INT8_MIN, // For FBInkState on non-sunxi platforms
FORCE_ROTA_CURRENT_ROTA =
-5, // Honor the gyro if it matches the working buffer's rotation; match the wb otherwise (NOTE: Requires fbdamage)
FORCE_ROTA_CURRENT_LAYOUT =
-4, // Honor the gyro if it matches the working buffer's layout; match the wb otherwise (NOTE: Requires fbdamage)
FORCE_ROTA_PORTRAIT = -3, // Honor the gyro if it matches a Portrait layout
FORCE_ROTA_LANDSCAPE = -2, // Honor the gyro if it matches a Landscape layout
FORCE_ROTA_GYRO = -1, // Honor the gyro (NOTE: default)
FORCE_ROTA_UR = 0, // FB_ROTATE_UR
FORCE_ROTA_CW = 1, // FB_ROTATE_CW
FORCE_ROTA_UD = 2, // FB_ROTATE_UD
FORCE_ROTA_CCW = 3, // FB_ROTATE_CCW
FORCE_ROTA_WORKBUF = 4, // Match the working buffer's rotation (NOTE: Requires fbdamage)
FORCE_ROTA_MAX = INT8_MAX, // int8_t
} __attribute__((packed)) SUNXI_FORCE_ROTA_INDEX_E;
typedef int8_t SUNXI_FORCE_ROTA_INDEX_T;
// List of swipe directions for fbink_mtk_set_swipe_data
typedef enum
{
MTK_SWIPE_DIR_DOWN = 0,
MTK_SWIPE_DIR_UP = 1,
MTK_SWIPE_DIR_LEFT = 2,
MTK_SWIPE_DIR_RIGHT = 3,
MTK_SWIPE_DIR_MAX = UINT8_MAX, // uint8_t
} __attribute__((packed)) MTK_SWIPE_DIRECTION_INDEX_E;
typedef uint8_t MTK_SWIPE_DIRECTION_INDEX_T;
// List of halftone pattern modes for fbink_mtk_set_halftone
typedef enum
{
MTK_HALFTONE_DISABLED = 0,
MTK_HALFTONE_DEFAULT_CHECKER_SIZE = 1,
MTK_HALFTONE_MAX_CHECKER_SIZE = INT32_MAX, // int
} __attribute__((packed)) MTK_HALFTONE_MODE_INDEX_E;
typedef int32_t MTK_HALFTONE_MODE_INDEX_T;
// List of supported pixel formats
typedef enum
{
FBINK_PXFMT_UNKNOWN = 0,
FBINK_PXFMT_Y4 = 4,
FBINK_PXFMT_Y8 = 8,
FBINK_PXFMT_BGR565 = 16,
FBINK_PXFMT_RGB565,
FBINK_PXFMT_BGR24 = 24,
FBINK_PXFMT_RGB24,
FBINK_PXFMT_BGRA = 32,
FBINK_PXFMT_RGBA,
FBINK_PXFMT_BGR32,
FBINK_PXFMT_RGB32,
FBINK_PXFMT_MAX = UINT8_MAX, // uint8_t
} __attribute__((packed)) FBINK_PXFMT_INDEX_E;
typedef uint8_t FBINK_PXFMT_INDEX_T;
//
// A struct to dump FBInk's internal state into, like fbink_state_dump() would, but in C ;)
typedef struct
{
long int user_hz; // USER_HZ (should pretty much always be 100)
const char* restrict font_name; // fbink_cfg->fontname (c.f., fontname_to_string())
uint32_t view_width; // viewWidth (MAY be different than screen_width on devices with a viewport)
uint32_t view_height; // viewHeight (ditto)
uint32_t screen_width; // screenWidth (Effective width, c.f., is_ntx_quirky_landscape & initialize_fbink())
uint32_t screen_height; // screenHeight (ditto)
uint32_t scanline_stride; // fInfo.line_length (scanline length in bytes, padding included)
uint32_t bpp; // vInfo.bits_per_pixel
bool inverted_grayscale; // true if vInfo.grayscale is set to GRAYSCALE_8BIT_INVERTED (@ 8bpp)
char device_name[32]; // deviceQuirks.deviceName (short common name, no brand)
char device_codename[32]; // deviceQuirks.deviceCodename
char device_platform[32]; // deviceQuirks.devicePlatform (often a codename, too)
DEVICE_ID_T device_id; // deviceQuirks.deviceId (decimal value, c.f., identify_device() on Kindle!)
uint8_t pen_fg_color; // penFGColor (Actual grayscale value, not FG_COLOR_INDEX_E)
uint8_t pen_bg_color; // penBGColor (ditto)
unsigned short int screen_dpi; // deviceQuirks.screenDPI
unsigned short int font_w; // FONTW (effective width of a glyph cell, i.e. scaled)
unsigned short int font_h; // FONTH (effective height of a glyph cell, i.e. scaled)
unsigned short int max_cols; // MAXCOLS (at current cell size)
unsigned short int max_rows; // MAXROWS (ditto)
uint8_t view_hori_origin; // viewHoriOrigin (would be non-zero on devices with a horizontal viewport)
uint8_t view_vert_origin; // viewVertOrigin (origin in px of row 0, includes viewport + viewVertOffset)
uint8_t view_vert_offset; // viewVertOffset (shift in px needed to vertically balance rows over viewHeight)
uint8_t fontsize_mult; // FONTSIZE_MULT (current cell scaling multiplier)
uint8_t glyph_width; // glyphWidth (native width of a glyph cell, i.e. unscaled)
uint8_t glyph_height; // glyphHeight (native height of a glyph cell, i.e. unscaled)
bool is_perfect_fit; // deviceQuirks.isPerfectFit (horizontal column balance is perfect over viewWidth)
bool is_mtk; // deviceQuirks.isMTK (device is running on a MediaTek SoC)
bool is_sunxi; // deviceQuirks.isSunxi (device is running on an AllWinner SoC)
bool sunxi_has_fbdamage; // sunxiCtx.has_fbdamage (true when fbdamage module is loaded)
SUNXI_FORCE_ROTA_INDEX_T sunxi_force_rota; // sunxiCtx.force_rota (current effective value)
bool is_kindle_legacy; // deviceQuirks.isKindleLegacy (device is a Kindle using the original einkfb EPDC API)
bool is_kobo_non_mt; // deviceQuirks.isKoboNonMT (device is a Kobo with no MultiTouch input support)
bool unreliable_wait_for; // deviceQuirks.unreliableWaitFor (MXCFB_WAIT_FOR_UPDATE_COMPLETE may timeout)
bool can_wake_epdc; // deviceQuirks.canWakeEPDC (i.e., fbink_wakeup_epdc is *NOT* a NOP)
uint8_t ntx_boot_rota; // deviceQuirks.ntxBootRota (Native rotation at boot)
NTX_ROTA_INDEX_T ntx_rota_quirk; // deviceQuirks.ntxRotaQuirk (c.f., tools/rota_map.c)
uint8_t rotation_map[4]; // deviceQuirks.rotationMap (index is native, value is canonical)
bool touch_swap_axes; // deviceQuirks.touchSwapAxes (panel reports swapped coordinates axes; handle this first)
bool touch_mirror_x; // deviceQuirks.touchMirrorX (post-swap, panel reports inverted x coordinates)
bool touch_mirror_y; // deviceQuirks.touchMirrorY (post-swap, panel reports inverted y coordinates)
bool is_ntx_quirky_landscape; // deviceQuirks.isNTX16bLandscape (rotation compensation is in effect)
uint8_t current_rota; // vInfo.rotate (current native rotation, <linux/fb.h>; Device rotation, not buffer!)
bool can_rotate; // deviceQuirks.canRotate (device has a gyro)
bool can_hw_invert; // deviceQuirks.canHWInvert (device can use EPDC inversion)
bool has_eclipse_wfm; // deviceQuirks.hasEclipseWfm (device can use nightmode waveform modes)
bool has_color_panel; // deviceQuirks.hasColorPanel (device features a Kaleido/CFA color panel)
FBINK_PXFMT_INDEX_T pixel_format; // deviceQuirks.pixelFormat
bool can_wait_for_submission; // deviceQuirks.canWaitForSubmission (devices supports fbink_wait_for_submission)
} FBInkState;
// What a FBInk config should look like. Perfectly sane when fully zero-initialized.
typedef struct
{
short int row; // y axis (i.e., line), counts down from the bottom of the screen if negative
short int col; // x axis (i.e., column), counts down from the right edge of the screen if negative
uint8_t fontmult; // Font scaling multiplier (i.e., 4 -> x4), 0 means automatic.
FONT_INDEX_T fontname; // Request a specific bundled font
bool is_inverted; // Invert colors when *drawing* stuff (e.g., text, clear & images).
// This is *NOT* mutually exclusive with is_nightmode, and is *always* supported.
bool is_flashing; // Request a black flash on refresh (e.g., UPDATE_MODE_FULL instead of PARTIAL)
bool is_cleared; // Clear the full screen beforehand (honors bg_color & is_inverted)
bool is_centered; // Center the text (horizontally)
short int hoffset; // Horizontal offset (in pixels) for text position
short int voffset; // Vertical offset (in pixels) for text position
bool is_halfway; // Vertically center the text, honoring row offsets
bool is_padded; // Pad the text with blanks (on the left, or on both sides if is_centered)
bool is_rpadded; // Right pad the text with blanks
FG_COLOR_INDEX_T fg_color; // Requested foreground color for text (palette index)
BG_COLOR_INDEX_T bg_color; // Requested background color for text (palette index)
bool is_overlay; // Don't draw bg and use inverse of fb's underlying pixel as pen fg color
bool is_bgless; // Don't draw bg (mutually exclusive with is_overlay, which will take precedence)
bool is_fgless; // Don't draw fg (takes precendence over is_overlay/is_bgless)
bool no_viewport; // Ignore viewport corrections, whether hardware-related on Kobo, or to center rows
bool is_verbose; // Print verbose diagnostic informations on stdout
bool is_quiet; // Hide fbink_init()'s hardware setup info (sent to stderr)
bool ignore_alpha; // Ignore any potential alpha channel in source image (i.e., flatten the image)
ALIGN_INDEX_T halign; // Horizontal alignment of images/dumps
ALIGN_INDEX_T valign; // Vertical alignment of images/dumps
short int scaled_width; // Output width of images/dumps (0 for no scaling, -1 for viewport width)
short int scaled_height; // Output height of images/dumps (0 for no scaling, -1 for viewport height)
// If only *one* of them is left at 0, the image's aspect ratio will be honored.
// If *either* of them is set to < -1, fit to screen while respecting AR.
// NOTE: Scaling is inherently costly. I highly recommend not relying on it,
// preferring instead proper preprocessing of your input images,
// c.f., https://www.mobileread.com/forums/showpost.php?p=3728291&postcount=17
WFM_MODE_INDEX_T wfm_mode; // Request a specific waveform mode (defaults to AUTO)
HW_DITHER_INDEX_T dithering_mode; // Request a specific dithering mode (defaults to PASSTHROUGH)
bool sw_dithering; // Request (ordered) *software* dithering when printing an image.
// This is *NOT* mutually exclusive with dithering_mode!
CFA_MODE_INDEX_T cfa_mode; // Request a specific CFA post-process mode (defaults to NONE, relevant wfm only).
bool is_nightmode; // Request hardware inversion (via EPDC_FLAG_ENABLE_INVERSION, if supported/safe).
// This is *NOT* mutually exclusive with is_inverted!
// NOTE: If the HW doesn't support inversion, a warning is printed during init.
// If you're convinced this is in error (i.e., up to date kernel),
// you can bypass that check by setting FBINK_ALLOW_HW_INVERT in your env.
bool no_refresh; // Skip actually refreshing the eInk screen (useful when drawing in batches)
bool no_merge; // Set the EINK_NO_MERGE flag (Kobo sunxi only)
bool is_animated; // Enable refresh animation, following fbink_mtk_set_swipe_data (Kindle MTK only)
uint8_t saturation_boost; // Boost image saturation, in %. Useful on Kaleido panels. Only affects 32bpp.
bool to_syslog; // Send messages & errors to the syslog instead of stdout/stderr
} FBInkConfig;
// Same, but for OT/TTF specific stuff. MUST be zero-initialized.
typedef struct
{
void* font; // NOTE: This is essentially a pointer to a local FBInkOTFonts instance,
// in order to use a set of fonts specific to an FBInkOTConfig,
// via fbink_add_ot_font_v2() & fbink_free_ot_fonts_v2().
// Consider it *private*: it needs to be NULL on init to be sane, but after that,
// it's only used & memory managed by FBInk itself (via the aforemented _v2 API), not the user.
struct
{
short int top; // Top margin in pixels (if negative, counts backwards from the bottom edge)
short int bottom; // Bottom margin in pixels (supports negative values, too)
short int left; // Left margin in pixels (if negative, counts backwards from the right edge)
short int right; // Right margin in pixels (supports negative values, too)
} margins; // Margins are to the top-left edge of the bounding box, e.g., assuming no alignment/centering,
// zero margins will lead to a bounding box flush with the top-left corner of the screen.
FONT_STYLE_T style; // Default font style to use when !is_formatted (defaults to Regular)
float size_pt; // Size of text in points. If not set (0.0f), defaults to 12pt
unsigned short int size_px; // Size of text in pixels. Optional, but takes precedence over size_pt.
bool is_centered; // Horizontal centering
PADDING_INDEX_T padding; // Pad the drawing area (i.e., paint it in the background color).
// Unlike in the fixed-cell codepath, this always applies to both sides (L&R/T&B),
// no matter the chosen axis.
// e.g., HORI_PADDING is useful to prevent overlaps when drawing
// consecutive strings on the same line(s).
bool is_formatted; // Is string "formatted"? Bold/Italic support only, markdown like syntax
bool compute_only; // Abort early after the line-break computation pass (no actual rendering).
// NOTE: This is early enough that it will *NOT* be able to predict *every*
// potential case of truncation.
// In particular, broken metrics may yield a late truncation at rendering time.
bool no_truncation; // Abort as early as possible (but not necessarily before the rendering pass),
// if the string cannot fit in the available area at the current font size.
} FBInkOTConfig;
// Optionally used with fbink_print_ot, if you need more details about the line-breaking computations,
// for instance if you want to dynamically compute a best-fit font size for n lines in a specific area.
typedef struct
{
unsigned short int computed_lines; // Expected amount of lines needed, according to font metrics.
unsigned short int rendered_lines; // Actually rendered amount of lines.
// Will stay 0 in case of an early abort (or a compute_only run),
// or < computed_lines in case of an unexpected truncation due to broken metrics.
struct
{
unsigned short int width;
unsigned short int height;
} bbox; // Bounding box of the string (at computation time, padding excluded).
bool truncated; // true if the string was truncated (at computation or rendering time).
} FBInkOTFit;
// This maps to an mxcfb rectangle, used for fbink_get_last_rect, as well as in FBInkDump
// NOTE: Unlike an mxcfb rectangle, left (x) comes *before* top (y)!
typedef struct
{
unsigned short int left; // x
unsigned short int top; // y
unsigned short int width;
unsigned short int height;
} FBInkRect;
// For use with fbink_dump & fbink_restore
typedef struct
{
unsigned char* restrict data;
size_t stride;
size_t size;
FBInkRect area;
FBInkRect clip; // Only restore this rectangular area of the screen (has to intersect w/ the dump's area)
uint8_t rota;
uint8_t bpp;
bool is_full;
} FBInkDump;
//
////
//
// NOTE: Unless otherwise specified,
// stuff returns a negative value (-(EXIT_FAILURE) by default) on failure & EXIT_SUCCESS otherwise ;).
// Returns the version of the currently loaded FBInk library.
FBINK_API const char* fbink_version(void) __attribute__((const));
// Returns the target platform of the currently loaded FBInk library.
// c.f., FBINK_TARGET_E enum
FBINK_API FBINK_TARGET_T fbink_target(void) __attribute__((const));
// Returns a bitmask of the features available in the currently loaded FBInk library.
// c.f., FBINK_FEATURE_ defines
FBINK_API uint32_t fbink_features(void) __attribute__((const));
//
// Open the framebuffer character device,
// and returns the newly opened file descriptor.
FBINK_API int fbink_open(void);
// Unmap the framebuffer (if need be) and close its file descriptor,
// (c.f., the recap at the bottom if you're concerned about mmap handling).
// fbfd: Open file descriptor to the framebuffer character device, as returned by fbink_open()
// NOTE: This is safe to call if fbfd is FBFD_AUTO (i.e., -1, which means this is also safe to call after an fbink_open failure).
FBINK_API int fbink_close(int fbfd);
// Initialize internal variables keeping track of the framebuffer's configuration and state, as well as the device's hardware.
// MUST be called at least *once* before any fbink_print*, fbink_dump/restore, fbink_cls or fbink_grid* functions.
// CAN safely be called multiple times,
// but doing so is only necessary if the framebuffer's state has changed (although fbink_reinit is preferred in this case),
// or if you modified one of the FBInkConfig fields that affects its results (listed below).
// Returns -(ENOSYS) if the device is unsupported (NOTE: Only on reMarkable!)
// fbfd: Open file descriptor to the framebuffer character device,
// if set to FBFD_AUTO, the fb is opened for the duration of this call.
// fbink_cfg: Pointer to an FBInkConfig struct.
// If you wish to customize them, the fields:
// is_centered, fontmult, fontname, fg_color, bg_color,
// no_viewport, is_verbose, is_quiet & to_syslog
// MUST be set beforehand.
// This means you MUST call fbink_init() again when you update them, too!
// (This also means the effects from those fields "stick" across the lifetime of your application,
// or until a subsequent fbink_init() (or effective fbink_reinit()) call gets fed different values).
// NOTE: For fg_color & bg_color, see fbink_update_pen_colors().
// NOTE: For is_verbose, is_quiet & to_syslog, see fbink_update_verbosity().
// NOTE: By virtue of, well, setting global variables, do NOT consider this thread-safe.
// The rest of the API should be, though, so make sure you init in your main thread *before* threading begins...
// NOTE: If you just need to make sure the framebuffer state is still up to date before an fbink_* call,
// (e.g., because you're running on a Kobo, which may switch from 16bpp to 32bpp, or simply change orientation),
// prefer using fbink_reinit instead of calling fbink_init *again*, as it's tailored for this use case.
// c.f., KFMon for an example of this use case in the wild.
// NOTE: You can perfectly well keep a few different FBInkConfig structs around, instead of modifying the same one over and over.
// Just remember that some fields *require* an fbink_init() call to be taken into account (see above),
// but if the only fields that differ don't fall into that category, you do *NOT* need an fbink_init() per FBInkConfig...
FBINK_API int fbink_init(int fbfd, const FBInkConfig* restrict fbink_cfg) __attribute__((nonnull));
//
// Dump a few of our internal state variables to stdout, in a format easily consumable by a shell (i.e., eval).
FBINK_API void fbink_state_dump(const FBInkConfig* restrict fbink_cfg) __attribute__((nonnull));
// Dump a few of our internal state variables to the FBInkState struct pointed to by fbink_state.
// NOTE: This includes quite a few useful things related to device identification, c.f., the FBInkState struct ;).
// You can also peek at the output of fbink -e to get a hint of what the data actually looks like.
FBINK_API void fbink_get_state(const FBInkConfig* restrict fbink_cfg, FBInkState* restrict fbink_state)
__attribute__((nonnull));
//
// Print a string on screen.
// NOTE: The string is expected to be encoded in valid UTF-8:
// * Invalid UTF-8 sequences will be *rejected* and the call will abort early with -(EILSEQ)
// * We assume a single multibyte sequence will occupy a maximum of 4 bytes.
// c.f., my rant about Kobo's broken libc in fbink_internal.h for more details behind this choice.
// Since any decent system built in the last decade should default to UTF-8, that should be pretty much transparent...
// Returns the amount of lines printed on success (helpful when you keep track of which row you're printing to).
// Returns -(EINVAL) if string is empty.
// Returns -(EILSEQ) if string is not a valid UTF-8 sequence.
// Returns -(ENOSYS) when fixed-cell font support is disabled (MINIMAL build w/o BITMAP).
// fbfd: Open file descriptor to the framebuffer character device,
// if set to FBFD_AUTO, the fb is opened & mmap'ed for the duration of this call.
// string: UTF-8 encoded string to print.
// fbink_cfg: Pointer to an FBInkConfig struct.
// Honors every field not specifically related to image/dump support.
FBINK_API int fbink_print(int fbfd, const char* restrict string, const FBInkConfig* restrict fbink_cfg)
__attribute__((nonnull));
//
// Add an OpenType font to FBInk.
// NOTE: At least one font must be added in order to use fbink_print_ot().
// filename: Path to the font file. This should be a valid *.otf or *.ttf font.
// style: Defines the specific style of the specified font (FNT_REGULAR, FNT_ITALIC, FNT_BOLD or FNT_BOLD_ITALIC).
// NOTE: You MUST free the fonts loaded when you are done with all of them by calling fbink_free_ot_fonts().
// NOTE: You MAY replace a font without first calling fbink_free_ot_fonts().
// NOTE: Default fonts are secreted away in /usr/java/lib/fonts on Kindle,
// and in /usr/local/Trolltech/QtEmbedded-4.6.2-arm/lib/fonts on Kobo,
// but you can't use the Kobo ones because they're obfuscated...
// Which leads me to a final, critical warning:
// NOTE: Don't try to pass non-font files or encrypted/obfuscated font files, because it *will* horribly segfault!
FBINK_API int fbink_add_ot_font(const char* filename, FONT_STYLE_T style) __attribute__((nonnull));
// Same API and behavior, except that the set of fonts being loaded is tied to this specific FBInkOTConfig instance,
// instead of being global.
// In which case, resources MUST be released via fbink_free_ot_fonts_v2()!
// NOTE: You can mix & match the v2 and legacy API, but for every fbink_add_ot_font() there must be an fbink_free_ot_fonts(),
// and for every fbink_add_ot_font_v2(), there must be an fbink_free_ot_fonts_v2()
// (for each matching FBInkOTConfig instance).
FBINK_API int fbink_add_ot_font_v2(const char* filename, FONT_STYLE_T style, FBInkOTConfig* restrict cfg)
__attribute__((nonnull));
// Free all loaded OpenType fonts. You MUST call this when you have finished all OT printing.
// NOTE: Safe to call even if no fonts were actually loaded.
FBINK_API int fbink_free_ot_fonts(void);
// Same, but for a specific FBInkOTConfig instance if fbink_add_ot_font_v2 was used.
// NOTE: Safe to call even if no fonts were actually loaded, in which case it'll return -(EINVAL)!
FBINK_API int fbink_free_ot_fonts_v2(FBInkOTConfig* restrict cfg) __attribute__((nonnull));
// Print a string using an OpenType font.
// NOTE: The caller MUST have loaded at least one font via fbink_add_ot_font() FIRST.
// This function uses margins (in pixels) instead of rows/columns for positioning and setting the printable area.
// Returns a new top margin for use in subsequent calls, if the return value is positive.
// NOTE: A zero return value indicates there is no room left to print another row of text at the current margins or font size.
// Returns -(ERANGE) if the provided margins are out of range, or sum to < view height or width.
// Returns -(ENOSYS) when OT support is disabled (MINIMAL build w/o OPENTYPE).
// Returns -(ENODATA) if fbink_add_ot_font() hasn't been called yet.
// Returns -(EINVAL) if string is empty.
// Returns -(EILSEQ) if string is not a valid UTF-8 sequence.
// Returns -(ENOSPC) if no_truncation is true, and string needs to be truncated to fit in the available draw area.
// NOTE: This *cannot* prevent *drawing* truncated content on screen in *every* case,
// because broken metrics may skew our initial computations.
// As such, if the intent is to compute a "best fit" font size,
// no_truncation ought to be combined with no_refresh on eInk,
// (as we otherwise do *NOT* inhibit the refresh, in order to preserve get_last_rect's accuracy).
// You'll also probably want to do a cheaper compute_only pass first,
// to catch more obviously predictable truncations.
// fbfd: Open file descriptor to the framebuffer character device,
// if set to FBFD_AUTO, the fb is opened & mmap'ed for the duration of this call.
// string: UTF-8 encoded string to print.
// cfg: Pointer to an FBInkOTConfig struct.
// fbink_cfg: Optional pointer to an FBInkConfig struct. If set, the fields
// is_inverted, is_flashing, is_cleared, is_centered, is_halfway,
// is_overlay, is_fgless, is_bgless, fg_color, bg_color, valign, halign,
// wfm_mode, dithering_mode, is_nightmode, no_refresh will be honored.
// Pass a NULL pointer if unneeded.
// fit: Optional pointer to an FBInkOTFit struct.
// If set, it will be used to return information about the amount of lines needed to render
// the string at the requested font size, and whether it was truncated or not.
// Pass a NULL pointer if unneeded.
// NOTE: Alignment is relative to the printable area, as defined by the margins.
// As such, it only makes sense in the context of a single, specific print call.
FBINK_API int fbink_print_ot(int fbfd,
const char* restrict string,
const FBInkOTConfig* restrict cfg,
const FBInkConfig* restrict fbink_cfg,
FBInkOTFit* restrict fit) __attribute__((nonnull(2)));
//
// Brings printf formatting to fbink_print and fbink_print_ot ;).
// fbfd: Open file descriptor to the framebuffer character device,
// if set to FBFD_AUTO, the fb is opened & mmap'ed for the duration of this call.
// ot_cfg: Optional pointer to an FBInkOTConfig struct.
// fbink_cfg: Optional pointer to an FBInkConfig struct.
// ot_fit: Optional pointer to an FBInkOTFit struct.
// NOTE: If ot_cfg is NULL, will call fbink_print, otherwise, fbink_print_ot!
// If ot_cfg is valid, fbink_cfg MAY be NULL (same behavior as fbink_print_ot).
// If ot_cfg is NULL, fbink_cfg MUST be valid.
// NOTE: Meaning you MUST at least pass either an fbink_cfg or an ot_cfg pointer!
// NOTE: ot_fit behaves like in fbink_print_ot (i.e., it's only relevant if you pass an ot_cfg pointer, but it may be NULL).
FBINK_API int fbink_printf(int fbfd,
const FBInkOTConfig* restrict ot_cfg,
const FBInkConfig* restrict fbink_cfg,
FBInkOTFit* restrict ot_fit,
const char* fmt,
...) __attribute__((format(printf, 5, 6)));
//
// A simple wrapper around the internal screen refresh handling, without requiring you to include einkfb/mxcfb headers.
// NOTE: Unlike FBInkRect, we *do* honor the original mxcfb rect order here (top before left).
// Returns -(ENOSYS) on non-eInk devices (i.e., pure Linux builds)
// fbfd: Open file descriptor to the framebuffer character device,
// if set to FBFD_AUTO, the fb is opened for the duration of this call.
// region_top: top (y) field of an mxcfb rectangle.
// region_left: left (x) field of an mxcfb rectangle.
// region_width: width field of an mxcfb rectangle.
// region_height: height field of an mxcfb rectangle.
// fbink_cfg: Pointer to an FBInkConfig struct. Honors wfm_mode, dithering_mode, is_nightmode, is_flashing.
// NOTE: If you request an empty region (0x0 @ (0, 0), a full-screen refresh will be performed!
// NOTE: This *ignores* no_refresh ;).
// NOTE: As far as dithering is concerned, c.f., HW_DITHER_INDEX_E enum.
// True HW dithering is only supported on devices with a recent EPDC (>= v2)!
// On Kindle, that's everything since the KOA2 (KOA2, PW4, KT4, KOA3),
// On Kobo, that's everything since Mk.7.
// NOTE: Even then, your device may not actually support anything other than PASSTHROUGH & ORDERED!
// On slightly older devices, the EPDC may support some sort of in-kernel software dithering, hence HWD_LEGACY.
// NOTE: If you do NOT want to request any dithering, set FBInkConfig's dithering_mode field to HWD_PASSTHROUGH (i.e., 0).
// This is also the fallback value.
// NOTE: On Kobo devices with a sunxi SoC, you will not be able to refresh content that you haven't drawn yourself first.
// (There's no "shared" framebuffer, each process gets its own private, zero-initialized (i.e., solid black) buffer).
// NOTE: In case of ioctl failure, errno *should* be preserved,
// allowing the caller to possibly handle some very specific edge-cases.
// NOTE: The confusing coordinates order is inherited from the layout of an mxcfb_rect struct.
// See fbink_refresh_rect below for a variant that takes an FBInkRect instead.
FBINK_API int fbink_refresh(int fbfd,
uint32_t region_top,
uint32_t region_left,
uint32_t region_width,
uint32_t region_height,
const FBInkConfig* restrict fbink_cfg) __attribute__((nonnull));
//
// Variant of fbink_refresh that takes an FBInkRect instead of broken out coordinates.
// Completely identical behavior.
FBINK_API int fbink_refresh_rect(int fbfd, const FBInkRect* restrict rect, const FBInkConfig* restrict fbink_cfg)
__attribute__((nonnull));
// A simple wrapper around the MXCFB_WAIT_FOR_UPDATE_SUBMISSION ioctl, without requiring you to include mxcfb headers.
// Returns -(EINVAL) when the update marker is invalid.
// Returns -(ENOSYS) on devices where this ioctl is unsupported.
// NOTE: It is only implemented by Kindle kernels (K5+), and Kobo kernels for MTK SoCs (Mk. 11)!
// fbfd: Open file descriptor to the framebuffer character device,
// if set to FBFD_AUTO, the fb is opened for the duration of this call.
// marker: The update marker you want to wait for.
// NOTE: If marker is set to LAST_MARKER (0U), the one from the last update sent by this FBInk session will be used instead.
// If there aren't any, the call will fail and return -(EINVAL)!
// NOTE: Waiting for a random marker *should* simply return early.
FBINK_API int fbink_wait_for_submission(int fbfd, uint32_t marker);
// A simple wrapper around the MXCFB_WAIT_FOR_UPDATE_COMPLETE ioctl, without requiring you to include mxcfb headers.
// Returns -(EINVAL) when the update marker is invalid.
// Returns -(ENOSYS) on non-eInk devices (i.e., pure Linux builds).
// fbfd: Open file descriptor to the framebuffer character device,
// if set to FBFD_AUTO, the fb is opened for the duration of this call.
// marker: The update marker you want to wait for.
// NOTE: If marker is set to LAST_MARKER (0U), the one from the last update sent by this FBInk session will be used instead.
// If there aren't any, the call will fail and return -(EINVAL)!
// NOTE: Waiting for a random marker *should* simply return early.
FBINK_API int fbink_wait_for_complete(int fbfd, uint32_t marker);
// NOTE: For most single-threaded use-cases, you *probably* don't need to bother with this,
// as all your writes to the framebuffer should obviously be serialized.
// NOTE: All our target devices should default to the QUEUE_AND_MERGE update scheme,
// which means the EPDC itself will attempt to bundle updates together in order to do the least amount of work possible.
// (By work, I mean moving the eInk capsules around, i.e., limiting to amount of effective refreshes).
// A fun example of this can be seen with the dump/restore tests in utils/dump.c:
// By default, it will call fbink_wait_for_complete at sensible times, but if you pass a random argument to it,
// it won't: the difference that makes in practcie should be extremely obvious, especially for the first set of tests!
// NOTE: I encourage you to strace your stock reader to see how it makes use of those ioctls:
// they're mostly used before and/or after FULL (i.e., flashing) updates,
// to make sure they don't get affected by surrounding updates.
// They can also be used to more predictably fence A2 updates.
// In fact, for the most part, you can think of them as a kind of vsync fence.
// Be aware that the ioctl will block for (relatively) longer than it takes for the refresh to visually end,
// and that the delay depends for the most part on the waveform mode (flashing & region size have a much smaller impact).
// With some waveform modes (mainly A2/DU), it'll return significantly earlier if the region's fb content hasn't changed.
// NOTE: See KOReader's mxc_update @ https://github.com/koreader/koreader-base/blob/master/ffi/framebuffer_mxcfb.lua
// for some fancier examples in a complex application, where one might want to wait for completion of previous updates