feat: kitty images patch applied
This commit is contained in:
7
Makefile
7
Makefile
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
include config.mk
|
include config.mk
|
||||||
|
|
||||||
SRC = st.c x.c
|
SRC = st.c x.c rowcolumn_diacritics_helpers.c graphics.c
|
||||||
OBJ = $(SRC:.c=.o)
|
OBJ = $(SRC:.c=.o)
|
||||||
|
|
||||||
all: st
|
all: st
|
||||||
@@ -15,8 +15,9 @@ config.h:
|
|||||||
.c.o:
|
.c.o:
|
||||||
$(CC) $(STCFLAGS) -c $<
|
$(CC) $(STCFLAGS) -c $<
|
||||||
|
|
||||||
st.o: config.h st.h win.h
|
st.o: config.h st.h win.h graphics.h
|
||||||
x.o: arg.h config.h st.h win.h
|
x.o: arg.h config.h st.h win.h graphics.h
|
||||||
|
graphics.c: graphics.h khash.h kvec.h st.h
|
||||||
|
|
||||||
$(OBJ): config.h config.mk
|
$(OBJ): config.h config.mk
|
||||||
|
|
||||||
|
|||||||
46
config.def.h
46
config.def.h
@@ -8,6 +8,13 @@
|
|||||||
static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true";
|
static char *font = "Liberation Mono:pixelsize=12:antialias=true:autohint=true";
|
||||||
static int borderpx = 2;
|
static int borderpx = 2;
|
||||||
|
|
||||||
|
/* How to align the content in the window when the size of the terminal
|
||||||
|
* doesn't perfectly match the size of the window. The values are percentages.
|
||||||
|
* 50 means center, 0 means flush left/top, 100 means flush right/bottom.
|
||||||
|
*/
|
||||||
|
static int anysize_halign = 50;
|
||||||
|
static int anysize_valign = 50;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* What program is execed by st depends of these precedence rules:
|
* What program is execed by st depends of these precedence rules:
|
||||||
* 1: program passed with -e
|
* 1: program passed with -e
|
||||||
@@ -23,7 +30,8 @@ char *scroll = NULL;
|
|||||||
char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400";
|
char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400";
|
||||||
|
|
||||||
/* identification sequence returned in DA and DECID */
|
/* identification sequence returned in DA and DECID */
|
||||||
char *vtiden = "\033[?6c";
|
/* By default, use the same one as kitty. */
|
||||||
|
char *vtiden = "\033[?62c";
|
||||||
|
|
||||||
/* Kerning / character bounding-box multipliers */
|
/* Kerning / character bounding-box multipliers */
|
||||||
static float cwscale = 1.0;
|
static float cwscale = 1.0;
|
||||||
@@ -163,6 +171,28 @@ static unsigned int mousebg = 0;
|
|||||||
*/
|
*/
|
||||||
static unsigned int defaultattr = 11;
|
static unsigned int defaultattr = 11;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Graphics configuration
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// The template for the cache directory.
|
||||||
|
const char graphics_cache_dir_template[] = "/tmp/st-images-XXXXXX";
|
||||||
|
/// The max size of a single image file, in bytes.
|
||||||
|
unsigned graphics_max_single_image_file_size = 20 * 1024 * 1024;
|
||||||
|
/// The max size of the cache, in bytes.
|
||||||
|
unsigned graphics_total_file_cache_size = 300 * 1024 * 1024;
|
||||||
|
/// The max ram size of an image or placement, in bytes.
|
||||||
|
unsigned graphics_max_single_image_ram_size = 100 * 1024 * 1024;
|
||||||
|
/// The max total size of all images loaded into RAM.
|
||||||
|
unsigned graphics_max_total_ram_size = 300 * 1024 * 1024;
|
||||||
|
/// The max total number of image placements and images.
|
||||||
|
unsigned graphics_max_total_placements = 4096;
|
||||||
|
/// The ratio by which limits can be exceeded. This is to reduce the frequency
|
||||||
|
/// of image removal.
|
||||||
|
double graphics_excess_tolerance_ratio = 0.05;
|
||||||
|
/// The minimum delay between redraws caused by animations, in milliseconds.
|
||||||
|
unsigned graphics_animation_min_delay = 20;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set).
|
* Force mouse select/shortcuts while mask is active (when MODE_MOUSE is set).
|
||||||
* Note that if you want to use ShiftMask with selmasks, set this to an other
|
* Note that if you want to use ShiftMask with selmasks, set this to an other
|
||||||
@@ -170,12 +200,18 @@ static unsigned int defaultattr = 11;
|
|||||||
*/
|
*/
|
||||||
static uint forcemousemod = ShiftMask;
|
static uint forcemousemod = ShiftMask;
|
||||||
|
|
||||||
|
/* Internal keyboard shortcuts. */
|
||||||
|
#define MODKEY Mod1Mask
|
||||||
|
#define TERMMOD (ControlMask|ShiftMask)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Internal mouse shortcuts.
|
* Internal mouse shortcuts.
|
||||||
* Beware that overloading Button1 will disable the selection.
|
* Beware that overloading Button1 will disable the selection.
|
||||||
*/
|
*/
|
||||||
static MouseShortcut mshortcuts[] = {
|
static MouseShortcut mshortcuts[] = {
|
||||||
/* mask button function argument release */
|
/* mask button function argument release */
|
||||||
|
{ TERMMOD, Button3, previewimage, {.s = "feh"} },
|
||||||
|
{ TERMMOD, Button2, showimageinfo, {}, 1 },
|
||||||
{ XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 },
|
{ XK_ANY_MOD, Button2, selpaste, {.i = 0}, 1 },
|
||||||
{ ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} },
|
{ ShiftMask, Button4, ttysend, {.s = "\033[5;2~"} },
|
||||||
{ XK_ANY_MOD, Button4, ttysend, {.s = "\031"} },
|
{ XK_ANY_MOD, Button4, ttysend, {.s = "\031"} },
|
||||||
@@ -183,10 +219,6 @@ static MouseShortcut mshortcuts[] = {
|
|||||||
{ XK_ANY_MOD, Button5, ttysend, {.s = "\005"} },
|
{ XK_ANY_MOD, Button5, ttysend, {.s = "\005"} },
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Internal keyboard shortcuts. */
|
|
||||||
#define MODKEY Mod1Mask
|
|
||||||
#define TERMMOD (ControlMask|ShiftMask)
|
|
||||||
|
|
||||||
static Shortcut shortcuts[] = {
|
static Shortcut shortcuts[] = {
|
||||||
/* mask keysym function argument */
|
/* mask keysym function argument */
|
||||||
{ XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} },
|
{ XK_ANY_MOD, XK_Break, sendbreak, {.i = 0} },
|
||||||
@@ -201,6 +233,10 @@ static Shortcut shortcuts[] = {
|
|||||||
{ TERMMOD, XK_Y, selpaste, {.i = 0} },
|
{ TERMMOD, XK_Y, selpaste, {.i = 0} },
|
||||||
{ ShiftMask, XK_Insert, selpaste, {.i = 0} },
|
{ ShiftMask, XK_Insert, selpaste, {.i = 0} },
|
||||||
{ TERMMOD, XK_Num_Lock, numlock, {.i = 0} },
|
{ TERMMOD, XK_Num_Lock, numlock, {.i = 0} },
|
||||||
|
{ TERMMOD, XK_F1, togglegrdebug, {.i = 0} },
|
||||||
|
{ TERMMOD, XK_F6, dumpgrstate, {.i = 0} },
|
||||||
|
{ TERMMOD, XK_F7, unloadimages, {.i = 0} },
|
||||||
|
{ TERMMOD, XK_F8, toggleimages, {.i = 0} },
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -14,9 +14,12 @@ PKG_CONFIG = pkg-config
|
|||||||
|
|
||||||
# includes and libs
|
# includes and libs
|
||||||
INCS = -I$(X11INC) \
|
INCS = -I$(X11INC) \
|
||||||
|
`$(PKG_CONFIG) --cflags imlib2` \
|
||||||
`$(PKG_CONFIG) --cflags fontconfig` \
|
`$(PKG_CONFIG) --cflags fontconfig` \
|
||||||
`$(PKG_CONFIG) --cflags freetype2`
|
`$(PKG_CONFIG) --cflags freetype2`
|
||||||
LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft \
|
LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft -lXrender \
|
||||||
|
`$(PKG_CONFIG) --libs imlib2` \
|
||||||
|
`$(PKG_CONFIG) --libs zlib` \
|
||||||
`$(PKG_CONFIG) --libs fontconfig` \
|
`$(PKG_CONFIG) --libs fontconfig` \
|
||||||
`$(PKG_CONFIG) --libs freetype2`
|
`$(PKG_CONFIG) --libs freetype2`
|
||||||
|
|
||||||
|
|||||||
4393
graphics.c
Normal file
4393
graphics.c
Normal file
File diff suppressed because it is too large
Load Diff
112
graphics.h
Normal file
112
graphics.h
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
|
||||||
|
/// Initialize the graphics module.
|
||||||
|
void gr_init(Display *disp, Visual *vis, Colormap cm);
|
||||||
|
/// Deinitialize the graphics module.
|
||||||
|
void gr_deinit();
|
||||||
|
|
||||||
|
/// Add an image rectangle to a list if rectangles to draw. This function may
|
||||||
|
/// actually draw some rectangles, or it may wait till more rectangles are
|
||||||
|
/// appended. Must be called between `gr_start_drawing` and `gr_finish_drawing`.
|
||||||
|
/// - `img_start_col..img_end_col` and `img_start_row..img_end_row` define the
|
||||||
|
/// part of the image to draw (row/col indices are zero-based, ends are
|
||||||
|
/// excluded).
|
||||||
|
/// - `x_col` and `y_row` are the coordinates of the top-left corner of the
|
||||||
|
/// image in the terminal grid.
|
||||||
|
/// - `x_pix` and `y_pix` are the same but in pixels.
|
||||||
|
/// - `reverse` indicates whether colors should be inverted.
|
||||||
|
void gr_append_imagerect(Drawable buf, uint32_t image_id, uint32_t placement_id,
|
||||||
|
int img_start_col, int img_end_col, int img_start_row,
|
||||||
|
int img_end_row, int x_col, int y_row, int x_pix,
|
||||||
|
int y_pix, int cw, int ch, int reverse);
|
||||||
|
/// Prepare for image drawing. `cw` and `ch` are dimensions of the cell.
|
||||||
|
void gr_start_drawing(Drawable buf, int cw, int ch);
|
||||||
|
/// Finish image drawing. This functions will draw all the rectangles left to
|
||||||
|
/// draw.
|
||||||
|
void gr_finish_drawing(Drawable buf);
|
||||||
|
/// Mark rows containing animations as dirty if it's time to redraw them. Must
|
||||||
|
/// be called right after `gr_start_drawing`.
|
||||||
|
void gr_mark_dirty_animations(int *dirty, int rows);
|
||||||
|
|
||||||
|
/// Parse and execute a graphics command. `buf` must start with 'G' and contain
|
||||||
|
/// at least `len + 1` characters (including '\0'). Returns 1 on success.
|
||||||
|
/// Additional informations is returned through `graphics_command_result`.
|
||||||
|
int gr_parse_command(char *buf, size_t len);
|
||||||
|
|
||||||
|
/// Executes `command` with the name of the file corresponding to `image_id` as
|
||||||
|
/// the argument. Executes xmessage with an error message on failure.
|
||||||
|
void gr_preview_image(uint32_t image_id, const char *command);
|
||||||
|
|
||||||
|
/// Executes `<st> -e less <file>` where <file> is the name of a temporary file
|
||||||
|
/// containing the information about an image and placement, and <st> is
|
||||||
|
/// specified with `st_executable`.
|
||||||
|
void gr_show_image_info(uint32_t image_id, uint32_t placement_id,
|
||||||
|
uint32_t imgcol, uint32_t imgrow,
|
||||||
|
char is_classic_placeholder, int32_t diacritic_count,
|
||||||
|
char *st_executable);
|
||||||
|
|
||||||
|
/// Dumps the internal state (images and placements) to stderr.
|
||||||
|
void gr_dump_state();
|
||||||
|
|
||||||
|
/// Unloads images to reduce RAM usage.
|
||||||
|
void gr_unload_images_to_reduce_ram();
|
||||||
|
|
||||||
|
/// Executes `callback` for each image cell. The callback should return 1 if it
|
||||||
|
/// changed the glyph. This function is implemented in `st.c`.
|
||||||
|
void gr_for_each_image_cell(int (*callback)(void *data, Glyph *gp),
|
||||||
|
void *data);
|
||||||
|
|
||||||
|
/// Marks all the rows containing the image with `image_id` as dirty.
|
||||||
|
void gr_schedule_image_redraw_by_id(uint32_t image_id);
|
||||||
|
|
||||||
|
/// Returns a pointer to the glyph under the classic placement with `image_id`
|
||||||
|
/// and `placement_id` at `col` and `row` (1-based). May return NULL if the
|
||||||
|
/// underneath text is unknown.
|
||||||
|
Glyph *gr_get_glyph_underneath_image(uint32_t image_id, uint32_t placement_id,
|
||||||
|
int col, int row);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GRAPHICS_DEBUG_NONE = 0,
|
||||||
|
GRAPHICS_DEBUG_LOG = 1,
|
||||||
|
GRAPHICS_DEBUG_LOG_AND_BOXES = 2,
|
||||||
|
} GraphicsDebugMode;
|
||||||
|
|
||||||
|
/// Print additional information, draw bounding bounding boxes, etc.
|
||||||
|
extern GraphicsDebugMode graphics_debug_mode;
|
||||||
|
|
||||||
|
/// Whether to display images or just draw bounding boxes.
|
||||||
|
extern char graphics_display_images;
|
||||||
|
|
||||||
|
/// The time in milliseconds until the next redraw to update animations.
|
||||||
|
/// INT_MAX means no redraw is needed. Populated by `gr_finish_drawing`.
|
||||||
|
extern int graphics_next_redraw_delay;
|
||||||
|
|
||||||
|
#define MAX_GRAPHICS_RESPONSE_LEN 256
|
||||||
|
|
||||||
|
/// A structure representing the result of a graphics command.
|
||||||
|
typedef struct {
|
||||||
|
/// Indicates if the terminal needs to be redrawn.
|
||||||
|
char redraw;
|
||||||
|
/// The response of the command that should be sent back to the client
|
||||||
|
/// (may be empty if the quiet flag is set).
|
||||||
|
char response[MAX_GRAPHICS_RESPONSE_LEN];
|
||||||
|
/// Whether there was an error executing this command (not very useful,
|
||||||
|
/// the response must be sent back anyway).
|
||||||
|
char error;
|
||||||
|
/// Whether the terminal has to create a placeholder for a non-virtual
|
||||||
|
/// placement.
|
||||||
|
char create_placeholder;
|
||||||
|
/// The placeholder that needs to be created.
|
||||||
|
struct {
|
||||||
|
uint32_t rows, columns;
|
||||||
|
uint32_t image_id, placement_id;
|
||||||
|
char do_not_move_cursor;
|
||||||
|
Glyph *text_underneath;
|
||||||
|
} placeholder;
|
||||||
|
} GraphicsCommandResult;
|
||||||
|
|
||||||
|
/// The result of a graphics command.
|
||||||
|
extern GraphicsCommandResult graphics_command_result;
|
||||||
875
icat-mini.sh
Executable file
875
icat-mini.sh
Executable file
@@ -0,0 +1,875 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# vim: shiftwidth=4
|
||||||
|
|
||||||
|
script_name="$(basename "$0")"
|
||||||
|
|
||||||
|
short_help="Usage: $script_name [OPTIONS] <image_file>
|
||||||
|
|
||||||
|
This is a script to display images in the terminal using the kitty graphics
|
||||||
|
protocol with Unicode placeholders. It is very basic, please use something else
|
||||||
|
if you have alternatives.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-h Show this help.
|
||||||
|
-s SCALE The scale of the image, may be floating point.
|
||||||
|
-c N, --cols N The number of columns.
|
||||||
|
-r N, --rows N The number of rows.
|
||||||
|
--max-cols N The maximum number of columns.
|
||||||
|
--max-rows N The maximum number of rows.
|
||||||
|
--cell-size WxH The cell size in pixels.
|
||||||
|
-m METHOD The uploading method, may be 'file', 'direct' or 'auto'.
|
||||||
|
--speed SPEED The multiplier for the animation speed (float).
|
||||||
|
"
|
||||||
|
|
||||||
|
# Exit the script on keyboard interrupt
|
||||||
|
trap "echo 'icat-mini was interrupted' >&2; exit 1" INT
|
||||||
|
|
||||||
|
cols=""
|
||||||
|
rows=""
|
||||||
|
file=""
|
||||||
|
command_tty=""
|
||||||
|
response_tty=""
|
||||||
|
uploading_method="auto"
|
||||||
|
cell_size=""
|
||||||
|
scale=1
|
||||||
|
max_cols=""
|
||||||
|
max_rows=""
|
||||||
|
speed=""
|
||||||
|
|
||||||
|
# Parse the command line.
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
-c|--columns|--cols)
|
||||||
|
cols="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-r|--rows|-l|--lines)
|
||||||
|
rows="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-s|--scale)
|
||||||
|
scale="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
echo "$short_help"
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
-m|--upload-method|--uploading-method)
|
||||||
|
uploading_method="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--cell-size)
|
||||||
|
cell_size="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--max-cols)
|
||||||
|
max_cols="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--max-rows)
|
||||||
|
max_rows="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--speed)
|
||||||
|
speed="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
file="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-*)
|
||||||
|
echo "Unknown option: $1" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [ -n "$file" ]; then
|
||||||
|
echo "Multiple image files are not supported: $file and $1" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
file="$1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
file="$(realpath "$file")"
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
# Detect imagemagick
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
# If there is the 'magick' command, use it instead of separate 'convert' and
|
||||||
|
# 'identify' commands.
|
||||||
|
if command -v magick > /dev/null; then
|
||||||
|
identify="magick identify"
|
||||||
|
convert="magick"
|
||||||
|
else
|
||||||
|
identify="identify"
|
||||||
|
convert="convert"
|
||||||
|
fi
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
# Detect tmux
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
# Check if we are inside tmux.
|
||||||
|
inside_tmux=""
|
||||||
|
if [ -n "$TMUX" ]; then
|
||||||
|
inside_tmux=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$command_tty" ] && [ -n "$inside_tmux" ]; then
|
||||||
|
# Get the pty of the current tmux pane.
|
||||||
|
command_tty="$(tmux display-message -t "$TMUX_PANE" -p "#{pane_tty}")"
|
||||||
|
if [ ! -e "$command_tty" ]; then
|
||||||
|
command_tty=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
# Adjust the terminal state
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
if [ -z "$command_tty" ]; then
|
||||||
|
command_tty="/dev/tty"
|
||||||
|
fi
|
||||||
|
if [ -z "$response_tty" ]; then
|
||||||
|
response_tty="/dev/tty"
|
||||||
|
fi
|
||||||
|
|
||||||
|
stty_orig="$(stty -g < "$response_tty")"
|
||||||
|
stty -echo < "$response_tty"
|
||||||
|
# Disable ctrl-z. Pressing ctrl-z during image uploading may cause some
|
||||||
|
# horrible issues otherwise.
|
||||||
|
stty susp undef < "$response_tty"
|
||||||
|
stty -icanon < "$response_tty"
|
||||||
|
|
||||||
|
restore_echo() {
|
||||||
|
[ -n "$stty_orig" ] || return
|
||||||
|
stty $stty_orig < "$response_tty"
|
||||||
|
}
|
||||||
|
|
||||||
|
trap restore_echo EXIT TERM
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
# Compute the number of rows and columns
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
is_pos_int() {
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
return 1 # false
|
||||||
|
fi
|
||||||
|
if [ -z "$(printf '%s' "$1" | tr -d '[:digit:]')" ]; then
|
||||||
|
if [ "$1" -gt 0 ]; then
|
||||||
|
return 0 # true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 1 # false
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -n "$cols" ] || [ -n "$rows" ]; then
|
||||||
|
if [ -n "$max_cols" ] || [ -n "$max_rows" ]; then
|
||||||
|
echo "You can't specify both max-cols/rows and cols/rows" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the max number of cols and rows.
|
||||||
|
[ -n "$max_cols" ] || max_cols="$(tput cols)"
|
||||||
|
[ -n "$max_rows" ] || max_rows="$(tput lines)"
|
||||||
|
if [ "$max_rows" -gt 255 ]; then
|
||||||
|
max_rows=255
|
||||||
|
fi
|
||||||
|
|
||||||
|
python_ioctl_command="import array, fcntl, termios
|
||||||
|
buf = array.array('H', [0, 0, 0, 0])
|
||||||
|
fcntl.ioctl(0, termios.TIOCGWINSZ, buf)
|
||||||
|
print(int(buf[2]/buf[1]), int(buf[3]/buf[0]))"
|
||||||
|
|
||||||
|
# Get the cell size in pixels if either cols or rows are not specified.
|
||||||
|
if [ -z "$cols" ] || [ -z "$rows" ]; then
|
||||||
|
cell_width=""
|
||||||
|
cell_height=""
|
||||||
|
# If the cell size is specified, use it.
|
||||||
|
if [ -n "$cell_size" ]; then
|
||||||
|
cell_width="${cell_size%x*}"
|
||||||
|
cell_height="${cell_size#*x}"
|
||||||
|
if ! is_pos_int "$cell_height" || ! is_pos_int "$cell_width"; then
|
||||||
|
echo "Invalid cell size: $cell_size" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# Otherwise try to use TIOCGWINSZ ioctl via python.
|
||||||
|
if [ -z "$cell_width" ] || [ -z "$cell_height" ]; then
|
||||||
|
cell_size_ioctl="$(python3 -c "$python_ioctl_command" < "$command_tty" 2> /dev/null)"
|
||||||
|
cell_width="${cell_size_ioctl% *}"
|
||||||
|
cell_height="${cell_size_ioctl#* }"
|
||||||
|
if ! is_pos_int "$cell_height" || ! is_pos_int "$cell_width"; then
|
||||||
|
cell_width=""
|
||||||
|
cell_height=""
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# If it didn't work, try to use csi XTWINOPS.
|
||||||
|
if [ -z "$cell_width" ] || [ -z "$cell_height" ]; then
|
||||||
|
if [ -n "$inside_tmux" ]; then
|
||||||
|
printf '\ePtmux;\e\e[16t\e\\' >> "$command_tty"
|
||||||
|
else
|
||||||
|
printf '\e[16t' >> "$command_tty"
|
||||||
|
fi
|
||||||
|
# The expected response will look like ^[[6;<height>;<width>t
|
||||||
|
term_response=""
|
||||||
|
while true; do
|
||||||
|
char=$(dd bs=1 count=1 <"$response_tty" 2>/dev/null)
|
||||||
|
if [ "$char" = "t" ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
term_response="$term_response$char"
|
||||||
|
done
|
||||||
|
cell_height="$(printf '%s' "$term_response" | cut -d ';' -f 2)"
|
||||||
|
cell_width="$(printf '%s' "$term_response" | cut -d ';' -f 3)"
|
||||||
|
if ! is_pos_int "$cell_height" || ! is_pos_int "$cell_width"; then
|
||||||
|
cell_width=8
|
||||||
|
cell_height=16
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Compute a formula with bc and round to the nearest integer.
|
||||||
|
bc_round() {
|
||||||
|
LC_NUMERIC=C printf '%.0f' "$(printf '%s\n' "scale=2;($1) + 0.5" | bc)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Compute the number of rows and columns of the image.
|
||||||
|
if [ -z "$cols" ] || [ -z "$rows" ]; then
|
||||||
|
# Get the size of the image and its resolution. If it's an animation, use
|
||||||
|
# the first frame.
|
||||||
|
format_output="$($identify -format '%w %h\n' "$file" | head -1)"
|
||||||
|
img_width="$(printf '%s' "$format_output" | cut -d ' ' -f 1)"
|
||||||
|
img_height="$(printf '%s' "$format_output" | cut -d ' ' -f 2)"
|
||||||
|
if ! is_pos_int "$img_width" || ! is_pos_int "$img_height"; then
|
||||||
|
echo "Couldn't get image size from identify: $format_output" >&2
|
||||||
|
echo >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
opt_cols_expr="(${scale}*${img_width}/${cell_width})"
|
||||||
|
opt_rows_expr="(${scale}*${img_height}/${cell_height})"
|
||||||
|
if [ -z "$cols" ] && [ -z "$rows" ]; then
|
||||||
|
# If columns and rows are not specified, compute the optimal values
|
||||||
|
# using the information about rows and columns per inch.
|
||||||
|
cols="$(bc_round "$opt_cols_expr")"
|
||||||
|
rows="$(bc_round "$opt_rows_expr")"
|
||||||
|
# Make sure that automatically computed rows and columns are within some
|
||||||
|
# sane limits
|
||||||
|
if [ "$cols" -gt "$max_cols" ]; then
|
||||||
|
rows="$(bc_round "$rows * $max_cols / $cols")"
|
||||||
|
cols="$max_cols"
|
||||||
|
fi
|
||||||
|
if [ "$rows" -gt "$max_rows" ]; then
|
||||||
|
cols="$(bc_round "$cols * $max_rows / $rows")"
|
||||||
|
rows="$max_rows"
|
||||||
|
fi
|
||||||
|
elif [ -z "$cols" ]; then
|
||||||
|
# If only one dimension is specified, compute the other one to match the
|
||||||
|
# aspect ratio as close as possible.
|
||||||
|
cols="$(bc_round "${opt_cols_expr}*${rows}/${opt_rows_expr}")"
|
||||||
|
elif [ -z "$rows" ]; then
|
||||||
|
rows="$(bc_round "${opt_rows_expr}*${cols}/${opt_cols_expr}")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$cols" -lt 1 ]; then
|
||||||
|
cols=1
|
||||||
|
fi
|
||||||
|
if [ "$rows" -lt 1 ]; then
|
||||||
|
rows=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
# Generate an image id
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
image_id=""
|
||||||
|
while [ -z "$image_id" ]; do
|
||||||
|
image_id="$(shuf -i 16777217-4294967295 -n 1)"
|
||||||
|
# Check that the id requires 24-bit fg colors.
|
||||||
|
if [ "$(expr \( "$image_id" / 256 \) % 65536)" -eq 0 ]; then
|
||||||
|
image_id=""
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
# Uploading the image
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
# Choose the uploading method
|
||||||
|
if [ "$uploading_method" = "auto" ]; then
|
||||||
|
if [ -n "$SSH_CLIENT" ] || [ -n "$SSH_TTY" ] || [ -n "$SSH_CONNECTION" ]; then
|
||||||
|
uploading_method="direct"
|
||||||
|
else
|
||||||
|
uploading_method="file"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Functions to emit the start and the end of a graphics command.
|
||||||
|
if [ -n "$inside_tmux" ]; then
|
||||||
|
# If we are in tmux we have to wrap the command in Ptmux.
|
||||||
|
graphics_command_start='\ePtmux;\e\e_G'
|
||||||
|
graphics_command_end='\e\e\\\e\\'
|
||||||
|
else
|
||||||
|
graphics_command_start='\e_G'
|
||||||
|
graphics_command_end='\e\\'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Send a graphics command with the correct start and end
|
||||||
|
gr_command() {
|
||||||
|
printf "${graphics_command_start}%s${graphics_command_end}" "$1" >> "$command_tty"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Compute the size of a data chunk for direct transmission.
|
||||||
|
if [ "$uploading_method" = "direct" ]; then
|
||||||
|
# Get the value of PIPE_BUF.
|
||||||
|
pipe_buf="$(getconf PIPE_BUF "$command_tty" 2> /dev/null)"
|
||||||
|
if is_pos_int "$pipe_buf"; then
|
||||||
|
# Make sure it's between 512 and 4096.
|
||||||
|
if [ "$(expr "$pipe_buf" \< 512)" -eq 1 ]; then
|
||||||
|
pipe_buf=512
|
||||||
|
elif [ "$(expr "$pipe_buf" \> 4096)" -eq 1 ]; then
|
||||||
|
pipe_buf=4096
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
pipe_buf=512
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The size of each graphics command shouldn't be more than PIPE_BUF, so we
|
||||||
|
# set the size of an encoded chunk to be PIPE_BUF - 128 to leave some space
|
||||||
|
# for the command.
|
||||||
|
chunk_size="$(expr "$pipe_buf" - 128)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if the image format is supported.
|
||||||
|
is_format_supported() {
|
||||||
|
arg_format="$1"
|
||||||
|
if [ "$arg_format" = "PNG" ]; then
|
||||||
|
return 0
|
||||||
|
elif [ "$arg_format" = "JPEG" ]; then
|
||||||
|
if [ -z "$inside_tmux" ]; then
|
||||||
|
actual_term="$TERM"
|
||||||
|
else
|
||||||
|
# Get the actual current terminal name from tmux.
|
||||||
|
actual_term="$(tmux display-message -p "#{client_termname}")"
|
||||||
|
fi
|
||||||
|
# st is known to support JPEG.
|
||||||
|
case "$actual_term" in
|
||||||
|
st | *-st | st-* | *-st-*)
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Send an uploading command. Usage: gr_upload <action> <command> <file>
|
||||||
|
# Where <action> is a part of command that specifies the action, it will be
|
||||||
|
# repeated for every chunk (if the method is direct), and <command> is the rest
|
||||||
|
# of the command that specifies the image parameters. <action> and <command>
|
||||||
|
# must not include the transmission method or ';'.
|
||||||
|
# Example:
|
||||||
|
# gr_upload "a=T,q=2" "U=1,i=${image_id},f=100,c=${cols},r=${rows}" "$file"
|
||||||
|
gr_upload() {
|
||||||
|
arg_action="$1"
|
||||||
|
arg_command="$2"
|
||||||
|
arg_file="$3"
|
||||||
|
if [ "$uploading_method" = "file" ]; then
|
||||||
|
# base64-encode the filename
|
||||||
|
encoded_filename=$(printf '%s' "$arg_file" | base64 -w0)
|
||||||
|
# If the file name contains 'tty-graphics-protocol', assume it's
|
||||||
|
# temporary and use t=t.
|
||||||
|
medium="t=f"
|
||||||
|
case "$arg_file" in
|
||||||
|
*tty-graphics-protocol*)
|
||||||
|
medium="t=t"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
medium="t=f"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
gr_command "${arg_action},${arg_command},${medium};${encoded_filename}"
|
||||||
|
fi
|
||||||
|
if [ "$uploading_method" = "direct" ]; then
|
||||||
|
# Create a temporary directory to store the chunked image.
|
||||||
|
chunkdir="$(mktemp -d)"
|
||||||
|
if [ ! "$chunkdir" ] || [ ! -d "$chunkdir" ]; then
|
||||||
|
echo "Can't create a temp dir" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# base64-encode the file and split it into chunks. The size of each
|
||||||
|
# graphics command shouldn't be more than 4096, so we set the size of an
|
||||||
|
# encoded chunk to be 3968, slightly less than that.
|
||||||
|
chunk_size=3968
|
||||||
|
cat "$arg_file" | base64 -w0 | split -b "$chunk_size" - "$chunkdir/chunk_"
|
||||||
|
|
||||||
|
# Issue a command indicating that we want to start data transmission for
|
||||||
|
# a new image.
|
||||||
|
gr_command "${arg_action},${arg_command},t=d,m=1"
|
||||||
|
|
||||||
|
# Transmit chunks.
|
||||||
|
for chunk in "$chunkdir/chunk_"*; do
|
||||||
|
gr_command "${arg_action},i=${image_id},m=1;$(cat "$chunk")"
|
||||||
|
rm "$chunk"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Tell the terminal that we are done.
|
||||||
|
gr_command "${arg_action},i=$image_id,m=0"
|
||||||
|
|
||||||
|
# Remove the temporary directory.
|
||||||
|
rmdir "$chunkdir"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
delayed_frame_dir_cleanup() {
|
||||||
|
arg_frame_dir="$1"
|
||||||
|
sleep 2
|
||||||
|
if [ -n "$arg_frame_dir" ]; then
|
||||||
|
for frame in "$arg_frame_dir"/frame_*.png; do
|
||||||
|
rm "$frame"
|
||||||
|
done
|
||||||
|
rmdir "$arg_frame_dir"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
upload_image_and_print_placeholder() {
|
||||||
|
# Check if the file is an animation.
|
||||||
|
format_output=$($identify -format '%n %m\n' "$file" | head -n 1)
|
||||||
|
frame_count="$(printf '%s' "$format_output" | cut -d ' ' -f 1)"
|
||||||
|
image_format="$(printf '%s' "$format_output" | cut -d ' ' -f 2)"
|
||||||
|
|
||||||
|
if [ "$frame_count" -gt 1 ]; then
|
||||||
|
# The file is an animation, decompose into frames and upload each frame.
|
||||||
|
frame_dir="$(mktemp -d)"
|
||||||
|
frame_dir="$HOME/temp/frames${frame_dir}"
|
||||||
|
mkdir -p "$frame_dir"
|
||||||
|
if [ ! "$frame_dir" ] || [ ! -d "$frame_dir" ]; then
|
||||||
|
echo "Can't create a temp dir for frames" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Decompose the animation into separate frames.
|
||||||
|
$convert "$file" -coalesce "$frame_dir/frame_%06d.png"
|
||||||
|
|
||||||
|
# Get all frame delays at once, in centiseconds, as a space-separated
|
||||||
|
# string.
|
||||||
|
delays=$($identify -format "%T " "$file")
|
||||||
|
|
||||||
|
frame_number=1
|
||||||
|
for frame in "$frame_dir"/frame_*.png; do
|
||||||
|
# Read the delay for the current frame and convert it from
|
||||||
|
# centiseconds to milliseconds.
|
||||||
|
delay=$(printf '%s' "$delays" | cut -d ' ' -f "$frame_number")
|
||||||
|
delay=$((delay * 10))
|
||||||
|
# If the delay is 0, set it to 100ms.
|
||||||
|
if [ "$delay" -eq 0 ]; then
|
||||||
|
delay=100
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$speed" ]; then
|
||||||
|
delay=$(bc_round "$delay / $speed")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$frame_number" -eq 1 ]; then
|
||||||
|
# Abort the previous transmission, just in case.
|
||||||
|
gr_command "q=2,a=t,i=${image_id},m=0"
|
||||||
|
# Upload the first frame with a=T
|
||||||
|
gr_upload "q=2,a=T" "f=100,U=1,i=${image_id},c=${cols},r=${rows}" "$frame"
|
||||||
|
# Set the delay for the first frame and also play the animation
|
||||||
|
# in loading mode (s=2).
|
||||||
|
gr_command "a=a,v=1,s=2,r=${frame_number},z=${delay},i=${image_id}"
|
||||||
|
# Print the placeholder after the first frame to reduce the wait
|
||||||
|
# time.
|
||||||
|
print_placeholder
|
||||||
|
else
|
||||||
|
# Upload subsequent frames with a=f
|
||||||
|
gr_upload "q=2,a=f" "f=100,i=${image_id},z=${delay}" "$frame"
|
||||||
|
fi
|
||||||
|
|
||||||
|
frame_number=$((frame_number + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
# Play the animation in loop mode (s=3).
|
||||||
|
gr_command "a=a,v=1,s=3,i=${image_id}"
|
||||||
|
|
||||||
|
# Remove the temporary directory, but do it in the background with a
|
||||||
|
# delay to avoid removing files before they are loaded by the terminal.
|
||||||
|
delayed_frame_dir_cleanup "$frame_dir" 2> /dev/null &
|
||||||
|
elif is_format_supported "$image_format"; then
|
||||||
|
# The file is not an animation and has a supported format, upload it
|
||||||
|
# directly.
|
||||||
|
gr_upload "q=2,a=T" "U=1,i=${image_id},f=100,c=${cols},r=${rows}" "$file"
|
||||||
|
# Print the placeholder
|
||||||
|
print_placeholder
|
||||||
|
else
|
||||||
|
# The format is not supported, try to convert it to png.
|
||||||
|
temp_file="$(mktemp --tmpdir "icat-mini-tty-graphics-protocol-XXXXX.png")"
|
||||||
|
if ! $convert "$file" "$temp_file"; then
|
||||||
|
echo "Failed to convert the image to PNG" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# Upload the converted image.
|
||||||
|
gr_upload "q=2,a=T" "U=1,i=${image_id},f=100,c=${cols},r=${rows}" "$temp_file"
|
||||||
|
# Print the placeholder
|
||||||
|
print_placeholder
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
# Printing the image placeholder
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
print_placeholder() {
|
||||||
|
# Each line starts with the escape sequence to set the foreground color to
|
||||||
|
# the image id.
|
||||||
|
blue="$(expr "$image_id" % 256 )"
|
||||||
|
green="$(expr \( "$image_id" / 256 \) % 256 )"
|
||||||
|
red="$(expr \( "$image_id" / 65536 \) % 256 )"
|
||||||
|
line_start="$(printf "\e[38;2;%d;%d;%dm" "$red" "$green" "$blue")"
|
||||||
|
line_end="$(printf "\e[39;m")"
|
||||||
|
|
||||||
|
id4th="$(expr \( "$image_id" / 16777216 \) % 256 )"
|
||||||
|
eval "id_diacritic=\$d${id4th}"
|
||||||
|
|
||||||
|
# Reset the brush state, mostly to reset the underline color.
|
||||||
|
printf "\e[0m"
|
||||||
|
|
||||||
|
# Fill the output with characters representing the image
|
||||||
|
for y in $(seq 0 "$(expr "$rows" - 1)"); do
|
||||||
|
eval "row_diacritic=\$d${y}"
|
||||||
|
line="$line_start"
|
||||||
|
for x in $(seq 0 "$(expr "$cols" - 1)"); do
|
||||||
|
eval "col_diacritic=\$d${x}"
|
||||||
|
# Note that when $x is out of bounds, the column diacritic will
|
||||||
|
# be empty, meaning that the column should be guessed by the
|
||||||
|
# terminal.
|
||||||
|
if [ "$x" -ge "$num_diacritics" ]; then
|
||||||
|
line="${line}${placeholder}${row_diacritic}"
|
||||||
|
else
|
||||||
|
line="${line}${placeholder}${row_diacritic}${col_diacritic}${id_diacritic}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
line="${line}${line_end}"
|
||||||
|
printf "%s\n" "$line"
|
||||||
|
done
|
||||||
|
|
||||||
|
printf "\e[0m"
|
||||||
|
}
|
||||||
|
|
||||||
|
d0="Ì…"
|
||||||
|
d1="Ì"
|
||||||
|
d2="ÌŽ"
|
||||||
|
d3="Ì"
|
||||||
|
d4="Ì’"
|
||||||
|
d5="̽"
|
||||||
|
d6="̾"
|
||||||
|
d7="Ì¿"
|
||||||
|
d8="͆"
|
||||||
|
d9="ÍŠ"
|
||||||
|
d10="Í‹"
|
||||||
|
d11="͌"
|
||||||
|
d12="Í"
|
||||||
|
d13="Í‘"
|
||||||
|
d14="Í’"
|
||||||
|
d15="Í—"
|
||||||
|
d16="Í›"
|
||||||
|
d17="Í£"
|
||||||
|
d18="ͤ"
|
||||||
|
d19="Í¥"
|
||||||
|
d20="ͦ"
|
||||||
|
d21="ͧ"
|
||||||
|
d22="ͨ"
|
||||||
|
d23="Í©"
|
||||||
|
d24="ͪ"
|
||||||
|
d25="Í«"
|
||||||
|
d26="ͬ"
|
||||||
|
d27="Í"
|
||||||
|
d28="Í®"
|
||||||
|
d29="ͯ"
|
||||||
|
d30="Òƒ"
|
||||||
|
d31="Ò„"
|
||||||
|
d32="Ò…"
|
||||||
|
d33="Ò†"
|
||||||
|
d34="Ò‡"
|
||||||
|
d35="Ö’"
|
||||||
|
d36="Ö“"
|
||||||
|
d37="Ö”"
|
||||||
|
d38="Ö•"
|
||||||
|
d39="Ö—"
|
||||||
|
d40="Ö˜"
|
||||||
|
d41="Ö™"
|
||||||
|
d42="֜"
|
||||||
|
d43="Ö"
|
||||||
|
d44="Öž"
|
||||||
|
d45="ÖŸ"
|
||||||
|
d46="Ö "
|
||||||
|
d47="Ö¡"
|
||||||
|
d48="Ö¨"
|
||||||
|
d49="Ö©"
|
||||||
|
d50="Ö«"
|
||||||
|
d51="Ö¬"
|
||||||
|
d52="Ö¯"
|
||||||
|
d53="ׄ"
|
||||||
|
d54="Ø"
|
||||||
|
d55="Ø‘"
|
||||||
|
d56="Ø’"
|
||||||
|
d57="Ø“"
|
||||||
|
d58="Ø”"
|
||||||
|
d59="Ø•"
|
||||||
|
d60="Ø–"
|
||||||
|
d61="Ø—"
|
||||||
|
d62="Ù—"
|
||||||
|
d63="Ù˜"
|
||||||
|
d64="Ù™"
|
||||||
|
d65="Ùš"
|
||||||
|
d66="Ù›"
|
||||||
|
d67="Ù"
|
||||||
|
d68="Ùž"
|
||||||
|
d69="Û–"
|
||||||
|
d70="Û—"
|
||||||
|
d71="Û˜"
|
||||||
|
d72="Û™"
|
||||||
|
d73="Ûš"
|
||||||
|
d74="Û›"
|
||||||
|
d75="ۜ"
|
||||||
|
d76="ÛŸ"
|
||||||
|
d77="Û "
|
||||||
|
d78="Û¡"
|
||||||
|
d79="Û¢"
|
||||||
|
d80="Û¤"
|
||||||
|
d81="Û§"
|
||||||
|
d82="Û¨"
|
||||||
|
d83="Û«"
|
||||||
|
d84="Û¬"
|
||||||
|
d85="ܰ"
|
||||||
|
d86="ܲ"
|
||||||
|
d87="ܳ"
|
||||||
|
d88="ܵ"
|
||||||
|
d89="ܶ"
|
||||||
|
d90="ܺ"
|
||||||
|
d91="ܽ"
|
||||||
|
d92="Ü¿"
|
||||||
|
d93="Ý€"
|
||||||
|
d94="Ý"
|
||||||
|
d95="݃"
|
||||||
|
d96="Ý…"
|
||||||
|
d97="݇"
|
||||||
|
d98="݉"
|
||||||
|
d99="ÝŠ"
|
||||||
|
d100="ß«"
|
||||||
|
d101="߬"
|
||||||
|
d102="ß"
|
||||||
|
d103="ß®"
|
||||||
|
d104="߯"
|
||||||
|
d105="ß°"
|
||||||
|
d106="ß±"
|
||||||
|
d107="ß³"
|
||||||
|
d108="à –"
|
||||||
|
d109="à —"
|
||||||
|
d110="à ˜"
|
||||||
|
d111="à ™"
|
||||||
|
d112="à ›"
|
||||||
|
d113="à œ"
|
||||||
|
d114="à "
|
||||||
|
d115="à ž"
|
||||||
|
d116="à Ÿ"
|
||||||
|
d117="à "
|
||||||
|
d118="à ¡"
|
||||||
|
d119="à ¢"
|
||||||
|
d120="à £"
|
||||||
|
d121="à ¥"
|
||||||
|
d122="à ¦"
|
||||||
|
d123="à §"
|
||||||
|
d124="à ©"
|
||||||
|
d125="à ª"
|
||||||
|
d126="à «"
|
||||||
|
d127="à ¬"
|
||||||
|
d128="à "
|
||||||
|
d129="॑"
|
||||||
|
d130="॓"
|
||||||
|
d131="॔"
|
||||||
|
d132="ྂ"
|
||||||
|
d133="ྃ"
|
||||||
|
d134="྆"
|
||||||
|
d135="྇"
|
||||||
|
d136="á"
|
||||||
|
d137="áž"
|
||||||
|
d138="áŸ"
|
||||||
|
d139="áŸ"
|
||||||
|
d140="᤺"
|
||||||
|
d141="ᨗ"
|
||||||
|
d142="᩵"
|
||||||
|
d143="á©¶"
|
||||||
|
d144="á©·"
|
||||||
|
d145="᩸"
|
||||||
|
d146="᩹"
|
||||||
|
d147="᩺"
|
||||||
|
d148="á©»"
|
||||||
|
d149="᩼"
|
||||||
|
d150="á«"
|
||||||
|
d151="á"
|
||||||
|
d152="á®"
|
||||||
|
d153="á¯"
|
||||||
|
d154="á°"
|
||||||
|
d155="á±"
|
||||||
|
d156="á²"
|
||||||
|
d157="á³"
|
||||||
|
d158="á³"
|
||||||
|
d159="᳑"
|
||||||
|
d160="á³’"
|
||||||
|
d161="᳚"
|
||||||
|
d162="á³›"
|
||||||
|
d163="á³ "
|
||||||
|
d164="á·€"
|
||||||
|
d165="á·"
|
||||||
|
d166="á·ƒ"
|
||||||
|
d167="á·„"
|
||||||
|
d168="á·…"
|
||||||
|
d169="á·†"
|
||||||
|
d170="á·‡"
|
||||||
|
d171="á·ˆ"
|
||||||
|
d172="á·‰"
|
||||||
|
d173="á·‹"
|
||||||
|
d174="᷌"
|
||||||
|
d175="á·‘"
|
||||||
|
d176="á·’"
|
||||||
|
d177="á·“"
|
||||||
|
d178="á·”"
|
||||||
|
d179="á·•"
|
||||||
|
d180="á·–"
|
||||||
|
d181="á·—"
|
||||||
|
d182="á·˜"
|
||||||
|
d183="á·™"
|
||||||
|
d184="á·š"
|
||||||
|
d185="á·›"
|
||||||
|
d186="ᷜ"
|
||||||
|
d187="á·"
|
||||||
|
d188="á·ž"
|
||||||
|
d189="á·Ÿ"
|
||||||
|
d190="á· "
|
||||||
|
d191="á·¡"
|
||||||
|
d192="á·¢"
|
||||||
|
d193="á·£"
|
||||||
|
d194="á·¤"
|
||||||
|
d195="á·¥"
|
||||||
|
d196="á·¦"
|
||||||
|
d197="á·¾"
|
||||||
|
d198="âƒ"
|
||||||
|
d199="⃑"
|
||||||
|
d200="⃔"
|
||||||
|
d201="⃕"
|
||||||
|
d202="⃖"
|
||||||
|
d203="⃗"
|
||||||
|
d204="⃛"
|
||||||
|
d205="⃜"
|
||||||
|
d206="⃡"
|
||||||
|
d207="⃧"
|
||||||
|
d208="⃩"
|
||||||
|
d209="⃰"
|
||||||
|
d210="⳯"
|
||||||
|
d211="â³°"
|
||||||
|
d212="â³±"
|
||||||
|
d213="â· "
|
||||||
|
d214="â·¡"
|
||||||
|
d215="â·¢"
|
||||||
|
d216="â·£"
|
||||||
|
d217="â·¤"
|
||||||
|
d218="â·¥"
|
||||||
|
d219="â·¦"
|
||||||
|
d220="â·§"
|
||||||
|
d221="â·¨"
|
||||||
|
d222="â·©"
|
||||||
|
d223="â·ª"
|
||||||
|
d224="â·«"
|
||||||
|
d225="â·¬"
|
||||||
|
d226="â·"
|
||||||
|
d227="â·®"
|
||||||
|
d228="â·¯"
|
||||||
|
d229="â·°"
|
||||||
|
d230="â·±"
|
||||||
|
d231="â·²"
|
||||||
|
d232="â·³"
|
||||||
|
d233="â·´"
|
||||||
|
d234="â·µ"
|
||||||
|
d235="â·¶"
|
||||||
|
d236="â··"
|
||||||
|
d237="â·¸"
|
||||||
|
d238="â·¹"
|
||||||
|
d239="â·º"
|
||||||
|
d240="â·»"
|
||||||
|
d241="â·¼"
|
||||||
|
d242="â·½"
|
||||||
|
d243="â·¾"
|
||||||
|
d244="â·¿"
|
||||||
|
d245="꙯"
|
||||||
|
d246="꙼"
|
||||||
|
d247="꙽"
|
||||||
|
d248="ê›°"
|
||||||
|
d249="ê›±"
|
||||||
|
d250="ê£ "
|
||||||
|
d251="꣡"
|
||||||
|
d252="꣢"
|
||||||
|
d253="꣣"
|
||||||
|
d254="꣤"
|
||||||
|
d255="꣥"
|
||||||
|
d256="꣦"
|
||||||
|
d257="꣧"
|
||||||
|
d258="꣨"
|
||||||
|
d259="꣩"
|
||||||
|
d260="꣪"
|
||||||
|
d261="꣫"
|
||||||
|
d262="꣬"
|
||||||
|
d263="ê£"
|
||||||
|
d264="꣮"
|
||||||
|
d265="꣯"
|
||||||
|
d266="꣰"
|
||||||
|
d267="꣱"
|
||||||
|
d268="ꪰ"
|
||||||
|
d269="ꪲ"
|
||||||
|
d270="ꪳ"
|
||||||
|
d271="ꪷ"
|
||||||
|
d272="ꪸ"
|
||||||
|
d273="ꪾ"
|
||||||
|
d274="꪿"
|
||||||
|
d275="ê«"
|
||||||
|
d276="ï¸ "
|
||||||
|
d277="︡"
|
||||||
|
d278="︢"
|
||||||
|
d279="︣"
|
||||||
|
d280="︤"
|
||||||
|
d281="︥"
|
||||||
|
d282="︦"
|
||||||
|
d283="ð¨"
|
||||||
|
d284="ð¨¸"
|
||||||
|
d285="ð†…"
|
||||||
|
d286="ð††"
|
||||||
|
d287="ð†‡"
|
||||||
|
d288="ð†ˆ"
|
||||||
|
d289="ð†‰"
|
||||||
|
d290="ð†ª"
|
||||||
|
d291="ð†«"
|
||||||
|
d292="ð†¬"
|
||||||
|
d293="ð†"
|
||||||
|
d294="ð‰‚"
|
||||||
|
d295="ð‰ƒ"
|
||||||
|
d296="ð‰„"
|
||||||
|
|
||||||
|
num_diacritics="297"
|
||||||
|
|
||||||
|
placeholder="ôŽ»®"
|
||||||
|
|
||||||
|
#####################################################################
|
||||||
|
# Upload the image and print the placeholder
|
||||||
|
#####################################################################
|
||||||
|
|
||||||
|
upload_image_and_print_placeholder
|
||||||
627
khash.h
Normal file
627
khash.h
Normal file
@@ -0,0 +1,627 @@
|
|||||||
|
/* The MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
An example:
|
||||||
|
|
||||||
|
#include "khash.h"
|
||||||
|
KHASH_MAP_INIT_INT(32, char)
|
||||||
|
int main() {
|
||||||
|
int ret, is_missing;
|
||||||
|
khiter_t k;
|
||||||
|
khash_t(32) *h = kh_init(32);
|
||||||
|
k = kh_put(32, h, 5, &ret);
|
||||||
|
kh_value(h, k) = 10;
|
||||||
|
k = kh_get(32, h, 10);
|
||||||
|
is_missing = (k == kh_end(h));
|
||||||
|
k = kh_get(32, h, 5);
|
||||||
|
kh_del(32, h, k);
|
||||||
|
for (k = kh_begin(h); k != kh_end(h); ++k)
|
||||||
|
if (kh_exist(h, k)) kh_value(h, k) = 1;
|
||||||
|
kh_destroy(32, h);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
2013-05-02 (0.2.8):
|
||||||
|
|
||||||
|
* Use quadratic probing. When the capacity is power of 2, stepping function
|
||||||
|
i*(i+1)/2 guarantees to traverse each bucket. It is better than double
|
||||||
|
hashing on cache performance and is more robust than linear probing.
|
||||||
|
|
||||||
|
In theory, double hashing should be more robust than quadratic probing.
|
||||||
|
However, my implementation is probably not for large hash tables, because
|
||||||
|
the second hash function is closely tied to the first hash function,
|
||||||
|
which reduce the effectiveness of double hashing.
|
||||||
|
|
||||||
|
Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php
|
||||||
|
|
||||||
|
2011-12-29 (0.2.7):
|
||||||
|
|
||||||
|
* Minor code clean up; no actual effect.
|
||||||
|
|
||||||
|
2011-09-16 (0.2.6):
|
||||||
|
|
||||||
|
* The capacity is a power of 2. This seems to dramatically improve the
|
||||||
|
speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
|
||||||
|
|
||||||
|
- http://code.google.com/p/ulib/
|
||||||
|
- http://nothings.org/computer/judy/
|
||||||
|
|
||||||
|
* Allow to optionally use linear probing which usually has better
|
||||||
|
performance for random input. Double hashing is still the default as it
|
||||||
|
is more robust to certain non-random input.
|
||||||
|
|
||||||
|
* Added Wang's integer hash function (not used by default). This hash
|
||||||
|
function is more robust to certain non-random input.
|
||||||
|
|
||||||
|
2011-02-14 (0.2.5):
|
||||||
|
|
||||||
|
* Allow to declare global functions.
|
||||||
|
|
||||||
|
2009-09-26 (0.2.4):
|
||||||
|
|
||||||
|
* Improve portability
|
||||||
|
|
||||||
|
2008-09-19 (0.2.3):
|
||||||
|
|
||||||
|
* Corrected the example
|
||||||
|
* Improved interfaces
|
||||||
|
|
||||||
|
2008-09-11 (0.2.2):
|
||||||
|
|
||||||
|
* Improved speed a little in kh_put()
|
||||||
|
|
||||||
|
2008-09-10 (0.2.1):
|
||||||
|
|
||||||
|
* Added kh_clear()
|
||||||
|
* Fixed a compiling error
|
||||||
|
|
||||||
|
2008-09-02 (0.2.0):
|
||||||
|
|
||||||
|
* Changed to token concatenation which increases flexibility.
|
||||||
|
|
||||||
|
2008-08-31 (0.1.2):
|
||||||
|
|
||||||
|
* Fixed a bug in kh_get(), which has not been tested previously.
|
||||||
|
|
||||||
|
2008-08-31 (0.1.1):
|
||||||
|
|
||||||
|
* Added destructor
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef __AC_KHASH_H
|
||||||
|
#define __AC_KHASH_H
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@header
|
||||||
|
|
||||||
|
Generic hash table library.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define AC_VERSION_KHASH_H "0.2.8"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
/* compiler specific configuration */
|
||||||
|
|
||||||
|
#if UINT_MAX == 0xffffffffu
|
||||||
|
typedef unsigned int khint32_t;
|
||||||
|
#elif ULONG_MAX == 0xffffffffu
|
||||||
|
typedef unsigned long khint32_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ULONG_MAX == ULLONG_MAX
|
||||||
|
typedef unsigned long khint64_t;
|
||||||
|
#else
|
||||||
|
typedef unsigned long long khint64_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef kh_inline
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define kh_inline __inline
|
||||||
|
#else
|
||||||
|
#define kh_inline inline
|
||||||
|
#endif
|
||||||
|
#endif /* kh_inline */
|
||||||
|
|
||||||
|
#ifndef klib_unused
|
||||||
|
#if (defined __clang__ && __clang_major__ >= 3) || (defined __GNUC__ && __GNUC__ >= 3)
|
||||||
|
#define klib_unused __attribute__ ((__unused__))
|
||||||
|
#else
|
||||||
|
#define klib_unused
|
||||||
|
#endif
|
||||||
|
#endif /* klib_unused */
|
||||||
|
|
||||||
|
typedef khint32_t khint_t;
|
||||||
|
typedef khint_t khiter_t;
|
||||||
|
|
||||||
|
#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
|
||||||
|
#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
|
||||||
|
#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
|
||||||
|
#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
|
||||||
|
#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
|
||||||
|
#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
|
||||||
|
#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
|
||||||
|
|
||||||
|
#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
|
||||||
|
|
||||||
|
#ifndef kroundup32
|
||||||
|
#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef kcalloc
|
||||||
|
#define kcalloc(N,Z) calloc(N,Z)
|
||||||
|
#endif
|
||||||
|
#ifndef kmalloc
|
||||||
|
#define kmalloc(Z) malloc(Z)
|
||||||
|
#endif
|
||||||
|
#ifndef krealloc
|
||||||
|
#define krealloc(P,Z) realloc(P,Z)
|
||||||
|
#endif
|
||||||
|
#ifndef kfree
|
||||||
|
#define kfree(P) free(P)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const double __ac_HASH_UPPER = 0.77;
|
||||||
|
|
||||||
|
#define __KHASH_TYPE(name, khkey_t, khval_t) \
|
||||||
|
typedef struct kh_##name##_s { \
|
||||||
|
khint_t n_buckets, size, n_occupied, upper_bound; \
|
||||||
|
khint32_t *flags; \
|
||||||
|
khkey_t *keys; \
|
||||||
|
khval_t *vals; \
|
||||||
|
} kh_##name##_t;
|
||||||
|
|
||||||
|
#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \
|
||||||
|
extern kh_##name##_t *kh_init_##name(void); \
|
||||||
|
extern void kh_destroy_##name(kh_##name##_t *h); \
|
||||||
|
extern void kh_clear_##name(kh_##name##_t *h); \
|
||||||
|
extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \
|
||||||
|
extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
|
||||||
|
extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
|
||||||
|
extern void kh_del_##name(kh_##name##_t *h, khint_t x);
|
||||||
|
|
||||||
|
#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||||
|
SCOPE kh_##name##_t *kh_init_##name(void) { \
|
||||||
|
return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \
|
||||||
|
} \
|
||||||
|
SCOPE void kh_destroy_##name(kh_##name##_t *h) \
|
||||||
|
{ \
|
||||||
|
if (h) { \
|
||||||
|
kfree((void *)h->keys); kfree(h->flags); \
|
||||||
|
kfree((void *)h->vals); \
|
||||||
|
kfree(h); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
SCOPE void kh_clear_##name(kh_##name##_t *h) \
|
||||||
|
{ \
|
||||||
|
if (h && h->flags) { \
|
||||||
|
memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
|
||||||
|
h->size = h->n_occupied = 0; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
|
||||||
|
{ \
|
||||||
|
if (h->n_buckets) { \
|
||||||
|
khint_t k, i, last, mask, step = 0; \
|
||||||
|
mask = h->n_buckets - 1; \
|
||||||
|
k = __hash_func(key); i = k & mask; \
|
||||||
|
last = i; \
|
||||||
|
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
|
||||||
|
i = (i + (++step)) & mask; \
|
||||||
|
if (i == last) return h->n_buckets; \
|
||||||
|
} \
|
||||||
|
return __ac_iseither(h->flags, i)? h->n_buckets : i; \
|
||||||
|
} else return 0; \
|
||||||
|
} \
|
||||||
|
SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
|
||||||
|
{ /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
|
||||||
|
khint32_t *new_flags = 0; \
|
||||||
|
khint_t j = 1; \
|
||||||
|
{ \
|
||||||
|
kroundup32(new_n_buckets); \
|
||||||
|
if (new_n_buckets < 4) new_n_buckets = 4; \
|
||||||
|
if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
|
||||||
|
else { /* hash table size to be changed (shrink or expand); rehash */ \
|
||||||
|
new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
|
||||||
|
if (!new_flags) return -1; \
|
||||||
|
memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
|
||||||
|
if (h->n_buckets < new_n_buckets) { /* expand */ \
|
||||||
|
khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
|
||||||
|
if (!new_keys) { kfree(new_flags); return -1; } \
|
||||||
|
h->keys = new_keys; \
|
||||||
|
if (kh_is_map) { \
|
||||||
|
khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
|
||||||
|
if (!new_vals) { kfree(new_flags); return -1; } \
|
||||||
|
h->vals = new_vals; \
|
||||||
|
} \
|
||||||
|
} /* otherwise shrink */ \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
if (j) { /* rehashing is needed */ \
|
||||||
|
for (j = 0; j != h->n_buckets; ++j) { \
|
||||||
|
if (__ac_iseither(h->flags, j) == 0) { \
|
||||||
|
khkey_t key = h->keys[j]; \
|
||||||
|
khval_t val; \
|
||||||
|
khint_t new_mask; \
|
||||||
|
new_mask = new_n_buckets - 1; \
|
||||||
|
if (kh_is_map) val = h->vals[j]; \
|
||||||
|
__ac_set_isdel_true(h->flags, j); \
|
||||||
|
while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
|
||||||
|
khint_t k, i, step = 0; \
|
||||||
|
k = __hash_func(key); \
|
||||||
|
i = k & new_mask; \
|
||||||
|
while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \
|
||||||
|
__ac_set_isempty_false(new_flags, i); \
|
||||||
|
if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
|
||||||
|
{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
|
||||||
|
if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
|
||||||
|
__ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
|
||||||
|
} else { /* write the element and jump out of the loop */ \
|
||||||
|
h->keys[i] = key; \
|
||||||
|
if (kh_is_map) h->vals[i] = val; \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
|
||||||
|
h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
|
||||||
|
if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
|
||||||
|
} \
|
||||||
|
kfree(h->flags); /* free the working space */ \
|
||||||
|
h->flags = new_flags; \
|
||||||
|
h->n_buckets = new_n_buckets; \
|
||||||
|
h->n_occupied = h->size; \
|
||||||
|
h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
|
||||||
|
} \
|
||||||
|
return 0; \
|
||||||
|
} \
|
||||||
|
SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
|
||||||
|
{ \
|
||||||
|
khint_t x; \
|
||||||
|
if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
|
||||||
|
if (h->n_buckets > (h->size<<1)) { \
|
||||||
|
if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
|
||||||
|
*ret = -1; return h->n_buckets; \
|
||||||
|
} \
|
||||||
|
} else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
|
||||||
|
*ret = -1; return h->n_buckets; \
|
||||||
|
} \
|
||||||
|
} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
|
||||||
|
{ \
|
||||||
|
khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \
|
||||||
|
x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
|
||||||
|
if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \
|
||||||
|
else { \
|
||||||
|
last = i; \
|
||||||
|
while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
|
||||||
|
if (__ac_isdel(h->flags, i)) site = i; \
|
||||||
|
i = (i + (++step)) & mask; \
|
||||||
|
if (i == last) { x = site; break; } \
|
||||||
|
} \
|
||||||
|
if (x == h->n_buckets) { \
|
||||||
|
if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
|
||||||
|
else x = i; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
if (__ac_isempty(h->flags, x)) { /* not present at all */ \
|
||||||
|
h->keys[x] = key; \
|
||||||
|
__ac_set_isboth_false(h->flags, x); \
|
||||||
|
++h->size; ++h->n_occupied; \
|
||||||
|
*ret = 1; \
|
||||||
|
} else if (__ac_isdel(h->flags, x)) { /* deleted */ \
|
||||||
|
h->keys[x] = key; \
|
||||||
|
__ac_set_isboth_false(h->flags, x); \
|
||||||
|
++h->size; \
|
||||||
|
*ret = 2; \
|
||||||
|
} else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
|
||||||
|
return x; \
|
||||||
|
} \
|
||||||
|
SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
|
||||||
|
{ \
|
||||||
|
if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \
|
||||||
|
__ac_set_isdel_true(h->flags, x); \
|
||||||
|
--h->size; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define KHASH_DECLARE(name, khkey_t, khval_t) \
|
||||||
|
__KHASH_TYPE(name, khkey_t, khval_t) \
|
||||||
|
__KHASH_PROTOTYPES(name, khkey_t, khval_t)
|
||||||
|
|
||||||
|
#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||||
|
__KHASH_TYPE(name, khkey_t, khval_t) \
|
||||||
|
__KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
|
||||||
|
|
||||||
|
#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
|
||||||
|
KHASH_INIT2(name, static kh_inline klib_unused, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
|
||||||
|
|
||||||
|
/* --- BEGIN OF HASH FUNCTIONS --- */
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Integer hash function
|
||||||
|
@param key The integer [khint32_t]
|
||||||
|
@return The hash value [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_int_hash_func(key) (khint32_t)(key)
|
||||||
|
/*! @function
|
||||||
|
@abstract Integer comparison function
|
||||||
|
*/
|
||||||
|
#define kh_int_hash_equal(a, b) ((a) == (b))
|
||||||
|
/*! @function
|
||||||
|
@abstract 64-bit integer hash function
|
||||||
|
@param key The integer [khint64_t]
|
||||||
|
@return The hash value [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
|
||||||
|
/*! @function
|
||||||
|
@abstract 64-bit integer comparison function
|
||||||
|
*/
|
||||||
|
#define kh_int64_hash_equal(a, b) ((a) == (b))
|
||||||
|
/*! @function
|
||||||
|
@abstract const char* hash function
|
||||||
|
@param s Pointer to a null terminated string
|
||||||
|
@return The hash value
|
||||||
|
*/
|
||||||
|
static kh_inline khint_t __ac_X31_hash_string(const char *s)
|
||||||
|
{
|
||||||
|
khint_t h = (khint_t)*s;
|
||||||
|
if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
/*! @function
|
||||||
|
@abstract Another interface to const char* hash function
|
||||||
|
@param key Pointer to a null terminated string [const char*]
|
||||||
|
@return The hash value [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_str_hash_func(key) __ac_X31_hash_string(key)
|
||||||
|
/*! @function
|
||||||
|
@abstract Const char* comparison function
|
||||||
|
*/
|
||||||
|
#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
|
||||||
|
|
||||||
|
static kh_inline khint_t __ac_Wang_hash(khint_t key)
|
||||||
|
{
|
||||||
|
key += ~(key << 15);
|
||||||
|
key ^= (key >> 10);
|
||||||
|
key += (key << 3);
|
||||||
|
key ^= (key >> 6);
|
||||||
|
key += ~(key << 11);
|
||||||
|
key ^= (key >> 16);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
#define kh_int_hash_func2(key) __ac_Wang_hash((khint_t)key)
|
||||||
|
|
||||||
|
/* --- END OF HASH FUNCTIONS --- */
|
||||||
|
|
||||||
|
/* Other convenient macros... */
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@abstract Type of the hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
*/
|
||||||
|
#define khash_t(name) kh_##name##_t
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Initiate a hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@return Pointer to the hash table [khash_t(name)*]
|
||||||
|
*/
|
||||||
|
#define kh_init(name) kh_init_##name()
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Destroy a hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
*/
|
||||||
|
#define kh_destroy(name, h) kh_destroy_##name(h)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Reset a hash table without deallocating memory.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
*/
|
||||||
|
#define kh_clear(name, h) kh_clear_##name(h)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Resize a hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param s New size [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_resize(name, h, s) kh_resize_##name(h, s)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Insert a key to the hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param k Key [type of keys]
|
||||||
|
@param r Extra return code: -1 if the operation failed;
|
||||||
|
0 if the key is present in the hash table;
|
||||||
|
1 if the bucket is empty (never used); 2 if the element in
|
||||||
|
the bucket has been deleted [int*]
|
||||||
|
@return Iterator to the inserted element [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Retrieve a key from the hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param k Key [type of keys]
|
||||||
|
@return Iterator to the found element, or kh_end(h) if the element is absent [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_get(name, h, k) kh_get_##name(h, k)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Remove a key from the hash table.
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param k Iterator to the element to be deleted [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_del(name, h, k) kh_del_##name(h, k)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Test whether a bucket contains data.
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param x Iterator to the bucket [khint_t]
|
||||||
|
@return 1 if containing data; 0 otherwise [int]
|
||||||
|
*/
|
||||||
|
#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Get key given an iterator
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param x Iterator to the bucket [khint_t]
|
||||||
|
@return Key [type of keys]
|
||||||
|
*/
|
||||||
|
#define kh_key(h, x) ((h)->keys[x])
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Get value given an iterator
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param x Iterator to the bucket [khint_t]
|
||||||
|
@return Value [type of values]
|
||||||
|
@discussion For hash sets, calling this results in segfault.
|
||||||
|
*/
|
||||||
|
#define kh_val(h, x) ((h)->vals[x])
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Alias of kh_val()
|
||||||
|
*/
|
||||||
|
#define kh_value(h, x) ((h)->vals[x])
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Get the start iterator
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@return The start iterator [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_begin(h) (khint_t)(0)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Get the end iterator
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@return The end iterator [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_end(h) ((h)->n_buckets)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Get the number of elements in the hash table
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@return Number of elements in the hash table [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_size(h) ((h)->size)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Get the number of buckets in the hash table
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@return Number of buckets in the hash table [khint_t]
|
||||||
|
*/
|
||||||
|
#define kh_n_buckets(h) ((h)->n_buckets)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Iterate over the entries in the hash table
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param kvar Variable to which key will be assigned
|
||||||
|
@param vvar Variable to which value will be assigned
|
||||||
|
@param code Block of code to execute
|
||||||
|
*/
|
||||||
|
#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \
|
||||||
|
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
|
||||||
|
if (!kh_exist(h,__i)) continue; \
|
||||||
|
(kvar) = kh_key(h,__i); \
|
||||||
|
(vvar) = kh_val(h,__i); \
|
||||||
|
code; \
|
||||||
|
} }
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Iterate over the values in the hash table
|
||||||
|
@param h Pointer to the hash table [khash_t(name)*]
|
||||||
|
@param vvar Variable to which value will be assigned
|
||||||
|
@param code Block of code to execute
|
||||||
|
*/
|
||||||
|
#define kh_foreach_value(h, vvar, code) { khint_t __i; \
|
||||||
|
for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
|
||||||
|
if (!kh_exist(h,__i)) continue; \
|
||||||
|
(vvar) = kh_val(h,__i); \
|
||||||
|
code; \
|
||||||
|
} }
|
||||||
|
|
||||||
|
/* More convenient interfaces */
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Instantiate a hash set containing integer keys
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
*/
|
||||||
|
#define KHASH_SET_INIT_INT(name) \
|
||||||
|
KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Instantiate a hash map containing integer keys
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param khval_t Type of values [type]
|
||||||
|
*/
|
||||||
|
#define KHASH_MAP_INIT_INT(name, khval_t) \
|
||||||
|
KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Instantiate a hash set containing 64-bit integer keys
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
*/
|
||||||
|
#define KHASH_SET_INIT_INT64(name) \
|
||||||
|
KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Instantiate a hash map containing 64-bit integer keys
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param khval_t Type of values [type]
|
||||||
|
*/
|
||||||
|
#define KHASH_MAP_INIT_INT64(name, khval_t) \
|
||||||
|
KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
|
||||||
|
|
||||||
|
typedef const char *kh_cstr_t;
|
||||||
|
/*! @function
|
||||||
|
@abstract Instantiate a hash map containing const char* keys
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
*/
|
||||||
|
#define KHASH_SET_INIT_STR(name) \
|
||||||
|
KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
|
||||||
|
|
||||||
|
/*! @function
|
||||||
|
@abstract Instantiate a hash map containing const char* keys
|
||||||
|
@param name Name of the hash table [symbol]
|
||||||
|
@param khval_t Type of values [type]
|
||||||
|
*/
|
||||||
|
#define KHASH_MAP_INIT_STR(name, khval_t) \
|
||||||
|
KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
|
||||||
|
|
||||||
|
#endif /* __AC_KHASH_H */
|
||||||
90
kvec.h
Normal file
90
kvec.h
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
/* The MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2008, by Attractive Chaos <attractor@live.co.uk>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
An example:
|
||||||
|
|
||||||
|
#include "kvec.h"
|
||||||
|
int main() {
|
||||||
|
kvec_t(int) array;
|
||||||
|
kv_init(array);
|
||||||
|
kv_push(int, array, 10); // append
|
||||||
|
kv_a(int, array, 20) = 5; // dynamic
|
||||||
|
kv_A(array, 20) = 4; // static
|
||||||
|
kv_destroy(array);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
2008-09-22 (0.1.0):
|
||||||
|
|
||||||
|
* The initial version.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AC_KVEC_H
|
||||||
|
#define AC_KVEC_H
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define kv_roundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
|
||||||
|
|
||||||
|
#define kvec_t(type) struct { size_t n, m; type *a; }
|
||||||
|
#define kv_init(v) ((v).n = (v).m = 0, (v).a = 0)
|
||||||
|
#define kv_destroy(v) free((v).a)
|
||||||
|
#define kv_A(v, i) ((v).a[(i)])
|
||||||
|
#define kv_pop(v) ((v).a[--(v).n])
|
||||||
|
#define kv_size(v) ((v).n)
|
||||||
|
#define kv_max(v) ((v).m)
|
||||||
|
|
||||||
|
#define kv_resize(type, v, s) ((v).m = (s), (v).a = (type*)realloc((v).a, sizeof(type) * (v).m))
|
||||||
|
|
||||||
|
#define kv_copy(type, v1, v0) do { \
|
||||||
|
if ((v1).m < (v0).n) kv_resize(type, v1, (v0).n); \
|
||||||
|
(v1).n = (v0).n; \
|
||||||
|
memcpy((v1).a, (v0).a, sizeof(type) * (v0).n); \
|
||||||
|
} while (0) \
|
||||||
|
|
||||||
|
#define kv_push(type, v, x) do { \
|
||||||
|
if ((v).n == (v).m) { \
|
||||||
|
(v).m = (v).m? (v).m<<1 : 2; \
|
||||||
|
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m); \
|
||||||
|
} \
|
||||||
|
(v).a[(v).n++] = (x); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define kv_pushp(type, v) ((((v).n == (v).m)? \
|
||||||
|
((v).m = ((v).m? (v).m<<1 : 2), \
|
||||||
|
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \
|
||||||
|
: 0), ((v).a + ((v).n++)))
|
||||||
|
|
||||||
|
#define kv_a(type, v, i) (((v).m <= (size_t)(i)? \
|
||||||
|
((v).m = (v).n = (i) + 1, kv_roundup32((v).m), \
|
||||||
|
(v).a = (type*)realloc((v).a, sizeof(type) * (v).m), 0) \
|
||||||
|
: (v).n <= (size_t)(i)? (v).n = (i) + 1 \
|
||||||
|
: 0), (v).a[(i)])
|
||||||
|
|
||||||
|
#endif
|
||||||
391
rowcolumn_diacritics_helpers.c
Normal file
391
rowcolumn_diacritics_helpers.c
Normal file
@@ -0,0 +1,391 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
uint16_t diacritic_to_num(uint32_t code)
|
||||||
|
{
|
||||||
|
switch (code) {
|
||||||
|
case 0x305:
|
||||||
|
return code - 0x305 + 1;
|
||||||
|
case 0x30d:
|
||||||
|
case 0x30e:
|
||||||
|
return code - 0x30d + 2;
|
||||||
|
case 0x310:
|
||||||
|
return code - 0x310 + 4;
|
||||||
|
case 0x312:
|
||||||
|
return code - 0x312 + 5;
|
||||||
|
case 0x33d:
|
||||||
|
case 0x33e:
|
||||||
|
case 0x33f:
|
||||||
|
return code - 0x33d + 6;
|
||||||
|
case 0x346:
|
||||||
|
return code - 0x346 + 9;
|
||||||
|
case 0x34a:
|
||||||
|
case 0x34b:
|
||||||
|
case 0x34c:
|
||||||
|
return code - 0x34a + 10;
|
||||||
|
case 0x350:
|
||||||
|
case 0x351:
|
||||||
|
case 0x352:
|
||||||
|
return code - 0x350 + 13;
|
||||||
|
case 0x357:
|
||||||
|
return code - 0x357 + 16;
|
||||||
|
case 0x35b:
|
||||||
|
return code - 0x35b + 17;
|
||||||
|
case 0x363:
|
||||||
|
case 0x364:
|
||||||
|
case 0x365:
|
||||||
|
case 0x366:
|
||||||
|
case 0x367:
|
||||||
|
case 0x368:
|
||||||
|
case 0x369:
|
||||||
|
case 0x36a:
|
||||||
|
case 0x36b:
|
||||||
|
case 0x36c:
|
||||||
|
case 0x36d:
|
||||||
|
case 0x36e:
|
||||||
|
case 0x36f:
|
||||||
|
return code - 0x363 + 18;
|
||||||
|
case 0x483:
|
||||||
|
case 0x484:
|
||||||
|
case 0x485:
|
||||||
|
case 0x486:
|
||||||
|
case 0x487:
|
||||||
|
return code - 0x483 + 31;
|
||||||
|
case 0x592:
|
||||||
|
case 0x593:
|
||||||
|
case 0x594:
|
||||||
|
case 0x595:
|
||||||
|
return code - 0x592 + 36;
|
||||||
|
case 0x597:
|
||||||
|
case 0x598:
|
||||||
|
case 0x599:
|
||||||
|
return code - 0x597 + 40;
|
||||||
|
case 0x59c:
|
||||||
|
case 0x59d:
|
||||||
|
case 0x59e:
|
||||||
|
case 0x59f:
|
||||||
|
case 0x5a0:
|
||||||
|
case 0x5a1:
|
||||||
|
return code - 0x59c + 43;
|
||||||
|
case 0x5a8:
|
||||||
|
case 0x5a9:
|
||||||
|
return code - 0x5a8 + 49;
|
||||||
|
case 0x5ab:
|
||||||
|
case 0x5ac:
|
||||||
|
return code - 0x5ab + 51;
|
||||||
|
case 0x5af:
|
||||||
|
return code - 0x5af + 53;
|
||||||
|
case 0x5c4:
|
||||||
|
return code - 0x5c4 + 54;
|
||||||
|
case 0x610:
|
||||||
|
case 0x611:
|
||||||
|
case 0x612:
|
||||||
|
case 0x613:
|
||||||
|
case 0x614:
|
||||||
|
case 0x615:
|
||||||
|
case 0x616:
|
||||||
|
case 0x617:
|
||||||
|
return code - 0x610 + 55;
|
||||||
|
case 0x657:
|
||||||
|
case 0x658:
|
||||||
|
case 0x659:
|
||||||
|
case 0x65a:
|
||||||
|
case 0x65b:
|
||||||
|
return code - 0x657 + 63;
|
||||||
|
case 0x65d:
|
||||||
|
case 0x65e:
|
||||||
|
return code - 0x65d + 68;
|
||||||
|
case 0x6d6:
|
||||||
|
case 0x6d7:
|
||||||
|
case 0x6d8:
|
||||||
|
case 0x6d9:
|
||||||
|
case 0x6da:
|
||||||
|
case 0x6db:
|
||||||
|
case 0x6dc:
|
||||||
|
return code - 0x6d6 + 70;
|
||||||
|
case 0x6df:
|
||||||
|
case 0x6e0:
|
||||||
|
case 0x6e1:
|
||||||
|
case 0x6e2:
|
||||||
|
return code - 0x6df + 77;
|
||||||
|
case 0x6e4:
|
||||||
|
return code - 0x6e4 + 81;
|
||||||
|
case 0x6e7:
|
||||||
|
case 0x6e8:
|
||||||
|
return code - 0x6e7 + 82;
|
||||||
|
case 0x6eb:
|
||||||
|
case 0x6ec:
|
||||||
|
return code - 0x6eb + 84;
|
||||||
|
case 0x730:
|
||||||
|
return code - 0x730 + 86;
|
||||||
|
case 0x732:
|
||||||
|
case 0x733:
|
||||||
|
return code - 0x732 + 87;
|
||||||
|
case 0x735:
|
||||||
|
case 0x736:
|
||||||
|
return code - 0x735 + 89;
|
||||||
|
case 0x73a:
|
||||||
|
return code - 0x73a + 91;
|
||||||
|
case 0x73d:
|
||||||
|
return code - 0x73d + 92;
|
||||||
|
case 0x73f:
|
||||||
|
case 0x740:
|
||||||
|
case 0x741:
|
||||||
|
return code - 0x73f + 93;
|
||||||
|
case 0x743:
|
||||||
|
return code - 0x743 + 96;
|
||||||
|
case 0x745:
|
||||||
|
return code - 0x745 + 97;
|
||||||
|
case 0x747:
|
||||||
|
return code - 0x747 + 98;
|
||||||
|
case 0x749:
|
||||||
|
case 0x74a:
|
||||||
|
return code - 0x749 + 99;
|
||||||
|
case 0x7eb:
|
||||||
|
case 0x7ec:
|
||||||
|
case 0x7ed:
|
||||||
|
case 0x7ee:
|
||||||
|
case 0x7ef:
|
||||||
|
case 0x7f0:
|
||||||
|
case 0x7f1:
|
||||||
|
return code - 0x7eb + 101;
|
||||||
|
case 0x7f3:
|
||||||
|
return code - 0x7f3 + 108;
|
||||||
|
case 0x816:
|
||||||
|
case 0x817:
|
||||||
|
case 0x818:
|
||||||
|
case 0x819:
|
||||||
|
return code - 0x816 + 109;
|
||||||
|
case 0x81b:
|
||||||
|
case 0x81c:
|
||||||
|
case 0x81d:
|
||||||
|
case 0x81e:
|
||||||
|
case 0x81f:
|
||||||
|
case 0x820:
|
||||||
|
case 0x821:
|
||||||
|
case 0x822:
|
||||||
|
case 0x823:
|
||||||
|
return code - 0x81b + 113;
|
||||||
|
case 0x825:
|
||||||
|
case 0x826:
|
||||||
|
case 0x827:
|
||||||
|
return code - 0x825 + 122;
|
||||||
|
case 0x829:
|
||||||
|
case 0x82a:
|
||||||
|
case 0x82b:
|
||||||
|
case 0x82c:
|
||||||
|
case 0x82d:
|
||||||
|
return code - 0x829 + 125;
|
||||||
|
case 0x951:
|
||||||
|
return code - 0x951 + 130;
|
||||||
|
case 0x953:
|
||||||
|
case 0x954:
|
||||||
|
return code - 0x953 + 131;
|
||||||
|
case 0xf82:
|
||||||
|
case 0xf83:
|
||||||
|
return code - 0xf82 + 133;
|
||||||
|
case 0xf86:
|
||||||
|
case 0xf87:
|
||||||
|
return code - 0xf86 + 135;
|
||||||
|
case 0x135d:
|
||||||
|
case 0x135e:
|
||||||
|
case 0x135f:
|
||||||
|
return code - 0x135d + 137;
|
||||||
|
case 0x17dd:
|
||||||
|
return code - 0x17dd + 140;
|
||||||
|
case 0x193a:
|
||||||
|
return code - 0x193a + 141;
|
||||||
|
case 0x1a17:
|
||||||
|
return code - 0x1a17 + 142;
|
||||||
|
case 0x1a75:
|
||||||
|
case 0x1a76:
|
||||||
|
case 0x1a77:
|
||||||
|
case 0x1a78:
|
||||||
|
case 0x1a79:
|
||||||
|
case 0x1a7a:
|
||||||
|
case 0x1a7b:
|
||||||
|
case 0x1a7c:
|
||||||
|
return code - 0x1a75 + 143;
|
||||||
|
case 0x1b6b:
|
||||||
|
return code - 0x1b6b + 151;
|
||||||
|
case 0x1b6d:
|
||||||
|
case 0x1b6e:
|
||||||
|
case 0x1b6f:
|
||||||
|
case 0x1b70:
|
||||||
|
case 0x1b71:
|
||||||
|
case 0x1b72:
|
||||||
|
case 0x1b73:
|
||||||
|
return code - 0x1b6d + 152;
|
||||||
|
case 0x1cd0:
|
||||||
|
case 0x1cd1:
|
||||||
|
case 0x1cd2:
|
||||||
|
return code - 0x1cd0 + 159;
|
||||||
|
case 0x1cda:
|
||||||
|
case 0x1cdb:
|
||||||
|
return code - 0x1cda + 162;
|
||||||
|
case 0x1ce0:
|
||||||
|
return code - 0x1ce0 + 164;
|
||||||
|
case 0x1dc0:
|
||||||
|
case 0x1dc1:
|
||||||
|
return code - 0x1dc0 + 165;
|
||||||
|
case 0x1dc3:
|
||||||
|
case 0x1dc4:
|
||||||
|
case 0x1dc5:
|
||||||
|
case 0x1dc6:
|
||||||
|
case 0x1dc7:
|
||||||
|
case 0x1dc8:
|
||||||
|
case 0x1dc9:
|
||||||
|
return code - 0x1dc3 + 167;
|
||||||
|
case 0x1dcb:
|
||||||
|
case 0x1dcc:
|
||||||
|
return code - 0x1dcb + 174;
|
||||||
|
case 0x1dd1:
|
||||||
|
case 0x1dd2:
|
||||||
|
case 0x1dd3:
|
||||||
|
case 0x1dd4:
|
||||||
|
case 0x1dd5:
|
||||||
|
case 0x1dd6:
|
||||||
|
case 0x1dd7:
|
||||||
|
case 0x1dd8:
|
||||||
|
case 0x1dd9:
|
||||||
|
case 0x1dda:
|
||||||
|
case 0x1ddb:
|
||||||
|
case 0x1ddc:
|
||||||
|
case 0x1ddd:
|
||||||
|
case 0x1dde:
|
||||||
|
case 0x1ddf:
|
||||||
|
case 0x1de0:
|
||||||
|
case 0x1de1:
|
||||||
|
case 0x1de2:
|
||||||
|
case 0x1de3:
|
||||||
|
case 0x1de4:
|
||||||
|
case 0x1de5:
|
||||||
|
case 0x1de6:
|
||||||
|
return code - 0x1dd1 + 176;
|
||||||
|
case 0x1dfe:
|
||||||
|
return code - 0x1dfe + 198;
|
||||||
|
case 0x20d0:
|
||||||
|
case 0x20d1:
|
||||||
|
return code - 0x20d0 + 199;
|
||||||
|
case 0x20d4:
|
||||||
|
case 0x20d5:
|
||||||
|
case 0x20d6:
|
||||||
|
case 0x20d7:
|
||||||
|
return code - 0x20d4 + 201;
|
||||||
|
case 0x20db:
|
||||||
|
case 0x20dc:
|
||||||
|
return code - 0x20db + 205;
|
||||||
|
case 0x20e1:
|
||||||
|
return code - 0x20e1 + 207;
|
||||||
|
case 0x20e7:
|
||||||
|
return code - 0x20e7 + 208;
|
||||||
|
case 0x20e9:
|
||||||
|
return code - 0x20e9 + 209;
|
||||||
|
case 0x20f0:
|
||||||
|
return code - 0x20f0 + 210;
|
||||||
|
case 0x2cef:
|
||||||
|
case 0x2cf0:
|
||||||
|
case 0x2cf1:
|
||||||
|
return code - 0x2cef + 211;
|
||||||
|
case 0x2de0:
|
||||||
|
case 0x2de1:
|
||||||
|
case 0x2de2:
|
||||||
|
case 0x2de3:
|
||||||
|
case 0x2de4:
|
||||||
|
case 0x2de5:
|
||||||
|
case 0x2de6:
|
||||||
|
case 0x2de7:
|
||||||
|
case 0x2de8:
|
||||||
|
case 0x2de9:
|
||||||
|
case 0x2dea:
|
||||||
|
case 0x2deb:
|
||||||
|
case 0x2dec:
|
||||||
|
case 0x2ded:
|
||||||
|
case 0x2dee:
|
||||||
|
case 0x2def:
|
||||||
|
case 0x2df0:
|
||||||
|
case 0x2df1:
|
||||||
|
case 0x2df2:
|
||||||
|
case 0x2df3:
|
||||||
|
case 0x2df4:
|
||||||
|
case 0x2df5:
|
||||||
|
case 0x2df6:
|
||||||
|
case 0x2df7:
|
||||||
|
case 0x2df8:
|
||||||
|
case 0x2df9:
|
||||||
|
case 0x2dfa:
|
||||||
|
case 0x2dfb:
|
||||||
|
case 0x2dfc:
|
||||||
|
case 0x2dfd:
|
||||||
|
case 0x2dfe:
|
||||||
|
case 0x2dff:
|
||||||
|
return code - 0x2de0 + 214;
|
||||||
|
case 0xa66f:
|
||||||
|
return code - 0xa66f + 246;
|
||||||
|
case 0xa67c:
|
||||||
|
case 0xa67d:
|
||||||
|
return code - 0xa67c + 247;
|
||||||
|
case 0xa6f0:
|
||||||
|
case 0xa6f1:
|
||||||
|
return code - 0xa6f0 + 249;
|
||||||
|
case 0xa8e0:
|
||||||
|
case 0xa8e1:
|
||||||
|
case 0xa8e2:
|
||||||
|
case 0xa8e3:
|
||||||
|
case 0xa8e4:
|
||||||
|
case 0xa8e5:
|
||||||
|
case 0xa8e6:
|
||||||
|
case 0xa8e7:
|
||||||
|
case 0xa8e8:
|
||||||
|
case 0xa8e9:
|
||||||
|
case 0xa8ea:
|
||||||
|
case 0xa8eb:
|
||||||
|
case 0xa8ec:
|
||||||
|
case 0xa8ed:
|
||||||
|
case 0xa8ee:
|
||||||
|
case 0xa8ef:
|
||||||
|
case 0xa8f0:
|
||||||
|
case 0xa8f1:
|
||||||
|
return code - 0xa8e0 + 251;
|
||||||
|
case 0xaab0:
|
||||||
|
return code - 0xaab0 + 269;
|
||||||
|
case 0xaab2:
|
||||||
|
case 0xaab3:
|
||||||
|
return code - 0xaab2 + 270;
|
||||||
|
case 0xaab7:
|
||||||
|
case 0xaab8:
|
||||||
|
return code - 0xaab7 + 272;
|
||||||
|
case 0xaabe:
|
||||||
|
case 0xaabf:
|
||||||
|
return code - 0xaabe + 274;
|
||||||
|
case 0xaac1:
|
||||||
|
return code - 0xaac1 + 276;
|
||||||
|
case 0xfe20:
|
||||||
|
case 0xfe21:
|
||||||
|
case 0xfe22:
|
||||||
|
case 0xfe23:
|
||||||
|
case 0xfe24:
|
||||||
|
case 0xfe25:
|
||||||
|
case 0xfe26:
|
||||||
|
return code - 0xfe20 + 277;
|
||||||
|
case 0x10a0f:
|
||||||
|
return code - 0x10a0f + 284;
|
||||||
|
case 0x10a38:
|
||||||
|
return code - 0x10a38 + 285;
|
||||||
|
case 0x1d185:
|
||||||
|
case 0x1d186:
|
||||||
|
case 0x1d187:
|
||||||
|
case 0x1d188:
|
||||||
|
case 0x1d189:
|
||||||
|
return code - 0x1d185 + 286;
|
||||||
|
case 0x1d1aa:
|
||||||
|
case 0x1d1ab:
|
||||||
|
case 0x1d1ac:
|
||||||
|
case 0x1d1ad:
|
||||||
|
return code - 0x1d1aa + 291;
|
||||||
|
case 0x1d242:
|
||||||
|
case 0x1d243:
|
||||||
|
case 0x1d244:
|
||||||
|
return code - 0x1d242 + 295;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
303
st.c
303
st.c
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "st.h"
|
#include "st.h"
|
||||||
#include "win.h"
|
#include "win.h"
|
||||||
|
#include "graphics.h"
|
||||||
|
|
||||||
#if defined(__linux)
|
#if defined(__linux)
|
||||||
#include <pty.h>
|
#include <pty.h>
|
||||||
@@ -36,6 +37,10 @@
|
|||||||
#define STR_BUF_SIZ ESC_BUF_SIZ
|
#define STR_BUF_SIZ ESC_BUF_SIZ
|
||||||
#define STR_ARG_SIZ ESC_ARG_SIZ
|
#define STR_ARG_SIZ ESC_ARG_SIZ
|
||||||
|
|
||||||
|
/* PUA character used as an image placeholder */
|
||||||
|
#define IMAGE_PLACEHOLDER_CHAR 0x10EEEE
|
||||||
|
#define IMAGE_PLACEHOLDER_CHAR_OLD 0xEEEE
|
||||||
|
|
||||||
/* macros */
|
/* macros */
|
||||||
#define IS_SET(flag) ((term.mode & (flag)) != 0)
|
#define IS_SET(flag) ((term.mode & (flag)) != 0)
|
||||||
#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f)
|
#define ISCONTROLC0(c) (BETWEEN(c, 0, 0x1f) || (c) == 0x7f)
|
||||||
@@ -113,6 +118,8 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
int row; /* nb row */
|
int row; /* nb row */
|
||||||
int col; /* nb col */
|
int col; /* nb col */
|
||||||
|
int pixw; /* width of the text area in pixels */
|
||||||
|
int pixh; /* height of the text area in pixels */
|
||||||
Line *line; /* screen */
|
Line *line; /* screen */
|
||||||
Line *alt; /* alternate screen */
|
Line *alt; /* alternate screen */
|
||||||
int *dirty; /* dirtyness of lines */
|
int *dirty; /* dirtyness of lines */
|
||||||
@@ -213,7 +220,6 @@ static Rune utf8decodebyte(char, size_t *);
|
|||||||
static char utf8encodebyte(Rune, size_t);
|
static char utf8encodebyte(Rune, size_t);
|
||||||
static size_t utf8validate(Rune *, size_t);
|
static size_t utf8validate(Rune *, size_t);
|
||||||
|
|
||||||
static char *base64dec(const char *);
|
|
||||||
static char base64dec_getc(const char **);
|
static char base64dec_getc(const char **);
|
||||||
|
|
||||||
static ssize_t xwrite(int, const char *, size_t);
|
static ssize_t xwrite(int, const char *, size_t);
|
||||||
@@ -232,6 +238,10 @@ static const uchar utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
|
|||||||
static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
|
static const Rune utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
|
||||||
static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
|
static const Rune utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
|
||||||
|
|
||||||
|
/* Converts a diacritic to a row/column/etc number. The result is 1-base, 0
|
||||||
|
* means "couldn't convert". Defined in rowcolumn_diacritics_helpers.c */
|
||||||
|
uint16_t diacritic_to_num(uint32_t code);
|
||||||
|
|
||||||
ssize_t
|
ssize_t
|
||||||
xwrite(int fd, const char *s, size_t len)
|
xwrite(int fd, const char *s, size_t len)
|
||||||
{
|
{
|
||||||
@@ -616,6 +626,12 @@ getsel(void)
|
|||||||
if (gp->mode & ATTR_WDUMMY)
|
if (gp->mode & ATTR_WDUMMY)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (gp->mode & ATTR_IMAGE) {
|
||||||
|
// TODO: Copy diacritics as well
|
||||||
|
ptr += utf8encode(IMAGE_PLACEHOLDER_CHAR, ptr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ptr += utf8encode(gp->u, ptr);
|
ptr += utf8encode(gp->u, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -715,11 +731,14 @@ sigchld(int a)
|
|||||||
int stat;
|
int stat;
|
||||||
pid_t p;
|
pid_t p;
|
||||||
|
|
||||||
if ((p = waitpid(pid, &stat, WNOHANG)) < 0)
|
if ((p = waitpid(-1, &stat, WNOHANG)) < 0)
|
||||||
die("waiting for pid %hd failed: %s\n", pid, strerror(errno));
|
die("waiting for pid %hd failed: %s\n", pid, strerror(errno));
|
||||||
|
|
||||||
if (pid != p)
|
if (pid != p) {
|
||||||
|
/* reinstall sigchld handler */
|
||||||
|
signal(SIGCHLD, sigchld);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (WIFEXITED(stat) && WEXITSTATUS(stat))
|
if (WIFEXITED(stat) && WEXITSTATUS(stat))
|
||||||
die("child exited with status %d\n", WEXITSTATUS(stat));
|
die("child exited with status %d\n", WEXITSTATUS(stat));
|
||||||
@@ -819,7 +838,11 @@ ttyread(void)
|
|||||||
{
|
{
|
||||||
static char buf[BUFSIZ];
|
static char buf[BUFSIZ];
|
||||||
static int buflen = 0;
|
static int buflen = 0;
|
||||||
int ret, written;
|
static int already_processing = 0;
|
||||||
|
int ret, written = 0;
|
||||||
|
|
||||||
|
if (buflen >= LEN(buf))
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* append read bytes to unprocessed bytes */
|
/* append read bytes to unprocessed bytes */
|
||||||
ret = read(cmdfd, buf+buflen, LEN(buf)-buflen);
|
ret = read(cmdfd, buf+buflen, LEN(buf)-buflen);
|
||||||
@@ -831,7 +854,24 @@ ttyread(void)
|
|||||||
die("couldn't read from shell: %s\n", strerror(errno));
|
die("couldn't read from shell: %s\n", strerror(errno));
|
||||||
default:
|
default:
|
||||||
buflen += ret;
|
buflen += ret;
|
||||||
written = twrite(buf, buflen, 0);
|
if (already_processing) {
|
||||||
|
/* Avoid recursive call to twrite() */
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
already_processing = 1;
|
||||||
|
while (1) {
|
||||||
|
int buflen_before_processing = buflen;
|
||||||
|
written += twrite(buf + written, buflen - written, 0);
|
||||||
|
// If buflen changed during the call to twrite, there is
|
||||||
|
// new data, and we need to keep processing, otherwise
|
||||||
|
// we can exit. This will not loop forever because the
|
||||||
|
// buffer is limited, and we don't clean it in this
|
||||||
|
// loop, so at some point ttywrite will have to drop
|
||||||
|
// some data.
|
||||||
|
if (buflen_before_processing == buflen)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
already_processing = 0;
|
||||||
buflen -= written;
|
buflen -= written;
|
||||||
/* keep any incomplete UTF-8 byte sequence for the next call */
|
/* keep any incomplete UTF-8 byte sequence for the next call */
|
||||||
if (buflen > 0)
|
if (buflen > 0)
|
||||||
@@ -874,6 +914,7 @@ ttywriteraw(const char *s, size_t n)
|
|||||||
fd_set wfd, rfd;
|
fd_set wfd, rfd;
|
||||||
ssize_t r;
|
ssize_t r;
|
||||||
size_t lim = 256;
|
size_t lim = 256;
|
||||||
|
int retries_left = 100;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remember that we are using a pty, which might be a modem line.
|
* Remember that we are using a pty, which might be a modem line.
|
||||||
@@ -882,6 +923,9 @@ ttywriteraw(const char *s, size_t n)
|
|||||||
* FIXME: Migrate the world to Plan 9.
|
* FIXME: Migrate the world to Plan 9.
|
||||||
*/
|
*/
|
||||||
while (n > 0) {
|
while (n > 0) {
|
||||||
|
if (retries_left-- <= 0)
|
||||||
|
goto too_many_retries;
|
||||||
|
|
||||||
FD_ZERO(&wfd);
|
FD_ZERO(&wfd);
|
||||||
FD_ZERO(&rfd);
|
FD_ZERO(&rfd);
|
||||||
FD_SET(cmdfd, &wfd);
|
FD_SET(cmdfd, &wfd);
|
||||||
@@ -923,11 +967,16 @@ ttywriteraw(const char *s, size_t n)
|
|||||||
|
|
||||||
write_error:
|
write_error:
|
||||||
die("write error on tty: %s\n", strerror(errno));
|
die("write error on tty: %s\n", strerror(errno));
|
||||||
|
too_many_retries:
|
||||||
|
fprintf(stderr, "Could not write %zu bytes to tty\n", n);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ttyresize(int tw, int th)
|
ttyresize(int tw, int th)
|
||||||
{
|
{
|
||||||
|
term.pixw = tw;
|
||||||
|
term.pixh = th;
|
||||||
|
|
||||||
struct winsize w;
|
struct winsize w;
|
||||||
|
|
||||||
w.ws_row = term.row;
|
w.ws_row = term.row;
|
||||||
@@ -1018,7 +1067,8 @@ treset(void)
|
|||||||
term.c = (TCursor){{
|
term.c = (TCursor){{
|
||||||
.mode = ATTR_NULL,
|
.mode = ATTR_NULL,
|
||||||
.fg = defaultfg,
|
.fg = defaultfg,
|
||||||
.bg = defaultbg
|
.bg = defaultbg,
|
||||||
|
.decor = DECOR_DEFAULT_COLOR
|
||||||
}, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
|
}, .x = 0, .y = 0, .state = CURSOR_DEFAULT};
|
||||||
|
|
||||||
memset(term.tabs, 0, term.col * sizeof(*term.tabs));
|
memset(term.tabs, 0, term.col * sizeof(*term.tabs));
|
||||||
@@ -1041,7 +1091,9 @@ treset(void)
|
|||||||
void
|
void
|
||||||
tnew(int col, int row)
|
tnew(int col, int row)
|
||||||
{
|
{
|
||||||
term = (Term){ .c = { .attr = { .fg = defaultfg, .bg = defaultbg } } };
|
term = (Term){.c = {.attr = {.fg = defaultfg,
|
||||||
|
.bg = defaultbg,
|
||||||
|
.decor = DECOR_DEFAULT_COLOR}}};
|
||||||
tresize(col, row);
|
tresize(col, row);
|
||||||
treset();
|
treset();
|
||||||
}
|
}
|
||||||
@@ -1218,9 +1270,24 @@ tsetchar(Rune u, const Glyph *attr, int x, int y)
|
|||||||
term.line[y][x-1].mode &= ~ATTR_WIDE;
|
term.line[y][x-1].mode &= ~ATTR_WIDE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (u == ' ' && term.line[y][x].mode & ATTR_IMAGE &&
|
||||||
|
tgetisclassicplaceholder(&term.line[y][x])) {
|
||||||
|
// This is a workaround: don't overwrite classic placement
|
||||||
|
// placeholders with space symbols (unlike Unicode placeholders
|
||||||
|
// which must be overwritten by anything).
|
||||||
|
term.line[y][x].bg = attr->bg;
|
||||||
|
term.dirty[y] = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
term.dirty[y] = 1;
|
term.dirty[y] = 1;
|
||||||
term.line[y][x] = *attr;
|
term.line[y][x] = *attr;
|
||||||
term.line[y][x].u = u;
|
term.line[y][x].u = u;
|
||||||
|
|
||||||
|
if (u == IMAGE_PLACEHOLDER_CHAR || u == IMAGE_PLACEHOLDER_CHAR_OLD) {
|
||||||
|
term.line[y][x].u = 0;
|
||||||
|
term.line[y][x].mode |= ATTR_IMAGE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1247,12 +1314,110 @@ tclearregion(int x1, int y1, int x2, int y2)
|
|||||||
selclear();
|
selclear();
|
||||||
gp->fg = term.c.attr.fg;
|
gp->fg = term.c.attr.fg;
|
||||||
gp->bg = term.c.attr.bg;
|
gp->bg = term.c.attr.bg;
|
||||||
|
gp->decor = term.c.attr.decor;
|
||||||
gp->mode = 0;
|
gp->mode = 0;
|
||||||
gp->u = ' ';
|
gp->u = ' ';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fills a rectangle area with an image placeholder. The starting point is the
|
||||||
|
/// cursor. Adds empty lines if needed. The placeholder will be marked as
|
||||||
|
/// classic.
|
||||||
|
void tcreateimgplaceholder(uint32_t image_id, uint32_t placement_id, int cols,
|
||||||
|
int rows, char do_not_move_cursor,
|
||||||
|
Glyph *text_underneath) {
|
||||||
|
for (int row = 0; row < rows; ++row) {
|
||||||
|
int y = term.c.y;
|
||||||
|
term.dirty[y] = 1;
|
||||||
|
for (int col = 0; col < cols; ++col) {
|
||||||
|
int x = term.c.x + col;
|
||||||
|
if (x >= term.col)
|
||||||
|
break;
|
||||||
|
Glyph *gp = &term.line[y][x];
|
||||||
|
if (selected(x, y))
|
||||||
|
selclear();
|
||||||
|
if (text_underneath) {
|
||||||
|
Glyph *to_save = gp;
|
||||||
|
// If there is already a classic placeholder,
|
||||||
|
// use the text underneath it. This will leave
|
||||||
|
// holes in images, but at least we are
|
||||||
|
// guaranteed to restore the original text.
|
||||||
|
if (gp->mode & ATTR_IMAGE &&
|
||||||
|
tgetisclassicplaceholder(gp)) {
|
||||||
|
Glyph *under =
|
||||||
|
gr_get_glyph_underneath_image(
|
||||||
|
tgetimgid(gp),
|
||||||
|
tgetimgplacementid(gp),
|
||||||
|
tgetimgcol(gp),
|
||||||
|
tgetimgrow(gp));
|
||||||
|
if (under)
|
||||||
|
to_save = under;
|
||||||
|
}
|
||||||
|
text_underneath[cols * row + col] = *to_save;
|
||||||
|
}
|
||||||
|
gp->mode = ATTR_IMAGE;
|
||||||
|
gp->u = 0;
|
||||||
|
tsetimgrow(gp, row + 1);
|
||||||
|
tsetimgcol(gp, col + 1);
|
||||||
|
tsetimgid(gp, image_id);
|
||||||
|
tsetimgplacementid(gp, placement_id);
|
||||||
|
tsetimgdiacriticcount(gp, 3);
|
||||||
|
tsetisclassicplaceholder(gp, 1);
|
||||||
|
}
|
||||||
|
// If moving the cursor is not allowed and this is the last line
|
||||||
|
// of the terminal, we are done.
|
||||||
|
if (do_not_move_cursor && y == term.row - 1)
|
||||||
|
break;
|
||||||
|
// Move the cursor down, maybe creating a new line. The x is
|
||||||
|
// preserved (we never change term.c.x in the loop above).
|
||||||
|
if (row != rows - 1)
|
||||||
|
tnewline(/*first_col=*/0);
|
||||||
|
}
|
||||||
|
if (do_not_move_cursor) {
|
||||||
|
// Return the cursor to the original position.
|
||||||
|
tmoveto(term.c.x, term.c.y - rows + 1);
|
||||||
|
} else {
|
||||||
|
// Move the cursor beyond the last column, as required by the
|
||||||
|
// protocol. If the cursor goes beyond the screen edge, insert a
|
||||||
|
// newline to match the behavior of kitty.
|
||||||
|
if (term.c.x + cols >= term.col)
|
||||||
|
tnewline(/*first_col=*/1);
|
||||||
|
else
|
||||||
|
tmoveto(term.c.x + cols, term.c.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gr_for_each_image_cell(int (*callback)(void *data, Glyph *gp),
|
||||||
|
void *data) {
|
||||||
|
for (int row = 0; row < term.row; ++row) {
|
||||||
|
for (int col = 0; col < term.col; ++col) {
|
||||||
|
Glyph *gp = &term.line[row][col];
|
||||||
|
if (gp->mode & ATTR_IMAGE) {
|
||||||
|
if (callback(data, gp))
|
||||||
|
term.dirty[row] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void gr_schedule_image_redraw_by_id(uint32_t image_id) {
|
||||||
|
for (int row = 0; row < term.row; ++row) {
|
||||||
|
if (term.dirty[row])
|
||||||
|
continue;
|
||||||
|
for (int col = 0; col < term.col; ++col) {
|
||||||
|
Glyph *gp = &term.line[row][col];
|
||||||
|
if (gp->mode & ATTR_IMAGE) {
|
||||||
|
uint32_t cell_image_id = tgetimgid(gp);
|
||||||
|
if (cell_image_id == image_id) {
|
||||||
|
term.dirty[row] = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
tdeletechar(int n)
|
tdeletechar(int n)
|
||||||
{
|
{
|
||||||
@@ -1371,6 +1536,7 @@ tsetattr(const int *attr, int l)
|
|||||||
ATTR_STRUCK );
|
ATTR_STRUCK );
|
||||||
term.c.attr.fg = defaultfg;
|
term.c.attr.fg = defaultfg;
|
||||||
term.c.attr.bg = defaultbg;
|
term.c.attr.bg = defaultbg;
|
||||||
|
term.c.attr.decor = DECOR_DEFAULT_COLOR;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
term.c.attr.mode |= ATTR_BOLD;
|
term.c.attr.mode |= ATTR_BOLD;
|
||||||
@@ -1383,6 +1549,20 @@ tsetattr(const int *attr, int l)
|
|||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
term.c.attr.mode |= ATTR_UNDERLINE;
|
term.c.attr.mode |= ATTR_UNDERLINE;
|
||||||
|
if (i + 1 < l) {
|
||||||
|
idx = attr[++i];
|
||||||
|
if (BETWEEN(idx, 1, 5)) {
|
||||||
|
tsetdecorstyle(&term.c.attr, idx);
|
||||||
|
} else if (idx == 0) {
|
||||||
|
term.c.attr.mode &= ~ATTR_UNDERLINE;
|
||||||
|
tsetdecorstyle(&term.c.attr, 0);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr,
|
||||||
|
"erresc: unknown underline "
|
||||||
|
"style %d\n",
|
||||||
|
idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 5: /* slow blink */
|
case 5: /* slow blink */
|
||||||
/* FALLTHROUGH */
|
/* FALLTHROUGH */
|
||||||
@@ -1406,6 +1586,7 @@ tsetattr(const int *attr, int l)
|
|||||||
break;
|
break;
|
||||||
case 24:
|
case 24:
|
||||||
term.c.attr.mode &= ~ATTR_UNDERLINE;
|
term.c.attr.mode &= ~ATTR_UNDERLINE;
|
||||||
|
tsetdecorstyle(&term.c.attr, 0);
|
||||||
break;
|
break;
|
||||||
case 25:
|
case 25:
|
||||||
term.c.attr.mode &= ~ATTR_BLINK;
|
term.c.attr.mode &= ~ATTR_BLINK;
|
||||||
@@ -1434,10 +1615,11 @@ tsetattr(const int *attr, int l)
|
|||||||
term.c.attr.bg = defaultbg;
|
term.c.attr.bg = defaultbg;
|
||||||
break;
|
break;
|
||||||
case 58:
|
case 58:
|
||||||
/* This starts a sequence to change the color of
|
if ((idx = tdefcolor(attr, &i, l)) >= 0)
|
||||||
* "underline" pixels. We don't support that and
|
tsetdecorcolor(&term.c.attr, idx);
|
||||||
* instead eat up a following "5;n" or "2;r;g;b". */
|
break;
|
||||||
tdefcolor(attr, &i, l);
|
case 59:
|
||||||
|
tsetdecorcolor(&term.c.attr, DECOR_DEFAULT_COLOR);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (BETWEEN(attr[i], 30, 37)) {
|
if (BETWEEN(attr[i], 30, 37)) {
|
||||||
@@ -1826,6 +2008,39 @@ csihandle(void)
|
|||||||
goto unknown;
|
goto unknown;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case '>':
|
||||||
|
switch (csiescseq.mode[1]) {
|
||||||
|
case 'q': /* XTVERSION -- Print terminal name and version */
|
||||||
|
len = snprintf(buf, sizeof(buf),
|
||||||
|
"\033P>|st-graphics(%s)\033\\", VERSION);
|
||||||
|
ttywrite(buf, len, 0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto unknown;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 't': /* XTWINOPS -- Window manipulation */
|
||||||
|
switch (csiescseq.arg[0]) {
|
||||||
|
case 14: /* Report text area size in pixels. */
|
||||||
|
len = snprintf(buf, sizeof(buf), "\033[4;%i;%it",
|
||||||
|
term.pixh, term.pixw);
|
||||||
|
ttywrite(buf, len, 0);
|
||||||
|
break;
|
||||||
|
case 16: /* Report character cell size in pixels. */
|
||||||
|
len = snprintf(buf, sizeof(buf), "\033[6;%i;%it",
|
||||||
|
term.pixh / term.row,
|
||||||
|
term.pixw / term.col);
|
||||||
|
ttywrite(buf, len, 0);
|
||||||
|
break;
|
||||||
|
case 18: /* Report the size of the text area in characters. */
|
||||||
|
len = snprintf(buf, sizeof(buf), "\033[8;%i;%it",
|
||||||
|
term.row, term.col);
|
||||||
|
ttywrite(buf, len, 0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto unknown;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1988,8 +2203,27 @@ strhandle(void)
|
|||||||
case 'k': /* old title set compatibility */
|
case 'k': /* old title set compatibility */
|
||||||
xsettitle(strescseq.args[0]);
|
xsettitle(strescseq.args[0]);
|
||||||
return;
|
return;
|
||||||
case 'P': /* DCS -- Device Control String */
|
|
||||||
case '_': /* APC -- Application Program Command */
|
case '_': /* APC -- Application Program Command */
|
||||||
|
if (gr_parse_command(strescseq.buf, strescseq.len)) {
|
||||||
|
GraphicsCommandResult *res = &graphics_command_result;
|
||||||
|
if (res->create_placeholder) {
|
||||||
|
tcreateimgplaceholder(
|
||||||
|
res->placeholder.image_id,
|
||||||
|
res->placeholder.placement_id,
|
||||||
|
res->placeholder.columns,
|
||||||
|
res->placeholder.rows,
|
||||||
|
res->placeholder.do_not_move_cursor,
|
||||||
|
res->placeholder.text_underneath);
|
||||||
|
}
|
||||||
|
if (res->response[0])
|
||||||
|
ttywrite(res->response, strlen(res->response),
|
||||||
|
0);
|
||||||
|
if (res->redraw)
|
||||||
|
tfulldirt();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case 'P': /* DCS -- Device Control String */
|
||||||
case '^': /* PM -- Privacy Message */
|
case '^': /* PM -- Privacy Message */
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2496,6 +2730,41 @@ check_control_code:
|
|||||||
if (selected(term.c.x, term.c.y))
|
if (selected(term.c.x, term.c.y))
|
||||||
selclear();
|
selclear();
|
||||||
|
|
||||||
|
// wcwidth is broken on some systems, set the width to 0 if it's a known
|
||||||
|
// diacritic used for images.
|
||||||
|
uint16_t num = diacritic_to_num(u);
|
||||||
|
if (num != 0)
|
||||||
|
width = 0;
|
||||||
|
// Set the width to 1 if it's an image placeholder character.
|
||||||
|
if (u == IMAGE_PLACEHOLDER_CHAR || u == IMAGE_PLACEHOLDER_CHAR_OLD)
|
||||||
|
width = 1;
|
||||||
|
|
||||||
|
if (width == 0) {
|
||||||
|
// It's probably a combining char. Combining characters are not
|
||||||
|
// supported, so we just ignore them, unless it denotes the row and
|
||||||
|
// column of an image character.
|
||||||
|
if (term.c.y <= 0 && term.c.x <= 0)
|
||||||
|
return;
|
||||||
|
else if (term.c.x == 0)
|
||||||
|
gp = &term.line[term.c.y-1][term.col-1];
|
||||||
|
else if (term.c.state & CURSOR_WRAPNEXT)
|
||||||
|
gp = &term.line[term.c.y][term.c.x];
|
||||||
|
else
|
||||||
|
gp = &term.line[term.c.y][term.c.x-1];
|
||||||
|
if (num && (gp->mode & ATTR_IMAGE)) {
|
||||||
|
unsigned diaccount = tgetimgdiacriticcount(gp);
|
||||||
|
if (diaccount == 0)
|
||||||
|
tsetimgrow(gp, num);
|
||||||
|
else if (diaccount == 1)
|
||||||
|
tsetimgcol(gp, num);
|
||||||
|
else if (diaccount == 2)
|
||||||
|
tsetimg4thbyteplus1(gp, num);
|
||||||
|
tsetimgdiacriticcount(gp, diaccount + 1);
|
||||||
|
}
|
||||||
|
term.lastc = u;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
gp = &term.line[term.c.y][term.c.x];
|
gp = &term.line[term.c.y][term.c.x];
|
||||||
if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) {
|
if (IS_SET(MODE_WRAP) && (term.c.state & CURSOR_WRAPNEXT)) {
|
||||||
gp->mode |= ATTR_WRAP;
|
gp->mode |= ATTR_WRAP;
|
||||||
@@ -2662,6 +2931,8 @@ drawregion(int x1, int y1, int x2, int y2)
|
|||||||
{
|
{
|
||||||
int y;
|
int y;
|
||||||
|
|
||||||
|
xstartimagedraw(term.dirty, term.row);
|
||||||
|
|
||||||
for (y = y1; y < y2; y++) {
|
for (y = y1; y < y2; y++) {
|
||||||
if (!term.dirty[y])
|
if (!term.dirty[y])
|
||||||
continue;
|
continue;
|
||||||
@@ -2669,6 +2940,8 @@ drawregion(int x1, int y1, int x2, int y2)
|
|||||||
term.dirty[y] = 0;
|
term.dirty[y] = 0;
|
||||||
xdrawline(term.line[y], x1, y, x2);
|
xdrawline(term.line[y], x1, y, x2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xfinishimagedraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -2703,3 +2976,9 @@ redraw(void)
|
|||||||
tfulldirt();
|
tfulldirt();
|
||||||
draw();
|
draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Glyph
|
||||||
|
getglyphat(int col, int row)
|
||||||
|
{
|
||||||
|
return term.line[row][col];
|
||||||
|
}
|
||||||
|
|||||||
84
st.h
84
st.h
@@ -12,7 +12,7 @@
|
|||||||
#define DEFAULT(a, b) (a) = (a) ? (a) : (b)
|
#define DEFAULT(a, b) (a) = (a) ? (a) : (b)
|
||||||
#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
|
#define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x)
|
||||||
#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \
|
#define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || \
|
||||||
(a).bg != (b).bg)
|
(a).bg != (b).bg || (a).decor != (b).decor)
|
||||||
#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
|
#define TIMEDIFF(t1, t2) ((t1.tv_sec-t2.tv_sec)*1000 + \
|
||||||
(t1.tv_nsec-t2.tv_nsec)/1E6)
|
(t1.tv_nsec-t2.tv_nsec)/1E6)
|
||||||
#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
|
#define MODBIT(x, set, bit) ((set) ? ((x) |= (bit)) : ((x) &= ~(bit)))
|
||||||
@@ -20,6 +20,10 @@
|
|||||||
#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b))
|
#define TRUECOLOR(r,g,b) (1 << 24 | (r) << 16 | (g) << 8 | (b))
|
||||||
#define IS_TRUECOL(x) (1 << 24 & (x))
|
#define IS_TRUECOL(x) (1 << 24 & (x))
|
||||||
|
|
||||||
|
// This decor color indicates that the fg color should be used. Note that it's
|
||||||
|
// not a 24-bit color because the 25-th bit is not set.
|
||||||
|
#define DECOR_DEFAULT_COLOR 0x0ffffff
|
||||||
|
|
||||||
enum glyph_attribute {
|
enum glyph_attribute {
|
||||||
ATTR_NULL = 0,
|
ATTR_NULL = 0,
|
||||||
ATTR_BOLD = 1 << 0,
|
ATTR_BOLD = 1 << 0,
|
||||||
@@ -34,6 +38,7 @@ enum glyph_attribute {
|
|||||||
ATTR_WIDE = 1 << 9,
|
ATTR_WIDE = 1 << 9,
|
||||||
ATTR_WDUMMY = 1 << 10,
|
ATTR_WDUMMY = 1 << 10,
|
||||||
ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
|
ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
|
||||||
|
ATTR_IMAGE = 1 << 14,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum selection_mode {
|
enum selection_mode {
|
||||||
@@ -52,6 +57,14 @@ enum selection_snap {
|
|||||||
SNAP_LINE = 2
|
SNAP_LINE = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum underline_style {
|
||||||
|
UNDERLINE_STRAIGHT = 1,
|
||||||
|
UNDERLINE_DOUBLE = 2,
|
||||||
|
UNDERLINE_CURLY = 3,
|
||||||
|
UNDERLINE_DOTTED = 4,
|
||||||
|
UNDERLINE_DASHED = 5,
|
||||||
|
};
|
||||||
|
|
||||||
typedef unsigned char uchar;
|
typedef unsigned char uchar;
|
||||||
typedef unsigned int uint;
|
typedef unsigned int uint;
|
||||||
typedef unsigned long ulong;
|
typedef unsigned long ulong;
|
||||||
@@ -65,6 +78,7 @@ typedef struct {
|
|||||||
ushort mode; /* attribute flags */
|
ushort mode; /* attribute flags */
|
||||||
uint32_t fg; /* foreground */
|
uint32_t fg; /* foreground */
|
||||||
uint32_t bg; /* background */
|
uint32_t bg; /* background */
|
||||||
|
uint32_t decor; /* decoration (like underline) */
|
||||||
} Glyph;
|
} Glyph;
|
||||||
|
|
||||||
typedef Glyph *Line;
|
typedef Glyph *Line;
|
||||||
@@ -105,6 +119,8 @@ void selextend(int, int, int, int);
|
|||||||
int selected(int, int);
|
int selected(int, int);
|
||||||
char *getsel(void);
|
char *getsel(void);
|
||||||
|
|
||||||
|
Glyph getglyphat(int, int);
|
||||||
|
|
||||||
size_t utf8encode(Rune, char *);
|
size_t utf8encode(Rune, char *);
|
||||||
|
|
||||||
void *xmalloc(size_t);
|
void *xmalloc(size_t);
|
||||||
@@ -124,3 +140,69 @@ extern unsigned int tabspaces;
|
|||||||
extern unsigned int defaultfg;
|
extern unsigned int defaultfg;
|
||||||
extern unsigned int defaultbg;
|
extern unsigned int defaultbg;
|
||||||
extern unsigned int defaultcs;
|
extern unsigned int defaultcs;
|
||||||
|
|
||||||
|
// Accessors to decoration properties stored in `decor`.
|
||||||
|
// The 25-th bit is used to indicate if it's a 24-bit color.
|
||||||
|
static inline uint32_t tgetdecorcolor(Glyph *g) { return g->decor & 0x1ffffff; }
|
||||||
|
static inline uint32_t tgetdecorstyle(Glyph *g) { return (g->decor >> 25) & 0x7; }
|
||||||
|
static inline void tsetdecorcolor(Glyph *g, uint32_t color) {
|
||||||
|
g->decor = (g->decor & ~0x1ffffff) | (color & 0x1ffffff);
|
||||||
|
}
|
||||||
|
static inline void tsetdecorstyle(Glyph *g, uint32_t style) {
|
||||||
|
g->decor = (g->decor & ~(0x7 << 25)) | ((style & 0x7) << 25);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Some accessors to image placeholder properties stored in `u`:
|
||||||
|
// - row (1-base) - 9 bits
|
||||||
|
// - column (1-base) - 9 bits
|
||||||
|
// - most significant byte of the image id plus 1 - 9 bits (0 means unspecified,
|
||||||
|
// don't forget to subtract 1).
|
||||||
|
// - the original number of diacritics (0, 1, 2, or 3) - 2 bits
|
||||||
|
// - whether this is a classic (1) or Unicode (0) placeholder - 1 bit
|
||||||
|
static inline uint32_t tgetimgrow(Glyph *g) { return g->u & 0x1ff; }
|
||||||
|
static inline uint32_t tgetimgcol(Glyph *g) { return (g->u >> 9) & 0x1ff; }
|
||||||
|
static inline uint32_t tgetimgid4thbyteplus1(Glyph *g) { return (g->u >> 18) & 0x1ff; }
|
||||||
|
static inline uint32_t tgetimgdiacriticcount(Glyph *g) { return (g->u >> 27) & 0x3; }
|
||||||
|
static inline uint32_t tgetisclassicplaceholder(Glyph *g) { return (g->u >> 29) & 0x1; }
|
||||||
|
static inline void tsetimgrow(Glyph *g, uint32_t row) {
|
||||||
|
g->u = (g->u & ~0x1ff) | (row & 0x1ff);
|
||||||
|
}
|
||||||
|
static inline void tsetimgcol(Glyph *g, uint32_t col) {
|
||||||
|
g->u = (g->u & ~(0x1ff << 9)) | ((col & 0x1ff) << 9);
|
||||||
|
}
|
||||||
|
static inline void tsetimg4thbyteplus1(Glyph *g, uint32_t byteplus1) {
|
||||||
|
g->u = (g->u & ~(0x1ff << 18)) | ((byteplus1 & 0x1ff) << 18);
|
||||||
|
}
|
||||||
|
static inline void tsetimgdiacriticcount(Glyph *g, uint32_t count) {
|
||||||
|
g->u = (g->u & ~(0x3 << 27)) | ((count & 0x3) << 27);
|
||||||
|
}
|
||||||
|
static inline void tsetisclassicplaceholder(Glyph *g, uint32_t isclassic) {
|
||||||
|
g->u = (g->u & ~(0x1 << 29)) | ((isclassic & 0x1) << 29);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the full image id. This is a naive implementation, if the most
|
||||||
|
/// significant byte is not specified, it's assumed to be 0 instead of inferring
|
||||||
|
/// it from the cells to the left.
|
||||||
|
static inline uint32_t tgetimgid(Glyph *g) {
|
||||||
|
uint32_t msb = tgetimgid4thbyteplus1(g);
|
||||||
|
if (msb != 0)
|
||||||
|
--msb;
|
||||||
|
return (msb << 24) | (g->fg & 0xFFFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the full image id.
|
||||||
|
static inline void tsetimgid(Glyph *g, uint32_t id) {
|
||||||
|
g->fg = (id & 0xFFFFFF) | (1 << 24);
|
||||||
|
tsetimg4thbyteplus1(g, ((id >> 24) & 0xFF) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t tgetimgplacementid(Glyph *g) {
|
||||||
|
if (tgetdecorcolor(g) == DECOR_DEFAULT_COLOR)
|
||||||
|
return 0;
|
||||||
|
return g->decor & 0xFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void tsetimgplacementid(Glyph *g, uint32_t id) {
|
||||||
|
g->decor = (id & 0xFFFFFF) | (1 << 24);
|
||||||
|
}
|
||||||
|
|||||||
6
st.info
6
st.info
@@ -195,6 +195,7 @@ st-mono| simpleterm monocolor,
|
|||||||
Ms=\E]52;%p1%s;%p2%s\007,
|
Ms=\E]52;%p1%s;%p2%s\007,
|
||||||
Se=\E[2 q,
|
Se=\E[2 q,
|
||||||
Ss=\E[%p1%d q,
|
Ss=\E[%p1%d q,
|
||||||
|
Smulx=\E[4:%p1%dm,
|
||||||
|
|
||||||
st| simpleterm,
|
st| simpleterm,
|
||||||
use=st-mono,
|
use=st-mono,
|
||||||
@@ -215,6 +216,11 @@ st-256color| simpleterm with 256 colors,
|
|||||||
initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\,
|
initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\,
|
||||||
setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m,
|
setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m,
|
||||||
setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m,
|
setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m,
|
||||||
|
# Underline colors
|
||||||
|
Su,
|
||||||
|
Setulc=\E[58:2:%p1%{65536}%/%d:%p1%{256}%/%{255}%&%d:%p1%{255}%&%d%;m,
|
||||||
|
Setulc1=\E[58:5:%p1%dm,
|
||||||
|
ol=\E[59m,
|
||||||
|
|
||||||
st-meta| simpleterm with meta key,
|
st-meta| simpleterm with meta key,
|
||||||
use=st,
|
use=st,
|
||||||
|
|||||||
3
win.h
3
win.h
@@ -39,3 +39,6 @@ void xsetpointermotion(int);
|
|||||||
void xsetsel(char *);
|
void xsetsel(char *);
|
||||||
int xstartdraw(void);
|
int xstartdraw(void);
|
||||||
void xximspot(int, int);
|
void xximspot(int, int);
|
||||||
|
|
||||||
|
void xstartimagedraw(int *dirty, int rows);
|
||||||
|
void xfinishimagedraw();
|
||||||
|
|||||||
409
x.c
409
x.c
@@ -4,6 +4,8 @@
|
|||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@@ -19,6 +21,7 @@ char *argv0;
|
|||||||
#include "arg.h"
|
#include "arg.h"
|
||||||
#include "st.h"
|
#include "st.h"
|
||||||
#include "win.h"
|
#include "win.h"
|
||||||
|
#include "graphics.h"
|
||||||
|
|
||||||
/* types used in config.h */
|
/* types used in config.h */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -59,6 +62,12 @@ static void zoom(const Arg *);
|
|||||||
static void zoomabs(const Arg *);
|
static void zoomabs(const Arg *);
|
||||||
static void zoomreset(const Arg *);
|
static void zoomreset(const Arg *);
|
||||||
static void ttysend(const Arg *);
|
static void ttysend(const Arg *);
|
||||||
|
static void previewimage(const Arg *);
|
||||||
|
static void showimageinfo(const Arg *);
|
||||||
|
static void togglegrdebug(const Arg *);
|
||||||
|
static void dumpgrstate(const Arg *);
|
||||||
|
static void unloadimages(const Arg *);
|
||||||
|
static void toggleimages(const Arg *);
|
||||||
|
|
||||||
/* config.h for applying patches and the configuration. */
|
/* config.h for applying patches and the configuration. */
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
@@ -81,6 +90,7 @@ typedef XftGlyphFontSpec GlyphFontSpec;
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
int tw, th; /* tty width and height */
|
int tw, th; /* tty width and height */
|
||||||
int w, h; /* window width and height */
|
int w, h; /* window width and height */
|
||||||
|
int hborderpx, vborderpx;
|
||||||
int ch; /* char height */
|
int ch; /* char height */
|
||||||
int cw; /* char width */
|
int cw; /* char width */
|
||||||
int mode; /* window state/mode flags */
|
int mode; /* window state/mode flags */
|
||||||
@@ -144,6 +154,8 @@ static inline ushort sixd_to_16bit(int);
|
|||||||
static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
|
static int xmakeglyphfontspecs(XftGlyphFontSpec *, const Glyph *, int, int, int);
|
||||||
static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
|
static void xdrawglyphfontspecs(const XftGlyphFontSpec *, Glyph, int, int, int);
|
||||||
static void xdrawglyph(Glyph, int, int);
|
static void xdrawglyph(Glyph, int, int);
|
||||||
|
static void xdrawimages(Glyph, Line, int x1, int y1, int x2);
|
||||||
|
static void xdrawoneimagecell(Glyph, int x, int y);
|
||||||
static void xclear(int, int, int, int);
|
static void xclear(int, int, int, int);
|
||||||
static int xgeommasktogravity(int);
|
static int xgeommasktogravity(int);
|
||||||
static int ximopen(Display *);
|
static int ximopen(Display *);
|
||||||
@@ -220,6 +232,7 @@ static DC dc;
|
|||||||
static XWindow xw;
|
static XWindow xw;
|
||||||
static XSelection xsel;
|
static XSelection xsel;
|
||||||
static TermWindow win;
|
static TermWindow win;
|
||||||
|
static unsigned int mouse_col = 0, mouse_row = 0;
|
||||||
|
|
||||||
/* Font Ring Cache */
|
/* Font Ring Cache */
|
||||||
enum {
|
enum {
|
||||||
@@ -328,10 +341,72 @@ ttysend(const Arg *arg)
|
|||||||
ttywrite(arg->s, strlen(arg->s), 1);
|
ttywrite(arg->s, strlen(arg->s), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
previewimage(const Arg *arg)
|
||||||
|
{
|
||||||
|
Glyph g = getglyphat(mouse_col, mouse_row);
|
||||||
|
if (g.mode & ATTR_IMAGE) {
|
||||||
|
uint32_t image_id = tgetimgid(&g);
|
||||||
|
fprintf(stderr, "Clicked on placeholder %u/%u, x=%d, y=%d\n",
|
||||||
|
image_id, tgetimgplacementid(&g), tgetimgcol(&g),
|
||||||
|
tgetimgrow(&g));
|
||||||
|
gr_preview_image(image_id, arg->s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
showimageinfo(const Arg *arg)
|
||||||
|
{
|
||||||
|
Glyph g = getglyphat(mouse_col, mouse_row);
|
||||||
|
if (g.mode & ATTR_IMAGE) {
|
||||||
|
uint32_t image_id = tgetimgid(&g);
|
||||||
|
fprintf(stderr, "Clicked on placeholder %u/%u, x=%d, y=%d\n",
|
||||||
|
image_id, tgetimgplacementid(&g), tgetimgcol(&g),
|
||||||
|
tgetimgrow(&g));
|
||||||
|
char stcommand[256] = {0};
|
||||||
|
size_t len = snprintf(stcommand, sizeof(stcommand), "%s -e less", argv0);
|
||||||
|
if (len > sizeof(stcommand) - 1) {
|
||||||
|
fprintf(stderr, "Executable name too long: %s\n",
|
||||||
|
argv0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gr_show_image_info(image_id, tgetimgplacementid(&g),
|
||||||
|
tgetimgcol(&g), tgetimgrow(&g),
|
||||||
|
tgetisclassicplaceholder(&g),
|
||||||
|
tgetimgdiacriticcount(&g), argv0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
togglegrdebug(const Arg *arg)
|
||||||
|
{
|
||||||
|
graphics_debug_mode = (graphics_debug_mode + 1) % 3;
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dumpgrstate(const Arg *arg)
|
||||||
|
{
|
||||||
|
gr_dump_state();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
unloadimages(const Arg *arg)
|
||||||
|
{
|
||||||
|
gr_unload_images_to_reduce_ram();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
toggleimages(const Arg *arg)
|
||||||
|
{
|
||||||
|
graphics_display_images = !graphics_display_images;
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
evcol(XEvent *e)
|
evcol(XEvent *e)
|
||||||
{
|
{
|
||||||
int x = e->xbutton.x - borderpx;
|
int x = e->xbutton.x - win.hborderpx;
|
||||||
LIMIT(x, 0, win.tw - 1);
|
LIMIT(x, 0, win.tw - 1);
|
||||||
return x / win.cw;
|
return x / win.cw;
|
||||||
}
|
}
|
||||||
@@ -339,7 +414,7 @@ evcol(XEvent *e)
|
|||||||
int
|
int
|
||||||
evrow(XEvent *e)
|
evrow(XEvent *e)
|
||||||
{
|
{
|
||||||
int y = e->xbutton.y - borderpx;
|
int y = e->xbutton.y - win.vborderpx;
|
||||||
LIMIT(y, 0, win.th - 1);
|
LIMIT(y, 0, win.th - 1);
|
||||||
return y / win.ch;
|
return y / win.ch;
|
||||||
}
|
}
|
||||||
@@ -452,6 +527,9 @@ mouseaction(XEvent *e, uint release)
|
|||||||
/* ignore Button<N>mask for Button<N> - it's set on release */
|
/* ignore Button<N>mask for Button<N> - it's set on release */
|
||||||
uint state = e->xbutton.state & ~buttonmask(e->xbutton.button);
|
uint state = e->xbutton.state & ~buttonmask(e->xbutton.button);
|
||||||
|
|
||||||
|
mouse_col = evcol(e);
|
||||||
|
mouse_row = evrow(e);
|
||||||
|
|
||||||
for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) {
|
for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) {
|
||||||
if (ms->release == release &&
|
if (ms->release == release &&
|
||||||
ms->button == e->xbutton.button &&
|
ms->button == e->xbutton.button &&
|
||||||
@@ -739,6 +817,9 @@ cresize(int width, int height)
|
|||||||
col = MAX(1, col);
|
col = MAX(1, col);
|
||||||
row = MAX(1, row);
|
row = MAX(1, row);
|
||||||
|
|
||||||
|
win.hborderpx = (win.w - col * win.cw) * anysize_halign / 100;
|
||||||
|
win.vborderpx = (win.h - row * win.ch) * anysize_valign / 100;
|
||||||
|
|
||||||
tresize(col, row);
|
tresize(col, row);
|
||||||
xresize(col, row);
|
xresize(col, row);
|
||||||
ttyresize(win.tw, win.th);
|
ttyresize(win.tw, win.th);
|
||||||
@@ -869,8 +950,8 @@ xhints(void)
|
|||||||
sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize;
|
sizeh->flags = PSize | PResizeInc | PBaseSize | PMinSize;
|
||||||
sizeh->height = win.h;
|
sizeh->height = win.h;
|
||||||
sizeh->width = win.w;
|
sizeh->width = win.w;
|
||||||
sizeh->height_inc = win.ch;
|
sizeh->height_inc = 1;
|
||||||
sizeh->width_inc = win.cw;
|
sizeh->width_inc = 1;
|
||||||
sizeh->base_height = 2 * borderpx;
|
sizeh->base_height = 2 * borderpx;
|
||||||
sizeh->base_width = 2 * borderpx;
|
sizeh->base_width = 2 * borderpx;
|
||||||
sizeh->min_height = win.ch + 2 * borderpx;
|
sizeh->min_height = win.ch + 2 * borderpx;
|
||||||
@@ -1014,6 +1095,7 @@ xloadfonts(const char *fontstr, double fontsize)
|
|||||||
FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12);
|
FcPatternAddDouble(pattern, FC_PIXEL_SIZE, 12);
|
||||||
usedfontsize = 12;
|
usedfontsize = 12;
|
||||||
}
|
}
|
||||||
|
if (defaultfontsize <= 0)
|
||||||
defaultfontsize = usedfontsize;
|
defaultfontsize = usedfontsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1024,7 +1106,7 @@ xloadfonts(const char *fontstr, double fontsize)
|
|||||||
FcPatternGetDouble(dc.font.match->pattern,
|
FcPatternGetDouble(dc.font.match->pattern,
|
||||||
FC_PIXEL_SIZE, 0, &fontval);
|
FC_PIXEL_SIZE, 0, &fontval);
|
||||||
usedfontsize = fontval;
|
usedfontsize = fontval;
|
||||||
if (fontsize == 0)
|
if (defaultfontsize <= 0 && fontsize == 0)
|
||||||
defaultfontsize = fontval;
|
defaultfontsize = fontval;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1152,8 +1234,8 @@ xinit(int cols, int rows)
|
|||||||
xloadcols();
|
xloadcols();
|
||||||
|
|
||||||
/* adjust fixed window geometry */
|
/* adjust fixed window geometry */
|
||||||
win.w = 2 * borderpx + cols * win.cw;
|
win.w = 2 * win.hborderpx + 2 * borderpx + cols * win.cw;
|
||||||
win.h = 2 * borderpx + rows * win.ch;
|
win.h = 2 * win.vborderpx + 2 * borderpx + rows * win.ch;
|
||||||
if (xw.gm & XNegative)
|
if (xw.gm & XNegative)
|
||||||
xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2;
|
xw.l += DisplayWidth(xw.dpy, xw.scr) - win.w - 2;
|
||||||
if (xw.gm & YNegative)
|
if (xw.gm & YNegative)
|
||||||
@@ -1240,12 +1322,15 @@ xinit(int cols, int rows)
|
|||||||
xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0);
|
xsel.xtarget = XInternAtom(xw.dpy, "UTF8_STRING", 0);
|
||||||
if (xsel.xtarget == None)
|
if (xsel.xtarget == None)
|
||||||
xsel.xtarget = XA_STRING;
|
xsel.xtarget = XA_STRING;
|
||||||
|
|
||||||
|
// Initialize the graphics (image display) module.
|
||||||
|
gr_init(xw.dpy, xw.vis, xw.cmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
|
xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x, int y)
|
||||||
{
|
{
|
||||||
float winx = borderpx + x * win.cw, winy = borderpx + y * win.ch, xp, yp;
|
float winx = win.hborderpx + x * win.cw, winy = win.vborderpx + y * win.ch, xp, yp;
|
||||||
ushort mode, prevmode = USHRT_MAX;
|
ushort mode, prevmode = USHRT_MAX;
|
||||||
Font *font = &dc.font;
|
Font *font = &dc.font;
|
||||||
int frcflags = FRC_NORMAL;
|
int frcflags = FRC_NORMAL;
|
||||||
@@ -1267,6 +1352,11 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
|
|||||||
if (mode == ATTR_WDUMMY)
|
if (mode == ATTR_WDUMMY)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
/* Draw spaces for image placeholders (images will be drawn
|
||||||
|
* separately). */
|
||||||
|
if (mode & ATTR_IMAGE)
|
||||||
|
rune = ' ';
|
||||||
|
|
||||||
/* Determine font for glyph if different from previous glyph. */
|
/* Determine font for glyph if different from previous glyph. */
|
||||||
if (prevmode != mode) {
|
if (prevmode != mode) {
|
||||||
prevmode = mode;
|
prevmode = mode;
|
||||||
@@ -1374,11 +1464,61 @@ xmakeglyphfontspecs(XftGlyphFontSpec *specs, const Glyph *glyphs, int len, int x
|
|||||||
return numspecs;
|
return numspecs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Draws a horizontal dashed line of length `w` starting at `(x, y)`. `wavelen`
|
||||||
|
* is the length of the dash plus the length of the gap. `fraction` is the
|
||||||
|
* fraction of the dash length compared to `wavelen`. */
|
||||||
|
static void
|
||||||
|
xdrawunderdashed(Draw draw, Color *color, int x, int y, int w,
|
||||||
|
int wavelen, float fraction, int thick)
|
||||||
|
{
|
||||||
|
int dashw = MAX(1, fraction * wavelen);
|
||||||
|
for (int i = x - x % wavelen; i < x + w; i += wavelen) {
|
||||||
|
int startx = MAX(i, x);
|
||||||
|
int endx = MIN(i + dashw, x + w);
|
||||||
|
if (startx < endx)
|
||||||
|
XftDrawRect(xw.draw, color, startx, y, endx - startx,
|
||||||
|
thick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draws an undercurl. `h` is the total height, including line thickness. */
|
||||||
|
static void
|
||||||
|
xdrawundercurl(Draw draw, Color *color, int x, int y, int w, int h, int thick)
|
||||||
|
{
|
||||||
|
XGCValues gcvals = {.foreground = color->pixel,
|
||||||
|
.line_width = thick,
|
||||||
|
.line_style = LineSolid,
|
||||||
|
.cap_style = CapRound};
|
||||||
|
GC gc = XCreateGC(xw.dpy, XftDrawDrawable(xw.draw),
|
||||||
|
GCForeground | GCLineWidth | GCLineStyle | GCCapStyle,
|
||||||
|
&gcvals);
|
||||||
|
|
||||||
|
XRectangle clip = {.x = x, .y = y, .width = w, .height = h};
|
||||||
|
XSetClipRectangles(xw.dpy, gc, 0, 0, &clip, 1, Unsorted);
|
||||||
|
|
||||||
|
int yoffset = thick / 2;
|
||||||
|
int segh = MAX(1, h - thick);
|
||||||
|
/* Make sure every segment is at a 45 degree angle, otherwise it doesn't
|
||||||
|
* look good without antialiasing. */
|
||||||
|
int segw = segh;
|
||||||
|
int wavelen = MAX(1, segw * 2);
|
||||||
|
|
||||||
|
for (int i = x - (x % wavelen); i < x + w; i += wavelen) {
|
||||||
|
XPoint points[3] = {{.x = i, .y = y + yoffset},
|
||||||
|
{.x = i + segw, .y = y + yoffset + segh},
|
||||||
|
{.x = i + wavelen, .y = y + yoffset}};
|
||||||
|
XDrawLines(xw.dpy, XftDrawDrawable(xw.draw), gc, points, 3,
|
||||||
|
CoordModeOrigin);
|
||||||
|
}
|
||||||
|
|
||||||
|
XFreeGC(xw.dpy, gc);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
|
xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, int y)
|
||||||
{
|
{
|
||||||
int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
|
int charlen = len * ((base.mode & ATTR_WIDE) ? 2 : 1);
|
||||||
int winx = borderpx + x * win.cw, winy = borderpx + y * win.ch,
|
int winx = win.hborderpx + x * win.cw, winy = win.vborderpx + y * win.ch,
|
||||||
width = charlen * win.cw;
|
width = charlen * win.cw;
|
||||||
Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
|
Color *fg, *bg, *temp, revfg, revbg, truefg, truebg;
|
||||||
XRenderColor colfg, colbg;
|
XRenderColor colfg, colbg;
|
||||||
@@ -1468,17 +1608,17 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
|
|||||||
|
|
||||||
/* Intelligent cleaning up of the borders. */
|
/* Intelligent cleaning up of the borders. */
|
||||||
if (x == 0) {
|
if (x == 0) {
|
||||||
xclear(0, (y == 0)? 0 : winy, borderpx,
|
xclear(0, (y == 0)? 0 : winy, win.hborderpx,
|
||||||
winy + win.ch +
|
winy + win.ch +
|
||||||
((winy + win.ch >= borderpx + win.th)? win.h : 0));
|
((winy + win.ch >= win.vborderpx + win.th)? win.h : 0));
|
||||||
}
|
}
|
||||||
if (winx + width >= borderpx + win.tw) {
|
if (winx + width >= win.hborderpx + win.tw) {
|
||||||
xclear(winx + width, (y == 0)? 0 : winy, win.w,
|
xclear(winx + width, (y == 0)? 0 : winy, win.w,
|
||||||
((winy + win.ch >= borderpx + win.th)? win.h : (winy + win.ch)));
|
((winy + win.ch >= win.vborderpx + win.th)? win.h : (winy + win.ch)));
|
||||||
}
|
}
|
||||||
if (y == 0)
|
if (y == 0)
|
||||||
xclear(winx, 0, winx + width, borderpx);
|
xclear(winx, 0, winx + width, win.vborderpx);
|
||||||
if (winy + win.ch >= borderpx + win.th)
|
if (winy + win.ch >= win.vborderpx + win.th)
|
||||||
xclear(winx, winy + win.ch, winx + width, win.h);
|
xclear(winx, winy + win.ch, winx + width, win.h);
|
||||||
|
|
||||||
/* Clean up the region we want to draw to. */
|
/* Clean up the region we want to draw to. */
|
||||||
@@ -1491,18 +1631,68 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
|
|||||||
r.width = width;
|
r.width = width;
|
||||||
XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1);
|
XftDrawSetClipRectangles(xw.draw, winx, winy, &r, 1);
|
||||||
|
|
||||||
|
/* Decoration color. */
|
||||||
|
Color decor;
|
||||||
|
uint32_t decorcolor = tgetdecorcolor(&base);
|
||||||
|
if (decorcolor == DECOR_DEFAULT_COLOR) {
|
||||||
|
decor = *fg;
|
||||||
|
} else if (IS_TRUECOL(decorcolor)) {
|
||||||
|
colfg.alpha = 0xffff;
|
||||||
|
colfg.red = TRUERED(decorcolor);
|
||||||
|
colfg.green = TRUEGREEN(decorcolor);
|
||||||
|
colfg.blue = TRUEBLUE(decorcolor);
|
||||||
|
XftColorAllocValue(xw.dpy, xw.vis, xw.cmap, &colfg, &decor);
|
||||||
|
} else {
|
||||||
|
decor = dc.col[decorcolor];
|
||||||
|
}
|
||||||
|
decor.color.alpha = 0xffff;
|
||||||
|
decor.pixel |= 0xff << 24;
|
||||||
|
|
||||||
|
/* Float thickness, used as a base to compute other values. */
|
||||||
|
float fthick = dc.font.height / 18.0;
|
||||||
|
/* Integer thickness in pixels. Must not be 0. */
|
||||||
|
int thick = MAX(1, roundf(fthick));
|
||||||
|
/* The default gap between the baseline and a single underline. */
|
||||||
|
int gap = roundf(fthick * 2);
|
||||||
|
/* The total thickness of a double underline. */
|
||||||
|
int doubleh = thick * 2 + ceilf(fthick * 0.5);
|
||||||
|
/* The total thickness of an undercurl. */
|
||||||
|
int curlh = thick * 2 + roundf(fthick * 0.75);
|
||||||
|
|
||||||
|
/* Render the underline before the glyphs. */
|
||||||
|
if (base.mode & ATTR_UNDERLINE) {
|
||||||
|
uint32_t style = tgetdecorstyle(&base);
|
||||||
|
int liney = winy + dc.font.ascent + gap;
|
||||||
|
/* Adjust liney to guarantee that a single underline fits. */
|
||||||
|
liney -= MAX(0, liney + thick - (winy + win.ch));
|
||||||
|
if (style == UNDERLINE_DOUBLE) {
|
||||||
|
liney -= MAX(0, liney + doubleh - (winy + win.ch));
|
||||||
|
XftDrawRect(xw.draw, &decor, winx, liney, width, thick);
|
||||||
|
XftDrawRect(xw.draw, &decor, winx,
|
||||||
|
liney + doubleh - thick, width, thick);
|
||||||
|
} else if (style == UNDERLINE_DOTTED) {
|
||||||
|
xdrawunderdashed(xw.draw, &decor, winx, liney, width,
|
||||||
|
thick * 2, 0.5, thick);
|
||||||
|
} else if (style == UNDERLINE_DASHED) {
|
||||||
|
int wavelen = MAX(2, win.cw * 0.9);
|
||||||
|
xdrawunderdashed(xw.draw, &decor, winx, liney, width,
|
||||||
|
wavelen, 0.65, thick);
|
||||||
|
} else if (style == UNDERLINE_CURLY) {
|
||||||
|
liney -= MAX(0, liney + curlh - (winy + win.ch));
|
||||||
|
xdrawundercurl(xw.draw, &decor, winx, liney, width,
|
||||||
|
curlh, thick);
|
||||||
|
} else {
|
||||||
|
XftDrawRect(xw.draw, &decor, winx, liney, width, thick);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Render the glyphs. */
|
/* Render the glyphs. */
|
||||||
XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
|
XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
|
||||||
|
|
||||||
/* Render underline and strikethrough. */
|
/* Render strikethrough. Alway use the fg color. */
|
||||||
if (base.mode & ATTR_UNDERLINE) {
|
|
||||||
XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent * chscale + 1,
|
|
||||||
width, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (base.mode & ATTR_STRUCK) {
|
if (base.mode & ATTR_STRUCK) {
|
||||||
XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent * chscale / 3,
|
XftDrawRect(xw.draw, fg, winx, winy + 2 * dc.font.ascent / 3,
|
||||||
width, 1);
|
width, thick);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reset clip to none. */
|
/* Reset clip to none. */
|
||||||
@@ -1517,6 +1707,11 @@ xdrawglyph(Glyph g, int x, int y)
|
|||||||
|
|
||||||
numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
|
numspecs = xmakeglyphfontspecs(&spec, &g, 1, x, y);
|
||||||
xdrawglyphfontspecs(&spec, g, numspecs, x, y);
|
xdrawglyphfontspecs(&spec, g, numspecs, x, y);
|
||||||
|
if (g.mode & ATTR_IMAGE) {
|
||||||
|
gr_start_drawing(xw.buf, win.cw, win.ch);
|
||||||
|
xdrawoneimagecell(g, x, y);
|
||||||
|
gr_finish_drawing(xw.buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1532,6 +1727,10 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
|
|||||||
if (IS_SET(MODE_HIDE))
|
if (IS_SET(MODE_HIDE))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// If it's an image, just draw a ballot box for simplicity.
|
||||||
|
if (g.mode & ATTR_IMAGE)
|
||||||
|
g.u = 0x2610;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Select the right color for the right mode.
|
* Select the right color for the right mode.
|
||||||
*/
|
*/
|
||||||
@@ -1572,39 +1771,167 @@ xdrawcursor(int cx, int cy, Glyph g, int ox, int oy, Glyph og)
|
|||||||
case 3: /* Blinking Underline */
|
case 3: /* Blinking Underline */
|
||||||
case 4: /* Steady Underline */
|
case 4: /* Steady Underline */
|
||||||
XftDrawRect(xw.draw, &drawcol,
|
XftDrawRect(xw.draw, &drawcol,
|
||||||
borderpx + cx * win.cw,
|
win.hborderpx + cx * win.cw,
|
||||||
borderpx + (cy + 1) * win.ch - \
|
win.vborderpx + (cy + 1) * win.ch - \
|
||||||
cursorthickness,
|
cursorthickness,
|
||||||
win.cw, cursorthickness);
|
win.cw, cursorthickness);
|
||||||
break;
|
break;
|
||||||
case 5: /* Blinking bar */
|
case 5: /* Blinking bar */
|
||||||
case 6: /* Steady bar */
|
case 6: /* Steady bar */
|
||||||
XftDrawRect(xw.draw, &drawcol,
|
XftDrawRect(xw.draw, &drawcol,
|
||||||
borderpx + cx * win.cw,
|
win.hborderpx + cx * win.cw,
|
||||||
borderpx + cy * win.ch,
|
win.vborderpx + cy * win.ch,
|
||||||
cursorthickness, win.ch);
|
cursorthickness, win.ch);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
XftDrawRect(xw.draw, &drawcol,
|
XftDrawRect(xw.draw, &drawcol,
|
||||||
borderpx + cx * win.cw,
|
win.hborderpx + cx * win.cw,
|
||||||
borderpx + cy * win.ch,
|
win.vborderpx + cy * win.ch,
|
||||||
win.cw - 1, 1);
|
win.cw - 1, 1);
|
||||||
XftDrawRect(xw.draw, &drawcol,
|
XftDrawRect(xw.draw, &drawcol,
|
||||||
borderpx + cx * win.cw,
|
win.hborderpx + cx * win.cw,
|
||||||
borderpx + cy * win.ch,
|
win.vborderpx + cy * win.ch,
|
||||||
1, win.ch - 1);
|
1, win.ch - 1);
|
||||||
XftDrawRect(xw.draw, &drawcol,
|
XftDrawRect(xw.draw, &drawcol,
|
||||||
borderpx + (cx + 1) * win.cw - 1,
|
win.hborderpx + (cx + 1) * win.cw - 1,
|
||||||
borderpx + cy * win.ch,
|
win.vborderpx + cy * win.ch,
|
||||||
1, win.ch - 1);
|
1, win.ch - 1);
|
||||||
XftDrawRect(xw.draw, &drawcol,
|
XftDrawRect(xw.draw, &drawcol,
|
||||||
borderpx + cx * win.cw,
|
win.hborderpx + cx * win.cw,
|
||||||
borderpx + (cy + 1) * win.ch - 1,
|
win.vborderpx + (cy + 1) * win.ch - 1,
|
||||||
win.cw, 1);
|
win.cw, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Draw (or queue for drawing) image cells between columns x1 and x2 assuming
|
||||||
|
* that they have the same attributes (and thus the same lower 24 bits of the
|
||||||
|
* image ID and the same placement ID). */
|
||||||
|
void
|
||||||
|
xdrawimages(Glyph base, Line line, int x1, int y1, int x2) {
|
||||||
|
int y_pix = win.vborderpx + y1 * win.ch;
|
||||||
|
uint32_t image_id_24bits = base.fg & 0xFFFFFF;
|
||||||
|
uint32_t placement_id = tgetimgplacementid(&base);
|
||||||
|
// Columns and rows are 1-based, 0 means unspecified.
|
||||||
|
int last_col = 0;
|
||||||
|
int last_row = 0;
|
||||||
|
int last_start_col = 0;
|
||||||
|
int last_start_x = x1;
|
||||||
|
// The most significant byte is also 1-base, subtract 1 before use.
|
||||||
|
uint32_t last_id_4thbyteplus1 = 0;
|
||||||
|
// We may need to inherit row/column/4th byte from the previous cell.
|
||||||
|
Glyph *prev = &line[x1 - 1];
|
||||||
|
if (x1 > 0 && (prev->mode & ATTR_IMAGE) &&
|
||||||
|
(prev->fg & 0xFFFFFF) == image_id_24bits &&
|
||||||
|
prev->decor == base.decor) {
|
||||||
|
last_row = tgetimgrow(prev);
|
||||||
|
last_col = tgetimgcol(prev);
|
||||||
|
last_id_4thbyteplus1 = tgetimgid4thbyteplus1(prev);
|
||||||
|
last_start_col = last_col + 1;
|
||||||
|
}
|
||||||
|
for (int x = x1; x < x2; ++x) {
|
||||||
|
Glyph *g = &line[x];
|
||||||
|
uint32_t cur_row = tgetimgrow(g);
|
||||||
|
uint32_t cur_col = tgetimgcol(g);
|
||||||
|
uint32_t cur_id_4thbyteplus1 = tgetimgid4thbyteplus1(g);
|
||||||
|
uint32_t num_diacritics = tgetimgdiacriticcount(g);
|
||||||
|
// If the row is not specified, assume it's the same as the row
|
||||||
|
// of the previous cell. Note that `cur_row` may contain a
|
||||||
|
// value imputed earlier, which will be preserved if `last_row`
|
||||||
|
// is zero (i.e. we don't know the row of the previous cell).
|
||||||
|
if (last_row && (num_diacritics == 0 || !cur_row))
|
||||||
|
cur_row = last_row;
|
||||||
|
// If the column is not specified and the row is the same as the
|
||||||
|
// row of the previous cell, then assume that the column is the
|
||||||
|
// next one.
|
||||||
|
if (last_col && (num_diacritics <= 1 || !cur_col) &&
|
||||||
|
cur_row == last_row)
|
||||||
|
cur_col = last_col + 1;
|
||||||
|
// If the additional id byte is not specified and the
|
||||||
|
// coordinates are consecutive, assume the byte is also the
|
||||||
|
// same.
|
||||||
|
if (last_id_4thbyteplus1 &&
|
||||||
|
(num_diacritics <= 2 || !cur_id_4thbyteplus1) &&
|
||||||
|
cur_row == last_row && cur_col == last_col + 1)
|
||||||
|
cur_id_4thbyteplus1 = last_id_4thbyteplus1;
|
||||||
|
// If we couldn't infer row and column, start from the top left
|
||||||
|
// corner.
|
||||||
|
if (cur_row == 0)
|
||||||
|
cur_row = 1;
|
||||||
|
if (cur_col == 0)
|
||||||
|
cur_col = 1;
|
||||||
|
// If this cell breaks a contiguous stripe of image cells, draw
|
||||||
|
// that line and start a new one.
|
||||||
|
if (cur_col != last_col + 1 || cur_row != last_row ||
|
||||||
|
cur_id_4thbyteplus1 != last_id_4thbyteplus1) {
|
||||||
|
uint32_t image_id = image_id_24bits;
|
||||||
|
if (last_id_4thbyteplus1)
|
||||||
|
image_id |= (last_id_4thbyteplus1 - 1) << 24;
|
||||||
|
if (last_row != 0) {
|
||||||
|
int x_pix =
|
||||||
|
win.hborderpx + last_start_x * win.cw;
|
||||||
|
gr_append_imagerect(
|
||||||
|
xw.buf, image_id, placement_id,
|
||||||
|
last_start_col - 1, last_col,
|
||||||
|
last_row - 1, last_row, last_start_x,
|
||||||
|
y1, x_pix, y_pix, win.cw, win.ch,
|
||||||
|
base.mode & ATTR_REVERSE);
|
||||||
|
}
|
||||||
|
last_start_col = cur_col;
|
||||||
|
last_start_x = x;
|
||||||
|
}
|
||||||
|
last_row = cur_row;
|
||||||
|
last_col = cur_col;
|
||||||
|
last_id_4thbyteplus1 = cur_id_4thbyteplus1;
|
||||||
|
// Populate the missing glyph data to enable inheritance between
|
||||||
|
// runs and support the naive implementation of tgetimgid.
|
||||||
|
if (!tgetimgrow(g))
|
||||||
|
tsetimgrow(g, cur_row);
|
||||||
|
// We cannot save this information if there are > 511 cols.
|
||||||
|
if (!tgetimgcol(g) && (cur_col & ~0x1ff) == 0)
|
||||||
|
tsetimgcol(g, cur_col);
|
||||||
|
if (!tgetimgid4thbyteplus1(g))
|
||||||
|
tsetimg4thbyteplus1(g, cur_id_4thbyteplus1);
|
||||||
|
}
|
||||||
|
uint32_t image_id = image_id_24bits;
|
||||||
|
if (last_id_4thbyteplus1)
|
||||||
|
image_id |= (last_id_4thbyteplus1 - 1) << 24;
|
||||||
|
// Draw the last contiguous stripe.
|
||||||
|
if (last_row != 0) {
|
||||||
|
int x_pix = win.hborderpx + last_start_x * win.cw;
|
||||||
|
gr_append_imagerect(xw.buf, image_id, placement_id,
|
||||||
|
last_start_col - 1, last_col, last_row - 1,
|
||||||
|
last_row, last_start_x, y1, x_pix, y_pix,
|
||||||
|
win.cw, win.ch, base.mode & ATTR_REVERSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draw just one image cell without inheriting attributes from the left. */
|
||||||
|
void xdrawoneimagecell(Glyph g, int x, int y) {
|
||||||
|
if (!(g.mode & ATTR_IMAGE))
|
||||||
|
return;
|
||||||
|
int x_pix = win.hborderpx + x * win.cw;
|
||||||
|
int y_pix = win.vborderpx + y * win.ch;
|
||||||
|
uint32_t row = tgetimgrow(&g) - 1;
|
||||||
|
uint32_t col = tgetimgcol(&g) - 1;
|
||||||
|
uint32_t placement_id = tgetimgplacementid(&g);
|
||||||
|
uint32_t image_id = tgetimgid(&g);
|
||||||
|
gr_append_imagerect(xw.buf, image_id, placement_id, col, col + 1, row,
|
||||||
|
row + 1, x, y, x_pix, y_pix, win.cw, win.ch,
|
||||||
|
g.mode & ATTR_REVERSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare for image drawing. */
|
||||||
|
void xstartimagedraw(int *dirty, int rows) {
|
||||||
|
gr_start_drawing(xw.buf, win.cw, win.ch);
|
||||||
|
gr_mark_dirty_animations(dirty, rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draw all queued image cells. */
|
||||||
|
void xfinishimagedraw() {
|
||||||
|
gr_finish_drawing(xw.buf);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
xsetenv(void)
|
xsetenv(void)
|
||||||
{
|
{
|
||||||
@@ -1671,6 +1998,8 @@ xdrawline(Line line, int x1, int y1, int x2)
|
|||||||
new.mode ^= ATTR_REVERSE;
|
new.mode ^= ATTR_REVERSE;
|
||||||
if (i > 0 && ATTRCMP(base, new)) {
|
if (i > 0 && ATTRCMP(base, new)) {
|
||||||
xdrawglyphfontspecs(specs, base, i, ox, y1);
|
xdrawglyphfontspecs(specs, base, i, ox, y1);
|
||||||
|
if (base.mode & ATTR_IMAGE)
|
||||||
|
xdrawimages(base, line, ox, y1, x);
|
||||||
specs += i;
|
specs += i;
|
||||||
numspecs -= i;
|
numspecs -= i;
|
||||||
i = 0;
|
i = 0;
|
||||||
@@ -1683,6 +2012,8 @@ xdrawline(Line line, int x1, int y1, int x2)
|
|||||||
}
|
}
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
xdrawglyphfontspecs(specs, base, i, ox, y1);
|
xdrawglyphfontspecs(specs, base, i, ox, y1);
|
||||||
|
if (i > 0 && base.mode & ATTR_IMAGE)
|
||||||
|
xdrawimages(base, line, ox, y1, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -1907,6 +2238,7 @@ cmessage(XEvent *e)
|
|||||||
}
|
}
|
||||||
} else if (e->xclient.data.l[0] == xw.wmdeletewin) {
|
} else if (e->xclient.data.l[0] == xw.wmdeletewin) {
|
||||||
ttyhangup();
|
ttyhangup();
|
||||||
|
gr_deinit();
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1957,6 +2289,13 @@ run(void)
|
|||||||
if (XPending(xw.dpy))
|
if (XPending(xw.dpy))
|
||||||
timeout = 0; /* existing events might not set xfd */
|
timeout = 0; /* existing events might not set xfd */
|
||||||
|
|
||||||
|
/* Decrease the timeout if there are active animations. */
|
||||||
|
if (graphics_next_redraw_delay != INT_MAX &&
|
||||||
|
IS_SET(MODE_VISIBLE))
|
||||||
|
timeout = timeout < 0 ? graphics_next_redraw_delay
|
||||||
|
: MIN(timeout,
|
||||||
|
graphics_next_redraw_delay);
|
||||||
|
|
||||||
seltv.tv_sec = timeout / 1E3;
|
seltv.tv_sec = timeout / 1E3;
|
||||||
seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec);
|
seltv.tv_nsec = 1E6 * (timeout - 1E3 * seltv.tv_sec);
|
||||||
tv = timeout >= 0 ? &seltv : NULL;
|
tv = timeout >= 0 ? &seltv : NULL;
|
||||||
|
|||||||
Reference in New Issue
Block a user