/* $XTermId: button.c,v 1.197 2005/11/03 13:17:27 tom Exp $ */ /* $Xorg: button.c,v 1.3 2000/08/17 19:55:08 cpqbld Exp $ */ /* * Copyright 1999-2004,2005 by Thomas E. Dickey * * All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the name(s) of the above copyright * holders shall not be used in advertising or otherwise to promote the * sale, use or other dealings in this Software without prior written * authorization. * * * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation, and that the name of Digital Equipment * Corporation not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior permission. * * * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* $XFree86: xc/programs/xterm/button.c,v 3.82 2005/11/03 13:17:27 dickey Exp $ */ /* button.c Handles button events in the terminal emulator. does cut/paste operations, change modes via menu, passes button events through to some applications. J. Gettys. */ #include #include #include #include #include #include #include #include #include #include #include #if OPT_WIDE_CHARS #include #else #define CharacterClass(value) \ charClass[value & ((sizeof(charClass)/sizeof(charClass[0]))-1)] #endif #define XTERM_CELL(row,col) getXtermCell(screen, row + screen->topline, col) #define XTERM_CELL_C1(row,col) getXtermCellComb1(screen, row + screen->topline, col) #define XTERM_CELL_C2(row,col) getXtermCellComb2(screen, row + screen->topline, col) /* * We reserve shift modifier for cut/paste operations. In principle we * can pass through control and meta modifiers, but in practice, the * popup menu uses control, and the window manager is likely to use meta, * so those events are not delivered to SendMousePosition. */ #define OurModifiers (ShiftMask | ControlMask | Mod1Mask) #define AllModifiers (ShiftMask | LockMask | ControlMask | Mod1Mask | \ Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask) #define KeyModifiers (event->xbutton.state & OurModifiers) #define KeyState(x) (((x) & (ShiftMask|ControlMask)) + (((x) & Mod1Mask) ? 2 : 0)) /* adds together the bits: shift key -> 1 meta key -> 2 control key -> 4 */ #define Coordinate(r,c) ((r) * MaxCols(&(term->screen)) + (c)) #if OPT_DEC_LOCATOR static ANSI reply; #endif /* Selection/extension variables */ /* Raw char position where the selection started */ static int rawRow, rawCol; /* Selected area before CHAR, WORD, LINE selectUnit processing */ static int startRRow, startRCol, endRRow, endRCol = 0; /* Selected area after CHAR, WORD, LINE selectUnit processing */ static int startSRow, startSCol, endSRow, endSCol = 0; /* Valid rows for selection clipping */ static int firstValidRow, lastValidRow; /* Start, end of extension */ static int startERow, startECol, endERow, endECol; /* Saved values of raw selection for extend to restore to */ static int saveStartRRow, saveStartRCol, saveEndRRow, saveEndRCol; /* Saved value of WORD selection for LINE processing to restore to */ static int saveStartWRow, saveStartWCol; /* Multi-click handling */ static int numberOfClicks = 0; static Time lastButtonUpTime = 0; #if OPT_READLINE static Time lastButtonDownTime = 0; static int ExtendingSelection = 0; static Time lastButton3UpTime = 0; static Time lastButton3DoubleDownTime = 0; static int lastButton3row, lastButton3col; /* At the release time */ #endif /* OPT_READLINE */ typedef int SelectUnit; #define SELECTCHAR 0 #define SELECTWORD 1 #define SELECTLINE 2 #define NSELECTUNITS 3 static SelectUnit selectUnit; /* Send emacs escape code when done selecting or extending? */ static int replyToEmacs; static Char *SaveText(TScreen * screen, int row, int scol, int ecol, Char * lp, int *eol); static int Length(TScreen * screen, int row, int scol, int ecol); static void ComputeSelect(int startRow, int startCol, int endRow, int endCol, Bool extend); static void EditorButton(XButtonEvent * event); static void EndExtend(Widget w, XEvent * event, String * params, Cardinal num_params, Bool use_cursor_loc); static void ExtendExtend(int row, int col); static void PointToRowCol(int y, int x, int *r, int *c); static void ReHiliteText(int frow, int fcol, int trow, int tcol); static void SaltTextAway(int crow, int ccol, int row, int col, String * params, Cardinal num_params); static void SelectSet(Widget w, XEvent * event, String * params, Cardinal num_params); static void SelectionReceived PROTO_XT_SEL_CB_ARGS; static void StartSelect(int startrow, int startcol); static void TrackDown(XButtonEvent * event); static void _OwnSelection(XtermWidget termw, String * selections, Cardinal count); static void do_select_end(Widget w, XEvent * event, String * params, Cardinal *num_params, Bool use_cursor_loc); Bool SendMousePosition(Widget w, XEvent * event) { TScreen *screen; if (!IsXtermWidget(w)) return False; screen = &((XtermWidget) w)->screen; /* If send_mouse_pos mode isn't on, we shouldn't be here */ if (screen->send_mouse_pos == MOUSE_OFF) return False; #if OPT_DEC_LOCATOR if (screen->send_mouse_pos == DEC_LOCATOR) { return (SendLocatorPosition(w, event)); } #endif /* OPT_DEC_LOCATOR */ /* Make sure the event is an appropriate type */ if ((screen->send_mouse_pos != BTN_EVENT_MOUSE) && (screen->send_mouse_pos != ANY_EVENT_MOUSE) && event->type != ButtonPress && event->type != ButtonRelease) return False; switch (screen->send_mouse_pos) { case X10_MOUSE: /* X10 compatibility sequences */ if (KeyModifiers == 0) { if (event->type == ButtonPress) EditorButton((XButtonEvent *) event); return True; } return False; case VT200_HIGHLIGHT_MOUSE: /* DEC vt200 hilite tracking */ if (event->type == ButtonPress && KeyModifiers == 0 && event->xbutton.button == Button1) { TrackDown((XButtonEvent *) event); return True; } if (KeyModifiers == 0 || KeyModifiers == ControlMask) { EditorButton((XButtonEvent *) event); return True; } return False; case VT200_MOUSE: /* DEC vt200 compatible */ /* xterm extension for motion reporting. June 1998 */ /* EditorButton() will distinguish between the modes */ case BTN_EVENT_MOUSE: case ANY_EVENT_MOUSE: if (KeyModifiers == 0 || KeyModifiers == ControlMask) { EditorButton((XButtonEvent *) event); return True; } return False; default: return False; } } #if OPT_DEC_LOCATOR #define LocatorCoords( row, col, x, y, oor ) \ if( screen->locator_pixels ) { \ (oor)=False; (row) = (y)+1; (col) = (x)+1; \ /* Limit to screen dimensions */ \ if ((row) < 1) (row) = 1,(oor)=True; \ else if ((row) > screen->border*2+Height(screen)) \ (row) = screen->border*2+Height(screen),(oor)=True; \ if ((col) < 1) (col) = 1,(oor)=True; \ else if ((col) > OriginX(screen)*2+Width(screen)) \ (col) = OriginX(screen)*2+Width(screen),(oor)=True; \ } else { \ (oor)=False; \ /* Compute character position of mouse pointer */ \ (row) = ((y) - screen->border) / FontHeight(screen); \ (col) = ((x) - OriginX(screen)) / FontWidth(screen); \ /* Limit to screen dimensions */ \ if ((row) < 0) (row) = 0,(oor)=True; \ else if ((row) > screen->max_row) \ (row) = screen->max_row,(oor)=True; \ if ((col) < 0) (col) = 0,(oor)=True; \ else if ((col) > screen->max_col) \ (col) = screen->max_col,(oor)=True; \ (row)++; (col)++; \ } Bool SendLocatorPosition(Widget w, XEvent * event) { TScreen *screen = &((XtermWidget) w)->screen; int row, col; Bool oor; int button; int state; /* Make sure the event is an appropriate type */ if ((event->type != ButtonPress && event->type != ButtonRelease && !screen->loc_filter) || (KeyModifiers != 0 && KeyModifiers != ControlMask)) return (False); if ((event->type == ButtonPress && !(screen->locator_events & LOC_BTNS_DN)) || (event->type == ButtonRelease && !(screen->locator_events & LOC_BTNS_UP))) return (True); if (event->type == MotionNotify) { CheckLocatorPosition(w, event); return (True); } /* get button # */ button = event->xbutton.button - 1; LocatorCoords(row, col, event->xbutton.x, event->xbutton.y, oor); /* * DECterm mouse: * * ESCAPE '[' event ; mask ; row ; column '&' 'w' */ reply.a_type = CSI; if (oor) { reply.a_nparam = 1; reply.a_param[0] = 0; /* Event - 0 = locator unavailable */ reply.a_inters = '&'; reply.a_final = 'w'; unparseseq(&reply, screen->respond); if (screen->locator_reset) { MotionOff(screen, term); screen->send_mouse_pos = MOUSE_OFF; } return (True); } /* * event: * 1 no buttons * 2 left button down * 3 left button up * 4 middle button down * 5 middle button up * 6 right button down * 7 right button up * 8 M4 down * 9 M4 up */ reply.a_nparam = 4; switch (event->type) { case ButtonPress: reply.a_param[0] = 2 + (button << 1); break; case ButtonRelease: reply.a_param[0] = 3 + (button << 1); break; default: return (True); } /* * mask: * bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 * M4 down left down middle down right down * * Notice that Button1 (left) and Button3 (right) are swapped in the mask. * Also, mask should be the state after the button press/release, * X provides the state not including the button press/release. */ state = (event->xbutton.state & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8; state ^= 1 << button; /* update mask to "after" state */ state = (state & ~(4 | 1)) | ((state & 1) ? 4 : 0) | ((state & 4) ? 1 : 0); /* swap Button1 & Button3 */ reply.a_param[1] = state; reply.a_param[2] = row; reply.a_param[3] = col; reply.a_inters = '&'; reply.a_final = 'w'; unparseseq(&reply, screen->respond); if (screen->locator_reset) { MotionOff(screen, term); screen->send_mouse_pos = MOUSE_OFF; } /* * DECterm turns the Locator off if a button is pressed while a filter rectangle * is active. This might be a bug, but I don't know, so I'll emulate it anyways. */ if (screen->loc_filter) { screen->send_mouse_pos = MOUSE_OFF; screen->loc_filter = False; screen->locator_events = 0; MotionOff(screen, term); } return (True); } /* * mask: * bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 * M4 down left down middle down right down * * Button1 (left) and Button3 (right) are swapped in the mask relative to X. */ #define ButtonState(state, mask) \ { (state) = ((mask) & (Button1Mask | Button2Mask | Button3Mask | Button4Mask)) >> 8; \ /* swap Button1 & Button3 */ \ (state) = ((state) & ~(4|1)) | (((state)&1)?4:0) | (((state)&4)?1:0); \ } void GetLocatorPosition(XtermWidget w) { TScreen *screen = &w->screen; Window root, child; int rx, ry, x, y; unsigned int mask; int row = 0, col = 0; Bool oor = False; Bool ret = False; int state; /* * DECterm turns the Locator off if the position is requested while a filter rectangle * is active. This might be a bug, but I don't know, so I'll emulate it anyways. */ if (screen->loc_filter) { screen->send_mouse_pos = MOUSE_OFF; screen->loc_filter = False; screen->locator_events = 0; MotionOff(screen, term); } reply.a_type = CSI; if (screen->send_mouse_pos == DEC_LOCATOR) { ret = XQueryPointer(screen->display, VWindow(screen), &root, &child, &rx, &ry, &x, &y, &mask); if (ret) { LocatorCoords(row, col, x, y, oor); } } if (ret == False || oor) { reply.a_nparam = 1; reply.a_param[0] = 0; /* Event - 0 = locator unavailable */ reply.a_inters = '&'; reply.a_final = 'w'; unparseseq(&reply, screen->respond); if (screen->locator_reset) { MotionOff(screen, term); screen->send_mouse_pos = MOUSE_OFF; } return; } ButtonState(state, mask); reply.a_nparam = 4; reply.a_param[0] = 1; /* Event - 1 = response to locator request */ reply.a_param[1] = state; reply.a_param[2] = row; reply.a_param[3] = col; reply.a_inters = '&'; reply.a_final = 'w'; unparseseq(&reply, screen->respond); if (screen->locator_reset) { MotionOff(screen, term); screen->send_mouse_pos = MOUSE_OFF; } } void InitLocatorFilter(XtermWidget w) { TScreen *screen = &w->screen; Window root, child; int rx, ry, x, y; unsigned int mask; int row = 0, col = 0; Bool oor = 0; Bool ret; int state; ret = XQueryPointer(screen->display, VWindow(screen), &root, &child, &rx, &ry, &x, &y, &mask); if (ret) { LocatorCoords(row, col, x, y, oor); } if (ret == False || oor) { /* Locator is unavailable */ if (screen->loc_filter_top != LOC_FILTER_POS || screen->loc_filter_left != LOC_FILTER_POS || screen->loc_filter_bottom != LOC_FILTER_POS || screen->loc_filter_right != LOC_FILTER_POS) { /* * If any explicit coordinates were received, * report immediately with no coordinates. */ reply.a_type = CSI; reply.a_nparam = 1; reply.a_param[0] = 0; /* Event - 0 = locator unavailable */ reply.a_inters = '&'; reply.a_final = 'w'; unparseseq(&reply, screen->respond); if (screen->locator_reset) { MotionOff(screen, term); screen->send_mouse_pos = MOUSE_OFF; } } else { /* * No explicit coordinates were received, and the pointer is * unavailable. Report when the pointer re-enters the window. */ screen->loc_filter = True; MotionOn(screen, term); } return; } /* * Adjust rectangle coordinates: * 1. Replace "LOC_FILTER_POS" with current coordinates * 2. Limit coordinates to screen size * 3. make sure top and left are less than bottom and right, resp. */ if (screen->locator_pixels) { rx = OriginX(screen) * 2 + Width(screen); ry = screen->border * 2 + Height(screen); } else { rx = screen->max_col; ry = screen->max_row; } #define Adjust( coord, def, max ) \ if( (coord) == LOC_FILTER_POS ) (coord) = (def); \ else if ((coord) < 1) (coord) = 1; \ else if ((coord) > (max)) (coord) = (max) Adjust(screen->loc_filter_top, row, ry); Adjust(screen->loc_filter_left, col, rx); Adjust(screen->loc_filter_bottom, row, ry); Adjust(screen->loc_filter_right, col, rx); if (screen->loc_filter_top > screen->loc_filter_bottom) { ry = screen->loc_filter_top; screen->loc_filter_top = screen->loc_filter_bottom; screen->loc_filter_bottom = ry; } if (screen->loc_filter_left > screen->loc_filter_right) { rx = screen->loc_filter_left; screen->loc_filter_left = screen->loc_filter_right; screen->loc_filter_right = rx; } if ((col < screen->loc_filter_left) || (col > screen->loc_filter_right) || (row < screen->loc_filter_top) || (row > screen->loc_filter_bottom)) { /* Pointer is already outside the rectangle - report immediately */ ButtonState(state, mask); reply.a_type = CSI; reply.a_nparam = 4; reply.a_param[0] = 10; /* Event - 10 = locator outside filter */ reply.a_param[1] = state; reply.a_param[2] = row; reply.a_param[3] = col; reply.a_inters = '&'; reply.a_final = 'w'; unparseseq(&reply, screen->respond); if (screen->locator_reset) { MotionOff(screen, term); screen->send_mouse_pos = MOUSE_OFF; } return; } /* * Rectangle is set up. Allow pointer tracking * to detect if the mouse leaves the rectangle. */ screen->loc_filter = True; MotionOn(screen, term); } void CheckLocatorPosition(Widget w, XEvent * event) { TScreen *screen = &((XtermWidget) w)->screen; int row, col; Bool oor; int state; LocatorCoords(row, col, event->xbutton.x, event->xbutton.y, oor); /* * Send report if the pointer left the filter rectangle, if * the pointer left the window, or if the filter rectangle * had no coordinates and the pointer re-entered the window. */ if (oor || (screen->loc_filter_top == LOC_FILTER_POS) || (col < screen->loc_filter_left) || (col > screen->loc_filter_right) || (row < screen->loc_filter_top) || (row > screen->loc_filter_bottom)) { /* Filter triggered - disable it */ screen->loc_filter = False; MotionOff(screen, term); reply.a_type = CSI; if (oor) { reply.a_nparam = 1; reply.a_param[0] = 0; /* Event - 0 = locator unavailable */ } else { ButtonState(state, event->xbutton.state); reply.a_nparam = 4; reply.a_param[0] = 10; /* Event - 10 = locator outside filter */ reply.a_param[1] = state; reply.a_param[2] = row; reply.a_param[3] = col; } reply.a_inters = '&'; reply.a_final = 'w'; unparseseq(&reply, screen->respond); if (screen->locator_reset) { MotionOff(screen, term); screen->send_mouse_pos = MOUSE_OFF; } } } #endif /* OPT_DEC_LOCATOR */ #if OPT_READLINE static int isClick1_clean(XEvent * event) { TScreen *screen = &term->screen; int delta; if (!(event->type == ButtonPress || event->type == ButtonRelease) /* Disable on Shift-Click-1, including the application-mouse modes */ || (KeyModifiers & ShiftMask) || (screen->send_mouse_pos != MOUSE_OFF) /* Kinda duplicate... */ ||ExtendingSelection) /* Was moved */ return 0; if (event->type != ButtonRelease) return 0; if (lastButtonDownTime == (Time) 0) /* first time or once in a blue moon */ delta = term->screen.multiClickTime + 1; else if (event->xbutton.time > lastButtonDownTime) /* most of the time */ delta = event->xbutton.time - lastButtonDownTime; else /* time has rolled over since lastButtonUpTime */ delta = (((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time; return delta <= term->screen.multiClickTime; } static int isDoubleClick3(XEvent * event) { int delta; if (event->type != ButtonRelease || (KeyModifiers & ShiftMask) || event->xbutton.button != Button3) { lastButton3UpTime = 0; /* Disable the cached info */ return 0; } /* Process Btn3Release. */ if (lastButton3DoubleDownTime == (Time) 0) /* No previous click or once in a blue moon */ delta = term->screen.multiClickTime + 1; else if (event->xbutton.time > lastButton3DoubleDownTime) /* most of the time */ delta = event->xbutton.time - lastButton3DoubleDownTime; else /* time has rolled over since lastButton3DoubleDownTime */ delta = (((Time) ~ 0) - lastButton3DoubleDownTime) + event->xbutton.time; if (delta <= term->screen.multiClickTime) { /* Double click */ int row, col; /* Cannot check ExtendingSelection, since mouse-3 always sets it */ PointToRowCol(event->xbutton.y, event->xbutton.x, &row, &col); if (row == lastButton3row && col == lastButton3col) { lastButton3DoubleDownTime = 0; /* Disable the third click */ return 1; } } /* Not a double click, memorize for future check. */ lastButton3UpTime = event->xbutton.time; PointToRowCol(event->xbutton.y, event->xbutton.x, &lastButton3row, &lastButton3col); return 0; } static int CheckSecondPress3(XEvent * event) { int delta, row, col; if (event->type != ButtonPress || (KeyModifiers & ShiftMask) || event->xbutton.button != Button3) { lastButton3DoubleDownTime = 0; /* Disable the cached info */ return 0; } /* Process Btn3Press. */ if (lastButton3UpTime == (Time) 0) /* No previous click or once in a blue moon */ delta = term->screen.multiClickTime + 1; else if (event->xbutton.time > lastButton3UpTime) /* most of the time */ delta = event->xbutton.time - lastButton3UpTime; else /* time has rolled over since lastButton3UpTime */ delta = (((Time) ~ 0) - lastButton3UpTime) + event->xbutton.time; if (delta <= term->screen.multiClickTime) { PointToRowCol(event->xbutton.y, event->xbutton.x, &row, &col); if (row == lastButton3row && col == lastButton3col) { /* A candidate for a double-click */ lastButton3DoubleDownTime = event->xbutton.time; PointToRowCol(event->xbutton.y, event->xbutton.x, &lastButton3row, &lastButton3col); return 1; } lastButton3UpTime = 0; /* Disable the info about the previous click */ } /* Either too long, or moved, disable. */ lastButton3DoubleDownTime = 0; return 0; } static int rowOnCurrentLine(int line, int *deltap) /* must be XButtonEvent */ { TScreen *screen = &term->screen; int l1, l2; *deltap = 0; if (line == screen->cur_row) return 1; if (line < screen->cur_row) l1 = line, l2 = screen->cur_row; else l2 = line, l1 = screen->cur_row; l1--; while (++l1 < l2) if (!ScrnTstWrapped(screen, l1)) return 0; /* Everything is on one "wrapped line" now */ *deltap = line - screen->cur_row; return 1; } static int eventRow(XEvent * event) /* must be XButtonEvent */ { TScreen *screen = &term->screen; return (event->xbutton.y - screen->border) / FontHeight(screen); } static int eventColBetween(XEvent * event) /* must be XButtonEvent */ { TScreen *screen = &term->screen; /* Correct by half a width - we are acting on a boundary, not on a cell. */ return ((event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1) / 2) / FontWidth(screen)); } static int ReadLineMovePoint(int col, int ldelta) { TScreen *screen = &term->screen; Char line[6]; unsigned count = 0; col += ldelta * MaxCols(screen) - screen->cur_col; if (col == 0) return 0; if (screen->control_eight_bits) { line[count++] = CSI; } else { line[count++] = ESC; line[count++] = '['; /* XXX maybe sometimes O is better? */ } line[count++] = (col > 0 ? 'C' : 'D'); if (col < 0) col = -col; while (col--) v_write(screen->respond, line, 3); return 1; } static int ReadLineDelete(int r1, int c1, int r2, int c2) { TScreen *screen = &term->screen; int del; del = c2 - c1 + (r2 - r1) * MaxCols(screen); if (del <= 0) /* Just in case... */ return 0; while (del--) v_write(screen->respond, (Char *) "\177", 1); return 1; } #endif /* OPT_READLINE */ /* ^XM-G */ void DiredButton(Widget w GCC_UNUSED, XEvent * event, /* must be XButtonEvent */ String * params GCC_UNUSED, /* selections */ Cardinal *num_params GCC_UNUSED) { TScreen *screen = &term->screen; Char Line[6]; unsigned line, col; if (event->type == ButtonPress || event->type == ButtonRelease) { line = (event->xbutton.y - screen->border) / FontHeight(screen); col = (event->xbutton.x - OriginX(screen)) / FontWidth(screen); Line[0] = CONTROL('X'); Line[1] = ESC; Line[2] = 'G'; Line[3] = ' ' + col; Line[4] = ' ' + line; v_write(screen->respond, Line, 5); } } #if OPT_READLINE void ReadLineButton(Widget w GCC_UNUSED, XEvent * event, /* must be XButtonEvent */ String * params GCC_UNUSED, /* selections */ Cardinal *num_params GCC_UNUSED) { TScreen *screen = &term->screen; Char Line[6]; int line, col, ldelta = 0; if (!(event->type == ButtonPress || event->type == ButtonRelease) || (screen->send_mouse_pos != MOUSE_OFF) || ExtendingSelection) goto finish; if (event->type == ButtonRelease) { int delta; if (lastButtonDownTime == (Time) 0) /* first time and once in a blue moon */ delta = screen->multiClickTime + 1; else if (event->xbutton.time > lastButtonDownTime) /* most of the time */ delta = event->xbutton.time - lastButtonDownTime; else /* time has rolled over since lastButtonUpTime */ delta = (((Time) ~ 0) - lastButtonDownTime) + event->xbutton.time; if (delta > screen->multiClickTime) goto finish; /* All this work for this... */ } line = (event->xbutton.y - screen->border) / FontHeight(screen); if (line != screen->cur_row) { int l1, l2; if (line < screen->cur_row) l1 = line, l2 = screen->cur_row; else l2 = line, l1 = screen->cur_row; l1--; while (++l1 < l2) if (!ScrnTstWrapped(screen, l1)) goto finish; /* Everything is on one "wrapped line" now */ ldelta = line - screen->cur_row; } /* Correct by half a width - we are acting on a boundary, not on a cell. */ col = (event->xbutton.x - OriginX(screen) + (FontWidth(screen) - 1) / 2) / FontWidth(screen) - screen->cur_col + ldelta * MaxCols(screen); if (col == 0) goto finish; Line[0] = ESC; /* XXX: sometimes it is better to send '['? */ Line[1] = 'O'; Line[2] = (col > 0 ? 'C' : 'D'); if (col < 0) col = -col; while (col--) v_write(screen->respond, Line, 3); finish: if (event->type == ButtonRelease) do_select_end(w, event, params, num_params, False); } #endif /* OPT_READLINE */ /* repeats n or p */ void ViButton(Widget w GCC_UNUSED, XEvent * event, /* must be XButtonEvent */ String * params GCC_UNUSED, /* selections */ Cardinal *num_params GCC_UNUSED) { TScreen *screen = &term->screen; int pty = screen->respond; Char Line[6]; int line; if (event->type == ButtonPress || event->type == ButtonRelease) { line = screen->cur_row - ((event->xbutton.y - screen->border) / FontHeight(screen)); if (line != 0) { Line[0] = ESC; /* force an exit from insert-mode */ v_write(pty, Line, 1); if (line < 0) { line = -line; Line[0] = CONTROL('n'); } else { Line[0] = CONTROL('p'); } while (--line >= 0) v_write(pty, Line, 1); } } } /* * This function handles button-motion events */ /*ARGSUSED*/ void HandleSelectExtend(Widget w, XEvent * event, /* must be XMotionEvent */ String * params GCC_UNUSED, Cardinal *num_params GCC_UNUSED) { TScreen *screen; int row, col; if (!IsXtermWidget(w)) return; screen = &((XtermWidget) w)->screen; screen->selection_time = event->xmotion.time; switch (eventMode) { /* If not in one of the DEC mouse-reporting modes */ case LEFTEXTENSION: case RIGHTEXTENSION: PointToRowCol(event->xmotion.y, event->xmotion.x, &row, &col); ExtendExtend(row, col); break; /* If in motion reporting mode, send mouse position to character process as a key sequence \E[M... */ case NORMAL: /* will get here if send_mouse_pos != MOUSE_OFF */ if (screen->send_mouse_pos == BTN_EVENT_MOUSE || screen->send_mouse_pos == ANY_EVENT_MOUSE) { (void) SendMousePosition(w, event); } break; } } void HandleKeyboardSelectExtend(Widget w, XEvent * event GCC_UNUSED, /* must be XButtonEvent */ String * params GCC_UNUSED, Cardinal *num_params GCC_UNUSED) { TScreen *screen; if (!IsXtermWidget(w)) return; screen = &((XtermWidget) w)->screen; ExtendExtend(screen->cursor_row, screen->cursor_col); } static void do_select_end(Widget w, XEvent * event, /* must be XButtonEvent */ String * params, /* selections */ Cardinal *num_params, Bool use_cursor_loc) { #if OPT_READLINE int ldelta1, ldelta2; TScreen *screen = &term->screen; #endif if (!IsXtermWidget(w)) { return; } ((XtermWidget) w)->screen.selection_time = event->xbutton.time; switch (eventMode) { case NORMAL: (void) SendMousePosition(w, event); break; case LEFTEXTENSION: case RIGHTEXTENSION: EndExtend(w, event, params, *num_params, use_cursor_loc); #if OPT_READLINE if (isClick1_clean(event) && SCREEN_FLAG(screen, click1_moves) && rowOnCurrentLine(eventRow(event), &ldelta1)) { ReadLineMovePoint(eventColBetween(event), ldelta1); } if (isDoubleClick3(event) && SCREEN_FLAG(screen, dclick3_deletes) && rowOnCurrentLine(startSRow, &ldelta1) && rowOnCurrentLine(endSRow, &ldelta2)) { ReadLineMovePoint(endSCol, ldelta2); ReadLineDelete(startSRow, startSCol, endSRow, endSCol); } #endif /* OPT_READLINE */ break; } } void HandleSelectEnd(Widget w, XEvent * event, /* must be XButtonEvent */ String * params, /* selections */ Cardinal *num_params) { do_select_end(w, event, params, num_params, False); } void HandleKeyboardSelectEnd(Widget w, XEvent * event, /* must be XButtonEvent */ String * params, /* selections */ Cardinal *num_params) { do_select_end(w, event, params, num_params, True); } struct _SelectionList { String *params; Cardinal count; Atom *targets; Time time; }; /* convert a UTF-8 string to Latin-1, replacing non Latin-1 characters * by `#'. */ #if OPT_WIDE_CHARS static Char * UTF8toLatin1(Char * s, unsigned len, unsigned long *result) { static Char *buffer; static size_t used; Char *p = s; Char *q; if (used == 0) { buffer = (Char *) XtMalloc(used = len); } else if (len > used) { buffer = (Char *) XtRealloc((char *) buffer, used = len); } q = buffer; /* We're assuming that the xterm widget never contains Unicode control characters. */ while (p < s + len) { if ((*p & 0x80) == 0) { *q++ = *p++; } else if ((*p & 0x7C) == 0x40 && p < s + len - 1) { *q++ = ((*p & 0x03) << 6) | (p[1] & 0x3F); p += 2; } else if ((*p & 0x60) == 0x40) { *q++ = '#'; p += 2; } else if ((*p & 0x50) == 0x40) { *q++ = '#'; p += 3; } else { /* this cannot happen */ *q++ = '#'; p++; } } *result = q - buffer; return buffer; } #endif /* OPT_WIDE_CHARS */ static Atom * _SelectionTargets(Widget w) { static Atom *eightBitSelectionTargets = NULL; TScreen *screen; int n; if (!IsXtermWidget(w)) return NULL; screen = &((XtermWidget) w)->screen; #if OPT_WIDE_CHARS if (screen->wide_chars) { static Atom *utf8SelectionTargets = NULL; if (utf8SelectionTargets == NULL) { utf8SelectionTargets = (Atom *) XtMalloc(5 * sizeof(Atom)); if (utf8SelectionTargets == NULL) { TRACE(("Couldn't allocate utf8SelectionTargets\n")); return NULL; } n = 0; utf8SelectionTargets[n++] = XA_UTF8_STRING(XtDisplay(w)); #ifdef X_HAVE_UTF8_STRING if (screen->i18nSelections) { utf8SelectionTargets[n++] = XA_TEXT(XtDisplay(w)); utf8SelectionTargets[n++] = XA_COMPOUND_TEXT(XtDisplay(w)); } #endif utf8SelectionTargets[n++] = XA_STRING; utf8SelectionTargets[n] = None; } return utf8SelectionTargets; } #endif /* not screen->wide_chars */ if (eightBitSelectionTargets == NULL) { eightBitSelectionTargets = (Atom *) XtMalloc(5 * sizeof(Atom)); if (eightBitSelectionTargets == NULL) { TRACE(("Couldn't allocate eightBitSelectionTargets\n")); return NULL; } n = 0; #ifdef X_HAVE_UTF8_STRING eightBitSelectionTargets[n++] = XA_UTF8_STRING(XtDisplay(w)); #endif if (screen->i18nSelections) { eightBitSelectionTargets[n++] = XA_TEXT(XtDisplay(w)); eightBitSelectionTargets[n++] = XA_COMPOUND_TEXT(XtDisplay(w)); } eightBitSelectionTargets[n++] = XA_STRING; eightBitSelectionTargets[n] = None; } return eightBitSelectionTargets; } /* * Lookup the cut-buffer number, which will be in the range 0-7. * If it is not a cut-buffer, it is the primary selection (-1). */ static int CutBuffer(unsigned code) { int cutbuffer; switch (code) { case XA_CUT_BUFFER0: cutbuffer = 0; break; case XA_CUT_BUFFER1: cutbuffer = 1; break; case XA_CUT_BUFFER2: cutbuffer = 2; break; case XA_CUT_BUFFER3: cutbuffer = 3; break; case XA_CUT_BUFFER4: cutbuffer = 4; break; case XA_CUT_BUFFER5: cutbuffer = 5; break; case XA_CUT_BUFFER6: cutbuffer = 6; break; case XA_CUT_BUFFER7: cutbuffer = 7; break; default: cutbuffer = -1; break; } return cutbuffer; } #define ResetPaste64() term->screen.base64_paste = 0 #if !OPT_PASTE64 static #endif void xtermGetSelection(Widget w, Time ev_time, String * params, /* selections in precedence order */ Cardinal num_params, Atom * targets) { Atom selection; int cutbuffer; Atom target; if (!IsXtermWidget(w)) return; #if OPT_TRACE TRACE(("xtermGetSelection\n")); if (num_params > 0) { Cardinal n; for (n = 0; n < num_params; ++n) { TRACE(("param[%d]:%s\n", n, params[n])); } } #endif XmuInternStrings(XtDisplay(w), params, (Cardinal) 1, &selection); cutbuffer = CutBuffer(selection); TRACE(("Cutbuffer: %d, target: %lu\n", cutbuffer, targets ? (unsigned long) targets[0] : 0)); if (cutbuffer >= 0) { int inbytes; unsigned long nbytes; int fmt8 = 8; Atom type = XA_STRING; char *line; /* 'line' is freed in SelectionReceived */ line = XFetchBuffer(XtDisplay(w), &inbytes, cutbuffer); nbytes = (unsigned long) inbytes; if (nbytes > 0) SelectionReceived(w, NULL, &selection, &type, (XtPointer) line, &nbytes, &fmt8); else if (num_params > 1) xtermGetSelection(w, ev_time, params + 1, num_params - 1, NULL); #if OPT_PASTE64 else ResetPaste64(); #endif return; } else { struct _SelectionList *list; if (targets == NULL || targets[0] == None) { targets = _SelectionTargets(w); } if (targets != 0) { target = targets[0]; if (targets[1] == None) { /* last target in list */ params++; num_params--; targets = _SelectionTargets(w); } else { targets = &(targets[1]); } if (num_params) { /* 'list' is freed in SelectionReceived */ list = XtNew(struct _SelectionList); if (list != 0) { list->params = params; list->count = num_params; list->targets = targets; list->time = ev_time; } } else { list = NULL; } XtGetSelectionValue(w, selection, target, SelectionReceived, (XtPointer) list, ev_time); } } } #if OPT_TRACE && OPT_WIDE_CHARS static void GettingSelection(Display * dpy, Atom type, Char * line, unsigned long len) { Char *cp; char *name; name = XGetAtomName(dpy, type); TRACE(("Getting %s (%ld)\n", name, (long int) type)); for (cp = line; cp < line + len; cp++) { TRACE(("[%d:%lu]", cp + 1 - line, len)); if (isprint(*cp)) { TRACE(("%c\n", *cp)); } else { TRACE(("\\x%02x\n", *cp)); } } } #else #define GettingSelection(dpy,type,line,len) /* nothing */ #endif #ifdef VMS # define tty_vwrite(pty,lag,l) tt_write(lag,l) #else /* !( VMS ) */ # define tty_vwrite(pty,lag,l) v_write(pty,lag,l) #endif /* defined VMS */ #if OPT_PASTE64 /* Return base64 code character given 6-bit number */ static const char base64_code[] = "\ ABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyz\ 0123456789+/"; static void base64_flush(TScreen * screen) { Char x; switch (screen->base64_count) { case 0: break; case 2: x = base64_code[screen->base64_accu << 4]; tty_vwrite(screen->respond, &x, 1); break; case 4: x = base64_code[screen->base64_accu << 2]; tty_vwrite(screen->respond, &x, 1); break; } if (screen->base64_pad & 3) tty_vwrite(screen->respond, (Char *) "===", 4 - (screen->base64_pad & 3)); screen->base64_count = 0; screen->base64_accu = 0; screen->base64_pad = 0; } #endif /* OPT_PASTE64 */ static void _qWriteSelectionData(TScreen * screen, Char * lag, unsigned length) { #if OPT_PASTE64 if (screen->base64_paste) { /* Send data as base64 */ Char *p = lag; Char buf[64]; unsigned x = 0; while (length--) { switch (screen->base64_count) { case 0: buf[x++] = base64_code[*p >> 2]; screen->base64_accu = (*p & 0x3); screen->base64_count = 2; ++p; break; case 2: buf[x++] = base64_code[(screen->base64_accu << 4) + (*p >> 4)]; screen->base64_accu = (*p & 0xF); screen->base64_count = 4; ++p; break; case 4: buf[x++] = base64_code[(screen->base64_accu << 2) + (*p >> 6)]; buf[x++] = base64_code[*p & 0x3F]; screen->base64_accu = 0; screen->base64_count = 0; ++p; break; } if (x >= 63) { /* Write 63 or 64 characters */ screen->base64_pad += x; tty_vwrite(screen->respond, buf, x); x = 0; } } if (x != 0) { screen->base64_pad += x; tty_vwrite(screen->respond, buf, x); } } else #endif /* OPT_PASTE64 */ #if OPT_READLINE if (SCREEN_FLAG(screen, paste_quotes)) { while (length--) { tty_vwrite(screen->respond, (Char *) "\026", 1); /* Control-V */ tty_vwrite(screen->respond, lag++, 1); } } else #endif tty_vwrite(screen->respond, lag, length); } static void _WriteSelectionData(TScreen * screen, Char * line, int length) { /* Write data to pty a line at a time. */ /* Doing this one line at a time may no longer be necessary because v_write has been re-written. */ Char *lag, *end; /* in the VMS version, if tt_pasting isn't set to True then qio reads aren't blocked and an infinite loop is entered, where the pasted text shows up as new input, goes in again, shows up again, ad nauseum. */ #ifdef VMS tt_pasting = True; #endif end = &line[length]; lag = line; #if OPT_PASTE64 if (screen->base64_paste) { _qWriteSelectionData(screen, lag, (unsigned) (end - lag)); base64_flush(screen); } else #endif { if (!SCREEN_FLAG(screen, paste_literal_nl)) { Char *cp; for (cp = line; cp != end; cp++) { if (*cp == '\n') { *cp = '\r'; _qWriteSelectionData(screen, lag, (unsigned) (cp - lag + 1)); lag = cp + 1; } } } if (lag != end) { _qWriteSelectionData(screen, lag, (unsigned) (end - lag)); } } #ifdef VMS tt_pasting = False; tt_start_read(); /* reenable reads or a character may be lost */ #endif } #if OPT_READLINE static void _WriteKey(TScreen * screen, Char * in) { Char line[16]; unsigned count = 0; unsigned length = strlen((char *) in); if (screen->control_eight_bits) { line[count++] = CSI; } else { line[count++] = ESC; line[count++] = '['; } while (length--) line[count++] = *in++; line[count++] = '~'; tty_vwrite(screen->respond, line, count); } #endif /* OPT_READLINE */ /* SelectionReceived: stuff received selection text into pty */ /* ARGSUSED */ static void SelectionReceived(Widget w, XtPointer client_data, Atom * selection GCC_UNUSED, Atom * type, XtPointer value, unsigned long *length, int *format GCC_UNUSED) { char **text_list = NULL; int text_list_count; XTextProperty text_prop; TScreen *screen; Display *dpy; #if OPT_TRACE && OPT_WIDE_CHARS Char *line = (Char *) value; #endif if (!IsXtermWidget(w)) return; screen = &((XtermWidget) w)->screen; dpy = XtDisplay(w); if (*type == 0 /*XT_CONVERT_FAIL */ || *length == 0 || value == NULL) goto fail; text_prop.value = (unsigned char *) value; text_prop.encoding = *type; text_prop.format = *format; text_prop.nitems = *length; #if OPT_WIDE_CHARS if (screen->wide_chars) { if (*type == XA_UTF8_STRING(XtDisplay(w)) || *type == XA_STRING || *type == XA_COMPOUND_TEXT(XtDisplay(w))) { GettingSelection(dpy, *type, line, *length); if (Xutf8TextPropertyToTextList(dpy, &text_prop, &text_list, &text_list_count) < 0) { TRACE(("Conversion failed\n")); text_list = NULL; } } } else #endif /* OPT_WIDE_CHARS */ { /* Convert the selection to locale's multibyte encoding. */ /* There's no need to special-case UTF8_STRING. If Xlib doesn't know about it, we didn't request it. If a broken selection holder sends it anyhow, the conversion function will fail. */ if (*type == XA_UTF8_STRING(XtDisplay(w)) || *type == XA_STRING || *type == XA_COMPOUND_TEXT(XtDisplay(w))) { Status rc; GettingSelection(dpy, *type, line, *length); if (*type == XA_STRING && screen->brokenSelections) { rc = XTextPropertyToStringList(&text_prop, &text_list, &text_list_count); } else { rc = XmbTextPropertyToTextList(dpy, &text_prop, &text_list, &text_list_count); } if (rc < 0) { TRACE(("Conversion failed\n")); text_list = NULL; } } } if (text_list != NULL && text_list_count != 0) { int i; #if OPT_PASTE64 if (screen->base64_paste) { ; } else #endif #if OPT_READLINE if (SCREEN_FLAG(screen, paste_brackets)) { _WriteKey(screen, (Char *) "200"); } #endif for (i = 0; i < text_list_count; i++) { int len = strlen(text_list[i]); _WriteSelectionData(screen, (Char *) text_list[i], len); } #if OPT_PASTE64 if (screen->base64_paste) { ResetPaste64(); } else #endif #if OPT_READLINE if (SCREEN_FLAG(screen, paste_brackets)) { _WriteKey(screen, (Char *) "201"); } #endif XFreeStringList(text_list); } else goto fail; XtFree((char *) client_data); XtFree((char *) value); return; fail: if (client_data != 0) { struct _SelectionList *list = (struct _SelectionList *) client_data; xtermGetSelection(w, list->time, list->params, list->count, list->targets); XtFree((char *) client_data); #if OPT_PASTE64 } else { ResetPaste64(); #endif } return; } void HandleInsertSelection(Widget w, XEvent * event, /* assumed to be XButtonEvent* */ String * params, /* selections in precedence order */ Cardinal *num_params) { #if OPT_READLINE int ldelta; TScreen *screen = &((XtermWidget) w)->screen; #endif if (SendMousePosition(w, event)) return; #if OPT_READLINE if ((event->type == ButtonPress || event->type == ButtonRelease) /* Disable on Shift-mouse, including the application-mouse modes */ && !(KeyModifiers & ShiftMask) && (screen->send_mouse_pos == MOUSE_OFF) && SCREEN_FLAG(screen, paste_moves) && rowOnCurrentLine(eventRow(event), &ldelta)) ReadLineMovePoint(eventColBetween(event), ldelta); #endif /* OPT_READLINE */ xtermGetSelection(w, event->xbutton.time, params, *num_params, NULL); } static SelectUnit EvalSelectUnit(Time buttonDownTime, SelectUnit defaultUnit) { int delta; if (lastButtonUpTime == (Time) 0) /* first time and once in a blue moon */ delta = term->screen.multiClickTime + 1; else if (buttonDownTime > lastButtonUpTime) /* most of the time */ delta = buttonDownTime - lastButtonUpTime; else /* time has rolled over since lastButtonUpTime */ delta = (((Time) ~ 0) - lastButtonUpTime) + buttonDownTime; if (delta > term->screen.multiClickTime) { numberOfClicks = 1; return defaultUnit; } else { ++numberOfClicks; return ((selectUnit + 1) % NSELECTUNITS); } } static void do_select_start(Widget w, XEvent * event, /* must be XButtonEvent* */ int startrow, int startcol) { if (SendMousePosition(w, event)) return; selectUnit = EvalSelectUnit(event->xbutton.time, SELECTCHAR); replyToEmacs = False; #if OPT_READLINE lastButtonDownTime = event->xbutton.time; #endif StartSelect(startrow, startcol); } /* ARGSUSED */ void HandleSelectStart(Widget w, XEvent * event, /* must be XButtonEvent* */ String * params GCC_UNUSED, Cardinal *num_params GCC_UNUSED) { TScreen *screen; int startrow, startcol; if (!IsXtermWidget(w)) return; screen = &((XtermWidget) w)->screen; firstValidRow = 0; lastValidRow = screen->max_row; PointToRowCol(event->xbutton.y, event->xbutton.x, &startrow, &startcol); #if OPT_READLINE ExtendingSelection = 0; #endif do_select_start(w, event, startrow, startcol); } /* ARGSUSED */ void HandleKeyboardSelectStart(Widget w, XEvent * event, /* must be XButtonEvent* */ String * params GCC_UNUSED, Cardinal *num_params GCC_UNUSED) { TScreen *screen; if (!IsXtermWidget(w)) return; screen = &((XtermWidget) w)->screen; do_select_start(w, event, screen->cursor_row, screen->cursor_col); } static void TrackDown(XButtonEvent * event) { int startrow, startcol; selectUnit = EvalSelectUnit(event->time, SELECTCHAR); if (numberOfClicks > 1) { PointToRowCol(event->y, event->x, &startrow, &startcol); replyToEmacs = True; StartSelect(startrow, startcol); } else { waitingForTrackInfo = True; EditorButton((XButtonEvent *) event); } } #define boundsCheck(x) if (x < 0) \ x = 0; \ else if (x >= screen->max_row) \ x = screen->max_row void TrackMouse(int func, int startrow, int startcol, int firstrow, int lastrow) { TScreen *screen = &term->screen; if (!waitingForTrackInfo) { /* Timed out, so ignore */ return; } waitingForTrackInfo = False; if (func == 0) return; boundsCheck(startrow); boundsCheck(firstrow); boundsCheck(lastrow); firstValidRow = firstrow; lastValidRow = lastrow; replyToEmacs = True; StartSelect(startrow, startcol); } static void StartSelect(int startrow, int startcol) { TScreen *screen = &term->screen; TRACE(("StartSelect row=%d, col=%d\n", startrow, startcol)); if (screen->cursor_state) HideCursor(); if (numberOfClicks == 1) { /* set start of selection */ rawRow = startrow; rawCol = startcol; } /* else use old values in rawRow, Col */ saveStartRRow = startERow = rawRow; saveStartRCol = startECol = rawCol; saveEndRRow = endERow = rawRow; saveEndRCol = endECol = rawCol; if (Coordinate(startrow, startcol) < Coordinate(rawRow, rawCol)) { eventMode = LEFTEXTENSION; startERow = startrow; startECol = startcol; } else { eventMode = RIGHTEXTENSION; endERow = startrow; endECol = startcol; } ComputeSelect(startERow, startECol, endERow, endECol, False); } static void EndExtend(Widget w, XEvent * event, /* must be XButtonEvent */ String * params, /* selections */ Cardinal num_params, Bool use_cursor_loc) { int row, col; unsigned count; TScreen *screen = &term->screen; Char line[9]; if (use_cursor_loc) { row = screen->cursor_row; col = screen->cursor_col; } else { PointToRowCol(event->xbutton.y, event->xbutton.x, &row, &col); } ExtendExtend(row, col); lastButtonUpTime = event->xbutton.time; if (startSRow != endSRow || startSCol != endSCol) { if (replyToEmacs) { count = 0; if (screen->control_eight_bits) { line[count++] = CSI; } else { line[count++] = ESC; line[count++] = '['; } if (rawRow == startSRow && rawCol == startSCol && row == endSRow && col == endSCol) { /* Use short-form emacs select */ line[count++] = 't'; line[count++] = ' ' + endSCol + 1; line[count++] = ' ' + endSRow + 1; } else { /* long-form, specify everything */ line[count++] = 'T'; line[count++] = ' ' + startSCol + 1; line[count++] = ' ' + startSRow + 1; line[count++] = ' ' + endSCol + 1; line[count++] = ' ' + endSRow + 1; line[count++] = ' ' + col + 1; line[count++] = ' ' + row + 1; } v_write(screen->respond, line, count); TrackText(0, 0, 0, 0); } } SelectSet(w, event, params, num_params); eventMode = NORMAL; } void HandleSelectSet(Widget w, XEvent * event, String * params, Cardinal *num_params) { SelectSet(w, event, params, *num_params); } /* ARGSUSED */ static void SelectSet(Widget w GCC_UNUSED, XEvent * event GCC_UNUSED, String * params, Cardinal num_params) { TRACE(("SelectSet\n")); /* Only do select stuff if non-null select */ if (startSRow != endSRow || startSCol != endSCol) { SaltTextAway(startSRow, startSCol, endSRow, endSCol, params, num_params); } else { DisownSelection(term); } } #define Abs(x) ((x) < 0 ? -(x) : (x)) /* ARGSUSED */ static void do_start_extend(Widget w, XEvent * event, /* must be XButtonEvent* */ String * params GCC_UNUSED, Cardinal *num_params GCC_UNUSED, Bool use_cursor_loc) { TScreen *screen; int row, col, coord; if (!IsXtermWidget(w)) return; screen = &((XtermWidget) w)->screen; if (SendMousePosition(w, event)) return; firstValidRow = 0; lastValidRow = screen->max_row; #if OPT_READLINE if ((KeyModifiers & ShiftMask) || event->xbutton.button != Button3 || !(SCREEN_FLAG(screen, dclick3_deletes))) #endif selectUnit = EvalSelectUnit(event->xbutton.time, selectUnit); replyToEmacs = False; #if OPT_READLINE CheckSecondPress3(event); #endif if (numberOfClicks == 1 || (SCREEN_FLAG(screen, dclick3_deletes) /* Dclick special */ &&!(KeyModifiers & ShiftMask))) { /* Save existing selection so we can reestablish it if the guy extends past the other end of the selection */ saveStartRRow = startERow = startRRow; saveStartRCol = startECol = startRCol; saveEndRRow = endERow = endRRow; saveEndRCol = endECol = endRCol; } else { /* He just needed the selection mode changed, use old values. */ startERow = startRRow = saveStartRRow; startECol = startRCol = saveStartRCol; endERow = endRRow = saveEndRRow; endECol = endRCol = saveEndRCol; } if (use_cursor_loc) { row = screen->cursor_row; col = screen->cursor_col; } else { PointToRowCol(event->xbutton.y, event->xbutton.x, &row, &col); } coord = Coordinate(row, col); if (Abs(coord - Coordinate(startSRow, startSCol)) < Abs(coord - Coordinate(endSRow, endSCol)) || coord < Coordinate(startSRow, startSCol)) { /* point is close to left side of selection */ eventMode = LEFTEXTENSION; startERow = row; startECol = col; } else { /* point is close to left side of selection */ eventMode = RIGHTEXTENSION; endERow = row; endECol = col; } ComputeSelect(startERow, startECol, endERow, endECol, True); #if OPT_READLINE if (startSRow != endSRow || startSCol != endSCol) ExtendingSelection = 1; #endif } static void ExtendExtend(int row, int col) { int coord = Coordinate(row, col); TRACE(("ExtendExtend row=%d, col=%d\n", row, col)); if (eventMode == LEFTEXTENSION && (coord + (selectUnit != SELECTCHAR)) > Coordinate(endSRow, endSCol)) { /* Whoops, he's changed his mind. Do RIGHTEXTENSION */ eventMode = RIGHTEXTENSION; startERow = saveStartRRow; startECol = saveStartRCol; } else if (eventMode == RIGHTEXTENSION && coord < Coordinate(startSRow, startSCol)) { /* Whoops, he's changed his mind. Do LEFTEXTENSION */ eventMode = LEFTEXTENSION; endERow = saveEndRRow; endECol = saveEndRCol; } if (eventMode == LEFTEXTENSION) { startERow = row; startECol = col; } else { endERow = row; endECol = col; } ComputeSelect(startERow, startECol, endERow, endECol, False); #if OPT_READLINE if (startSRow != endSRow || startSCol != endSCol) ExtendingSelection = 1; #endif } void HandleStartExtend(Widget w, XEvent * event, /* must be XButtonEvent* */ String * params, /* unused */ Cardinal *num_params) /* unused */ { do_start_extend(w, event, params, num_params, False); } void HandleKeyboardStartExtend(Widget w, XEvent * event, /* must be XButtonEvent* */ String * params, /* unused */ Cardinal *num_params) /* unused */ { do_start_extend(w, event, params, num_params, True); } void ScrollSelection(TScreen * screen, int amount, Bool always) { int minrow = -screen->savedlines - screen->topline; int maxrow = screen->max_row - screen->topline; int maxcol = screen->max_col; #define scroll_update_one(row, col) \ row += amount; \ if (row < minrow) { \ row = minrow; \ col = 0; \ } \ if (row > maxrow) { \ row = maxrow; \ col = maxcol; \ } scroll_update_one(startRRow, startRCol); scroll_update_one(endRRow, endRCol); scroll_update_one(startSRow, startSCol); scroll_update_one(endSRow, endSCol); scroll_update_one(rawRow, rawCol); /* * If we are told to scroll the selection but it lies outside the scrolling * margins, then that could cause the selection to move (bad). It is not * simple to fix, because this function is called both for the scrollbar * actions as well as application scrolling. The 'always' flag is set in * the former case. The rest of the logic handles the latter. */ if (ScrnHaveSelection(screen)) { int adjust; adjust = screen->startHRow + screen->topline; if (always || !ScrnHaveLineMargins(screen) || ScrnIsLineInMargins(screen, adjust)) { scroll_update_one(screen->startHRow, screen->startHCol); } adjust = screen->endHRow + screen->topline; if (always || !ScrnHaveLineMargins(screen) || ScrnIsLineInMargins(screen, adjust)) { scroll_update_one(screen->endHRow, screen->endHCol); } } screen->startHCoord = Coordinate(screen->startHRow, screen->startHCol); screen->endHCoord = Coordinate(screen->endHRow, screen->endHCol); } /*ARGSUSED*/ void ResizeSelection(TScreen * screen GCC_UNUSED, int rows, int cols) { rows--; /* decr to get 0-max */ cols--; if (startRRow > rows) startRRow = rows; if (startSRow > rows) startSRow = rows; if (endRRow > rows) endRRow = rows; if (endSRow > rows) endSRow = rows; if (rawRow > rows) rawRow = rows; if (startRCol > cols) startRCol = cols; if (startSCol > cols) startSCol = cols; if (endRCol > cols) endRCol = cols; if (endSCol > cols) endSCol = cols; if (rawCol > cols) rawCol = cols; } #if OPT_WIDE_CHARS Bool iswide(int i) { return (i == HIDDEN_CHAR) || (my_wcwidth(i) == 2); } #define isWideCell(row, col) iswide((int)XTERM_CELL(row, col)) #endif static void PointToRowCol(int y, int x, int *r, int *c) /* Convert pixel coordinates to character coordinates. Rows are clipped between firstValidRow and lastValidRow. Columns are clipped between to be 0 or greater, but are not clipped to some maximum value. */ { TScreen *screen = &term->screen; int row, col; row = (y - screen->border) / FontHeight(screen); if (row < firstValidRow) row = firstValidRow; else if (row > lastValidRow) row = lastValidRow; col = (x - OriginX(screen)) / FontWidth(screen); if (col < 0) col = 0; else if (col > MaxCols(screen)) { col = MaxCols(screen); } #if OPT_WIDE_CHARS /* * If we got a click on the right half of a doublewidth character, * pretend it happened on the left half. */ if (col > 0 && isWideCell(row, col - 1) && (XTERM_CELL(row, col) == HIDDEN_CHAR)) { col -= 1; } #endif *r = row; *c = col; } static int LastTextCol(int row) { TScreen *screen = &term->screen; int i; Char *ch; if ((row += screen->topline) + screen->savedlines >= 0) { for (i = screen->max_col, ch = SCRN_BUF_ATTRS(screen, row) + i; i >= 0 && !(*ch & CHARDRAWN); ch--, i--) ; #if OPT_DEC_CHRSET if (CSET_DOUBLE(SCRN_BUF_CSETS(screen, row)[0])) { i *= 2; } #endif } else { i = -1; } return (i); } #if !OPT_WIDE_CHARS /* ** double click table for cut and paste in 8 bits ** ** This table is divided in four parts : ** ** - control characters [0,0x1f] U [0x80,0x9f] ** - separators [0x20,0x3f] U [0xa0,0xb9] ** - binding characters [0x40,0x7f] U [0xc0,0xff] ** - exceptions */ /* *INDENT-OFF* */ static int charClass[256] = { /* NUL SOH STX ETX EOT ENQ ACK BEL */ 32, 1, 1, 1, 1, 1, 1, 1, /* BS HT NL VT NP CR SO SI */ 1, 32, 1, 1, 1, 1, 1, 1, /* DLE DC1 DC2 DC3 DC4 NAK SYN ETB */ 1, 1, 1, 1, 1, 1, 1, 1, /* CAN EM SUB ESC FS GS RS US */ 1, 1, 1, 1, 1, 1, 1, 1, /* SP ! " # $ % & ' */ 32, 33, 34, 35, 36, 37, 38, 39, /* ( ) * + , - . / */ 40, 41, 42, 43, 44, 45, 46, 47, /* 0 1 2 3 4 5 6 7 */ 48, 48, 48, 48, 48, 48, 48, 48, /* 8 9 : ; < = > ? */ 48, 48, 58, 59, 60, 61, 62, 63, /* @ A B C D E F G */ 64, 48, 48, 48, 48, 48, 48, 48, /* H I J K L M N O */ 48, 48, 48, 48, 48, 48, 48, 48, /* P Q R S T U V W */ 48, 48, 48, 48, 48, 48, 48, 48, /* X Y Z [ \ ] ^ _ */ 48, 48, 48, 91, 92, 93, 94, 48, /* ` a b c d e f g */ 96, 48, 48, 48, 48, 48, 48, 48, /* h i j k l m n o */ 48, 48, 48, 48, 48, 48, 48, 48, /* p q r s t u v w */ 48, 48, 48, 48, 48, 48, 48, 48, /* x y z { | } ~ DEL */ 48, 48, 48, 123, 124, 125, 126, 1, /* x80 x81 x82 x83 IND NEL SSA ESA */ 1, 1, 1, 1, 1, 1, 1, 1, /* HTS HTJ VTS PLD PLU RI SS2 SS3 */ 1, 1, 1, 1, 1, 1, 1, 1, /* DCS PU1 PU2 STS CCH MW SPA EPA */ 1, 1, 1, 1, 1, 1, 1, 1, /* x98 x99 x9A CSI ST OSC PM APC */ 1, 1, 1, 1, 1, 1, 1, 1, /* - i c/ L ox Y- | So */ 160, 161, 162, 163, 164, 165, 166, 167, /* .. c0 ip << _ R0 - */ 168, 169, 170, 171, 172, 173, 174, 175, /* o +- 2 3 ' u q| . */ 176, 177, 178, 179, 180, 181, 182, 183, /* , 1 2 >> 1/4 1/2 3/4 ? */ 184, 185, 186, 187, 188, 189, 190, 191, /* A` A' A^ A~ A: Ao AE C, */ 48, 48, 48, 48, 48, 48, 48, 48, /* E` E' E^ E: I` I' I^ I: */ 48, 48, 48, 48, 48, 48, 48, 48, /* D- N~ O` O' O^ O~ O: X */ 48, 48, 48, 48, 48, 48, 48, 215, /* O/ U` U' U^ U: Y' P B */ 48, 48, 48, 48, 48, 48, 48, 48, /* a` a' a^ a~ a: ao ae c, */ 48, 48, 48, 48, 48, 48, 48, 48, /* e` e' e^ e: i` i' i^ i: */ 48, 48, 48, 48, 48, 48, 48, 48, /* d n~ o` o' o^ o~ o: -: */ 48, 48, 48, 48, 48, 48, 48, 247, /* o/ u` u' u^ u: y' P y: */ 48, 48, 48, 48, 48, 48, 48, 48}; /* *INDENT-ON* */ int SetCharacterClassRange(int low, /* in range of [0..255] */ int high, int value) /* arbitrary */ { if (low < 0 || high > 255 || high < low) return (-1); for (; low <= high; low++) charClass[low] = value; return (0); } #endif #if OPT_WIDE_CHARS static int class_of(TScreen * screen, int row, int col) { int value; #if OPT_DEC_CHRSET if (CSET_DOUBLE(SCRN_BUF_CSETS(screen, row + screen->topline)[0])) { col /= 2; } #endif value = XTERM_CELL(row, col); if_OPT_WIDE_CHARS(screen, { return CharacterClass(value); }); return CharacterClass(value); } #define ClassSelects(screen, row, col, cclass) \ (class_of(screen, row, col) == cclass \ || XTERM_CELL(row, col) == HIDDEN_CHAR) #else #define class_of(screen,row,col) charClass[XTERM_CELL(row, col)] #define ClassSelects(screen, row, col, cclass) \ (class_of(screen,row, col) == cclass) #endif /* * sets startSRow startSCol endSRow endSCol * ensuring that they have legal values */ static void ComputeSelect(int startRow, int startCol, int endRow, int endCol, Bool extend) { TScreen *screen = &term->screen; int length; int cclass; TRACE(("ComputeSelect(startRow=%d, startCol=%d, endRow=%d, endCol=%d, %sextend)\n", startRow, startCol, endRow, endCol, extend ? "" : "no")); #if OPT_WIDE_CHARS if (startCol > 1 && isWideCell(startRow, startCol - 1) && XTERM_CELL(startRow, startCol - 0) == HIDDEN_CHAR) { fprintf(stderr, "Adjusting start. Changing downwards from %i.\n", startCol); startCol -= 1; if (endCol == (startCol + 1)) endCol--; } if (endCol > 1 && isWideCell(endRow, endCol - 1) && XTERM_CELL(endRow, endCol) == HIDDEN_CHAR) { endCol += 1; } #endif if (Coordinate(startRow, startCol) <= Coordinate(endRow, endCol)) { startSRow = startRRow = startRow; startSCol = startRCol = startCol; endSRow = endRRow = endRow; endSCol = endRCol = endCol; } else { /* Swap them */ startSRow = startRRow = endRow; startSCol = startRCol = endCol; endSRow = endRRow = startRow; endSCol = endRCol = startCol; } switch (selectUnit) { case SELECTCHAR: if (startSCol > (LastTextCol(startSRow) + 1)) { startSCol = 0; startSRow++; } if (endSCol > (LastTextCol(endSRow) + 1)) { endSCol = 0; endSRow++; } break; case SELECTWORD: if (startSCol > (LastTextCol(startSRow) + 1)) { startSCol = 0; startSRow++; } else { cclass = class_of(screen, startSRow, startSCol); do { --startSCol; if (startSCol < 0 && ScrnTstWrapped(screen, startSRow - 1)) { --startSRow; startSCol = LastTextCol(startSRow); } } while (startSCol >= 0 && ClassSelects(screen, startSRow, startSCol, cclass)); ++startSCol; } #if OPT_WIDE_CHARS if (startSCol && XTERM_CELL(startSRow, startSCol) == HIDDEN_CHAR) startSCol++; #endif if (endSCol > (LastTextCol(endSRow) + 1)) { endSCol = 0; endSRow++; } else { length = LastTextCol(endSRow); cclass = class_of(screen, endSRow, endSCol); do { ++endSCol; if (endSCol > length && ScrnTstWrapped(screen, endSRow)) { endSCol = 0; ++endSRow; length = LastTextCol(endSRow); } } while (endSCol <= length && ClassSelects(screen, endSRow, endSCol, cclass)); /* Word select selects if pointing to any char in "word", especially in that it includes the last character in a word. So no --endSCol and do special eol handling */ if (endSCol > length + 1) { endSCol = 0; ++endSRow; } } #if OPT_WIDE_CHARS if (endSCol && XTERM_CELL(endSRow, endSCol) == HIDDEN_CHAR) endSCol++; #endif saveStartWRow = startSRow; saveStartWCol = startSCol; break; case SELECTLINE: while (ScrnTstWrapped(screen, endSRow)) { ++endSRow; } if (term->screen.cutToBeginningOfLine || startSRow < saveStartWRow) { startSCol = 0; while (ScrnTstWrapped(screen, startSRow - 1)) { --startSRow; } } else if (!extend) { if ((startRow < saveStartWRow) || (startRow == saveStartWRow && startCol < saveStartWCol)) { startSCol = 0; while (ScrnTstWrapped(screen, startSRow - 1)) { --startSRow; } } else { startSRow = saveStartWRow; startSCol = saveStartWCol; } } if (term->screen.cutNewline) { endSCol = 0; ++endSRow; } else { endSCol = LastTextCol(endSRow) + 1; } break; } /* check boundaries */ ScrollSelection(screen, 0, False); TrackText(startSRow, startSCol, endSRow, endSCol); return; } void TrackText(int frow, int fcol, int trow, int tcol) /* Guaranteed (frow, fcol) <= (trow, tcol) */ { int from, to; TScreen *screen = &term->screen; int old_startrow, old_startcol, old_endrow, old_endcol; TRACE(("TrackText(frow=%d, fcol=%d, trow=%d, tcol=%d)\n", frow, fcol, trow, tcol)); old_startrow = screen->startHRow; old_startcol = screen->startHCol; old_endrow = screen->endHRow; old_endcol = screen->endHCol; if (frow == old_startrow && fcol == old_startcol && trow == old_endrow && tcol == old_endcol) return; screen->startHRow = frow; screen->startHCol = fcol; screen->endHRow = trow; screen->endHCol = tcol; from = Coordinate(frow, fcol); to = Coordinate(trow, tcol); if (to <= screen->startHCoord || from > screen->endHCoord) { /* No overlap whatsoever between old and new hilite */ ReHiliteText(old_startrow, old_startcol, old_endrow, old_endcol); ReHiliteText(frow, fcol, trow, tcol); } else { if (from < screen->startHCoord) { /* Extend left end */ ReHiliteText(frow, fcol, old_startrow, old_startcol); } else if (from > screen->startHCoord) { /* Shorten left end */ ReHiliteText(old_startrow, old_startcol, frow, fcol); } if (to > screen->endHCoord) { /* Extend right end */ ReHiliteText(old_endrow, old_endcol, trow, tcol); } else if (to < screen->endHCoord) { /* Shorten right end */ ReHiliteText(trow, tcol, old_endrow, old_endcol); } } screen->startHCoord = from; screen->endHCoord = to; } static void ReHiliteText(int frow, int fcol, int trow, int tcol) /* Guaranteed that (frow, fcol) <= (trow, tcol) */ { TScreen *screen = &term->screen; int i; TRACE(("ReHiliteText from %d.%d to %d.%d\n", frow, fcol, trow, tcol)); if (frow < 0) frow = fcol = 0; else if (frow > screen->max_row) return; /* nothing to do, since trow >= frow */ if (trow < 0) return; /* nothing to do, since frow <= trow */ else if (trow > screen->max_row) { trow = screen->max_row; tcol = MaxCols(screen); } if (frow == trow && fcol == tcol) return; if (frow != trow) { /* do multiple rows */ if ((i = screen->max_col - fcol + 1) > 0) { /* first row */ ScrnRefresh(screen, frow, fcol, 1, i, True); } if ((i = trow - frow - 1) > 0) { /* middle rows */ ScrnRefresh(screen, frow + 1, 0, i, MaxCols(screen), True); } if (tcol > 0 && trow <= screen->max_row) { /* last row */ ScrnRefresh(screen, trow, 0, 1, tcol, True); } } else { /* do single row */ ScrnRefresh(screen, frow, fcol, 1, tcol - fcol, True); } } static void SaltTextAway(int crow, int ccol, int row, int col, String * params, /* selections */ Cardinal num_params) /* Guaranteed that (crow, ccol) <= (row, col), and that both points are valid (may have row = screen->max_row+1, col = 0) */ { TScreen *screen = &term->screen; int i, j = 0; int eol; Char *line; Char *lp; if (crow == row && ccol > col) { int tmp = ccol; ccol = col; col = tmp; } --col; /* first we need to know how long the string is before we can save it */ if (row == crow) { j = Length(screen, crow, ccol, col); } else { /* two cases, cut is on same line, cut spans multiple lines */ j += Length(screen, crow, ccol, screen->max_col) + 1; for (i = crow + 1; i < row; i++) j += Length(screen, i, 0, screen->max_col) + 1; if (col >= 0) j += Length(screen, row, 0, col); } /* UTF-8 may require more space */ if_OPT_WIDE_CHARS(screen, { j *= 4; }); /* now get some memory to save it in */ if (screen->selection_size <= j) { if ((line = (Char *) malloc((unsigned) j + 1)) == 0) SysError(ERROR_BMALLOC2); XtFree((char *) screen->selection_data); screen->selection_data = line; screen->selection_size = j + 1; } else { line = screen->selection_data; } if ((line == 0) || (j < 0)) return; line[j] = '\0'; /* make sure it is null terminated */ lp = line; /* lp points to where to save the text */ if (row == crow) { lp = SaveText(screen, row, ccol, col, lp, &eol); } else { lp = SaveText(screen, crow, ccol, screen->max_col, lp, &eol); if (eol) *lp++ = '\n'; /* put in newline at end of line */ for (i = crow + 1; i < row; i++) { lp = SaveText(screen, i, 0, screen->max_col, lp, &eol); if (eol) *lp++ = '\n'; } if (col >= 0) lp = SaveText(screen, row, 0, col, lp, &eol); } *lp = '\0'; /* make sure we have end marked */ TRACE(("Salted TEXT:%d:%.*s\n", lp - line, lp - line, line)); screen->selection_length = (lp - line); _OwnSelection(term, params, num_params); } #if OPT_PASTE64 void ClearSelectionBuffer() { TScreen *screen = &term->screen; screen->selection_length = 0; screen->base64_count = 0; } static void AppendStrToSelectionBuffer(Char * text, int len) { TScreen *screen = &term->screen; if (len != 0) { int j = screen->selection_length + len; /* New length */ int k = j + (j >> 2) + 80; /* New size if we grow buffer: grow by ~50% */ if (j + 1 >= screen->selection_size) { if (!screen->selection_length) { /* New buffer */ Char *line; if ((line = (Char *) malloc((unsigned) k)) == 0) SysError(ERROR_BMALLOC2); XtFree((char *) screen->selection_data); screen->selection_data = line; } else { /* Realloc buffer */ screen->selection_data = (Char *) realloc(screen->selection_data, (unsigned) k); if (screen->selection_data == 0) SysError(ERROR_BMALLOC2); } screen->selection_size = k; } memcpy(screen->selection_data + screen->selection_length, text, len); screen->selection_length += len; screen->selection_data[screen->selection_length] = 0; } } void AppendToSelectionBuffer(TScreen * screen, unsigned c) { int six; Char ch; /* Decode base64 character */ if (c >= 'A' && c <= 'Z') six = c - 'A'; else if (c >= 'a' && c <= 'z') six = c - 'a' + 26; else if (c >= '0' && c <= '9') six = c - '0' + 52; else if (c == '+') six = 62; else if (c == '/') six = 63; else return; /* Accumulate bytes */ switch (screen->base64_count) { case 0: screen->base64_accu = six; screen->base64_count = 6; break; case 2: ch = (screen->base64_accu << 6) + six; screen->base64_count = 0; AppendStrToSelectionBuffer(&ch, 1); break; case 4: ch = (screen->base64_accu << 4) + (six >> 2); screen->base64_accu = (six & 0x3); screen->base64_count = 2; AppendStrToSelectionBuffer(&ch, 1); break; case 6: ch = (screen->base64_accu << 2) + (six >> 4); screen->base64_accu = (six & 0xF); screen->base64_count = 4; AppendStrToSelectionBuffer(&ch, 1); break; } } void CompleteSelection(char **args, Cardinal len) { term->screen.base64_count = 0; term->screen.base64_accu = 0; _OwnSelection(term, args, len); } #endif /* OPT_PASTE64 */ static Bool _ConvertSelectionHelper(Widget w, Atom * type, XtPointer *value, unsigned long *length, int *format, int (*conversion_function) (Display *, char **, int, XICCEncodingStyle, XTextProperty *), XICCEncodingStyle conversion_style) { Display *d = XtDisplay(w); TScreen *screen; XTextProperty textprop; if (!IsXtermWidget(w)) return False; screen = &((XtermWidget) w)->screen; if (conversion_function(d, (char **) &screen->selection_data, 1, conversion_style, &textprop) < Success) return False; *value = (XtPointer) textprop.value; *length = textprop.nitems; *type = textprop.encoding; *format = textprop.format; return True; } static Boolean ConvertSelection(Widget w, Atom * selection, Atom * target, Atom * type, XtPointer *value, unsigned long *length, int *format) { Display *d = XtDisplay(w); TScreen *screen; Bool result = False; if (!IsXtermWidget(w)) return False; screen = &((XtermWidget) w)->screen; if (screen->selection_data == NULL) return False; /* can this happen? */ if (*target == XA_TARGETS(d)) { Atom *targetP; Atom *std_targets; XPointer std_return = 0; unsigned long std_length; if (XmuConvertStandardSelection(w, screen->selection_time, selection, target, type, &std_return, &std_length, format)) { std_targets = (Atom *) (std_return); *length = std_length + 6; targetP = (Atom *) XtMalloc(sizeof(Atom) * (*length)); *value = (XtPointer) targetP; *targetP++ = XA_STRING; *targetP++ = XA_TEXT(d); #ifdef X_HAVE_UTF8_STRING *targetP++ = XA_COMPOUND_TEXT(d); *targetP++ = XA_UTF8_STRING(d); #else *targetP = XA_COMPOUND_TEXT(d); if_OPT_WIDE_CHARS(screen, { *targetP = XA_UTF8_STRING(d); }); targetP++; #endif *targetP++ = XA_LENGTH(d); *targetP++ = XA_LIST_LENGTH(d); memcpy(targetP, std_targets, sizeof(Atom) * std_length); XtFree((char *) std_targets); *type = XA_ATOM; *format = 32; result = True; } } #if OPT_WIDE_CHARS else if (screen->wide_chars && *target == XA_STRING) { result = _ConvertSelectionHelper(w, type, value, length, format, Xutf8TextListToTextProperty, XStringStyle); } else if (screen->wide_chars && *target == XA_UTF8_STRING(d)) { result = _ConvertSelectionHelper(w, type, value, length, format, Xutf8TextListToTextProperty, XUTF8StringStyle); } else if (screen->wide_chars && *target == XA_TEXT(d)) { result = _ConvertSelectionHelper(w, type, value, length, format, Xutf8TextListToTextProperty, XStdICCTextStyle); } else if (screen->wide_chars && *target == XA_COMPOUND_TEXT(d)) { result = _ConvertSelectionHelper(w, type, value, length, format, Xutf8TextListToTextProperty, XCompoundTextStyle); } #endif else if (*target == XA_STRING) { /* not wide_chars */ /* We can only reach this point if the selection requestor requested STRING before any of TEXT, COMPOUND_TEXT or UTF8_STRING. We therefore assume that the requestor is not properly internationalised, and dump raw eight-bit data with no conversion into the selection. Yes, this breaks the ICCCM in non-Latin-1 locales. */ *type = XA_STRING; *value = (XtPointer) screen->selection_data; *length = screen->selection_length; *format = 8; result = True; } else if (*target == XA_TEXT(d)) { /* not wide_chars */ result = _ConvertSelectionHelper(w, type, value, length, format, XmbTextListToTextProperty, XStdICCTextStyle); } else if (*target == XA_COMPOUND_TEXT(d)) { /* not wide_chars */ result = _ConvertSelectionHelper(w, type, value, length, format, XmbTextListToTextProperty, XCompoundTextStyle); } #ifdef X_HAVE_UTF8_STRING else if (*target == XA_UTF8_STRING(d)) { /* not wide_chars */ result = _ConvertSelectionHelper(w, type, value, length, format, XmbTextListToTextProperty, XUTF8StringStyle); } #endif else if (*target == XA_LIST_LENGTH(d)) { *value = XtMalloc(4); if (sizeof(long) == 4) *(long *) *value = 1; else { long temp = 1; memcpy((char *) *value, ((char *) &temp) + sizeof(long) - 4, 4); } *type = XA_INTEGER; *length = 1; *format = 32; result = True; } else if (*target == XA_LENGTH(d)) { /* This value is wrong if we have UTF-8 text */ *value = XtMalloc(4); if (sizeof(long) == 4) { *(long *) *value = screen->selection_length; } else { long temp = screen->selection_length; memcpy((char *) *value, ((char *) &temp) + sizeof(long) - 4, 4); } *type = XA_INTEGER; *length = 1; *format = 32; result = True; } else if (XmuConvertStandardSelection(w, screen->selection_time, selection, target, type, (XPointer *) value, length, format)) { result = True; } /* else */ return result; } static void LoseSelection(Widget w, Atom * selection) { TScreen *screen; Atom *atomP; Cardinal i; if (!IsXtermWidget(w)) return; screen = &((XtermWidget) w)->screen; for (i = 0, atomP = screen->selection_atoms; i < screen->selection_count; i++, atomP++) { if (*selection == *atomP) *atomP = (Atom) 0; if (CutBuffer(*atomP) >= 0) { *atomP = (Atom) 0; } } for (i = screen->selection_count; i; i--) { if (screen->selection_atoms[i - 1] != 0) break; } screen->selection_count = i; for (i = 0, atomP = screen->selection_atoms; i < screen->selection_count; i++, atomP++) { if (*atomP == (Atom) 0) { *atomP = screen->selection_atoms[--screen->selection_count]; } } if (screen->selection_count == 0) TrackText(0, 0, 0, 0); } /* ARGSUSED */ static void SelectionDone(Widget w GCC_UNUSED, Atom * selection GCC_UNUSED, Atom * target GCC_UNUSED) { /* empty proc so Intrinsics know we want to keep storage */ } static void _OwnSelection(XtermWidget termw, String * selections, Cardinal count) { Atom *atoms = termw->screen.selection_atoms; Cardinal i; Bool have_selection = False; if (termw->screen.selection_length < 0) return; if (count > termw->screen.sel_atoms_size) { XtFree((char *) atoms); atoms = (Atom *) XtMalloc(count * sizeof(Atom)); termw->screen.selection_atoms = atoms; termw->screen.sel_atoms_size = count; } XmuInternStrings(XtDisplay((Widget) termw), selections, count, atoms); for (i = 0; i < count; i++) { int cutbuffer = CutBuffer(atoms[i]); if (cutbuffer >= 0) { if (termw->screen.selection_length > 4 * XMaxRequestSize(XtDisplay((Widget) termw)) - 32) { fprintf(stderr, "%s: selection too big (%d bytes), not storing in CUT_BUFFER%d\n", xterm_name, termw->screen.selection_length, cutbuffer); } else { /* This used to just use the UTF-8 data, which was totally * broken as not even the corresponding paste code in Xterm * understood this! So now it converts to Latin1 first. * Robert Brady, 2000-09-05 */ unsigned long length = termw->screen.selection_length; Char *data = termw->screen.selection_data; if_OPT_WIDE_CHARS((&(termw->screen)), { data = UTF8toLatin1(data, length, &length); }); TRACE(("XStoreBuffer(%d)\n", cutbuffer)); XStoreBuffer(XtDisplay((Widget) termw), (char *) data, (int) length, cutbuffer); } } else if (!replyToEmacs) { have_selection |= XtOwnSelection((Widget) termw, atoms[i], termw->screen.selection_time, ConvertSelection, LoseSelection, SelectionDone); } } if (!replyToEmacs) termw->screen.selection_count = count; if (!have_selection) TrackText(0, 0, 0, 0); } static void ResetSelectionState(TScreen * screen) { screen->selection_count = 0; screen->startHRow = screen->startHCol = 0; screen->endHRow = screen->endHCol = 0; } void DisownSelection(XtermWidget termw) { TScreen *screen = &(termw->screen); Atom *atoms = screen->selection_atoms; Cardinal count = screen->selection_count; Cardinal i; TRACE(("DisownSelection count %d, start %d.%d, end %d.%d\n", count, screen->startHRow, screen->startHCol, screen->endHRow, screen->endHCol)); for (i = 0; i < count; i++) { int cutbuffer = CutBuffer(atoms[i]); if (cutbuffer < 0) { XtDisownSelection((Widget) termw, atoms[i], screen->selection_time); } } /* * If none of the callbacks via XtDisownSelection() reset highlighting * do it now. */ if (ScrnHaveSelection(screen)) { /* save data which will be reset */ int start_row = screen->startHRow; int start_col = screen->startHCol; int end_row = screen->endHRow; int end_col = screen->endHCol; ResetSelectionState(screen); ReHiliteText(start_row, start_col, end_row, end_col); } else { ResetSelectionState(screen); } } /* returns number of chars in line from scol to ecol out */ /* ARGSUSED */ static int Length(TScreen * screen GCC_UNUSED, int row, int scol, int ecol) { int lastcol = LastTextCol(row); if (ecol > lastcol) ecol = lastcol; return (ecol - scol + 1); } /* copies text into line, preallocated */ static Char * SaveText(TScreen * screen, int row, int scol, int ecol, Char * lp, /* pointer to where to put the text */ int *eol) { int i = 0; unsigned c; Char *result = lp; #if OPT_WIDE_CHARS int previous = 0; unsigned c_1 = 0, c_2 = 0; #endif i = Length(screen, row, scol, ecol); ecol = scol + i; #if OPT_DEC_CHRSET if (CSET_DOUBLE(SCRN_BUF_CSETS(screen, row + screen->topline)[0])) { scol = (scol + 0) / 2; ecol = (ecol + 1) / 2; } #endif *eol = !ScrnTstWrapped(screen, row); for (i = scol; i < ecol; i++) { c = E2A(XTERM_CELL(row, i)); #if OPT_WIDE_CHARS if (screen->utf8_mode != uFalse) { c_1 = E2A(XTERM_CELL_C1(row, i)); c_2 = E2A(XTERM_CELL_C2(row, i)); } /* We want to strip out every occurrence of HIDDEN_CHAR AFTER a * wide character. */ if (c == HIDDEN_CHAR && iswide(previous)) { previous = c; /* Combining characters attached to double-width characters are in memory attached to the HIDDEN_CHAR */ if (c_1) { lp = convertToUTF8(lp, c_1); if (c_2) lp = convertToUTF8(lp, c_2); } continue; } previous = c; if (screen->utf8_mode != uFalse) { lp = convertToUTF8(lp, (c != 0) ? c : ' '); if (c_1) { lp = convertToUTF8(lp, c_1); if (c_2) lp = convertToUTF8(lp, c_2); } } else #endif { if (c == 0) { c = E2A(' '); } else if (c < E2A(' ')) { if (c == XPOUND) c = 0x23; /* char on screen is pound sterling */ else c += 0x5f; /* char is from DEC drawing set */ } else if (c == 0x7f) { c = 0x5f; } *lp++ = A2E(c); } if (c != E2A(' ')) result = lp; } /* * If requested, trim trailing blanks from selected lines. Do not do this * if the line is wrapped. */ if (!*eol || !screen->trim_selection) result = lp; return (result); } /* 32 + following 7-bit word: 1:0 Button no: 0, 1, 2. 3=release. 2 shift 3 meta 4 ctrl 5 set for motion notify 6 set for wheel */ /* Position: 32 - 255. */ static int BtnCode(XButtonEvent * event, int button) { int result = 32 + (KeyState(event->state) << 2); if (button < 0 || button > 5) { result += 3; } else { if (button > 3) result += (64 - 4); if (event->type == MotionNotify) result += 32; result += button; } return result; } #define MOUSE_LIMIT (255 - 32) static void EditorButton(XButtonEvent * event) { TScreen *screen = &term->screen; int pty = screen->respond; Char line[6]; int row, col; int button; unsigned count = 0; /* If button event, get button # adjusted for DEC compatibility */ button = event->button - 1; if (button >= 3) button++; /* Compute character position of mouse pointer */ row = (event->y - screen->border) / FontHeight(screen); col = (event->x - OriginX(screen)) / FontWidth(screen); /* Limit to screen dimensions */ if (row < 0) row = 0; else if (row > screen->max_row) row = screen->max_row; else if (row > MOUSE_LIMIT) row = MOUSE_LIMIT; if (col < 0) col = 0; else if (col > screen->max_col) col = screen->max_col; else if (col > MOUSE_LIMIT) col = MOUSE_LIMIT; /* Build key sequence starting with \E[M */ if (screen->control_eight_bits) { line[count++] = CSI; } else { line[count++] = ESC; line[count++] = '['; } line[count++] = 'M'; /* Add event code to key sequence */ if (screen->send_mouse_pos == X10_MOUSE) { line[count++] = ' ' + button; } else { /* Button-Motion events */ switch (event->type) { case ButtonPress: line[count++] = BtnCode(event, screen->mouse_button = button); break; case ButtonRelease: /* * Wheel mouse interface generates release-events for buttons * 4 and 5, coded here as 3 and 4 respectively. We change the * release for buttons 1..3 to a -1. */ if (button < 3) button = -1; line[count++] = BtnCode(event, screen->mouse_button = button); break; case MotionNotify: /* BTN_EVENT_MOUSE and ANY_EVENT_MOUSE modes send motion * events only if character cell has changed. */ if ((row == screen->mouse_row) && (col == screen->mouse_col)) return; line[count++] = BtnCode(event, screen->mouse_button); break; default: return; } } screen->mouse_row = row; screen->mouse_col = col; /* Add pointer position to key sequence */ line[count++] = ' ' + col + 1; line[count++] = ' ' + row + 1; TRACE(("mouse at %d,%d button+mask = %#x\n", row, col, (screen->control_eight_bits) ? line[2] : line[3])); /* Transmit key sequence to process running under xterm */ v_write(pty, line, count); } /*ARGSUSED*/ #if OPT_TEK4014 void HandleGINInput(Widget w GCC_UNUSED, XEvent * event GCC_UNUSED, String * param_list, Cardinal *nparamsp) { if (term->screen.TekGIN && *nparamsp == 1) { int c = param_list[0][0]; switch (c) { case 'l': case 'm': case 'r': case 'L': case 'M': case 'R': break; default: Bell(XkbBI_MinorError, 0); /* let them know they goofed */ c = 'l'; /* provide a default */ } TekEnqMouse(c | 0x80); TekGINoff(); } else { Bell(XkbBI_MinorError, 0); } } #endif /* OPT_TEK4014 */ /* ARGSUSED */ void HandleSecure(Widget w GCC_UNUSED, XEvent * event, /* unused */ String * params GCC_UNUSED, /* [0] = volume */ Cardinal *param_count GCC_UNUSED) /* 0 or 1 */ { Time ev_time = CurrentTime; if ((event->xany.type == KeyPress) || (event->xany.type == KeyRelease)) ev_time = event->xkey.time; else if ((event->xany.type == ButtonPress) || (event->xany.type == ButtonRelease)) ev_time = event->xbutton.time; DoSecureKeyboard(ev_time); }