#!/usr/bin/env bash BASEDIR="$PWD" ENVDIR="$PWD/env" export GREP_OPTIONS= usage() { cat < [arguments] Commands: help This help text list List environments clear Delete all environment and revert to flat config/files new Create a new environment switch Switch to a different environment delete Delete an environment rename Rename the current environment diff Show differences between current state and environment save [message] Save your changes to the environment, optionally using the given commit message revert Revert your changes since last save Options: EOF exit "${1:-1}" } error() { echo "$0: $*" exit 1 } ask_bool() { local DEFAULT="$1"; shift local def defstr val case "$DEFAULT" in 1) def=0; defstr="Y/n";; 0) def=1; defstr="y/N";; *) def=; defstr="y/n";; esac while [ -z "$val" ]; do local VAL echo -n "$* ($defstr): " read -r VAL case "$VAL" in y*|Y*) val=0;; n*|N*) val=1;; *) val="$def";; esac done return "$val" } env_init() { local CREATE="$1" if [ -z "$CREATE" ]; then [ -d "$ENVDIR" ] || exit 0 fi command -v git >/dev/null || error "Git is not installed" mkdir -p "$ENVDIR" || error "Failed to create the environment directory" cd "$ENVDIR" || error "Failed to switch to the environment directory" [ -d .git ] || { git init && touch .config && mkdir files && git add . && git commit -q -m "Initial import" } || { rm -rf .git error "Failed to initialize the environment directory" } } env_sync_data() { [ ! -L "$BASEDIR/.config" ] && [ -f "$BASEDIR/.config" ] && mv "$BASEDIR/.config" "$ENVDIR" git add . git add -u } env_sync() { local STR="$1" env_sync_data git commit -m "${STR:-Update} at $(date)" } env_link_config() { rm -f "$BASEDIR/.config" ln -s env/.config "$BASEDIR/.config" mkdir -p "$ENVDIR/files" [ -L "$BASEDIR/files" ] || ln -s env/files "$BASEDIR/files" } env_do_reset() { git reset --hard HEAD git clean -d -f } env_list() { env_init git branch --color | grep -vE '^. master$' } env_diff() { env_init env_sync_data git diff --cached --color=auto env_link_config } env_save() { env_init env_sync "$@" env_link_config } env_revert() { env_init env_do_reset env_link_config } env_ask_sync() { env_sync_data LINES="$(env_diff | wc -l)" # implies env_init [ "$LINES" -gt 0 ] && { if ask_bool 1 "Do you want to save your changes"; then env_sync else env_do_reset fi } } env_clear() { env_init [ -L "$BASEDIR/.config" ] && rm -f "$BASEDIR/.config" [ -L "$BASEDIR/files" ] && rm -f "$BASEDIR/files" [ -f "$ENVDIR/.config" ] || ( cd "$ENVDIR/files" && find . | grep -vE '^\.$' > /dev/null ) env_sync_data if ask_bool 1 "Do you want to keep your current config and files"; then mkdir -p "$BASEDIR/files" shopt -s dotglob cp -a "$ENVDIR/files/"* "$BASEDIR/files" 2>/dev/null >/dev/null shopt -u dotglob cp "$ENVDIR/.config" "$BASEDIR/" else rm -rf "$BASEDIR/files" "$BASEDIR/.config" fi cd "$BASEDIR" || exit 1 rm -rf "$ENVDIR" } env_delete() { local name="${1##*/}" env_init [ -z "$name" ] && usage branch="$(git branch | grep '^\* ' | awk '{print $2}')" [ "$name" = "$branch" ] && error "cannot delete the currently selected environment" git branch -D "$name" } env_switch() { local name="${1##*/}" [ -z "$name" ] && usage env_init env_ask_sync git checkout "$name" || error "environment '$name' not found" env_link_config } env_rename() { local NAME="${1##*/}" env_init git branch -m "$NAME" } env_new() { local NAME="$1" local branch local from="master" [ -z "$NAME" ] && usage env_init 1 branch="$(git branch | grep '^\* ' | awk '{print $2}')" if [ -n "$branch" ] && [ "$branch" != "master" ]; then env_ask_sync if ask_bool 0 "Do you want to clone the current environment?"; then from="$branch" fi rm -f "$BASEDIR/.config" "$BASEDIR/files" fi git checkout -b "$1" "$from" if [ -f "$BASEDIR/.config" ] || [ -d "$BASEDIR/files" ]; then if ask_bool 1 "Do you want to start your configuration repository with the current configuration?"; then if [ -d "$BASEDIR/files" ] && [ ! -L "$BASEDIR/files" ]; then mkdir -p "$ENVDIR/files" shopt -s dotglob mv "$BASEDIR/files/"* "$ENVDIR/files/" 2>/dev/null shopt -u dotglob rmdir "$BASEDIR/files" fi env_sync else rm -rf "$BASEDIR/.config" "$BASEDIR/files" fi fi env_link_config } COMMAND="$1"; shift case "$COMMAND" in help) usage 0;; new) env_new "$@";; list) env_list "$@";; clear) env_clear "$@";; switch) env_switch "$@";; delete) env_delete "$@";; rename) env_rename "$@";; diff) env_diff "$@";; save) env_save "$@";; revert) env_revert "$@";; *) usage;; esac id='n50' href='#n50'>50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
#define CONCAT_I(a, b) a ## b
#define CONCAT(a, b) CONCAT_I(a, b)
#define pixel_t CONCAT(uint, CONCAT(BPP, _t))

static void CONCAT(send_hextile_tile_, BPP)(VncState *vs,
					    int x, int y, int w, int h,
					    pixel_t *last_bg, pixel_t *last_fg,
					    int *has_bg, int *has_fg)
{
    char *row = (vs->ds->data + y * vs->ds->linesize + x * vs->depth);
    pixel_t *irow = (pixel_t *)row;
    int j, i;
    pixel_t bg = 0;
    pixel_t fg = 0;
    int n_colors = 0;
    int bg_count = 0;
    int fg_count = 0;
    int flags = 0;
    uint8_t data[(sizeof(pixel_t) + 2) * 16 * 16];
    int n_data = 0;
    int n_subtiles = 0;

    for (j = 0; j < h; j++) {
	for (i = 0; i < w; i++) {
	    switch (n_colors) {
	    case 0:
		bg = irow[i];
		n_colors = 1;
		break;
	    case 1:
		if (irow[i] != bg) {
		    fg = irow[i];
		    n_colors = 2;
		}
		break;
	    case 2:
		if (irow[i] != bg && irow[i] != fg) {
		    n_colors = 3;
		} else {
		    if (irow[i] == bg)
			bg_count++;
		    else if (irow[i] == fg)
			fg_count++;
		}
		break;
	    default:
		break;
	    }
	}
	if (n_colors > 2)
	    break;
	irow += vs->ds->linesize / sizeof(pixel_t);
    }

    if (n_colors > 1 && fg_count > bg_count) {
	pixel_t tmp = fg;
	fg = bg;
	bg = tmp;
    }

    if (!*has_bg || *last_bg != bg) {
	flags |= 0x02;
	*has_bg = 1;
	*last_bg = bg;
    }

    if (!*has_fg || *last_fg != fg) {
	flags |= 0x04;
	*has_fg = 1;
	*last_fg = fg;
    }

    switch (n_colors) {
    case 1:
	n_data = 0;
	break;
    case 2:
	flags |= 0x08;

	irow = (pixel_t *)row;
	
	for (j = 0; j < h; j++) {
	    int min_x = -1;
	    for (i = 0; i < w; i++) {
		if (irow[i] == fg) {
		    if (min_x == -1)
			min_x = i;
		} else if (min_x != -1) {
		    hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1);
		    n_data += 2;
		    n_subtiles++;
		    min_x = -1;
		}
	    }
	    if (min_x != -1) {
		hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1);
		n_data += 2;
		n_subtiles++;
	    }
	    irow += vs->ds->linesize / sizeof(pixel_t);
	}
	break;
    case 3:
	flags |= 0x18;

	irow = (pixel_t *)row;

	if (!*has_bg || *last_bg != bg)
	    flags |= 0x02;

	for (j = 0; j < h; j++) {
	    int has_color = 0;
	    int min_x = -1;
	    pixel_t color;

	    for (i = 0; i < w; i++) {
		if (!has_color) {
		    if (irow[i] == bg)
			continue;
		    color = irow[i];
		    min_x = i;
		    has_color = 1;
		} else if (irow[i] != color) {
		    has_color = 0;

		    memcpy(data + n_data, &color, sizeof(color));
		    hextile_enc_cord(data + n_data + sizeof(pixel_t), min_x, j, i - min_x, 1);
		    n_data += 2 + sizeof(pixel_t);
		    n_subtiles++;

		    min_x = -1;
		    if (irow[i] != bg) {
			color = irow[i];
			min_x = i;
			has_color = 1;
		    }
		}
	    }
	    if (has_color) {
		memcpy(data + n_data, &color, sizeof(color));
		hextile_enc_cord(data + n_data + sizeof(pixel_t), min_x, j, i - min_x, 1);
		n_data += 2 + sizeof(pixel_t);
		n_subtiles++;
	    }
	    irow += vs->ds->linesize / sizeof(pixel_t);
	}

	/* A SubrectsColoured subtile invalidates the foreground color */
	*has_fg = 0;
	if (n_data > (w * h * sizeof(pixel_t))) {
	    n_colors = 4;
	    flags = 0x01;
	    *has_bg = 0;

	    /* we really don't have to invalidate either the bg or fg
	       but we've lost the old values.  oh well. */
	}
    default:
	break;
    }

    if (n_colors > 3) {
	flags = 0x01;
	*has_fg = 0;
	*has_bg = 0;
	n_colors = 4;
    }

    vnc_write_u8(vs, flags);
    if (n_colors < 4) {
	if (flags & 0x02)
	    vnc_write(vs, last_bg, sizeof(pixel_t));
	if (flags & 0x04)
	    vnc_write(vs, last_fg, sizeof(pixel_t));
	if (n_subtiles) {
	    vnc_write_u8(vs, n_subtiles);
	    vnc_write(vs, data, n_data);
	}
    } else {
	for (j = 0; j < h; j++) {
	    vnc_write(vs, row, w * vs->depth);
	    row += vs->ds->linesize;
	}
    }
}

#undef pixel_t
#undef CONCAT_I
#undef CONCAT