static struct timespec timer_last_idleness_start_time;
+/* Predefined strings for core device names. */
+
+static Lisp_Object virtual_core_pointer_name;
+static Lisp_Object virtual_core_keyboard_name;
+
\f
/* Global variable declarations. */
goto reread_for_input_method;
}
+ Vlast_event_device = Qnil;
+
if (!NILP (Vexecuting_kbd_macro))
{
/* We set this to Qmacro; since that's not a frame, nobody will
obj = make_lispy_switch_frame (frame);
internal_last_event_frame = frame;
+ if (EQ (event->ie.device, Qt))
+ Vlast_event_device = ((event->ie.kind == ASCII_KEYSTROKE_EVENT
+ || event->ie.kind == MULTIBYTE_CHAR_KEYSTROKE_EVENT
+ || event->ie.kind == NON_ASCII_KEYSTROKE_EVENT)
+ ? virtual_core_keyboard_name
+ : virtual_core_pointer_name);
+ else
+ Vlast_event_device = event->ie.device;
+
/* If we didn't decide to make a switch-frame event, go ahead
and build a real event from the queue entry. */
if (NILP (obj))
XSETCAR (Fnthcdr (make_fixnum (3),
maybe_event->ie.arg),
make_float (fmod (pinch_angle, 360.0)));
+
+ if (!EQ (maybe_event->ie.device, Qt))
+ Vlast_event_device = maybe_event->ie.device;
+
maybe_event = next_kbd_event (event);
}
}
return a mouse-motion event. */
if (!NILP (x) && NILP (obj))
obj = make_lispy_movement (f, bar_window, part, x, y, t);
+
+ if (!NILP (obj))
+ Vlast_event_device = (STRINGP (f->last_mouse_device)
+ ? f->last_mouse_device
+ : virtual_core_pointer_name);
}
else
/* We were promised by the above while loop that there was
interrupt_input_blocked = 0;
pending_signals = false;
+ virtual_core_pointer_name = build_string ("Virtual core pointer");
+ virtual_core_keyboard_name = build_string ("Virtual core keyboard");
+ Vlast_event_device = Qnil;
+
/* This means that command_loop_1 won't try to select anything the first
time through. */
internal_last_event_frame = Qnil;
staticpro (&poll_timer_time);
#endif
+ virtual_core_pointer_name = Qnil;
+ staticpro (&virtual_core_pointer_name);
+
+ virtual_core_keyboard_name = Qnil;
+ staticpro (&virtual_core_keyboard_name);
+
defsubr (&Scurrent_idle_time);
defsubr (&Sevent_symbol_parse_modifiers);
defsubr (&Sevent_convert_list);
If the last event came from a keyboard macro, this is set to `macro'. */);
Vlast_event_frame = Qnil;
+ DEFVAR_LISP ("last-event-device", Vlast_event_device,
+ doc: /* The name of the input device of the most recently read event.
+When the input extension is being used on X, this is the name of the X
+Input Extension device from which the last event was generated as a
+string. Otherwise, this is "Virtual core keyboard" for keyboard input
+events, and "Virtual core pointer" for other events.
+
+It is nil if the last event did not come from an input device (i.e. it
+came from `unread-command-events' instead). */);
+ Vlast_event_device = Qnil;
+
/* This variable is set up in sysdep.c. */
DEFVAR_LISP ("tty-erase-char", Vtty_erase_char,
doc: /* The ERASE character as set by the user with stty. */);
#ifdef HAVE_XINPUT2_2
xi_device->direct_p = false;
#endif
+ xi_device->name = build_string (device->name);
for (int c = 0; c < device->num_classes; ++c)
{
if (x_dnd_return_frame == 3
&& FRAME_LIVE_P (x_dnd_return_frame_object))
{
+ /* Deliberately preserve the last device if
+ x_dnd_return_frame_object is the drag source. */
+
+ if (x_dnd_return_frame_object != x_dnd_frame)
+ x_dnd_return_frame_object->last_mouse_device = Qnil;
+
x_dnd_return_frame_object->mouse_moved = true;
XSETFRAME (action, x_dnd_return_frame_object);
XI_Enter and XI_Leave labels inside `handle_one_xevent'. */
static bool
-x_note_mouse_movement (struct frame *frame, const XMotionEvent *event)
+x_note_mouse_movement (struct frame *frame, const XMotionEvent *event,
+ Lisp_Object device)
{
XRectangle *r;
struct x_display_info *dpyinfo;
if (event->window != FRAME_X_WINDOW (frame))
{
frame->mouse_moved = true;
+ frame->last_mouse_device = device;
dpyinfo->last_mouse_scroll_bar = NULL;
note_mouse_highlight (frame, -1, -1);
dpyinfo->last_mouse_glyph_frame = NULL;
|| event->y < r->y || event->y >= r->y + r->height)
{
frame->mouse_moved = true;
+ frame->last_mouse_device = device;
dpyinfo->last_mouse_scroll_bar = NULL;
note_mouse_highlight (frame, event->x, event->y);
/* Remember which glyph we're now on. */
/* EnterNotify counts as mouse movement,
so update things that depend on mouse position. */
if (f && !f->output_data.x->hourglass_p)
- x_note_mouse_movement (f, &event->xmotion);
+ x_note_mouse_movement (f, &event->xmotion, Qnil);
#ifdef USE_GTK
/* We may get an EnterNotify on the buttons in the toolbar. In that
case we moved out of any highlighted area and need to note this. */
if (!f && dpyinfo->last_mouse_glyph_frame)
- x_note_mouse_movement (dpyinfo->last_mouse_glyph_frame, &event->xmotion);
+ x_note_mouse_movement (dpyinfo->last_mouse_glyph_frame, &event->xmotion,
+ Qnil);
#endif
goto OTHER;
#ifdef USE_GTK
/* See comment in EnterNotify above */
else if (dpyinfo->last_mouse_glyph_frame)
- x_note_mouse_movement (dpyinfo->last_mouse_glyph_frame, &event->xmotion);
+ x_note_mouse_movement (dpyinfo->last_mouse_glyph_frame,
+ &event->xmotion, Qnil);
#endif
goto OTHER;
last_mouse_window = window;
}
- if (!x_note_mouse_movement (f, &xmotion))
+ if (!x_note_mouse_movement (f, &xmotion, Qnil))
help_echo_string = previous_help_echo_string;
}
else
{
XIEnterEvent *enter = (XIEnterEvent *) xi_event;
XMotionEvent ev;
+ struct xi_device_t *source;
any = x_top_window_to_frame (dpyinfo, enter->event);
+ source = xi_device_from_id (dpyinfo, enter->sourceid);
ev.x = lrint (enter->event_x);
ev.y = lrint (enter->event_y);
ev.window = enter->event;
/* EnterNotify counts as mouse movement,
so update things that depend on mouse position. */
if (f && !f->output_data.x->hourglass_p)
- x_note_mouse_movement (f, &ev);
+ x_note_mouse_movement (f, &ev, source ? source->name : Qnil);
#ifdef USE_GTK
/* We may get an EnterNotify on the buttons in the toolbar. In that
case we moved out of any highlighted area and need to note this. */
if (!f && dpyinfo->last_mouse_glyph_frame)
- x_note_mouse_movement (dpyinfo->last_mouse_glyph_frame, &ev);
+ x_note_mouse_movement (dpyinfo->last_mouse_glyph_frame, &ev,
+ source ? source->name : Qnil);
#endif
goto XI_OTHER;
}
case XI_Leave:
{
XILeaveEvent *leave = (XILeaveEvent *) xi_event;
+ struct xi_device_t *source;
#ifdef USE_GTK
XMotionEvent ev;
#endif
any = x_top_window_to_frame (dpyinfo, leave->event);
+ source = xi_device_from_id (dpyinfo, leave->sourceid);
/* This allows us to catch LeaveNotify events generated by
popup menu grabs. FIXME: this is right when there is a
#ifdef USE_GTK
/* See comment in EnterNotify above */
else if (dpyinfo->last_mouse_glyph_frame)
- x_note_mouse_movement (dpyinfo->last_mouse_glyph_frame, &ev);
+ x_note_mouse_movement (dpyinfo->last_mouse_glyph_frame, &ev,
+ source ? source->name : Qnil);
#endif
goto XI_OTHER;
}
case XI_Motion:
{
- struct xi_device_t *device;
+ struct xi_device_t *device, *source;
#ifdef HAVE_XINPUT2_1
XIValuatorState *states;
double *values;
xm_top_level_enter_message emsg;
xm_drag_motion_message dmsg;
+ source = xi_device_from_id (dpyinfo, xev->sourceid);
#ifdef HAVE_XINPUT2_1
states = &xev->valuators;
XSETFRAME (inev.ie.frame_or_window, f);
}
+ if (source && source->name)
+ inev.ie.device = source->name;
+
goto XI_OTHER;
}
#ifdef HAVE_XWIDGETS
{
inev.ie.kind = SELECT_WINDOW_EVENT;
inev.ie.frame_or_window = window;
+
+ if (source)
+ inev.ie.device = source->name;
}
/* Remember the last window where we saw the mouse. */
last_mouse_window = window;
}
- if (!x_note_mouse_movement (f, &ev))
+ if (!x_note_mouse_movement (f, &ev, source ? source->name : Qnil))
help_echo_string = previous_help_echo_string;
}
else
Lisp_Object tab_bar_arg = Qnil;
bool tab_bar_p = false;
bool tool_bar_p = false;
- struct xi_device_t *device;
+ struct xi_device_t *device, *source;
#ifdef HAVE_XWIDGETS
struct xwidget_view *xvw;
#endif
if (xev->evtype == XI_ButtonPress)
x_display_set_last_user_time (dpyinfo, xev->time);
+ source = xi_device_from_id (dpyinfo, xev->sourceid);
+
#ifdef HAVE_XWIDGETS
xvw = xwidget_view_from_window (xev->event);
if (xvw)
{
inev.ie.kind = SELECT_WINDOW_EVENT;
inev.ie.frame_or_window = xvw->w;
+
+ if (source)
+ inev.ie.device = source->name;
}
*finish = X_EVENT_DROP;
else
inev.ie.kind = HORIZ_WHEEL_EVENT;
+ if (source)
+ inev.ie.device = source->name;
+
inev.ie.timestamp = xev->time;
XSETINT (inev.ie.x, real_x);
else
inev.ie.kind = HORIZ_WHEEL_EVENT;
+ if (source)
+ inev.ie.device = source->name;
+
inev.ie.timestamp = xev->time;
XSETINT (inev.ie.x, lrint (xev->event_x));
device->grab &= ~(1 << xev->detail);
}
+ if (source && inev.ie.kind != NO_EVENT)
+ inev.ie.device = source->name;
+
if (f)
f->mouse_moved = false;
char *copy_bufptr = copy_buffer;
int copy_bufsiz = sizeof (copy_buffer);
ptrdiff_t i;
- struct xi_device_t *device;
+ struct xi_device_t *device, *source;
coding = Qlatin_1;
device = xi_device_from_id (dpyinfo, xev->deviceid);
+ source = xi_device_from_id (dpyinfo, xev->sourceid);
if (!device)
goto XI_OTHER;
inev.ie.kind = ASCII_KEYSTROKE_EVENT;
inev.ie.code = keysym;
+ if (source)
+ inev.ie.device = source->name;
+
goto xi_done_keysym;
}
inev.ie.kind = ASCII_KEYSTROKE_EVENT;
else
inev.ie.kind = MULTIBYTE_CHAR_KEYSTROKE_EVENT;
+
+ if (source)
+ inev.ie.device = source->name;
+
inev.ie.code = keysym & 0xFFFFFF;
goto xi_done_keysym;
}
? ASCII_KEYSTROKE_EVENT
: MULTIBYTE_CHAR_KEYSTROKE_EVENT);
inev.ie.code = XFIXNAT (c);
+
+ if (source)
+ inev.ie.device = source->name;
+
goto xi_done_keysym;
}
key. */
inev.ie.kind = NON_ASCII_KEYSTROKE_EVENT;
inev.ie.code = keysym;
+
+ if (source)
+ inev.ie.device = source->name;
+
goto xi_done_keysym;
}
Fput_text_property (make_fixnum (0), make_fixnum (nbytes),
Qcoding, coding, inev.ie.arg);
+
+ if (source)
+ inev.ie.device = source->name;
}
goto xi_done_keysym;
}
#ifdef HAVE_XINPUT2_2
case XI_TouchBegin:
{
- struct xi_device_t *device;
+ struct xi_device_t *device, *source;
bool menu_bar_p = false, tool_bar_p = false;
#ifdef HAVE_GTK3
GdkRectangle test_rect;
#endif
device = xi_device_from_id (dpyinfo, xev->deviceid);
+ source = xi_device_from_id (dpyinfo, xev->sourceid);
x_display_set_last_user_time (dpyinfo, xev->time);
if (!device)
XSETINT (inev.ie.x, lrint (xev->event_x));
XSETINT (inev.ie.y, lrint (xev->event_y));
XSETINT (inev.ie.arg, xev->detail);
+
+ if (source)
+ inev.ie.device = source->name;
}
x_uncatch_errors_after_check ();
}
case XI_TouchUpdate:
{
- struct xi_device_t *device;
+ struct xi_device_t *device, *source;
struct xi_touch_point_t *touchpoint;
Lisp_Object arg = Qnil;
device = xi_device_from_id (dpyinfo, xev->deviceid);
+ source = xi_device_from_id (dpyinfo, xev->sourceid);
x_display_set_last_user_time (dpyinfo, xev->time);
if (!device)
arg);
}
+ if (source)
+ inev.ie.device = source->name;
+
inev.ie.arg = arg;
}
case XI_TouchEnd:
{
- struct xi_device_t *device;
+ struct xi_device_t *device, *source;
bool unlinked_p;
device = xi_device_from_id (dpyinfo, xev->deviceid);
+ source = xi_device_from_id (dpyinfo, xev->sourceid);
x_display_set_last_user_time (dpyinfo, xev->time);
if (!device)
{
inev.ie.kind = TOUCHSCREEN_END_EVENT;
inev.ie.timestamp = xev->time;
+
XSETFRAME (inev.ie.frame_or_window, f);
XSETINT (inev.ie.x, lrint (xev->event_x));
XSETINT (inev.ie.y, lrint (xev->event_y));
XSETINT (inev.ie.arg, xev->detail);
+
+ if (source)
+ inev.ie.device = source->name;
}
}
case XI_GesturePinchBegin:
case XI_GesturePinchUpdate:
{
- x_display_set_last_user_time (dpyinfo, xi_event->time);
-
XIGesturePinchEvent *pev = (XIGesturePinchEvent *) xi_event;
- struct xi_device_t *device = xi_device_from_id (dpyinfo, pev->deviceid);
+ struct xi_device_t *device, *source;
+
+ device = xi_device_from_id (dpyinfo, pev->deviceid);
+ source = xi_device_from_id (dpyinfo, pev->sourceid);
+ x_display_set_last_user_time (dpyinfo, xi_event->time);
if (!device)
goto XI_OTHER;
make_float (pev->delta_y),
make_float (pev->scale),
make_float (pev->delta_angle));
+
+ if (source)
+ inev.ie.device = source->name;
}
/* Once again GTK seems to crash when confronted by
mark_xterm (void)
{
Lisp_Object val;
+#ifdef HAVE_XINPUT2
+ struct x_display_info *dpyinfo;
+ int i;
+#endif
if (x_dnd_return_frame_object)
{
XSETFRAME (val, x_dnd_movement_frame);
mark_object (val);
}
+
+#ifdef HAVE_XINPUT2
+ for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
+ {
+ for (i = 0; i < dpyinfo->num_devices; ++i)
+ mark_object (dpyinfo->devices[i].name);
+ }
+#endif
}
void