about summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/Zle/zle_refresh.c214
-rw-r--r--Src/init.c8
-rw-r--r--Src/zsh.h59
3 files changed, 219 insertions, 62 deletions
diff --git a/Src/Zle/zle_refresh.c b/Src/Zle/zle_refresh.c
index 798541646..3967b110d 100644
--- a/Src/Zle/zle_refresh.c
+++ b/Src/Zle/zle_refresh.c
@@ -207,7 +207,7 @@ int predisplaylen, postdisplaylen;
  * displayed on screen.
  */
 
-static int special_atr_on, special_atr_off;
+static int special_atr_on;
 
 /* Flags for the region_highlight structure */
 enum {
@@ -357,19 +357,41 @@ match_highlight(const char *teststr, int *on_var)
 	const struct highlight *hl;
 
 	found = 0;
-	for (hl = highlights; hl->name; hl++) {
-	    if (strpfx(hl->name, teststr)) {
-		const char *val = teststr + strlen(hl->name);
+	if (strpfx("fg=", teststr) || strpfx("bg=", teststr)) {
+	    int is_fg = (teststr[0] == 'f');
+	    int colour = (int)zstrtol(teststr+3, (char **)&teststr, 10);
+	    int shft, on;
+	    if (*teststr == ',')
+		teststr++;
+	    else if (*teststr)
+		break;
+	    found = 1;
+	    /* skip out of range colours but keep scanning attributes */
+	    if (colour >= 256)
+		continue;
+	    if (is_fg) {
+		shft = TXT_ATTR_FG_COL_SHIFT;
+		on = TXTFGCOLOUR;
+	    } else {
+		shft = TXT_ATTR_BG_COL_SHIFT;
+		on = TXTBGCOLOUR;
+	    }
+	    *on_var |= on | (colour << shft);
+	} else {
+	    for (hl = highlights; hl->name; hl++) {
+		if (strpfx(hl->name, teststr)) {
+		    const char *val = teststr + strlen(hl->name);
 
-		if (*val == ',')
-		    val++;
-		else if (*val)
-		    break;
+		    if (*val == ',')
+			val++;
+		    else if (*val)
+			break;
 
-		*on_var |= hl->mask_on;
-		*on_var &= ~hl->mask_off;
-		teststr = val;
-		found = 1;
+		    *on_var |= hl->mask_on;
+		    *on_var &= ~hl->mask_off;
+		    teststr = val;
+		    found = 1;
+		}
 	    }
 	}
     }
@@ -431,7 +453,6 @@ void zle_set_highlight(void)
 	region_highlights->atr = TXTSTANDOUT;
     if (!isearch_atr_on_set)
 	region_highlights[1].atr = TXTUNDERLINE;
-    special_atr_off = special_atr_on << TXT_ATTR_OFF_ON_SHIFT;
 }
 
 
@@ -610,19 +631,23 @@ zwcputc(const REFRESH_ELEMENT *c, int *curatrp)
 
     if (lastatr & ~c->atr) {
 	/* Stuff on we don't want, turn it off */
-	settextattributes((lastatr & ~c->atr) << TXT_ATTR_OFF_ON_SHIFT);
+	settextattributes(TXT_ATTR_OFF_FROM_ON(lastatr & ~c->atr));
 	lastatr = 0;
     }
 
     /*
      * Don't output "on" attributes in a string of characters with
-     * the same attributes.
+     * the same attributes.  Be careful in case a different colour
+     * needs setting.
      */
     if ((c->atr & TXT_ATTR_ON_MASK) &&
 	(!curatrp ||
-	 ((*curatrp & TXT_ATTR_ON_MASK) != (c->atr & TXT_ATTR_ON_MASK)))) {
+	 ((*curatrp & TXT_ATTR_ON_VALUES_MASK) !=
+	  (c->atr & TXT_ATTR_ON_VALUES_MASK)))) {
+	/* Record just the control flags we might need to turn off... */
 	lastatr = c->atr & TXT_ATTR_ON_MASK;
-	settextattributes(lastatr);
+	/* ...but set including the values for colour attributes */
+	settextattributes(c->atr & TXT_ATTR_ON_VALUES_MASK);
     }
 
 #ifdef MULTIBYTE_SUPPORT
@@ -656,9 +681,11 @@ zwcputc(const REFRESH_ELEMENT *c, int *curatrp)
     if (curatrp) {
 	/*
 	 * Remember the current attributes:  those that are turned
-	 * on, less those that are turned off again.
+	 * on, less those that are turned off again.  Include
+	 * colour attributes here in case the colour changes to
+	 * another non-default one.
 	 */
-	*curatrp = (c->atr & TXT_ATTR_ON_MASK) &
+	*curatrp = (c->atr & TXT_ATTR_ON_VALUES_MASK) &
 	    ~((c->atr & TXT_ATTR_OFF_MASK) >> TXT_ATTR_OFF_ON_SHIFT);
     }
 }
@@ -915,6 +942,54 @@ snextline(Rparams rpms)
     rpms->sen = rpms->s + winw;
 }
 
+/*
+ * HERE: these need to be made configurable, somehow.
+ * Ideally we need to make the complist stuff use the
+ * same system, but that may be too much tied to the GNU ls
+ * interface to make that possible.
+ */
+/* Start of escape sequence for foreground colour */
+#define TC_COL_FG_START	"\033[3"
+/* Start of escape sequence for background colour */
+#define TC_COL_BG_START	"\033[4"
+/* End of either escape sequence */
+#define TC_COL_END	"m"
+/* Numeric code (to be turned into ASCII) to reset default colour */
+#define TC_COL_DEFAULT	9
+
+static void
+setcolourattribute(int colour, char *start, int tc, int def,
+		   int use_termcap)
+{
+    char out[16], *ptr;
+    /*
+     * If we're not restoring the default, and either have a
+     * colour value that is too large for ANSI, or have been told
+     * to use the termcap sequence (which at the time of writing
+     * we never are), try to use the termcap sequence.
+     */
+    if (!def && (colour > 7 || use_termcap)) {
+	/*
+	 * We can if it's available, and either we couldn't get
+	 * the maximum number of colours, or the colour is in range.
+	 */
+	if (tccan(tc) && (tccolours < 0 || colour < tccolours))
+	    tcoutarg(tc, colour);
+	/* for 0 to 7 assume standard ANSI works, otherwise it won't. */
+	if (colour > 7)
+	    return;
+    }
+
+    strcpy(out, start);
+    if (def)
+	colour = TC_COL_DEFAULT;
+
+    ptr = out + strlen(start);
+    *ptr++ = colour + '0';
+    strcpy(ptr, TC_COL_END);
+    tputs(out, 1, putshout);
+}
+
 /**/
 static void
 settextattributes(int atr)
@@ -931,6 +1006,18 @@ settextattributes(int atr)
 	tsetcap(TCSTANDOUTBEG, 0);
     if (txtchangeisset(atr, TXTUNDERLINE))
 	tsetcap(TCUNDERLINEBEG, 0);
+    if (txtchangeisset(atr, TXTFGCOLOUR|TXTNOFGCOLOUR)) {
+	setcolourattribute(txtchangeget(atr, TXT_ATTR_FG_COL),
+			   TC_COL_FG_START, TCFGCOLOUR,
+			   txtchangeisset(atr, TXTNOFGCOLOUR),
+			   txtchangeisset(atr, TXT_ATTR_FG_TERMCAP));
+    }
+    if (txtchangeisset(atr, TXTBGCOLOUR|TXTNOBGCOLOUR)) {
+	setcolourattribute(txtchangeget(atr, TXT_ATTR_BG_COL),
+			   TC_COL_BG_START, TCBGCOLOUR,
+			   txtchangeisset(atr, TXTNOBGCOLOUR),
+			   txtchangeisset(atr, TXT_ATTR_BG_TERMCAP));
+    }
 }
 
 #ifdef MULTIBYTE_SUPPORT
@@ -1209,6 +1296,7 @@ zrefresh(void)
     rpms.sen = *nbuf + winw;
     for (t = tmpline, tmppos = 0; tmppos < tmpll; t++, tmppos++) {
 	int base_atr_on = 0, base_atr_off = 0, ireg;
+	int all_atr_on, all_atr_off;
 	struct region_highlight *rhp;
 	/*
 	 * Calculate attribute based on region.
@@ -1223,12 +1311,27 @@ zrefresh(void)
 		offset = predisplaylen; /* increment over it */
 	    if (rhp->start + offset <= tmppos &&
 		tmppos < rhp->end + offset) {
-		base_atr_on |= rhp->atr;
+		if (base_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) {
+		    /* keep colour already set */
+		    base_atr_on |= rhp->atr & ~TXT_ATTR_COLOUR_ON_MASK;
+		} else {
+		    /* no colour set yet */
+		    base_atr_on |= rhp->atr;
+		}
 		if (tmppos == rhp->end + offset - 1 ||
 		    tmppos == tmpll - 1)
-		    base_atr_off |= rhp->atr << TXT_ATTR_OFF_ON_SHIFT;
+		    base_atr_off |= TXT_ATTR_OFF_FROM_ON(rhp->atr);
 	    }
 	}
+	if (special_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) {
+	    /* keep colours from special attributes */
+	    all_atr_on = special_atr_on |
+		(base_atr_on & ~TXT_ATTR_COLOUR_ON_MASK);
+	} else {
+	    /* keep colours from standard attributes */
+	    all_atr_on = special_atr_on | base_atr_on;
+	}
+	all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on);
 
 	if (t == scs)			/* if cursor is here, remember it */
 	    rpms.nvcs = rpms.s - nbuf[rpms.nvln = rpms.ln];
@@ -1264,11 +1367,11 @@ zrefresh(void)
 		    rpms.s->chr = ZWC(' ');
 		    if (!started)
 			started = 1;
-		    rpms.s->atr = special_atr_on | base_atr_on;
+		    rpms.s->atr = all_atr_on;
 		    rpms.s++;
 		} while (rpms.s < rpms.sen);
 		if (started)
-		    rpms.s[-1].atr |= special_atr_off | base_atr_off;
+		    rpms.s[-1].atr |= all_atr_off;
 		if (nextline(&rpms, 1))
 		    break;
 		if (t == scs) {
@@ -1292,8 +1395,7 @@ zrefresh(void)
 		 * occurrence.
 		 */
 		rpms.s->chr = ZWC('?');
-		rpms.s->atr = special_atr_on | special_atr_off |
-		    base_atr_on | base_atr_off;
+		rpms.s->atr = all_atr_on | all_atr_off;
 		rpms.s++;
 	    } else {
 		/* We can fit it without reaching the end of the line. */
@@ -1334,18 +1436,17 @@ zrefresh(void)
 #endif
 	    ) {	/* other control character */
 	    rpms.s->chr = ZWC('^');
-	    rpms.s->atr = special_atr_on | base_atr_on;
+	    rpms.s->atr = all_atr_on;
 	    rpms.s++;
 	    if (rpms.s == rpms.sen) {
 		/* text wrapped */
-		rpms.s[-1].atr |= special_atr_off | base_atr_off;
+		rpms.s[-1].atr |= all_atr_off;
 		if (nextline(&rpms, 1))
 		    break;
 	    }
 	    rpms.s->chr = (((unsigned int)*t & ~0x80u) > 31) ?
 		ZWC('?') : (*t | ZWC('@'));
-	    rpms.s->atr = special_atr_on | special_atr_off |
-		base_atr_on | base_atr_off;
+	    rpms.s->atr = all_atr_on | all_atr_off;
 	    rpms.s++;
 	}
 #ifdef MULTIBYTE_SUPPORT
@@ -1370,12 +1471,12 @@ zrefresh(void)
 		    rpms.s->chr = wc;
 		    if (!started)
 			started = 1;
-		    rpms.s->atr = special_atr_on | base_atr_on;
+		    rpms.s->atr = all_atr_on;
 		    rpms.s++;
 		    if (rpms.s == rpms.sen) {
 			/* text wrapped */
 			if (started) {
-			    rpms.s[-1].atr |= special_atr_off | base_atr_off;
+			    rpms.s[-1].atr |= all_atr_off;
 			    started = 0;
 			}
 			if (nextline(&rpms, 1))
@@ -1385,7 +1486,7 @@ zrefresh(void)
 		dispptr++;
 	    }
 	    if (started)
-		rpms.s[-1].atr |= special_atr_off | base_atr_off;
+		rpms.s[-1].atr |= all_atr_off;
 	    if (*dispptr) /* nextline said stop processing */
 		break;
 	}
@@ -1417,11 +1518,14 @@ zrefresh(void)
 	more_end = 1;
 
     if (statusline) {
-	int outll, outsz;
+	int outll, outsz, all_atr_on, all_atr_off;
 	char *statusdup = ztrdup(statusline);
 	ZLE_STRING_T outputline =
 	    stringaszleline(statusdup, 0, &outll, &outsz, NULL); 
 
+	all_atr_on = special_atr_on;
+	all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on);
+
 	rpms.tosln = rpms.ln + 1;
 	nbuf[rpms.ln][winw + 1] = zr_zr;	/* text not wrapped */
 	snextline(&rpms);
@@ -1440,7 +1544,7 @@ zrefresh(void)
 		}
 		if (width > rpms.sen - rpms.s) {
 		    rpms.s->chr = ZWC('?');
-		    rpms.s->atr = special_atr_on | special_atr_off;
+		    rpms.s->atr = all_atr_on | all_atr_off;
 		    rpms.s++;
 		} else {
 		    rpms.s->chr = *u;
@@ -1457,7 +1561,7 @@ zrefresh(void)
 #endif
 	    if (ZC_icntrl(*u)) { /* simplified processing in the status line */
 		rpms.s->chr = ZWC('^');
-		rpms.s->atr = special_atr_on;
+		rpms.s->atr = all_atr_on;
 		rpms.s++;
 		if (rpms.s == rpms.sen) {
 		    nbuf[rpms.ln][winw + 1] = zr_nl;/* text wrapped */
@@ -1465,7 +1569,7 @@ zrefresh(void)
 		}
 		rpms.s->chr = (((unsigned int)*u & ~0x80u) > 31)
 		    ? ZWC('?') : (*u | ZWC('@'));
-		rpms.s->atr = special_atr_on | special_atr_off;
+		rpms.s->atr = all_atr_on | all_atr_off;
 		rpms.s++;
 	    } else {
 		rpms.s->chr = *u;
@@ -2037,7 +2141,7 @@ refreshline(int ln)
 	     */
 	    int now_off = ol->atr & ~nl->atr & TXT_ATTR_ON_MASK;
 	    if (now_off)
-		settextattributes(now_off << TXT_ATTR_OFF_ON_SHIFT);
+		settextattributes(TXT_ATTR_OFF_FROM_ON(now_off));
 
 	    zputc(nl);
 	    nl++, ol++;
@@ -2324,7 +2428,7 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
     if (tmpcs < 0) {
 #ifdef DEBUG
 	fprintf(stderr, "BUG: negative cursor position\n");
-	fflush(stderr); 
+	fflush(stderr);
 #endif
 	tmpcs = 0;
     }
@@ -2336,6 +2440,7 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
 
     for (t0 = 0; t0 < tmpll; t0++) {
 	int base_atr_on = 0, base_atr_off = 0, ireg;
+	int all_atr_on, all_atr_off;
 	struct region_highlight *rhp;
 	/*
 	 * Calculate attribute based on region.
@@ -2350,12 +2455,27 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
 		offset = predisplaylen; /* increment over it */
 	    if (rhp->start + offset <= t0 &&
 		t0 < rhp->end + offset) {
-		base_atr_on |= rhp->atr;
+		if (base_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) {
+		    /* keep colour already set */
+		    base_atr_on |= rhp->atr & ~TXT_ATTR_COLOUR_ON_MASK;
+		} else {
+		    /* no colour set yet */
+		    base_atr_on |= rhp->atr;
+		}
 		if (t0 == rhp->end + offset - 1 ||
 		    t0 == tmpll - 1)
-		    base_atr_off |= rhp->atr << TXT_ATTR_OFF_ON_SHIFT;
+		    base_atr_off |= TXT_ATTR_OFF_FROM_ON(rhp->atr);
 	    }
 	}
+	if (special_atr_on & (TXTFGCOLOUR|TXTBGCOLOUR)) {
+	    /* keep colours from special attributes */
+	    all_atr_on = special_atr_on |
+		(base_atr_on & ~TXT_ATTR_COLOUR_ON_MASK);
+	} else {
+	    /* keep colours from standard attributes */
+	    all_atr_on = special_atr_on | base_atr_on;
+	}
+	all_atr_off = TXT_ATTR_OFF_FROM_ON(all_atr_on);
 
 	if (tmpline[t0] == ZWC('\t')) {
 	    REFRESH_ELEMENT sp = zr_sp;
@@ -2365,11 +2485,10 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
 	    vp[-1].atr |= base_atr_off;
 	} else if (tmpline[t0] == ZWC('\n')) {
 	    vp->chr = ZWC('\\');
-	    vp->atr = special_atr_on | base_atr_on;
+	    vp->atr = all_atr_on;
 	    vp++;
 	    vp->chr = ZWC('n');
-	    vp->atr = special_atr_on | special_atr_off |
-		base_atr_on | base_atr_off;
+	    vp->atr = all_atr_on | all_atr_off;
 	    vp++;
 #ifdef MULTIBYTE_SUPPORT
 	} else if (iswprint(tmpline[t0]) &&
@@ -2406,12 +2525,11 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
 	    ZLE_INT_T t = tmpline[++t0];
 
 	    vp->chr = ZWC('^');
-	    vp->atr = special_atr_on | base_atr_on;
+	    vp->atr = all_atr_on;
 	    vp++;
 	    vp->chr = (((unsigned int)t & ~0x80u) > 31) ?
 		ZWC('?') : (t | ZWC('@'));
-	    vp->atr = special_atr_on | special_atr_off | base_atr_on |
-		base_atr_off;
+	    vp->atr = all_atr_on | all_atr_off;
 	    vp++;
 	}
 #ifdef MULTIBYTE_SUPPORT
@@ -2431,13 +2549,13 @@ singlerefresh(ZLE_STRING_T tmpline, int tmpll, int tmpcs)
 		    vp->chr = wc;
 		    if (!started)
 			started = 1;
-		    vp->atr = special_atr_on | base_atr_on;
+		    vp->atr = all_atr_on;
 		    vp++;
 		}
 		dispptr++;
 	    }
 	    if (started)
-		vp[-1].atr |= special_atr_off | base_atr_off;
+		vp[-1].atr |= all_atr_off;
 	}
 #else
 	else {
diff --git a/Src/init.c b/Src/init.c
index df859a619..253a689b6 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -79,6 +79,11 @@ int tclines, tccolumns;
 /**/
 mod_export int hasam, hasxn;
 
+/* Value of the Co (max_colors) entry: may not be set */
+
+/**/
+mod_export int tccolours;
+
 /* Pointer to read-key function from zle */
 
 /**/
@@ -531,7 +536,7 @@ static char *tccapnams[TC_COUNT] = {
     "cl", "le", "LE", "nd", "RI", "up", "UP", "do",
     "DO", "dc", "DC", "ic", "IC", "cd", "ce", "al", "dl", "ta",
     "md", "so", "us", "me", "se", "ue", "ch",
-    "ku", "kd", "kl", "kr", "sc", "rc", "bc"
+    "ku", "kd", "kl", "kr", "sc", "rc", "bc", "AF", "AB"
 };
 
 /* Initialise termcap */
@@ -590,6 +595,7 @@ init_term(void)
 
 	tclines = tgetnum("li");
 	tccolumns = tgetnum("co");
+	tccolours = tgetnum("Co");
 
 	/* if there's no termcap entry for cursor up, use single line mode: *
 	 * this is flagged by termflags which is examined in zle_refresh.c  *
diff --git a/Src/zsh.h b/Src/zsh.h
index 6bf682265..54a31507f 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -1949,7 +1949,9 @@ struct ttyinfo {
 #define TCSAVECURSOR   29
 #define TCRESTRCURSOR  30
 #define TCBACKSPACE    31
-#define TC_COUNT       32
+#define TCFGCOLOUR     32
+#define TCBGCOLOUR     33
+#define TC_COUNT       34
 
 #define tccan(X) (tclen[X])
 
@@ -1957,32 +1959,63 @@ struct ttyinfo {
  * Text attributes for displaying in ZLE
  */
 
-#define TXTBOLDFACE   0x01
-#define TXTSTANDOUT   0x02
-#define TXTUNDERLINE  0x04
-#define TXTDIRTY      0x80
+#define TXTBOLDFACE   0x0001
+#define TXTSTANDOUT   0x0002
+#define TXTUNDERLINE  0x0004
+#define TXTFGCOLOUR   0x0008
+#define TXTBGCOLOUR   0x0010
+#define TXTDIRTY      0x0020
 
-#define TXT_ATTR_ON_MASK   0x07
+#define TXT_ATTR_ON_MASK   0x001F
 
 #define txtisset(X)  (txtattrmask & (X))
 #define txtset(X)    (txtattrmask |= (X))
 #define txtunset(X)  (txtattrmask &= ~(X))
 
-#define TXTNOBOLDFACE	0x10
-#define TXTNOSTANDOUT	0x20
-#define TXTNOUNDERLINE	0x40
+#define TXTNOBOLDFACE	0x0040
+#define TXTNOSTANDOUT	0x0080
+#define TXTNOUNDERLINE	0x0100
+#define TXTNOFGCOLOUR	0x0200
+#define TXTNOBGCOLOUR	0x0400
 
-#define TXT_ATTR_OFF_MASK  0x70
+#define TXT_ATTR_OFF_MASK  0x07C0
 /* Bits to shift off right to get on */
-#define TXT_ATTR_OFF_ON_SHIFT (4)
-
+#define TXT_ATTR_OFF_ON_SHIFT 6
+#define TXT_ATTR_OFF_FROM_ON(attr)	\
+    (((attr) & TXT_ATTR_ON_MASK) << TXT_ATTR_OFF_ON_SHIFT)
 /*
  * Indicates to zle_refresh.c that the character entry is an
  * index into the list of multiword symbols.
  */
-#define TXT_MULTIWORD_MASK  0x100
+#define TXT_MULTIWORD_MASK  0x0800
+
+/* Mask for colour to use in foreground */
+#define TXT_ATTR_FG_COL_MASK     0x000FF000
+/* Bits to shift the foreground colour */
+#define TXT_ATTR_FG_COL_SHIFT    (12)
+/* Mask for colour to use in background */
+#define TXT_ATTR_BG_COL_MASK     0x0FF00000
+/* Bits to shift the background colour */
+#define TXT_ATTR_BG_COL_SHIFT    (20)
+
+/* Flag to use termcap AF sequence to set colour, if available */
+#define TXT_ATTR_FG_TERMCAP      0x10000000
+/* Flag to use termcap AB sequence to set colour, if available */
+#define TXT_ATTR_BG_TERMCAP      0x20000000
+
+/* Things to turn on, including values for the colour elements */
+#define TXT_ATTR_ON_VALUES_MASK	\
+    (TXT_ATTR_ON_MASK|TXT_ATTR_FG_COL_MASK|TXT_ATTR_BG_COL_MASK|\
+     TXT_ATTR_FG_TERMCAP|TXT_ATTR_BG_TERMCAP)
+
+/* Mask out everything to do with activating colours */
+#define TXT_ATTR_COLOUR_ON_MASK			\
+    (TXTFGCOLOUR|TXTBGCOLOUR|			\
+     TXT_ATTR_FG_COL_MASK|TXT_ATTR_BG_COL_MASK| \
+     TXT_ATTR_FG_TERMCAP|TXT_ATTR_BG_TERMCAP)
 
 #define txtchangeisset(T,X)	((T) & (X))
+#define txtchangeget(T,A)	(((T) & A ## _MASK) >> A ## _SHIFT)
 #define txtchangeset(X, Y)	(txtchange |= (X), txtchange &= ~(Y))
 
 /****************************************/