/* * FUNCTION * mousemove(int *sel_pending, * POINT *first, * POINT *current, * MARK *lmbdn_mark, * int rect_rgn) * * sel_pending - Boolean, T -> client has recorded a left mouse button (LMB) * click, and so, a selection is pending. * * first - editor row/col coordinates where LMB was initially recorded. * * latest - during the mouse move, assuming the LMB is still down, * "latest" tracks the cursor position wrt window resizing * operations (via a modeline drag). * * current - current cursor row/col coordiantes. * * lmbdn_mark - editor MARK when "LMB down" was initially recorded. * * rect_rgn - Boolean, T -> user wants rectangular region selection. * * DESCRIPTION * Using several state variables, this function handles all the semantics * of a left mouse button "MOVE" event. The semantics are as follows: * * 1) This function will not be called unless the LMB is down and the * cursor is not being used to drage the mode line (enforced by caller). * 2) a LMB move within the current editor window selects a region of text. * Later, when the user releases the LMB, that text is yanked to the * unnamed register (the yank code is not handled in this function). * * RETURNS * None */ static void mousemove(int *sel_pending, COORD * first, COORD * current, MARK *lmbdn_mark, int rect_rgn) { int dummy; if (WaitForSingleObject(hAsMutex, AS_TMOUT) == WAIT_OBJECT_0) { if (*sel_pending) { /* * Selection pending. If the mouse has moved at least one char, * start a selection. */ if (MouseClickSetPos(current, &dummy)) { /* ignore mouse jitter */ if (current->X != first->X || current->Y != first->Y) { *sel_pending = FALSE; DOT = *lmbdn_mark; (void) sel_begin(); (void) update(TRUE); } else { (void) ReleaseMutex(hAsMutex); return; } } } if (mouse_wp != row2window(current->Y)) { /* * mouse moved into a different editor window or row2window() * returned a NULL ptr. */ (void) ReleaseMutex(hAsMutex); return; } if (!setcursor(current->Y, current->X)) { (void) ReleaseMutex(hAsMutex); return; } if (rect_rgn) (void) sel_setshape(rgn_RECTANGLE); if (sel_extend(TRUE, TRUE)) (void) update(TRUE); (void) ReleaseMutex(hAsMutex); } /* * Else either the worker thread abandonded the mutex (not possible as * currently coded) or timed out. If the latter, something is * hung--don't do anything. */ }
static int MouseClickSetPos(COORD * result, int *onmode) { WINDOW *wp; TRACE(("GETC:setcursor(%d, %d)\n", result->Y, result->X)); /* * If we're getting a button-down in a window, allow it to begin a * selection. A button-down on its modeline will allow resizing the * window. */ *onmode = FALSE; if ((wp = row2window(result->Y)) != 0) { if (result->Y == mode_row(wp)) { *onmode = TRUE; return TRUE; } return setcursor(result->Y, result->X); } return FALSE; }
static int xterm_button(int c) { WINDOW *wp; int event; int button; int x; int y; int status; #if OPT_XTERM >= 3 int save_row = ttrow; int save_col = ttcol; int firstrow, lastrow; int startx, endx, mousex; int starty, endy, mousey; MARK save_dot; char temp[NSTRING]; static const char *fmt = "\033[%d;%d;%d;%d;%dT"; #endif /* OPT_XTERM >= 3 */ if (insertmode) return ABORT; if ((status = (global_g_val(GMDXTERM_MOUSE))) != 0) { beginDisplay; switch(c) { case 'M': /* button-event */ event = keystroke(); x = XtermPos() + x_origin; y = XtermPos() + y_origin; button = (event & 3) + 1; TRACE(("M-button event:%d x:%d y:%d\n", event, x, y)) if (button > 3) { endofDisplay; return TRUE; /* button up */ } wp = row2window(y-1); #if OPT_XTERM >= 3 /* Tell the xterm how to highlight the selection. * It won't do anything else until we do this. */ if (wp != 0) { firstrow = wp->w_toprow + 1; lastrow = mode_row(wp) + 1; } else { /* from message-line */ firstrow = term.t_nrow ; lastrow = term.t_nrow + 1; } if (y >= lastrow) /* don't select modeline */ y = lastrow - 1; (void)lsprintf(temp, fmt, 1, x, y, firstrow, lastrow); putpad(temp); TTflush(); #endif /* OPT_XTERM >= 3 */ /* Set the dot-location if button 1 was pressed in a * window. */ if (wp != 0 && button == 1 && !reading_msg_line && setcursor(y-1, x-1)) { /*mlerase();*/ (void)update(TRUE); status = TRUE; } else if (button <= 3) { #if OPT_XTERM >= 3 /* abort the selection */ (void)lsprintf(temp, fmt, 0, x, y, firstrow, lastrow); putpad(temp); TTflush(); #endif /* OPT_XTERM >= 3 */ status = ABORT; } else { status = FALSE; } break; #if OPT_XTERM >= 3 case 't': /* reports valid text-location */ x = XtermPos(); y = XtermPos(); TRACE(("t: x:%d y:%d\n", x, y)) setwmark(y-1, x-1); yankregion(); movecursor(save_row, save_col); /*mlerase();*/ (void)update(TRUE); break; case 'T': /* reports invalid text-location */ /* * The starting-location returned is not the location * at which the mouse was pressed. Instead, it is the * top-most location of the selection. In turn, the * ending-location is the bottom-most location of the * selection. The mouse-up location is not necessarily * a pointer to valid text. * * This case handles multi-clicking events as well as * selections whose start or end location was not * pointing to text. */ save_dot = DOT; startx = XtermPos(); /* starting-location */ starty = XtermPos(); endx = XtermPos(); /* ending-location */ endy = XtermPos(); mousex = XtermPos(); /* location at mouse-up */ mousey = XtermPos(); TRACE(("T: start(%d,%d) end(%d,%d) mouse(%d,%d)\n", starty, startx, endy, endx, mousey, mousex)) setcursor(starty - 1, startx - 1); setwmark (endy - 1, endx - 1); if (MK.o != 0 && !is_at_end_of_line(MK)) MK.o += 1; yankregion(); DOT = save_dot; movecursor(save_row, save_col); /*mlerase();*/ (void)update(TRUE); break; #endif /* OPT_XTERM >= 3 */ default: status = FALSE; } endofDisplay; }
static void handle_mouse_event(MOUSE_EVENT_RECORD mer) { static DWORD lastclick = 0; static int clicks = 0; int onmode = FALSE; COORD current, first, latest; MARK lmbdn_mark; /* left mouse button down here */ int sel_pending = 0, state; DWORD thisclick; UINT clicktime = GetDoubleClickTime(); buttondown = FALSE; for_ever { current = mer.dwMousePosition; switch (mer.dwEventFlags) { case 0: state = mer.dwButtonState; if (state == 0) { /* button released */ thisclick = GetTickCount(); TRACE(("CLICK %d/%d\n", lastclick, thisclick)); if (thisclick - lastclick < clicktime) { clicks++; TRACE(("MOUSE CLICKS %d\n", clicks)); } else { clicks = 0; } lastclick = thisclick; switch (clicks) { case 1: on_double_click(); break; case 2: on_triple_click(); break; } if (buttondown) { int dummy; halt_autoscroll_thread(); /* Finalize cursor position. */ (void) MouseClickSetPos(¤t, &dummy); if (!(onmode || sel_pending)) sel_yank(0); } return; } if (state & FROM_LEFT_1ST_BUTTON_PRESSED) { if (MouseClickSetPos(¤t, &onmode)) { first = latest = current; lmbdn_mark = DOT; sel_pending = FALSE; mouse_wp = row2window(latest.Y); if (onmode) { buttondown = TRUE; sel_release(); update(TRUE); } else { HWND hwnd; (void) update(TRUE); /* possible wdw change */ buttondown = FALSE; /* until all inits are successful */ /* Capture mouse to console vile's window handle. */ hwnd = GetVileWindow(); (void) SetCapture(hwnd); /* Compute pixel height of each row on screen. */ (void) GetClientRect(hwnd, &client_rect); row_height = client_rect.bottom / term.rows; /* * Create mutex to ensure that main thread and worker * thread don't update display at the same time. */ if ((hAsMutex = CreateMutex(0, FALSE, 0)) == NULL) mlforce("[Can't create autoscroll mutex]"); else { /* * Setup a worker thread to act as a pseudo * timer that kicks off autoscroll when * necessary. */ if (_beginthread(autoscroll_thread, 0, NULL) == (unsigned long) -1) { (void) CloseHandle(hAsMutex); mlforce("[Can't create autoscroll thread]"); } else sel_pending = buttondown = TRUE; } if (!buttondown) (void) ReleaseCapture(); } } } else if (state & FROM_LEFT_2ND_BUTTON_PRESSED) { if (MouseClickSetPos(¤t, &onmode) && !onmode) { sel_yank(0); sel_release(); paste_selection(); (void) update(TRUE); } return; } else { if (MouseClickSetPos(¤t, &onmode) && onmode) { sel_release(); update(TRUE); } else { kbd_alarm(); } } break; case MOUSE_MOVED: if (!buttondown) return; if (onmode) { /* on mode line, resize window (if possible). */ if (!adjust_window(mouse_wp, ¤t, &latest)) { /* * left mouse button still down, but cursor moved off mode * line. Update latest to keep track of cursor in case * it wanders back on the mode line. */ latest = current; } } else { mousemove(&sel_pending, &first, ¤t, &lmbdn_mark, (mer.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) ); } break; #ifdef MOUSE_WHEELED case MOUSE_WHEELED: /* * Trial and error experimentation shows that dwButtonState * has its high bit set when the wheel moves back and not * set otherwise. */ mvupwind(TRUE, ((long) mer.dwButtonState < 0) ? -3 : 3); update(TRUE); return; #endif /* MOUSE_WHEELED */ } for_ever { INPUT_RECORD ir; DWORD nr; int key; if (!ReadConsoleInput(hConsoleInput, &ir, 1, &nr)) imdying(0); switch (ir.EventType) { case KEY_EVENT: key = decode_key_event(&ir); if (key == ESC) { if (buttondown) halt_autoscroll_thread(); sel_release(); (void) update(TRUE); return; } continue; case MOUSE_EVENT: mer = ir.Event.MouseEvent; break; } break; } } }
/* * On button press, we get an explicit number (1,2,3), and on release we don't * really know which button, but assume it is the last-pressed button. */ int on_mouse_click(int button, int y, int x) { static int first_x, first_y, pending; WINDOW *this_wp, *that_wp; int status; if (button > 0) { if (valid_window(this_wp = row2window(y)) && (y != mode_row(this_wp))) { /* * If we get a click on the "<" marking the left side * of a shifted window, force the screen right. This * makes it more consistent if there's a tab. */ if (w_val(this_wp, WVAL_SIDEWAYS) && x == 0) { mvleftwind(FALSE, 1); } if (!doingsweep) { if (button == BTN_EXTEND) { first_x = offs2col(this_wp, this_wp->w_dot.l, this_wp->w_dot.o); first_y = line_no(this_wp->w_bufp, this_wp->w_dot.l) - line_no(this_wp->w_bufp, this_wp->w_line.l); } else { first_x = x; first_y = y; } } status = setcursor(y, x); /* * Check for button1-down while we're in multimotion * sweep, so we can suppress highlighting extension. */ if (button != BTN_EXTEND && status == TRUE && doingsweep) { status = SORTOFTRUE; if (button == BTN_BEGIN) { first_x = x; first_y = y; } } } else { /* pressed button on modeline */ status = SORTOFTRUE; first_x = x; first_y = y; } pending = button; } else if (pending) { button = pending; pending = FALSE; this_wp = row2window(y); that_wp = row2window(first_y); if (this_wp == 0 || that_wp == 0 || reading_msg_line) { TRACE(("MOUSE cannot move msg-line\n")); status = FALSE; } else if (insertmode && (this_wp != curwp || that_wp != curwp)) { TRACE(("MOUSE cannot move from window while inserting\n")); kbd_alarm(); status = ABORT; } else if (first_y == mode_row(that_wp)) { /* drag modeline? */ if (first_y == y) { sel_release(); status = SEL_RELEASE; } else { WINDOW *save_wp = curwp; TRACE(("MOUSE dragging modeline\n")); set_curwp(that_wp); status = shrinkwind(FALSE, first_y - y); set_curwp(save_wp); } } else if (y != first_y || x != first_x) { /* drag selection */ if (button == BTN_PASTE) { (void) setcursor(y, x); status = paste_selection(); } else if (doingsweep) { switch (button) { case BTN_BEGIN: (void) release_selection(TRUE); status = setcursor(first_y, first_x); if (status == TRUE) { MK = DOT; status = SEL_BEGIN; TRACE(("MOUSE setting SEL_BEGIN MK %d.%d\n", line_no(curbp, MK.l), MK.o)); } break; default: (void) setcursor(y, x); status = SEL_EXTEND; TRACE(("MOUSE setting SEL_EXTEND DOT %d.%d MK %d.%d\n", line_no(curbp, MK.l), MK.o, line_no(curbp, DOT.l), DOT.o)); break; } } else { TRACE(("MOUSE begin multimotion on button%d-up\n", button)); if (button == BTN_EXTEND) { (void) setcursor(y, x); y = first_y; x = first_x; } do_sweep(SORTOFTRUE); (void) sel_begin(); (void) sel_setshape(rgn_EXACT); (void) setcursor(y, x); status = multimotion(TRUE, 1); TRACE(("MOUSE end multimotion after button%d-up\n", button)); if (status == SEL_PASTE) status = paste_selection(); } } else { /* position the cursor */ TRACE(("MOUSE button %d position cursor\n", button)); (void) setcursor(y, x); switch (button) { case BTN_BEGIN: status = SEL_FINISH; break; case BTN_PASTE: status = paste_selection(); break; default: status = release_selection(TRUE); break; } } } else { TRACE(("MOUSE ignored (illegal state)\n")); status = FALSE; } if (status == TRUE || status >= SORTOFTRUE) (void) update(TRUE); TRACE(("MOUSE status:%d\n", status)); return status; }