diff options
author | Po Lu <luangruo@yahoo.com> | 2022-04-07 21:16:11 +0800 |
---|---|---|
committer | Po Lu <luangruo@yahoo.com> | 2022-04-07 21:16:11 +0800 |
commit | 3b411417086ceb2ce3838160d01c6f250e47bbf3 (patch) | |
tree | 7605008be2090be2cc58ba6df8df6c2d22b8184e | |
parent | c1a6aa0c3eb1029e3f7f5c3b227d7952bee775b8 (diff) | |
download | emacs-3b411417086ceb2ce3838160d01c6f250e47bbf3.tar.gz emacs-3b411417086ceb2ce3838160d01c6f250e47bbf3.tar.bz2 emacs-3b411417086ceb2ce3838160d01c6f250e47bbf3.zip |
Expose the name of an event's input device to Lisp
This name can be used to identify the device for special
treatment, i.e. only interpolating scrolls coming from mice and
not touchpads inside pixel-scroll-precision-mode.
* doc/lispref/commands.texi (Command Loop Info): Document new
variable `last-event-device'.
* etc/NEWS: Announce new variable `last-event-device'.
* src/frame.h (struct frame): New field `last_mouse_device'.
* src/keyboard.c (read_char): Clear last-event-device.
(kbd_buffer_get_event): Set last-event-device to the event's
recorded device.
(init_keyboard): Clear last-event-device.
(syms_of_keyboard): New defvar `last-event-device'.
* src/termhooks.h (struct input_event): New field `device'.
(EVENT_INIT): Set it to the special value `Qt' by default.
* src/xterm.c (x_init_master_valuators): Record the device's
name.
(x_dnd_begin_drag_and_drop): Only preserve last event device if
the mouse ended up in the source frame.
(x_note_mouse_movement): New argument `source'.
(handle_one_xevent): Set input event sources whenever
appropriate.
(mark_xterm): Mark device names.
* src/xterm.h (struct xi_device_t): New field `name'.
-rw-r--r-- | doc/lispref/commands.texi | 14 | ||||
-rw-r--r-- | etc/NEWS | 5 | ||||
-rw-r--r-- | src/frame.h | 4 | ||||
-rw-r--r-- | src/keyboard.c | 46 | ||||
-rw-r--r-- | src/termhooks.h | 10 | ||||
-rw-r--r-- | src/xterm.c | 124 | ||||
-rw-r--r-- | src/xterm.h | 2 |
7 files changed, 186 insertions, 19 deletions
diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index a4ae68af5b2..74bf0f48692 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -1127,6 +1127,20 @@ frame, the value is the frame to which the event was redirected. If the last event came from a keyboard macro, the value is @code{macro}. @end defvar +@defvar last-event-device +This variable records the name of the input device from which the last +input event read was generated. It is @code{nil} if no such device +exists, i.e., the last input event was read from +@code{unread-command-events}, or it came from a keyboard macro. + +When the X Input Extension is being used on X Windows, the device name +is a string that is unique to each physical keyboard, pointing device +and touchscreen attached to the X server. Otherwise, it is either the +string @samp{"Virtual core pointer"} or @samp{"Virtual core +keyboard"}, depending on whether the event was generated by a pointing +device (such as a mouse) or a keyboard. +@end defvar + @node Adjusting Point @section Adjusting Point After Commands @cindex adjusting point @@ -1354,6 +1354,11 @@ functions. * Lisp Changes in Emacs 29.1 +++ +** New variable 'last-event-device'. +On X Windows, this specifies the input extension device from which the +last input event originated. + ++++ ** 'track-mouse' can be a new value 'drag-source'. This means the same as 'dropping', but modifies the mouse position list in reported motion events if there is no frame underneath the diff --git a/src/frame.h b/src/frame.h index 61df57e966a..4942e640d27 100644 --- a/src/frame.h +++ b/src/frame.h @@ -102,6 +102,10 @@ struct frame Lisp_Object parent_frame; #endif /* HAVE_WINDOW_SYSTEM */ + /* Last device to move over this frame. Any value that isn't a + string means the "Virtual core pointer". */ + Lisp_Object last_mouse_device; + /* The frame which should receive keystrokes that occur in this frame, or nil if they should go to the frame itself. This is usually nil, but if the frame is minibufferless, we can use this diff --git a/src/keyboard.c b/src/keyboard.c index d99fe4be092..8142ffec2df 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -336,6 +336,11 @@ static struct timespec timer_idleness_start_time; 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; + /* Global variable declarations. */ @@ -2520,6 +2525,8 @@ read_char (int commandflag, Lisp_Object map, 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 @@ -4118,6 +4125,15 @@ kbd_buffer_get_event (KBOARD **kbp, 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)) @@ -4173,6 +4189,10 @@ kbd_buffer_get_event (KBOARD **kbp, 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); } } @@ -4296,6 +4316,11 @@ kbd_buffer_get_event (KBOARD **kbp, 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 @@ -11805,6 +11830,10 @@ init_keyboard (void) 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; @@ -12225,6 +12254,12 @@ syms_of_keyboard (void) 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); @@ -12423,6 +12458,17 @@ This does not include events generated by keyboard macros. */); 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. */); diff --git a/src/termhooks.h b/src/termhooks.h index 0f02b56e9ee..8c193914ba8 100644 --- a/src/termhooks.h +++ b/src/termhooks.h @@ -392,9 +392,17 @@ struct input_event when building events. Unfortunately some events have to pass much more data than it's reasonable to pack directly into this structure. */ Lisp_Object arg; + + /* The name of the device from which this event originated. + + It can either be a string, or Qt, which means to use the name + "Virtual core pointer" for all events other than keystroke + events, and "Virtual core keyboard" for those. */ + Lisp_Object device; }; -#define EVENT_INIT(event) memset (&(event), 0, sizeof (struct input_event)) +#define EVENT_INIT(event) (memset (&(event), 0, sizeof (struct input_event)), \ + (event).device = Qt) /* Bits in the modifiers member of the input_event structure. Note that reorder_modifiers assumes that the bits are in canonical diff --git a/src/xterm.c b/src/xterm.c index 57a64cb5d12..d4a5e0ab3dc 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -3922,6 +3922,7 @@ x_init_master_valuators (struct x_display_info *dpyinfo) #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) { @@ -9653,6 +9654,12 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction, 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); @@ -10170,7 +10177,8 @@ x_construct_mouse_click (struct input_event *result, 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; @@ -10187,6 +10195,7 @@ x_note_mouse_movement (struct frame *frame, const XMotionEvent *event) 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; @@ -10201,6 +10210,7 @@ x_note_mouse_movement (struct frame *frame, const XMotionEvent *event) || 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. */ @@ -14788,12 +14798,13 @@ handle_one_xevent (struct x_display_info *dpyinfo, /* 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; @@ -14884,7 +14895,8 @@ handle_one_xevent (struct x_display_info *dpyinfo, #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; @@ -15123,7 +15135,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, 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 @@ -15903,8 +15915,10 @@ handle_one_xevent (struct x_display_info *dpyinfo, { 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; @@ -15972,12 +15986,13 @@ handle_one_xevent (struct x_display_info *dpyinfo, /* 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; } @@ -15985,6 +16000,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, case XI_Leave: { XILeaveEvent *leave = (XILeaveEvent *) xi_event; + struct xi_device_t *source; #ifdef USE_GTK XMotionEvent ev; @@ -15995,6 +16011,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, #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 @@ -16098,14 +16115,15 @@ handle_one_xevent (struct x_display_info *dpyinfo, #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; @@ -16117,6 +16135,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, 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; @@ -16333,6 +16352,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, XSETFRAME (inev.ie.frame_or_window, f); } + if (source && source->name) + inev.ie.device = source->name; + goto XI_OTHER; } #ifdef HAVE_XWIDGETS @@ -16588,13 +16610,16 @@ handle_one_xevent (struct x_display_info *dpyinfo, { 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 @@ -16628,7 +16653,7 @@ handle_one_xevent (struct x_display_info *dpyinfo, 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 @@ -16837,6 +16862,8 @@ handle_one_xevent (struct x_display_info *dpyinfo, 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) @@ -16849,6 +16876,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, { inev.ie.kind = SELECT_WINDOW_EVENT; inev.ie.frame_or_window = xvw->w; + + if (source) + inev.ie.device = source->name; } *finish = X_EVENT_DROP; @@ -16918,6 +16948,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, 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); @@ -16953,6 +16986,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, 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)); @@ -17070,6 +17106,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, device->grab &= ~(1 << xev->detail); } + if (source && inev.ie.kind != NO_EVENT) + inev.ie.device = source->name; + if (f) f->mouse_moved = false; @@ -17108,11 +17147,12 @@ handle_one_xevent (struct x_display_info *dpyinfo, 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; @@ -17350,6 +17390,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, inev.ie.kind = ASCII_KEYSTROKE_EVENT; inev.ie.code = keysym; + if (source) + inev.ie.device = source->name; + goto xi_done_keysym; } @@ -17360,6 +17403,10 @@ handle_one_xevent (struct x_display_info *dpyinfo, 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; } @@ -17375,6 +17422,10 @@ handle_one_xevent (struct x_display_info *dpyinfo, ? ASCII_KEYSTROKE_EVENT : MULTIBYTE_CHAR_KEYSTROKE_EVENT); inev.ie.code = XFIXNAT (c); + + if (source) + inev.ie.device = source->name; + goto xi_done_keysym; } @@ -17479,6 +17530,10 @@ handle_one_xevent (struct x_display_info *dpyinfo, key. */ inev.ie.kind = NON_ASCII_KEYSTROKE_EVENT; inev.ie.code = keysym; + + if (source) + inev.ie.device = source->name; + goto xi_done_keysym; } @@ -17494,6 +17549,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, 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; } @@ -17688,12 +17746,13 @@ handle_one_xevent (struct x_display_info *dpyinfo, #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) @@ -17741,6 +17800,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, 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 (); } @@ -17774,11 +17836,12 @@ handle_one_xevent (struct x_display_info *dpyinfo, 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) @@ -17809,6 +17872,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, arg); } + if (source) + inev.ie.device = source->name; + inev.ie.arg = arg; } @@ -17817,10 +17883,11 @@ handle_one_xevent (struct x_display_info *dpyinfo, 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) @@ -17836,10 +17903,14 @@ handle_one_xevent (struct x_display_info *dpyinfo, { 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; } } @@ -17852,10 +17923,12 @@ handle_one_xevent (struct x_display_info *dpyinfo, 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; @@ -17884,6 +17957,9 @@ handle_one_xevent (struct x_display_info *dpyinfo, 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 @@ -23142,6 +23218,10 @@ void mark_xterm (void) { Lisp_Object val; +#ifdef HAVE_XINPUT2 + struct x_display_info *dpyinfo; + int i; +#endif if (x_dnd_return_frame_object) { @@ -23154,6 +23234,14 @@ mark_xterm (void) 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 diff --git a/src/xterm.h b/src/xterm.h index d8898162d11..c12fd6c3fe1 100644 --- a/src/xterm.h +++ b/src/xterm.h @@ -244,6 +244,8 @@ struct xi_device_t #ifdef HAVE_XINPUT2_2 struct xi_touch_point_t *touchpoints; #endif + + Lisp_Object name; }; #endif |