/* $Xorg: button.c,v 1.3 2000/08/17 19:55:08 cpqbld Exp $ */ /* * Copyright 1999-2000 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.63 2001/09/09 01:07:25 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 #if OPT_WIDE_CHARS #include #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) * (term->screen.max_col+1) + (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; 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); Boolean 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)++; \ } #define MotionOff( s, t ) { \ (s)->event_mask |= ButtonMotionMask; \ (s)->event_mask &= ~PointerMotionMask; \ XSelectInput(XtDisplay((t)), XtWindow((t)), (s)->event_mask); } #define MotionOn( s, t ) { \ (s)->event_mask &= ~ButtonMotionMask; \ (s)->event_mask |= PointerMotionMask; \ XSelectInput(XtDisplay((t)), XtWindow((t)), (s)->event_mask); } Boolean SendLocatorPosition(Widget w, XEvent* event) { TScreen *screen = &((XtermWidget)w)->screen; int row, col; Boolean 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; Boolean 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; Boolean 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; Boolean 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 */ void DiredButton( Widget w GCC_UNUSED, XEvent *event, /* must be XButtonEvent */ String *params GCC_UNUSED, /* selections */ Cardinal *num_params GCC_UNUSED) { /* ^XM-G */ register TScreen *screen = &term->screen; Char Line[ 6 ]; register 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 ); } } void ViButton( Widget w GCC_UNUSED, XEvent *event, /* must be XButtonEvent */ String *params GCC_UNUSED, /* selections */ Cardinal *num_params GCC_UNUSED) { /* ^XM-G */ register TScreen *screen = &term->screen; int pty = screen->respond; Char Line[ 6 ]; register 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) { register 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 ) SendMousePosition(w,event); break; } } static void do_select_end ( Widget w, XEvent *event, /* must be XButtonEvent */ String *params, /* selections */ Cardinal *num_params, Bool use_cursor_loc) { 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); 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, int 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 > (int) 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; } #if 0 /* Eliminate all control characters from a UTF-8 string, doing something reasonable with PS and LS */ static Char* filterUTF8(Char *s, int len, int *len_return) { Char *p=s; Char *t; Char *q; unsigned codepoint; int size; t = (Char *)XtMalloc(len); if (t == 0) { TRACE(("Couldn't allocate target string\n")); return 0; } q = t; while (p < (s + len) && q < (t + len)) { if ((*p & 0x80) == 0) { codepoint = *p & 0x7F; size = 1; } else if ((*p & 0x60) == 0x40 && p < s + len - 1) { codepoint = ((p[0] & 0x1F) << 6) | (p[1] & 0x3F); size = 2; } else if ((*p & 0x70) == 0x60 && p < s + len - 2) { codepoint = ((p[0] & 0x0F) << 12) | ((p[1] & 0x3F) << 6) | (p[2] & 0x3F); size = 3; } else if ((*p & 0x78) == 0x70 && p < s + len - 3) { p += 4; /* eliminate surrogates */ continue; } else if ((*p & 0x7C) == 0x78 && p < s + len - 4) { p += 5; continue; } else if ((*p & 0x7E) == 0x7C && p < s + len - 5) { p += 6; continue; } else { /* wrong UTF-8? Silently discard. */ p++; continue; } if(codepoint == 0x2028) { /* line separator -- replace by NL*/ p += size; *q++ = 0x0A; } else if (codepoint == 0x2029) { /* paragraph separator -- replace by NL NL */ p += size; *q++ = 0x0A; if (q < t + len) *q++ = 0x0A; } else if (codepoint >= 0x202A && codepoint <= 0x202E) { /* ignore Unicode control characters; surrogates have already been eliminated */ p += size; } else { /* just copy the UTF-8 */ while (size--) *q++ = *p++; } } *len_return = q - t; return t; } #endif #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)); n++; #ifdef X_HAVE_UTF8_STRING if (screen->i18nSelections) { utf8SelectionTargets[n] = XA_TEXT(XtDisplay(w)); n++; utf8SelectionTargets[n] = XA_COMPOUND_TEXT(XtDisplay(w)); n++; } #endif utf8SelectionTargets[n] = XA_STRING; n++; 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)); n++; #endif if (screen->i18nSelections) { eightBitSelectionTargets[n] = XA_TEXT(XtDisplay(w)); n++; eightBitSelectionTargets[n] = XA_COMPOUND_TEXT(XtDisplay(w)); n++; } eightBitSelectionTargets[n] = XA_STRING; n++; eightBitSelectionTargets[n] = None; } return eightBitSelectionTargets; } static void _GetSelection( 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; XmuInternStrings(XtDisplay(w), params, (Cardinal)1, &selection); switch (selection) { 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; } TRACE(("Cutbuffer: %d, target: %lu\n", cutbuffer, (unsigned long)targets[0])); if (cutbuffer >= 0) { int inbytes; unsigned long nbytes; int fmt8 = 8; Atom type = XA_STRING; char *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) _GetSelection(w, ev_time, params+1, num_params-1, NULL); return; } else { struct _SelectionList* list; if (targets == NULL || targets[0] == None) { targets = _SelectionTargets(w); } 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 = XtNew(struct _SelectionList); 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, int len) { Char *cp; char *name; name = XGetAtomName(dpy, type); Trace("Getting %s (%ld)\n", XGetAtomName(dpy, type), (long int)type); for (cp = line; cp < line + len; cp++) Trace("%c\n", *cp); } #else #define GettingSelection(dpy,type,line,len) /* nothing */ #endif #ifndef VMS static void _WriteSelectionData(int pty, 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. */ register Char *lag, *cp, *end; end = &line[length]; lag = line; for (cp = line; cp != end; cp++) { if (*cp == '\n') { *cp = '\r'; v_write(pty, lag, cp - lag + 1); lag = cp + 1; } } if (lag != end) { v_write(pty, lag, end - lag); } } #else /* VMS */ static void _WriteSelectionData(int pty, Char *line, int length) { /* 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. */ register Char *lag, *cp, *end; tt_pasting = TRUE; end = &line[length]; lag = line; for (cp = line; cp != end; cp++) { if (*cp == '\n') { *cp = '\r'; tt_write(lag, cp - lag + 1); lag = cp + 1; } } if (lag != end) { tt_write(lag, end - lag); } tt_pasting = FALSE; tt_start_read(); /* reenable reads or a character may be lost */ } #endif /* VMS */ /* 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; for(i = 0; i < text_list_count; i++) { int len = strlen(text_list[i]); _WriteSelectionData(((XtermWidget)w)->screen.respond, (Char*)text_list[i], len); } 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; _GetSelection(w, list->time, list->params, list->count, list->targets); XtFree((char *)client_data); } return; } void HandleInsertSelection( Widget w, XEvent *event, /* assumed to be XButtonEvent* */ String *params, /* selections in precedence order */ Cardinal *num_params) { if (SendMousePosition(w, event)) return; _GetSelection(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; StartSelect(startrow, startcol); } /* ARGSUSED */ void HandleSelectStart( Widget w, XEvent *event, /* must be XButtonEvent* */ String *params GCC_UNUSED, Cardinal *num_params GCC_UNUSED) { register 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); 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) { register TScreen *screen; if (!IsXtermWidget(w)) return; screen = &((XtermWidget)w)->screen; do_select_start (w, event, screen->cursor_row, screen->cursor_col); } static void TrackDown(register 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 = 1; 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 = 0; 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, 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) { /* 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; selectUnit = EvalSelectUnit(event->xbutton.time, selectUnit); replyToEmacs = FALSE; if (numberOfClicks == 1) { /* 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); } 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); } 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(register TScreen* screen, register int amount) { register int minrow = -screen->savedlines - screen->topline; register int maxrow = screen->max_row - screen->topline; register 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); scroll_update_one(screen->startHRow, screen->startHCol); 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 int iswide(int i) { return (i == HIDDEN_CHAR) || (my_wcwidth(i) == 2); } #endif static void PointToRowCol( register int y, register 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. */ { register TScreen *screen = &term->screen; register 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 > screen->max_col+1) { col = screen->max_col+1; } #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 && iswide(XTERM_CELL(row, col-1)) && (XTERM_CELL(row, col) == HIDDEN_CHAR)) { col -= 1; } #endif *r = row; *c = col; } static int LastTextCol(register int row) { register TScreen *screen = &term->screen; register int i; register 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 */ 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, 216, /* 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, 248, /* o/ u` u' u^ u: y' P y: */ 48, 48, 48, 48, 48, 48, 48, 48}; int SetCharacterClassRange ( register int low, /* in range of [0..255] */ register int high, register 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) { unsigned 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) { register TScreen *screen = &term->screen; register int length; register int cclass; #if OPT_WIDE_CHARS if (startCol > 1 && iswide(XTERM_CELL(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 (iswide(XTERM_CELL(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; } TrackText(startSRow, startSCol, endSRow, endSCol); return; } void TrackText( register int frow, register int fcol, register int trow, register int tcol) /* Guaranteed (frow, fcol) <= (trow, tcol) */ { register int from, to; register TScreen *screen = &term->screen; int old_startrow, old_startcol, old_endrow, old_endcol; 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( register int frow, register int fcol, register int trow, register int tcol) /* Guaranteed that (frow, fcol) <= (trow, tcol) */ { register TScreen *screen = &term->screen; register int i; 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 = screen->max_col+1; } 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, screen->max_col+1, 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) */ { register TScreen *screen = &term->screen; register 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(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 = (Char *)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:%.*s\n", (char *)lp - line, line)); screen->selection_length = ((char *)lp - line); _OwnSelection(term, params, num_params); } static Boolean _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, &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; 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; unsigned long std_length; XmuConvertStandardSelection(w, screen->selection_time, selection, target, type, (XPointer *)&std_targets, &std_length, format); *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 ( (char*)targetP, (char*)std_targets, sizeof(Atom)*std_length); XtFree((char*)std_targets); *type = XA_ATOM; *format = 32; return True; } #if OPT_WIDE_CHARS if (screen->wide_chars && *target == XA_STRING) { return _ConvertSelectionHelper(w, type, value, length, format, Xutf8TextListToTextProperty, XStringStyle); } if (screen->wide_chars && *target == XA_UTF8_STRING(d)) { return _ConvertSelectionHelper(w, type, value, length, format, Xutf8TextListToTextProperty, XUTF8StringStyle); } if (screen->wide_chars && *target == XA_TEXT(d)) { return _ConvertSelectionHelper(w, type, value, length, format, Xutf8TextListToTextProperty, XStdICCTextStyle); } if (screen->wide_chars && *target == XA_COMPOUND_TEXT(d)) { return _ConvertSelectionHelper(w, type, value, length, format, Xutf8TextListToTextProperty, XCompoundTextStyle); } #endif 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 = screen->selection_data; *length = screen->selection_length; *format = 8; return True; } if (*target == XA_TEXT(d)) { /* not wide_chars */ return _ConvertSelectionHelper(w, type, value, length, format, XmbTextListToTextProperty, XStdICCTextStyle); } if (*target == XA_COMPOUND_TEXT(d)) { /* not wide_chars */ return _ConvertSelectionHelper(w, type, value, length, format, XmbTextListToTextProperty, XCompoundTextStyle); } #ifdef X_HAVE_UTF8_STRING if (*target == XA_UTF8_STRING(d)) { /* not wide_chars */ return _ConvertSelectionHelper(w, type, value, length, format, XmbTextListToTextProperty, XUTF8StringStyle); } #endif 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; return True; } 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; return True; } if (XmuConvertStandardSelection(w, screen->selection_time, selection, target, type, (XPointer *)value, length, format)) return True; /* else */ return False; } static void LoseSelection(Widget w, Atom *selection) { register TScreen* screen; register 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; switch (*atomP) { case XA_CUT_BUFFER0: case XA_CUT_BUFFER1: case XA_CUT_BUFFER2: case XA_CUT_BUFFER3: case XA_CUT_BUFFER4: case XA_CUT_BUFFER5: case XA_CUT_BUFFER6: case XA_CUT_BUFFER7: *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( register XtermWidget termw, String *selections, Cardinal count) { Atom* atoms = termw->screen.selection_atoms; Cardinal i; Boolean 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; switch (atoms[i]) { 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; } 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 = (Char *)termw->screen.selection_data; if_OPT_WIDE_CHARS((&(termw->screen)), { data = UTF8toLatin1(data, length, &length); }) XStoreBuffer( XtDisplay((Widget)termw), (char *)data, 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); } void DisownSelection(register XtermWidget termw) { Atom* atoms = termw->screen.selection_atoms; Cardinal count = termw->screen.selection_count; Cardinal i; for (i = 0; i < count; i++) { int cutbuffer; switch (atoms[i]) { 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; } if (cutbuffer < 0) XtDisownSelection( (Widget)termw, atoms[i], termw->screen.selection_time ); } termw->screen.selection_count = 0; termw->screen.startHRow = termw->screen.startHCol = 0; termw->screen.endHRow = termw->screen.endHCol = 0; } /* returns number of chars in line from scol to ecol out */ /* ARGSUSED */ static int Length( register TScreen *screen GCC_UNUSED, register int row, register int scol, register int ecol) { register 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, register 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) { 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) { lp = convertToUTF8(lp, 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); } 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(register XButtonEvent *event) { TScreen *screen = &term->screen; int pty = screen->respond; Char line[6]; int row, col; int button, 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); }