abclinuxu.cz AbcLinuxu.cz itbiz.cz ITBiz.cz HDmag.cz HDmag.cz abcprace.cz AbcPráce.cz
AbcLinuxu hledá autory!
Inzerujte na AbcPráce.cz od 950 Kč
Rozšířené hledání
×
včera 14:33 | Nová verze

Byla vydána nová verze 1.30 editoru zdrojových kódů Visual Studio Code (Wikipedie). Přehled novinek i s náhledy a animovanými gify v poznámkách k vydání.

Ladislav Hagara | Komentářů: 2
včera 14:22 | Nová verze

Deset dnů po představení beta verze byla vydána stabilní verze Steam Linku pro Raspberry Pi umožňující streamovat hry ve službě Steam z počítače na televizní obrazovku.

Ladislav Hagara | Komentářů: 6
13.12. 20:00 | Nová verze

Byla vydána (YouTube) verze 2018.3 multiplatformního herního enginu Unity (Wikipedie). Přehled novinek i s videoukázkami v příspěvku na blogu a v poznámkách k vydání.

Ladislav Hagara | Komentářů: 0
13.12. 19:33 | Nová verze

Byla vydána verze 18.12.0 KDE Aplikací (KDE Applications). Přehled novinek v kompletním seznamu změn a na stránce s dalšími informacemi. Správce souborů Dolphin umí nově například zobrazovat náhledy dokumentů vytvořených v LibreOffice a aplikací ve formátu AppImage. Konsole plně podporuje obrázkové znaky emoji. V Okularu lze k pdf souborům přidávat poznámky.

Ladislav Hagara | Komentářů: 10
13.12. 17:11 | Nová verze

Byla vydána nová stabilní verze 2.2 (2.2.1388.34) webového prohlížeče Vivaldi (Wikipedie). Z novinek vývojáři zdůrazňují například vylepšení správy listů - vybrané listy lze uložit jako relaci, možnost zobrazení klávesových zkratek určených webovou stránkou nebo možnost přehrávání videí v režimu obrazu v obraze. Nejnovější Vivaldi je postaveno na Chromiu 71.0.3578.85.

Ladislav Hagara | Komentářů: 8
13.12. 14:22 | Nová verze

Po 4 měsících vývoje od vydání verze 3.0.0 byla vydána nová verze 3.1.0 otevřeného emulátoru procesorů a virtualizačního nástroje QEMU (Wikipedie). Přispělo 189 vývojářů. Provedeno bylo více než 1 900 commitů. Přehled úprav a nových vlastností v seznamu změn.

Ladislav Hagara | Komentářů: 0
13.12. 01:32 | Nová verze

Letos bylo v komunitě Mageia hodně změn. Po volbě nových vedoucích přišla velká aktualizace a krátce na to udržovací verze 6.1. 7.12., dle plánu, vyšla Mageia s číslem 7 v její první beta verzi. Chyby můžete hlásit v bugzille. Chyby v českých překladech pak na fóru české komunity.

Joelp | Komentářů: 3
13.12. 00:11 | Zajímavý projekt

Kvůli rychlejšímu vývojovému cyklu byla přemístěna Cinelerra-gg. Cinelerra-gg je fork Cinelerry-hv. Některé rozdíly forků popisuje sám hlavní vývojář William Morrow (aka GoodGuy). Není zde popsán i fork Lumiera, zřejmě kvůli zatím nepoužitelnému stavu.

… více »
D81 | Komentářů: 1
12.12. 19:11 | Nová verze

Do aplikace pro instant messaging Telegram (Wikipedie) lze nově nahrát češtinu. Více v příspěvku na blogu Telegramu.

Ladislav Hagara | Komentářů: 7
12.12. 10:55 | Nová verze

Jean-Baptiste Kempf, prezident neziskové organizace VideoLAN stojící za svobodným multiplatformním multimediálním přehrávačem a frameworkem VLC, oznámil v příspěvku na svém blogu vydání první oficiální verze 0.1.0 v říjnu představeného dekodéru svobodného videoformátu AV1 (AOMedia Video 1) s názvem dav1d (Dav1d is an AV1 Decoder). Jedná se o alternativu k referenčnímu dekodéru libaom. Kódový název dav1da verze 0.1.0 je Gazelle.

Ladislav Hagara | Komentářů: 3
Chystáte se přejít na Wayland na „desktopu“?
 (25%)
 (7%)
 (12%)
 (31%)
 (25%)
Celkem 129 hlasů
 Komentářů: 19, poslední včera 18:37
Rozcestník

How crazy can I get? part 2

5.8. 03:41 | Přečteno: 2721× | Výběrový blog | poslední úprava: 5.8. 03:51

Let's get the insanity to continue...

OK these were funny early tests, but with a new engine I've had to solve the real limitations of shell. Let's go a little back first ... :-P

Input system

If you want to write any complex shell script with the user input you need to get the input data inside script variables. This actually seems to be impossible. You cannot make multithreaded scripts even in the bash and the interprocess communication is limited to the signal trapping or reading the data through pipes.

A possible solution would be to have one reader and multiple writers, where every writer can block-read the device (keys, touchscreen, ...). The problem is even with this "multiprocess" input reads it is probably impossible to parse stdin in a shell. Maybe in the bash but not in the busybox shell used in the phone.

The bash "read" function supports settings for the reading a specified number of characters and the timeout when they are not delivered. You need read a mupltiple bytes from the stdin for arrows, function keys, ctrl+something, etc... ). The busybox shell (an implementation of a posix Almquist shell) doesn't reliably support this (and in the past it only supported an integer number of the timeout in seconds). Funny thing: if a stdin read ends with a newline, the read function returns immediately.

As the parsing of a touchscreen events gone wrong too (otherwise you could have parsed an evtest utility or just a raw evdev input data) I've decided to write a C backend. The libc read() does block and it returns any number of characters (depends on the terminal setting). Same is the parsing of the touchscreen events where we can just use the kernel uapi headers for the evdev. And finally multiple threads in a C are a trivial thing.

This backend just formats all inputs (there is a timer event too) and write them all to the stdout where the shell script is listening with its own (dumb) read.

Almquist shell

Yeah the busybox doesn't even use bash. It is /bin/sh equivalent with some expansions which are compatible with bash (many features are configurable during build). The most obvious disadvantage is the lack of bash arrays, but it's fine, the language is still turing-universal even without them :-D. I've actually never used arrays in a bash so yeah screw them. Well you don't need them until you want to write a GUI in a shell script.

So, back to the history...

ANSI escape codes craziness:

zeta version

Wow what a nicely moving ζ version demo... (and what ugly colors). How this ugly beauty can be done in the shell?

BTW it's a rose on the background...

Do you know how unicode UTF-8 works for parsing? The ansi escape codes are a level of magnitude worse. They are basically a terminal control sequences (colors, goto, clear, bell or switching the fonts). That's why when you run cat /dev/urandom in a framebuffer terminal it can make all texts unreadable. The parsing of the ansi escape codes works this way: it doesn't. You would need to make a state machine in shell without arrays and it would be too much. Best thing is to design and to limit ourselves in a way where every string fits on its place. This is the first limitation of the shell GUI: you cannot have partially visible line, where the rest of it is outside the screen.

Problems begin if you actually want to write something new over something old, without a parsing hydra obviously. A simple solution is to just write the lowest layer (background), the middle layer (icons menu) and the top layer (flying window). With the layers the strings can be partially visible, but they still need to be rendered under a higher layer. The leaking colors from lower layers can bleed to a higher layer, but luckily there is an escape code "\x1b[0m" which resets all attributes (and it even works in the most cases). To fix the color leaks we just need to end every line of the layer with this code.

OK let's escape all strings, so there is no attribute leakage... oh and change the background:

eta scroll animation

But now we need to draw even invisible strings, so what is the solution to speed things now? Let's implement a z-buffer in the shell! ... Did I mention no arrays?

These versions had about 2-3 layers and every graphical object consisted of line elements. So how do we make a z-buffer? We can put every line of the every layer into as an item of an array. And as we don't have arrays, let's make them artificially by using the eval command.

In that way, a C equivalent of:

struct line {
    char *data;
};

struct line layer[40];
...
layer[idx].data="hello world";

became this:

eval MENU_LINE_${idx}=\"hello world\"

... yes, this makes a new variable for every line. No, there is no safety checking and if you do this:

badvar=" echo hello"
eval somevar="${badvar}"

The eval won't store the string in "somevar" variable, instead it will execute the echo command. This is because the spaces in a string assigned to the badvar variable will be split in the second stage of the eval. We would need to escape the quotation marks for the second stage of the eval like this:

badvar=" echo hello"
eval somevar=\"${badvar}\"

The problem is that this code is still wrong, let's demonstrate it by running this code:

badvar=" echo hello\" \"format c:"
eval somevar=\"${badvar}\"
...
bash: format c:: command not found

The evil \" in the string will get evaluated in the second stage so the command will effectively do this:

somevar=" echo hello" format c:

which is a normal command with a set environment variable. Now imagine escaping a string with spaces, unicode characters and ansi escape codes mixed with variables in an eval expression. Yum!

There are two ways to fix that: either use apostrophes everywhere or make the variable to evaluate in the second eval stage by escaping its dollar sign (too bad there is no third eval stage :-D ... I'm really using double evals in the current code of the engine).

So what about doing it at the same time?

eval somevar=\'\${badvar}\'

Nope it gets into the second stage as '${badvar}' and things inside the apostrophes doesn't evaluate at all. We can chose only one of the two solutions. Are as we are making an artificial arrays, we need to evaluate the index in the first stage:

eval oneline=\"\${array_line_${idx}}\"

So only way to be at least a little bit save is by escaping the final variable name and hope the index variable has a safe content.

BTW we can make multidimensional sparse arrays this way too.

mindblown

BTW the first idea of the array of the lines was to put it in one really long string and to do an offset strcpy:

output="${var:position:offset}"

It turned out that any string operation in the shell is really slow (as we will see in the future).

Z-buffer

We have individual lines of different layers, so let's make them ordered. For θ version it was easy:

theta version

there were hardcoded levels for the background (tux got colors!), scrollbar, frame focus and menu. When there was a change on the specified line the engine would just redraw it with a specified layer. All the modification events would be stored in the lisp-like string. For example an event buffer "1 A 2 B 3 C ":

while [ "$ZBUFFER_LINE" != "" ] ; do
    T_LINE="${ZBUFFER_LINE%% *}"
    ZBUFFER_LINE="${ZBUFFER_LINE#* }"
    T_LAYER="${ZBUFFER_LINE%% *}"
    ZBUFFER_LINE="${ZBUFFER_LINE#* }"
    ...

We can use a string pattern match and delete everything after the first space. We get T_LINE=1 that way, and then delete everything before and including with the first space and repeat the procedure for getting T_LAYER="A" ... and repeating the whole procedure until the buffer (ehm string) is empty.

Let's see how the command looked like for the line reading from arrays for a focus frame (yeah it had a hardcoded size):

elif  [ "$T_LAYER" == "C" ] ; then  #focus
    eval TEMP_FOCUS_LINE="\$FOCUS_LINE_$(($T_LINE-($FOCUS_LINE_Y_BEG-$LCD_LINE_CURR+$LCD_LINE_FIRST)))"

It is just a trivial computation for the position of the menu below and it is using an equation evaluation as a part of the "array" name. I actually found a bug in the busybox almquist shell with this code (can somebody tell me why nobody found it ahead of me? Such an everyday-use code :-D).

BTW during the development I've found an ansi escape function "\x1b[nM" and "\x1b[nL" which written to the stdout can scroll a region of the terminal by "n" lines up or down. This is a very useful function as you have to only redraw the new added lines on the bottom or on the top. With this I've got so much computation power I've could even use touchscreen to drag the menu up and down in nearly a realtime speed :-D.

The final part of a flush is to dump the whole buffer to /dev/tty device.

echo -e -n "\x1b[11m${FB_CMD_MENU_SCROLL}${T_FB_BUFFER}${FB_FOCUS}${FB_POPUP}${FB_HOVER}${FB_SCROLL}" > $SERVICE_TTY

The code is inefficient as it is called every loop in a flush() function. If we run strace on the script we will find the redirection does an open-write-close sequence every loop, which is slow on PXA272 SoC (the context switches!). We need to have the stdout file descriptor constantly opened outside the main loop.

The horror of the main loop

As written before, the shell scripts are singlethreaded. Every input read for the whole system must be done in a single loop as it depends on an active pipe from the input backend and the shell "read" function. It is the only place where the code can block with the waiting for an event. This makes things like a "choice" dialog above a big challenge (one big state machine challenge).

In the current version the main loop takes just a few lines, but there were times it took about 20% lines of the whole project. But let's look on an actual version rather (with some spoilers):

exec 4>${c_service_tty}
rm ${c_event_fifo}
mkfifo ${c_event_fifo}
exec 3<>${c_event_fifo}

./backend 2>/dev/null >&3 <${c_service_tty} &

while read -s time type params ; do
    case ${type} in
    ms1|drag1|unms1)
        lib_engine_first_method_at_xy ${type} ${params}
        if [ ${ret_wid} -ge 0 ] ; then
            lib_engine_queue ${ret_wid} ${type} ${time} ${params}
        else
            continue
        fi
        ;;
    dump)
        dump
        continue
        ;;
    quit)
        break
        ;;
    menu)
        lib_engine_queue ${fs_start} menu ${time} ${params}
        ;;
    timer)
        lib_engine_timer_trig ${time}
        ;;
    *)
        #event on focused WID
        lib_engine_top_method_of_focus_tree ${type}
        if [ ${ret_wid} -ge 0 ] ; then
            lib_engine_queue ${ret_wid} ${type} ${time} ${params}
        else
            continue
        fi
        ;;
    esac

    lib_engine_flush

done <&3 >&4

One problem with this solution (except it is in a shell) is the use of an explicitly created named pipe (mkfifo). There is a hack to steal an anonymous pipe "|" from a subprocess, but it has timing problems.

The stdin/stdout descriptors on the end of the "while" loop makes sure there is no open/close kernel calls.

How to load an external "applet"?

Executing menu items (scripts with devmem/modprobe/gpio toggling) everytime from the filesystem would be slow. Why not preload all the applet code into a single shell function. Guess which built-in command we use? .... yup eval again.

So how we do that?

T_APPLET="applet_code() { ${LF} $(cat "${FILENAME}") ${LF} }"
eval "$T_APPLET"

And with multiple objects the situation gets better:

fcn="wid_${new_wid}_exec() { $(cat "${filename}") ; }"
eval "$fcn"

As the function name is stored as a string, we can dynamically construct names!

And it gets even better:

fcn="wid_${new_wid}_exec() { param1=${value1}; $(cat "${filename}") ; }"
eval "$fcn"

Why not add some constant code inside a general function?

I could continue with dynamic function renaming by using set, grep, sed and eval, but the behavior is not enough consistent between the shell implementations and it is an ugly code, so let's draw a line here. I left out the feature in a new rewrite of the engine actually.

But first let take a look what can be achieved with calls of a dynamically loaded script:

iota version iota version

... a very fast (for a shell script) virtual touchscreen keyboard. The fields (icons) of all applets (= scripts = buttons) are changed by the top level layout of the keyboard. Written text can be run in a real linux terminal. The return button is just a directory change to "../" so the engine can load other applets (only one directory was loaded at the same time - variable names are still highly hardcoded).

One last change before the complete rewrite .. of course the background :-D.

kappa background

You can do it with any libcaca application by setting the environment variables, for example mplayer:

CACA_GEOMETRY=40x30 CACA_DRIVER=ncurses mplayer -vo caca ./tux2.png -really-quiet > /tmp/tux.nc

And what happens when you try to save the ansi escape code file in a variable:

rip tux

In the next part we will have a fun with evaluation and finally some source code.

If you have found this idea funny or you want me to rewrite the engine in a more sane language, consider a donate to my BTC address: 1CqzWKDLdcbjbzvzkrcP5bHsScjm5CwcjH or to my ETH address: 0x1440d31eD70fc6b084c71f03590F6e1d121C919E

       

Hodnocení: 80 %

        špatnédobré        

Tiskni Sdílej: Linkuj Jaggni to Vybrali.sme.sk Google Del.icio.us Facebook

Komentáře

Vložit další komentář

5.8. 15:08 Odin
Rozbalit Rozbalit vše Re: How crazy can I get? part 2
Tolik textu, jiste mnoho straveneho casu a efekt nijaky. Toto uz dnes neni cool a ani uzitecne.
6.8. 23:18 pc2005 | skóre: 37 | blog: GardenOfEdenConfiguration | liberec
Rozbalit Rozbalit vše Re: How crazy can I get? part 2
Tak to bych tedy nečetl další části tam to teprve nabere ty grády :-D.
5.8. 15:31 _
Rozbalit Rozbalit vše Re: How crazy can I get? part 2
Česky prosím, nevidim přidanou hodnotu v louskání czenglish...
6.8. 12:07 Nechce se mi přihlasovat
Rozbalit Rozbalit vše Re: How crazy can I get? part 2
Číst to nemusíš. Ber to jako jazykové cvičení, jež se pisatel z nějakého důvodu rozhodl dát na web.
6.8. 23:16 pc2005 | skóre: 37 | blog: GardenOfEdenConfiguration | liberec
Rozbalit Rozbalit vše Re: How crazy can I get? part 2
Jsem to psal v angličtině s tím, že bych na konci dal odkazy na reddit, ale bystroushaak to už udělal za mě. Asi bych to mohl dát i do češtiny kdybych měl čas, klidně mě motivuj, viz adresy na konci :-P.
Bystroushaak avatar 7.8. 09:58 Bystroushaak | skóre: 33 | blog: Bystroushaakův blog | Praha
Rozbalit Rozbalit vše Re: How crazy can I get? part 2
Na konci je tam určitě hoď všechny, ale jak jsem psal dole, asi bych dal lepší titulek o čem to je.
IRC: #mindspace@freenode.net
Bystroushaak avatar 5.8. 15:56 Bystroushaak | skóre: 33 | blog: Bystroushaakův blog | Praha
Rozbalit Rozbalit vše Re: How crazy can I get? part 2
Good job. Kinda creepy, but nicely done at the same time. I've reposted your blogs to /r/programming to get some love for them:
IRC: #mindspace@freenode.net
Bystroushaak avatar 5.8. 20:58 Bystroushaak | skóre: 33 | blog: Bystroushaakův blog | Praha
Rozbalit Rozbalit vše Re: How crazy can I get? part 2
Yeah. I should have used more descriptive titles for this subreddit.
IRC: #mindspace@freenode.net
5.8. 21:28 mech
Rozbalit Rozbalit vše Re: How crazy can I get? part 2
Dobrá práce jako vždy /s
Bystroushaak avatar 7.8. 10:00 Bystroushaak | skóre: 33 | blog: Bystroushaakův blog | Praha
Rozbalit Rozbalit vše Re: How crazy can I get? part 2
¯\_(ツ)_/¯
IRC: #mindspace@freenode.net
5.8. 20:25 coooool
Rozbalit Rozbalit vše Re: How crazy can I get? part 2
Super informative - the best tutorial to sHell I ever read :).

Založit nové vláknoNahoru

ISSN 1214-1267   www.czech-server.cz
© 1999-2015 Nitemedia s. r. o. Všechna práva vyhrazena.