1 /*
2  * $Id: xterm.c,v 1.10 2010/02/09 21:32:14 tom Exp $
3  *
4  * xterm-specific code for vi-like-emacs.
5  */
6 
7 #include	"estruct.h"
8 #include	"edef.h"
9 
10 #if DISP_TERMCAP || DISP_CURSES
11 
12 #define putpad(s)	vl_fputs(s, stdout)
13 #define	XtermPos()	(keystroke() - 040)
14 
15 #if OPT_XTERM >= 3
16 # define XTERM_ENABLE_TRACKING   "\033[?1001h"	/* mouse hilite tracking */
17 # define XTERM_DISABLE_TRACKING  "\033[?1001l"
18 #else
19 # if OPT_XTERM >= 2
20 #  define XTERM_ENABLE_TRACKING   "\033[?1000h"		/* normal tracking mode */
21 #  define XTERM_DISABLE_TRACKING  "\033[?1000l"
22 # else
23 #  define XTERM_ENABLE_TRACKING   "\033[?9h"	/* X10 compatibility mode */
24 #  define XTERM_DISABLE_TRACKING  "\033[?9l"
25 # endif
26 #endif
27 
28 static int x_origin = 1, y_origin = 1;
29 
30 void
xterm_open(TERM * tp)31 xterm_open(TERM * tp)
32 {
33     x_origin = 0;
34     y_origin = 0;
35 
36     /* use xterm #251 feature to save title */
37     putpad("\033[22t");
38 
39     if (tp != 0) {
40 	tp->set_title = xterm_settitle;
41 	tp->mopen = xterm_mopen;
42 	tp->mclose = xterm_mclose;
43 
44 #if OPT_XTERM
45 	addtosysmap("\033[M", 3, KEY_Mouse);
46 #if OPT_XTERM >= 3
47 	addtosysmap("\033[t", 3, KEY_text);
48 	addtosysmap("\033[T", 3, KEY_textInvalid);
49 #endif
50 #endif
51     }
52 }
53 
54 void
xterm_close()55 xterm_close()
56 {
57     /* use xterm #251 feature to restore title */
58     putpad("\033[23t");
59 }
60 
61 void
xterm_mopen(void)62 xterm_mopen(void)
63 {
64     TRACE((T_CALLED "xterm_mopen()\n"));
65     if (global_g_val(GMDXTERM_MOUSE)) {
66 	putpad(XTERM_ENABLE_TRACKING);
67 	fflush(stdout);
68     }
69     returnVoid();
70 }
71 
72 void
xterm_mclose(void)73 xterm_mclose(void)
74 {
75     TRACE((T_CALLED "xterm_mclose()\n"));
76     if (global_g_val(GMDXTERM_MOUSE)) {
77 	putpad(XTERM_DISABLE_TRACKING);
78 	fflush(stdout);
79     }
80     returnVoid();
81 }
82 
83 /* Finish decoding a mouse-click in an xterm, after the ESC and '[' chars.
84  *
85  * There are 3 mutually-exclusive xterm mouse-modes (selected here by values of
86  * OPT_XTERM):
87  *	(1) X10-compatibility (not used here)
88  *		Button-press events are received.
89  *	(2) normal-tracking
90  *		Button-press and button-release events are received.
91  *		Button-events have modifiers (e.g., shift, control, meta).
92  *	(3) hilite-tracking
93  *		Button-press and text-location events are received.
94  *		Button-events have modifiers (e.g., shift, control, meta).
95  *		Dragging with the mouse produces highlighting.
96  *		The text-locations are checked by xterm to ensure validity.
97  *
98  * NOTE:
99  *	The hilite-tracking code is here for testing and (later) use.  Because
100  *	we cannot guarantee that we always are decoding escape-sequences when
101  *	reading from the terminal, there is the potential for the xterm hanging
102  *	when a mouse-dragging operation is begun: it waits for us to specify
103  *	the screen locations that limit the highlighting.
104  *
105  *	While highlighting, the xterm accepts other characters, but the display
106  *	does not appear to be refreshed until highlighting is ended. So (even
107  *	if we always capture the beginning of highlighting) we cannot simply
108  *	loop here waiting for the end of highlighting.
109  *
110  *	1993/aug/6 dickey@software.org
111  */
112 
113 static int xterm_button(int c);
114 
115 /*ARGSUSED*/
116 int
mouse_motion(int f GCC_UNUSED,int n GCC_UNUSED)117 mouse_motion(int f GCC_UNUSED, int n GCC_UNUSED)
118 {
119     return xterm_button('M');
120 }
121 
122 #if OPT_XTERM >= 3
123 /*ARGSUSED*/
124 int
xterm_mouse_t(int f,int n)125 xterm_mouse_t(int f, int n)
126 {
127     return xterm_button('t');
128 }
129 
130 /*ARGSUSED*/
131 int
xterm_mouse_T(int f,int n)132 xterm_mouse_T(int f, int n)
133 {
134     return xterm_button('T');
135 }
136 #endif /* OPT_XTERM >= 3 */
137 
138 static int
xterm_button(int c)139 xterm_button(int c)
140 {
141     int event;
142     int button;
143     int x;
144     int y;
145     int status;
146 #if OPT_XTERM >= 3
147     WINDOW *wp;
148     int save_row = ttrow;
149     int save_col = ttcol;
150     int firstrow, lastrow;
151     int startx, endx, mousex;
152     int starty, endy, mousey;
153     MARK save_dot;
154     char temp[NSTRING];
155     static const char *fmt = "\033[%d;%d;%d;%d;%dT";
156 #endif /* OPT_XTERM >= 3 */
157 
158     if ((status = (global_g_val(GMDXTERM_MOUSE))) != 0) {
159 	beginDisplay();
160 	switch (c) {
161 #if OPT_XTERM < 3
162 	    /*
163 	     * If we get a click on a modeline, clear the current selection,
164 	     * if any.  Allow implied movement of the mouse (distance between
165 	     * pressing and releasing a mouse button) to drag the modeline.
166 	     *
167 	     * Likewise, if we get a click _not_ on a modeline, make that
168 	     * start/extend a selection.
169 	     */
170 	case 'M':		/* button-event */
171 	    event = keystroke();
172 	    x = XtermPos() + x_origin - 1;
173 	    y = XtermPos() + y_origin - 1;
174 	    button = (event & 3) + 1;
175 
176 	    if (button > 3)
177 		button = 0;
178 	    TRACE(("MOUSE-button event %d -> btn %d loc %d.%d\n",
179 		   event, button, y, x));
180 	    status = on_mouse_click(button, y, x);
181 	    break;
182 #else /* OPT_XTERM >=3, highlighting mode */
183 	case 'M':		/* button-event */
184 	    event = keystroke();
185 	    x = XtermPos() + x_origin;
186 	    y = XtermPos() + y_origin;
187 
188 	    button = (event & 3) + 1;
189 	    TRACE(("MOUSE-button event:%d x:%d y:%d\n", event, x, y));
190 	    if (button > 3) {
191 		endofDisplay();
192 		return TRUE;	/* button up */
193 	    }
194 	    wp = row2window(y - 1);
195 	    if (insertmode && wp != curwp) {
196 		kbd_alarm();
197 		return ABORT;
198 	    }
199 	    /* Tell the xterm how to highlight the selection.
200 	     * It won't do anything else until we do this.
201 	     */
202 	    if (wp != 0) {
203 		firstrow = wp->w_toprow + 1;
204 		lastrow = mode_row(wp) + 1;
205 	    } else {		/* from message-line */
206 		firstrow = term.rows;
207 		lastrow = term.rows + 1;
208 	    }
209 	    if (y >= lastrow)	/* don't select modeline */
210 		y = lastrow - 1;
211 	    (void) lsprintf(temp, fmt, 1, x, y, firstrow, lastrow);
212 	    putpad(temp);
213 	    term.flush();
214 	    /* Set the dot-location if button 1 was pressed in a
215 	     * window.
216 	     */
217 	    if (wp != 0
218 		&& button == 1
219 		&& !reading_msg_line
220 		&& setcursor(y - 1, x - 1)) {
221 		(void) update(TRUE);
222 		status = TRUE;
223 	    } else if (button <= 3) {
224 		/* abort the selection */
225 		(void) lsprintf(temp, fmt, 0, x, y, firstrow, lastrow);
226 		putpad(temp);
227 		term.flush();
228 		status = ABORT;
229 	    } else {
230 		status = FALSE;
231 	    }
232 	    break;
233 	case 't':		/* reports valid text-location */
234 	    x = XtermPos();
235 	    y = XtermPos();
236 
237 	    TRACE(("MOUSE-valid: x:%d y:%d\n", x, y));
238 	    setwmark(y - 1, x - 1);
239 	    yankregion();
240 
241 	    movecursor(save_row, save_col);
242 	    (void) update(TRUE);
243 	    break;
244 	case 'T':		/* reports invalid text-location */
245 	    /*
246 	     * The starting-location returned is not the location
247 	     * at which the mouse was pressed.  Instead, it is the
248 	     * top-most location of the selection.  In turn, the
249 	     * ending-location is the bottom-most location of the
250 	     * selection.  The mouse-up location is not necessarily
251 	     * a pointer to valid text.
252 	     *
253 	     * This case handles multi-clicking events as well as
254 	     * selections whose start or end location was not
255 	     * pointing to text.
256 	     */
257 	    save_dot = DOT;
258 	    startx = XtermPos();	/* starting-location */
259 	    starty = XtermPos();
260 	    endx = XtermPos();	/* ending-location */
261 	    endy = XtermPos();
262 	    mousex = XtermPos();	/* location at mouse-up */
263 	    mousey = XtermPos();
264 
265 	    TRACE(("MOUSE-invalid: start(%d,%d) end(%d,%d) mouse(%d,%d)\n",
266 		   starty, startx,
267 		   endy, endx,
268 		   mousey, mousex));
269 	    setcursor(starty - 1, startx - 1);
270 	    setwmark(endy - 1, endx - 1);
271 	    if (MK.o != 0 && !is_at_end_of_line(MK)) {
272 		MK.o += BytesAt(MK.l, MK.o);
273 	    }
274 	    yankregion();
275 
276 	    DOT = save_dot;
277 	    movecursor(save_row, save_col);
278 	    (void) update(TRUE);
279 	    break;
280 #endif /* OPT_XTERM < 3 */
281 	default:
282 	    status = FALSE;
283 	}
284 	endofDisplay();
285     }
286     return status;
287 }
288 
289 void
xterm_settitle(const char * string)290 xterm_settitle(const char *string)
291 {
292     if (global_g_val(GMDXTERM_TITLE) && string != 0) {
293 #if OPT_MULTIBYTE
294 	int check;
295 	UINT ch;
296 	UCHAR temp[10];
297 	ENC_CHOICES want_encoding = title_encoding;
298 #endif
299 	TRACE(("xterm_settitle(%s)\n", string));
300 	putpad("\033]0;");
301 #if OPT_MULTIBYTE
302 	if (want_encoding == enc_LOCALE) {
303 	    if (vl_encoding >= enc_UTF8) {
304 		want_encoding = enc_UTF8;
305 	    } else {
306 		want_encoding = enc_8BIT;
307 	    }
308 	}
309 	if (curbp == 0) {
310 	} else if (want_encoding == enc_UTF8 && !b_is_utfXX(curbp)) {
311 	    TRACE(("...converting to UTF-8\n"));
312 	    while (*string != EOS) {
313 		ch = CharOf(*string++);
314 		if (vl_8bit_to_ucs(&check, (int) ch))
315 		    ch = (UINT) check;
316 		else
317 		    ch = '?';
318 		check = vl_conv_to_utf8(temp, ch, sizeof(temp));
319 		temp[check] = EOS;
320 		putpad((char *) temp);
321 	    }
322 	} else if (want_encoding == enc_8BIT && b_is_utfXX(curbp)) {
323 	    TRACE(("...converting to 8-bit\n"));
324 	    while (*string != EOS) {
325 		check = vl_conv_to_utf32(&ch, string, strlen(string));
326 		if (check <= 0) {
327 		    string++;
328 		    continue;
329 		} else {
330 		    string += check;
331 		}
332 		if (isPrint(ch) && vl_ucs_to_8bit(&check, (int) ch)) {
333 		    ch = (UINT) (check < 256 ? check : '?');
334 		} else {
335 		    ch = '?';
336 		}
337 		temp[0] = (UCHAR) ch;
338 		temp[1] = EOS;
339 		putpad((char *) temp);
340 	    }
341 	} else
342 #endif
343 	    putpad(string);
344 	putpad("\007");
345 	term.flush();
346     }
347 }
348 #endif /* DISP_TERMCAP || DISP_CURSES */
349