View Single Post
Old 09-06-2023, 06:43 PM   #6
elinkser
Groupie
elinkser has survived committing the World's Second Greatest Blunder.elinkser has survived committing the World's Second Greatest Blunder.elinkser has survived committing the World's Second Greatest Blunder.elinkser has survived committing the World's Second Greatest Blunder.elinkser has survived committing the World's Second Greatest Blunder.elinkser has survived committing the World's Second Greatest Blunder.elinkser has survived committing the World's Second Greatest Blunder.elinkser has survived committing the World's Second Greatest Blunder.elinkser has survived committing the World's Second Greatest Blunder.elinkser has survived committing the World's Second Greatest Blunder.elinkser has survived committing the World's Second Greatest Blunder.
 
Posts: 185
Karma: 146236
Join Date: Oct 2022
Device: Kobo Clara HD
OSCREEN KEYBOARD

MAKING AN ONSCREEN KEYBOARD FOR OUR TERMINAL:

Fbkeyboard is a framebuffer keyboard that we can port to Kobo's fbink library, following the fbpad-eink example.


$ mkdir fbkeyboard-build

$ cd fbkeyboard-build/


***

First we build fbpad as in the previous post, except we leave some rows empty at the bottom of the screen for the keyboard.

Assuming you have already built KOReader's koxtoolchain, NiLuJe's FBInk library, and Aligrudi/Droske's fbpads as before:

$ source ~/koxtoolchain/refs/x-compile.sh kobo env bare

$ unzip fbpad-eink-master.zip

$ cd fbpad-eink-master/

$ cp -r ../FBInk-v1.25.0/Release FBInk/

$ cp ../FBInk-v1.25.0/fbink.h FBInk/

(First make fbpadkbS, the small font version of fbpad for onscreen keyboard.)

$ nano -l conf.h
Code:
17 typedef int fbval_t;
$ nano -l Makefile
Code:
 3 CC := arm-kobo-linux-gnueabihf-gcc
 4 all: fbpadkbS
...
11 fbpadkbS: fbpad.o term.o pad.o draw.o font.o isdw.o scrsnap.o FBInk/Release/libfbink.a
...
15         rm -f *.o fbpadkbS
$ nano -l pad.c
Code:
 25         rows = fb_rows() * 29 / 51 / fnrows;
$ make

Copy the fbpadkbS binary to the /mnt/onboard/.adds/koreader/scripts/ folder on your Kobo.


(Now make fbpadkb, the large font version of fbpad for onscreen keyboard.)

$ cp ../fbpads-master/fbpad_mkfn/ar.tf fonts/
$ cp ../fbpads-master/fbpad_mkfn/ai.tf fonts/
$ cp ../fbpads-master/fbpad_mkfn/ab.tf fonts/

$ nano -l conf.h
Code:
20 #define FR              "ab.tf"
21 #define FI              "ab.tf"
22 #define FB              "ab.tf"
$ nano -l Makefile
Code:
 4 all: fbpadkb
10         xxd -i fonts/ab.tf > font.h
11 fbpadkb: fbpad.o term.o pad.o draw.o font.o isdw.o scrsnap.o FBInk/Release/libfbink.a
...
15         rm -f *.o fbpadkb
$ nano -l font.c
Code:
40     memcpy(&head, fonts_ab_tf, sizeof(head));
...
46         font->glyphs = (int*)(fonts_ab_tf + sizeof(head));
47         font->data =(char*) (fonts_ab_tf + sizeof(head) + font->n * sizeof(int));
$ xxd -i fonts/ab.tf > font.h

$ nano -l pad.c
Code:
 25         rows = fb_rows() * 25 / 42 / fnrows;
$ make

Copy the fbpadkb binary to the /mnt/onboard/.adds/koreader/scripts/ folder on your Kobo.

$ cd ..


Add the following entries to /mnt/onboard/.adds/nm/config.txt:
Code:
...
menu_item :main :fbpadkb :cmd_spawn :quiet:/mnt/onboard/.adds/koreader/scripts/fbpadkb /bin/sh 0</dev/tty1
    chain_success                      :dbg_toast          :Started fbpadkb       
    chain_failure                      :dbg_toast          :Error                                   
menu_item :main :Stop fbpadkb :cmd_spawn :quiet:/usr/bin/pkill fbpadkb 
    chain_success                      :dbg_toast          :Stopped fbpadkb                        
    chain_failure                      :dbg_toast          :Error
...


***

Freetype is a dependency of Fbkeyboard:

Freetype
https://freetype.org/download.html

Download from:
https://sourceforge.net/projects/fre...etype2/2.13.1/

$ tar zxvf freetype-2.13.1.tar.gz

$ cd freetype-2.13.1/

$ ./configure --host=arm-kobo-linux-gnueabihf

$ make

$ cd ..


***

bakonyiferenc/fbkeyboard
forked from julianwi/fbkeyboard
https://github.com/bakonyiferenc/fbkeyboard

$ wget https://github.com/bakonyiferenc/fbk...ads/master.zip

$ mv master.zip fbkeyboard-master.zip

$ unzip fbkeyboard-master.zip

$ cd fbkeyboard-master/

$ nano -l fbkeyboard.c
Code:
...
 18 */
 19
 20 #include <stdlib.h>
 21 #include <signal.h>
 22 #include <fcntl.h>
 23 #include <string.h>
 24 #include <unistd.h>
 25 #include <dirent.h>
 26 #include <errno.h>
 27 #include <linux/fb.h>
 28 #include <linux/input.h>
 29 #include <linux/uinput.h>
 30 #include <linux/vt.h>
 31 #include <ft2build.h>
 32 #include FT_FREETYPE_H
 33 #include "conf.h"
 34 #include "draw.h"
 35 volatile sig_atomic_t done = 0;
 36 char *font = "/mnt/onboard/.adds/koreader/fonts/noto/NotoSans-Bold.ttf";
...
 38 char *special[][7] = {
 39         { "Esc", "Tab", "Up ", "Dn ", "Lt ", "Rt ", "PgD" },
...
53 __u16 keys[][26] = {
 54         { KEY_ESC, KEY_TAB, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_PAGEDOWN },
......
 88 FT_Face face;
 89 int advance;    // offset to the next glyph
 90 int fduinput;
 91 struct input_event ie;
 92 int oldrow = 0, oldpressed = 0;
 93 struct timeval oldstamp, newstamp, diffstamp;
 94 int theight;    // of touchscreen
 95 int twidth;     // of touchscreen
 96 int trowh;      // heigth of one keyboard row on touchscreen
 97 
 98 void fill_rect(int x, int y, int w, int h, int color)
...
299 void show_fbkeyboard(int fbfd)
300 {
301         switch (rotate) {
302                 case FB_ROTATE_UR:
303                         lseek(fbfd, fblinelength * (fbheight - height * 6), SEEK_SET);
304                         write(fbfd, buf, buflen);
305                         keyb_refresh(fbfd, fbheight - height * 6, 0, width, fbheight);
306                         break;
307                 case FB_ROTATE_UD:
308                         lseek(fbfd, 0, SEEK_SET);
309                         write(fbfd, buf, buflen);
310                         keyb_refresh(fbfd, 0, 0, width, height * 5);
311                         break;
312                 case FB_ROTATE_CW:
313                         for (int i = 0; i < width; i++) {
314                                 lseek(fbfd, fblinelength * i, SEEK_SET);
315                                 write(fbfd, (int32_t *) (buf + linelength * i), linelength);
316                         }
317                         keyb_refresh(fbfd, 0, 0, height * 5, width);
318                         break;
319                 case FB_ROTATE_CCW:
320                         for (int i = 0; i < width; i++) {
321                                 lseek(fbfd, fblinelength * i + (fbwidth - height * 5) * 4, SEEK_SET);
322                                 write(fbfd, (int32_t *) (buf + linelength * i), linelength);
323                         }
324                         keyb_refresh(fbfd, 0, fbwidth - height * 5, fbwidth, width);
325                         break;
326         }
327 }
328 /*  Waits for a relevant input event. Returns 0 if touched, 1 if released. */
329 int check_input_events(int fdinput, int *x, int *y) 
330 {
331         int released = 0;
332         int key = 1;
333         int absolute_x = -1, absolute_y = -1;
334         while (!done && !released && (absolute_x == -1 || absolute_y == -1))
335                 while (read(fdinput, &ie, sizeof(struct input_event))
336                        && !(ie.type == EV_SYN && ie.code == SYN_REPORT)) {
337                         if (ie.type == EV_ABS) {
338                                 switch (ie.code) {
339                                         case ABS_MT_POSITION_X:
340                                                 absolute_x = ie.value;
341                                                 released = 0;
342                                                 key = 0;
343                                                 break;
344                                         case ABS_MT_POSITION_Y:
345                                                 absolute_y = ie.value;
346                                                 released = 0;
347                                                 key = 0;
348                                                 break;
349                                         case ABS_MT_TRACKING_ID:
350                                                 if (ie.value == -1) {
351                                                         released = 1;
352                                                 }
353                                                 break;
354                                 }
355                         }
...
356                         if (ie.type == EV_SYN && ie.code == SYN_MT_REPORT) {
...
357                                 released = 1;
358                         }
359                 }
360         switch (rotate) {
361                 case FB_ROTATE_UR:
362                         *x = 0x10000 - absolute_y * 0x10000 / theight;
363                         *y = absolute_x * 0x10000 / twidth;
364                         break;
365                 case FB_ROTATE_UD:
366                         *x = 0x10000 - absolute_x * 0x10000 / twidth;
367                         *y = 0x10000 - absolute_y * 0x10000 / theight;
368                         break;
369                 case FB_ROTATE_CW:
370                         *x = absolute_y * 0x10000 / theight;
371                         *y = 0x10000 - absolute_x * 0x10000 / twidth;
372                         break;
373                 case FB_ROTATE_CCW:
374                         *x = 0x10000 - absolute_y * 0x10000 / theight;
375                         *y = absolute_x * 0x10000 / twidth;
376                         break;
377         }
378         oldstamp.tv_sec = newstamp.tv_sec;
379         oldstamp.tv_usec = newstamp.tv_usec;
380         newstamp.tv_sec  = ie.time.tv_sec;
381         newstamp.tv_usec  = ie.time.tv_usec;
382         timersub(&newstamp,&oldstamp,&diffstamp);
383         return released;
384 }
385 /* x, y and trowh are scaled to 2^16 (e.g. min x = 0, max x = 65535) */
386 void identify_touched_key(int x, int y, int *row, int *pressed) 
387 {
388         switch ((0x10000 - y) / trowh) {
389                 case 5:
390                         *row = 0;               // Esc, Tab, F10, etc
391                         *pressed = x * 7 / 0x10000;
392                         break;
393                 case 4:
394                         *row = 1;               // q - p
395                         *pressed = x * 10 / 0x10000;
396                         break;
397                 case 3:
398                         *row = 1;               // a - l
399                         if (x > 0x10000 / 20 && x < 0x10000 * 19 / 20)
400                                 *pressed = 10 + (x * 10 - 0x10000 / 2) / 0x10000;
401                         break;
402                 case 2:
403                         if (x < 0x10000 * 3 / 20) {
404                                 *row = 3;
405                                 *pressed = 0;   // Left Shift
406                         } else if (x < 0x10000 * 17 / 20) {
407                                 *row = 1;       // z - m
408                                 *pressed = 19 + (x * 10 - 0x10000 * 3 / 2) / 0x10000;
409                         } else {
410                                 *row = 3;
411                                 *pressed = 1;   // Bcksp
412                         }
413                         break;
414                 case 1:
415                         *row = 4;
416                         if (x < 0x10000 * 3 / 20)
417                                 *pressed = 99;  // 123!@
418                         else if (x < 0x10000 * 5 / 20)
419                                 *pressed = 0;   // Left Alt
420                         else if (x < 0x10000 * 15 / 20)
421                                 *pressed = 1;   // Space
422                         else if (x < 0x10000 * 17 / 20)
423                                 *pressed = 2;   // Right Ctrl
424                         else
425                                 *pressed = 3;   // Enter
426                         break;
427                 default:
428                         *row = 5;               // cursor, Enter, Home, PgDn, etc
429                         *pressed = 3 * y / (0x10000 - trowh * 6);
430                         *pressed *= 3;
431                         *pressed += 3 * x / 0x10000;
432                         break;
433         }
434 }
435
...
489         } else {
490                 send_key(keys[row][pressed]);
491         }
492         oldrow = row;
493         oldpressed = pressed;
494 }
495 /* return max of rows  */
496 int reset_window_size(int fd)
...
559
560 /*      fdcons = open("/dev/tty1", O_RDWR | O_NOCTTY);
561         if (fdcons < 0) {
562                 perror("Error opening /dev/tty1");
563                 exit(-1);
564         } */
565
...
599
600         if (fb_init(FBDEV)) {
601                 fprintf(stderr, "fbkeyboard: failed to initialize the framebuffer\n");
602                 return 1;
603         }
604         if (sizeof(fbval_t) != FBM_BPP(fb_mode())) {
605                 fprintf(stderr, "fbkeyboard: fbval_t does not match framebuffer depth (%d bytes$
606                 return 1;
607         }
608         
609         timerclear(&oldstamp);
610         timerclear(&newstamp);
611         timerclear(&diffstamp);
612         fbfd = fb_fd();
613         fbwidth = fb_cols();
614         fbheight = fb_rows();
615         fblinelength = fb_length();
616         switch (rotate) {
617                 case FB_ROTATE_UR:
...
724
725         while (!done) {
726         /*      if (!ioctl(fdcons, VT_GETSTATE, &ttyinfo)) {
727                         if (tty != ttyinfo.v_active) {
728                                 tty = ttyinfo.v_active;
729                                 close(fdcons);
730                                 fdcons = open("/dev/tty1", O_RDWR | O_NOCTTY);
731                                 set_window_size(fdcons);
732                                 resized[tty] = 1;
733                         }
734                 } else {
735                         perror("VT_GETSTATE ioctl failed");
736                 } */
737
738                 draw_keyboard(row, pressed);
739                 show_fbkeyboard(fbfd);
740                 released = check_input_events(fdinput, &x, &y);
741
742                 if (released)
743                         identify_touched_key(x, y, &row, &pressed);
744                 if (pressed != -1 && (pressed != oldpressed || (long)diffstamp.tv_usec > 50000))
745                         send_uinput_event(row, pressed);
746                 pressed = -1;
747         }
748
...
760                         reset_window_size(fdcons);
761                 }
762         }
763         fb_free();
764 }
765
...

$ mkdir FBInk

$ cp -r ../FBInk-v1.25.0/Release FBInk/

$ cp ../FBInk-v1.25.0/fbink.h FBInk/

$ cp ../fbpad-eink-master/draw.c .

$ cp ../fbpad-eink-master/draw.h .

$ cp ../fbpad-eink-master/conf.h .

$ cp ../freetype-2.13.1/objs/.libs/libfreetype.so* .


$ nano -l draw.c
Code:
...
160
161 int fb_length(void) 
162 { 
163         return finfo.line_length; 
164 }
165
166 void keyb_refresh(int fd, int invalid_top, int invalid_left, int invalid_right, int invalid_bot$
167   fbink_refresh( fb_fd(),
168                  invalid_top,
169                  invalid_left,
170                  invalid_right - invalid_left,
171                  invalid_bottom - invalid_top,
172                  cfg() );
173 /*  invalid_nonempty = 0; */
174   invalid_top = invalid_bottom = invalid_left = invalid_right = 0;
175 }
176

$ nano -l draw.h
Code:
 19 int fb_length(void);
 20 void  keyb_refresh(int fd, int invalid_top, int invalid_left, int invalid_right, int invalid_bottom);
$ cp ../freetype-2.13.1/objs/.libs/libfreetype.a .

$ arm-kobo-linux-gnueabihf-gcc -o fbkeyboard -I../freetype-2.13.1/include/ fbkeyboard.c draw.c libfreetype.a FBInk/Release/libfbink.a


Copy the fbkeyboard binary to the /mnt/onboard/.adds/koreader/scripts/ folder on your Kobo.

$ cd ..


Add the following entries to /mnt/onboard/.adds/nm/config.txt:
Code:
...

          
menu_item :main :fbkeyboard :cmd_spawn :quiet:/mnt/onboard/.adds/koreader/scripts/fbkeyboard
    chain_success                      :dbg_toast          :Started fbkeyboard     
    chain_failure                      :dbg_toast          :Error                                   
menu_item :main :Stop fbkeyboard :cmd_spawn :quiet:/usr/bin/pkill fbkeyboard 
    chain_success                      :dbg_toast          :Stopped fbkeyboard                            
    chain_failure                      :dbg_toast          :Error
...

From MyBooks, go to the Activity page on your Kobo (second tab from left at bottom of screen.)

***UPDATE***
If you are not in SideloadedMode=true (in [ApplicationPreferences] in /mnt/onboard/.kobo/Kobo/Kobo eReader.conf) then you go to More (second tab from RIGHT at bottom of screen, then select Activity.)
***


From NickelMenu select Stop Agetty, fbpadkb, and fbkeyboard entries. (i.e. requires that you implemented the USB keyboard hack of the previous post.) Edit: Agetty not needed for fbkeyboard, just for fbpad+USB keyboard.

Unfortunately too slow for practical use right now.
Hopefully something can be tweaked to optimize it.

Can exit with Stop fbkeyboard and Stop fbpadkb entries.

***


EDIT: Added arrow keys to lines 39, 40 and 54 of fbkeyboard.c:
Code:
 38 char *special[][7] = {
 39         { "Esc", "Tab", " ^ ", " v ", " < ", " > ", ">>⇓" },
 40         { "Esc", "Tab", " ^ ", " v ", " < ", " > ", ">>⇓" },
 41 }; 
...
 53 __u16 keys[][26] = {
 54         { KEY_ESC, KEY_TAB, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_PAGEDOWN },
...

EDIT: Added non-blocking IO to fbkeyboard.c to get faster response:
Code:
...
334         while (!done && !released && (absolute_x == -1 || absolute_y == -1))
335                 while ((read(fdinput, &ie, sizeof(struct input_event)) > 0)
336                     /*   && !(ie.type == EV_SYN && ie.code == SYN_REPORT) */ ) {
337                         if (ie.type == EV_ABS) {
338                                 switch (ie.code) {
339                                         case ABS_MT_POSITION_X:
340                                                 absolute_x = ie.value;
341                                                 released = 0;
342                                                 key = 0;
343                                                 break;
344                                         case ABS_MT_POSITION_Y:
345                                                 absolute_y = ie.value;
346                                                 released = 0;
347                                                 key = 0;
348                                                 break;
349                                         case ABS_MT_TRACKING_ID:
350                                                 if (ie.value == -1) {
351                                                         released = 1;
352                                                 }
353                                                 break;
354                                 }
355                         }
356                         if (ie.type == EV_SYN && ie.code == SYN_REPORT) {
357                                 released = 1;
358                         }
359                 }
360         switch (rotate) {
'''
659                 while ((dptr = readdir(inputdevs))) {
660                         if ((fdinput =
661                              openat(dirfd(inputdevs), dptr->d_name,
662                                     O_RDONLY | O_NONBLOCK)) != -1
663                             && ioctl(fdinput, EVIOCGBIT(0, sizeof(key)),
664                                      &key) != -1 && key >> EV_ABS & 1)
665                                 break;
...
742                 if (released)
743                         identify_touched_key(x, y, &row, &pressed);
744                 if (pressed != -1 && (pressed != oldpressed || (long)diffstamp.tv_usec > 200000))
745                         send_uinput_event(row, pressed);
746                 pressed = -1;
747                 usleep(50000);
748         }
749         int i;
...



Unicode Character 21d3 (PageDown arrow on line 39) was entered while holding Ctrl-Sh-u.
https://en.wikipedia.org/wiki/Arrow_(symbol)



***

This code is probably in need of cleanup.
Feel free to jump in!





***
***UPDATE***
See POST #7 - Need WiFi ON (or USB networking) to avoid freezing.
***



***UPDATE***
CRITICAL STEP (or agetty won't run):
# cp /mnt/onboard/.adds/koreader/libs/ld-musl-armhf.so.1 /lib/
Edit: Agetty not needed for fbkeyboard, just for fbpad+USB keyboard.
***
Attached Files
File Type: bin fbpadkb.bin (1,000.0 KB, 67 views)
File Type: bin fbpadkbS.bin (168.3 KB, 65 views)
File Type: bin fbkeyboard.bin (575.2 KB, 73 views)

Last edited by elinkser; 12-03-2023 at 06:24 PM. Reason: EDIT: Added arrow keys,nonblock,need wifi on,agetty not needed for fbkeyboard
elinkser is offline   Reply With Quote