Example #1
0
NCURSES_SP_NAME(getmouse) (NCURSES_SP_DCLx MEVENT * aevent)
{
    int result = ERR;

    T((T_CALLED("getmouse(%p,%p)"), (void *) SP_PARM, (void *) aevent));

    if ((aevent != 0) && (SP_PARM != 0) && (SP_PARM->_mouse_type != M_NONE)) {
	MEVENT *eventp = SP_PARM->_mouse_eventp;
	/* compute the current-event pointer */
	MEVENT *prev = PREV(eventp);

	if (prev->id != INVALID_EVENT) {
	    /* copy the event we find there */
	    *aevent = *prev;

	    TR(TRACE_IEVENT, ("getmouse: returning event %s from slot %ld",
			      _nc_tracemouse(SP_PARM, prev),
			      (long) IndexEV(SP_PARM, prev)));

	    prev->id = INVALID_EVENT;	/* so the queue slot becomes free */
	    SP_PARM->_mouse_eventp = PREV(prev);
	    result = OK;
	}
    }
    returnCode(result);
}
Example #2
0
static void
_trace_slot(SCREEN *sp, const char *tag)
{
    MEVENT *ep;

    _tracef("%s", tag);

    for (ep = FirstEV(sp); ep <= LastEV(sp); ep++)
	_tracef("mouse event queue slot %ld = %s",
		(long) IndexEV(sp, ep),
		_nc_tracemouse(sp, ep));
}
Example #3
0
NCURSES_SP_NAME(getmouse) (NCURSES_SP_DCLx MEVENT * aevent)
{
    int result = ERR;

    T((T_CALLED("getmouse(%p,%p)"), (void *) SP_PARM, (void *) aevent));

    if ((aevent != 0) && (SP_PARM != 0) && (SP_PARM->_mouse_type != M_NONE)) {
	MEVENT *eventp = SP_PARM->_mouse_eventp;
	/* compute the current-event pointer */
	MEVENT *prev = PREV(eventp);

	/*
	 * Discard events not matching mask (there could be still some if
	 * _nc_mouse_parse was not called, e.g., when _nc_mouse_inline returns
	 * false).
	 */
	while (ValidEvent(prev) && (!(prev->bstate & SP_PARM->_mouse_mask2))) {
	    Invalidate(prev);
	    prev = PREV(prev);
	}
	if (ValidEvent(prev)) {
	    /* copy the event we find there */
	    *aevent = *prev;

	    TR(TRACE_IEVENT, ("getmouse: returning event %s from slot %ld",
			      _nc_tracemouse(SP_PARM, prev),
			      (long) IndexEV(SP_PARM, prev)));

	    Invalidate(prev);	/* so the queue slot becomes free */
	    SP_PARM->_mouse_eventp = prev;
	    result = OK;
	} else {
	    /* Reset the provided event */
	    aevent->bstate = 0;
	    Invalidate(aevent);
	    aevent->x = 0;
	    aevent->y = 0;
	    aevent->z = 0;
	}
    }
    returnCode(result);
}
Example #4
0
static bool
_nc_mouse_inline(SCREEN *sp)
/* mouse report received in the keyboard stream -- parse its info */
{
    int b;
    bool result = FALSE;
    MEVENT *eventp = sp->_mouse_eventp;

    TR(MY_TRACE, ("_nc_mouse_inline() called"));

    if (sp->_mouse_type == M_XTERM) {
	unsigned char kbuf[4];
	mmask_t prev;
	size_t grabbed;
	int res;

	/* This code requires that your xterm entry contain the kmous
	 * capability and that it be set to the \E[M documented in the
	 * Xterm Control Sequences reference.  This is how we
	 * arrange for mouse events to be reported via a KEY_MOUSE
	 * return value from wgetch().  After this value is received,
	 * _nc_mouse_inline() gets called and is immediately
	 * responsible for parsing the mouse status information
	 * following the prefix.
	 *
	 * The following quotes from the ctrlseqs.ms document in the
	 * X distribution, describing the X mouse tracking feature:
	 *
	 * Parameters for all mouse tracking escape sequences
	 * generated by xterm encode numeric parameters in a single
	 * character as value+040.  For example, !  is 1.
	 *
	 * On button press or release, xterm sends ESC [ M CbCxCy.
	 * The low two bits of Cb encode button information: 0=MB1
	 * pressed, 1=MB2 pressed, 2=MB3 pressed, 3=release.  The
	 * upper bits encode what modifiers were down when the
	 * button was pressed and are added together.  4=Shift,
	 * 8=Meta, 16=Control.  Cx and Cy are the x and y coordinates
	 * of the mouse event.  The upper left corner is (1,1).
	 *
	 * (End quote)  By the time we get here, we've eaten the
	 * key prefix.  FYI, the loop below is necessary because
	 * mouse click info isn't guaranteed to present as a
	 * single clist item.
	 *
	 * Wheel mice may return buttons 4 and 5 when the wheel is turned.
	 * We encode those as button presses.
	 */
# if USE_PTHREADS_EINTR
#  if USE_WEAK_SYMBOLS
	if ((pthread_self) && (pthread_kill) && (pthread_equal))
#  endif
	    _nc_globals.read_thread = pthread_self();
# endif
	for (grabbed = 0; grabbed < 3; grabbed += (size_t) res) {

	    /* For VIO mouse we add extra bit 64 to disambiguate button-up. */
#if USE_EMX_MOUSE
	    res = (int) read(M_FD(sp) >= 0 ? M_FD(sp) : sp->_ifd, &kbuf, 3);
#else
	    res = (int) read(sp->_ifd, kbuf + grabbed, 3 - grabbed);
#endif
	    if (res == -1)
		break;
	}
#if USE_PTHREADS_EINTR
	_nc_globals.read_thread = 0;
#endif
	kbuf[3] = '\0';

	TR(TRACE_IEVENT,
	   ("_nc_mouse_inline sees the following xterm data: '%s'", kbuf));

	/* there's only one mouse... */
	eventp->id = NORMAL_EVENT;

	/* processing code goes here */
	eventp->bstate = 0;
	prev = PREV(eventp)->bstate;

#if USE_EMX_MOUSE
#define PRESS_POSITION(n) \
	eventp->bstate = MASK_PRESS(n); \
	if (kbuf[0] & 0x40) \
	    eventp->bstate = MASK_RELEASE(n)
#else
#define PRESS_POSITION(n) \
	eventp->bstate = (mmask_t) (prev & MASK_PRESS(n) \
				    ? REPORT_MOUSE_POSITION \
				    : MASK_PRESS(n))
#endif

	switch (kbuf[0] & 0x3) {
	case 0x0:
	    if (kbuf[0] & 64)
		eventp->bstate = MASK_PRESS(4);
	    else
		PRESS_POSITION(1);
	    break;

	case 0x1:
#if NCURSES_MOUSE_VERSION == 2
	    if (kbuf[0] & 64)
		eventp->bstate = MASK_PRESS(5);
	    else
#endif
		PRESS_POSITION(2);
	    break;

	case 0x2:
	    PRESS_POSITION(3);
	    break;

	case 0x3:
	    /*
	     * Release events aren't reported for individual buttons, just for
	     * the button set as a whole.  However, because there are normally
	     * no mouse events under xterm that intervene between press and
	     * release, we can infer the button actually released by looking at
	     * the previous event.
	     */
	    if (prev & (BUTTON_PRESSED | BUTTON_RELEASED)) {
		eventp->bstate = BUTTON_RELEASED;
		for (b = 1; b <= MAX_BUTTONS; ++b) {
		    if (!(prev & MASK_PRESS(b)))
			eventp->bstate &= ~MASK_RELEASE(b);
		}
	    } else {
		/*
		 * XFree86 xterm will return a stream of release-events to
		 * let the application know where the mouse is going, if the
		 * private mode 1002 or 1003 is enabled.
		 */
		eventp->bstate = REPORT_MOUSE_POSITION;
	    }
	    break;
	}
	result = (eventp->bstate & REPORT_MOUSE_POSITION) ? TRUE : FALSE;

	if (kbuf[0] & 4) {
	    eventp->bstate |= BUTTON_SHIFT;
	}
	if (kbuf[0] & 8) {
	    eventp->bstate |= BUTTON_ALT;
	}
	if (kbuf[0] & 16) {
	    eventp->bstate |= BUTTON_CTRL;
	}

	eventp->x = (kbuf[1] - ' ') - 1;
	eventp->y = (kbuf[2] - ' ') - 1;
	TR(MY_TRACE,
	   ("_nc_mouse_inline: primitive mouse-event %s has slot %ld",
	    _nc_tracemouse(sp, eventp),
	    (long) IndexEV(sp, eventp)));

	/* bump the next-free pointer into the circular list */
	sp->_mouse_eventp = NEXT(eventp);
#if 0				/* this return would be needed for QNX's mods to lib_getch.c */
	return (TRUE);
#endif
    }

    return (result);
}
Example #5
0
static bool
_nc_mouse_parse(SCREEN *sp, int runcount)
/* parse a run of atomic mouse events into a gesture */
{
    MEVENT *eventp = sp->_mouse_eventp;
    MEVENT *ep, *runp, *next, *prev = PREV(eventp);
    int n;
    int b;
    bool merge;

    TR(MY_TRACE, ("_nc_mouse_parse(%d) called", runcount));

    /*
     * When we enter this routine, the event list next-free pointer
     * points just past a run of mouse events that we know were separated
     * in time by less than the critical click interval. The job of this
     * routine is to collapse this run into a single higher-level event
     * or gesture.
     *
     * We accomplish this in two passes.  The first pass merges press/release
     * pairs into click events.  The second merges runs of click events into
     * double or triple-click events.
     *
     * It's possible that the run may not resolve to a single event (for
     * example, if the user quadruple-clicks).  If so, leading events
     * in the run are ignored.
     *
     * Note that this routine is independent of the format of the specific
     * format of the pointing-device's reports.  We can use it to parse
     * gestures on anything that reports press/release events on a per-
     * button basis, as long as the device-dependent mouse code puts stuff
     * on the queue in MEVENT format.
     */
    if (runcount == 1) {
	TR(MY_TRACE,
	   ("_nc_mouse_parse: returning simple mouse event %s at slot %ld",
	    _nc_tracemouse(sp, prev),
	    (long) IndexEV(sp, prev)));
	return (prev->id >= NORMAL_EVENT)
	    ? ((prev->bstate & sp->_mouse_mask) ? TRUE : FALSE)
	    : FALSE;
    }

    /* find the start of the run */
    runp = eventp;
    for (n = runcount; n > 0; n--) {
	runp = PREV(runp);
    }

#ifdef TRACE
    if (USE_TRACEF(TRACE_IEVENT)) {
	_trace_slot(sp, "before mouse press/release merge:");
	_tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
		RunParams(sp, eventp, runp),
		runcount);
	_nc_unlock_global(tracef);
    }
#endif /* TRACE */

    /* first pass; merge press/release pairs */
    do {
	merge = FALSE;
	for (ep = runp; (next = NEXT(ep)) != eventp; ep = next) {

#define MASK_CHANGED(x) (!(ep->bstate & MASK_PRESS(x)) \
		      == !(next->bstate & MASK_RELEASE(x)))

	    if (ep->x == next->x && ep->y == next->y
		&& (ep->bstate & BUTTON_PRESSED)
		&& MASK_CHANGED(1)
		&& MASK_CHANGED(2)
		&& MASK_CHANGED(3)
		&& MASK_CHANGED(4)
#if NCURSES_MOUSE_VERSION == 2
		&& MASK_CHANGED(5)
#endif
		) {
		for (b = 1; b <= MAX_BUTTONS; ++b) {
		    if ((sp->_mouse_mask & MASK_CLICK(b))
			&& (ep->bstate & MASK_PRESS(b))) {
			ep->bstate &= ~MASK_PRESS(b);
			ep->bstate |= MASK_CLICK(b);
			merge = TRUE;
		    }
		}
		if (merge)
		    next->id = INVALID_EVENT;
	    }
	}
    } while
	(merge);

#ifdef TRACE
    if (USE_TRACEF(TRACE_IEVENT)) {
	_trace_slot(sp, "before mouse click merge:");
	_tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
		RunParams(sp, eventp, runp),
		runcount);
	_nc_unlock_global(tracef);
    }
#endif /* TRACE */

    /*
     * Second pass; merge click runs.  At this point, click events are
     * each followed by one invalid event. We merge click events
     * forward in the queue.
     *
     * NOTE: There is a problem with this design!  If the application
     * allows enough click events to pile up in the circular queue so
     * they wrap around, it will cheerfully merge the newest forward
     * into the oldest, creating a bogus doubleclick and confusing
     * the queue-traversal logic rather badly.  Generally this won't
     * happen, because calling getmouse() marks old events invalid and
     * ineligible for merges.  The true solution to this problem would
     * be to timestamp each MEVENT and perform the obvious sanity check,
     * but the timer element would have to have sub-second resolution,
     * which would get us into portability trouble.
     */
    do {
	MEVENT *follower;

	merge = FALSE;
	for (ep = runp; (next = NEXT(ep)) != eventp; ep = next)
	    if (ep->id != INVALID_EVENT) {
		if (next->id != INVALID_EVENT)
		    continue;
		follower = NEXT(next);
		if (follower->id == INVALID_EVENT)
		    continue;

		/* merge click events forward */
		if ((ep->bstate & BUTTON_CLICKED)
		    && (follower->bstate & BUTTON_CLICKED)) {
		    for (b = 1; b <= MAX_BUTTONS; ++b) {
			if ((sp->_mouse_mask & MASK_DOUBLE_CLICK(b))
			    && (follower->bstate & MASK_CLICK(b))) {
			    follower->bstate &= ~MASK_CLICK(b);
			    follower->bstate |= MASK_DOUBLE_CLICK(b);
			    merge = TRUE;
			}
		    }
		    if (merge)
			ep->id = INVALID_EVENT;
		}

		/* merge double-click events forward */
		if ((ep->bstate & BUTTON_DOUBLE_CLICKED)
		    && (follower->bstate & BUTTON_CLICKED)) {
		    for (b = 1; b <= MAX_BUTTONS; ++b) {
			if ((sp->_mouse_mask & MASK_TRIPLE_CLICK(b))
			    && (follower->bstate & MASK_CLICK(b))) {
			    follower->bstate &= ~MASK_CLICK(b);
			    follower->bstate |= MASK_TRIPLE_CLICK(b);
			    merge = TRUE;
			}
		    }
		    if (merge)
			ep->id = INVALID_EVENT;
		}
	    }
    } while
	(merge);

#ifdef TRACE
    if (USE_TRACEF(TRACE_IEVENT)) {
	_trace_slot(sp, "before mouse event queue compaction:");
	_tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
		RunParams(sp, eventp, runp),
		runcount);
	_nc_unlock_global(tracef);
    }
#endif /* TRACE */

    /*
     * Now try to throw away trailing events flagged invalid, or that
     * don't match the current event mask.
     */
    for (; runcount; prev = PREV(eventp), runcount--)
	if (prev->id == INVALID_EVENT || !(prev->bstate & sp->_mouse_mask)) {
	    sp->_mouse_eventp = eventp = prev;
	}
#ifdef TRACE
    if (USE_TRACEF(TRACE_IEVENT)) {
	_trace_slot(sp, "after mouse event queue compaction:");
	_tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
		RunParams(sp, eventp, runp),
		runcount);
	_nc_unlock_global(tracef);
    }
    for (ep = runp; ep != eventp; ep = NEXT(ep))
	if (ep->id != INVALID_EVENT)
	    TR(MY_TRACE,
	       ("_nc_mouse_parse: returning composite mouse event %s at slot %ld",
		_nc_tracemouse(sp, ep),
		(long) IndexEV(sp, ep)));
#endif /* TRACE */

    /* after all this, do we have a valid event? */
    return (PREV(eventp)->id != INVALID_EVENT);
}
Example #6
0
static bool
_nc_mouse_parse(SCREEN *sp, int runcount)
/* parse a run of atomic mouse events into a gesture */
{
    MEVENT *eventp = sp->_mouse_eventp;
    MEVENT *next, *ep;
    MEVENT *first_valid = NULL;
    MEVENT *first_invalid = NULL;
    int n;
    int b;
    bool merge;
    bool endLoop;

    TR(MY_TRACE, ("_nc_mouse_parse(%d) called", runcount));

    /*
     * When we enter this routine, the event list next-free pointer
     * points just past a run of mouse events that we know were separated
     * in time by less than the critical click interval. The job of this
     * routine is to collapse this run into a single higher-level event
     * or gesture.
     *
     * We accomplish this in two passes.  The first pass merges press/release
     * pairs into click events.  The second merges runs of click events into
     * double or triple-click events.
     *
     * It's possible that the run may not resolve to a single event (for
     * example, if the user quadruple-clicks).  If so, leading events
     * in the run are ignored if user does not call getmouse in a loop (getting
     * them from newest to older).
     *
     * Note that this routine is independent of the format of the specific
     * format of the pointing-device's reports.  We can use it to parse
     * gestures on anything that reports press/release events on a per-
     * button basis, as long as the device-dependent mouse code puts stuff
     * on the queue in MEVENT format.
     */

    /*
     * Reset all events that were not set, in case the user sometimes calls
     * getmouse only once and other times until there are no more events in
     * queue.
     *
     * This also allows reaching the beginning of the run.
     */
    ep = eventp;
    for (n = runcount; n < EV_MAX; n++) {
	Invalidate(ep);
	ep = NEXT(ep);
    }

#ifdef TRACE
    if (USE_TRACEF(TRACE_IEVENT)) {
	_trace_slot(sp, "before mouse press/release merge:");
	_tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
		RunParams(sp, eventp, ep),
		runcount);
	_nc_unlock_global(tracef);
    }
#endif /* TRACE */

    /* first pass; merge press/release pairs */
    endLoop = FALSE;
    while (!endLoop) {
	next = NEXT(ep);
	if (next == eventp) {
	    /* Will end the loop, but compact before */
	    endLoop = TRUE;
	} else {

#define MASK_CHANGED(x) (!(ep->bstate & MASK_PRESS(x)) \
		      == !(next->bstate & MASK_RELEASE(x)))

	    if (ValidEvent(ep) && ValidEvent(next)
		&& ep->x == next->x && ep->y == next->y
		&& (ep->bstate & BUTTON_PRESSED)
		&& (!(next->bstate & BUTTON_PRESSED))) {
		bool changed = TRUE;

		for (b = 1; b <= MAX_BUTTONS; ++b) {
		    if (!MASK_CHANGED(b)) {
			changed = FALSE;
			break;
		    }
		}

		if (changed) {
		    merge = FALSE;
		    for (b = 1; b <= MAX_BUTTONS; ++b) {
			if ((sp->_mouse_mask & MASK_CLICK(b))
			    && (ep->bstate & MASK_PRESS(b))) {
			    next->bstate &= ~MASK_RELEASE(b);
			    next->bstate |= MASK_CLICK(b);
			    merge = TRUE;
			}
		    }
		    if (merge) {
			Invalidate(ep);
		    }
		}
	    }
	}

	/* Compact valid events */
	if (!ValidEvent(ep)) {
	    if ((first_valid != NULL) && (first_invalid == NULL)) {
		first_invalid = ep;
	    }
	} else {
	    if (first_valid == NULL) {
		first_valid = ep;
	    } else if (first_invalid != NULL) {
		*first_invalid = *ep;
		Invalidate(ep);
		first_invalid = NEXT(first_invalid);
	    }
	}

	ep = next;
    }

    if (first_invalid != NULL) {
	eventp = first_invalid;
    }
#ifdef TRACE
    if (USE_TRACEF(TRACE_IEVENT)) {
	_trace_slot(sp, "before mouse click merge:");
	if (first_valid == NULL) {
	    _tracef("_nc_mouse_parse: no valid event");
	} else {
	    _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
		    RunParams(sp, eventp, first_valid),
		    runcount);
	    _nc_unlock_global(tracef);
	}
    }
#endif /* TRACE */

    /*
     * Second pass; merge click runs.  We merge click events forward in the
     * queue.  For example, double click can be changed to triple click.
     *
     * NOTE: There is a problem with this design!  If the application
     * allows enough click events to pile up in the circular queue so
     * they wrap around, it will cheerfully merge the newest forward
     * into the oldest, creating a bogus doubleclick and confusing
     * the queue-traversal logic rather badly.  Generally this won't
     * happen, because calling getmouse() marks old events invalid and
     * ineligible for merges.  The true solution to this problem would
     * be to timestamp each MEVENT and perform the obvious sanity check,
     * but the timer element would have to have sub-second resolution,
     * which would get us into portability trouble.
     */
    first_invalid = NULL;
    endLoop = (first_valid == NULL);
    ep = first_valid;
    while (!endLoop) {
	next = NEXT(ep);

	if (next == eventp) {
	    /* Will end the loop, but check event type and compact before */
	    endLoop = TRUE;
	} else if (!ValidEvent(next)) {
	    continue;
	} else {
	    /* merge click events forward */
	    if ((ep->bstate & BUTTON_CLICKED)
		&& (next->bstate & BUTTON_CLICKED)) {
		merge = FALSE;
		for (b = 1; b <= MAX_BUTTONS; ++b) {
		    if ((sp->_mouse_mask & MASK_DOUBLE_CLICK(b))
			&& (ep->bstate & MASK_CLICK(b))
			&& (next->bstate & MASK_CLICK(b))) {
			next->bstate &= ~MASK_CLICK(b);
			next->bstate |= MASK_DOUBLE_CLICK(b);
			merge = TRUE;
		    }
		}
		if (merge) {
		    Invalidate(ep);
		}
	    }

	    /* merge double-click events forward */
	    if ((ep->bstate & BUTTON_DOUBLE_CLICKED)
		&& (next->bstate & BUTTON_CLICKED)) {
		merge = FALSE;
		for (b = 1; b <= MAX_BUTTONS; ++b) {
		    if ((sp->_mouse_mask & MASK_TRIPLE_CLICK(b))
			&& (ep->bstate & MASK_DOUBLE_CLICK(b))
			&& (next->bstate & MASK_CLICK(b))) {
			next->bstate &= ~MASK_CLICK(b);
			next->bstate |= MASK_TRIPLE_CLICK(b);
			merge = TRUE;
		    }
		}
		if (merge) {
		    Invalidate(ep);
		}
	    }
	}

	/* Discard event if it does not match event mask */
	if (!(ep->bstate & sp->_mouse_mask2)) {
	    Invalidate(ep);
	}

	/* Compact valid events */
	if (!ValidEvent(ep)) {
	    if (ep == first_valid) {
		first_valid = next;
	    } else if (first_invalid == NULL) {
		first_invalid = ep;
	    }
	} else if (first_invalid != NULL) {
	    *first_invalid = *ep;
	    Invalidate(ep);
	    first_invalid = NEXT(first_invalid);
	}

	ep = next;
    }

    if (first_invalid == NULL) {
	first_invalid = eventp;
    }
    sp->_mouse_eventp = first_invalid;

#ifdef TRACE
    if (first_valid != NULL) {
	if (USE_TRACEF(TRACE_IEVENT)) {
	    _trace_slot(sp, "after mouse event queue compaction:");
	    _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
		    RunParams(sp, first_invalid, first_valid),
		    runcount);
	    _nc_unlock_global(tracef);
	}
	for (ep = first_valid; ep != first_invalid; ep = NEXT(ep)) {
	    if (ValidEvent(ep))
		TR(MY_TRACE,
		   ("_nc_mouse_parse: returning composite mouse event %s at slot %ld",
		    _nc_tracemouse(sp, ep),
		    (long) IndexEV(sp, ep)));
	}
    }
#endif /* TRACE */

    /* after all this, do we have a valid event? */
    return ValidEvent(PREV(first_invalid));
}