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