├── .gitignore ├── LEGACY ├── LICENSE ├── Makefile ├── README.md ├── build-rg35xxplus.sh ├── images ├── st-img1-trimuisp.jpg ├── st-img1.jpeg ├── st-img2.jpeg └── st-img3.jpeg ├── install-rg35xxplus.sh ├── makefiles ├── config-generic-linux-upscale.mk ├── config-rg35xxplus.mk ├── config-trimuisp.mk └── config.mk ├── rg35xxplus └── APPS │ ├── Imgs │ ├── SimpleTerminal-HighRes.png │ └── SimpleTerminal.png │ ├── SimpleTerminal-HighRes.sh │ └── SimpleTerminal.sh └── src ├── config.h ├── font.c ├── font.h ├── keyboard.c ├── keyboard.h ├── msg_queue.c ├── msg_queue.h └── st.c /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | toolchains/ 3 | rg35xxplus/APPS/SimpleTerminal -------------------------------------------------------------------------------- /LEGACY: -------------------------------------------------------------------------------- 1 | A STATEMENT ON LEGACY SUPPORT 2 | 3 | In the terminal world there is much cruft that comes from old and unsup‐ 4 | ported terminals that inherit incompatible modes and escape sequences 5 | which noone is able to know, except when he/she comes from that time and 6 | developed a graphical vt100 emulator at that time. 7 | 8 | One goal of st is to only support what is really needed. When you en‐ 9 | counter a sequence which you really need, implement it. But while you 10 | are at it, do not add the other cruft you might encounter while sneek‐ 11 | ing at other terminal emulators. History has bloated them and there is 12 | no real evidence that most of the sequences are used today. 13 | 14 | 15 | Christoph Lohmann <20h@r-36.net> 16 | 2012-09-13T07:00:36.081271045+02:00 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT/X Consortium License 2 | 3 | © 2009-2012 Aurélien APTEL 4 | © 2012 Roberto E. Vargas Caballero 5 | © 2012 Christoph Lohmann <20h at r-36 dot net> 6 | © 2009 Anselm R Garbe 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a 9 | copy of this software and associated documentation files (the "Software"), 10 | to deal in the Software without restriction, including without limitation 11 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 12 | and/or sell copies of the Software, and to permit persons to whom the 13 | Software is furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24 | DEALINGS IN THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # st - simple terminal 2 | # See LICENSE file for copyright and license details. 3 | 4 | include makefiles/config.mk 5 | 6 | ifeq ($(UNION_PLATFORM),rg35xxplus) 7 | include makefiles/config-rg35xxplus.mk 8 | else ifeq ($(UNION_PLATFORM),upscale) 9 | include makefiles/config-generic-linux-upscale.mk 10 | endif 11 | 12 | SRC = $(wildcard src/*.c) 13 | 14 | all: options build 15 | 16 | options: 17 | @echo st build options: 18 | @echo "UNION_PLATFORM = ${UNION_PLATFORM}" 19 | @echo "PREFIX = ${PREFIX}" 20 | @echo "CROSS_COMPILE = ${CROSS_COMPILE}" 21 | @echo "SYSROOT = ${SYSROOT}" 22 | @echo "CFLAGS = ${CFLAGS}" 23 | @echo "LDFLAGS = ${LDFLAGS}" 24 | @echo "CC = ${CC}" 25 | @echo "SRC = ${SRC}" 26 | @echo "VERSION = ${VERSION}" 27 | 28 | 29 | build: 30 | mkdir -p build 31 | ${CC} -o build/SimpleTerminal ${SRC} ${CFLAGS} ${LDFLAGS} 32 | 33 | clean: 34 | @echo cleaning 35 | rm -rf build 36 | 37 | .PHONY: all options clean 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | st - simple terminal 2 | 3 | 4 | How to install: 5 | 6 | RG35XX-Plus/H 7 | - Extract the downloaded zip file 8 | - Copy the files to "Roms/APPS/" folder 9 | 10 | TRIMUI Smart Pro 11 | - Download the .zip file and extract to the SDCARD Apps folder: 12 | Apps\SimpleTerminal\[files] 13 | 14 | How to run: 15 | 16 | - From your device main menu -> RA Game -> APPS -> SimpleTerminal (or SimpleTerminal-HighRes) 17 | 18 | Image1 19 | Image2 20 | Image3 21 | Image1-TrimuiSP 22 | 23 | 24 | -------------------- 25 | st is a simple virtual terminal emulator for X which sucks less. 26 | 27 | Modified to run on RS-97. 28 | => line doubling to deal with the 320x480 resolution 29 | => TTF fonts replaced by embedded pixel font (from TIC-80) 30 | => onscreen keyboard (see keyboard.c for button bindings) 31 | 32 | Keys: 33 | - pad: select key 34 | - A: press key 35 | - B: toggles key (useful for shift/ctrl...) 36 | - L: is shift 37 | - R: is backspace 38 | - Y: change keyboard location (top/bottom) 39 | - X: show / hide keyboard 40 | - SELECT: quit 41 | 42 | 43 | Requirements 44 | ------------ 45 | In order to build st you need the Xlib header files. 46 | 47 | Build 48 | ------------ 49 | For generic linux: 50 | 51 | make 52 | 53 | For RG35XX Plus: 54 | 55 | ./build-rg35xxplus.sh 56 | (or run inside rg35xxplus-toolchain docker container: UNION_PLATFORM=rg35xxplus make) 57 | 58 | For generic linux (with bigger window size) 59 | 60 | UNION_PLATFORM=upscale make 61 | 62 | 63 | Running st 64 | ---------- 65 | 66 | ./build/SimpleTerminal 67 | 68 | Run with High resolution 69 | HIGH_RES=1 ./build/SimpleTerminal 70 | 71 | 72 | If you don't install st, define TNAME to "xterm" in config.h or make sure to at 73 | least compile st terminfo entry with the following command: 74 | 75 | tic -s st.info 76 | 77 | It should print the path of the compiled terminfo entry. You can 78 | safely remove it if you don't plan to use st anymore. 79 | See the man page for additional details. 80 | 81 | Credits 82 | ------- 83 | Based on Aurélien APTEL bt source code. 84 | 85 | And 86 | https://github.com/jamesofarrell/st-sdl 87 | 88 | -------------------------------------------------------------------------------- /build-rg35xxplus.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -v 3 | 4 | PLATFORM=rg35xxplus 5 | HOST_WORKSPACE=$(pwd) 6 | 7 | if [ ! "$(docker images -q rg35xxplus-toolchain:latest 2> /dev/null)" ]; then 8 | echo 'toolchain image does not exist, building a new image' 9 | mkdir -p toolchains 10 | git clone https://github.com/haoict/union-$PLATFORM-toolchain/ toolchains/$PLATFORM-toolchain 11 | cd toolchains/$PLATFORM-toolchain && make .build 12 | cd $HOST_WORKSPACE 13 | fi 14 | 15 | docker run -it --rm -v $HOST_WORKSPACE:/root/workspace $PLATFORM-toolchain /bin/bash -c '. ~/.bashrc && cd /root/workspace && git config --global --add safe.directory /root/workspace && make && chmod 777 -R ./build' -------------------------------------------------------------------------------- /images/st-img1-trimuisp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoict/SimpleTerminal/a04cab63ec2d24f9ad2d4b8f220bb09847ab4f5c/images/st-img1-trimuisp.jpg -------------------------------------------------------------------------------- /images/st-img1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoict/SimpleTerminal/a04cab63ec2d24f9ad2d4b8f220bb09847ab4f5c/images/st-img1.jpeg -------------------------------------------------------------------------------- /images/st-img2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoict/SimpleTerminal/a04cab63ec2d24f9ad2d4b8f220bb09847ab4f5c/images/st-img2.jpeg -------------------------------------------------------------------------------- /images/st-img3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoict/SimpleTerminal/a04cab63ec2d24f9ad2d4b8f220bb09847ab4f5c/images/st-img3.jpeg -------------------------------------------------------------------------------- /install-rg35xxplus.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cp ./build/SimpleTerminal ./rg35xxplus/APPS/ 4 | rsync -rv ./rg35xxplus/APPS/* root@192.168.1.140:/mnt/mmc/Roms/APPS 5 | -------------------------------------------------------------------------------- /makefiles/config-generic-linux-upscale.mk: -------------------------------------------------------------------------------- 1 | INCS += -DUPSCALE -------------------------------------------------------------------------------- /makefiles/config-rg35xxplus.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Simple Terminal config for RG35xx Plus 3 | # based on RG350 ver https://github.com/jamesofarrell/st-sdl 4 | # 5 | export LD_LIBRARY_PATH=/opt/rg35xxplus-toolchain/usr/lib 6 | 7 | PREFIX=/opt/rg35xxplus-toolchain/usr/arm-buildroot-linux-gnueabihf/sysroot/usr 8 | CROSS_COMPILE=/opt/rg35xxplus-toolchain/usr/bin/arm-buildroot-linux-gnueabihf- 9 | 10 | INCS += -DRG35XXPLUS 11 | 12 | CFLAGS += -marm -mtune=cortex-a53 -mcpu=cortex-a53 -mfpu=neon-fp-armv8 -mfloat-abi=hard 13 | -------------------------------------------------------------------------------- /makefiles/config-trimuisp.mk: -------------------------------------------------------------------------------- 1 | CROSS_COMPILE = /opt/toolchains/arm64/aarch64-linux-gnu-7.5.0-linaro/bin/aarch64-linux-gnu- 2 | 3 | # compiler and linker 4 | CC = ${CROSS_COMPILE}gcc 5 | SYSROOT = /opt/toolchains/arm64/sysroot 6 | 7 | INCS += -I$(SYSROOT)/usr/include -DTRIMUISP 8 | CFLAGS += -mtune=cortex-a53 -mcpu=cortex-a53 9 | 10 | -------------------------------------------------------------------------------- /makefiles/config.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Simple Terminal config 3 | # based on RG350 ver https://github.com/jamesofarrell/st-sdl 4 | # 5 | 6 | # st version 7 | GIT_SHA_FETCH := $(shell git rev-parse HEAD | cut -c 1-7) 8 | VERSION = "1.0.1-$(shell git rev-parse HEAD | cut -c 1-7)" 9 | 10 | # Customize below to fit your system 11 | 12 | # paths 13 | PREFIX = /usr 14 | CROSS_COMPILE = 15 | 16 | # compiler and linker 17 | CC = ${CROSS_COMPILE}gcc 18 | SYSROOT = $(shell ${CC} --print-sysroot) 19 | 20 | # includes and libs 21 | INCS = -I. -I${SYSROOT}/usr/include/SDL 22 | LIBS = -lc -L${SYSROOT}/usr/lib -lSDL -lpthread -Wl,-Bstatic,-lutil,-Bdynamic 23 | 24 | # flags 25 | CPPFLAGS = -DVERSION=\"${VERSION}\" -D_GNU_SOURCE=1 -D_REENTRANT 26 | CFLAGS += -Os -Wall ${INCS} ${CPPFLAGS} -fPIC -std=gnu11 -flto -Wno-unused-result -Wno-unused-variable 27 | LDFLAGS += ${LIBS} -s 28 | -------------------------------------------------------------------------------- /rg35xxplus/APPS/Imgs/SimpleTerminal-HighRes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoict/SimpleTerminal/a04cab63ec2d24f9ad2d4b8f220bb09847ab4f5c/rg35xxplus/APPS/Imgs/SimpleTerminal-HighRes.png -------------------------------------------------------------------------------- /rg35xxplus/APPS/Imgs/SimpleTerminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haoict/SimpleTerminal/a04cab63ec2d24f9ad2d4b8f220bb09847ab4f5c/rg35xxplus/APPS/Imgs/SimpleTerminal.png -------------------------------------------------------------------------------- /rg35xxplus/APPS/SimpleTerminal-HighRes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | progdir=$(dirname "$0") 3 | cd $progdir 4 | HOME=$progdir 5 | HIGH_RES=1 ./SimpleTerminal 6 | # sync -------------------------------------------------------------------------------- /rg35xxplus/APPS/SimpleTerminal.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | progdir=$(dirname "$0") 3 | cd $progdir 4 | HOME=$progdir 5 | ./SimpleTerminal 6 | # sync 7 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | /* appearance */ 4 | #define USE_ANTIALIASING 5 | //static char font[] = "fonts/LiberationMono-Regular.ttf:fonts/LiberationMono-Bold.ttf"; 6 | //static char font[] = "fonts/inconsolata-dz.ttf:fonts/inconsolata-dz.ttf"; 7 | //static char font[] = "fonts/monaco.ttf:fonts/monaco.ttf"; 8 | //static char font[] = "fonts/TerminusTTF-4.46.0.ttf:fonts/TerminusTTF-4.46.0.ttf"; 9 | static char font[] = "fonts/FIXED_V0.TTF:fonts/FIXED_V0.TTF"; 10 | static int fontsize = 8; 11 | static int borderpx = 2; 12 | static int initial_width = 320; 13 | static int initial_height = 240; 14 | static char shell[] = "/bin/sh"; 15 | 16 | /* double-click timeout (in milliseconds) between clicks for selection */ 17 | static unsigned int doubleclicktimeout = 300; 18 | static unsigned int tripleclicktimeout = 600; 19 | 20 | /* TERM value */ 21 | static char termname[] = "xterm"; 22 | 23 | static unsigned int tabspaces = 4; 24 | #define WORD_BREAK " " 25 | 26 | /* Terminal colors (16 first used in escape sequence) */ 27 | SDL_Color colormap[] = { 28 | /* 8 normal colors */ 29 | { 0, 0, 0, 0 },//black 30 | { 128, 0, 0, 0 },//"red3", 31 | { 0, 128, 0, 0 },//"green3", 32 | { 128, 128, 0, 0 },//"yellow3", 33 | { 0, 0, 128, 0 },//"blue2", 34 | { 128, 0, 128, 0 },//"magenta3", 35 | { 0, 128, 128, 0 },//"cyan3", 36 | { 192, 192, 192, 0 },//"gray90", 37 | 38 | /* 8 bright colors */ 39 | { 128, 128, 128, 0}, //"gray50", 40 | { 255, 0, 0, 0 },//red 41 | { 0, 255, 0, 0 },//green 42 | { 255, 255, 0, 0 },//"yellow", 43 | { 0, 0, 255, 0 },//"#0000ff", 44 | { 255, 0, 255, 0 },//"magenta", 45 | { 0, 255, 255, 0 },//"cyan", 46 | { 255, 255, 255, 0 },//"white", 47 | 48 | [255] = { 0, 0, 0, 0 }, 49 | 50 | /* more colors can be added after 255 to use with DefaultXX */ 51 | { 204, 204, 204, 0}, 52 | { 51, 51, 51, 0}, 53 | }; 54 | 55 | /* 56 | * Default colors (colorname index) 57 | * foreground, background, cursor, unfocused cursor 58 | */ 59 | static unsigned int defaultfg = 7; 60 | static unsigned int defaultbg = 0; 61 | static unsigned int defaultcs = 256; 62 | static unsigned int defaultucs = 257; 63 | 64 | /* 65 | * Special keys (change & recompile st.info accordingly) 66 | * Keep in mind that kpress() in st.c hardcodes some keys. 67 | * 68 | * Mask value: 69 | * * Use XK_ANY_MOD to match the key no matter modifiers state 70 | * * Use XK_NO_MOD to match the key alone (no modifiers) 71 | */ 72 | 73 | /* key, mask, output */ 74 | static Key key[] = { 75 | { SDLK_LEFT, KMOD_ALT, "\033[1;3D" }, 76 | { SDLK_RIGHT, KMOD_ALT, "\033[1;3C" }, 77 | 78 | { SDLK_BACKSPACE, 0, "\177" }, 79 | { SDLK_INSERT, 0, "\033[2~" }, 80 | { SDLK_DELETE, 0, "\033[3~" }, 81 | { SDLK_HOME, 0, "\033[1~" }, 82 | { SDLK_END, 0, "\033[4~" }, 83 | { SDLK_PAGEUP, 0, "\033[5~" }, 84 | { SDLK_PAGEDOWN, 0, "\033[6~" }, 85 | { SDLK_F1, 0, "\033OP" }, 86 | { SDLK_F2, 0, "\033OQ" }, 87 | { SDLK_F3, 0, "\033OR" }, 88 | { SDLK_F4, 0, "\033OS" }, 89 | { SDLK_F5, 0, "\033[15~" }, 90 | { SDLK_F6, 0, "\033[17~" }, 91 | { SDLK_F7, 0, "\033[18~" }, 92 | { SDLK_F8, 0, "\033[19~" }, 93 | { SDLK_F9, 0, "\033[20~" }, 94 | { SDLK_F10, 0, "\033[21~" }, 95 | { SDLK_F11, 0, "\033[23~" }, 96 | { SDLK_F12, 0, "\033[24~" }, 97 | }; 98 | 99 | /* Internal shortcuts. */ 100 | #define MODKEY KMOD_ALT 101 | 102 | static Shortcut shortcuts[] = { 103 | /* modifier key function argument */ 104 | { MODKEY|KMOD_SHIFT, SDLK_PAGEUP, xzoom, {.i = +1} }, 105 | { MODKEY|KMOD_SHIFT, SDLK_PAGEDOWN, xzoom, {.i = -1} }, 106 | }; 107 | 108 | -------------------------------------------------------------------------------- /src/font.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "font.h" 3 | 4 | // font from https://github.com/nesbox/TIC-80 5 | static const unsigned char embedded_font[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0xf8, 0x50, 0xf8, 0x50, 0x00, 0x00, 0x00, 0x78, 0xa0, 0x70, 0x28, 0xf0, 0x00, 0x00, 0x00, 0x88, 0x10, 0x20, 0x40, 0x88, 0x00, 0x00, 0x00, 0x40, 0xa0, 0x68, 0x90, 0x68, 0x00, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x40, 0x40, 0x40, 0x20, 0x00, 0x00, 0x00, 0x40, 0x20, 0x20, 0x20, 0x40, 0x00, 0x00, 0x00, 0x20, 0xa8, 0x70, 0xa8, 0x20, 0x00, 0x00, 0x00, 0x00, 0x20, 0x70, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x70, 0xc8, 0xc8, 0xc8, 0x70, 0x00, 0x00, 0x00, 0x30, 0x70, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0xf0, 0x18, 0x70, 0xc0, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x30, 0x98, 0x70, 0x00, 0x00, 0x00, 0x30, 0x70, 0xd0, 0xf8, 0x10, 0x00, 0x00, 0x00, 0xf8, 0xc0, 0xf0, 0x18, 0xf0, 0x00, 0x00, 0x00, 0x70, 0xc0, 0xf0, 0xc8, 0x70, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x30, 0x60, 0xc0, 0x00, 0x00, 0x00, 0x70, 0xc8, 0x70, 0xc8, 0x70, 0x00, 0x00, 0x00, 0x70, 0xc8, 0x78, 0x08, 0x70, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x60, 0x20, 0x40, 0x00, 0x00, 0x10, 0x20, 0x40, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x10, 0x20, 0x40, 0x00, 0x00, 0x00, 0x78, 0x18, 0x30, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0xa8, 0xb8, 0x80, 0x70, 0x00, 0x00, 0x00, 0x70, 0xc8, 0xc8, 0xf8, 0xc8, 0x00, 0x00, 0x00, 0xf0, 0xc8, 0xf0, 0xc8, 0xf0, 0x00, 0x00, 0x00, 0x70, 0xc8, 0xc0, 0xc8, 0x70, 0x00, 0x00, 0x00, 0xf0, 0xc8, 0xc8, 0xc8, 0xf0, 0x00, 0x00, 0x00, 0xf8, 0xc0, 0xf0, 0xc0, 0xf8, 0x00, 0x00, 0x00, 0xf8, 0xc0, 0xf0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x78, 0xc0, 0xd8, 0xc8, 0x78, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xf8, 0xc8, 0xc8, 0x00, 0x00, 0x00, 0x78, 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0xd8, 0x70, 0x00, 0x00, 0x00, 0xc8, 0xd0, 0xe0, 0xd0, 0xc8, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xf8, 0x00, 0x00, 0x00, 0xd8, 0xf8, 0xf8, 0xa8, 0x88, 0x00, 0x00, 0x00, 0xc8, 0xe8, 0xf8, 0xd8, 0xc8, 0x00, 0x00, 0x00, 0x70, 0xc8, 0xc8, 0xc8, 0x70, 0x00, 0x00, 0x00, 0xf0, 0xc8, 0xc8, 0xf0, 0xc0, 0x00, 0x00, 0x00, 0x70, 0xc8, 0xc8, 0xc8, 0x70, 0x08, 0x00, 0x00, 0xf0, 0xc8, 0xc8, 0xf0, 0xc8, 0x00, 0x00, 0x00, 0x78, 0xe0, 0x70, 0x38, 0xf0, 0x00, 0x00, 0x00, 0x78, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8, 0xc8, 0x70, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8, 0x70, 0x20, 0x00, 0x00, 0x00, 0x88, 0xa8, 0xf8, 0xf8, 0xd8, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0x70, 0xc8, 0xc8, 0x00, 0x00, 0x00, 0x68, 0x68, 0x78, 0x30, 0x30, 0x00, 0x00, 0x00, 0xf8, 0x30, 0x60, 0xc0, 0xf8, 0x00, 0x00, 0x00, 0x60, 0x40, 0x40, 0x40, 0x60, 0x00, 0x00, 0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x00, 0x00, 0x00, 0x60, 0x20, 0x20, 0x20, 0x60, 0x00, 0x00, 0x00, 0x20, 0x50, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x40, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x98, 0x98, 0x78, 0x00, 0x00, 0x00, 0xc0, 0xf0, 0xc8, 0xc8, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x78, 0xe0, 0xe0, 0x78, 0x00, 0x00, 0x00, 0x18, 0x78, 0x98, 0x98, 0x78, 0x00, 0x00, 0x00, 0x00, 0x70, 0xd8, 0xe0, 0x70, 0x00, 0x00, 0x00, 0x38, 0x60, 0xf8, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x70, 0x98, 0xf8, 0x18, 0x70, 0x00, 0x00, 0xc0, 0xf0, 0xc8, 0xc8, 0xc8, 0x00, 0x00, 0x00, 0x30, 0x00, 0x70, 0x30, 0x78, 0x00, 0x00, 0x00, 0x18, 0x00, 0x18, 0x18, 0x98, 0x70, 0x00, 0x00, 0xc0, 0xc8, 0xf0, 0xc8, 0xc8, 0x00, 0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x38, 0x00, 0x00, 0x00, 0x00, 0xd0, 0xf8, 0xa8, 0xa8, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xc8, 0xc8, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x70, 0xc8, 0xc8, 0x70, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xc8, 0xc8, 0xf0, 0xc0, 0x00, 0x00, 0x00, 0x78, 0x98, 0x98, 0x78, 0x18, 0x00, 0x00, 0x00, 0xf0, 0xc8, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x78, 0xe0, 0x38, 0xf0, 0x00, 0x00, 0x00, 0x60, 0xf8, 0x60, 0x60, 0x38, 0x00, 0x00, 0x00, 0x00, 0x98, 0x98, 0x98, 0x78, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xd0, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x88, 0xa8, 0xf8, 0xd8, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x70, 0x70, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x98, 0x98, 0x78, 0x18, 0x70, 0x00, 0x00, 0x00, 0xf8, 0x30, 0x60, 0xf8, 0x00, 0x00, 0x00, 0x30, 0x20, 0x60, 0x20, 0x30, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x60, 0x20, 0x30, 0x20, 0x60, 0x00, 0x00, 0x00, 0x00, 0x28, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 6 | 7 | void draw_char(SDL_Surface* surface, unsigned char symbol, int x, int y, unsigned short color) { 8 | x += (8 - 1) * 1; 9 | int flip = 0; 10 | if(symbol > 127) { // flip symbols over 128 11 | flip = 1; 12 | symbol -= 128; 13 | } 14 | const unsigned char* ptr = embedded_font + symbol * 8; 15 | 16 | for(int i = 0, ys = 0; i < 6; i++, ptr++, ys += 1) 17 | for(int col = 8 - 6, xs = x - col; col < 8; col++, xs -= 1) 18 | if((*ptr & 1 << col) && y + ys < surface->h && xs < surface->w ) 19 | ((unsigned short*)surface->pixels)[(y + flip * 4 + (1 - 2 * flip) * ys) * (surface->pitch >> 1) + xs] = color; 20 | 21 | } 22 | 23 | void draw_string(SDL_Surface* surface, const char* text, int orig_x, int orig_y, unsigned short color) { 24 | int x = orig_x, y = orig_y; 25 | while(*text) { 26 | if(*text == '\n') { 27 | x = orig_x; 28 | y += 8; 29 | } else { 30 | draw_char(surface, *text, x, y, color); 31 | x += 6; 32 | } 33 | text++; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/font.h: -------------------------------------------------------------------------------- 1 | #ifndef __FONT_H__ 2 | #define __FONT_H__ 3 | 4 | void draw_char(SDL_Surface* surface, unsigned char symbol, int x, int y, unsigned short color); 5 | void draw_string(SDL_Surface* surface, const char* text, int x, int y, unsigned short color); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /src/keyboard.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "font.h" 4 | 5 | #define NUM_ROWS 6 6 | #define NUM_KEYS 18 7 | 8 | #ifdef RG35XXPLUS 9 | 10 | #define RAW_UP 103 11 | #define RAW_DOWN 108 12 | #define RAW_LEFT 105 13 | #define RAW_RIGHT 106 14 | #define RAW_A 304 15 | #define RAW_B 305 16 | #define RAW_X 307 17 | #define RAW_Y 306 18 | #define RAW_START 311 19 | #define RAW_SELECT 310 20 | #define RAW_MENU 312 21 | #define RAW_L1 308 22 | #define RAW_L2 314 23 | #define RAW_L3 313 24 | #define RAW_R1 309 25 | #define RAW_R2 315 26 | #define RAW_R3 316 27 | #define RAW_PLUS 115 28 | #define RAW_MINUS 114 29 | #define RAW_POWER 116 30 | #define RAW_YAXIS 17 31 | #define RAW_XAXIS 16 32 | 33 | #define RAW_MENU1 RAW_L3 34 | #define RAW_MENU2 RAW_R3 35 | 36 | // RG35xx 37 | #define KEY_UP RAW_UP 38 | #define KEY_DOWN RAW_DOWN 39 | #define KEY_LEFT RAW_LEFT 40 | #define KEY_RIGHT RAW_RIGHT 41 | #define KEY_ENTER RAW_A 42 | #define KEY_TOGGLE RAW_R1 43 | #define KEY_BACKSPACE RAW_B 44 | #define KEY_SHIFT RAW_L1 45 | #define KEY_LOCATION RAW_Y 46 | #define KEY_ACTIVATE RAW_X 47 | #define KEY_QUIT RAW_MENU 48 | #define KEY_TAB RAW_SELECT 49 | #define KEY_RETURN RAW_START 50 | #define KEY_ARROW_LEFT RAW_L2 51 | #define KEY_ARROW_RIGHT RAW_R2 52 | #define KEY_ARROW_UP RAW_PLUS 53 | #define KEY_ARROW_DOWN RAW_MINUS 54 | 55 | #elif R36S_SDL12COMPAT 56 | 57 | #define RAW_UP 544 58 | #define RAW_DOWN 545 59 | #define RAW_LEFT 546 60 | #define RAW_RIGHT 547 61 | #define RAW_A 305 62 | #define RAW_B 304 63 | #define RAW_X 307 64 | #define RAW_Y 308 65 | #define RAW_START 705 66 | #define RAW_SELECT 704 67 | #define RAW_MENU 708 68 | #define RAW_L1 310 69 | #define RAW_L2 312 70 | #define RAW_L3 706 71 | #define RAW_R1 311 72 | #define RAW_R2 313 73 | #define RAW_R3 707 74 | #define RAW_PLUS 115 75 | #define RAW_MINUS 114 76 | #define RAW_POWER 116 77 | 78 | #define KEY_UP RAW_UP 79 | #define KEY_DOWN RAW_DOWN 80 | #define KEY_LEFT RAW_LEFT 81 | #define KEY_RIGHT RAW_RIGHT 82 | #define KEY_ENTER RAW_A 83 | #define KEY_TOGGLE RAW_R1 84 | #define KEY_BACKSPACE RAW_B 85 | #define KEY_SHIFT RAW_L1 86 | #define KEY_LOCATION RAW_Y 87 | #define KEY_ACTIVATE RAW_X 88 | #define KEY_QUIT RAW_MENU 89 | #define KEY_TAB RAW_SELECT 90 | #define KEY_RETURN RAW_START 91 | #define KEY_ARROW_LEFT RAW_L2 92 | #define KEY_ARROW_RIGHT RAW_R2 93 | #define KEY_ARROW_UP RAW_PLUS 94 | #define KEY_ARROW_DOWN RAW_MINUS 95 | 96 | #elif TRIMUISP 97 | 98 | #define RAW_UP 103 99 | #define RAW_DOWN 108 100 | #define RAW_LEFT 105 101 | #define RAW_RIGHT 106 102 | #define RAW_A 305 103 | #define RAW_B 304 104 | #define RAW_X 307 105 | #define RAW_Y 308 106 | #define RAW_START 315 107 | #define RAW_SELECT 314 108 | #define RAW_MENU 316 109 | #define RAW_L1 310 110 | #define RAW_L2 2 111 | #define RAW_L3 999 // no L3 112 | #define RAW_R1 311 113 | #define RAW_R2 5 114 | #define RAW_R3 998 // no R3 115 | #define RAW_PLUS 997 116 | #define RAW_MINUS 996 117 | #define RAW_POWER 995 118 | 119 | #define KEY_UP RAW_UP 120 | #define KEY_DOWN RAW_DOWN 121 | #define KEY_LEFT RAW_LEFT 122 | #define KEY_RIGHT RAW_RIGHT 123 | #define KEY_ENTER RAW_A 124 | #define KEY_TOGGLE RAW_R1 125 | #define KEY_BACKSPACE RAW_B 126 | #define KEY_SHIFT RAW_L1 127 | #define KEY_LOCATION RAW_Y 128 | #define KEY_ACTIVATE RAW_X 129 | #define KEY_QUIT RAW_MENU 130 | #define KEY_TAB RAW_SELECT 131 | #define KEY_RETURN RAW_START 132 | #define KEY_ARROW_LEFT RAW_L2 133 | #define KEY_ARROW_RIGHT RAW_R2 134 | #define KEY_ARROW_UP RAW_PLUS 135 | #define KEY_ARROW_DOWN RAW_MINUS 136 | 137 | #elif MIYOOMINI 138 | // miyoomini 139 | #define KEY_UP SDLK_UP 140 | #define KEY_DOWN SDLK_DOWN 141 | #define KEY_LEFT SDLK_LEFT 142 | #define KEY_RIGHT SDLK_RIGHT 143 | #define KEY_ENTER SDLK_SPACE // A 144 | #define KEY_TOGGLE SDLK_LCTRL // B 145 | #define KEY_BACKSPACE SDLK_t // R1 146 | #define KEY_SHIFT SDLK_e // L1 147 | #define KEY_LOCATION SDLK_LALT // Y 148 | #define KEY_ACTIVATE SDLK_LSHIFT // X 149 | #define KEY_QUIT SDLK_ESCAPE // MENU 150 | // #define KEY_HELP SDLK_RETURN // 151 | #define KEY_TAB SDLK_RCTRL // SELECT 152 | #define KEY_RETURN SDLK_RETURN // START 153 | #define KEY_ARROW_LEFT SDLK_TAB // L2 154 | #define KEY_ARROW_RIGHT SDLK_BACKSPACE // R2 155 | // #define KEY_ARROW_UP SDLK_KP_DIVIDE // 156 | // #define KEY_ARROW_DOWN SDLK_KP_PERIOD // 157 | 158 | #elif TRIMUISMART 159 | // TRIMUI smart (same as TRIMUI) 160 | #define KEY_UP SDLK_UP 161 | #define KEY_DOWN SDLK_DOWN 162 | #define KEY_LEFT SDLK_LEFT 163 | #define KEY_RIGHT SDLK_RIGHT 164 | #define KEY_ENTER SDLK_SPACE // A 165 | #define KEY_TOGGLE SDLK_LCTRL // B 166 | #define KEY_BACKSPACE SDLK_BACKSPACE // R 167 | #define KEY_SHIFT SDLK_TAB // L 168 | #define KEY_LOCATION SDLK_LSHIFT // Y 169 | #define KEY_ACTIVATE SDLK_LALT // X 170 | #define KEY_QUIT SDLK_ESCAPE // MENU 171 | #define KEY_TAB SDLK_RCTRL // SELECT 172 | #define KEY_RETURN SDLK_RETURN // START 173 | 174 | #else 175 | // generic Linux PC 176 | #define KEY_UP SDLK_UP 177 | #define KEY_DOWN SDLK_DOWN 178 | #define KEY_LEFT SDLK_LEFT 179 | #define KEY_RIGHT SDLK_RIGHT 180 | #define KEY_ENTER SDLK_RETURN 181 | #define KEY_TOGGLE SDLK_LALT 182 | #define KEY_BACKSPACE SDLK_BACKSPACE 183 | #define KEY_SHIFT SDLK_LSHIFT 184 | #define KEY_LOCATION SDLK_ESCAPE 185 | #define KEY_ACTIVATE SDLK_ESCAPE 186 | #define KEY_QUIT SDLK_HOME 187 | #define KEY_HELP SDLK_F1 188 | #define KEY_TAB SDLK_TAB 189 | #define KEY_RETURN SDLK_LCTRL 190 | #define KEY_ARROW_LEFT SDLK_PAGEUP 191 | #define KEY_ARROW_RIGHT SDLK_PAGEDOWN 192 | #define KEY_ARROW_UP SDLK_KP_DIVIDE 193 | #define KEY_ARROW_DOWN SDLK_KP_PERIOD 194 | 195 | #endif 196 | 197 | #define KMOD_SYNTHETIC (1 << 13) 198 | 199 | static int row_length[NUM_ROWS] = {13, 17, 17, 15, 14, 9}; 200 | 201 | static SDLKey keys[2][NUM_ROWS][NUM_KEYS] = { 202 | {{SDLK_ESCAPE, SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5, SDLK_F6, SDLK_F8, SDLK_F9, SDLK_F10, SDLK_F11, SDLK_F12}, 203 | {SDLK_BACKQUOTE, SDLK_1, SDLK_2, SDLK_3, SDLK_4, SDLK_5, SDLK_6, SDLK_7, SDLK_8, SDLK_9, SDLK_0, SDLK_MINUS, SDLK_EQUALS, SDLK_BACKSPACE, SDLK_INSERT, SDLK_DELETE, SDLK_UP}, 204 | {SDLK_TAB, SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_t, SDLK_y, SDLK_u, SDLK_i, SDLK_o, SDLK_p, SDLK_LEFTBRACKET, SDLK_RIGHTBRACKET, SDLK_BACKSLASH, SDLK_HOME, SDLK_END, SDLK_DOWN}, 205 | {SDLK_CAPSLOCK, SDLK_a, SDLK_s, SDLK_d, SDLK_f, SDLK_g, SDLK_h, SDLK_j, SDLK_k, SDLK_l, SDLK_SEMICOLON, SDLK_QUOTE, SDLK_RETURN, SDLK_PAGEUP, SDLK_LEFT}, 206 | {SDLK_LSHIFT, SDLK_z, SDLK_x, SDLK_c, SDLK_v, SDLK_b, SDLK_n, SDLK_m, SDLK_COMMA, SDLK_PERIOD, SDLK_SLASH, SDLK_RSHIFT, SDLK_PAGEDOWN, SDLK_RIGHT}, 207 | {SDLK_LCTRL, SDLK_LSUPER, SDLK_LALT, SDLK_SPACE, SDLK_RALT, SDLK_RSUPER, SDLK_MENU, SDLK_RCTRL, SDLK_PRINT}}, 208 | {{SDLK_ESCAPE, SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5, SDLK_F6, SDLK_F8, SDLK_F9, SDLK_F10, SDLK_F11, SDLK_F12}, 209 | {'~', SDLK_EXCLAIM, SDLK_AT, SDLK_HASH, SDLK_DOLLAR, '%', SDLK_CARET, SDLK_AMPERSAND, SDLK_ASTERISK, SDLK_LEFTPAREN, SDLK_RIGHTPAREN, SDLK_UNDERSCORE, SDLK_PLUS, SDLK_BACKSPACE, SDLK_INSERT, SDLK_DELETE, SDLK_UP}, 210 | {SDLK_TAB, SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_t, SDLK_y, SDLK_u, SDLK_i, SDLK_o, SDLK_p, '{', '}', '|', SDLK_HOME, SDLK_END, SDLK_DOWN}, 211 | {SDLK_CAPSLOCK, SDLK_a, SDLK_s, SDLK_d, SDLK_f, SDLK_g, SDLK_h, SDLK_j, SDLK_k, SDLK_l, SDLK_COLON, SDLK_QUOTEDBL, SDLK_RETURN, SDLK_PAGEUP, SDLK_LEFT}, 212 | {SDLK_LSHIFT, SDLK_z, SDLK_x, SDLK_c, SDLK_v, SDLK_b, SDLK_n, SDLK_m, SDLK_LESS, SDLK_GREATER, SDLK_QUESTION, SDLK_RSHIFT, SDLK_PAGEDOWN, SDLK_RIGHT}, 213 | {SDLK_LCTRL, SDLK_LSUPER, SDLK_LALT, SDLK_SPACE, SDLK_RALT, SDLK_RSUPER, SDLK_MENU, SDLK_RCTRL, SDLK_PRINT}}}; 214 | 215 | static char *syms[2][NUM_ROWS][NUM_KEYS] = { 216 | {{"Esc", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", NULL}, 217 | {"` ", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "Bsp", "Ins", "Del", " ^ ", NULL}, 218 | {"Tab", "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\\", "Home", "End", " \xde ", NULL}, 219 | {"Caps", "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "Enter", "Pg Up", " < ", NULL}, 220 | {"Shift", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", " Shift", "Pg Dn", " > ", NULL}, 221 | {"Ctrl", " ", "Alt", " Space ", "Alt", " ", "Fn", "Ctrl", "PsS", NULL}}, 222 | {{"Esc", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", NULL}, 223 | {"~ ", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "Bsp", "Ins", "Del", " ^ ", NULL}, 224 | {"Tab", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "|", "Home", "End", " \xde ", NULL}, 225 | {"Caps", "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "Enter", "Pg Up", " < ", NULL}, 226 | {"Shift", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", " Shift", "Pg Dn", " > ", NULL}, 227 | {"Ctrl", " ", "Alt", " Space ", "Alt", " ", "Fn", "Ctrl", "PsS", NULL}}}; 228 | 229 | static unsigned char toggled[NUM_ROWS][NUM_KEYS]; 230 | 231 | static int selected_i = 0, selected_j = 0; 232 | static int shifted = 0; 233 | static int location = 0; 234 | static int mod_state = 0; 235 | int active = 1; 236 | int show_help = 1; 237 | 238 | void init_keyboard() 239 | { 240 | for (int j = 0; j < NUM_ROWS; j++) 241 | for (int i = 0; i < NUM_KEYS; i++) 242 | toggled[j][i] = 0; 243 | selected_i = selected_j = shifted = location = 0; 244 | // active = 1; 245 | mod_state = 0; 246 | } 247 | 248 | char *help = 249 | "How to use:\n" 250 | " ARROWS: select key from keyboard\n" 251 | " A: press key\n" 252 | " B: backspace\n" 253 | #ifndef TRIMUISMART 254 | " L1: shift\n" 255 | " R1: toggle key (for shift/ctrl...)\n" 256 | #else 257 | " L: shift\n" 258 | " R: backspace\n" 259 | #endif 260 | " Y: change keyboard location\n" 261 | " X: show / hide keyboard\n" 262 | " START: enter\n" 263 | " SELECT: tab\n" 264 | #ifndef TRIMUISMART 265 | " L2: left\n" 266 | " R2: right\n" 267 | #endif 268 | " MENU: quit\n\n" 269 | "Cheatcheet (tutorial at www.shellscript.sh):\n" 270 | " TAB key complete path\n" 271 | " UP/DOWN keys navigate history\n" 272 | " pwd print current directory\n" 273 | " ls list files (-l for file size)\n" 274 | " cd change directory (.. = go up)\n" 275 | " cp copy files (dest can be dir)\n" 276 | " mv move files (dest can be dir)\n" 277 | " rm remove files (use -rf for dir)\n\n"; 278 | 279 | #define CREDIT "Update by @haoict (c) 2024. Version: " VERSION 280 | 281 | void draw_keyboard(SDL_Surface *surface) 282 | { 283 | unsigned short bg_color = SDL_MapRGB(surface->format, 64, 64, 64); 284 | unsigned short key_color = SDL_MapRGB(surface->format, 128, 128, 128); 285 | unsigned short text_color = SDL_MapRGB(surface->format, 0, 0, 0); 286 | unsigned short sel_color = SDL_MapRGB(surface->format, 128, 255, 128); 287 | unsigned short sel_toggled_color = SDL_MapRGB(surface->format, 255, 255, 128); 288 | unsigned short toggled_color = SDL_MapRGB(surface->format, 192, 192, 0); 289 | if (show_help) 290 | { 291 | SDL_FillRect(surface, NULL, text_color); 292 | draw_string(surface, "SDL Terminal by Benob, based on st-sdl", 2, 10, sel_toggled_color); 293 | draw_string(surface, help, 8, 30, sel_color); 294 | draw_string(surface, CREDIT, 2, 220, sel_toggled_color); 295 | return; 296 | } 297 | if (!active) 298 | return; 299 | int total_length = -1; 300 | for (int i = 0; i < NUM_KEYS && syms[0][0][i]; i++) 301 | { 302 | total_length += (1 + strlen(syms[0][0][i])) * 6; 303 | } 304 | int center_x = (surface->w - total_length) / 2; 305 | int x = center_x, y = surface->h - 8 * (NUM_ROWS)-16; 306 | if (location == 1) 307 | y = 16; 308 | 309 | SDL_Rect rect = {x - 4, y - 3, total_length + 3, NUM_ROWS * 8 + 3}; 310 | SDL_FillRect(surface, &rect, bg_color); 311 | 312 | for (int j = 0; j < NUM_ROWS; j++) 313 | { 314 | x = center_x; 315 | for (int i = 0; i < row_length[j]; i++) 316 | { 317 | int length = strlen(syms[shifted][j][i]); 318 | SDL_Rect r2 = {x - 2, y - 1, length * 6 + 4, 7}; 319 | if (toggled[j][i]) 320 | { 321 | if (selected_i == i && selected_j == j) 322 | { 323 | SDL_FillRect(surface, &r2, sel_toggled_color); 324 | } 325 | else 326 | { 327 | SDL_FillRect(surface, &r2, toggled_color); 328 | } 329 | } 330 | else if (selected_i == i && selected_j == j) 331 | { 332 | SDL_FillRect(surface, &r2, sel_color); 333 | } 334 | else 335 | { 336 | SDL_FillRect(surface, &r2, key_color); 337 | } 338 | draw_string(surface, syms[shifted][j][i], x, y, text_color); 339 | x += 6 * (length + 1); 340 | } 341 | y += 8; 342 | } 343 | } 344 | 345 | enum 346 | { 347 | STATE_TYPED, 348 | STATE_UP, 349 | STATE_DOWN 350 | }; 351 | 352 | void update_modstate(int key, int state) 353 | { 354 | // SDLMod mod_state = SDL_GetModState(); 355 | if (state == STATE_DOWN) 356 | { 357 | if (key == SDLK_LSHIFT) 358 | mod_state |= KMOD_LSHIFT; 359 | else if (key == SDLK_RSHIFT) 360 | mod_state |= KMOD_RSHIFT; 361 | else if (key == SDLK_LCTRL) 362 | mod_state |= KMOD_LCTRL; 363 | else if (key == SDLK_RCTRL) 364 | mod_state |= KMOD_RCTRL; 365 | else if (key == SDLK_LALT) 366 | mod_state |= KMOD_LALT; 367 | else if (key == SDLK_RALT) 368 | mod_state |= KMOD_RALT; 369 | else if (key == SDLK_LMETA) 370 | mod_state |= KMOD_LMETA; 371 | else if (key == SDLK_RMETA) 372 | mod_state |= KMOD_RMETA; 373 | // else if(key == SDLK_NUM) mod_state |= KMOD_NUM; 374 | else if (key == SDLK_CAPSLOCK) 375 | mod_state |= KMOD_CAPS; 376 | else if (key == SDLK_MODE) 377 | mod_state |= KMOD_MODE; 378 | } 379 | else if (state == STATE_UP) 380 | { 381 | if (key == SDLK_LSHIFT) 382 | mod_state &= ~KMOD_LSHIFT; 383 | else if (key == SDLK_RSHIFT) 384 | mod_state &= ~KMOD_RSHIFT; 385 | else if (key == SDLK_LCTRL) 386 | mod_state &= ~KMOD_LCTRL; 387 | else if (key == SDLK_RCTRL) 388 | mod_state &= ~KMOD_RCTRL; 389 | else if (key == SDLK_LALT) 390 | mod_state &= ~KMOD_LALT; 391 | else if (key == SDLK_RALT) 392 | mod_state &= ~KMOD_RALT; 393 | else if (key == SDLK_LMETA) 394 | mod_state &= ~KMOD_LMETA; 395 | else if (key == SDLK_RMETA) 396 | mod_state &= ~KMOD_RMETA; 397 | // else if(key == SDLK_NUM) mod_state &= ~KMOD_NUM; 398 | else if (key == SDLK_CAPSLOCK) 399 | mod_state &= ~KMOD_CAPS; 400 | else if (key == SDLK_MODE) 401 | mod_state &= ~KMOD_MODE; 402 | } 403 | SDL_SetModState(mod_state); 404 | } 405 | 406 | void simulate_key(int key, int state) 407 | { 408 | update_modstate(key, state); 409 | unsigned short unicode = 0; 410 | if (key < 128) 411 | { 412 | unicode = key; 413 | } 414 | SDL_Event event = { 415 | .key = { 416 | .type = SDL_KEYDOWN, 417 | .state = SDL_PRESSED, 418 | .keysym = { 419 | .scancode = 0, 420 | .sym = key, 421 | .mod = KMOD_SYNTHETIC, 422 | .unicode = unicode, 423 | }}}; 424 | if (state == STATE_TYPED) 425 | { 426 | SDL_PushEvent(&event); 427 | event.key.type = SDL_KEYUP; 428 | event.key.state = SDL_RELEASED; 429 | } 430 | else if (state == STATE_UP) 431 | { 432 | event.key.type = SDL_KEYUP; 433 | event.key.state = SDL_RELEASED; 434 | } 435 | SDL_PushEvent(&event); 436 | // printf("%d\n", key); 437 | } 438 | 439 | int compute_visual_offset(int col, int row) 440 | { 441 | int sum = 0; 442 | for (int i = 0; i < col; i++) 443 | sum += 1 + strlen(syms[0][row][i]); 444 | sum += (1 + strlen(syms[0][row][col])) / 2; 445 | return sum; 446 | } 447 | 448 | int compute_new_col(int visual_offset, int old_row, int new_row) 449 | { 450 | int new_sum = 0; 451 | int new_col = 0; 452 | while (new_col < row_length[new_row] - 1 && new_sum + (1 + strlen(syms[0][new_row][new_col])) / 2 < visual_offset) 453 | { 454 | new_sum += 1 + strlen(syms[0][new_row][new_col]); 455 | new_col++; 456 | } 457 | return new_col; 458 | } 459 | 460 | int handle_keyboard_event(SDL_Event *event) 461 | { 462 | // printf("handle_keyboard_event: sym: %d, scancode:%d\n",event->key.keysym.sym, event->key.keysym.scancode); 463 | #if defined(R36S_SDL12COMPAT) 464 | // TODO: some keys are regconiized as "`" key. Temporary disable it. 465 | if (event->key.keysym.sym == SDLK_BACKQUOTE) 466 | { 467 | return 1; 468 | } 469 | #endif 470 | 471 | static int visual_offset = 0; 472 | if (event->key.type == SDL_KEYDOWN && !(event->key.keysym.mod & KMOD_SYNTHETIC) && event->key.keysym.sym == KEY_ACTIVATE) 473 | { 474 | active = !active; 475 | return 1; 476 | } 477 | 478 | if ((event->key.type == SDL_KEYUP || event->key.type == SDL_KEYDOWN) && event->key.keysym.mod & KMOD_SYNTHETIC) 479 | { 480 | if (event->key.type == SDL_KEYDOWN && event->key.keysym.sym == SDLK_PRINT) 481 | { 482 | show_help = 1; 483 | // time_t seconds = time(NULL); 484 | // char screen_shot_filename[255]; 485 | // snprintf(screen_shot_filename, 255, "SimpleTerminal-screenshot-%ld.bmp", seconds); 486 | // SDL_SaveBMP(SDL_GetVideoSurface(), screen_shot_filename); 487 | 488 | // TODO: not working 489 | // int w = 640; 490 | // int h = 480; 491 | 492 | // SDL_Surface *surface = SDL_CreateRGBSurface(0, w, h, 16, 0, 0, 0, 0); 493 | // SDL_LockSurface(surface); 494 | // int bpp = surface->format->BitsPerPixel; 495 | // for (int i = 0; i < h; i++) 496 | // { 497 | // for (int j = 0; j < w; j++) 498 | // { 499 | // // Uint32 *p = (Uint32 *)surface->pixels + (i * surface->pitch) + (j * bpp); 500 | // Uint32 *p = (Uint32 *)((Uint8 *)surface->pixels + (i * surface->pitch) + (j * bpp)); 501 | // *p = SDL_MapRGB(surface->format, i, j, i); 502 | // } 503 | // } 504 | // SDL_UnlockSurface(surface); 505 | // SDL_SaveBMP(surface, "Test.bmp"); 506 | // SDL_FreeSurface(surface); 507 | } 508 | return 0; 509 | } 510 | 511 | if (!active) 512 | { 513 | #if defined(MIYOOMINI) || defined(TRIMUISMART) || defined(RG35XXPLUS) || defined(R36S_SDL12COMPAT) 514 | if (event->key.type == SDL_KEYDOWN && event->key.state == SDL_PRESSED) 515 | { 516 | if (event->key.keysym.sym == KEY_QUIT) 517 | { 518 | return -1; 519 | } 520 | // TODO: fix this: when connect to bluetooth keyboard, some keys (L:108, G:103) are confliciting with arrow keys 521 | // else if (event->key.keysym.sym == KEY_UP || event->key.keysym.sym == KEY_ARROW_UP) 522 | // { 523 | // simulate_key(SDLK_UP, STATE_TYPED); 524 | // } 525 | // else if (event->key.keysym.sym == KEY_DOWN || event->key.keysym.sym == KEY_ARROW_DOWN) 526 | // { 527 | // simulate_key(SDLK_DOWN, STATE_TYPED); 528 | // } 529 | // else if (event->key.keysym.sym == KEY_LEFT || event->key.keysym.sym == KEY_ARROW_LEFT) 530 | // { 531 | // simulate_key(SDLK_LEFT, STATE_TYPED); 532 | // } 533 | // else if (event->key.keysym.sym == KEY_RIGHT || event->key.keysym.sym == KEY_ARROW_RIGHT) 534 | // { 535 | // simulate_key(SDLK_RIGHT, STATE_TYPED); 536 | // } 537 | else if (event->key.keysym.sym == KEY_RETURN) 538 | { 539 | simulate_key(SDLK_RETURN, STATE_TYPED); 540 | } 541 | } 542 | #endif 543 | return 0; 544 | } 545 | 546 | if (event->key.type == SDL_KEYDOWN && event->key.state == SDL_PRESSED) 547 | { 548 | if (show_help) 549 | { 550 | // do nothing 551 | } 552 | else if (event->key.keysym.sym == KEY_QUIT) 553 | { 554 | return -1; 555 | } 556 | else if (event->key.keysym.sym == KEY_UP) 557 | { 558 | if (selected_j > 0) 559 | { 560 | selected_i = compute_new_col(visual_offset, selected_j, selected_j - 1); 561 | selected_j--; 562 | } 563 | else 564 | selected_j = NUM_ROWS - 1; 565 | if (selected_i >= row_length[selected_j]) 566 | { 567 | selected_i = row_length[selected_j] - 1; 568 | } 569 | // selected_i = selected_i * row_length[selected_j] / row_length[selected_j + 1]; 570 | } 571 | else if (event->key.keysym.sym == KEY_DOWN) 572 | { 573 | if (selected_j < NUM_ROWS - 1) 574 | { 575 | selected_i = compute_new_col(visual_offset, selected_j, selected_j + 1); 576 | selected_j++; 577 | } 578 | else 579 | selected_j = 0; 580 | if (selected_i < 0) 581 | { 582 | selected_i = 0; 583 | } 584 | // selected_i = selected_i * row_length[selected_j] / row_length[selected_j - 1]; 585 | } 586 | else if (event->key.keysym.sym == KEY_LEFT) 587 | { 588 | if (selected_i > 0) 589 | selected_i--; 590 | else 591 | selected_i = row_length[selected_j] - 1; 592 | visual_offset = compute_visual_offset(selected_i, selected_j); 593 | } 594 | else if (event->key.keysym.sym == KEY_RIGHT) 595 | { 596 | if (selected_i < row_length[selected_j] - 1) 597 | selected_i++; 598 | else 599 | selected_i = 0; 600 | visual_offset = compute_visual_offset(selected_i, selected_j); 601 | } 602 | else if (event->key.keysym.sym == KEY_SHIFT) 603 | { 604 | shifted = 1; 605 | toggled[4][0] = 1; 606 | update_modstate(SDLK_LSHIFT, STATE_DOWN); 607 | } 608 | else if (event->key.keysym.sym == KEY_LOCATION) 609 | { 610 | location = !location; 611 | } 612 | else if (event->key.keysym.sym == KEY_BACKSPACE) 613 | { 614 | simulate_key(SDLK_BACKSPACE, STATE_TYPED); 615 | } 616 | else if (event->key.keysym.sym == KEY_ARROW_UP) 617 | { 618 | simulate_key(SDLK_UP, STATE_TYPED); 619 | } 620 | else if (event->key.keysym.sym == KEY_ARROW_DOWN) 621 | { 622 | simulate_key(SDLK_DOWN, STATE_TYPED); 623 | 624 | #ifndef TRIMUISMART 625 | } 626 | else if (event->key.keysym.sym == KEY_ARROW_LEFT) 627 | { 628 | simulate_key(SDLK_LEFT, STATE_TYPED); 629 | } 630 | else if (event->key.keysym.sym == KEY_ARROW_RIGHT) 631 | { 632 | simulate_key(SDLK_RIGHT, STATE_TYPED); 633 | #endif 634 | } 635 | else if (event->key.keysym.sym == KEY_TAB) 636 | { 637 | simulate_key(SDLK_TAB, STATE_TYPED); 638 | } 639 | else if (event->key.keysym.sym == KEY_RETURN) 640 | { 641 | simulate_key(SDLK_RETURN, STATE_TYPED); 642 | } 643 | else if (event->key.keysym.sym == KEY_TOGGLE) 644 | { 645 | toggled[selected_j][selected_i] = 1 - toggled[selected_j][selected_i]; 646 | if (toggled[selected_j][selected_i]) 647 | simulate_key(keys[shifted][selected_j][selected_i], STATE_DOWN); 648 | else 649 | simulate_key(keys[shifted][selected_j][selected_i], STATE_UP); 650 | if (selected_j == 4 && (selected_i == 0 || selected_i == 11)) 651 | shifted = toggled[selected_j][selected_i]; 652 | } 653 | else if (event->key.keysym.sym == KEY_ENTER) 654 | { 655 | int key = keys[shifted][selected_j][selected_i]; 656 | if (mod_state & KMOD_CTRL) 657 | { 658 | if (key >= 64 && key < 64 + 32) 659 | simulate_key(key - 64, STATE_DOWN); 660 | else if (key >= 97 && key < 97 + 31) 661 | simulate_key(key - 96, STATE_DOWN); 662 | } 663 | else if (mod_state & KMOD_SHIFT && key >= SDLK_a && key <= SDLK_z) 664 | { 665 | simulate_key(key - SDLK_a + 'A', STATE_TYPED); 666 | } 667 | else 668 | { 669 | simulate_key(key, STATE_TYPED); 670 | } 671 | } 672 | else 673 | { 674 | // fprintf(stderr,"unrecognized key: %d\n",event->key.keysym.sym); 675 | } 676 | } 677 | else if (event->key.type == SDL_KEYUP || event->key.state == SDL_RELEASED) 678 | { 679 | if (show_help) 680 | { 681 | if (event->key.keysym.sym != SDLK_PRINT && event->key.keysym.sym != KEY_ENTER && event->key.keysym.scancode != 0) 682 | { 683 | show_help = 0; 684 | } 685 | } 686 | else if (event->key.keysym.sym == KEY_SHIFT) 687 | { 688 | shifted = 0; 689 | toggled[4][0] = 0; 690 | update_modstate(SDLK_LSHIFT, STATE_UP); 691 | } 692 | } 693 | return 1; 694 | } 695 | 696 | #ifdef TEST_KEYBOARD 697 | 698 | int main() 699 | { 700 | SDL_Init(SDL_INIT_EVERYTHING); 701 | SDL_Surface *screen = SDL_SetVideoMode(320 * 4, 240 * 4, 16, SDL_SWSURFACE); 702 | SDL_Surface *buffer = SDL_CreateRGBSurface(SDL_SWSURFACE, 320, 240, 16, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask); 703 | while (1) 704 | { 705 | SDL_Event event; 706 | while (SDL_PollEvent(&event)) 707 | { 708 | if (event.type == SDL_QUIT) 709 | { 710 | return 0; 711 | } 712 | else 713 | { 714 | handle_keyboard_event(&event); 715 | } 716 | } 717 | SDL_Rect r = {0, 0, buffer->w, buffer->h}; 718 | SDL_FillRect(buffer, &r, 0); 719 | draw_keyboard(buffer); 720 | SDL_LockSurface(buffer); 721 | for (int j = 0; j < buffer->h; j++) 722 | { 723 | for (int i = 0; i < buffer->w; i++) 724 | { 725 | SDL_Rect rect = {i * 4, j * 4, 4, 4}; 726 | SDL_FillRect(screen, &rect, ((unsigned short *)buffer->pixels)[j * (buffer->pitch >> 1) + i]); 727 | } 728 | } 729 | SDL_UnlockSurface(buffer); 730 | SDL_Flip(screen); 731 | SDL_Delay(1000 / 30); 732 | } 733 | SDL_Quit(); 734 | } 735 | 736 | #endif 737 | -------------------------------------------------------------------------------- /src/keyboard.h: -------------------------------------------------------------------------------- 1 | #ifndef __KEYBOARD_H__ 2 | #define __KEYBOARD_H__ 3 | 4 | #include 5 | 6 | void init_keyboard(); 7 | void draw_keyboard(SDL_Surface* surface); 8 | int handle_keyboard_event(SDL_Event* event); 9 | extern int active; 10 | extern int show_help; 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /src/msg_queue.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "msg_queue.h" 8 | 9 | queue_t queue_create() { 10 | key_t keyval = ftok(QUEUE_NAME, QUEUE_ID); 11 | return msgget( keyval, IPC_CREAT | 0660 ); 12 | } 13 | 14 | queue_t queue_open() { 15 | key_t keyval = ftok(QUEUE_NAME, QUEUE_ID); 16 | return msgget( keyval, 0660 ); 17 | } 18 | 19 | int queue_send(queue_t qid, message_t *message) { 20 | int length = sizeof(message_t) - sizeof(long); 21 | 22 | return msgsnd( qid, message, length, 0); 23 | } 24 | 25 | int queue_read(queue_t qid, long type, message_t *message) { 26 | int length = sizeof(message_t) - sizeof(long); 27 | 28 | return msgrcv( qid, message, length, type, 0); 29 | } 30 | 31 | int queue_peek(queue_t qid, long type) { 32 | if(msgrcv( qid, NULL, 0, type, IPC_NOWAIT) == -1) { 33 | if(errno == E2BIG) return 1; 34 | } 35 | 36 | return 0; 37 | } 38 | 39 | int queue_remove(queue_t qid) { 40 | return msgctl(qid, IPC_RMID, 0); 41 | } 42 | 43 | -------------------------------------------------------------------------------- /src/msg_queue.h: -------------------------------------------------------------------------------- 1 | #ifndef __MSG_QUEUE__ 2 | #define __MSG_QUEUE__ 3 | 4 | #define QUEUE_NAME "st-sdl" 5 | #define QUEUE_ID 1 6 | 7 | typedef struct { 8 | long type; 9 | long data; 10 | } message_t; 11 | 12 | typedef int queue_t; 13 | 14 | enum { MSG_CLIENT=1, MSG_SERVER, MSG_REQUEST_SHUTDOWN, MSG_SHUTDOWN, MSG_REQUEST_INIT }; 15 | 16 | queue_t queue_create(); 17 | queue_t queue_open(); 18 | int queue_send(queue_t qid, message_t *message); 19 | int queue_read(queue_t qid, long type, message_t *message); 20 | int queue_peek(queue_t qid, long type); 21 | int queue_remove(queue_t qid); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /src/st.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE for licence details. */ 2 | #define _XOPEN_SOURCE 600 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | //#include 28 | 29 | #include "font.h" 30 | #include "keyboard.h" 31 | #include "msg_queue.h" 32 | 33 | #define Glyph Glyph_ 34 | #define Font Font_ 35 | 36 | #if defined(__linux) 37 | #include 38 | #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) 39 | #include 40 | #elif defined(__FreeBSD__) || defined(__DragonFly__) 41 | #include 42 | #elif defined(__QNXNTO__) 43 | #include 44 | #endif 45 | 46 | #define USAGE \ 47 | "st " VERSION " (c) 2010-2012 st engineers\n" \ 48 | "usage: st [-v] [-c class] [-f font] [-g geometry] [-o file]" \ 49 | " [-t title] [-e command ...]\n" 50 | 51 | /* Arbitrary sizes */ 52 | #define ESC_BUF_SIZ 256 53 | #define ESC_ARG_SIZ 16 54 | #define STR_BUF_SIZ 256 55 | #define STR_ARG_SIZ 16 56 | #define DRAW_BUF_SIZ 20*1024 57 | #define UTF_SIZ 4 58 | #define XK_NO_MOD UINT_MAX 59 | #define XK_ANY_MOD 0 60 | 61 | #define REDRAW_TIMEOUT (80*1000) /* 80 ms */ 62 | 63 | /* macros */ 64 | #define CLEANMASK(mask) (mask & (KMOD_SHIFT|KMOD_CTRL|KMOD_ALT|KMOD_META)) 65 | #define SERRNO strerror(errno) 66 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 67 | #define MAX(a, b) ((a) < (b) ? (b) : (a)) 68 | #define LEN(a) (sizeof(a) / sizeof(a[0])) 69 | #define DEFAULT(a, b) (a) = (a) ? (a) : (b) 70 | #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) 71 | #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) 72 | #define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || (a).bg != (b).bg) 73 | #define IS_SET(flag) (term.mode & (flag)) 74 | #define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + (t1.tv_usec-t2.tv_usec)/1000) 75 | 76 | #define VT102ID "\033[?6c" 77 | 78 | enum glyph_attribute { 79 | ATTR_NULL = 0, 80 | ATTR_REVERSE = 1, 81 | ATTR_UNDERLINE = 2, 82 | ATTR_BOLD = 4, 83 | ATTR_GFX = 8, 84 | ATTR_ITALIC = 16, 85 | ATTR_BLINK = 32, 86 | }; 87 | 88 | enum cursor_movement { 89 | CURSOR_UP, 90 | CURSOR_DOWN, 91 | CURSOR_LEFT, 92 | CURSOR_RIGHT, 93 | CURSOR_SAVE, 94 | CURSOR_LOAD 95 | }; 96 | 97 | enum cursor_state { 98 | CURSOR_DEFAULT = 0, 99 | CURSOR_HIDE = 1, 100 | CURSOR_WRAPNEXT = 2 101 | }; 102 | 103 | enum glyph_state { 104 | GLYPH_SET = 1, 105 | GLYPH_DIRTY = 2 106 | }; 107 | 108 | enum term_mode { 109 | MODE_WRAP = 1, 110 | MODE_INSERT = 2, 111 | MODE_APPKEYPAD = 4, 112 | MODE_ALTSCREEN = 8, 113 | MODE_CRLF = 16, 114 | MODE_MOUSEBTN = 32, 115 | MODE_MOUSEMOTION = 64, 116 | MODE_MOUSE = 32|64, 117 | MODE_REVERSE = 128, 118 | MODE_KBDLOCK = 256 119 | }; 120 | 121 | enum escape_state { 122 | ESC_START = 1, 123 | ESC_CSI = 2, 124 | ESC_STR = 4, /* DSC, OSC, PM, APC */ 125 | ESC_ALTCHARSET = 8, 126 | ESC_STR_END = 16, /* a final string was encountered */ 127 | ESC_TEST = 32, /* Enter in test mode */ 128 | }; 129 | 130 | enum window_state { 131 | WIN_VISIBLE = 1, 132 | WIN_REDRAW = 2, 133 | WIN_FOCUSED = 4 134 | }; 135 | 136 | /* bit macro */ 137 | #undef B0 138 | enum { B0=1, B1=2, B2=4, B3=8, B4=16, B5=32, B6=64, B7=128 }; 139 | 140 | typedef unsigned char uchar; 141 | typedef unsigned int uint; 142 | typedef unsigned long ulong; 143 | typedef unsigned short ushort; 144 | 145 | typedef struct { 146 | char c[UTF_SIZ]; /* character code */ 147 | uchar mode; /* attribute flags */ 148 | ushort fg; /* foreground */ 149 | ushort bg; /* background */ 150 | uchar state; /* state flags */ 151 | } Glyph; 152 | 153 | typedef Glyph* Line; 154 | 155 | typedef struct { 156 | Glyph attr; /* current char attributes */ 157 | int x; 158 | int y; 159 | char state; 160 | } TCursor; 161 | 162 | /* CSI Escape sequence structs */ 163 | /* ESC '[' [[ [] [;]] ] */ 164 | typedef struct { 165 | char buf[ESC_BUF_SIZ]; /* raw string */ 166 | int len; /* raw string length */ 167 | char priv; 168 | int arg[ESC_ARG_SIZ]; 169 | int narg; /* nb of args */ 170 | char mode; 171 | } CSIEscape; 172 | 173 | /* STR Escape sequence structs */ 174 | /* ESC type [[ [] [;]] ] ESC '\' */ 175 | typedef struct { 176 | char type; /* ESC type ... */ 177 | char buf[STR_BUF_SIZ]; /* raw string */ 178 | int len; /* raw string length */ 179 | char *args[STR_ARG_SIZ]; 180 | int narg; /* nb of args */ 181 | } STREscape; 182 | 183 | /* Internal representation of the screen */ 184 | typedef struct { 185 | int row; /* nb row */ 186 | int col; /* nb col */ 187 | Line *line; /* screen */ 188 | Line *alt; /* alternate screen */ 189 | bool *dirty; /* dirtyness of lines */ 190 | TCursor c; /* cursor */ 191 | int top; /* top scroll limit */ 192 | int bot; /* bottom scroll limit */ 193 | int mode; /* terminal mode flags */ 194 | int esc; /* escape state flags */ 195 | bool *tabs; 196 | } Term; 197 | 198 | /* Purely graphic info */ 199 | typedef struct { 200 | //Colormap cmap; 201 | SDL_Surface *win; 202 | int scr; 203 | bool isfixed; /* is fixed geometry? */ 204 | int fx, fy, fw, fh; /* fixed geometry */ 205 | int tw, th; /* tty width and height */ 206 | int w; /* window width */ 207 | int h; /* window height */ 208 | int ch; /* char height */ 209 | int cw; /* char width */ 210 | char state; /* focus, redraw, visible */ 211 | } XWindow; 212 | 213 | typedef struct { 214 | SDLKey k; 215 | SDLMod mask; 216 | char s[ESC_BUF_SIZ]; 217 | } Key; 218 | 219 | /* TODO: use better name for vars... */ 220 | typedef struct { 221 | int mode; 222 | int bx, by; 223 | int ex, ey; 224 | struct { 225 | int x, y; 226 | } b, e; 227 | char *clip; 228 | //Atom xtarget; 229 | bool alt; 230 | struct timeval tclick1; 231 | struct timeval tclick2; 232 | } Selection; 233 | 234 | typedef union { 235 | int i; 236 | unsigned int ui; 237 | float f; 238 | const void *v; 239 | } Arg; 240 | 241 | typedef struct { 242 | SDLMod mod; 243 | SDLKey keysym; 244 | void (*func)(const Arg *); 245 | const Arg arg; 246 | } Shortcut; 247 | 248 | /* function definitions used in config.h */ 249 | static void xzoom(const Arg *); 250 | 251 | /* Config.h for applying patches and the configuration. */ 252 | #include "config.h" 253 | 254 | SDL_Surface* screen; 255 | #if defined(MIYOOMINI) || defined(TRIMUISMART) || defined(RG35XX) 256 | SDL_Surface* screen2; 257 | #endif 258 | char preload_libname[PATH_MAX + 17]; 259 | 260 | /* Drawing Context */ 261 | typedef struct { 262 | SDL_Color colors[LEN(colormap) < 256 ? 256 : LEN(colormap)]; 263 | //TTF_Font *font, *ifont, *bfont, *ibfont; 264 | } DC; 265 | 266 | static void die(const char *, ...); 267 | static bool is_word_break(char); 268 | static void draw(void); 269 | static void redraw(void); 270 | static void drawregion(int, int, int, int); 271 | static void execsh(void); 272 | static void sigchld(int); 273 | static void run(void); 274 | int ttythread(void *unused); 275 | 276 | static void csidump(void); 277 | static void csihandle(void); 278 | static void csiparse(void); 279 | static void csireset(void); 280 | static void strdump(void); 281 | static void strhandle(void); 282 | static void strparse(void); 283 | static void strreset(void); 284 | 285 | static void tclearregion(int, int, int, int); 286 | static void tcursor(int); 287 | static void tdeletechar(int); 288 | static void tdeleteline(int); 289 | static void tinsertblank(int); 290 | static void tinsertblankline(int); 291 | static void tmoveto(int, int); 292 | static void tnew(int, int); 293 | static void tnewline(int); 294 | static void tputtab(bool); 295 | static void tputc(char *, int); 296 | static void treset(void); 297 | static int tresize(int, int); 298 | static void tscrollup(int, int); 299 | static void tscrolldown(int, int); 300 | static void tsetattr(int*, int); 301 | static void tsetchar(char *, Glyph *, int, int); 302 | static void tsetscroll(int, int); 303 | static void tswapscreen(void); 304 | static void tsetdirt(int, int); 305 | static void tsetmode(bool, bool, int *, int); 306 | static void tfulldirt(void); 307 | 308 | static void ttynew(void); 309 | static void ttyread(void); 310 | static void ttyresize(void); 311 | static void ttywrite(const char *, size_t); 312 | 313 | static void xdraws(char *, Glyph, int, int, int, int); 314 | static void xclear(int, int, int, int); 315 | static void xdrawcursor(void); 316 | static void sdlinit(void); 317 | static void initcolormap(void); 318 | static void sdlresettitle(void); 319 | static void xsetsel(char*); 320 | static void sdltermclear(int, int, int, int); 321 | static void xresize(int, int); 322 | 323 | static void expose(SDL_Event *); 324 | static void visibility(SDL_Event *); 325 | static void unmap(SDL_Event *); 326 | static char *kmap(SDLKey, SDLMod); 327 | static void kpress(SDL_Event *); 328 | static void cresize(int width, int height); 329 | static void resize(SDL_Event *); 330 | static void focus(SDL_Event *); 331 | static void activeEvent(SDL_Event *); 332 | static void brelease(SDL_Event *); 333 | static void bpress(SDL_Event *); 334 | static void bmotion(SDL_Event *); 335 | #if 0 336 | static void selnotify(SDL_Event *); 337 | static void selclear(SDL_Event *); 338 | static void selrequest(SDL_Event *); 339 | #endif 340 | 341 | static void selinit(void); 342 | static inline bool selected(int, int); 343 | static void selcopy(void); 344 | static void selpaste(void); 345 | static void selscroll(int, int); 346 | 347 | static int utf8decode(char *, long *); 348 | static int utf8encode(long *, char *); 349 | static int utf8size(char *); 350 | static int isfullutf8(char *, int); 351 | 352 | static ssize_t xwrite(int, char *, size_t); 353 | static void *xmalloc(size_t); 354 | static void *xrealloc(void *, size_t); 355 | static void *xcalloc(size_t nmemb, size_t size); 356 | static void xflip(void); 357 | 358 | static void (*handler[SDL_NUMEVENTS])(SDL_Event *) = { 359 | [SDL_KEYDOWN] = kpress, 360 | [SDL_VIDEORESIZE] = resize, 361 | [SDL_VIDEOEXPOSE] = expose, 362 | [SDL_ACTIVEEVENT] = activeEvent, 363 | [SDL_MOUSEMOTION] = bmotion, 364 | [SDL_MOUSEBUTTONDOWN] = bpress, 365 | [SDL_MOUSEBUTTONUP] = brelease, 366 | #if 0 367 | [SelectionClear] = selclear, 368 | [SelectionNotify] = selnotify, 369 | [SelectionRequest] = selrequest, 370 | #endif 371 | }; 372 | 373 | /* Globals */ 374 | static DC dc; 375 | static XWindow xw; 376 | static Term term; 377 | static CSIEscape csiescseq; 378 | static STREscape strescseq; 379 | static int cmdfd; 380 | static pid_t pid; 381 | static Selection sel; 382 | static int iofd = -1; 383 | static char **opt_cmd = NULL; 384 | static char *opt_io = NULL; 385 | static char *opt_title = NULL; 386 | static char *opt_class = NULL; 387 | static char *opt_font = NULL; 388 | 389 | static char *usedfont = NULL; 390 | static int usedfontsize = 0; 391 | 392 | ssize_t 393 | xwrite(int fd, char *s, size_t len) { 394 | size_t aux = len; 395 | 396 | while(len > 0) { 397 | ssize_t r = write(fd, s, len); 398 | if(r < 0) 399 | return r; 400 | len -= r; 401 | s += r; 402 | } 403 | return aux; 404 | } 405 | 406 | void * 407 | xmalloc(size_t len) { 408 | void *p = malloc(len); 409 | 410 | if(!p) 411 | die("Out of memory\n"); 412 | 413 | return p; 414 | } 415 | 416 | void * 417 | xrealloc(void *p, size_t len) { 418 | if((p = realloc(p, len)) == NULL) 419 | die("Out of memory\n"); 420 | 421 | return p; 422 | } 423 | 424 | void * 425 | xcalloc(size_t nmemb, size_t size) { 426 | void *p = calloc(nmemb, size); 427 | 428 | if(!p) 429 | die("Out of memory\n"); 430 | 431 | return p; 432 | } 433 | 434 | #ifdef RG35XX 435 | // upscale 320x240x16 -> 640x480x16 436 | void upscale2x(uint32_t* restrict src, uint32_t* restrict dst) { 437 | uint32_t x, y, pix, dpix1, dpix2; 438 | for(y = 240; y>0; y--, dst+=320) { 439 | for(x = 320/2; x>0; x--, dst+=2) { 440 | pix=*src++; 441 | dpix1=(pix & 0x0000FFFF)|(pix<<16); 442 | dpix2=(pix & 0xFFFF0000)|(pix>>16); 443 | dst[0] = dpix1; dst[1] = dpix2; 444 | dst[320] = dpix1; dst[321] = dpix2; 445 | } 446 | } 447 | } 448 | #elif MIYOOMINI 449 | // upscale 320x240x16 -> 640x480x32 with rotate180 450 | void upscale_and_rotate(uint32_t* restrict src, uint32_t* restrict dst) { 451 | dst = dst + 640*480 -1; 452 | uint32_t x, y, pix, dpix; 453 | for(y = 240; y>0 ; y--, dst-=640) { 454 | for(x = 320/2; x>0 ; x--, dst-=4) { 455 | pix=*src++; 456 | // 00000000RRRRRRRRGGGGGGGGBBBBBBBB 457 | dpix= ((pix>>2) &0b00000000000000000000000000000111)| 458 | ((pix>>1) &0b00000000000000000000001100000000)| 459 | ((pix<<3) &0b00000000000001110000000011111000)| 460 | ((pix<<5) &0b00000000000000001111110000000000)| 461 | ((pix<<8) &0b00000000111110000000000000000000); 462 | *dst=dpix; *(dst-1)=dpix; *(dst-640)=dpix; *(dst-641)=dpix; 463 | dpix= ((pix>>8) &0b00000000111110000000000000000000)| 464 | ((pix>>11)&0b00000000000000001111110000000000)| 465 | ((pix>>13)&0b00000000000001110000000011111000)| 466 | ((pix>>17)&0b00000000000000000000001100000000)| 467 | ((pix>>18)&0b00000000000000000000000000000111); 468 | *(dst-2)=dpix; *(dst-3)=dpix; *(dst-642)=dpix; *(dst-643)=dpix; 469 | } 470 | } 471 | } 472 | #elif TRIMUISMART 473 | // 320x240 -> 240x320 rotate90 CCW 474 | // AB BD 475 | // CD -> AC 476 | void rotate320x240_rw32(void* __restrict src, void* __restrict dst) { 477 | uint32_t *s, *d, pix1, pix2; 478 | int x, y; 479 | 480 | s = (uint32_t*)src + 159; 481 | d = (uint32_t*)dst; 482 | for (x=160; x>0; x--, s -= 160*240+1, d += 120) { 483 | for (y=120; y>0; y--, s += 320, d++) { 484 | pix1 = s[0]; // read AB 485 | pix2 = s[160]; // read CD 486 | d[0] = (pix1>>16) | (pix2 & 0xFFFF0000); // write BD 487 | d[120] = (pix1 & 0xFFFF) | (pix2<<16); // write AC 488 | } 489 | } 490 | } 491 | #endif 492 | 493 | void 494 | xflip(void) { 495 | if(xw.win == NULL) return; 496 | //printf("flip\n"); 497 | #if defined(MIYOOMINI) || defined(TRIMUISMART) || defined(RG35XX) 498 | memcpy(screen2->pixels, xw.win->pixels, 320*240*2); // copy for keyboardMix 499 | draw_keyboard(screen2); // screen2(SW) = console + keyboard 500 | #ifdef RG35XX 501 | upscale2x(screen2->pixels, screen->pixels); 502 | #elif MIYOOMINI 503 | upscale_and_rotate(screen2->pixels, screen->pixels); 504 | #else 505 | rotate320x240_rw32(screen2->pixels, screen->pixels); 506 | #endif 507 | SDL_Flip(screen); 508 | #else 509 | SDL_BlitSurface(xw.win, NULL, screen, NULL); 510 | draw_keyboard(screen); 511 | if(SDL_Flip(screen)) { 512 | //fputs("FLIP ERROR\n", stderr); 513 | //exit(EXIT_FAILURE); 514 | } 515 | #endif 516 | } 517 | 518 | int 519 | utf8decode(char *s, long *u) { 520 | uchar c; 521 | int i, n, rtn; 522 | 523 | rtn = 1; 524 | c = *s; 525 | if(~c & B7) { /* 0xxxxxxx */ 526 | *u = c; 527 | return rtn; 528 | } else if((c & (B7|B6|B5)) == (B7|B6)) { /* 110xxxxx */ 529 | *u = c&(B4|B3|B2|B1|B0); 530 | n = 1; 531 | } else if((c & (B7|B6|B5|B4)) == (B7|B6|B5)) { /* 1110xxxx */ 532 | *u = c&(B3|B2|B1|B0); 533 | n = 2; 534 | } else if((c & (B7|B6|B5|B4|B3)) == (B7|B6|B5|B4)) { /* 11110xxx */ 535 | *u = c & (B2|B1|B0); 536 | n = 3; 537 | } else { 538 | goto invalid; 539 | } 540 | 541 | for(i = n, ++s; i > 0; --i, ++rtn, ++s) { 542 | c = *s; 543 | if((c & (B7|B6)) != B7) /* 10xxxxxx */ 544 | goto invalid; 545 | *u <<= 6; 546 | *u |= c & (B5|B4|B3|B2|B1|B0); 547 | } 548 | 549 | if((n == 1 && *u < 0x80) || 550 | (n == 2 && *u < 0x800) || 551 | (n == 3 && *u < 0x10000) || 552 | (*u >= 0xD800 && *u <= 0xDFFF)) { 553 | goto invalid; 554 | } 555 | 556 | return rtn; 557 | invalid: 558 | *u = 0xFFFD; 559 | 560 | return rtn; 561 | } 562 | 563 | int 564 | utf8encode(long *u, char *s) { 565 | uchar *sp; 566 | ulong uc; 567 | int i, n; 568 | 569 | sp = (uchar *)s; 570 | uc = *u; 571 | if(uc < 0x80) { 572 | *sp = uc; /* 0xxxxxxx */ 573 | return 1; 574 | } else if(*u < 0x800) { 575 | *sp = (uc >> 6) | (B7|B6); /* 110xxxxx */ 576 | n = 1; 577 | } else if(uc < 0x10000) { 578 | *sp = (uc >> 12) | (B7|B6|B5); /* 1110xxxx */ 579 | n = 2; 580 | } else if(uc <= 0x10FFFF) { 581 | *sp = (uc >> 18) | (B7|B6|B5|B4); /* 11110xxx */ 582 | n = 3; 583 | } else { 584 | goto invalid; 585 | } 586 | 587 | for(i=n,++sp; i>0; --i,++sp) 588 | *sp = ((uc >> 6*(i-1)) & (B5|B4|B3|B2|B1|B0)) | B7; /* 10xxxxxx */ 589 | 590 | return n+1; 591 | invalid: 592 | /* U+FFFD */ 593 | *s++ = '\xEF'; 594 | *s++ = '\xBF'; 595 | *s = '\xBD'; 596 | 597 | return 3; 598 | } 599 | 600 | /* use this if your buffer is less than UTF_SIZ, it returns 1 if you can decode 601 | UTF-8 otherwise return 0 */ 602 | int 603 | isfullutf8(char *s, int b) { 604 | uchar *c1, *c2, *c3; 605 | 606 | c1 = (uchar *)s; 607 | c2 = (uchar *)++s; 608 | c3 = (uchar *)++s; 609 | if(b < 1) { 610 | return 0; 611 | } else if((*c1&(B7|B6|B5)) == (B7|B6) && b == 1) { 612 | return 0; 613 | } else if((*c1&(B7|B6|B5|B4)) == (B7|B6|B5) && 614 | ((b == 1) || 615 | ((b == 2) && (*c2&(B7|B6)) == B7))) { 616 | return 0; 617 | } else if((*c1&(B7|B6|B5|B4|B3)) == (B7|B6|B5|B4) && 618 | ((b == 1) || 619 | ((b == 2) && (*c2&(B7|B6)) == B7) || 620 | ((b == 3) && (*c2&(B7|B6)) == B7 && (*c3&(B7|B6)) == B7))) { 621 | return 0; 622 | } else { 623 | return 1; 624 | } 625 | } 626 | 627 | int 628 | utf8size(char *s) { 629 | uchar c = *s; 630 | 631 | if(~c&B7) { 632 | return 1; 633 | } else if((c&(B7|B6|B5)) == (B7|B6)) { 634 | return 2; 635 | } else if((c&(B7|B6|B5|B4)) == (B7|B6|B5)) { 636 | return 3; 637 | } else { 638 | return 4; 639 | } 640 | } 641 | 642 | void 643 | selinit(void) { 644 | // TODO 645 | #if 0 646 | memset(&sel.tclick1, 0, sizeof(sel.tclick1)); 647 | memset(&sel.tclick2, 0, sizeof(sel.tclick2)); 648 | sel.mode = 0; 649 | sel.bx = -1; 650 | sel.clip = NULL; 651 | sel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0); 652 | if(sel.xtarget == None) 653 | sel.xtarget = XA_STRING; 654 | #endif 655 | } 656 | 657 | static int 658 | x2col(int x) { 659 | x -= borderpx; 660 | x /= xw.cw; 661 | 662 | return LIMIT(x, 0, term.col-1); 663 | } 664 | 665 | static int 666 | y2row(int y) { 667 | y -= borderpx; 668 | y /= xw.ch; 669 | 670 | return LIMIT(y, 0, term.row-1); 671 | } 672 | 673 | static inline bool 674 | selected(int x, int y) { 675 | int bx, ex; 676 | 677 | if(sel.ey == y && sel.by == y) { 678 | bx = MIN(sel.bx, sel.ex); 679 | ex = MAX(sel.bx, sel.ex); 680 | return BETWEEN(x, bx, ex); 681 | } 682 | 683 | return ((sel.b.y < y && y < sel.e.y) 684 | || (y == sel.e.y && x <= sel.e.x)) 685 | || (y == sel.b.y && x >= sel.b.x 686 | && (x <= sel.e.x || sel.b.y != sel.e.y)); 687 | } 688 | 689 | void 690 | getbuttoninfo(SDL_Event *e, int *b, int *x, int *y) { 691 | if(b) 692 | *b = e->button.button; 693 | 694 | *x = x2col(e->button.x); 695 | *y = y2row(e->button.y); 696 | 697 | sel.b.x = sel.by < sel.ey ? sel.bx : sel.ex; 698 | sel.b.y = MIN(sel.by, sel.ey); 699 | sel.e.x = sel.by < sel.ey ? sel.ex : sel.bx; 700 | sel.e.y = MAX(sel.by, sel.ey); 701 | } 702 | 703 | void 704 | mousereport(SDL_Event *e) { 705 | int x = x2col(e->button.x); 706 | int y = y2row(e->button.y); 707 | int button = e->button.button; 708 | char buf[] = { '\033', '[', 'M', 0, 32+x+1, 32+y+1 }; 709 | static int ob, ox, oy; 710 | 711 | /* from urxvt */ 712 | if(e->type == SDL_MOUSEMOTION) { 713 | if(!IS_SET(MODE_MOUSEMOTION) || (x == ox && y == oy)) 714 | return; 715 | button = ob + 32; 716 | ox = x, oy = y; 717 | } else if(e->button.type == SDL_MOUSEBUTTONUP) { 718 | button = 3; 719 | } else { 720 | button -= SDL_BUTTON_LEFT; 721 | if(button >= 3) 722 | button += 64 - 3; 723 | if(e->button.type == SDL_MOUSEBUTTONDOWN) { 724 | ob = button; 725 | ox = x, oy = y; 726 | } 727 | } 728 | 729 | //TODO 730 | #if 0 731 | buf[3] = 32 + button + (state & ShiftMask ? 4 : 0) 732 | + (state & Mod4Mask ? 8 : 0) 733 | + (state & ControlMask ? 16 : 0); 734 | #endif 735 | buf[3] = 32 + button; 736 | 737 | ttywrite(buf, sizeof(buf)); 738 | } 739 | 740 | void 741 | bpress(SDL_Event *e) { 742 | if(IS_SET(MODE_MOUSE)) { 743 | mousereport(e); 744 | } else if(e->button.button == SDL_BUTTON_LEFT) { 745 | if(sel.bx != -1) { 746 | sel.bx = -1; 747 | tsetdirt(sel.b.y, sel.e.y); 748 | draw(); 749 | } 750 | sel.mode = 1; 751 | sel.ex = sel.bx = x2col(e->button.x); 752 | sel.ey = sel.by = y2row(e->button.y); 753 | } 754 | } 755 | 756 | void 757 | selcopy(void) { 758 | char *str, *ptr, *p; 759 | int x, y, bufsize, is_selected = 0, size; 760 | Glyph *gp, *last; 761 | 762 | if(sel.bx == -1) { 763 | str = NULL; 764 | } else { 765 | bufsize = (term.col+1) * (sel.e.y-sel.b.y+1) * UTF_SIZ; 766 | ptr = str = xmalloc(bufsize); 767 | 768 | /* append every set & selected glyph to the selection */ 769 | for(y = 0; y < term.row; y++) { 770 | gp = &term.line[y][0]; 771 | last = gp + term.col; 772 | 773 | while(--last >= gp && !(last->state & GLYPH_SET)) 774 | /* nothing */; 775 | 776 | for(x = 0; gp <= last; x++, ++gp) { 777 | if(!(is_selected = selected(x, y))) 778 | continue; 779 | 780 | p = (gp->state & GLYPH_SET) ? gp->c : " "; 781 | size = utf8size(p); 782 | memcpy(ptr, p, size); 783 | ptr += size; 784 | } 785 | /* \n at the end of every selected line except for the last one */ 786 | if(is_selected && y < sel.e.y) 787 | *ptr++ = '\n'; 788 | } 789 | *ptr = 0; 790 | } 791 | sel.alt = IS_SET(MODE_ALTSCREEN); 792 | xsetsel(str); 793 | } 794 | 795 | #if 0 796 | void 797 | selnotify(SDL_Event *e) { 798 | (void)e; 799 | // TODO 800 | ulong nitems, ofs, rem; 801 | int format; 802 | uchar *data; 803 | Atom type; 804 | 805 | ofs = 0; 806 | do { 807 | if(XGetWindowProperty(xw.dpy, xw.win, XA_PRIMARY, ofs, BUFSIZ/4, 808 | False, AnyPropertyType, &type, &format, 809 | &nitems, &rem, &data)) { 810 | fprintf(stderr, "Clipboard allocation failed\n"); 811 | return; 812 | } 813 | ttywrite((const char *) data, nitems * format / 8); 814 | XFree(data); 815 | /* number of 32-bit chunks returned */ 816 | ofs += nitems * format / 32; 817 | } while(rem > 0); 818 | } 819 | #endif 820 | 821 | void 822 | selpaste(void) { 823 | // TODO 824 | #if 0 825 | XConvertSelection(xw.dpy, XA_PRIMARY, sel.xtarget, XA_PRIMARY, 826 | xw.win, CurrentTime); 827 | #endif 828 | } 829 | 830 | #if 0 831 | void selclear(SDL_Event *e) { 832 | (void)e; 833 | if(sel.bx == -1) 834 | return; 835 | sel.bx = -1; 836 | tsetdirt(sel.b.y, sel.e.y); 837 | } 838 | 839 | void 840 | selrequest(SDL_Event *e) { 841 | (void)e; 842 | // TODO 843 | XSelectionRequestEvent *xsre; 844 | XSelectionEvent xev; 845 | Atom xa_targets, string; 846 | 847 | xsre = (XSelectionRequestEvent *) e; 848 | xev.type = SelectionNotify; 849 | xev.requestor = xsre->requestor; 850 | xev.selection = xsre->selection; 851 | xev.target = xsre->target; 852 | xev.time = xsre->time; 853 | /* reject */ 854 | xev.property = None; 855 | 856 | xa_targets = XInternAtom(xw.dpy, "TARGETS", 0); 857 | if(xsre->target == xa_targets) { 858 | /* respond with the supported type */ 859 | string = sel.xtarget; 860 | XChangeProperty(xsre->display, xsre->requestor, xsre->property, 861 | XA_ATOM, 32, PropModeReplace, 862 | (uchar *) &string, 1); 863 | xev.property = xsre->property; 864 | } else if(xsre->target == sel.xtarget && sel.clip != NULL) { 865 | XChangeProperty(xsre->display, xsre->requestor, xsre->property, 866 | xsre->target, 8, PropModeReplace, 867 | (uchar *) sel.clip, strlen(sel.clip)); 868 | xev.property = xsre->property; 869 | } 870 | 871 | /* all done, send a notification to the listener */ 872 | if(!XSendEvent(xsre->display, xsre->requestor, True, 0, (XEvent *) &xev)) 873 | fprintf(stderr, "Error sending SelectionNotify event\n"); 874 | } 875 | #endif 876 | 877 | void 878 | xsetsel(char *str) { 879 | (void)str; 880 | // TODO 881 | # if 0 882 | /* register the selection for both the clipboard and the primary */ 883 | Atom clipboard; 884 | 885 | free(sel.clip); 886 | sel.clip = str; 887 | 888 | XSetSelectionOwner(xw.dpy, XA_PRIMARY, xw.win, CurrentTime); 889 | 890 | clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0); 891 | XSetSelectionOwner(xw.dpy, clipboard, xw.win, CurrentTime); 892 | #endif 893 | } 894 | 895 | void 896 | brelease(SDL_Event *e) { 897 | struct timeval now; 898 | 899 | if(IS_SET(MODE_MOUSE)) { 900 | mousereport(e); 901 | return; 902 | } 903 | 904 | if(e->button.button == SDL_BUTTON_MIDDLE) { 905 | selpaste(); 906 | } else if(e->button.button == SDL_BUTTON_LEFT) { 907 | sel.mode = 0; 908 | getbuttoninfo(e, NULL, &sel.ex, &sel.ey); 909 | term.dirty[sel.ey] = 1; 910 | if(sel.bx == sel.ex && sel.by == sel.ey) { 911 | sel.bx = -1; 912 | gettimeofday(&now, NULL); 913 | 914 | if(TIMEDIFF(now, sel.tclick2) <= tripleclicktimeout) { 915 | /* triple click on the line */ 916 | sel.b.x = sel.bx = 0; 917 | sel.e.x = sel.ex = term.col; 918 | sel.b.y = sel.e.y = sel.ey; 919 | selcopy(); 920 | } else if(TIMEDIFF(now, sel.tclick1) <= doubleclicktimeout) { 921 | /* double click to select word */ 922 | sel.bx = sel.ex; 923 | while(sel.bx > 0 && term.line[sel.ey][sel.bx-1].state & GLYPH_SET && 924 | !is_word_break(term.line[sel.ey][sel.bx-1].c[0])) { 925 | sel.bx--; 926 | } 927 | sel.b.x = sel.bx; 928 | while(sel.ex < term.col-1 && term.line[sel.ey][sel.ex+1].state & GLYPH_SET && 929 | !is_word_break(term.line[sel.ey][sel.ex+1].c[0])) { 930 | sel.ex++; 931 | } 932 | sel.e.x = sel.ex; 933 | sel.b.y = sel.e.y = sel.ey; 934 | selcopy(); 935 | } 936 | } else { 937 | selcopy(); 938 | } 939 | } 940 | 941 | memcpy(&sel.tclick2, &sel.tclick1, sizeof(struct timeval)); 942 | gettimeofday(&sel.tclick1, NULL); 943 | } 944 | 945 | void 946 | bmotion(SDL_Event *e) { 947 | int starty, endy, oldey, oldex; 948 | 949 | if(IS_SET(MODE_MOUSE)) { 950 | mousereport(e); 951 | return; 952 | } 953 | 954 | if(sel.mode) { 955 | oldey = sel.ey; 956 | oldex = sel.ex; 957 | getbuttoninfo(e, NULL, &sel.ex, &sel.ey); 958 | 959 | if(oldey != sel.ey || oldex != sel.ex) { 960 | starty = MIN(oldey, sel.ey); 961 | endy = MAX(oldey, sel.ey); 962 | tsetdirt(starty, endy); 963 | } 964 | } 965 | } 966 | 967 | void 968 | die(const char *errstr, ...) { 969 | va_list ap; 970 | 971 | va_start(ap, errstr); 972 | vfprintf(stderr, errstr, ap); 973 | va_end(ap); 974 | exit(EXIT_FAILURE); 975 | } 976 | 977 | bool 978 | is_word_break(char c) { 979 | static char *word_break = WORD_BREAK; 980 | char *s = word_break; 981 | while(*s) { 982 | if(*s == c) return true; 983 | s++; 984 | } 985 | return false; 986 | } 987 | 988 | void 989 | execsh(void) { 990 | char **args; 991 | char *envshell = getenv("SHELL"); 992 | const struct passwd *pass = getpwuid(getuid()); 993 | #if 0 994 | char buf[sizeof(long) * 8 + 1]; 995 | #endif 996 | 997 | unsetenv("COLUMNS"); 998 | unsetenv("LINES"); 999 | unsetenv("TERMCAP"); 1000 | 1001 | if(pass) { 1002 | setenv("LOGNAME", pass->pw_name, 1); 1003 | setenv("USER", pass->pw_name, 1); 1004 | setenv("SHELL", pass->pw_shell, 0); 1005 | setenv("HOME", pass->pw_dir, 0); 1006 | } 1007 | chdir(getenv("HOME")); 1008 | 1009 | char *home = get_current_dir_name(); 1010 | setenv("HOME", home, 1); 1011 | free(home); 1012 | 1013 | setenv("LD_PRELOAD", preload_libname, 1); 1014 | setenv("PS1", "\\[\\033[32m\\]\\W\\[\\033[00m\\]\\$ ", 1); 1015 | 1016 | #if 0 1017 | snprintf(buf, sizeof(buf), "%lu", xw.win); 1018 | setenv("WINDOWID", buf, 1); 1019 | #endif 1020 | 1021 | signal(SIGCHLD, SIG_DFL); 1022 | signal(SIGHUP, SIG_DFL); 1023 | signal(SIGINT, SIG_DFL); 1024 | signal(SIGQUIT, SIG_DFL); 1025 | signal(SIGTERM, SIG_DFL); 1026 | signal(SIGALRM, SIG_DFL); 1027 | 1028 | DEFAULT(envshell, shell); 1029 | setenv("TERM", termname, 1); 1030 | args = opt_cmd ? opt_cmd : (char *[]){envshell, "-i", NULL}; 1031 | execvp(args[0], args); 1032 | exit(EXIT_FAILURE); 1033 | } 1034 | 1035 | void 1036 | sigchld(int a) { 1037 | int stat = 0; 1038 | (void)a; 1039 | 1040 | if(waitpid(pid, &stat, 0) < 0) 1041 | die("Waiting for pid %hd failed: %s\n", pid, SERRNO); 1042 | 1043 | if(WIFEXITED(stat)) { 1044 | exit(WEXITSTATUS(stat)); 1045 | } else { 1046 | exit(EXIT_FAILURE); 1047 | } 1048 | } 1049 | 1050 | void 1051 | ttynew(void) { 1052 | int m, s; 1053 | struct winsize w = {term.row, term.col, 0, 0}; 1054 | 1055 | /* seems to work fine on linux, openbsd and freebsd */ 1056 | if(openpty(&m, &s, NULL, NULL, &w) < 0) 1057 | die("openpty failed: %s\n", SERRNO); 1058 | 1059 | switch(pid = fork()) { 1060 | case -1: 1061 | die("fork failed\n"); 1062 | break; 1063 | case 0: 1064 | setsid(); /* create a new process group */ 1065 | dup2(s, STDIN_FILENO); 1066 | dup2(s, STDOUT_FILENO); 1067 | dup2(s, STDERR_FILENO); 1068 | if(ioctl(s, TIOCSCTTY, NULL) < 0) 1069 | die("ioctl TIOCSCTTY failed: %s\n", SERRNO); 1070 | close(s); 1071 | close(m); 1072 | execsh(); 1073 | break; 1074 | default: 1075 | close(s); 1076 | cmdfd = m; 1077 | signal(SIGCHLD, sigchld); 1078 | if(opt_io) { 1079 | iofd = (!strcmp(opt_io, "-")) ? 1080 | STDOUT_FILENO : 1081 | open(opt_io, O_WRONLY | O_CREAT, 0666); 1082 | if(iofd < 0) { 1083 | fprintf(stderr, "Error opening %s:%s\n", 1084 | opt_io, strerror(errno)); 1085 | } 1086 | } 1087 | } 1088 | } 1089 | 1090 | void 1091 | dump(char c) { 1092 | static int col; 1093 | 1094 | fprintf(stderr, " %02x '%c' ", c, isprint(c)?c:'.'); 1095 | if(++col % 10 == 0) 1096 | fprintf(stderr, "\n"); 1097 | } 1098 | 1099 | void 1100 | ttyread(void) { 1101 | static char buf[BUFSIZ]; 1102 | static int buflen = 0; 1103 | char *ptr; 1104 | char s[UTF_SIZ]; 1105 | int charsize; /* size of utf8 char in bytes */ 1106 | long utf8c; 1107 | int ret; 1108 | 1109 | /* append read bytes to unprocessed bytes */ 1110 | if((ret = read(cmdfd, buf+buflen, LEN(buf)-buflen)) < 0) 1111 | die("Couldn't read from shell: %s\n", SERRNO); 1112 | 1113 | /* process every complete utf8 char */ 1114 | buflen += ret; 1115 | ptr = buf; 1116 | while(buflen >= UTF_SIZ || isfullutf8(ptr,buflen)) { 1117 | charsize = utf8decode(ptr, &utf8c); 1118 | utf8encode(&utf8c, s); 1119 | tputc(s, charsize); 1120 | ptr += charsize; 1121 | buflen -= charsize; 1122 | } 1123 | 1124 | /* keep any uncomplete utf8 char for the next call */ 1125 | memmove(buf, ptr, buflen); 1126 | } 1127 | 1128 | void 1129 | ttywrite(const char *s, size_t n) { 1130 | if(write(cmdfd, s, n) == -1) 1131 | die("write error on tty: %s\n", SERRNO); 1132 | } 1133 | 1134 | void 1135 | ttyresize(void) { 1136 | struct winsize w; 1137 | 1138 | w.ws_row = term.row; 1139 | w.ws_col = term.col; 1140 | w.ws_xpixel = xw.tw; 1141 | w.ws_ypixel = xw.th; 1142 | if(ioctl(cmdfd, TIOCSWINSZ, &w) < 0) 1143 | fprintf(stderr, "Couldn't set window size: %s\n", SERRNO); 1144 | } 1145 | 1146 | void 1147 | tsetdirt(int top, int bot) { 1148 | int i; 1149 | 1150 | LIMIT(top, 0, term.row-1); 1151 | LIMIT(bot, 0, term.row-1); 1152 | 1153 | for(i = top; i <= bot; i++) 1154 | term.dirty[i] = 1; 1155 | } 1156 | 1157 | void 1158 | tfulldirt(void) { 1159 | tsetdirt(0, term.row-1); 1160 | } 1161 | 1162 | void 1163 | tcursor(int mode) { 1164 | static TCursor c; 1165 | 1166 | if(mode == CURSOR_SAVE) { 1167 | c = term.c; 1168 | } else if(mode == CURSOR_LOAD) { 1169 | term.c = c; 1170 | tmoveto(c.x, c.y); 1171 | } 1172 | } 1173 | 1174 | void 1175 | treset(void) { 1176 | uint i; 1177 | 1178 | term.c = (TCursor){{ 1179 | .mode = ATTR_NULL, 1180 | .fg = defaultfg, 1181 | .bg = defaultbg 1182 | }, .x = 0, .y = 0, .state = CURSOR_DEFAULT}; 1183 | 1184 | memset(term.tabs, 0, term.col * sizeof(*term.tabs)); 1185 | for(i = tabspaces; i < term.col; i += tabspaces) 1186 | term.tabs[i] = 1; 1187 | term.top = 0; 1188 | term.bot = term.row - 1; 1189 | term.mode = MODE_WRAP; 1190 | 1191 | tclearregion(0, 0, term.col-1, term.row-1); 1192 | } 1193 | 1194 | void 1195 | tnew(int col, int row) { 1196 | /* set screen size */ 1197 | term.row = row; 1198 | term.col = col; 1199 | term.line = xmalloc(term.row * sizeof(Line)); 1200 | term.alt = xmalloc(term.row * sizeof(Line)); 1201 | term.dirty = xmalloc(term.row * sizeof(*term.dirty)); 1202 | term.tabs = xmalloc(term.col * sizeof(*term.tabs)); 1203 | 1204 | for(row = 0; row < term.row; row++) { 1205 | term.line[row] = xmalloc(term.col * sizeof(Glyph)); 1206 | term.alt [row] = xmalloc(term.col * sizeof(Glyph)); 1207 | term.dirty[row] = 0; 1208 | } 1209 | memset(term.tabs, 0, term.col * sizeof(*term.tabs)); 1210 | /* setup screen */ 1211 | treset(); 1212 | } 1213 | 1214 | void 1215 | tswapscreen(void) { 1216 | Line *tmp = term.line; 1217 | 1218 | term.line = term.alt; 1219 | term.alt = tmp; 1220 | term.mode ^= MODE_ALTSCREEN; 1221 | tfulldirt(); 1222 | } 1223 | 1224 | void 1225 | tscrolldown(int orig, int n) { 1226 | int i; 1227 | Line temp; 1228 | 1229 | LIMIT(n, 0, term.bot-orig+1); 1230 | 1231 | tclearregion(0, term.bot-n+1, term.col-1, term.bot); 1232 | 1233 | for(i = term.bot; i >= orig+n; i--) { 1234 | temp = term.line[i]; 1235 | term.line[i] = term.line[i-n]; 1236 | term.line[i-n] = temp; 1237 | 1238 | term.dirty[i] = 1; 1239 | term.dirty[i-n] = 1; 1240 | } 1241 | 1242 | selscroll(orig, n); 1243 | } 1244 | 1245 | void 1246 | tscrollup(int orig, int n) { 1247 | int i; 1248 | Line temp; 1249 | LIMIT(n, 0, term.bot-orig+1); 1250 | 1251 | tclearregion(0, orig, term.col-1, orig+n-1); 1252 | 1253 | for(i = orig; i <= term.bot-n; i++) { 1254 | temp = term.line[i]; 1255 | term.line[i] = term.line[i+n]; 1256 | term.line[i+n] = temp; 1257 | 1258 | term.dirty[i] = 1; 1259 | term.dirty[i+n] = 1; 1260 | } 1261 | 1262 | selscroll(orig, -n); 1263 | } 1264 | 1265 | void 1266 | selscroll(int orig, int n) { 1267 | if(sel.bx == -1) 1268 | return; 1269 | 1270 | if(BETWEEN(sel.by, orig, term.bot) || BETWEEN(sel.ey, orig, term.bot)) { 1271 | if((sel.by += n) > term.bot || (sel.ey += n) < term.top) { 1272 | sel.bx = -1; 1273 | return; 1274 | } 1275 | if(sel.by < term.top) { 1276 | sel.by = term.top; 1277 | sel.bx = 0; 1278 | } 1279 | if(sel.ey > term.bot) { 1280 | sel.ey = term.bot; 1281 | sel.ex = term.col; 1282 | } 1283 | sel.b.y = sel.by, sel.b.x = sel.bx; 1284 | sel.e.y = sel.ey, sel.e.x = sel.ex; 1285 | } 1286 | } 1287 | 1288 | void 1289 | tnewline(int first_col) { 1290 | int y = term.c.y; 1291 | 1292 | if(y == term.bot) { 1293 | tscrollup(term.top, 1); 1294 | } else { 1295 | y++; 1296 | } 1297 | tmoveto(first_col ? 0 : term.c.x, y); 1298 | } 1299 | 1300 | void 1301 | csiparse(void) { 1302 | /* int noarg = 1; */ 1303 | char *p = csiescseq.buf; 1304 | 1305 | csiescseq.narg = 0; 1306 | if(*p == '?') 1307 | csiescseq.priv = 1, p++; 1308 | 1309 | while(p < csiescseq.buf+csiescseq.len) { 1310 | while(isdigit(*p)) { 1311 | csiescseq.arg[csiescseq.narg] *= 10; 1312 | csiescseq.arg[csiescseq.narg] += *p++ - '0'/*, noarg = 0 */; 1313 | } 1314 | if(*p == ';' && csiescseq.narg+1 < ESC_ARG_SIZ) { 1315 | csiescseq.narg++, p++; 1316 | } else { 1317 | csiescseq.mode = *p; 1318 | csiescseq.narg++; 1319 | 1320 | return; 1321 | } 1322 | } 1323 | } 1324 | 1325 | void 1326 | tmoveto(int x, int y) { 1327 | LIMIT(x, 0, term.col-1); 1328 | LIMIT(y, 0, term.row-1); 1329 | term.c.state &= ~CURSOR_WRAPNEXT; 1330 | term.c.x = x; 1331 | term.c.y = y; 1332 | } 1333 | 1334 | void 1335 | tsetchar(char *c, Glyph *attr, int x, int y) { 1336 | static char *vt100_0[62] = { /* 0x41 - 0x7e */ 1337 | "↑", "↓", "→", "←", "█", "▚", "☃", /* A - G */ 1338 | 0, 0, 0, 0, 0, 0, 0, 0, /* H - O */ 1339 | 0, 0, 0, 0, 0, 0, 0, 0, /* P - W */ 1340 | 0, 0, 0, 0, 0, 0, 0, " ", /* X - _ */ 1341 | "◆", "▒", "␉", "␌", "␍", "␊", "°", "±", /* ` - g */ 1342 | "␤", "␋", "┘", "┐", "┌", "└", "┼", "⎺", /* h - o */ 1343 | "⎻", "─", "⎼", "⎽", "├", "┤", "┴", "┬", /* p - w */ 1344 | "│", "≤", "≥", "π", "≠", "£", "·", /* x - ~ */ 1345 | }; 1346 | 1347 | /* 1348 | * The table is proudly stolen from rxvt. 1349 | */ 1350 | if(attr->mode & ATTR_GFX) { 1351 | if(c[0] >= 0x41 && c[0] <= 0x7e 1352 | && vt100_0[c[0] - 0x41]) { 1353 | c = vt100_0[c[0] - 0x41]; 1354 | } 1355 | } 1356 | 1357 | term.dirty[y] = 1; 1358 | term.line[y][x] = *attr; 1359 | memcpy(term.line[y][x].c, c, UTF_SIZ); 1360 | term.line[y][x].state |= GLYPH_SET; 1361 | } 1362 | 1363 | void 1364 | tclearregion(int x1, int y1, int x2, int y2) { 1365 | int x, y, temp; 1366 | 1367 | if(x1 > x2) 1368 | temp = x1, x1 = x2, x2 = temp; 1369 | if(y1 > y2) 1370 | temp = y1, y1 = y2, y2 = temp; 1371 | 1372 | LIMIT(x1, 0, term.col-1); 1373 | LIMIT(x2, 0, term.col-1); 1374 | LIMIT(y1, 0, term.row-1); 1375 | LIMIT(y2, 0, term.row-1); 1376 | 1377 | for(y = y1; y <= y2; y++) { 1378 | term.dirty[y] = 1; 1379 | for(x = x1; x <= x2; x++) 1380 | term.line[y][x].state = 0; 1381 | } 1382 | } 1383 | 1384 | void 1385 | tdeletechar(int n) { 1386 | int src = term.c.x + n; 1387 | int dst = term.c.x; 1388 | int size = term.col - src; 1389 | 1390 | term.dirty[term.c.y] = 1; 1391 | 1392 | if(src >= term.col) { 1393 | tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); 1394 | return; 1395 | } 1396 | 1397 | memmove(&term.line[term.c.y][dst], &term.line[term.c.y][src], 1398 | size * sizeof(Glyph)); 1399 | tclearregion(term.col-n, term.c.y, term.col-1, term.c.y); 1400 | } 1401 | 1402 | void 1403 | tinsertblank(int n) { 1404 | int src = term.c.x; 1405 | int dst = src + n; 1406 | int size = term.col - dst; 1407 | 1408 | term.dirty[term.c.y] = 1; 1409 | 1410 | if(dst >= term.col) { 1411 | tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); 1412 | return; 1413 | } 1414 | 1415 | memmove(&term.line[term.c.y][dst], &term.line[term.c.y][src], 1416 | size * sizeof(Glyph)); 1417 | tclearregion(src, term.c.y, dst - 1, term.c.y); 1418 | } 1419 | 1420 | void 1421 | tinsertblankline(int n) { 1422 | if(term.c.y < term.top || term.c.y > term.bot) 1423 | return; 1424 | 1425 | tscrolldown(term.c.y, n); 1426 | } 1427 | 1428 | void 1429 | tdeleteline(int n) { 1430 | if(term.c.y < term.top || term.c.y > term.bot) 1431 | return; 1432 | 1433 | tscrollup(term.c.y, n); 1434 | } 1435 | 1436 | void 1437 | tsetattr(int *attr, int l) { 1438 | int i; 1439 | 1440 | for(i = 0; i < l; i++) { 1441 | switch(attr[i]) { 1442 | case 0: 1443 | term.c.attr.mode &= ~(ATTR_REVERSE | ATTR_UNDERLINE | ATTR_BOLD \ 1444 | | ATTR_ITALIC | ATTR_BLINK); 1445 | term.c.attr.fg = defaultfg; 1446 | term.c.attr.bg = defaultbg; 1447 | break; 1448 | case 1: 1449 | term.c.attr.mode |= ATTR_BOLD; 1450 | break; 1451 | case 3: /* enter standout (highlight) */ 1452 | term.c.attr.mode |= ATTR_ITALIC; 1453 | break; 1454 | case 4: 1455 | term.c.attr.mode |= ATTR_UNDERLINE; 1456 | break; 1457 | case 5: 1458 | term.c.attr.mode |= ATTR_BLINK; 1459 | break; 1460 | case 7: 1461 | term.c.attr.mode |= ATTR_REVERSE; 1462 | break; 1463 | case 21: 1464 | case 22: 1465 | term.c.attr.mode &= ~ATTR_BOLD; 1466 | break; 1467 | case 23: /* leave standout (highlight) mode */ 1468 | term.c.attr.mode &= ~ATTR_ITALIC; 1469 | break; 1470 | case 24: 1471 | term.c.attr.mode &= ~ATTR_UNDERLINE; 1472 | break; 1473 | case 25: 1474 | term.c.attr.mode &= ~ATTR_BLINK; 1475 | break; 1476 | case 27: 1477 | term.c.attr.mode &= ~ATTR_REVERSE; 1478 | break; 1479 | case 38: 1480 | if(i + 2 < l && attr[i + 1] == 5) { 1481 | i += 2; 1482 | if(BETWEEN(attr[i], 0, 255)) { 1483 | term.c.attr.fg = attr[i]; 1484 | } else { 1485 | fprintf(stderr, 1486 | "erresc: bad fgcolor %d\n", 1487 | attr[i]); 1488 | } 1489 | } else { 1490 | fprintf(stderr, 1491 | "erresc(38): gfx attr %d unknown\n", 1492 | attr[i]); 1493 | } 1494 | break; 1495 | case 39: 1496 | term.c.attr.fg = defaultfg; 1497 | break; 1498 | case 48: 1499 | if(i + 2 < l && attr[i + 1] == 5) { 1500 | i += 2; 1501 | if(BETWEEN(attr[i], 0, 255)) { 1502 | term.c.attr.bg = attr[i]; 1503 | } else { 1504 | fprintf(stderr, 1505 | "erresc: bad bgcolor %d\n", 1506 | attr[i]); 1507 | } 1508 | } else { 1509 | fprintf(stderr, 1510 | "erresc(48): gfx attr %d unknown\n", 1511 | attr[i]); 1512 | } 1513 | break; 1514 | case 49: 1515 | term.c.attr.bg = defaultbg; 1516 | break; 1517 | default: 1518 | if(BETWEEN(attr[i], 30, 37)) { 1519 | term.c.attr.fg = attr[i] - 30; 1520 | } else if(BETWEEN(attr[i], 40, 47)) { 1521 | term.c.attr.bg = attr[i] - 40; 1522 | } else if(BETWEEN(attr[i], 90, 97)) { 1523 | term.c.attr.fg = attr[i] - 90 + 8; 1524 | } else if(BETWEEN(attr[i], 100, 107)) { 1525 | term.c.attr.bg = attr[i] - 100 + 8; 1526 | } else { 1527 | fprintf(stderr, 1528 | "erresc(default): gfx attr %d unknown\n", 1529 | attr[i]), csidump(); 1530 | } 1531 | break; 1532 | } 1533 | } 1534 | } 1535 | 1536 | void 1537 | tsetscroll(int t, int b) { 1538 | int temp; 1539 | 1540 | LIMIT(t, 0, term.row-1); 1541 | LIMIT(b, 0, term.row-1); 1542 | if(t > b) { 1543 | temp = t; 1544 | t = b; 1545 | b = temp; 1546 | } 1547 | term.top = t; 1548 | term.bot = b; 1549 | } 1550 | 1551 | #define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit))) 1552 | 1553 | void 1554 | tsetmode(bool priv, bool set, int *args, int narg) { 1555 | int *lim, mode; 1556 | 1557 | for(lim = args + narg; args < lim; ++args) { 1558 | if(priv) { 1559 | switch(*args) { 1560 | break; 1561 | case 1: /* DECCKM -- Cursor key */ 1562 | MODBIT(term.mode, set, MODE_APPKEYPAD); 1563 | break; 1564 | case 5: /* DECSCNM -- Reverse video */ 1565 | mode = term.mode; 1566 | MODBIT(term.mode, set, MODE_REVERSE); 1567 | if(mode != term.mode) 1568 | redraw(); 1569 | break; 1570 | case 6: /* XXX: DECOM -- Origin */ 1571 | break; 1572 | case 7: /* DECAWM -- Auto wrap */ 1573 | MODBIT(term.mode, set, MODE_WRAP); 1574 | break; 1575 | case 8: /* XXX: DECARM -- Auto repeat */ 1576 | break; 1577 | case 0: /* Error (IGNORED) */ 1578 | case 12: /* att610 -- Start blinking cursor (IGNORED) */ 1579 | break; 1580 | case 25: 1581 | MODBIT(term.c.state, !set, CURSOR_HIDE); 1582 | break; 1583 | case 1000: /* 1000,1002: enable xterm mouse report */ 1584 | MODBIT(term.mode, set, MODE_MOUSEBTN); 1585 | break; 1586 | case 1002: 1587 | MODBIT(term.mode, set, MODE_MOUSEMOTION); 1588 | break; 1589 | case 1049: /* = 1047 and 1048 */ 1590 | case 47: 1591 | case 1047: 1592 | if(IS_SET(MODE_ALTSCREEN)) 1593 | tclearregion(0, 0, term.col-1, term.row-1); 1594 | if((set && !IS_SET(MODE_ALTSCREEN)) || 1595 | (!set && IS_SET(MODE_ALTSCREEN))) { 1596 | tswapscreen(); 1597 | } 1598 | if(*args != 1049) 1599 | break; 1600 | /* pass through */ 1601 | case 1048: 1602 | tcursor((set) ? CURSOR_SAVE : CURSOR_LOAD); 1603 | break; 1604 | default: 1605 | /* case 2: DECANM -- ANSI/VT52 (NOT SUPPOURTED) */ 1606 | /* case 3: DECCOLM -- Column (NOT SUPPORTED) */ 1607 | /* case 4: DECSCLM -- Scroll (NOT SUPPORTED) */ 1608 | /* case 18: DECPFF -- Printer feed (NOT SUPPORTED) */ 1609 | /* case 19: DECPEX -- Printer extent (NOT SUPPORTED) */ 1610 | /* case 42: DECNRCM -- National characters (NOT SUPPORTED) */ 1611 | fprintf(stderr, 1612 | "erresc: unknown private set/reset mode %d\n", 1613 | *args); 1614 | break; 1615 | } 1616 | } else { 1617 | switch(*args) { 1618 | case 0: /* Error (IGNORED) */ 1619 | break; 1620 | case 2: /* KAM -- keyboard action */ 1621 | MODBIT(term.mode, set, MODE_KBDLOCK); 1622 | break; 1623 | case 4: /* IRM -- Insertion-replacement */ 1624 | MODBIT(term.mode, set, MODE_INSERT); 1625 | break; 1626 | case 12: /* XXX: SRM -- Send/Receive */ 1627 | break; 1628 | case 20: /* LNM -- Linefeed/new line */ 1629 | MODBIT(term.mode, set, MODE_CRLF); 1630 | break; 1631 | default: 1632 | fprintf(stderr, 1633 | "erresc: unknown set/reset mode %d\n", 1634 | *args); 1635 | break; 1636 | } 1637 | } 1638 | } 1639 | } 1640 | #undef MODBIT 1641 | 1642 | 1643 | void 1644 | csihandle(void) { 1645 | switch(csiescseq.mode) { 1646 | default: 1647 | unknown: 1648 | fprintf(stderr, "erresc: unknown csi "); 1649 | csidump(); 1650 | /* die(""); */ 1651 | break; 1652 | case '@': /* ICH -- Insert blank char */ 1653 | DEFAULT(csiescseq.arg[0], 1); 1654 | tinsertblank(csiescseq.arg[0]); 1655 | break; 1656 | case 'A': /* CUU -- Cursor Up */ 1657 | case 'e': 1658 | DEFAULT(csiescseq.arg[0], 1); 1659 | tmoveto(term.c.x, term.c.y-csiescseq.arg[0]); 1660 | break; 1661 | case 'B': /* CUD -- Cursor Down */ 1662 | DEFAULT(csiescseq.arg[0], 1); 1663 | tmoveto(term.c.x, term.c.y+csiescseq.arg[0]); 1664 | break; 1665 | case 'c': /* DA -- Device Attributes */ 1666 | if(csiescseq.arg[0] == 0) 1667 | ttywrite(VT102ID, sizeof(VT102ID) - 1); 1668 | break; 1669 | case 'C': /* CUF -- Cursor Forward */ 1670 | case 'a': 1671 | DEFAULT(csiescseq.arg[0], 1); 1672 | tmoveto(term.c.x+csiescseq.arg[0], term.c.y); 1673 | break; 1674 | case 'D': /* CUB -- Cursor Backward */ 1675 | DEFAULT(csiescseq.arg[0], 1); 1676 | tmoveto(term.c.x-csiescseq.arg[0], term.c.y); 1677 | break; 1678 | case 'E': /* CNL -- Cursor Down and first col */ 1679 | DEFAULT(csiescseq.arg[0], 1); 1680 | tmoveto(0, term.c.y+csiescseq.arg[0]); 1681 | break; 1682 | case 'F': /* CPL -- Cursor Up and first col */ 1683 | DEFAULT(csiescseq.arg[0], 1); 1684 | tmoveto(0, term.c.y-csiescseq.arg[0]); 1685 | break; 1686 | case 'g': /* TBC -- Tabulation clear */ 1687 | switch (csiescseq.arg[0]) { 1688 | case 0: /* clear current tab stop */ 1689 | term.tabs[term.c.x] = 0; 1690 | break; 1691 | case 3: /* clear all the tabs */ 1692 | memset(term.tabs, 0, term.col * sizeof(*term.tabs)); 1693 | break; 1694 | default: 1695 | goto unknown; 1696 | } 1697 | break; 1698 | case 'G': /* CHA -- Move to */ 1699 | case '`': /* HPA */ 1700 | DEFAULT(csiescseq.arg[0], 1); 1701 | tmoveto(csiescseq.arg[0]-1, term.c.y); 1702 | break; 1703 | case 'H': /* CUP -- Move to */ 1704 | case 'f': /* HVP */ 1705 | DEFAULT(csiescseq.arg[0], 1); 1706 | DEFAULT(csiescseq.arg[1], 1); 1707 | tmoveto(csiescseq.arg[1]-1, csiescseq.arg[0]-1); 1708 | break; 1709 | case 'I': /* CHT -- Cursor Forward Tabulation tab stops */ 1710 | DEFAULT(csiescseq.arg[0], 1); 1711 | while(csiescseq.arg[0]--) 1712 | tputtab(1); 1713 | break; 1714 | case 'J': /* ED -- Clear screen */ 1715 | sel.bx = -1; 1716 | switch(csiescseq.arg[0]) { 1717 | case 0: /* below */ 1718 | tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); 1719 | if(term.c.y < term.row-1) 1720 | tclearregion(0, term.c.y+1, term.col-1, term.row-1); 1721 | break; 1722 | case 1: /* above */ 1723 | if(term.c.y > 1) 1724 | tclearregion(0, 0, term.col-1, term.c.y-1); 1725 | tclearregion(0, term.c.y, term.c.x, term.c.y); 1726 | break; 1727 | case 2: /* all */ 1728 | tclearregion(0, 0, term.col-1, term.row-1); 1729 | break; 1730 | default: 1731 | goto unknown; 1732 | } 1733 | break; 1734 | case 'K': /* EL -- Clear line */ 1735 | switch(csiescseq.arg[0]) { 1736 | case 0: /* right */ 1737 | tclearregion(term.c.x, term.c.y, term.col-1, term.c.y); 1738 | break; 1739 | case 1: /* left */ 1740 | tclearregion(0, term.c.y, term.c.x, term.c.y); 1741 | break; 1742 | case 2: /* all */ 1743 | tclearregion(0, term.c.y, term.col-1, term.c.y); 1744 | break; 1745 | } 1746 | break; 1747 | case 'S': /* SU -- Scroll line up */ 1748 | DEFAULT(csiescseq.arg[0], 1); 1749 | tscrollup(term.top, csiescseq.arg[0]); 1750 | break; 1751 | case 'T': /* SD -- Scroll line down */ 1752 | DEFAULT(csiescseq.arg[0], 1); 1753 | tscrolldown(term.top, csiescseq.arg[0]); 1754 | break; 1755 | case 'L': /* IL -- Insert blank lines */ 1756 | DEFAULT(csiescseq.arg[0], 1); 1757 | tinsertblankline(csiescseq.arg[0]); 1758 | break; 1759 | case 'l': /* RM -- Reset Mode */ 1760 | tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg); 1761 | break; 1762 | case 'M': /* DL -- Delete lines */ 1763 | DEFAULT(csiescseq.arg[0], 1); 1764 | tdeleteline(csiescseq.arg[0]); 1765 | break; 1766 | case 'X': /* ECH -- Erase char */ 1767 | DEFAULT(csiescseq.arg[0], 1); 1768 | tclearregion(term.c.x, term.c.y, term.c.x + csiescseq.arg[0], term.c.y); 1769 | break; 1770 | case 'P': /* DCH -- Delete char */ 1771 | DEFAULT(csiescseq.arg[0], 1); 1772 | tdeletechar(csiescseq.arg[0]); 1773 | break; 1774 | case 'Z': /* CBT -- Cursor Backward Tabulation tab stops */ 1775 | DEFAULT(csiescseq.arg[0], 1); 1776 | while(csiescseq.arg[0]--) 1777 | tputtab(0); 1778 | break; 1779 | case 'd': /* VPA -- Move to */ 1780 | DEFAULT(csiescseq.arg[0], 1); 1781 | tmoveto(term.c.x, csiescseq.arg[0]-1); 1782 | break; 1783 | case 'h': /* SM -- Set terminal mode */ 1784 | tsetmode(csiescseq.priv, 1, csiescseq.arg, csiescseq.narg); 1785 | break; 1786 | case 'm': /* SGR -- Terminal attribute (color) */ 1787 | tsetattr(csiescseq.arg, csiescseq.narg); 1788 | break; 1789 | case 'r': /* DECSTBM -- Set Scrolling Region */ 1790 | if(csiescseq.priv) { 1791 | goto unknown; 1792 | } else { 1793 | DEFAULT(csiescseq.arg[0], 1); 1794 | DEFAULT(csiescseq.arg[1], term.row); 1795 | tsetscroll(csiescseq.arg[0]-1, csiescseq.arg[1]-1); 1796 | tmoveto(0, 0); 1797 | } 1798 | break; 1799 | case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ 1800 | tcursor(CURSOR_SAVE); 1801 | break; 1802 | case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ 1803 | tcursor(CURSOR_LOAD); 1804 | break; 1805 | } 1806 | } 1807 | 1808 | void 1809 | csidump(void) { 1810 | int i; 1811 | uint c; 1812 | 1813 | printf("ESC["); 1814 | for(i = 0; i < csiescseq.len; i++) { 1815 | c = csiescseq.buf[i] & 0xff; 1816 | if(isprint(c)) { 1817 | putchar(c); 1818 | } else if(c == '\n') { 1819 | printf("(\\n)"); 1820 | } else if(c == '\r') { 1821 | printf("(\\r)"); 1822 | } else if(c == 0x1b) { 1823 | printf("(\\e)"); 1824 | } else { 1825 | printf("(%02x)", c); 1826 | } 1827 | } 1828 | putchar('\n'); 1829 | } 1830 | 1831 | void 1832 | csireset(void) { 1833 | memset(&csiescseq, 0, sizeof(csiescseq)); 1834 | } 1835 | 1836 | void 1837 | strhandle(void) { 1838 | char *p; 1839 | 1840 | /* 1841 | * TODO: make this being useful in case of color palette change. 1842 | */ 1843 | strparse(); 1844 | 1845 | p = strescseq.buf; 1846 | 1847 | switch(strescseq.type) { 1848 | case ']': /* OSC -- Operating System Command */ 1849 | switch(p[0]) { 1850 | case '0': 1851 | case '1': 1852 | case '2': 1853 | /* 1854 | * TODO: Handle special chars in string, like umlauts. 1855 | */ 1856 | if(p[1] == ';') { 1857 | SDL_WM_SetCaption(strescseq.buf+2, NULL); 1858 | } 1859 | break; 1860 | case ';': 1861 | SDL_WM_SetCaption(strescseq.buf+1, NULL); 1862 | break; 1863 | case '4': /* TODO: Set color (arg0) to "rgb:%hexr/$hexg/$hexb" (arg1) */ 1864 | break; 1865 | default: 1866 | fprintf(stderr, "erresc: unknown str "); 1867 | strdump(); 1868 | break; 1869 | } 1870 | break; 1871 | case 'k': /* old title set compatibility */ 1872 | SDL_WM_SetCaption(strescseq.buf, NULL); 1873 | break; 1874 | case 'P': /* DSC -- Device Control String */ 1875 | case '_': /* APC -- Application Program Command */ 1876 | case '^': /* PM -- Privacy Message */ 1877 | default: 1878 | fprintf(stderr, "erresc: unknown str "); 1879 | strdump(); 1880 | /* die(""); */ 1881 | break; 1882 | } 1883 | } 1884 | 1885 | void 1886 | strparse(void) { 1887 | /* 1888 | * TODO: Implement parsing like for CSI when required. 1889 | * Format: ESC type cmd ';' arg0 [';' argn] ESC \ 1890 | */ 1891 | return; 1892 | } 1893 | 1894 | void 1895 | strdump(void) { 1896 | int i; 1897 | uint c; 1898 | 1899 | printf("ESC%c", strescseq.type); 1900 | for(i = 0; i < strescseq.len; i++) { 1901 | c = strescseq.buf[i] & 0xff; 1902 | if(isprint(c)) { 1903 | putchar(c); 1904 | } else if(c == '\n') { 1905 | printf("(\\n)"); 1906 | } else if(c == '\r') { 1907 | printf("(\\r)"); 1908 | } else if(c == 0x1b) { 1909 | printf("(\\e)"); 1910 | } else { 1911 | printf("(%02x)", c); 1912 | } 1913 | } 1914 | printf("ESC\\\n"); 1915 | } 1916 | 1917 | void 1918 | strreset(void) { 1919 | memset(&strescseq, 0, sizeof(strescseq)); 1920 | } 1921 | 1922 | void 1923 | tputtab(bool forward) { 1924 | uint x = term.c.x; 1925 | 1926 | if(forward) { 1927 | if(x == term.col) 1928 | return; 1929 | for(++x; x < term.col && !term.tabs[x]; ++x) 1930 | /* nothing */ ; 1931 | } else { 1932 | if(x == 0) 1933 | return; 1934 | for(--x; x > 0 && !term.tabs[x]; --x) 1935 | /* nothing */ ; 1936 | } 1937 | tmoveto(x, term.c.y); 1938 | } 1939 | 1940 | void 1941 | tputc(char *c, int len) { 1942 | uchar ascii = *c; 1943 | bool control = ascii < '\x20' || ascii == 0177; 1944 | 1945 | if(iofd != -1) { 1946 | if (xwrite(iofd, c, len) < 0) { 1947 | fprintf(stderr, "Error writting in %s:%s\n", 1948 | opt_io, strerror(errno)); 1949 | close(iofd); 1950 | iofd = -1; 1951 | } 1952 | } 1953 | /* 1954 | * STR sequences must be checked before of anything 1955 | * because it can use some control codes as part of the sequence 1956 | */ 1957 | if(term.esc & ESC_STR) { 1958 | switch(ascii) { 1959 | case '\033': 1960 | term.esc = ESC_START | ESC_STR_END; 1961 | break; 1962 | case '\a': /* backwards compatibility to xterm */ 1963 | term.esc = 0; 1964 | strhandle(); 1965 | break; 1966 | default: 1967 | strescseq.buf[strescseq.len++] = ascii; 1968 | if(strescseq.len+1 >= STR_BUF_SIZ) { 1969 | term.esc = 0; 1970 | strhandle(); 1971 | } 1972 | } 1973 | return; 1974 | } 1975 | /* 1976 | * Actions of control codes must be performed as soon they arrive 1977 | * because they can be embedded inside a control sequence, and 1978 | * they must not cause conflicts with sequences. 1979 | */ 1980 | if(control) { 1981 | switch(ascii) { 1982 | case '\t': /* HT */ 1983 | tputtab(1); 1984 | return; 1985 | case '\b': /* BS */ 1986 | tmoveto(term.c.x-1, term.c.y); 1987 | return; 1988 | case '\r': /* CR */ 1989 | tmoveto(0, term.c.y); 1990 | return; 1991 | case '\f': /* LF */ 1992 | case '\v': /* VT */ 1993 | case '\n': /* LF */ 1994 | /* go to first col if the mode is set */ 1995 | tnewline(IS_SET(MODE_CRLF)); 1996 | return; 1997 | case '\a': /* BEL */ 1998 | #if 0 1999 | if(!(xw.state & WIN_FOCUSED)) 2000 | xseturgency(1); 2001 | #endif 2002 | return; 2003 | case '\033': /* ESC */ 2004 | csireset(); 2005 | term.esc = ESC_START; 2006 | return; 2007 | case '\016': /* SO */ 2008 | term.c.attr.mode |= ATTR_GFX; 2009 | return; 2010 | case '\017': /* SI */ 2011 | term.c.attr.mode &= ~ATTR_GFX; 2012 | return; 2013 | case '\032': /* SUB */ 2014 | case '\030': /* CAN */ 2015 | csireset(); 2016 | return; 2017 | case '\005': /* ENQ (IGNORED) */ 2018 | case '\000': /* NUL (IGNORED) */ 2019 | case '\021': /* XON (IGNORED) */ 2020 | case '\023': /* XOFF (IGNORED) */ 2021 | case 0177: /* DEL (IGNORED) */ 2022 | return; 2023 | } 2024 | } else if(term.esc & ESC_START) { 2025 | if(term.esc & ESC_CSI) { 2026 | csiescseq.buf[csiescseq.len++] = ascii; 2027 | if(BETWEEN(ascii, 0x40, 0x7E) 2028 | || csiescseq.len >= ESC_BUF_SIZ) { 2029 | term.esc = 0; 2030 | csiparse(), csihandle(); 2031 | } 2032 | } else if(term.esc & ESC_STR_END) { 2033 | term.esc = 0; 2034 | if(ascii == '\\') 2035 | strhandle(); 2036 | } else if(term.esc & ESC_ALTCHARSET) { 2037 | switch(ascii) { 2038 | case '0': /* Line drawing set */ 2039 | term.c.attr.mode |= ATTR_GFX; 2040 | break; 2041 | case 'B': /* USASCII */ 2042 | term.c.attr.mode &= ~ATTR_GFX; 2043 | break; 2044 | case 'A': /* UK (IGNORED) */ 2045 | case '<': /* multinational charset (IGNORED) */ 2046 | case '5': /* Finnish (IGNORED) */ 2047 | case 'C': /* Finnish (IGNORED) */ 2048 | case 'K': /* German (IGNORED) */ 2049 | break; 2050 | default: 2051 | fprintf(stderr, "esc unhandled charset: ESC ( %c\n", ascii); 2052 | } 2053 | term.esc = 0; 2054 | } else if(term.esc & ESC_TEST) { 2055 | if(ascii == '8') { /* DEC screen alignment test. */ 2056 | char E[UTF_SIZ] = "E"; 2057 | int x, y; 2058 | 2059 | for(x = 0; x < term.col; ++x) { 2060 | for(y = 0; y < term.row; ++y) 2061 | tsetchar(E, &term.c.attr, x, y); 2062 | } 2063 | } 2064 | term.esc = 0; 2065 | } else { 2066 | switch(ascii) { 2067 | case '[': 2068 | term.esc |= ESC_CSI; 2069 | break; 2070 | case '#': 2071 | term.esc |= ESC_TEST; 2072 | break; 2073 | case 'P': /* DCS -- Device Control String */ 2074 | case '_': /* APC -- Application Program Command */ 2075 | case '^': /* PM -- Privacy Message */ 2076 | case ']': /* OSC -- Operating System Command */ 2077 | case 'k': /* old title set compatibility */ 2078 | strreset(); 2079 | strescseq.type = ascii; 2080 | term.esc |= ESC_STR; 2081 | break; 2082 | case '(': /* set primary charset G0 */ 2083 | term.esc |= ESC_ALTCHARSET; 2084 | break; 2085 | case ')': /* set secondary charset G1 (IGNORED) */ 2086 | case '*': /* set tertiary charset G2 (IGNORED) */ 2087 | case '+': /* set quaternary charset G3 (IGNORED) */ 2088 | term.esc = 0; 2089 | break; 2090 | case 'D': /* IND -- Linefeed */ 2091 | if(term.c.y == term.bot) { 2092 | tscrollup(term.top, 1); 2093 | } else { 2094 | tmoveto(term.c.x, term.c.y+1); 2095 | } 2096 | term.esc = 0; 2097 | break; 2098 | case 'E': /* NEL -- Next line */ 2099 | tnewline(1); /* always go to first col */ 2100 | term.esc = 0; 2101 | break; 2102 | case 'H': /* HTS -- Horizontal tab stop */ 2103 | term.tabs[term.c.x] = 1; 2104 | term.esc = 0; 2105 | break; 2106 | case 'M': /* RI -- Reverse index */ 2107 | if(term.c.y == term.top) { 2108 | tscrolldown(term.top, 1); 2109 | } else { 2110 | tmoveto(term.c.x, term.c.y-1); 2111 | } 2112 | term.esc = 0; 2113 | break; 2114 | case 'Z': /* DECID -- Identify Terminal */ 2115 | ttywrite(VT102ID, sizeof(VT102ID) - 1); 2116 | term.esc = 0; 2117 | break; 2118 | case 'c': /* RIS -- Reset to initial state */ 2119 | treset(); 2120 | term.esc = 0; 2121 | sdlresettitle(); 2122 | break; 2123 | case '=': /* DECPAM -- Application keypad */ 2124 | term.mode |= MODE_APPKEYPAD; 2125 | term.esc = 0; 2126 | break; 2127 | case '>': /* DECPNM -- Normal keypad */ 2128 | term.mode &= ~MODE_APPKEYPAD; 2129 | term.esc = 0; 2130 | break; 2131 | case '7': /* DECSC -- Save Cursor */ 2132 | tcursor(CURSOR_SAVE); 2133 | term.esc = 0; 2134 | break; 2135 | case '8': /* DECRC -- Restore Cursor */ 2136 | tcursor(CURSOR_LOAD); 2137 | term.esc = 0; 2138 | break; 2139 | case '\\': /* ST -- Stop */ 2140 | term.esc = 0; 2141 | break; 2142 | default: 2143 | fprintf(stderr, "erresc: unknown sequence ESC 0x%02X '%c'\n", 2144 | (uchar) ascii, isprint(ascii)? ascii:'.'); 2145 | term.esc = 0; 2146 | } 2147 | } 2148 | /* 2149 | * All characters which forms part of a sequence are not 2150 | * printed 2151 | */ 2152 | return; 2153 | } 2154 | /* 2155 | * Display control codes only if we are in graphic mode 2156 | */ 2157 | if(control && !(term.c.attr.mode & ATTR_GFX)) 2158 | return; 2159 | if(sel.bx != -1 && BETWEEN(term.c.y, sel.by, sel.ey)) 2160 | sel.bx = -1; 2161 | if(IS_SET(MODE_WRAP) && term.c.state & CURSOR_WRAPNEXT) 2162 | tnewline(1); /* always go to first col */ 2163 | tsetchar(c, &term.c.attr, term.c.x, term.c.y); 2164 | if(term.c.x+1 < term.col) 2165 | tmoveto(term.c.x+1, term.c.y); 2166 | else 2167 | term.c.state |= CURSOR_WRAPNEXT; 2168 | } 2169 | 2170 | int 2171 | tresize(int col, int row) { 2172 | int i, x; 2173 | int minrow = MIN(row, term.row); 2174 | int mincol = MIN(col, term.col); 2175 | int slide = term.c.y - row + 1; 2176 | bool *bp; 2177 | 2178 | if(col < 1 || row < 1) 2179 | return 0; 2180 | 2181 | /* free unneeded rows */ 2182 | i = 0; 2183 | if(slide > 0) { 2184 | /* slide screen to keep cursor where we expect it - 2185 | * tscrollup would work here, but we can optimize to 2186 | * memmove because we're freeing the earlier lines */ 2187 | for(/* i = 0 */; i < slide; i++) { 2188 | free(term.line[i]); 2189 | free(term.alt[i]); 2190 | } 2191 | memmove(term.line, term.line + slide, row * sizeof(Line)); 2192 | memmove(term.alt, term.alt + slide, row * sizeof(Line)); 2193 | } 2194 | for(i += row; i < term.row; i++) { 2195 | free(term.line[i]); 2196 | free(term.alt[i]); 2197 | } 2198 | 2199 | /* resize to new height */ 2200 | term.line = xrealloc(term.line, row * sizeof(Line)); 2201 | term.alt = xrealloc(term.alt, row * sizeof(Line)); 2202 | term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); 2203 | term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); 2204 | 2205 | /* resize each row to new width, zero-pad if needed */ 2206 | for(i = 0; i < minrow; i++) { 2207 | term.dirty[i] = 1; 2208 | term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); 2209 | term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); 2210 | for(x = mincol; x < col; x++) { 2211 | term.line[i][x].state = 0; 2212 | term.alt[i][x].state = 0; 2213 | } 2214 | } 2215 | 2216 | /* allocate any new rows */ 2217 | for(/* i == minrow */; i < row; i++) { 2218 | term.dirty[i] = 1; 2219 | term.line[i] = xcalloc(col, sizeof(Glyph)); 2220 | term.alt [i] = xcalloc(col, sizeof(Glyph)); 2221 | } 2222 | if(col > term.col) { 2223 | bp = term.tabs + term.col; 2224 | 2225 | memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); 2226 | while(--bp > term.tabs && !*bp) 2227 | /* nothing */ ; 2228 | for(bp += tabspaces; bp < term.tabs + col; bp += tabspaces) 2229 | *bp = 1; 2230 | } 2231 | /* update terminal size */ 2232 | term.col = col; 2233 | term.row = row; 2234 | /* make use of the LIMIT in tmoveto */ 2235 | tmoveto(term.c.x, term.c.y); 2236 | /* reset scrolling region */ 2237 | tsetscroll(0, row-1); 2238 | 2239 | return (slide > 0); 2240 | } 2241 | 2242 | void 2243 | xresize(int col, int row) { 2244 | xw.tw = MAX(1, 2*borderpx + col * xw.cw); 2245 | xw.th = MAX(1, 2*borderpx + row * xw.ch); 2246 | } 2247 | 2248 | void 2249 | initcolormap(void) { 2250 | int i, r, g, b; 2251 | 2252 | // TODO: allow these to override the xterm ones somehow? 2253 | memcpy(dc.colors, colormap, sizeof(dc.colors)); 2254 | 2255 | /* init colors [16-255] ; same colors as xterm */ 2256 | for(i = 16, r = 0; r < 6; r++) { 2257 | for(g = 0; g < 6; g++) { 2258 | for(b = 0; b < 6; b++) { 2259 | dc.colors[i].r = r == 0 ? 0 : 0x3737 + 0x2828 * r; 2260 | dc.colors[i].g = g == 0 ? 0 : 0x3737 + 0x2828 * g; 2261 | dc.colors[i].b = b == 0 ? 0 : 0x3737 + 0x2828 * b; 2262 | i++; 2263 | } 2264 | } 2265 | } 2266 | 2267 | for(r = 0; r < 24; r++, i++) { 2268 | b = 0x0808 + 0x0a0a * r; 2269 | dc.colors[i].r = b; 2270 | dc.colors[i].g = b; 2271 | dc.colors[i].b = b; 2272 | } 2273 | } 2274 | 2275 | void 2276 | sdltermclear(int col1, int row1, int col2, int row2) { 2277 | if(xw.win == NULL) return; 2278 | SDL_Rect r = { 2279 | borderpx + col1 * xw.cw, 2280 | borderpx + row1 * xw.ch, 2281 | (col2-col1+1) * xw.cw, 2282 | (row2-row1+1) * xw.ch 2283 | }; 2284 | SDL_Color c = dc.colors[IS_SET(MODE_REVERSE) ? defaultfg : defaultbg]; 2285 | SDL_FillRect(xw.win, &r, SDL_MapRGB(xw.win->format, c.r, c.g, c.b)); 2286 | } 2287 | 2288 | /* 2289 | * Absolute coordinates. 2290 | */ 2291 | void 2292 | xclear(int x1, int y1, int x2, int y2) { 2293 | if(xw.win == NULL) return; 2294 | SDL_Rect r = { x1, y1, x2-x1, y2-y1 }; 2295 | SDL_Color c = dc.colors[IS_SET(MODE_REVERSE) ? defaultfg : defaultbg]; 2296 | SDL_FillRect(xw.win, &r, SDL_MapRGB(xw.win->format, c.r, c.g, c.b)); 2297 | } 2298 | 2299 | void 2300 | sdlloadfonts(char *fontstr, int fontsize) { 2301 | //char *bfontstr; 2302 | 2303 | usedfont = fontstr; 2304 | usedfontsize = fontsize; 2305 | 2306 | /* XXX: Strongly assumes the original setting had a : in it! */ 2307 | /*if((bfontstr = strchr(fontstr, ':'))) { 2308 | *bfontstr = '\0'; 2309 | bfontstr++; 2310 | } else { 2311 | bfontstr = strchr(fontstr, '\0'); 2312 | bfontstr++; 2313 | }*/ 2314 | 2315 | /*if(dc.font) TTF_CloseFont(dc.font); 2316 | dc.font = TTF_OpenFont(fontstr, fontsize); 2317 | fprintf(stderr, "%s\n", fontstr);*/ 2318 | //TTF_SizeUTF8(dc.font, "O", &xw.cw, &xw.ch); 2319 | xw.cw = 6; 2320 | xw.ch = 8; 2321 | 2322 | /*if(dc.ifont) TTF_CloseFont(dc.ifont); 2323 | dc.ifont = TTF_OpenFont(fontstr, fontsize); 2324 | fprintf(stderr, "%s\n", fontstr); 2325 | TTF_SetFontStyle(dc.ifont, TTF_STYLE_ITALIC); 2326 | 2327 | if(dc.bfont) TTF_CloseFont(dc.bfont); 2328 | dc.bfont = TTF_OpenFont(bfontstr, fontsize); 2329 | fprintf(stderr, "%s\n", bfontstr); 2330 | 2331 | if(dc.ibfont) TTF_CloseFont(dc.ibfont); 2332 | dc.ibfont = TTF_OpenFont(bfontstr, fontsize); 2333 | fprintf(stderr, "%s\n", bfontstr); 2334 | TTF_SetFontStyle(dc.ibfont, TTF_STYLE_ITALIC);*/ 2335 | } 2336 | 2337 | void 2338 | xzoom(const Arg *arg) 2339 | { 2340 | sdlloadfonts(usedfont, usedfontsize + arg->i); 2341 | cresize(0, 0); 2342 | draw(); 2343 | } 2344 | 2345 | SDL_Thread *thread = NULL; 2346 | 2347 | void sdlshutdown(void) { 2348 | if(SDL_WasInit(SDL_INIT_EVERYTHING) != 0) { 2349 | fprintf(stderr, "SDL shutdown\n"); 2350 | if(thread) SDL_KillThread(thread); 2351 | if(xw.win) SDL_FreeSurface(xw.win); 2352 | #if defined(MIYOOMINI) || defined(TRIMUISMART) || defined(RG35XX) 2353 | if(screen2) SDL_FreeSurface(screen2); 2354 | #endif 2355 | xw.win = NULL; 2356 | SDL_Quit(); 2357 | } 2358 | } 2359 | 2360 | void 2361 | sdlinit(void) { 2362 | //const SDL_VideoInfo *vi; 2363 | fprintf(stderr, "SDL init\n"); 2364 | 2365 | //dc.font = dc.ifont = dc.bfont = dc.ibfont = NULL; 2366 | 2367 | if(SDL_Init(SDL_INIT_VIDEO) == -1) { 2368 | fprintf(stderr,"Unable to initialize SDL: %s\n", SDL_GetError()); 2369 | exit(EXIT_FAILURE); 2370 | } 2371 | 2372 | fprintf(stderr, "SDL font\n"); 2373 | SDL_EnableUNICODE(1); 2374 | 2375 | /*if(TTF_Init() == -1) { 2376 | printf("TTF_Init: %s\n", TTF_GetError()); 2377 | exit(EXIT_FAILURE); 2378 | } 2379 | 2380 | if(atexit(TTF_Quit)) { 2381 | fprintf(stderr,"Unable to register TTF_Quit atexit\n"); 2382 | }*/ 2383 | 2384 | //vi = SDL_GetVideoInfo(); 2385 | 2386 | /* font */ 2387 | usedfont = (opt_font == NULL)? font : opt_font; 2388 | sdlloadfonts(usedfont, fontsize); 2389 | 2390 | fprintf(stderr, "SDL font\n"); 2391 | /* colors */ 2392 | initcolormap(); 2393 | 2394 | // /* adjust fixed window geometry */ 2395 | // if(xw.isfixed) { 2396 | // if(xw.fx < 0) 2397 | // xw.fx = vi->current_w + xw.fx - xw.fw - 1; 2398 | // if(xw.fy < 0) 2399 | // xw.fy = vi->current_h + xw.fy - xw.fh - 1; 2400 | // 2401 | // xw.h = xw.fh; 2402 | // xw.w = xw.fw; 2403 | // } else { 2404 | // /* window - default size */ 2405 | // xw.h = 2*borderpx + term.row * xw.ch; 2406 | // xw.w = 2*borderpx + term.col * xw.cw; 2407 | // xw.fx = 0; 2408 | // xw.fy = 0; 2409 | // } 2410 | 2411 | xw.w = initial_width; 2412 | xw.h = initial_height; 2413 | 2414 | #ifdef RG35XX 2415 | if(!(screen = SDL_SetVideoMode(640, 480, 16, SDL_HWSURFACE))) { 2416 | #elif MIYOOMINI 2417 | if(!(screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE))) { 2418 | #elif TRIMUISMART 2419 | setenv("SDL_USE_PAN", "true", 1); // allow DOUBLEBUF 2420 | if(!(screen = SDL_SetVideoMode(240, 320, 16, SDL_HWSURFACE | SDL_DOUBLEBUF))) { // rotated LCD 2421 | #else 2422 | if(!(screen = SDL_SetVideoMode(320, 240, 16, SDL_HWSURFACE | SDL_DOUBLEBUF))) { 2423 | #endif 2424 | fprintf(stderr,"Unable to set video mode: %s\n", SDL_GetError()); 2425 | exit(EXIT_FAILURE); 2426 | } 2427 | #if defined(MIYOOMINI) || defined(TRIMUISMART) || defined(RG35XX) 2428 | xw.win = SDL_CreateRGBSurface(SDL_SWSURFACE, xw.w, xw.h, 16, 0xF800, 0x7E0, 0x1F, 0); // console screen 2429 | screen2 = SDL_CreateRGBSurface(SDL_SWSURFACE, xw.w, xw.h, 16, 0xF800, 0x7E0, 0x1F, 0); // for keyboardMix 2430 | #else 2431 | xw.win = SDL_CreateRGBSurface(SDL_SWSURFACE, xw.w, xw.h, 16, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask); 2432 | #endif 2433 | 2434 | sdlresettitle(); 2435 | // 2436 | // TODO: might need to use system threads 2437 | if(!(thread = SDL_CreateThread(ttythread, NULL))) { 2438 | fprintf(stderr, "Unable to create thread: %s\n", SDL_GetError()); 2439 | exit(EXIT_FAILURE); 2440 | } 2441 | 2442 | expose(NULL); 2443 | //vi = SDL_GetVideoInfo(); 2444 | //cresize(vi->current_w, vi->current_h); 2445 | 2446 | //SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); 2447 | SDL_EnableKeyRepeat(200, 20); 2448 | 2449 | SDL_Event event = {.type = SDL_VIDEOEXPOSE }; 2450 | SDL_PushEvent(&event); 2451 | } 2452 | 2453 | void 2454 | xdraws(char *s, Glyph base, int x, int y, int charlen, int bytelen) { 2455 | int winx = borderpx + x * xw.cw, winy = borderpx + y * xw.ch, 2456 | width = charlen * xw.cw; 2457 | //TTF_Font *font = dc.font; 2458 | SDL_Color *fg = &dc.colors[base.fg], *bg = &dc.colors[base.bg], 2459 | *temp, revfg, revbg; 2460 | 2461 | s[bytelen] = '\0'; 2462 | 2463 | if(base.mode & ATTR_BOLD) { 2464 | if(BETWEEN(base.fg, 0, 7)) { 2465 | /* basic system colors */ 2466 | fg = &dc.colors[base.fg + 8]; 2467 | } else if(BETWEEN(base.fg, 16, 195)) { 2468 | /* 256 colors */ 2469 | fg = &dc.colors[base.fg + 36]; 2470 | } else if(BETWEEN(base.fg, 232, 251)) { 2471 | /* greyscale */ 2472 | fg = &dc.colors[base.fg + 4]; 2473 | } 2474 | /* 2475 | * Those ranges will not be brightened: 2476 | * 8 - 15 – bright system colors 2477 | * 196 - 231 – highest 256 color cube 2478 | * 252 - 255 – brightest colors in greyscale 2479 | */ 2480 | //font = dc.bfont; 2481 | } 2482 | 2483 | /*if(base.mode & ATTR_ITALIC) 2484 | font = dc.ifont; 2485 | if((base.mode & ATTR_ITALIC) && (base.mode & ATTR_BOLD)) 2486 | font = dc.ibfont;*/ 2487 | 2488 | if(IS_SET(MODE_REVERSE)) { 2489 | if(fg == &dc.colors[defaultfg]) { 2490 | fg = &dc.colors[defaultbg]; 2491 | } else { 2492 | revfg.r = ~fg->r; 2493 | revfg.g = ~fg->g; 2494 | revfg.b = ~fg->b; 2495 | fg = &revfg; 2496 | } 2497 | 2498 | if(bg == &dc.colors[defaultbg]) { 2499 | bg = &dc.colors[defaultfg]; 2500 | } else { 2501 | revbg.r = ~bg->r; 2502 | revbg.g = ~bg->g; 2503 | revbg.b = ~bg->b; 2504 | bg = &revbg; 2505 | } 2506 | } 2507 | 2508 | if(base.mode & ATTR_REVERSE) 2509 | temp = fg, fg = bg, bg = temp; 2510 | 2511 | /* Intelligent cleaning up of the borders. */ 2512 | if(x == 0) { 2513 | xclear(0, (y == 0)? 0 : winy, borderpx, 2514 | winy + xw.ch + (y == term.row-1)? xw.h : 0); 2515 | } 2516 | if(x + charlen >= term.col-1) { 2517 | xclear(winx + width, (y == 0)? 0 : winy, xw.w, 2518 | (y == term.row-1)? xw.h : (winy + xw.ch)); 2519 | } 2520 | if(y == 0) 2521 | xclear(winx, 0, winx + width, borderpx); 2522 | if(y == term.row-1) 2523 | xclear(winx, winy + xw.ch, winx + width, xw.h); 2524 | 2525 | { 2526 | //SDL_Surface *text_surface; 2527 | SDL_Rect r = {winx, winy, width, xw.ch}; 2528 | 2529 | if(xw.win != NULL) 2530 | SDL_FillRect(xw.win, &r, SDL_MapRGB(xw.win->format, bg->r, bg->g, bg->b)); 2531 | //draw_keyboard(xw.win); 2532 | 2533 | /*#ifdef USE_ANTIALIASING 2534 | if(!(text_surface=TTF_RenderUTF8_Shaded(font,s,*fg, *bg))) { 2535 | #else 2536 | if(!(text_surface=TTF_RenderUTF8_Solid(font,s,*fg))) { 2537 | #endif 2538 | printf("Could not TTF_RenderUTF8_Solid: %s\n", TTF_GetError()); 2539 | exit(EXIT_FAILURE); 2540 | } else { 2541 | SDL_BlitSurface(text_surface,NULL,xw.win,&r); 2542 | SDL_FreeSurface(text_surface); 2543 | }*/ 2544 | int xs = r.x; 2545 | if(xw.win != NULL) 2546 | draw_string(xw.win, s, xs, r.y, SDL_MapRGB(xw.win->format, fg->r, fg->g, fg->b)); 2547 | 2548 | /*while(*s) { 2549 | draw_char(xw.win, *s, xs, r.y, SDL_MapRGB(xw.win->format, fg->r, fg->g, fg->b)); 2550 | xs += 6; 2551 | s++; 2552 | }*/ 2553 | 2554 | if(base.mode & ATTR_UNDERLINE) { 2555 | //r.y += TTF_FontAscent(font) + 1; 2556 | r.y += xw.ch; 2557 | r.h = 1; 2558 | if(xw.win != NULL) 2559 | SDL_FillRect(xw.win, &r, SDL_MapRGB(xw.win->format, fg->r, fg->g, fg->b)); 2560 | } 2561 | } 2562 | } 2563 | 2564 | void 2565 | xdrawcursor(void) { 2566 | static int oldx = 0, oldy = 0; 2567 | int sl; 2568 | Glyph g = {{' '}, ATTR_NULL, defaultbg, defaultcs, 0}; 2569 | 2570 | LIMIT(oldx, 0, term.col-1); 2571 | LIMIT(oldy, 0, term.row-1); 2572 | 2573 | if(term.line[term.c.y][term.c.x].state & GLYPH_SET) 2574 | memcpy(g.c, term.line[term.c.y][term.c.x].c, UTF_SIZ); 2575 | 2576 | /* remove the old cursor */ 2577 | if(term.line[oldy][oldx].state & GLYPH_SET) { 2578 | sl = utf8size(term.line[oldy][oldx].c); 2579 | xdraws(term.line[oldy][oldx].c, term.line[oldy][oldx], oldx, 2580 | oldy, 1, sl); 2581 | } else { 2582 | sdltermclear(oldx, oldy, oldx, oldy); 2583 | } 2584 | 2585 | /* draw the new one */ 2586 | if(!(term.c.state & CURSOR_HIDE)) { 2587 | if(!(xw.state & WIN_FOCUSED)) 2588 | g.bg = defaultucs; 2589 | 2590 | if(IS_SET(MODE_REVERSE)) 2591 | g.mode |= ATTR_REVERSE, g.fg = defaultcs, g.bg = defaultfg; 2592 | 2593 | sl = utf8size(g.c); 2594 | xdraws(g.c, g, term.c.x, term.c.y, 1, sl); 2595 | oldx = term.c.x, oldy = term.c.y; 2596 | } 2597 | } 2598 | 2599 | void 2600 | sdlresettitle(void) { 2601 | SDL_WM_SetCaption(opt_title ? opt_title : "st", NULL); 2602 | } 2603 | 2604 | void 2605 | redraw(void) { 2606 | struct timespec tv = {0, REDRAW_TIMEOUT * 1000}; 2607 | 2608 | tfulldirt(); 2609 | draw(); 2610 | nanosleep(&tv, NULL); 2611 | } 2612 | 2613 | void 2614 | draw(void) { 2615 | drawregion(0, 0, term.col, term.row); 2616 | xflip(); 2617 | } 2618 | 2619 | void 2620 | drawregion(int x1, int y1, int x2, int y2) { 2621 | int ic, ib, x, y, ox, sl; 2622 | Glyph base, new; 2623 | char buf[DRAW_BUF_SIZ]; 2624 | bool ena_sel = sel.bx != -1, alt = IS_SET(MODE_ALTSCREEN); 2625 | 2626 | if((sel.alt && !alt) || (!sel.alt && alt)) 2627 | ena_sel = 0; 2628 | if(!(xw.state & WIN_VISIBLE)) 2629 | return; 2630 | 2631 | for(y = y1; y < y2; y++) { 2632 | if(!term.dirty[y]) 2633 | continue; 2634 | 2635 | sdltermclear(0, y, term.col, y); 2636 | term.dirty[y] = 0; 2637 | base = term.line[y][0]; 2638 | ic = ib = ox = 0; 2639 | for(x = x1; x < x2; x++) { 2640 | new = term.line[y][x]; 2641 | if(ena_sel && *(new.c) && selected(x, y)) 2642 | new.mode ^= ATTR_REVERSE; 2643 | if(ib > 0 && (!(new.state & GLYPH_SET) 2644 | || ATTRCMP(base, new) 2645 | || ib >= DRAW_BUF_SIZ-UTF_SIZ)) { 2646 | xdraws(buf, base, ox, y, ic, ib); 2647 | ic = ib = 0; 2648 | } 2649 | if(new.state & GLYPH_SET) { 2650 | if(ib == 0) { 2651 | ox = x; 2652 | base = new; 2653 | } 2654 | sl = utf8size(new.c); 2655 | memcpy(buf+ib, new.c, sl); 2656 | ib += sl; 2657 | ++ic; 2658 | } 2659 | } 2660 | if(ib > 0) 2661 | xdraws(buf, base, ox, y, ic, ib); 2662 | } 2663 | xdrawcursor(); 2664 | } 2665 | 2666 | void activeEvent(SDL_Event *ev) { 2667 | switch(ev->active.type) { 2668 | case SDL_APPACTIVE: 2669 | visibility(ev); 2670 | if(!ev->active.gain) unmap(ev); 2671 | break; 2672 | case SDL_APPMOUSEFOCUS: 2673 | case SDL_APPINPUTFOCUS: 2674 | focus(ev); 2675 | break; 2676 | } 2677 | } 2678 | 2679 | void 2680 | expose(SDL_Event *ev) { 2681 | (void)ev; 2682 | xw.state |= WIN_VISIBLE | WIN_REDRAW; 2683 | } 2684 | 2685 | void 2686 | visibility(SDL_Event *ev) { 2687 | SDL_ActiveEvent *e = &ev->active; 2688 | 2689 | if(!e->gain) { 2690 | xw.state &= ~WIN_VISIBLE; 2691 | } else if(!(xw.state & WIN_VISIBLE)) { 2692 | /* need a full redraw for next Expose, not just a buf copy */ 2693 | xw.state |= WIN_VISIBLE | WIN_REDRAW; 2694 | } 2695 | } 2696 | 2697 | void 2698 | unmap(SDL_Event *ev) { 2699 | (void)ev; 2700 | xw.state &= ~WIN_VISIBLE; 2701 | } 2702 | 2703 | void 2704 | focus(SDL_Event *ev) { 2705 | if(ev->active.gain) { 2706 | xw.state |= WIN_FOCUSED; 2707 | #if 0 2708 | xseturgency(0); 2709 | #endif 2710 | } else { 2711 | xw.state &= ~WIN_FOCUSED; 2712 | } 2713 | } 2714 | 2715 | char* 2716 | kmap(SDLKey k, SDLMod state) { 2717 | int i; 2718 | SDLMod mask; 2719 | 2720 | for(i = 0; i < LEN(key); i++) { 2721 | mask = key[i].mask; 2722 | 2723 | if(key[i].k == k && ((state & mask) == mask 2724 | || (mask == 0 && !state))) { 2725 | return (char*)key[i].s; 2726 | } 2727 | } 2728 | return NULL; 2729 | } 2730 | 2731 | void 2732 | kpress(SDL_Event *ev) { 2733 | SDL_KeyboardEvent *e = &ev->key; 2734 | char buf[32], *customkey; 2735 | int meta, shift, i; 2736 | SDLKey ksym = e->keysym.sym; 2737 | 2738 | if (IS_SET(MODE_KBDLOCK)) 2739 | return; 2740 | 2741 | meta = e->keysym.mod & KMOD_ALT; 2742 | shift = e->keysym.mod & KMOD_SHIFT; 2743 | 2744 | /* 1. shortcuts */ 2745 | for(i = 0; i < LEN(shortcuts); i++) { 2746 | if((ksym == shortcuts[i].keysym) 2747 | && ((CLEANMASK(shortcuts[i].mod) & \ 2748 | CLEANMASK(e->keysym.mod)) == CLEANMASK(e->keysym.mod)) 2749 | && shortcuts[i].func) { 2750 | shortcuts[i].func(&(shortcuts[i].arg)); 2751 | } 2752 | } 2753 | 2754 | /* 2. custom keys from config.h */ 2755 | if((customkey = kmap(ksym, e->keysym.mod))) { 2756 | ttywrite(customkey, strlen(customkey)); 2757 | /* 2. hardcoded (overrides X lookup) */ 2758 | } else { 2759 | switch(ksym) { 2760 | case SDLK_UP: 2761 | case SDLK_DOWN: 2762 | case SDLK_LEFT: 2763 | case SDLK_RIGHT: 2764 | /* XXX: shift up/down doesn't work */ 2765 | sprintf(buf, "\033%c%c", 2766 | IS_SET(MODE_APPKEYPAD) ? 'O' : '[', 2767 | (shift ? "abcd":"ABCD")[ksym - SDLK_UP]); 2768 | ttywrite(buf, 3); 2769 | break; 2770 | case SDLK_INSERT: 2771 | if(shift) 2772 | selpaste(); 2773 | break; 2774 | case SDLK_RETURN: 2775 | if(meta) 2776 | ttywrite("\033", 1); 2777 | 2778 | if(IS_SET(MODE_CRLF)) { 2779 | ttywrite("\r\n", 2); 2780 | } else { 2781 | ttywrite("\r", 1); 2782 | } 2783 | break; 2784 | /* 3. X lookup */ 2785 | default: 2786 | if(e->keysym.unicode) { 2787 | long u = e->keysym.unicode; 2788 | int len = utf8encode(&u, buf); 2789 | if(meta && len == 1) 2790 | ttywrite("\033", 1); 2791 | ttywrite(buf, len); 2792 | } 2793 | break; 2794 | } 2795 | } 2796 | } 2797 | 2798 | void 2799 | cresize(int width, int height) 2800 | { 2801 | printf("%d %d\n", width, height); 2802 | int col, row; 2803 | 2804 | if(width != 0) 2805 | xw.w = width; 2806 | if(height != 0) 2807 | xw.h = height; 2808 | 2809 | col = (xw.w - 2*borderpx) / xw.cw; 2810 | row = (xw.h - 2*borderpx) / xw.ch; 2811 | 2812 | printf("set videomode %dx%d\n", xw.w, xw.h); 2813 | #ifdef RG35XX 2814 | if(!(screen = SDL_SetVideoMode(640, 480, 16, SDL_HWSURFACE))) { 2815 | #elif MIYOOMINI 2816 | if(!(screen = SDL_SetVideoMode(640, 480, 32, SDL_HWSURFACE))) { 2817 | #elif TRIMUISMART 2818 | if(!(screen = SDL_SetVideoMode(240, 320, 16, SDL_HWSURFACE | SDL_DOUBLEBUF))) { 2819 | #else 2820 | if(!(screen = SDL_SetVideoMode(320, 240, 16, SDL_HWSURFACE | SDL_DOUBLEBUF))) { 2821 | #endif 2822 | fprintf(stderr,"Unable to set video mode: %s\n", SDL_GetError()); 2823 | exit(EXIT_FAILURE); 2824 | } 2825 | if(xw.win) SDL_FreeSurface(xw.win); 2826 | #if defined(MIYOOMINI) || defined(TRIMUISMART) || defined(RG35XX) 2827 | xw.win = SDL_CreateRGBSurface(SDL_SWSURFACE, xw.w, xw.h, 16, 0xF800, 0x7E0, 0x1F, 0); // console screen 2828 | if(screen2) SDL_FreeSurface(screen2); 2829 | screen2 = SDL_CreateRGBSurface(SDL_SWSURFACE, xw.w, xw.h, 16, 0xF800, 0x7E0, 0x1F, 0); // for keyboardMix 2830 | #else 2831 | xw.win = SDL_CreateRGBSurface(SDL_SWSURFACE, xw.w, xw.h, 16, screen->format->Rmask, screen->format->Gmask, screen->format->Bmask, screen->format->Amask); 2832 | #endif 2833 | tresize(col, row); 2834 | xresize(col, row); 2835 | ttyresize(); 2836 | } 2837 | 2838 | void 2839 | resize(SDL_Event *e) { 2840 | if(e->resize.w == xw.w && e->resize.h == xw.h) 2841 | return; 2842 | 2843 | cresize(e->resize.w, e->resize.h); 2844 | } 2845 | 2846 | int ttythread(void *unused) { 2847 | int i; 2848 | fd_set rfd; 2849 | struct timeval drawtimeout, *tv = NULL; 2850 | SDL_Event event; 2851 | (void)unused; 2852 | 2853 | event.type = SDL_USEREVENT; 2854 | event.user.code = 0; 2855 | event.user.data1 = NULL; 2856 | event.user.data2 = NULL; 2857 | 2858 | for(i = 0;; i++) { 2859 | FD_ZERO(&rfd); 2860 | FD_SET(cmdfd, &rfd); 2861 | if(select(cmdfd+1, &rfd, NULL, NULL, tv) < 0) { 2862 | if(errno == EINTR) 2863 | continue; 2864 | die("select failed: %s\n", SERRNO); 2865 | } 2866 | 2867 | /* 2868 | * Stop after a certain number of reads so the user does not 2869 | * feel like the system is stuttering. 2870 | */ 2871 | if(i < 1000 && FD_ISSET(cmdfd, &rfd)) { 2872 | ttyread(); 2873 | 2874 | /* 2875 | * Just wait a bit so it isn't disturbing the 2876 | * user and the system is able to write something. 2877 | */ 2878 | drawtimeout.tv_sec = 0; 2879 | drawtimeout.tv_usec = 5; 2880 | tv = &drawtimeout; 2881 | continue; 2882 | } 2883 | i = 0; 2884 | tv = NULL; 2885 | 2886 | if(SDL_PushEvent(&event)) { 2887 | fputs("Warning: unable to push tty update event.\n", stderr); 2888 | } 2889 | } 2890 | 2891 | return 0; 2892 | } 2893 | 2894 | void 2895 | run(void) { 2896 | queue_t qid = queue_create(); 2897 | SDL_Event ev; 2898 | 2899 | while(SDL_WaitEvent(&ev)) { 2900 | 2901 | /* check for preload library wanting a sdl shutdown */ 2902 | if(queue_peek(qid, MSG_CLIENT)) { 2903 | message_t message; 2904 | queue_read(qid, MSG_CLIENT, &message); 2905 | fprintf(stderr, "msg: %ld %ld\n", message.type, message.data); 2906 | if(message.data == MSG_REQUEST_SHUTDOWN) { 2907 | message.type = MSG_SERVER; 2908 | message.data = MSG_SHUTDOWN; 2909 | queue_send(qid, &message); 2910 | sdlshutdown(); 2911 | while(queue_read(qid, MSG_CLIENT, &message)) { 2912 | if(message.data == MSG_REQUEST_INIT) { 2913 | sdlinit(); 2914 | break; 2915 | } 2916 | } 2917 | } 2918 | } 2919 | 2920 | if(ev.type == SDL_QUIT) break; 2921 | 2922 | if(ev.type == SDL_KEYDOWN || ev.type == SDL_KEYUP) { 2923 | if(handle_keyboard_event(&ev)) { 2924 | /*SDL_Event expose_event = { 2925 | .type = SDL_VIDEOEXPOSE 2926 | }; 2927 | SDL_PushEvent(&expose_event);*/ 2928 | } else { 2929 | if(handler[ev.type]) 2930 | (handler[ev.type])(&ev); 2931 | } 2932 | } else { 2933 | if(handler[ev.type]) 2934 | (handler[ev.type])(&ev); 2935 | } 2936 | 2937 | switch(ev.type) { 2938 | case SDL_VIDEORESIZE: 2939 | case SDL_VIDEOEXPOSE: 2940 | case SDL_USEREVENT: 2941 | draw(); 2942 | } 2943 | xflip(); 2944 | } 2945 | 2946 | SDL_KillThread(thread); 2947 | } 2948 | 2949 | int 2950 | main(int argc, char *argv[]) { 2951 | int i; 2952 | 2953 | xw.fw = xw.fh = xw.fx = xw.fy = 0; 2954 | xw.isfixed = false; 2955 | 2956 | for(i = 1; i < argc; i++) { 2957 | switch(argv[i][0] != '-' || argv[i][2] ? -1 : argv[i][1]) { 2958 | case 'c': 2959 | if(++i < argc) 2960 | opt_class = argv[i]; 2961 | break; 2962 | case 'e': 2963 | /* eat every remaining arguments */ 2964 | if(++i < argc) { 2965 | opt_cmd = &argv[i]; 2966 | show_help = 0; 2967 | } 2968 | goto run; 2969 | case 'f': 2970 | if(++i < argc) 2971 | opt_font = argv[i]; 2972 | break; 2973 | // TODO 2974 | #if 0 2975 | case 'g': 2976 | if(++i >= argc) 2977 | break; 2978 | 2979 | bitm = XParseGeometry(argv[i], &xr, &yr, &wr, &hr); 2980 | if(bitm & XValue) 2981 | xw.fx = xr; 2982 | if(bitm & YValue) 2983 | xw.fy = yr; 2984 | if(bitm & WidthValue) 2985 | xw.fw = (int)wr; 2986 | if(bitm & HeightValue) 2987 | xw.fh = (int)hr; 2988 | if(bitm & XNegative && xw.fx == 0) 2989 | xw.fx = -1; 2990 | if(bitm & XNegative && xw.fy == 0) 2991 | xw.fy = -1; 2992 | 2993 | if(xw.fh != 0 && xw.fw != 0) 2994 | xw.isfixed = True; 2995 | break; 2996 | #endif 2997 | case 'o': 2998 | if(++i < argc) 2999 | opt_io = argv[i]; 3000 | break; 3001 | case 't': 3002 | if(++i < argc) 3003 | opt_title = argv[i]; 3004 | break; 3005 | case 'q': 3006 | active = show_help = 0; 3007 | break; 3008 | case 'v': 3009 | default: 3010 | die(USAGE); 3011 | } 3012 | } 3013 | 3014 | if(atexit(sdlshutdown)) { 3015 | fprintf(stderr,"Unable to register SDL_Quit atexit\n"); 3016 | } 3017 | /* 3018 | char path[PATH_MAX]; 3019 | realpath(dirname(argv[0]), path); 3020 | snprintf(preload_libname, PATH_MAX + 17, "%s/libst-preload.so", path); */ 3021 | 3022 | run: 3023 | setlocale(LC_CTYPE, ""); 3024 | tnew((initial_width - 2) / 6, (initial_height - 2) / 8); 3025 | ttynew(); 3026 | sdlinit(); /* Must have TTY before cresize */ 3027 | init_keyboard(); 3028 | selinit(); 3029 | run(); 3030 | return 0; 3031 | } 3032 | 3033 | --------------------------------------------------------------------------------