├── .gitignore ├── README.md ├── configs ├── .fbtermrc ├── .zshrc └── ido.zsh-theme ├── images ├── bg │ ├── bg1.jpg │ └── bg2.jpg ├── bgc-image.jpg ├── devterm.jpg ├── en2ja.jpg └── fcitx-moz.jpg └── scripts ├── battery ├── brightness ├── en2ja ├── fbstart ├── gearbox └── ja2en /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DevTerm hobby project 2 | 3 | I bought this amazing device: [DevTerm A-0604](https://www.clockworkpi.com/devterm). It has a beefy ARM processor, runs a custom version of [Armbian](https://www.armbian.com/), embraces Open Source / Open Hardware philosophy and looks dope! 4 | 5 | It also has LightDM + xfce as a GUI but it's kinda boring to use this awesome retro-looking device with any kind of a graphical interface. **Let's try to set it up for a serious business and a maximum entertainment without any GUI** - just how it was in the good old days. 6 | 7 | Last Linux distro I've touched (not counting servers) was Red Hat Linux 9 (not RHEL!). I know nothing about modern linuxes + I always forget all the voodoo you have to do to make them work. So here I'll be storing my notes and useful scripts as I go with this hobby project 8 | 9 | 10 | 11 | ## Roadmap 12 | - [x] disable GUI 13 | - [x] enable ssh connection 14 | - [x] connect to home wifi 15 | - [x] basic scripts (battery charge and brightness control) 16 | - [x] render UTF, Cyrillic, Japanese in TTY 17 | - [x] battery indicator in zsh prompt 18 | - [ ] nice setup for tmux (themes, plugins) 19 | - [ ] email client (gmail, protonmail) 20 | - [x] games: nethack 21 | - [ ] games: dcss 22 | - [x] ~~twitter~~ mastodon client 23 | - [x] hckrnews client 24 | - [x] stonks tracker 25 | - [ ] IDE / code editor 26 | - [ ] text editor 27 | - [ ] rust 28 | - [ ] can I see pictures in the TTY? 29 | - [ ] what about video? 30 | - [x] music / spotify player 31 | - [ ] gamepad to scroll text in terminal 32 | - [ ] soluiton for browser 33 | - [ ] start fdterm+fcitx on load 34 | - [x] bg image in fbterm 35 | - [x] japanese input in tty 36 | - [x] google translate in terminal 37 | - [x] auth by usb stick 38 | - [ ] plug a mini cassette recorder, do some magic to let me store data using cute tiny mini audio-cassettes.. yum! 39 | - [x] some cool p2p stuff 40 | 41 | ## Notes 42 | 43 | 1) Terminal. Standard TTY has a very limited capabilities to render fonts. Had troubles to render Japanese characters (kanji, hirogana and katakana). Got success with using `fdterm` with [Iosevka Term](https://typeof.net/Iosevka/) and [Noto Sans JP](https://fonts.google.com/noto/specimen/Noto+Sans+JP?subset=japanese) fonts. Downloaded them and copied into `/usr/share/fonts/(truetype|opentype)` and update `~/.fbtermrc`. If the first font doesn't contain the glyph for the rendering character, it will try second font and etc (1). (Still need to try to figure out how to autostart `fdterm`) 44 | 45 | 2) To connect to WiFi I've tried `wpa_cli` but you need to be really smart to use it. Not my case. Fallbacked to use `nmcli` and it works like a charm `nmcli device wifi connect password 46 | 47 | 3) Browser. Had some hopes for [Browsh](https://www.brow.sh/) but it's super slow (uses headless Firefox behind the scenes) and rendering getting borked in `fdterm`. Have to use `lynx` for now, and my Macbook 48 | 49 | 4) Translations. After quick search was able to find `libtranslate-bin` but there were no builds for ARM64 and I was about to start building it from sources. However, looked for alternatives in the apt repo and found [translate-shell](https://github.com/soimort/translate-shell). Works amazingly well for my needs 50 | 51 | 5) There is a cool zsh plugin as part of `oh-my-zsh` called [battery](https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/battery). To make it work you need to install `acpi` and update your prompt. Works flawlessly but I want to use battery indicator in `tmux` 52 | 53 | 6) Games. [DCSS](https://crawl.develz.org/) is a cool roguelike crawler. Doesn't have ARM64 binary, got the [source](https://github.com/crawl/crawl), followed the [instructions](https://github.com/crawl/crawl/blob/master/crawl-ref/INSTALL.md) and built it without any problems. However attempting to start it throws an error `Terminal too small` because it needs min height of 24 lines (DevTerm is only 19). Tried to change [this](https://github.com/crawl/crawl/blob/01218726429e4ea9e687ec3926d8e238243f126e/crawl-ref/source/defines.h#L24) and disable checks for the resolution. Now it crashes. 54 | 55 | 7) Used [this guide](https://forum.clockworkpi.com/t/using-gamepad-arrows-and-buttons-in-command-line-apps/7059/2?u=godzil) to change the way gamepad on DevTerm keyboard works 56 | 57 | 8) Installed `gpm` (using [this](https://www.geeksforgeeks.org/gpm-command-in-linux-with-examples/)) to enable mouse in tty. Didn't need to setup anything. It just works 58 | 59 | 9) Hackernews. Use [hn-text](https://github.com/piqoni/hn-text), install via `go install github.com/piqoni/hn-text@latest` 60 | 61 | 10) Games. [Nethack](https://packages.debian.org/sid/games/nethack-console) just works! 62 | 63 | 11) CPU scaling. Clockworkpi made a [script](https://forum.clockworkpi.com/t/devterm-a-06-core-cpu-frequency-scaling/7135) to adjust CPU/GPU cores dynamically 64 | 65 | 12) Spotify. You can get [spotifyd](https://github.com/Spotifyd/spotifyd) complied from sources without a problem, just follow the official [docs](https://spotifyd.github.io/spotifyd/installation/Ubuntu.html) - that allows to play spotify on a devterm. Now, there is [spotify-tui](https://github.com/Rigellute/spotify-tui) to control it via the terminal (to register correctly I [had to tunnel the dev server](https://github.com/Rigellute/spotify-tui/issues/732) as lynx wasn't able to render the spotify auth page. App itself works but still missing a lot of features. 66 | 67 | 13) Mastodon client. [toot](https://github.com/ihabunek/toot) is a terminal based client written in Python. Install it by running `pip install --user toot`. Doesn't support 2-factor auth but otherwise, works like charm 68 | 69 | 15) To track our stocks portfolio, we will be using [mop](https://github.com/mop-tracker/mop). It's a Go program that we will have to build ourselves. We need Go amd64 binaries ([provided officially](https://golang.google.cn/dl/)) then just follow the [manual](https://go.dev/doc/install) to move them into the right folder, then build `mop` according to official documentation 70 | 71 | ## Japanese input method in terminal 72 | 73 | I stumbled upon [this good video](https://www.youtube.com/watch?v=lJoXhS4EUJs) where Brodie Robertson talks about input method frameworks, input method engines and shows how to setup `fcitx` with `fcitx-moz` to enable Japanese input method for Arch linux. 74 | 75 | However we need to make all of it working in our terminal. Luckily for us there is `fbterm` support and as a double win - we have all needed packages in Armbian repo. So I've installed `sudo apt install fcitx fcitx-mozc fcitx-frontend-fbterm fcitx-ui-classic`. 76 | 77 | Configuration in `~/.config/fcitx/config` seems pretty odd. I wasn't able to make it work with any other trigger key except for the default one (Ctrl+Space). However I changed `SwitchKey` to be `L_ALT` (instead of defaule L_SHIFT) and it made my life better. 78 | 79 | To be able to use Ctlr+Space combination I had to update permissions for `fbterm` by executing `setcap 'cap_sys_tty_config+ep' /usr/bin/fbterm ` 80 | 81 | Then to start `fbterm` with `fcitx` you simply run `fcitx-fbterm-helper -l`. Press Ctrl+Space to enable it and then Alt to switch between US and JA input methods. 82 | 83 | 84 | 85 | ## Set background image in terminal 86 | 87 | We have [this good guide](https://askubuntu.com/questions/278863/how-do-i-set-up-a-background-image-for-console) that I followed to make the magic happen. We need to build `fbv` from sources and for that we need to: 88 | 89 | - get stock libjpeg dev by using `sudo apt-get install libjpeg-dev` 90 | - build libungif from sources: 91 | ``` 92 | git clone https://github.com/Distrotech/libungif.git 93 | autoreconf -f -i 94 | ./configure 95 | make 96 | sudo make install 97 | ``` 98 | - build old libpng (1.2) because `fbv` doesn't like the latest one Armbian repo has 99 | 100 | ``` 101 | # get .tar.gz from https://sourceforge.net/projects/libpng/ 102 | tar xfv 103 | ./configure 104 | make 105 | sudo make install 106 | sudo ldconfig 107 | ``` 108 | 109 | Now it's time to get and build `fbv`: 110 | 111 | ``` 112 | wget http://s-tech.elsat.net.pl/fbv/fbv-1.0b.tar.gz 113 | tar xfv fbv-1.0b.tar.gz 114 | ./configure 115 | sudo checkinstall 116 | ``` 117 | 118 | Now according to the `fbterm` [manual](https://linux.die.net/man/1/fbterm), this is the script we can create to run fbterm with the background image: 119 | 120 | ``` 121 | #!/bin/bash 122 | 123 | # fbterm-bi: a wrapper script to enable background image with fbterm 124 | # usage: fbterm-bi /path/to/image fbterm-options 125 | 126 | echo -ne "\e[?25l" # hide cursor 127 | 128 | fbv -ciuker "$1" << EOF 129 | q 130 | EOF 131 | 132 | shift 133 | export FBTERM_BACKGROUND_IMAGE=1 134 | exec fbterm "$@" 135 | ``` 136 | 137 | I've slightly modified it and put in this repo as `./scripts/fbstart`. Remember, we actually don't start `fbterm` directly but run it via `fcitx-fbterm-helper` instead. If you are doing the same thing, you would also would like to edit `/usr/bin/fcitx-fbterm-helper` to comment all `echo` lines there. Otherwise those messages will become the part of your background. 138 | 139 | One more thing: for some reason my `fbterm` requires a counter clockwise rotation to render itself correctly, so I had to rotate the image as well. 140 | 141 | At the end it looks like this: 142 | 143 | 144 | 145 | ## Login & sudo via Yubikey (USB Security Key) 146 | 147 | I have [Yubikey 5 NFC](https://www.yubico.com/products/yubikey-5-overview/) so it was logical to set it up with DevTerm. 148 | 149 | First you need to generate One Time Password (OTP) and upload it to [Yubikey Cloud](https://upload.yubico.com/), then you have to generate [API keys](https://upgrade.yubico.com/getapikey/) - store it somewhere secure. 150 | 151 | For the same step you can download and use Yubikey Manager app on your other machine. I used it to setup long button press on a key to return the OTP 152 | 153 | Then you will have to get [yubico-pam](https://github.com/Yubico/yubico-pam). I've built it myself following the instruction in the README. But to do that you will have to build a fee other dependencies first. Namely 154 | [yubico-c](https://developers.yubico.com/yubico-c/) and [yubico-c-client](https://developers.yubico.com/yubico-c-client/) 155 | 156 | And to build them I had to install quite a few libraries (maybe even missing something): 157 | 158 | ``` 159 | sudo apt-get install libcurl4-openssl-dev libpam-dev help2man libxslt1-dev docbook-xsl xsltproc libxml2-utils asciidoc 160 | ``` 161 | 162 | After that follow the configuration instructions for [yubico-pam](https://github.com/Yubico/yubico-pam#configuration), add to `/etc/pam.d/sudo` and `/etc/pam.d/login` this (you can remove `debug` later after you verify that it works: 163 | 164 | ``` 165 | auth sufficient pam_yubico.so id=[Your API ID] debug 166 | ``` 167 | 168 | then create `~/.yubico/authorized_yubikeys` with your username and Yubikey ID (that's the info from "Public identity" field when you generate your OTP password) 169 | 170 | ``` 171 | username: 172 | ``` 173 | 174 | then move `pam_yubico.so` to `/lib/security/` 175 | 176 | ``` 177 | mv /usr/local/lib/security/pam_yubico.so /lib/security/ 178 | ``` 179 | 180 | and that's it! Next time you login/sudo you will prompted by `YubiKey for 'USERNAME':` and you just need to simply touch your key. You should see something like this: 181 | 182 | ``` 183 | debug: pam_yubico.c:943 (parse_cfg): called. 184 | debug: pam_yubico.c:944 (parse_cfg): flags 32768 argc 2 185 | debug: pam_yubico.c:946 (parse_cfg): argv[0]=id=00000 186 | debug: pam_yubico.c:946 (parse_cfg): argv[1]=debug 187 | debug: pam_yubico.c:947 (parse_cfg): id=00000 188 | debug: pam_yubico.c:948 (parse_cfg): key=(null) 189 | debug: pam_yubico.c:949 (parse_cfg): debug=1 190 | debug: pam_yubico.c:950 (parse_cfg): debug_file=1 191 | debug: pam_yubico.c:951 (parse_cfg): alwaysok=0 192 | debug: pam_yubico.c:952 (parse_cfg): verbose_otp=0 193 | debug: pam_yubico.c:953 (parse_cfg): try_first_pass=0 194 | debug: pam_yubico.c:954 (parse_cfg): use_first_pass=0 195 | debug: pam_yubico.c:955 (parse_cfg): always_prompt=0 196 | debug: pam_yubico.c:956 (parse_cfg): nullok=0 197 | debug: pam_yubico.c:957 (parse_cfg): ldap_starttls=0 198 | debug: pam_yubico.c:958 (parse_cfg): ldap_bind_as_user=0 199 | debug: pam_yubico.c:959 (parse_cfg): authfile=(null) 200 | debug: pam_yubico.c:960 (parse_cfg): ldapserver=(null) 201 | debug: pam_yubico.c:961 (parse_cfg): ldap_uri=(null) 202 | debug: pam_yubico.c:962 (parse_cfg): ldap_connection_timeout=0 203 | debug: pam_yubico.c:963 (parse_cfg): ldap_bind_user=(null) 204 | debug: pam_yubico.c:964 (parse_cfg): ldap_bind_password=(null) 205 | debug: pam_yubico.c:965 (parse_cfg): ldap_filter=(null) 206 | debug: pam_yubico.c:966 (parse_cfg): ldap_cacertfile=(null) 207 | debug: pam_yubico.c:967 (parse_cfg): ldapdn=(null) 208 | debug: pam_yubico.c:968 (parse_cfg): ldap_clientcertfile=(null) 209 | debug: pam_yubico.c:969 (parse_cfg): ldap_clientkeyfile=(null) 210 | debug: pam_yubico.c:970 (parse_cfg): user_attr=(null) 211 | debug: pam_yubico.c:971 (parse_cfg): yubi_attr=(null) 212 | debug: pam_yubico.c:972 (parse_cfg): yubi_attr_prefix=(null) 213 | debug: pam_yubico.c:973 (parse_cfg): url=(null) 214 | debug: pam_yubico.c:974 (parse_cfg): urllist=(null) 215 | debug: pam_yubico.c:975 (parse_cfg): capath=(null) 216 | debug: pam_yubico.c:976 (parse_cfg): cainfo=(null) 217 | debug: pam_yubico.c:977 (parse_cfg): proxy=(null) 218 | debug: pam_yubico.c:978 (parse_cfg): token_id_length=12 219 | debug: pam_yubico.c:979 (parse_cfg): mode=client 220 | debug: pam_yubico.c:980 (parse_cfg): chalresp_path=(null) 221 | debug: pam_yubico.c:981 (parse_cfg): mysql_server=(null) 222 | debug: pam_yubico.c:982 (parse_cfg): mysql_port=0 223 | debug: pam_yubico.c:983 (parse_cfg): mysql_user=(null) 224 | debug: pam_yubico.c:984 (parse_cfg): mysql_database=(null) 225 | debug: pam_yubico.c:1020 (pam_sm_authenticate): pam_yubico version: 2.28 226 | debug: pam_yubico.c:1035 (pam_sm_authenticate): get user returned: ido 227 | debug: pam_yubico.c:222 (authorize_user_token): Dropping privileges 228 | debug: util.c:351 (check_user_token): Authorization line: ido:*********** 229 | debug: util.c:356 (check_user_token): Matched user: ido 230 | debug: util.c:362 (check_user_token): Authorization token: :*********** 231 | debug: util.c:362 (check_user_token): Authorization token: (null) 232 | debug: pam_yubico.c:1157 (pam_sm_authenticate): Tokens found for user 233 | YubiKey for `ido': 234 | debug: pam_yubico.c:1220 (pam_sm_authenticate): conv returned 44 bytes 235 | debug: pam_yubico.c:1234 (pam_sm_authenticate): Skipping first 0 bytes. Length is 44, token_id set to 12 and token OTP always 32. 236 | debug: pam_yubico.c:222 (authorize_user_token): Dropping privileges 237 | debug: util.c:351 (check_user_token): Authorization line: ido::*********** 238 | debug: util.c:356 (check_user_token): Matched user: ido 239 | debug: util.c:362 (check_user_token): Authorization token: :*********** 240 | debug: util.c:366 (check_user_token): Match user/token as ido/:*********** 241 | debug: pam_yubico.c:1276 (pam_sm_authenticate): OTP: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ID: :*********** 242 | debug: pam_yubico.c:1277 (pam_sm_authenticate): Token is associated to the user. Validating the OTP... 243 | debug: pam_yubico.c:1279 (pam_sm_authenticate): ykclient return value (0): Success 244 | debug: pam_yubico.c:1280 (pam_sm_authenticate): ykclient URL used: https://api.yubico.com/wsapi/2.0/verify?id=00000&nonce=aaaaaaaayxnvkpqixgkufyhbxnyxztvg&otp=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa×tamp=1 245 | debug: pam_yubico.c:1348 (pam_sm_authenticate): done. [Success] 246 | ```` 247 | 248 | Done! 249 | 250 | ## Screenshots 251 | 252 | (1) Translations + JA glyph rendering in fbterm 253 | 254 | 255 | 256 | ## License 257 | 258 | MIT unless specified otherwise 259 | -------------------------------------------------------------------------------- /configs/.fbtermrc: -------------------------------------------------------------------------------- 1 | # Configuration for FbTerm 2 | 3 | # Lines starting with '#' are ignored. 4 | # Note that end-of-line comments are NOT supported, comments must be on a line of their own. 5 | 6 | 7 | # font family names/pixelsize used by fbterm, multiple font family names must be seperated by ',' 8 | # and using a fixed width font as the first is strongly recommended 9 | font-names=Iosevka Term,Noto Sans JP 10 | font-size=20 11 | 12 | # force font width (and/or height), usually for non-fixed width fonts 13 | # legal value format: n (fw_new = n), +n (fw_new = fw_old + n), -n (fw_new = fw_old - n) 14 | # 15 | font-width=9 16 | #font-height= 17 | 18 | # default color of foreground/background text 19 | # available colors: 0 = black, 1 = red, 2 = green, 3 = brown, 4 = blue, 5 = magenta, 6 = cyan, 7 = white 20 | color-foreground=7 21 | color-background=0 22 | 23 | # max scroll-back history lines of every window, value must be [0 - 65535], 0 means disable it 24 | history-lines=1000 25 | 26 | # up to 5 additional text encodings, multiple encodings must be seperated by ',' 27 | # run 'iconv --list' to get available encodings. 28 | text-encodings= 29 | 30 | # cursor shape: 0 = underline, 1 = block 31 | # cursor flash interval in milliseconds, 0 means disable flashing 32 | cursor-shape=0 33 | cursor-interval=500 34 | 35 | # additional ascii chars considered as part of a word while auto-selecting text, except ' ', 0-9, a-z, A-Z 36 | word-chars=._- 37 | 38 | # change the clockwise orientation angle of screen display 39 | # available values: 0 = 0 degree, 1 = 90 degrees, 2 = 180 degrees, 3 = 270 degrees 40 | screen-rotate=1 41 | 42 | # specify the favorite input method program to run 43 | input-method= 44 | 45 | # treat ambiguous width characters as wide 46 | #ambiguous-wide=yes 47 | -------------------------------------------------------------------------------- /configs/.zshrc: -------------------------------------------------------------------------------- 1 | # If you come from bash you might have to change your $PATH. 2 | export PATH=$HOME/bin:/usr/local/bin:$HOME/devterm-items/scripts:$PATH 3 | 4 | # Path to your oh-my-zsh installation. 5 | export ZSH=/etc/oh-my-zsh 6 | export ZSH_CACHE_DIR=~/.oh-my-zsh/cache 7 | 8 | # Set name of the theme to load --- if set to "random", it will 9 | # load a random theme each time oh-my-zsh is loaded, in which case, 10 | # to know which specific one was loaded, run: echo $RANDOM_THEME 11 | # See https://github.com/ohmyzsh/ohmyzsh/wiki/Themes 12 | ZSH_THEME="ido" 13 | 14 | # Set list of themes to pick from when loading at random 15 | # Setting this variable when ZSH_THEME=random will cause zsh to load 16 | # a theme from this variable instead of looking in $ZSH/themes/ 17 | # If set to an empty array, this variable will have no effect. 18 | # ZSH_THEME_RANDOM_CANDIDATES=( "robbyrussell" "agnoster" ) 19 | 20 | # Uncomment the following line to use case-sensitive completion. 21 | # CASE_SENSITIVE="true" 22 | 23 | # Uncomment the following line to use hyphen-insensitive completion. 24 | # Case-sensitive completion must be off. _ and - will be interchangeable. 25 | # HYPHEN_INSENSITIVE="true" 26 | 27 | # Uncomment the following line to disable bi-weekly auto-update checks. 28 | DISABLE_AUTO_UPDATE="true" 29 | 30 | # Uncomment the following line to automatically update without prompting. 31 | DISABLE_UPDATE_PROMPT="true" 32 | 33 | # Uncomment the following line to change how often to auto-update (in days). 34 | # export UPDATE_ZSH_DAYS=13 35 | 36 | # Uncomment the following line if pasting URLs and other text is messed up. 37 | # DISABLE_MAGIC_FUNCTIONS="true" 38 | 39 | # Uncomment the following line to disable colors in ls. 40 | # DISABLE_LS_COLORS="true" 41 | 42 | # Uncomment the following line to disable auto-setting terminal title. 43 | # DISABLE_AUTO_TITLE="true" 44 | 45 | # Uncomment the following line to enable command auto-correction. 46 | # ENABLE_CORRECTION="true" 47 | 48 | # Uncomment the following line to display red dots whilst waiting for completion. 49 | # Caution: this setting can cause issues with multiline prompts (zsh 5.7.1 and newer seem to work) 50 | # See https://github.com/ohmyzsh/ohmyzsh/issues/5765 51 | # COMPLETION_WAITING_DOTS="true" 52 | 53 | # Uncomment the following line if you want to disable marking untracked files 54 | # under VCS as dirty. This makes repository status check for large repositories 55 | # much, much faster. 56 | # DISABLE_UNTRACKED_FILES_DIRTY="true" 57 | 58 | # Uncomment the following line if you want to change the command execution time 59 | # stamp shown in the history command output. 60 | # You can set one of the optional three formats: 61 | # "mm/dd/yyyy"|"dd.mm.yyyy"|"yyyy-mm-dd" 62 | # or set a custom format using the strftime function format specifications, 63 | # see 'man strftime' for details. 64 | # HIST_STAMPS="mm/dd/yyyy" 65 | 66 | # Would you like to use another custom folder than $ZSH/custom? 67 | # ZSH_CUSTOM=/path/to/new-custom-folder 68 | 69 | # Which plugins would you like to load? 70 | # Standard plugins can be found in $ZSH/plugins/ 71 | # Custom plugins may be added to $ZSH_CUSTOM/plugins/ 72 | # Example format: plugins=(rails git textmate ruby lighthouse) 73 | # Add wisely, as too many plugins slow down shell startup. 74 | plugins=(evalcache git git-extras debian tmux screen history extract colorize web-search zsh-autosuggestions battery) 75 | 76 | ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE="fg=4" 77 | 78 | source $ZSH/oh-my-zsh.sh 79 | 80 | # User configuration 81 | 82 | # export MANPATH="/usr/local/man:$MANPATH" 83 | 84 | # You may need to manually set your language environment 85 | # export LANG=en_US.UTF-8 86 | 87 | # Preferred editor for local and remote sessions 88 | # if [[ -n $SSH_CONNECTION ]]; then 89 | # export EDITOR='vim' 90 | # else 91 | # export EDITOR='mvim' 92 | # fi 93 | 94 | # Compilation flags 95 | # export ARCHFLAGS="-arch x86_64" 96 | 97 | # Set personal aliases, overriding those provided by oh-my-zsh libs, 98 | # plugins, and themes. Aliases can be placed here, though oh-my-zsh 99 | # users are encouraged to define aliases within the ZSH_CUSTOM folder. 100 | # For a full list of active aliases, run `alias`. 101 | # 102 | # Example aliases 103 | # alias zshconfig="mate ~/.zshrc" 104 | # alias ohmyzsh="mate ~/.oh-my-zsh" 105 | -------------------------------------------------------------------------------- /configs/ido.zsh-theme: -------------------------------------------------------------------------------- 1 | if [ $UID -eq 0 ]; then CARETCOLOR="red"; else CARETCOLOR="blue"; fi 2 | 3 | local return_code="%(?..%{$fg[red]%}%? ↵%{$reset_color%})" 4 | 5 | PROMPT='%{$reset_color%}%{${fg[green]}%}%3~ $(git_prompt_info)%{${fg_bold[$CARETCOLOR]}%}$CARET_POINTER%{${reset_color}%} ' 6 | RPROMPT='$(battery_pct_prompt) %F{yellow}%D{%L:%M} %D{%p}%f' 7 | 8 | CARET_POINTER="%{$fg[red]%}❯%{$reset_color%}%{$fg[yellow]%}❯%{$reset_color%}%{$fg[green]%}❯%{$reset_color%}" 9 | 10 | ZSH_THEME_GIT_PROMPT_PREFIX="%{$fg[yellow]%}‹" 11 | ZSH_THEME_GIT_PROMPT_SUFFIX="› %{$reset_color%}" 12 | -------------------------------------------------------------------------------- /images/bg/bg1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idooo/devterm-hobby-project/cfeebedc9cf9bda2bc1edc43ade1718424f7e86a/images/bg/bg1.jpg -------------------------------------------------------------------------------- /images/bg/bg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idooo/devterm-hobby-project/cfeebedc9cf9bda2bc1edc43ade1718424f7e86a/images/bg/bg2.jpg -------------------------------------------------------------------------------- /images/bgc-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idooo/devterm-hobby-project/cfeebedc9cf9bda2bc1edc43ade1718424f7e86a/images/bgc-image.jpg -------------------------------------------------------------------------------- /images/devterm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idooo/devterm-hobby-project/cfeebedc9cf9bda2bc1edc43ade1718424f7e86a/images/devterm.jpg -------------------------------------------------------------------------------- /images/en2ja.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idooo/devterm-hobby-project/cfeebedc9cf9bda2bc1edc43ade1718424f7e86a/images/en2ja.jpg -------------------------------------------------------------------------------- /images/fcitx-moz.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/idooo/devterm-hobby-project/cfeebedc9cf9bda2bc1edc43ade1718424f7e86a/images/fcitx-moz.jpg -------------------------------------------------------------------------------- /scripts/battery: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | upower -i /org/freedesktop/UPower/devices/battery_axp20x_battery 3 | -------------------------------------------------------------------------------- /scripts/brightness: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | # From here 4 | # https://forum.clockworkpi.com/t/changing-brightness-terminal/7070 5 | 6 | sudo bash -c "echo $1 > /sys/class/backlight/backlight@0/brightness" 7 | -------------------------------------------------------------------------------- /scripts/en2ja: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | # install https://github.com/soimort/translate-shell 4 | # sudo apt-get install translate-shell 5 | 6 | trans -t ja -f en "$1" 7 | 8 | -------------------------------------------------------------------------------- /scripts/fbstart: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # starts fbterm with background image and fcitx 4 | 5 | clear 6 | 7 | DIR=$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P) 8 | IMAGE=../images/bg/bg2.jpg 9 | 10 | echo $DIR 11 | echo -ne "\e[?25l" # hide cursor 12 | 13 | fbv -ciuker "$DIR/$IMAGE" << EOF 14 | q 15 | EOF 16 | 17 | shift 18 | export FBTERM_BACKGROUND_IMAGE=1 19 | exec fcitx-fbterm-helper -l 20 | -------------------------------------------------------------------------------- /scripts/gearbox: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # From official DevTerm repo 4 | # https://github.com/clockworkpi/DevTerm 5 | # https://forum.clockworkpi.com/t/devterm-a-06-core-cpu-frequency-scaling/7135 6 | 7 | import glob 8 | import os 9 | import sys,getopt 10 | import subprocess 11 | import time 12 | 13 | # The gearings below were picked based on various tests by the ClockworkPi devs. 14 | # The maximum-performance maximum-power gearing is present for completeness, but 15 | # shouldn't be needed for most uses. 16 | # 17 | # You can customise the gearings by editing the list below. The valid freqencies 18 | # for CPU can be looked up here (substituting for ): 19 | # /sys/devices/system/cpu/cpu/cpufreq/scaling_available_frequencies 20 | # 21 | # The valid GPU frequencies can be looked up here: 22 | # /sys/devices/platform/ff9a0000.gpu/devfreq/ff9a0000.gpu/available_frequencies 23 | # 24 | # Gears are numbered in-order, starting from 1. 25 | # It's up to you to ensure that they are sorted by performance :) 26 | def gears(): 27 | return [ 28 | gear( 29 | little=(600000,), 30 | use="simple writing tasks with long battery life"), 31 | gear( 32 | little=(800000,) * 2, 33 | use="browsing most websites with long battery life"), 34 | gear( 35 | little=(1008000,) * 4, 36 | gpu_freq=400000000, 37 | use="most 2D games and emulators"), 38 | gear( 39 | big=(1008000,) * 2, 40 | gpu_freq=400000000, 41 | use="playing videos and 3D games"), 42 | gear( 43 | big=(1200000,) * 2, 44 | gpu_freq=400000000, 45 | use="performance-first tasks"), 46 | gear( 47 | little=(1416000,) * 4, 48 | big=(1800000,) * 2, 49 | gpu_freq=800000000, 50 | use="max performance, max power (usage)"), 51 | ] 52 | 53 | GPU_GOV_SIMPLE = "simple_ondemand" 54 | GPU_GOV_PERF = "performance" 55 | 56 | # Helper to convert the concise gear format above into a full description. 57 | # 58 | # `little` and `big` define the number of A53 and A72 CPU cores to enable, and 59 | # their maximum frequencies (in kHZ). Cores that are omitted or set to zero are 60 | # disabled. 61 | def gear( 62 | little=(0, 0, 0, 0), 63 | big=(0, 0), 64 | gpu_freq=200000000, 65 | gpu_gov=GPU_GOV_SIMPLE, 66 | use="", 67 | ): 68 | # Extend to 4 little and 2 big cores (matching the A06). 69 | assert len(little) <= 4 70 | assert len(big) <= 2 71 | cpu = little + (0,) * (4 - len(little)) + big + (0,) * (2 - len(big)) 72 | 73 | # At least one CPU must be enabled 74 | assert sum(cpu) > 0 75 | return { 76 | "cpu": cpu, 77 | "gpu_freq": gpu_freq, 78 | "gpu_gov": gpu_gov, 79 | "use": use, 80 | } 81 | 82 | # We placed gears() at the top of the file to make it easier to find and edit. 83 | # Now that we've defined the helpers it needs, evaluate the gears. 84 | gears = gears() 85 | 86 | def load_gear(gear): 87 | return gears[gear - 1] 88 | 89 | 90 | 91 | cur_stat = [] 92 | cur_stat.append("+-----------------------------------+-----------------+-----------+") 93 | cur_stat.append("| Cortex-A53 | Cortex-A72 | Mali-T860 |") 94 | cur_stat.append("+--------+--------+--------+--------+--------+--------+-----------+") 95 | cur_stat.append("| CPU 0 | CPU 1 | CPU 2 | CPU 3 | CPU 4 | CPU 5 | GPU |") 96 | cur_stat.append("+--------+--------+--------+--------+--------+--------+-----------+") 97 | cur_stat.append("| 600MHz | OFF | OFF | OFF | OFF | OFF | 400MHz |") #5 98 | cur_stat.append("+--------+--------+--------+--------+--------+--------+-----------+") 99 | 100 | 101 | def isDigit(x): 102 | try: 103 | float(x) 104 | return True 105 | except ValueError: 106 | return False 107 | 108 | 109 | class A06: 110 | cpus = [] 111 | cpu_scaling_governor= "schedutil" 112 | gear = load_gear(1) # 1-5 113 | null_out = "2>/dev/null" 114 | def __init__(self): 115 | self.cpus = [] 116 | self.init_cpu_infos() 117 | self.cpu_total_count = len(self.cpus) 118 | 119 | def init_cpu_infos(self): 120 | self.cpus = glob.glob('/sys/devices/system/cpu/cpu[0-9]') 121 | self.cpus.sort() 122 | 123 | def get_cpu_gov(self): 124 | if self.gear["cpu"][0] > 0: 125 | cpu_gov_path = "/sys/devices/system/cpu/cpufreq/policy0/scaling_governor" 126 | else: 127 | cpu_gov_path = "/sys/devices/system/cpu/cpufreq/policy4/scaling_governor" 128 | gov = "" 129 | with open(cpu_gov_path,"r") as f: gov = f.read().strip() 130 | return gov 131 | 132 | def set_cpu_gov0( self,gov): 133 | cpu_gov_path = "/sys/devices/system/cpu/cpufreq/policy0/scaling_governor" 134 | try: 135 | subprocess.run( "echo %s | sudo tee %s " %(gov,cpu_gov_path),shell=True,stdout=subprocess.DEVNULL) 136 | except: 137 | print("set cpu governor failed") 138 | 139 | def set_cpu_gov4( self,gov): 140 | cpu_gov_path = "/sys/devices/system/cpu/cpufreq/policy4/scaling_governor" 141 | try: 142 | subprocess.run( "echo %s | sudo tee %s" %(gov,cpu_gov_path),shell=True,stdout=subprocess.DEVNULL) 143 | except: 144 | print("set cpu governor failed") 145 | 146 | 147 | def get_cpu_on_off(self,cpu_num): 148 | cpu_onoff_file = "/sys/devices/system/cpu/cpu%d/online" % cpu_num 149 | onoff = "0" 150 | max_freq = "0" 151 | with open(cpu_onoff_file,"r") as f: onoff = f.read().strip() 152 | if onoff == "1": 153 | cpu_max_freq_file = "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq" % cpu_num 154 | with open(cpu_max_freq_file,"r") as f: max_freq = f.read().strip() 155 | mhz = int(max_freq)/1000 156 | return "%dMhz" % mhz 157 | 158 | return "OFF" 159 | 160 | 161 | def set_cpu_on_off(self,cpu_num,onoff): 162 | cpu_onoff_file = "/sys/devices/system/cpu/cpu%d/online" % cpu_num 163 | try: 164 | #print("echo %d | sudo tee %s" %(onoff,cpu_onoff_file) ) 165 | subprocess.run( "echo %d | sudo tee %s" %(onoff,cpu_onoff_file),shell=True,stdout=subprocess.DEVNULL) 166 | except: 167 | print("set cpu %d on off failed" % cpu_num) 168 | 169 | def set_cpu_max_freq(self,cpu_num,max_freq): 170 | cpu_max_freq_file = "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq" % cpu_num 171 | try: 172 | subprocess.run( "echo %d | sudo tee %s" %(max_freq,cpu_max_freq_file),shell=True,stdout=subprocess.DEVNULL) 173 | except: 174 | print("set cpu %d max freq failed" % cpu_num) 175 | 176 | def get_gpu_freq(self): 177 | gpu_sys_path = "/sys/devices/platform/ff9a0000.gpu/devfreq/ff9a0000.gpu" 178 | gpu_freq_path = os.path.join(gpu_sys_path,"max_freq") 179 | freq = "" 180 | with open(gpu_freq_path,"r") as f: freq = f.read().strip() 181 | mhz = int(freq)/1000000 182 | return "%dMHz" % mhz 183 | 184 | def set_gpu(self,gov,hz): 185 | gpu_sys_path = "/sys/devices/platform/ff9a0000.gpu/devfreq/ff9a0000.gpu" 186 | gpu_gov_path = os.path.join(gpu_sys_path,"governor") 187 | gpu_freq_path = os.path.join(gpu_sys_path,"max_freq") 188 | try: 189 | subprocess.run("echo %s | sudo tee %s" %(gov,gpu_gov_path),shell=True,stdout=subprocess.DEVNULL) 190 | subprocess.run("echo %d | sudo tee %s" %(hz, gpu_freq_path),shell=True,stdout=subprocess.DEVNULL) 191 | except: 192 | print("set gpu failed") 193 | 194 | 195 | def print_cpu_gpu_gov(self): 196 | print("CPU Governor: %s GPU Governor: %s" % (self.get_cpu_gov(), self.gear["gpu_gov"])) 197 | 198 | def print_cur_status(self): 199 | global cur_stat 200 | 201 | stat_str = "|%s|%s|%s|%s|%s|%s|%s|" 202 | 203 | cpu0 = self.get_cpu_on_off(0).center(8)[:8] 204 | cpu1 = self.get_cpu_on_off(1).center(8)[:8] 205 | cpu2 = self.get_cpu_on_off(2).center(8)[:8] 206 | cpu3 = self.get_cpu_on_off(3).center(8)[:8] 207 | cpu4 = self.get_cpu_on_off(4).center(8)[:8] 208 | cpu5 = self.get_cpu_on_off(5).center(8)[:8] 209 | gpu = self.get_gpu_freq().center(11)[:11] 210 | 211 | table_str = stat_str %(cpu0,cpu1,cpu2,cpu3,cpu4,cpu5,gpu) 212 | print("\nCurrent Status:") 213 | for idx,val in enumerate(cur_stat): 214 | if idx == 5: 215 | print(table_str) 216 | else: 217 | print(val) 218 | 219 | self.print_cpu_gpu_gov() 220 | 221 | def set_gear(self,g): 222 | self.gear = load_gear(g) 223 | 224 | if g > 3: 225 | for (cpu, freq) in reversed(list(enumerate(self.gear["cpu"]))): 226 | enabled = freq > 0 227 | self.set_cpu_on_off(cpu, int(enabled)) 228 | if enabled: 229 | self.set_cpu_max_freq(cpu, freq) 230 | else: 231 | for (cpu, freq) in enumerate(self.gear["cpu"]): 232 | enabled = freq > 0 233 | self.set_cpu_on_off(cpu, int(enabled)) 234 | if enabled: 235 | self.set_cpu_max_freq(cpu, freq) 236 | 237 | self.set_gpu(self.gear["gpu_gov"], self.gear["gpu_freq"]) 238 | 239 | # TODO: Generalise this 240 | if self.gear["cpu"][0] > 0: 241 | self.set_cpu_gov0(self.cpu_scaling_governor) 242 | else: 243 | self.set_cpu_gov4(self.cpu_scaling_governor) 244 | 245 | 246 | 247 | def print_gear_map(gear): 248 | print(" +-----------------------------------+-----------------+-----------+") 249 | print(" | Cortex-A53 | Cortex-A72 | Mali-T860 |") 250 | print(" +--------+--------+--------+--------+--------+--------+-----------+") 251 | print(" | CPU 0 | CPU 1 | CPU 2 | CPU 3 | CPU 4 | CPU 5 | GPU |") 252 | div = "+---+--------+--------+--------+--------+--------+--------+-----------+" 253 | print(div) 254 | 255 | def freq(khz): 256 | mhz = khz/1000 257 | if mhz >= 1000: 258 | return "%d MHz" % mhz 259 | elif mhz > 0: 260 | return " %d MHz" % mhz 261 | else: 262 | return " OFF " 263 | 264 | for idx, val in enumerate(gears): 265 | g = idx + 1 266 | selected = g == gear 267 | print("|%s|%s| %s |%s" % ( 268 | ("*%s*" if selected else " %s ") % g, 269 | "|".join([freq(cpu) for cpu in val["cpu"]]), 270 | freq(val["gpu_freq"]/1000), 271 | " <===" if selected else "", 272 | )) 273 | print(div) 274 | 275 | def print_help_msg(): 276 | print("Usage: devterm-a06-gearbox [OPTION]...") 277 | print("Show or set the CPU operating frequency,online status and GPU operating frequency for DevTerm A06.") 278 | print() 279 | print(" -s, --set [n] set a speed mode between the number 1-%d:" % len(gears)) 280 | for (i, _) in enumerate(gears): 281 | print(" %d for %s." % (i + 1, gears[i]["use"])) 282 | print() 283 | print("Examples:") 284 | # TODO: Generate this example 285 | print("Set to mode 1, single LITTLE core @600MHz(max), GPU@200MHz.") 286 | print(" $ devterm-a06-gearbox -s 1") 287 | 288 | def is_root(): 289 | return os.geteuid() == 0 290 | 291 | def main(argv): 292 | gear = 1 293 | try: 294 | opts, args = getopt.getopt(argv,"hs:",["set="]) 295 | except getopt.GetoptError: 296 | print_help_msg() 297 | sys.exit(2) 298 | for opt, arg in opts: 299 | if opt == '-h': 300 | print_help_msg() 301 | sys.exit() 302 | elif opt in ("-s","--set"): 303 | if(isDigit(arg)): 304 | gear = int(arg) 305 | if gear not in range(1, len(gears) + 1): 306 | print("illegal input: mode range 1-%d" % len(gears)) 307 | sys.exit(-1) 308 | 309 | 310 | DT = A06() 311 | 312 | if len(argv) == 0: 313 | DT.print_cur_status() 314 | sys.exit(0) 315 | 316 | DT = A06() 317 | if is_root(): 318 | DT.set_gear(gear) 319 | print_gear_map(gear) 320 | DT.print_cpu_gpu_gov() 321 | else: 322 | print("Require super user privilege to set mode,try run it with sudo") 323 | sys.exit(1) 324 | 325 | if __name__ == "__main__": 326 | main(sys.argv[1:]) 327 | -------------------------------------------------------------------------------- /scripts/ja2en: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | # install https://github.com/soimort/translate-shell 4 | # sudo apt-get install translate-shell 5 | 6 | trans -t en -f ja "$1" 7 | 8 | --------------------------------------------------------------------------------