1: #include <X/mit-copyright.h>
2:
3: /* Copyright Massachusetts Institute of Technology 1984, 1985 */
4: /*
5: button.c Handles button events in the terminal emulator.
6: does cut/paste operations, change modes via menu,
7: passes button events through to some applications.
8: J. Gettys.
9: */
10: #ifndef lint
11: static char *rcsid_window_c = "$Header: button.c,v 10.11 86/02/01 16:05:41 tony Rel $";
12: #endif lint
13: #include <stdio.h>
14: #include <X/Xlib.h>
15: #include "ptyx.h"
16: #ifdef MODEMENU
17: #include <X/XMenu.h>
18: #endif
19:
20: #define NBUTS 3
21: #define DIRS 2
22: #define UP 1
23: #define DOWN 0
24: #define SHIFTS 8 /* three keys, so eight combinations */
25: char *GetRestOfLine();
26: char *SaveText();
27: extern UnSaltText();
28: extern SaltTextAway();
29: extern StartCut();
30: extern ReExecute();
31: extern EditorDown();
32: #ifdef MODEMENU
33: extern ModeMenu();
34: extern char *xterm_name;
35: #else
36: #define Bogus
37: #endif MODEMENU
38: extern Bogus(), Silence();
39: /* due to LK201 limitations, not all of the below are actually possible */
40: int (*bfunc[SHIFTS][DIRS][NBUTS])() = {
41: /* left middle right */
42: EditorDown, EditorDown, EditorDown, /* down | */
43: Silence, Silence, Silence, /* up |no shift */
44:
45: Silence, StartCut, Silence, /* down | */
46: ReExecute, SaltTextAway, UnSaltText, /* up |shift */
47:
48: Bogus, Bogus, Bogus, /* down | */
49: Silence, Silence, Silence, /* up |meta */
50:
51: Bogus, Bogus, Bogus, /* down | */
52: Silence, Silence, Silence, /* up |meta shift */
53:
54: Bogus, ModeMenu, Bogus, /* down | */
55: Silence, Silence, Silence, /* up |control */
56:
57: Bogus, Bogus, Bogus, /* down | */
58: Silence, Silence, Silence, /* up |ctl shift */
59:
60: Bogus, Bogus, Bogus, /* down | */
61: Silence, Silence, Silence, /* up |no shift */
62:
63: Bogus, Bogus, Bogus, /* down | control */
64: Silence, Silence, Silence /* up |meta shift*/
65:
66: }; /* button, shift keys, and direction */
67:
68: static crow, ccol; /* cut row and column */
69:
70: HandleButtons(term, reply, pty)
71: register Terminal *term;
72: register XEvent *reply;
73: int pty; /* file descriptor of pty */
74: {
75: int (*bp)();
76: int dir = DOWN;
77: /* so table above will be nice, we change left to right */
78: int button = 2 - ((XKeyOrButtonEvent *)reply)->detail & 0177;
79: int shift = KeyState(((XKeyOrButtonEvent *)reply)->detail);
80: switch (reply->type) {
81: case ButtonPressed:
82: dir = DOWN;
83: break;
84: case ButtonReleased:
85: dir = UP;
86: break;
87: default:
88: break;
89: }
90: bp = bfunc[shift][dir][button];
91: if (bp != NULL) return ((*bp)(term, reply, pty));
92: XFeep(0);
93: return(0);
94: }
95:
96: UnSaltText(term, reply, pty)
97: register Terminal *term;
98: register XKeyOrButtonEvent *reply;
99: int pty; /* file descriptor of pty */
100: {
101: char *line;
102: int nbytes;
103: register char *lag, *cp, *end;
104:
105: line = XFetchBytes(&nbytes);
106: end = &line[nbytes];
107: lag = line;
108: for (cp = line; cp != end; cp++)
109: {
110: if (*cp != '\n') continue;
111: *cp = '\r';
112: write(pty, lag, cp - lag + 1);
113: lag = cp + 1;
114: }
115: if (lag != end)
116: write(pty, lag, end - lag);
117: free (line); /* free text from fetch */
118: return(0);
119: }
120:
121: ReExecute(term, reply, pty)
122: register Terminal *term;
123: register XKeyOrButtonEvent *reply;
124: int pty; /* file descriptor of pty */
125: {
126: char *line = GetRestOfLine(&term->screen, reply);
127: register int nbytes = strlen(line);
128:
129: write(pty, line, nbytes);
130: line[nbytes] = '\n';
131: XStoreBytes(line, nbytes);
132: free (line); /* free text from fetch */
133: return(0);
134: }
135:
136: char *GetRestOfLine(screen, reply)
137: register XKeyOrButtonEvent *reply;
138: register Screen *screen;
139: {
140: char *line;
141: int i;
142: register int row, col;
143: row = (reply->y - screen->border) / screen->f_height;
144: col = (reply->x - screen->border) / screen->f_width;
145: if ( row < 0 ) row = 0;
146: else if ( row > screen->max_row ) row = screen->max_row;
147: if ( col < 0 ) col = 0;
148: else if ( col > screen->max_col ) col = screen->max_col;
149: i = Length(screen, row, col, screen->max_col);
150: if((line = (char *)malloc(i + 2)) == NULL) Error();
151: SaveText(screen, row, col, screen->max_col, line);
152: line[i] = '\r';
153: line[i+1] = '\0';
154: return(line);
155: }
156:
157: StartCut(term, reply, pty)
158: register XKeyOrButtonEvent *reply;
159: Terminal *term;
160: int pty;
161: {
162: register Screen *screen = &term->screen;
163: crow = (reply->y - screen->border) / screen->f_height;
164: ccol = (reply->x - screen->border) / screen->f_width;
165: return(0);
166: }
167:
168: SaltTextAway(term, reply, pty)
169: register XKeyOrButtonEvent *reply;
170: Terminal *term;
171: int pty;
172: {
173: register Screen *screen = &term->screen;
174: register int i, j = 0;
175: register row, col;
176: register char *str; /* string to be saved */
177: char *line, *lp;
178: row = (reply->y - screen->border) / screen->f_height;
179: col = (reply->x - screen->border) / screen->f_width;
180:
181: /* first order of business is to guarantee that crow,ccol is before */
182: /* row,col. */
183: if ( row == crow ) { /* must exchange as pairs */
184: if ( ccol > col ) { /* may have to exchange columns */
185: register int tmp;
186: tmp = ccol; ccol = col; col = tmp;
187: }
188: }
189: else {
190: if ( crow > row ) { /* may have to exchange row and col */
191: register int tmp;
192: tmp = ccol; ccol = col; col = tmp;
193: tmp = crow; crow = row; row = tmp;
194: }
195: }
196:
197: if (ccol < 0) ccol = 0;
198: else if (ccol > screen->max_col) { crow++; ccol = 0; }
199: if (crow < 0) crow = ccol = 0;
200: else if (crow > screen->max_row) { crow = screen->max_row; ccol = 0; }
201:
202: if (row > screen->max_row) { row = screen->max_row + 1 ; col = 0; }
203: else if (--col > screen->max_col) col = screen->max_col;
204:
205: /* first we need to know how long the string is before we can save it*/
206:
207: if ( row == crow ) j = Length(screen, crow, ccol, col);
208: else { /* two cases, cut is on same line, cut spans multiple lines */
209: j += Length(screen, crow, ccol, screen->max_col) + 1;
210: for(i = crow + 1; i < row; i++)
211: j += Length(screen, i, 0, screen->max_col) + 1;
212: if (col >= 0)
213: j += Length(screen, row, 0, col);
214: }
215:
216: /* now get some memory to save it in */
217:
218: if((line = (char *)malloc(j + 1)) == NULL) Error();
219: line[j] = '\0'; /* make sure it is null terminated */
220: lp = line; /* lp points to where to save the text */
221: if ( row == crow ) lp = SaveText(screen, row, ccol, col, lp);
222: else {
223: lp = SaveText(screen, crow, ccol, screen->max_col, lp);
224: *lp ++ = '\n'; /* put in newline at end of line */
225: for(i = crow +1; i < row; i++) {
226: lp = SaveText(screen, i, 0, screen->max_col, lp);
227: *lp ++ = '\n';
228: }
229: if (col >= 0)
230: lp = SaveText(screen, row, 0, col, lp);
231: }
232: *lp = '\0'; /* make sure we have end marked */
233:
234: XStoreBytes(line, j);
235: free(line);
236: return(0);
237: }
238:
239: /* returns number of chars in line from scol to ecol out */
240: int Length(screen, row, scol, ecol)
241: register int row, scol, ecol;
242: register Screen *screen;
243: {
244: register short *ch;
245: int end = 0;
246: ch = screen->buf[row];
247: while (ecol >= scol &&
248: ((ch[ecol] & CHAR) == 0 || (ch[ecol] & CHAR) == ' '))
249: ecol--;
250: return (ecol - scol + 1);
251: }
252:
253: /* copies text into line, preallocated */
254: char *SaveText(screen, row, scol, ecol, lp)
255: int row;
256: int scol, ecol;
257: Screen *screen;
258: register char *lp; /* pointer to where to put the text */
259: {
260: register int i = 0;
261: register short *ch = screen->buf[row];
262: register int c;
263: if ((i = Length(screen, row, scol, ecol)) == 0) return(lp);
264: ecol = scol + i;
265: for (i = scol; i < ecol; i++)
266: {
267: c = ch[i] & CHAR;
268: if (c == 0) c = ' ';
269: *lp++ = c;
270: }
271: return(lp);
272: }
273:
274: EditorDown (term, reply, pty)
275: Terminal *term;
276: register XKeyOrButtonEvent *reply;
277: int pty; /* file descriptor of pty */
278: {
279: register Screen *screen = &term->screen;
280: char line[6];
281: register unsigned row, col;
282: int button = 2 - reply->detail & 0177;
283: if (!screen->send_mouse_pos) {
284: XFeep(0);
285: return(0);
286: }
287: row = (reply->y - screen->border) / screen->f_height;
288: col = (reply->x - screen->border) / screen->f_width;
289: strcpy(line, "\033[M");
290: line[3] = ' ' + button;
291: line[4] = ' '+col+1;
292: line[5] = ' '+row+1;
293: write(pty, line, 6);
294: return(0);
295: }
296:
297: #ifdef MODEMENU
298:
299: static int ;
300: static Terminal *menuterm;
301:
302: HandleMenuEvent (rep)
303: XEvent *rep;
304: {
305: register Screen *screen = &menuterm->screen;
306:
307: switch (rep->type)
308: {
309: case ExposeWindow:
310: menutoggled = 1;
311: if (ScreenResize (screen,
312: ((XExposeWindowEvent *)rep)->width,
313: ((XExposeWindowEvent *)rep)->height,
314: menuterm->flags) != -1)
315: {
316: TabReset (menuterm->tabs);
317: XClear (screen->window);
318: ScrnRefresh (screen, 0, 0,
319: screen->max_row + 1,
320: screen->max_col + 1);
321:
322: if (screen->TekEmu) TekRefresh (menuterm);
323: }
324: break;
325:
326: case ExposeRegion:
327: if (HandleExposure (screen, rep))
328: menutoggled = 1;
329: break;
330: }
331: }
332:
333: #define 1
334: #define 2
335: #define 3
336: #define 4
337: #define 5
338: #define 6
339: #define 7
340: #define 8
341:
342: static XMenu * = NULL;
343: static int = 0;
344: static int = 0;
345: static int = 0;
346: static int = 0;
347:
348: (term, reply, pty)
349: Terminal *term;
350: register XKeyOrButtonEvent *reply;
351: int pty; /* file descriptor of pty */
352: {
353: register Screen *screen = &term->screen;
354: register char **ptr;
355: int x, y;
356: int ulx, uly;
357: int menuw, menuh;
358: int pnum = lastmenupane, snum = lastmenusel;
359: int status, val;
360:
361: if (menu == NULL) {
362: if ((menu = (XMenu *)XMenuCreate(RootWindow, xterm_name)) == NULL)
363: return(0);
364: XMenuAddPane(menu, "Modes", 1);
365: #ifdef JUMPSCROLL
366: XMenuAddSelection(menu, 0, MMENU_SCROLL,
367: (term->flags & SMOOTHSCROLL) ? "jump scroll" : "smooth scroll",
368: 1);
369: #endif
370: XMenuAddSelection(menu, 0, MMENU_VIDEO,
371: (term->flags & REVERSE_VIDEO) ? "normal video" : "reverse video",
372: 1);
373: XMenuAddSelection(menu, 0, MMENU_WRAP,
374: (term->flags & WRAPAROUND) ? "no wrap" : "auto wrap",
375: 1);
376: XMenuAddSelection(menu, 0, MMENU_NLM,
377: (term->flags & LINEFEED) ? "normal linefeed" : "auto linefeed",
378: 1);
379: XMenuAddSelection(menu, 0, MMENU_CURSOR,
380: (term->keyboard.flags & CURSOR_APL) ? "normal cursors" : "application cursors",
381: 1);
382: XMenuAddSelection(menu, 0, MMENU_PAD,
383: (term->keyboard.flags & KYPD_APL) ? "numeric pad" : "application pad",
384: 1);
385: XMenuAddSelection(menu, 0, MMENU_RESET, "soft reset", 1);
386: XMenuAddSelection(menu, 0, MMENU_FULLRESET, "full reset", 1);
387: menuterm = term;
388: menutermflags = term->flags;
389: menukbdflags = term->keyboard.flags;
390: }
391: #ifdef JUMPSCROLL
392: if ((menutermflags ^ term->flags) & SMOOTHSCROLL) {
393: XMenuChangeSelection(menu, 0, MMENU_SCROLL-1, MMENU_SCROLL, 1,
394: (term->flags & SMOOTHSCROLL) ? "jump scroll" : "smooth scroll",
395: 1);
396: }
397: #endif
398: if ((menutermflags ^ term->flags) & REVERSE_VIDEO) {
399: XMenuChangeSelection(menu, 0, MMENU_VIDEO-1, MMENU_VIDEO, 1,
400: (term->flags & REVERSE_VIDEO) ? "normal video" : "reverse video",
401: 1);
402: }
403: if ((menutermflags ^ term->flags) & WRAPAROUND) {
404: XMenuChangeSelection(menu, 0, MMENU_WRAP-1, MMENU_WRAP, 1,
405: (term->flags & WRAPAROUND) ? "no wrap" : "auto wrap",
406: 1);
407: }
408: if ((menutermflags ^ term->flags) & LINEFEED) {
409: XMenuChangeSelection(menu, 0, MMENU_NLM-1, MMENU_NLM, 1,
410: (term->flags & LINEFEED) ? "normal linefeed" : "auto linefeed",
411: 1);
412: }
413: if ((menukbdflags ^ term->keyboard.flags) & CURSOR_APL) {
414: XMenuChangeSelection(menu, 0, MMENU_CURSOR-1, MMENU_CURSOR, 1,
415: (term->keyboard.flags & CURSOR_APL) ? "normal cursors" : "application cursors",
416: 1);
417: }
418: if ((menukbdflags ^ term->keyboard.flags) & KYPD_APL) {
419: XMenuChangeSelection(menu, 0, MMENU_PAD-1, MMENU_PAD, 1,
420: (term->keyboard.flags & KYPD_APL) ? "numeric pad" : "application pad",
421: 1);
422: }
423: XMenuRecompute(menu);
424: menutermflags = term->flags;
425: menukbdflags = term->keyboard.flags;
426: x = (reply->location >> 16) & 0xffff;
427: y = reply->location & 0xffff;
428: XMenuLocate(menu, pnum, snum, x, y, &ulx, &uly, &menuw, &menuh);
429: if ((ulx + menuw) > DisplayWidth())
430: x -= ((ulx + menuw) - DisplayWidth());
431: if (ulx < 0)
432: x -= ulx;
433: if ((uly + menuh) > DisplayHeight())
434: y -= ((uly + menuh) - DisplayHeight());
435: if (uly < 0)
436: y -= uly;
437: XMenuEventHandler(HandleMenuEvent);
438: menutoggled = 0;
439: status = XMenuActivate(
440: menu,
441: &pnum, &snum,
442: x, y,
443: MiddleMask|ButtonReleased,
444: (char **)&val
445: );
446: if (status == XM_FAILURE) return(menutoggled);
447: if (status == XM_NO_SELECT) return(menutoggled);
448: else {
449: lastmenupane = pnum;
450: lastmenusel = snum;
451: }
452: switch (val) {
453: #ifdef JUMPSCROLL
454: case :
455: term->flags ^= SMOOTHSCROLL;
456: if (term->flags & SMOOTHSCROLL) {
457: screen->jumpscroll = 0;
458: if (screen->scroll_amt) FlushScroll(screen);
459: } else if (!screen->TekEmu) screen->jumpscroll = 1;
460: break;
461: #endif
462: case :
463: term->flags ^= REVERSE_VIDEO;
464: ReverseVideo(term);
465: menutoggled = 1;
466: break;
467: case :
468: term->flags ^= WRAPAROUND;
469: case :
470: term->flags ^= LINEFEED;
471: case :
472: term->keyboard.flags ^= CURSOR_APL;
473: break;
474: case :
475: term->keyboard.flags ^= KYPD_APL;
476: break;
477: case :
478: TabReset (term->tabs);
479: term->keyboard.flags = NULL;
480: screen->mode = ANSInormal;
481: /* Reset Tektronix alpha mode */
482: screen->TekGMode = 0;
483: screen->TekAMode = 0;
484: screen->gsets[0] = 'B';
485: screen->gsets[1] = 'B';
486: screen->gsets[2] = '<';
487: screen->gsets[3] = '<';
488: screen->curgl = 0;
489: screen->curgr = 2;
490: screen->cur_x = screen->cur_y = 0;
491: screen->cur_X = screen->cur_Y = 0;
492: if (term->flags & REVERSE_VIDEO) ReverseVideo(term);
493: term->flags &= ~REVERSE_VIDEO;
494: menutoggled = 1;
495: CursorSet(screen, 0, 0, term->flags);
496: ClearScreen(screen);
497: term->flags = WRAPAROUND|SMOOTHSCROLL;
498: case :
499: /* reset scrolling region */
500: screen->top_marg = 0;
501: screen->bot_marg = screen->max_row;
502: term->flags &= ~ORIGIN;
503: break;
504: }
505: return(menutoggled);
506: }
507: #endif MODEMENU
508:
509: Bogus(term, reply, pty)
510: Terminal *term;
511: XKeyOrButtonEvent *reply;
512: int pty; /* file descriptor of pty */
513: {
514: XFeep(0);
515: return(0);
516: }
517:
518: Silence(term, reply, pty)
519: Terminal *term;
520: XKeyOrButtonEvent *reply;
521: int pty; /* file descriptor of pty */
522: {
523: return(0);
524: }