Giter VIP home page Giter VIP logo

niluje / fbink Goto Github PK

View Code? Open in Web Editor NEW
319.0 13.0 23.0 10.77 MB

FrameBuffer eInker, a small tool & library to print text & images to an eInk Linux framebuffer

Home Page: https://www.mobileread.com/forums/showthread.php?t=299110

License: GNU General Public License v3.0

C 99.27% Makefile 0.26% Python 0.11% Shell 0.07% Perl 0.02% C++ 0.27%
kobo framebuffer eink kindle linux c cervantes remarkable

fbink's People

Contributors

benoit-pierre avatar fulldecent avatar jdek avatar lgtm-migrator avatar llandsmeer avatar niluje avatar pazos avatar pgaskin avatar rain92 avatar shermp avatar szybet avatar tcrs avatar yparitcher avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fbink's Issues

[Idea] Remember (partial) framebuffer state

Hi @NiLuJe , I'm baaack :D

I thought it would be great if one could show an image (or dialog box, or text...), and then when done, restore the original framebuffer state, so that the user isn't presented with the remnants of what we printed when we are done.

I was thinking perhaps it could be implemented as a 2 or three element stack, with each element saving the origin, dimensions, and raw scanlines of the region to be saved.

The user could then call a function like fbink_save_region() up to as many times until the stack is full. The could then restore the previous state with a function like fbink_restore_region(), again could be called multiple times until the stack is empty.

The main issue I see is memory usage, which is why the stack size would have to be fairly limited.

Thoughts?

Specifying "-fPIC" CFLAG for static library

Hi @NiLuJe

Would you be willing to add "-fPIC" as an optional CFLAG when compiling the static library?

The reason is that CGo does not seem to like linking to a static build without that flag, unless specifying the "--static" argument in cgo, which in turn breaks linking to glibc.

I did it myself, but I figured you may want to consider it for the project as a whole.

Thanks

[Discussion]: add support for bq cervantes devices.

Since the device is based on Freescale I.Mx6 sololite processor is similar to kobos, but without hardfloat support (a-la kindle).

As kobos, cervantes devices rely on /dev/ntx_io to read chunks of HWCONFIG from the unpartitioned space at the top of internal storage.

There are mainly three kind of devices to support, which differ on resolution, frontlight and "optimalight". But as long as FBInk doesn't need to know if a device has light capabilities we can just read resolution.

framebuffer seems pretty standard too.

Need some help to figure out how to do device identification. Any hint?

Automated Go bindings & Errors

Hi @NiLuJe

I've started work on automated bindings using c-for-go here: https://github.com/shermp/FBInk/tree/go-bindings

I'm at the point where I seem to have a working binary, at least with the very limited testing I've done with it.

What I haven't decided on is how I'm actually going to deal with errors. Currently, the functions just return ints with no context.

I hate to say it, but dealing with C errno's is a bit of a PITA, doubly so when they're negative. To get the errno constants in c-for-go, one would have to find and include the appropriate errno.h file, which is compiler specific etc.

Have you any ideas on how I might be able to deal with FBInk errors, hopefully without too mich manual coding?

K4: not working

every time i try to use FBInk (no matter what options) i get an error

[FBInk] MXCFB_SEND_UPDATE: Invalid argument
[FBInk] update_region={top=0, left=0, width=600, height=800}
[FBInk] Failed to refresh the screen!

thanks
if you need more info just let me know

Amazon Kobo support

Please add it. It has a 2" color epd screen and a i.mx980 chip thingy and a ia64 processer.

Rotation 90 kobo glo hd

Display rotation error. Fbink just ignores any rotatation I set.failed miserably.

[root@(none) gotest]# echo 1 >>/sys/class/graphics/fb0/rotate
[root@(none) gotest]# fbink -x 1 -y 10 "Hello World!"
[FBInk] Detected a Kobo Glo HD (371 => Alyssum @ Mark 6)
[FBInk] Clock tick frequency appears to be 100 Hz
[FBInk] Screen density set to 300 dpi
[FBInk] Variable fb info: 1072x1448, 16bpp @ rotation: 1 (Clockwise, 90°)
[FBInk] Fixed fb info: ID is "mxc_epdc_fb", length of fb mem: 6782976 bytes & line length: 2176 bytes
[FBInk] Canonical rotation: 2 (Upside Down, 180°)

I am writing a small go application which would communicate with an REST-API. Like a streamdeck, just with kobo. might have to render text as image on api end

Compilation Issue

cc fails at gettimeofday(&ev.time, NULL); with error fbink_button_scan.h:70:41: error: 'struct input_event' has no member named 'time'. I am using a Kobo Aura Second Edition and it doesn't have any hardware buttons. Is there any way to disable the compilation of this file?

question regarding API for image centering.

Sorry for bother you again @NiLuJe.

I'm trying to print images to the screen using the library, but I'm unable to center the image on the screen. I set up halign and valign to CENTER in fbink_config, but no way.

I'm looking for something like fbink -g file=/path/to/image,halign=CENTER,valign=CENTER -cf

[Cervantes]: messages are upside down.

Hey! Something weird is happening w/ my device. I did notice it because I started to tinker with FBInk library and saw the weird rotation, so I tried with the console tool and found the same.

output of FBInk v1.9.0-24-g4a29b0f for Cervantes [Minimalistic build].

./fbink hello
[FBInk] Clock tick frequency appears to be 100 Hz
[FBInk] Screen density set to 300 dpi
[FBInk] Variable fb info: 1448x1072, 16bpp @ rotation: 0 (Upright, 0°)
[FBInk] Enabled Kobo @ 16bpp boot rotation quirks (1448x1072 -> 1072x1448)
[FBInk] Fontsize set to 32x32 (IBM base glyph size: 8x8)
[FBInk] Line length: 33 cols, Page size: 45 rows
[FBInk] Vertical fit isn't perfect, shifting rows down by 4 pixels
[FBInk] Fixed fb info: ID is "mxc_epdc_fb", length of fb mem: 6782976 bytes & line length: 2944 bytes
[FBInk] Pen colors set to #000000 for the foreground and #FFFFFF for the background
Printing string 'hello' @ column 0 + 0px, row 0 + 0px (overlay: N, no BG: N, no FG: N, inverted: N, flashing: N, centered: N, halfway: N, left padded: N, clear screen: N, font: 0, font scaling: x0)
[FBInk] OpenType support is disabled in this FBInk build!

Output of FBInk v1.7.0 for Kobo [Minimalistic build] (Version used on lastest KOReader)

./fbink hello
[FBInk] Couldn't find a Kobo version tag (not running on a Kobo?)!
[FBInk] Clock tick frequency appears to be 100 Hz
[FBInk] Variable fb info: 1448x1072, 16bpp @ rotation: 0 (Upright, 0°)
[FBInk] Enabled Kobo @ 16bpp fb rotation quirks (1448x1072 -> 1072x1448)
[FBInk] Fontsize set to 24x24 (IBM base glyph size: 8x8)
[FBInk] Line length: 44 cols, Page size: 60 rows
[FBInk] Vertical fit isn't perfect, shitfting rows by 4 pixels to the bottom
[FBInk] Fixed fb info: ID is "mxc_epdc_fb", length of fb mem: 6782976 bytes & line length: 2944 bytes
[FBInk] Pen colors set to #000000 for the foreground and #FFFFFF for the background
Printing string 'hello' @ column 0 + 0px, row 0 + 0px (overlay: false, backgroundless: false, inverted: false, flashing: false, centered: false, left padded: false, clear screen: false, font: 0, font scaling: x0)

I can't find any difference in stdout (besides opentype support and the error finding a Kobo model on my Cervantes) but old build works fine and new build writes messages rotated 180º.

ICU for Unicode handling?

Hi @NiLuJe

I'm mulling the idea of adding basic freetype2 support, and was having a look at the FBInk codebase to see if I could figure out how to add support, and I've noticed your rant on Kobo's broken libc with regard to unicode support.

I notice that the Kobo firmware appears to include the ICU library (libicu*.so, vers. 4.6). Have you looked into using this library for dealing with strings in FBInk?

The API documentation for ICU 4.6.1 is here

button_scan: struct input_event has no member named 'time'

Hello,
Compiled FBInk for an initrd once again this morning and I came across an error that I encounter quite frequently these times with it. A quick fix is to pass MINIMAL=1 to the make environment variables, but the normal target fails with the error message I pasted below.
I've tried compiling it with a musl armv7l cross-toolchain (GCC 10.2.1) on a x86_64 Debian 10 VM.
Probably some musl-specific stuff I'd guess, although IIRC I've encountered this with other (non-musl) toolchains before too...

Expand
build@inkbox:~/inkbox/kernel/kernel/modules/mxc_epdc_fb_damage/FBInk$ make CROSS_COMPILE=armv7l-linux-musleabihf- static
make staticlib
make[1]: Entering directory '/home/build/inkbox/kernel/kernel/modules/mxc_epdc_fb_damage/FBInk'
mkdir -p Release/shared/cutef8 Release/static/cutef8 Release/shared/libunibreak/src Release/static/libunibreak/src Release/shared/qimagescale Release/static/qimagescale
armv7l-linux-musleabihf-cc  -DNDEBUG -D_REENTRANT=1 -D_GNU_SOURCE -DFBINK_FOR_KOBO -DFBINK_WITH_FONTS -DFBINK_WITH_IMAGE -DFBINK_WITH_OPENTYPE -DFBINK_WITH_BUTTON_SCAN -O2 -fomit-frame-pointer -pipe -ftree-vectorize -funroll-loops -fno-common -Wall -Wextra -Wunused -Wformat=2 -Wformat-signedness -Wformat-truncation=1 -Wnull-dereference -Wuninitialized -Wduplicated-branches -Wduplicated-cond -Wundef -Wbad-function-cast -Wwrite-strings -Wjump-misses-init -Wlogical-op -Wstrict-prototypes -Wold-style-definition -Wshadow -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -Winline -Wcast-qual -Wcast-align -Wconversion -Wno-jump-misses-init   -DFBINK_VERSION='"v1.23.2-36-gefec340 for Kobo"' -o Release/static/fbink.o -c fbink.c
fbink.c: In function ‘wait_for_complete_kobo_mk7’:
fbink.c:2548:23: warning: signed conversion from ‘unsigned int’ to ‘int’ changes value from ‘3221767727’ to ‘-1073199569’ [-Wsign-conversion]
 2548 |  int rv = ioctl(fbfd, MXCFB_WAIT_FOR_UPDATE_COMPLETE_V3, &update_marker);
      |                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from fbink.c:9721:
fbink_device_id.c: In function ‘identify_kobo’:
fbink_device_id.c:908:26: warning: signed conversion from ‘unsigned int’ to ‘int’ changes value from ‘2147750514’ to ‘-2147216782’ [-Wsign-conversion]
  908 |    if (ioctl(fileno(fp), BLKGETSIZE64, &storagesize)) {
      |                          ^~~~~~~~~~~~
In file included from fbink_button_scan.c:22,
                 from fbink.c:9758:
fbink_button_scan.c: In function ‘generate_button_press’:
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:224:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  224 |   SEND_INPUT_EVENT(EV_ABS, ABS_Y, match_coords->y);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:225:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  225 |   SEND_INPUT_EVENT(EV_ABS, ABS_X, match_coords->x);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:226:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  226 |   SEND_INPUT_EVENT(EV_ABS, ABS_PRESSURE, 100);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:227:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  227 |   SEND_INPUT_EVENT(EV_KEY, BTN_TOUCH, 1);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:228:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  228 |   SEND_INPUT_EVENT(EV_SYN, SYN_REPORT, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:230:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  230 |   SEND_INPUT_EVENT(EV_ABS, ABS_Y, match_coords->y);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:231:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  231 |   SEND_INPUT_EVENT(EV_ABS, ABS_X, match_coords->x);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:232:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  232 |   SEND_INPUT_EVENT(EV_ABS, ABS_PRESSURE, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:233:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  233 |   SEND_INPUT_EVENT(EV_KEY, BTN_TOUCH, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:234:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  234 |   SEND_INPUT_EVENT(EV_SYN, SYN_REPORT, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:239:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  239 |   SEND_INPUT_EVENT(EV_KEY, BTN_TOOL_FINGER, 1);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:240:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  240 |   SEND_INPUT_EVENT(EV_KEY, BTN_TOUCH, 1);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:241:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  241 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_TRACKING_ID, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:242:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  242 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_DISTANCE, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:243:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  243 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_POSITION_X, match_coords->x);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:244:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  244 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_POSITION_Y, match_coords->y);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:245:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  245 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_PRESSURE, 20);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:246:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  246 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_TOUCH_MAJOR, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:247:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  247 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_TOUCH_MINOR, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:248:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  248 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_ORIENTATION, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:249:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  249 |   SEND_INPUT_EVENT(EV_SYN, SYN_MT_REPORT, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:250:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  250 |   SEND_INPUT_EVENT(EV_SYN, SYN_REPORT, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:252:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  252 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_TRACKING_ID, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:253:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  253 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_DISTANCE, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:254:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  254 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_POSITION_X, match_coords->x);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:255:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  255 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_POSITION_Y, match_coords->y);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:256:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  256 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_PRESSURE, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:257:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  257 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_TOUCH_MAJOR, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:258:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  258 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_TOUCH_MINOR, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:259:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  259 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_ORIENTATION, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:260:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  260 |   SEND_INPUT_EVENT(EV_SYN, SYN_MT_REPORT, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:261:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  261 |   SEND_INPUT_EVENT(EV_SYN, SYN_REPORT, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:262:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  262 |   SEND_INPUT_EVENT(EV_KEY, BTN_TOUCH, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:263:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  263 |   SEND_INPUT_EVENT(EV_KEY, BTN_TOOL_FINGER, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:264:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  264 |   SEND_INPUT_EVENT(EV_SYN, SYN_REPORT, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:270:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  270 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_TRACKING_ID, 1);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:271:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  271 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_TOUCH_MAJOR, 1);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:272:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  272 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_WIDTH_MAJOR, 1);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:273:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  273 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_POSITION_X, match_coords->x);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:274:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  274 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_POSITION_Y, match_coords->y);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:278:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  278 |   SEND_INPUT_EVENT(EV_ABS, ABS_PRESSURE, 1024);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:279:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  279 |   SEND_INPUT_EVENT(EV_KEY, BTN_TOUCH, 1);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:281:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  281 |   SEND_INPUT_EVENT(EV_SYN, SYN_MT_REPORT, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:282:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  282 |   SEND_INPUT_EVENT(EV_SYN, SYN_REPORT, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:284:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  284 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_TRACKING_ID, 1);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:285:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  285 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_TOUCH_MAJOR, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:286:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  286 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_WIDTH_MAJOR, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:287:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  287 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_POSITION_X, match_coords->x);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:288:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  288 |   SEND_INPUT_EVENT(EV_ABS, ABS_MT_POSITION_Y, match_coords->y);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:290:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  290 |   SEND_INPUT_EVENT(EV_ABS, ABS_PRESSURE, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:291:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  291 |   SEND_INPUT_EVENT(EV_KEY, BTN_TOUCH, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:293:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  293 |   SEND_INPUT_EVENT(EV_SYN, SYN_MT_REPORT, 0);
      |   ^~~~~~~~~~~~~~~~
fbink_button_scan.h:71:20: error: ‘struct input_event’ has no member named ‘time’
   71 |    gettimeofday(&ev.time, NULL);                                                                    \
      |                    ^
fbink_button_scan.c:294:3: note: in expansion of macro ‘SEND_INPUT_EVENT’
  294 |   SEND_INPUT_EVENT(EV_SYN, SYN_REPORT, 0);
      |   ^~~~~~~~~~~~~~~~
make[1]: *** [Makefile:476: Release/static/fbink.o] Error 1
make[1]: Leaving directory '/home/build/inkbox/kernel/kernel/modules/mxc_epdc_fb_damage/FBInk'
make: *** [Makefile:586: static] Error 2

Segfault/overflow in print_ot when drawing near the bottom of the screen

Hello, thanks for a great project!

I've noticed that drawing text near the bottom of the screen causes occasional issues (either a segfault, or breaking font metrics enough that it thinks it has to scale to infinity). I've managed to come up with a script that can reproduce the issue, and I think I've pinpointed where the problem happens, but I don't know the code well enough to know what the fix should be :)

lua script
#!/usr/bin/env luajit
ffi = require("ffi")
require("ffi/fbink_h")
FBInk = ffi.load("/opt/lib/libfbink.so.1.0.0")

print("Loaded FBInk " .. ffi.string(FBInk.fbink_version()))

fbink_cfg = ffi.new("FBInkConfig")
fbink_cfg.is_verbose = true
fbfd = FBInk.fbink_open()
FBInk.fbink_init(fbfd, fbink_cfg)

font_cfg = ffi.new("FBInkOTConfig")
FBInk.fbink_add_ot_font_v2("/usr/share/fonts/ttf/noto/NotoSerif-Regular.ttf", FBInk.FNT_REGULAR, font_cfg)
FBInk.fbink_add_ot_font_v2("/usr/share/fonts/ttf/noto/NotoSerif-Bold.ttf", FBInk.FNT_BOLD, font_cfg)
FBInk.fbink_add_ot_font_v2("/usr/share/fonts/ttf/noto/NotoSerif-Italic.ttf", FBInk.FNT_ITALIC, font_cfg)

font_cfg.size_px = 48
font_cfg.padding = FBInk.HORI_PADDING

-- Remarkable 2 with screen size 1404x1872
font_cfg.margins.left = 900
font_cfg.margins.top = 1547
font_cfg.margins.right = 302
font_cfg.margins.bottom = 25

for i = 1,2 do
  -- The second iteration returns early with an impossible line height
  -- Max BL: 38  Max Desc: -2147483648  Max LG: 0  =>  Max LH (according to metrics): -2147483610
  FBInk.fbink_print_ot(
    fbfd,
    "Hello aaaa bbbbb cc ddddd eee ff g hhhhhhh iiii",
    font_cfg, fbink_cfg, nil)
end
verbose log
Loaded FBInk v1.23.1-git for reMarkable
[FBInk] Detected a reMarkable 2 (Zero Sugar)
[FBInk] This device does not support HW inversion
[FBInk] Running under the rm2fb compatibility shim (version 0.1), functionality may be limited
[FBInk] Clock tick frequency appears to be 100 Hz
[FBInk] Screen density set to 226 dpi
[FBInk] Variable fb info: 1404x1872, 16bpp @ rotation: 0 (Upright, 0°)
[FBInk] Fixed fb info: ID is "mxcfb", length of fb mem: 5256576 bytes & line length: 2808 bytes
[FBInk] Fontsize set to 24x24 (IBM base glyph size: 8x8)
[FBInk] Line length: 58 cols, Page size: 78 rows
[FBInk] Pen colors set to #000000 for the foreground and #FFFFFF for the background
Loading font data in a local FBInkOTFonts instance (0x6c1fc0) . . .
Initialized libunibreak
[FBInk] Font `/usr/share/fonts/ttf/noto/NotoSerif-Regular.ttf` loaded for style 'Regular'
Loading font data in a local FBInkOTFonts instance (0x6c1fc0) . . .
[FBInk] Font `/usr/share/fonts/ttf/noto/NotoSerif-Bold.ttf` loaded for style 'Bold'
Loading font data in a local FBInkOTFonts instance (0x6c1fc0) . . .
[FBInk] Font `/usr/share/fonts/ttf/noto/NotoSerif-Italic.ttf` loaded for style 'Italic'
Printing OpenType text.
Using fonts from a local FBInkOTFonts instance (0x6c1fc0)
Unformatted text defaulting to Regular font style
Max BL: 38  Max Desc: -10  Max LG: 0  =>  Max LH (according to metrics): 48
Finished looking for linebreaks
Current Measured LW: 27  Line# 0
Current Measured LW: 46  Line# 0
Current Measured LW: 58  Line# 0
Current Measured LW: 69  Line# 0
Current Measured LW: 88  Line# 0
Current Measured LW: 89  Line# 0
Current Measured LW: 117  Line# 0
Current Measured LW: 137  Line# 0
Current Measured LW: 157  Line# 0
Current Measured LW: 177  Line# 0
Current Measured LW: 178  Line# 0
Current Measured LW: 207  Line# 0
Looking for a linebreak opportunity . . .
Flagging a last-resort break @ #10
Found a break @ #10
Current Measured LW: 20  Line# 1
Current Measured LW: 42  Line# 1
Current Measured LW: 64  Line# 1
Current Measured LW: 86  Line# 1
Current Measured LW: 108  Line# 1
Current Measured LW: 110  Line# 1
Current Measured LW: 135  Line# 1
Current Measured LW: 152  Line# 1
Current Measured LW: 153  Line# 1
Current Measured LW: 184  Line# 1
Current Measured LW: 206  Line# 1
Looking for a linebreak opportunity . . .
Flagging a last-resort break @ #20
Found a break @ #19
Current Measured LW: 22  Line# 2
Current Measured LW: 44  Line# 2
Current Measured LW: 66  Line# 2
Current Measured LW: 88  Line# 2
Current Measured LW: 110  Line# 2
Current Measured LW: 110  Line# 2
Current Measured LW: 137  Line# 2
Current Measured LW: 156  Line# 2
Current Measured LW: 175  Line# 2
Current Measured LW: 176  Line# 2
Current Measured LW: 201  Line# 2
Current Measured LW: 214  Line# 2
Looking for a linebreak opportunity . . .
Flagging a last-resort break @ #30
Found a break @ #29
Current Measured LW: 16  Line# 3
Current Measured LW: 29  Line# 3
Current Measured LW: 26  Line# 3
Current Measured LW: 54  Line# 3
Current Measured LW: 54  Line# 3
Current Measured LW: 85  Line# 3
Current Measured LW: 107  Line# 3
Current Measured LW: 129  Line# 3
Current Measured LW: 151  Line# 3
Current Measured LW: 173  Line# 3
Current Measured LW: 195  Line# 3
Current Measured LW: 217  Line# 3
Looking for a linebreak opportunity . . .
Flagging a last-resort break @ #40
Found a break @ #34
Current Measured LW: 22  Line# 4
Current Measured LW: 44  Line# 4
Current Measured LW: 66  Line# 4
Current Measured LW: 88  Line# 4
Current Measured LW: 110  Line# 4
Current Measured LW: 132  Line# 4
Current Measured LW: 154  Line# 4
Current Measured LW: 154  Line# 4
Current Measured LW: 174  Line# 4
Current Measured LW: 185  Line# 4
Current Measured LW: 196  Line# 4
Current Measured LW: 207  Line# 4
Looking for a linebreak opportunity . . .
Flagging a last-resort break @ #45
Found a break @ #42
Current Measured LW: 11  Line# 5
Current Measured LW: 22  Line# 5
Current Measured LW: 33  Line# 5
Current Measured LW: 44  Line# 5
Flagging a mandatory break at EOS @ #46
6 lines to be printed
Maximum printable height is 300
Actual print height is 288
Max LW: 202  Max LH: 48  Max BL: 38  FntSize: 48
Filled a #FF 0x288 rectangle @ (900, 1547)
Filled a #FF 326x288 rectangle @ (1078, 1547)
Finished printing line# 0
Filled a #FF 0x288 rectangle @ (900, 1595)
Filled a #FF 351x288 rectangle @ (1053, 1595)
Finished printing line# 1
Filled a #FF 0x288 rectangle @ (900, 1643)
Filled a #FF 328x288 rectangle @ (1076, 1643)
Finished printing line# 2
Filled a #FF 0x288 rectangle @ (900, 1691)
Filled a #FF 450x288 rectangle @ (954, 1691)
Finished printing line# 3
Filled a #FF 0x288 rectangle @ (900, 1739)
Filled a #FF 350x288 rectangle @ (1054, 1739)
Finished printing line# 4
Filled a #FF 0x288 rectangle @ (900, 1787)
Filled a #FF 460x288 rectangle @ (944, 1787)
Ran out of drawing area at the end of line# 5 after ~47 characters!
Finished printing line# 5
Printed 6 visible lines
Refreshing region from LEFT: 900, TOP: 1547, WIDTH: 202, HEIGHT: 288
waveform_mode is now 0x101 (AUTO)
Printing OpenType text.
Using fonts from a local FBInkOTFonts instance (0x6c1fc0)
Unformatted text defaulting to Regular font style
Max BL: 38  Max Desc: -2147483648  Max LG: 0  =>  Max LH (according to metrics): -2147483610
[FBInk] Max line height not set!
Refreshing region from LEFT: 0, TOP: 0, WIDTH: 0, HEIGHT: 0

I went in with gdb and figured out that the first time around all 3 of the font styles produce reasonable metrics, but the second time around the bold font results in an impossible scaling factor (infinity) here:

FBInk/fbink.c

Line 5584 in 21023a9

bdSF = stbtt_ScaleForPixelHeight(ot_fonts->otBold, (float) font_size_px);

Looking further into stbtt_ScaleForPixelHeight, info->data + info->hhea + 4 and + 6 are both 0xffff, so the denominator in that function ends up being 0. In the first iteration, that location points to reasonable data, suggesting that somewhere before the second call to print_ot, that data got overwritten with 0xff.

gdb tells me that happens in fill_rect_RGB565, called from the right padding section of print_ot:

FBInk/fbink.c

Lines 6270 to 6275 in 21023a9

// Right padding (final pen position to the right edge of the drawing area)
(*fxpFillRect)((unsigned short int) (paint_point.x + lw),
paint_point.y,
(unsigned short int) (viewWidth - (paint_point.x + lw)),
(unsigned short int) curr_print_height,
&bgP);

I don't understand what all the different variables are at that point, but based on the log it looks like that function gets called for each wrapped line of text. The logs below are taken from the full log, and I'm pretty sure they're each call to fill_rect_RGB565 to fill in the right padding. It looks like the height of the fill is the full calculated height of all 6 lines (48 pixels * 6 lines = 288), not the height of a single line, but the top of the rectangle keeps moving down the screen. The screen in this example is 1872 pixels tall, so all but the first call ends up filling 0xff bytes off the bottom of the screen. I assume eventually this overflows into the bold font's data.

Filled a #FF 326x288 rectangle @ (1078, 1547)
Filled a #FF 351x288 rectangle @ (1053, 1595)
Filled a #FF 328x288 rectangle @ (1076, 1643)
Filled a #FF 450x288 rectangle @ (954, 1691)
Filled a #FF 350x288 rectangle @ (1054, 1739)
Filled a #FF 460x288 rectangle @ (944, 1787)

Musings and ideas for a potential v2 API

Carrying on from our discussions in shermp/go-fbink-v2/pull/9, I just wanted to jot down a few ideas rolling around in my skull while they are fresh.

Feel free to ignore, or wait on this.

  • Splitting the library into eink refresh/blitting component, and a text/image rendering components. The idea is if one wants to use different libraries for text or images, they can. The hard part in many cases is dealing with all the quirks of eink, and device framebuffer formats etc, and it would be nice to have a standalone library to deal with that.
  • Not using a massive FBConfig struct to configure everything under the sun. IMO, it's gotten rather unwieldly as more features have been added. One potential idea:
    • An opaque context struct that contains per session config variables
    • Change options via "methods" on that context struct.
    • Use an init 'method' to create said struct for the user.
    • While we're at it, create a fbfd in the init function and store it in the context. Drop the AUTO fbfd completely
    • There could be an internal function to test the context for any re-init requirements, so the user doesn't have to think about it.
    • Then when finished, close the context to unmap the fbfd etc.

That's one initial thought from me. I'm generally not a fan of getter/setters, coming from a Python/C background, but in this case it might help prevent API breakage by hiding internal structs etc.

pushd is not posix sh

Hi, another small issue :)

Executing make kobo gave me:

pushd Kobo && zip -r ../Release/FBInk-v1.21.0-31-gfdd7300.zip . && popd
/bin/sh: 1: pushd: not found

Quick fix with:

diff --git a/Makefile b/Makefile
index f4694fe..6036e03 100644
--- a/Makefile
+++ b/Makefile
@@ -621,7 +621,7 @@ kobo: armcheck
        cp -av $(CURDIR)/README.md Kobo/README.md
        cp -av $(CURDIR)/LICENSE Kobo/LICENSE
        cp -av $(CURDIR)/CREDITS Kobo/CREDITS
-       pushd Kobo && zip -r ../Release/FBInk-$(FBINK_VERSION).zip . && popd
+       (cd Kobo && zip -r ../Release/FBInk-$(FBINK_VERSION).zip . )
        mv -v Release/FBInk-$(FBINK_VERSION).zip Kobo/

 libunibreakclean:

Not sure if that is something you want fixed. Shall I just sent a pull request next time (which you can ignore if you don't want the fix)?

Furthermore, I came across some small issues when trying to use fbink.h from C++, which can be fixed with a extern "C" and #define restrict __restrict__. But maybe C++ support is not a design goal.

Also, since you seem to be the author of KFMon and I have some questions about its use, whats the best way to contact you - if you're willing to answer some questions? :)

Kind regards,
Lennart

Color Font on the Kobo Clara Colour

Hejo, I recently got one of the new color ereaders (as they've become quite affordable) and am looking forward to do some tinkering. Maybe port Doom some piece of productivity software

From what I can tell FBInk stable is working, (unlike stable releases of KOReader and Plato 😞 ).

It looks like printing text in color is not implemented yet?

FBInk/CLI.md

Lines 208 to 217 in c4cc9b6

* `-C`, `--color` `NAME`
`-B`, `--background` `NAME`
Color of the background the text will be printed on (Default: WHITE).
Color the text will be printed in (Default: BLACK).
Available colors: `BLACK`, `GRAY1`, `GRAY2`, `GRAY3`, `GRAY4`, `GRAY5`, `GRAY6`, `GRAY7`, `GRAY8`, `GRAY9`, `GRAYA`, `GRAYB`, `GRAYC`, `GRAYD`, `GRAYE`, `WHITE`.

However one of the example screenshots shows a color image... haven't tried yet, are color images implemented?

I'd love to have at least RED, YELLOW, GREEN, BLUE available! Is that within the scope of this library?


Device: Kobo Clara Colour (But should apply to Kobo Libra Colour as well)
Software: 4.39.22861

Version:Probably 1.25.0 ( whatever was bundled with the all in one. )

[Idea] Activity bar

Quick! Hide! I have another one...

So, FBInk has a progress bar. But sometimes we have to wait for an indeterminate amount of time for a task to complete. Wouldn't it be great if we had a nice scrolling activity bar to reassure the user that, no, we haven't crashed?

The idea would be to define a fixed range of positions that the dev can set the 'scroll bar/widget' to, and that way the dev can decide on their scrolling pattern and frequency. Sure, such an idea is not going to result in smooth scrolling, but it would be effective enough.

Thoughts?

Right, I'll see myself out now. :)

Getting a few warnings when building with Clang

In file included from fbink.c:39:
./fbink_internal.h:478:26: warning: missing field 'enable' initializer [-Wmissing-field-initializers]
        .layer        = { { 0 } },
                                ^
fbink.c:4338:64: warning: implicit conversion changes signedness: 'uint64_t' (aka 'unsigned long') to 'long long' [-Wsign-conversion]
                sunxiCtx.layer.info.fb.crop.width    = (uint64_t) vInfo.xres << 32U;
                                                     ~ ~~~~~~~~~~~~~~~~~~~~~~^~~~~~
fbink.c:4339:64: warning: implicit conversion changes signedness: 'uint64_t' (aka 'unsigned long') to 'long long' [-Wsign-conversion]
                sunxiCtx.layer.info.fb.crop.height   = (uint64_t) vInfo.yres << 32U;
                                                     ~ ~~~~~~~~~~~~~~~~~~~~~~^~~~~~
3 warnings generated.

Can't build with `make LINUX=true` - undefined reference to get_hwd_mode()

Hi, I just came across FBInk, which looks exactly like what I'm searching for!

However, while trying to build a linux build, I got a lot of undefined reference to get_hwd_mode messages. Moving get_hwd_mode and hwd_to_string outside the #ifndef FBINK_FOR_LINUX
got everything working again and I can write to the framebuffer now. I suppose it's not a good fix (those ifdefs must be there for a reason), but maybe this helps in some way :)

Commit: 747530d

Diff (just 2 lines moved):

diff --git a/fbink.c b/fbink.c
index 3f99c50..bb779b8 100644
--- a/fbink.c
+++ b/fbink.c
@@ -5448,6 +5448,7 @@ static const char*
        }
 }
 #      endif    // FBINK_FOR_KINDLE
+#endif    // !FBINK_FOR_LINUX

 // Convert our public HW_DITHER_INDEX_T values to an appropriate mxcfb dithering mode constant
 static int
@@ -5511,7 +5512,6 @@ static const char*
                        return "Unknown";
        }
 }
-#endif    // !FBINK_FOR_LINUX

 // Small public wrapper around refresh(), without the caller having to depend on mxcfb headers
 int
diff --git a/fbink_internal.h b/fbink_internal.h
index dea4cd1..87a9f69 100644
--- a/fbink_internal.h
+++ b/fbink_internal.h
@@ -593,9 +593,9 @@ static const char* wfm_to_string(uint8_t);
 static const char* kindle_wfm_to_string(uint32_t);
 static const char* kindle_zelda_wfm_to_string(uint32_t);
 #      endif
+#endif
 static int         get_hwd_mode(uint8_t);
 static const char* hwd_to_string(uint8_t);
-#endif

 // For identify_device, which we need outside of fbink_device_id.c ;)
 #ifndef FBINK_FOR_LINUX

Kind regards,
Lennart

free_ot_font: Double Free

When calling fbink_free_ot_fonts() twice there is a double free.
for example:

fbink_add_ot_font("font.ttf", FNT_REGULAR);
fbink_free_ot_fonts();
...
fbink_add_ot_font("font.ttf", FNT_REGULAR);
fbink_free_ot_fonts();

in a long running program.

FBInk/fbink.c

Line 2720 in 5642e16

font_info = NULL;

only nulls the local copy of font_info
however the global pointer otFonts.otRegular is not nulled, which leaves a dangling pointer -> double free.

I am not sure how to pass the pointer to fix this.
Thanks

Return value of button_scan

So, I was testing button_scan with kobo-rclone this evening, and things just were not working right. After looking at the code, I realised what the problem is. There is currently now way to tell if a button was found or not based on the return code. I had naively expected it to return a negative number if no button was found.

The only time button_scan returns with anything other than EXIT_SUCCESS is if the button press "fails", or it isn't run on a Kobo.

I hate to suggest it, but maybe button_scan needs to return a "magic number" depending on the final state. Maybe a few are needed.

Thoughts?

MXCFB_SEND_UPDATE_V2: Operation not permitted!

Hello,
Got the usual xdamage/FBInk bundle compiled on my Libra today for running X11 (I want to set up a portable computer with a RPi as hardware and the Kobo as the screen), but it seems that something doesn't want to work. When I first try to print 'hello' on the screen, no problem encountered, all's good.
Then, I start fbink_xdamage (which works) but after a couple of minutes, it starts to spit out some "Operation not permitted" errors. I have no idea why.
Then I try to print 'hello' on the screen again, but it doesn't work either:

[FBInk] Couldn't find a Kobo version tag (onboard unmounted or not running on a Kobo?)!
[FBInk] Detected a Kobo Libra H2O (384 => Storm @ Mark 7)
[FBInk] Enabled Kobo Mark 7 quirks
[FBInk] Clock tick frequency appears to be 100 Hz
[FBInk] Screen density set to 300 dpi
[FBInk] Variable fb info: 1680x1264, 32bpp @ rotation: 3 (Counter Clockwise, 270°)
[FBInk] Fixed fb info: ID is "mxc_epdc_fb", length of fb mem: 9175040 bytes & line length: 6784 bytes
[FBInk] Canonical rotation: 3 (Counter Clockwise, 270°)
[FBInk] Fontsize set to 32x32 (IBM base glyph size: 8x8)
[FBInk] Line length: 52 cols, Page size: 39 rows
[FBInk] Vertical fit isn't perfect, shifting rows down by 8 pixels
[FBInk] Pen colors set to #000000 for the foreground and #FFFFFF for the background
Printing string 'hello' @ column 0 + 0px, row 0 + 0px (overlay: N, no BG: N, no FG: N, inverted: N, flashing: N, centered: N, halfway: N, left padded: N, right padded: N, clear screen: N, waveform: AUTO, dithering: PASSTHROUGH, nightmode: N, skip refresh: N, font: 0, font scaling: x0)
[FBInk] [refresh_kobo_mk7] MXCFB_SEND_UPDATE_V2: Operation not permitted!
[FBInk] [fbink_print] Failed to refresh the screen!
[CLI] Failed to print that string!

Here's the stack trace, I bet it will be more useful than what I pasted above:

strace FBInk/Release/fbink hello
execve("FBInk/Release/fbink", ["FBInk/Release/fbink", "hello"], 0x7e9e7e54 /* 9 vars */) = 0
set_tls(0x76f80400)                     = 0
set_tid_address(0x76f7f7cc)             = 5775
brk(NULL)                               = 0x55926000
brk(0x55928000)                         = 0x55928000
mmap2(0x55926000, 4096, PROT_NONE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x55926000
mprotect(0x54bd3000, 4096, PROT_READ)   = 0
open("/dev/fb0", O_RDWR|O_LARGEFILE|O_CLOEXEC) = 3
fcntl64(3, F_SETFD, FD_CLOEXEC)         = 0
open("/mnt/onboard/.kobo/version", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = -1 ENOENT (No such file or directory)
writev(2, [{iov_base="", iov_len=0}, {iov_base="[FBInk] Couldn't find a Kobo ver"..., iov_len=88}], 2[FBInk] Couldn't find a Kobo version tag (onboard unmounted or not running on a Kobo?)!
) = 88
open("/dev/mmcblk0", O_RDONLY|O_LARGEFILE|O_CLOEXEC) = 4
fcntl64(4, F_SETFD, FD_CLOEXEC)         = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x76f7d000
fcntl64(4, F_SETFD, FD_CLOEXEC)         = 0
_llseek(4, 524288, [524288], SEEK_SET)  = 0
readv(4, [{iov_base="HW CONFIG v3.1\0", iov_len=15}, {iov_base="FW\32\0\0\10\0\0\16\3\25'\6\2\t\n\4\3\2\0\2\2\1h\2\344\0\0\n\1\5\0"..., iov_len=1024}], 2) = 1039
ioctl(4, BLKGETSIZE64, [7818182656])    = 0
_llseek(4, -953, [524374], SEEK_CUR)    = 0
close(4)                                = 0
munmap(0x76f7d000, 4096)                = 0
writev(2, [{iov_base="[FBInk] Detected a Kobo Libra H2"..., iov_len=58}, {iov_base=NULL, iov_len=0}], 2[FBInk] Detected a Kobo Libra H2O (384 => Storm @ Mark 7)
) = 58
writev(2, [{iov_base="", iov_len=0}, {iov_base="[FBInk] Enabled Kobo Mark 7 quir"..., iov_len=35}], 2[FBInk] Enabled Kobo Mark 7 quirks
) = 35
writev(2, [{iov_base="[FBInk] Clock tick frequency app"..., iov_len=50}, {iov_base=NULL, iov_len=0}], 2[FBInk] Clock tick frequency appears to be 100 Hz
) = 50
writev(2, [{iov_base="[FBInk] Screen density set to 30"..., iov_len=38}, {iov_base=NULL, iov_len=0}], 2[FBInk] Screen density set to 300 dpi
) = 38
ioctl(3, FBIOGET_VSCREENINFO, 0x54bd4120) = 0
writev(2, [{iov_base="[FBInk] Variable fb info: 1680x1"..., iov_len=58}, {iov_base="Counter Clockwise, 270\302\260", iov_len=24}], 2[FBInk] Variable fb info: 1680x1264, 32bpp @ rotation: 3 (Counter Clockwise, 270°) = 82
writev(2, [{iov_base=")\n", iov_len=2}, {iov_base=NULL, iov_len=0}], 2)
) = 2
ioctl(3, FBIOGET_FSCREENINFO, 0x54bd40d4) = 0
writev(2, [{iov_base="[FBInk] Fixed fb info: ID is \"mx"..., iov_len=69}, {iov_base=" bytes & line length: ", iov_len=22}], 2[FBInk] Fixed fb info: ID is "mxc_epdc_fb", length of fb mem: 9175040 bytes & line length: ) = 91
writev(2, [{iov_base="6784 bytes\n", iov_len=11}, {iov_base=NULL, iov_len=0}], 26784 bytes
) = 11
writev(2, [{iov_base="[FBInk] Canonical rotation: 3 (C"..., iov_len=57}, {iov_base=NULL, iov_len=0}], 2[FBInk] Canonical rotation: 3 (Counter Clockwise, 270°)
) = 57
writev(2, [{iov_base="[FBInk] Fontsize set to 32x32 (I"..., iov_len=57}, {iov_base=NULL, iov_len=0}], 2[FBInk] Fontsize set to 32x32 (IBM base glyph size: 8x8)
) = 57
writev(2, [{iov_base="[FBInk] Line length: 52 cols, Pa"..., iov_len=49}, {iov_base=NULL, iov_len=0}], 2[FBInk] Line length: 52 cols, Page size: 39 rows
) = 49
writev(2, [{iov_base="[FBInk] Vertical fit isn't perfe"..., iov_len=67}, {iov_base=NULL, iov_len=0}], 2[FBInk] Vertical fit isn't perfect, shifting rows down by 8 pixels
) = 67
writev(2, [{iov_base="[FBInk] Pen colors set to #00000"..., iov_len=64}, {iov_base=" for the background\n", iov_len=20}], 2[FBInk] Pen colors set to #000000 for the foreground and #FFFFFF for the background
) = 84
writev(2, [{iov_base="", iov_len=0}, {iov_base=NULL, iov_len=0}], 2) = 0
ioctl(1, TIOCGWINSZ, {ws_row=37, ws_col=167, ws_xpixel=0, ws_ypixel=0}) = 0
writev(1, [{iov_base="Printing string 'hello' @ column"..., iov_len=284}, {iov_base=")\n", iov_len=2}], 2Printing string 'hello' @ column 0 + 0px, row 0 + 0px (overlay: N, no BG: N, no FG: N, inverted: N, flashing: N, centered: N, halfway: N, left padded: N, right padded: N, clear screen: N, waveform: AUTO, dithering: PASSTHROUGH, nightmode: N, skip refresh: N, font: 0, font scaling: x0)
) = 286
mmap2(NULL, 9175040, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0x76636000
getpid()                                = 5775
ioctl(3, _IOC(_IOC_WRITE, 0x46, 0x2e, 0x48), 0x7eea89cc) = -1 EPERM (Operation not permitted)
writev(2, [{iov_base="[FBInk] [refresh_kobo_mk7] MXCFB"..., iov_len=74}, {iov_base=NULL, iov_len=0}], 2[FBInk] [refresh_kobo_mk7] MXCFB_SEND_UPDATE_V2: Operation not permitted!
) = 74
writev(2, [{iov_base="[FBInk] [fbink_print] Failed to "..., iov_len=52}, {iov_base=NULL, iov_len=0}], 2[FBInk] [fbink_print] Failed to refresh the screen!
) = 52
writev(2, [{iov_base="", iov_len=0}, {iov_base="[CLI] Failed to print that strin"..., iov_len=35}], 2[CLI] Failed to print that string!
) = 35
munmap(0x76636000, 9175040)             = 0
close(3)                                = 0
exit_group(-1)                          = ?
+++ exited with 255 +++

drawing many elements in a single refresh

Is it possible to add an option to fbink that will just draw to the framebuffer, without telling the screen to actually update?

Use case example, a weather display as shown in this thread:

https://www.mobileread.com/forums/showthread.php?t=194376

This displays a lot of info on a single page. Five days of weather forecast, each with a day name (today, tomorrow, friday, saturday, sunday), weather summary (partly sunny, showers likely, mostly sunny), a weather icon, high and low temperatures (each with high and low and °C / °F labels), the update time, some lines as background elements...

If I were to script a weather display using the fbink CLI utility that would mean 50+ calls to fbink with just as many refreshes to the screen. I'd like to skip all of those and follow up with a single full screen refresh.

OT/TT Baseline mode (single line?)

So, I finally chanced upon (what became) the miniclock thread, and I was thinking a single line, baseline insert mode might be quite useful.

I envision the mode to use the left and right margin, with the insert point being the baseline, and only prints one line. The mode could also refresh the entire 'line' to ensure that there are no 'leftovers' when overwriting a line.

The baseline insertion would assist those who are looking to align the clock with the page n of nn text, for example.

Your thoughts?

Too many arguments build error

Hi,
I'm NiMa (from Mobileread) and as you probably saw in the last few days, I tried to get Xorg running on Kobos. I used this https://github.com/schuhumi/fbink-xdamage repository which refreshes the content of the eInk display when an X window's content is updated. Since then, I forked it (https://github.com/tux-linux/fbink-xdamage) and added the latest FBInk commit to be able to support the new Kobo Nia. I encountered the following build error on my Glo HD:

gcc -LFBInk/Release/ -IFBInk main.c -o fbink_xdamage -lX11 -lXext -lXdamage -lXfixes -lfbink
main.c: In function 'refresh':
main.c:104:66: warning: passing argument 6 of 'fbink_refresh' makes pointer from integer without a cast [-Wint-conversion]
  104 |     fbink_refresh(fbfd, area.y, area.x, area.width, area.height, HWD_ORDERED, &fbink_cfg);
      |                                                                  ^~~~~~~~~~~
      |                                                                  |
      |                                                                  int
In file included from main.c:12:
FBInk/fbink.h:594:36: note: expected 'const FBInkConfig * restrict' {aka 'const struct <anonymous> * restrict'} but argument is of type 'int'
  594 |        const FBInkConfig* restrict fbink_cfg);
      |        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~
main.c:104:5: error: too many arguments to function 'fbink_refresh'
  104 |     fbink_refresh(fbfd, area.y, area.x, area.width, area.height, HWD_ORDERED, &fbink_cfg);
      |     ^~~~~~~~~~~~~
In file included from main.c:12:
FBInk/fbink.h:589:15: note: declared here
  589 | FBINK_API int fbink_refresh(int                         fbfd,
      |               ^~~~~~~~~~~~~
main.c: In function 'main':
main.c:243:34: warning: passing argument 6 of 'fbink_refresh' makes pointer from integer without a cast [-Wint-conversion]
  243 |     fbink_refresh(fbfd, 0,0,0,0, HWD_ORDERED, &fbink_cfg);
      |                                  ^~~~~~~~~~~
      |                                  |
      |                                  int
In file included from main.c:12:
FBInk/fbink.h:594:36: note: expected 'const FBInkConfig * restrict' {aka 'const struct <anonymous> * restrict'} but argument is of type 'int'
  594 |        const FBInkConfig* restrict fbink_cfg);
      |        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~
main.c:243:5: error: too many arguments to function 'fbink_refresh'
  243 |     fbink_refresh(fbfd, 0,0,0,0, HWD_ORDERED, &fbink_cfg);
      |     ^~~~~~~~~~~~~
In file included from main.c:12:
FBInk/fbink.h:589:15: note: declared here
  589 | FBINK_API int fbink_refresh(int                         fbfd,
      |               ^~~~~~~~~~~~~
make: *** [Makefile:3: all] Error 1

I have GCC/G++ 9.3.0 running on an Alpine Linux chroot.
Don't exactly know what to do, maybe I should fall back to a standard stable release submodule. Any clue? Thanks.

[Idea] Detect when Nickel has "imported" content

So, hi, it's me again with another "Idea".

It would be great if we could determine when Nickel has finished adding content to the device, so one could perform further processing without the need for user interaction. The idea is basically an extended version of what FBInk does to determine a successful button press.

The process seems to be the following. (1)Connected screen[black] -> (2)Home screen[white] -> (3)Importing content screen[black] -> (4)Home screen[white]

With an appropriate timeout between (2) and (3) (to account for the fact that no new content may be added), do you think watching for this pattern would work as a basic method of determining when we can perform further processing?

Cheers,
Sherman

Does not work on non-interactive shell

It fails to clear the screen and hangs at the end (leaving the connection open)

Works fine when in interactive. Kindle 4 non-touch

[parker@stealth ~]$ ssh [email protected] fbink -c
[email protected]'s password: 
[FBInk] Enabled Legacy einkfb Kindle quirks
[FBInk] Clock tick frequency appears to be 100 Hz
[FBInk] Screen density set to 167 dpi
[FBInk] Variable fb info: 600x800, 8bpp @ rotation: 0 (Upright, 0°)
[FBInk] Fontsize set to 16x16 (IBM base glyph size: 8x8)
[FBInk] Line length: 37 cols, Page size: 50 rows
[FBInk] Fixed fb info: ID is "eink_fb", length of fb mem: 966656 bytes & line length: 600 bytes
[FBInk] Pen colors set to #000000 -> #FFFFFF for the foreground and #FFFFFF -> #000000 for the background

The future of button_scan

Have you given any thought as to how you would like to proceed with the button scanner? Keep it with FBInk? Spin it off to it's own project? Turn it into a library?

I'm sure I'm not the only person who would find it useful.

Option to choose framebuffer device

I realized I had a couple of old LeapPads lying around in my house and wondered if I could get a shell with a serial connection. Turns out that yes, it's possible and it even has built-in USBNet modules. Although having a crappy CPU and only 43 MiB of RAM, it runs Linux 2.6.31 and I managed to compile FBInk for it using one of the musl toolchains for armv5l.
For the record, here's what it looks like:
PXL_20210428_144651382
To use it, though, I had to change the framebuffer device to /dev/fb1 but FBInk always uses fb0. I did get around this by changing the file descriptor in fbink.c but it would be nice to have an option to change it at run-time (and default to fb0 when not specified).
Just a suggestion...

wrong ioctl seen on Kobo Clara HD

On Kobo Clara HD with 5.17.10 near-mainline-kernel + Debian + user in video group:

andi@akepd:~/FBInk$ groups 
andi video
andi@akepd:~/FBInk$ Release/fbink -s
[FBInk] Couldn't find a Kobo version tag (onboard unmounted or not running on a Kobo?)!
[FBInk] [identify_kobo] Couldn't read from `/dev/mmcblk0` (Permission denied), unable to identify the Kobo model!
[FBInk] Detected a Kobo  (0 =>  @ )
[FBInk] This device does not support HW inversion
[FBInk] Clock tick frequency appears to be 100 Hz
[FBInk] Screen density set to 167 dpi
[FBInk] Variable fb info: 1072x1448, 16bpp @ rotation: 3 (Counter Clockwise, 270°)
[FBInk] Fixed fb info: ID is "mxc_epdc_fb", length of fb mem: 6782976 bytes & line length: 2176 bytes
[FBInk] Canonical rotation: 2 (Upside Down, 180°)
[FBInk] Fontsize set to 16x16 (IBM base glyph size: 8x8)
[FBInk] Line length: 67 cols, Page size: 90 rows
[FBInk] Horizontal fit is perfect!
[FBInk] Vertical fit isn't perfect, shifting rows down by 4 pixels
[FBInk] Pen colors set to #000000 for the foreground and #FFFFFF for the background
Refreshing the screen from top=0, left=0 for width=0, height=0 with waveform mode AUTO and dithering mode PASSTHROUGH (nightmode: N)
[FBInk] [refresh_kobo] MXCFB_SEND_UPDATE_V1_NTX: Invalid argument!
[FBInk] update_region={top=0, left=0, width=1072, height=1448}!
[FBInk] [fbink_refresh] Failed to refresh the screen!
[CLI] Failed to refresh the screen as per your specification!
andi@akepd:~/FBInk$ ls -l /dev/fb0 
crw-rw---- 1 root video 29, 0 Aug 29 18:56 /dev/fb0
andi@akepd:~/FBInk$ xxd /sys/firmware/    
devicetree/ fdt         
andi@akepd:~/FBInk$ xxd /sys/firmware/devicetree/base/
#address-cells   aliases/         clock-ckil/      clock-ipp-di1/   compatible       gpio-keys/       memory@80000000/ name             soc/             wifi_pwrseq/     
#size-cells      chosen/          clock-ipp-di0/   clock-osc-24m/   cpus/            leds/            model            regulator-wifi/  thermal-zones/   
andi@akepd:~/FBInk$ xxd /sys/firmware/devicetree/base/compatible 
00000000: 6b6f 626f 2c63 6c61 7261 6864 0066 736c  kobo,clarahd.fsl
00000010: 2c69 6d78 3673 6c6c 00                   ,imx6sll.
andi@akepd:~/FBInk$ 

MXCFB_SEND_UPDATE_V2 should be sent there (and is done when /dev/mmcblk0 is accessible)

Conclusion the logic of interface detection should be improved.

I think using /sys/firmware/devicetree/base/compatible is the best way to detect the device used (if it is there), if there is kobo,something in there, that is a near-mainline kernel is used and the model can be detected properly. If there is some imx6sl, imx6sll, imx6ull in there, it should be safe to send MXCFB_SEND_UPDATE_V2. To distinguish from upcoming drm drivers (which do not support any freescale ioctl stuff, the availability of /sys/bus/platform/drivers/imx_epdc_*fb could be checked.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.