diff options
| author | Po Lu | 2024-06-01 15:41:54 +0800 |
|---|---|---|
| committer | Po Lu | 2024-06-01 15:41:54 +0800 |
| commit | 2b7056db424ab0f8bf9e96b5a3c6aa12a3debf48 (patch) | |
| tree | 2e1bb7731a0e25001b2df993b2c218f797b00df9 /src | |
| parent | b1692e23edc32ce8938d3af200c0c42c8aa6b313 (diff) | |
| download | emacs-2b7056db424ab0f8bf9e96b5a3c6aa12a3debf48.tar.gz emacs-2b7056db424ab0f8bf9e96b5a3c6aa12a3debf48.zip | |
Implement touch screen events on PGTK
* etc/NEWS: Better qualify entry for touch screen events.
* lisp/loadup.el (featurep 'pgtk): Load touch-screen.el.
* lisp/touch-screen.el: Revise list of systems where touch
screen events are reported.
* src/gtkutil.c (xg_create_frame_widgets): Request
GDK_TOUCH_MASK.
* src/pgtkfns.c (pgtk_frame_parm_handlers, tip_window): Pacify
compiler warning.
* src/pgtkterm.c (pgtk_free_frame_resources): Free touch points
linked to this frame.
(pgtk_link_touch_point, pgtk_unlink_touch_point)
(pgtk_unlink_touch_points, pgtk_find_touch_point): New
functions, ported from X.
(touch_event_cb): New event callback.
(pgtk_set_event_handler): Register `touch_event_cb' as handler
for `touch-event'.
(pgtk_delete_display): Free residual touch points on this
display.
* src/pgtkterm.h (struct pgtk_touch_point): New structure.
(struct pgtk_display_info) <touchpoints>: New field.
Diffstat (limited to 'src')
| -rw-r--r-- | src/gtkutil.c | 1 | ||||
| -rw-r--r-- | src/pgtkfns.c | 3 | ||||
| -rw-r--r-- | src/pgtkterm.c | 241 | ||||
| -rw-r--r-- | src/pgtkterm.h | 32 |
4 files changed, 274 insertions, 3 deletions
diff --git a/src/gtkutil.c b/src/gtkutil.c index 7de8eba0aa1..d57627f152f 100644 --- a/src/gtkutil.c +++ b/src/gtkutil.c | |||
| @@ -1669,6 +1669,7 @@ xg_create_frame_widgets (struct frame *f) | |||
| 1669 | #ifdef HAVE_PGTK | 1669 | #ifdef HAVE_PGTK |
| 1670 | | GDK_SCROLL_MASK | 1670 | | GDK_SCROLL_MASK |
| 1671 | | GDK_SMOOTH_SCROLL_MASK | 1671 | | GDK_SMOOTH_SCROLL_MASK |
| 1672 | | GDK_TOUCH_MASK | ||
| 1672 | #endif | 1673 | #endif |
| 1673 | | GDK_VISIBILITY_NOTIFY_MASK); | 1674 | | GDK_VISIBILITY_NOTIFY_MASK); |
| 1674 | 1675 | ||
diff --git a/src/pgtkfns.c b/src/pgtkfns.c index 6a8efb6d0bf..bdc6c5836fa 100644 --- a/src/pgtkfns.c +++ b/src/pgtkfns.c | |||
| @@ -945,6 +945,7 @@ unless TYPE is `png'. */) | |||
| 945 | return pgtk_cr_export_frames (frames, surface_type); | 945 | return pgtk_cr_export_frames (frames, surface_type); |
| 946 | } | 946 | } |
| 947 | 947 | ||
| 948 | extern frame_parm_handler pgtk_frame_parm_handlers[]; | ||
| 948 | frame_parm_handler pgtk_frame_parm_handlers[] = | 949 | frame_parm_handler pgtk_frame_parm_handlers[] = |
| 949 | { | 950 | { |
| 950 | gui_set_autoraise, /* generic OK */ | 951 | gui_set_autoraise, /* generic OK */ |
| @@ -2619,7 +2620,7 @@ static Lisp_Object tip_frame; | |||
| 2619 | 2620 | ||
| 2620 | /* The window-system window corresponding to the frame of the | 2621 | /* The window-system window corresponding to the frame of the |
| 2621 | currently visible tooltip. */ | 2622 | currently visible tooltip. */ |
| 2622 | GtkWidget *tip_window; | 2623 | static GtkWidget *tip_window; |
| 2623 | 2624 | ||
| 2624 | /* A timer that hides or deletes the currently visible tooltip when it | 2625 | /* A timer that hides or deletes the currently visible tooltip when it |
| 2625 | fires. */ | 2626 | fires. */ |
diff --git a/src/pgtkterm.c b/src/pgtkterm.c index 8d9a47b932f..886f115c391 100644 --- a/src/pgtkterm.c +++ b/src/pgtkterm.c | |||
| @@ -450,6 +450,8 @@ pgtk_frame_raise_lower (struct frame *f, bool raise_flag) | |||
| 450 | 450 | ||
| 451 | /* Free X resources of frame F. */ | 451 | /* Free X resources of frame F. */ |
| 452 | 452 | ||
| 453 | static void pgtk_unlink_touch_points (struct frame *); | ||
| 454 | |||
| 453 | void | 455 | void |
| 454 | pgtk_free_frame_resources (struct frame *f) | 456 | pgtk_free_frame_resources (struct frame *f) |
| 455 | { | 457 | { |
| @@ -462,6 +464,7 @@ pgtk_free_frame_resources (struct frame *f) | |||
| 462 | 464 | ||
| 463 | block_input (); | 465 | block_input (); |
| 464 | 466 | ||
| 467 | pgtk_unlink_touch_points (f); | ||
| 465 | #ifdef HAVE_XWIDGETS | 468 | #ifdef HAVE_XWIDGETS |
| 466 | kill_frame_xwidget_views (f); | 469 | kill_frame_xwidget_views (f); |
| 467 | #endif | 470 | #endif |
| @@ -6524,6 +6527,230 @@ drag_drop (GtkWidget *widget, GdkDragContext *context, | |||
| 6524 | return TRUE; | 6527 | return TRUE; |
| 6525 | } | 6528 | } |
| 6526 | 6529 | ||
| 6530 | |||
| 6531 | |||
| 6532 | /* Touch screen events. */ | ||
| 6533 | |||
| 6534 | /* Record a touch sequence with the identifier DETAIL from the given | ||
| 6535 | FRAME on the specified DPYINFO. Round X and Y and record them as its | ||
| 6536 | current position, assign an identifier to the touch sequence suitable | ||
| 6537 | for reporting to Lisp, and return the same. */ | ||
| 6538 | |||
| 6539 | static EMACS_INT | ||
| 6540 | pgtk_link_touch_point (struct pgtk_display_info *dpyinfo, | ||
| 6541 | GdkEventSequence *detail, gdouble x, | ||
| 6542 | gdouble y, struct frame *frame) | ||
| 6543 | { | ||
| 6544 | struct pgtk_touch_point *touchpoint; | ||
| 6545 | static EMACS_INT local_detail; | ||
| 6546 | |||
| 6547 | /* Assign an identifier suitable for reporting to Lisp. On builds | ||
| 6548 | with 64-bit Lisp_Object, this is largely a theoretical problem, but | ||
| 6549 | CARD32s easily overflow 32-bit systems, as they are not specific to | ||
| 6550 | X clients (e.g. Emacs) but grow uniformly across all of them. */ | ||
| 6551 | |||
| 6552 | if (FIXNUM_OVERFLOW_P (local_detail)) | ||
| 6553 | local_detail = 0; | ||
| 6554 | |||
| 6555 | touchpoint = xmalloc (sizeof *touchpoint); | ||
| 6556 | touchpoint->next = dpyinfo->touchpoints; | ||
| 6557 | touchpoint->x = lrint (x); | ||
| 6558 | touchpoint->y = lrint (y); | ||
| 6559 | touchpoint->number = detail; | ||
| 6560 | touchpoint->local_detail = local_detail++; | ||
| 6561 | touchpoint->frame = frame; | ||
| 6562 | dpyinfo->touchpoints = touchpoint; | ||
| 6563 | return touchpoint->local_detail; | ||
| 6564 | } | ||
| 6565 | |||
| 6566 | /* Free and remove the touch sequence with the identifier DETAIL. | ||
| 6567 | DPYINFO is the display in which the touch sequence should be | ||
| 6568 | recorded. If such a touch sequence exists, return its local | ||
| 6569 | identifier in *LOCAL_DETAIL. | ||
| 6570 | |||
| 6571 | Value is 0 if no touch sequence by that identifier exists inside | ||
| 6572 | DPYINFO, or 1 if a touch sequence has been found. */ | ||
| 6573 | |||
| 6574 | static int | ||
| 6575 | pgtk_unlink_touch_point (GdkEventSequence *detail, | ||
| 6576 | struct pgtk_display_info *dpyinfo, | ||
| 6577 | EMACS_INT *local_detail) | ||
| 6578 | { | ||
| 6579 | struct pgtk_touch_point *last, *tem; | ||
| 6580 | |||
| 6581 | for (last = NULL, tem = dpyinfo->touchpoints; tem; | ||
| 6582 | last = tem, tem = tem->next) | ||
| 6583 | { | ||
| 6584 | if (tem->number == detail) | ||
| 6585 | { | ||
| 6586 | if (!last) | ||
| 6587 | dpyinfo->touchpoints = tem->next; | ||
| 6588 | else | ||
| 6589 | last->next = tem->next; | ||
| 6590 | |||
| 6591 | *local_detail = tem->local_detail; | ||
| 6592 | xfree (tem); | ||
| 6593 | |||
| 6594 | return 1; | ||
| 6595 | } | ||
| 6596 | } | ||
| 6597 | |||
| 6598 | return 0; | ||
| 6599 | } | ||
| 6600 | |||
| 6601 | /* Unlink all touch points associated with the frame F. This is done | ||
| 6602 | upon destroying F's window (or its being destroyed), because touch | ||
| 6603 | point delivery after that point is undefined. */ | ||
| 6604 | |||
| 6605 | static void | ||
| 6606 | pgtk_unlink_touch_points (struct frame *f) | ||
| 6607 | { | ||
| 6608 | struct pgtk_touch_point **next, *last; | ||
| 6609 | struct pgtk_display_info *dpyinfo; | ||
| 6610 | |||
| 6611 | /* Now unlink all touch points on F's display matching F. */ | ||
| 6612 | |||
| 6613 | dpyinfo = FRAME_DISPLAY_INFO (f); | ||
| 6614 | for (next = &dpyinfo->touchpoints; (last = *next);) | ||
| 6615 | { | ||
| 6616 | if (last->frame == f) | ||
| 6617 | { | ||
| 6618 | *next = last->next; | ||
| 6619 | xfree (last); | ||
| 6620 | } | ||
| 6621 | else | ||
| 6622 | next = &last->next; | ||
| 6623 | } | ||
| 6624 | } | ||
| 6625 | |||
| 6626 | /* Return the data associated with a touch sequence DETAIL recorded by | ||
| 6627 | `pgtk_link_touch_point' from DPYINFO, or NULL if it can't be | ||
| 6628 | found. */ | ||
| 6629 | |||
| 6630 | static struct pgtk_touch_point * | ||
| 6631 | pgtk_find_touch_point (struct pgtk_display_info *dpyinfo, | ||
| 6632 | GdkEventSequence *detail) | ||
| 6633 | { | ||
| 6634 | struct pgtk_touch_point *point; | ||
| 6635 | |||
| 6636 | for (point = dpyinfo->touchpoints; point; point = point->next) | ||
| 6637 | { | ||
| 6638 | if (point->number == detail) | ||
| 6639 | return point; | ||
| 6640 | } | ||
| 6641 | |||
| 6642 | return NULL; | ||
| 6643 | } | ||
| 6644 | |||
| 6645 | static bool | ||
| 6646 | touch_event_cb (GtkWidget *self, GdkEvent *event, gpointer user_data) | ||
| 6647 | { | ||
| 6648 | struct pgtk_display_info *dpyinfo; | ||
| 6649 | struct frame *f; | ||
| 6650 | EMACS_INT local_detail; | ||
| 6651 | union buffered_input_event inev; | ||
| 6652 | struct pgtk_touch_point *touchpoint; | ||
| 6653 | Lisp_Object arg = Qnil; | ||
| 6654 | int state; | ||
| 6655 | |||
| 6656 | EVENT_INIT (inev.ie); | ||
| 6657 | |||
| 6658 | f = pgtk_any_window_to_frame (gtk_widget_get_window (self)); | ||
| 6659 | eassert (f); | ||
| 6660 | dpyinfo = FRAME_DISPLAY_INFO (f); | ||
| 6661 | switch (event->type) | ||
| 6662 | { | ||
| 6663 | case GDK_TOUCH_BEGIN: | ||
| 6664 | |||
| 6665 | /* Verify that no touch point with this identifier is already at | ||
| 6666 | large. */ | ||
| 6667 | if (pgtk_find_touch_point (dpyinfo, event->touch.sequence)) | ||
| 6668 | break; | ||
| 6669 | |||
| 6670 | /* Record this in the display structure. */ | ||
| 6671 | local_detail = pgtk_link_touch_point (dpyinfo, event->touch.sequence, | ||
| 6672 | event->touch.x, event->touch.y, | ||
| 6673 | f); | ||
| 6674 | /* Generate the input event. */ | ||
| 6675 | inev.ie.kind = TOUCHSCREEN_BEGIN_EVENT; | ||
| 6676 | inev.ie.timestamp = event->touch.time; | ||
| 6677 | XSETFRAME (inev.ie.frame_or_window, f); | ||
| 6678 | XSETINT (inev.ie.x, lrint (event->touch.x)); | ||
| 6679 | XSETINT (inev.ie.y, lrint (event->touch.y)); | ||
| 6680 | XSETINT (inev.ie.arg, local_detail); | ||
| 6681 | break; | ||
| 6682 | |||
| 6683 | case GDK_TOUCH_UPDATE: | ||
| 6684 | touchpoint = pgtk_find_touch_point (dpyinfo, | ||
| 6685 | event->touch.sequence); | ||
| 6686 | |||
| 6687 | if (!touchpoint | ||
| 6688 | /* Don't send this event if nothing has changed | ||
| 6689 | either. */ | ||
| 6690 | || (touchpoint->x == lrint (event->touch.x) | ||
| 6691 | && touchpoint->y == lrint (event->touch.y))) | ||
| 6692 | break; | ||
| 6693 | |||
| 6694 | /* Construct the input event. */ | ||
| 6695 | touchpoint->x = lrint (event->touch.x); | ||
| 6696 | touchpoint->y = lrint (event->touch.y); | ||
| 6697 | inev.ie.kind = TOUCHSCREEN_UPDATE_EVENT; | ||
| 6698 | inev.ie.timestamp = event->touch.time; | ||
| 6699 | XSETFRAME (inev.ie.frame_or_window, f); | ||
| 6700 | |||
| 6701 | for (touchpoint = dpyinfo->touchpoints; | ||
| 6702 | touchpoint; touchpoint = touchpoint->next) | ||
| 6703 | { | ||
| 6704 | if (touchpoint->frame == f) | ||
| 6705 | arg = Fcons (list3i (touchpoint->x, touchpoint->y, | ||
| 6706 | touchpoint->local_detail), | ||
| 6707 | arg); | ||
| 6708 | } | ||
| 6709 | |||
| 6710 | inev.ie.arg = arg; | ||
| 6711 | break; | ||
| 6712 | |||
| 6713 | case GDK_TOUCH_END: | ||
| 6714 | case GDK_TOUCH_CANCEL: | ||
| 6715 | /* Remove this touch point's record, also establishing its | ||
| 6716 | existence. */ | ||
| 6717 | state = pgtk_unlink_touch_point (event->touch.sequence, | ||
| 6718 | dpyinfo, &local_detail); | ||
| 6719 | /* If it did exist... */ | ||
| 6720 | if (state) | ||
| 6721 | { | ||
| 6722 | /* ... generate a suitable event. */ | ||
| 6723 | inev.ie.kind = TOUCHSCREEN_END_EVENT; | ||
| 6724 | inev.ie.timestamp = event->touch.time; | ||
| 6725 | inev.ie.modifiers = (event->type != GDK_TOUCH_END); | ||
| 6726 | |||
| 6727 | XSETFRAME (inev.ie.frame_or_window, f); | ||
| 6728 | XSETINT (inev.ie.x, lrint (event->touch.x)); | ||
| 6729 | XSETINT (inev.ie.y, lrint (event->touch.y)); | ||
| 6730 | XSETINT (inev.ie.arg, local_detail); | ||
| 6731 | } | ||
| 6732 | break; | ||
| 6733 | |||
| 6734 | default: | ||
| 6735 | break; | ||
| 6736 | } | ||
| 6737 | |||
| 6738 | /* If the above produced a workable event, report the name of the | ||
| 6739 | device that gave rise to it. */ | ||
| 6740 | |||
| 6741 | if (inev.ie.kind != NO_EVENT) | ||
| 6742 | { | ||
| 6743 | inev.ie.device = pgtk_get_device_for_event (dpyinfo, event); | ||
| 6744 | evq_enqueue (&inev); | ||
| 6745 | } | ||
| 6746 | |||
| 6747 | return inev.ie.kind != NO_EVENT; | ||
| 6748 | } | ||
| 6749 | |||
| 6750 | |||
| 6751 | |||
| 6752 | /* Callbacks for sundries. */ | ||
| 6753 | |||
| 6527 | static void | 6754 | static void |
| 6528 | pgtk_monitors_changed_cb (GdkScreen *screen, gpointer user_data) | 6755 | pgtk_monitors_changed_cb (GdkScreen *screen, gpointer user_data) |
| 6529 | { | 6756 | { |
| @@ -6540,6 +6767,8 @@ pgtk_monitors_changed_cb (GdkScreen *screen, gpointer user_data) | |||
| 6540 | 6767 | ||
| 6541 | static gboolean pgtk_selection_event (GtkWidget *, GdkEvent *, gpointer); | 6768 | static gboolean pgtk_selection_event (GtkWidget *, GdkEvent *, gpointer); |
| 6542 | 6769 | ||
| 6770 | |||
| 6771 | |||
| 6543 | void | 6772 | void |
| 6544 | pgtk_set_event_handler (struct frame *f) | 6773 | pgtk_set_event_handler (struct frame *f) |
| 6545 | { | 6774 | { |
| @@ -6609,6 +6838,8 @@ pgtk_set_event_handler (struct frame *f) | |||
| 6609 | G_CALLBACK (pgtk_selection_event), NULL); | 6838 | G_CALLBACK (pgtk_selection_event), NULL); |
| 6610 | g_signal_connect (G_OBJECT (FRAME_GTK_WIDGET (f)), "selection-notify-event", | 6839 | g_signal_connect (G_OBJECT (FRAME_GTK_WIDGET (f)), "selection-notify-event", |
| 6611 | G_CALLBACK (pgtk_selection_event), NULL); | 6840 | G_CALLBACK (pgtk_selection_event), NULL); |
| 6841 | g_signal_connect (G_OBJECT (FRAME_GTK_WIDGET (f)), "touch-event", | ||
| 6842 | G_CALLBACK (touch_event_cb), NULL); | ||
| 6612 | g_signal_connect (G_OBJECT (FRAME_GTK_WIDGET (f)), "event", | 6843 | g_signal_connect (G_OBJECT (FRAME_GTK_WIDGET (f)), "event", |
| 6613 | G_CALLBACK (pgtk_handle_event), NULL); | 6844 | G_CALLBACK (pgtk_handle_event), NULL); |
| 6614 | } | 6845 | } |
| @@ -7028,6 +7259,7 @@ static void | |||
| 7028 | pgtk_delete_display (struct pgtk_display_info *dpyinfo) | 7259 | pgtk_delete_display (struct pgtk_display_info *dpyinfo) |
| 7029 | { | 7260 | { |
| 7030 | struct terminal *t; | 7261 | struct terminal *t; |
| 7262 | struct pgtk_touch_point *last, *tem; | ||
| 7031 | 7263 | ||
| 7032 | /* Close all frames and delete the generic struct terminal for this | 7264 | /* Close all frames and delete the generic struct terminal for this |
| 7033 | X display. */ | 7265 | X display. */ |
| @@ -7049,6 +7281,15 @@ pgtk_delete_display (struct pgtk_display_info *dpyinfo) | |||
| 7049 | tail->next = tail->next->next; | 7281 | tail->next = tail->next->next; |
| 7050 | } | 7282 | } |
| 7051 | 7283 | ||
| 7284 | /* Free remaining touchpoints. */ | ||
| 7285 | tem = dpyinfo->touchpoints; | ||
| 7286 | while (tem) | ||
| 7287 | { | ||
| 7288 | last = tem; | ||
| 7289 | tem = tem->next; | ||
| 7290 | xfree (last); | ||
| 7291 | } | ||
| 7292 | |||
| 7052 | pgtk_free_devices (dpyinfo); | 7293 | pgtk_free_devices (dpyinfo); |
| 7053 | xfree (dpyinfo); | 7294 | xfree (dpyinfo); |
| 7054 | } | 7295 | } |
diff --git a/src/pgtkterm.h b/src/pgtkterm.h index 8072d963691..90ca2aa22d4 100644 --- a/src/pgtkterm.h +++ b/src/pgtkterm.h | |||
| @@ -50,13 +50,38 @@ struct pgtk_bitmap_record | |||
| 50 | 50 | ||
| 51 | struct pgtk_device_t | 51 | struct pgtk_device_t |
| 52 | { | 52 | { |
| 53 | /* Lisp name of the device. */ | ||
| 54 | Lisp_Object name; | ||
| 55 | |||
| 56 | /* Seat to which this device appertains. */ | ||
| 53 | GdkSeat *seat; | 57 | GdkSeat *seat; |
| 58 | |||
| 59 | /* Pointer to this device's GdkDevice object. */ | ||
| 54 | GdkDevice *device; | 60 | GdkDevice *device; |
| 55 | 61 | ||
| 56 | Lisp_Object name; | 62 | /* Next device in this chain. */ |
| 57 | struct pgtk_device_t *next; | 63 | struct pgtk_device_t *next; |
| 58 | }; | 64 | }; |
| 59 | 65 | ||
| 66 | struct pgtk_touch_point | ||
| 67 | { | ||
| 68 | /* The detail code reported to Lisp. */ | ||
| 69 | EMACS_INT local_detail; | ||
| 70 | |||
| 71 | /* The frame associated with this touch point. */ | ||
| 72 | struct frame *frame; | ||
| 73 | |||
| 74 | /* The next touch point in this list. */ | ||
| 75 | struct pgtk_touch_point *next; | ||
| 76 | |||
| 77 | /* The touchpoint detail. This purports to be a pointer, but is a | ||
| 78 | number. */ | ||
| 79 | GdkEventSequence *number; | ||
| 80 | |||
| 81 | /* The last known rounded X and Y positions of the touchpoint. */ | ||
| 82 | int x, y; | ||
| 83 | }; | ||
| 84 | |||
| 60 | #define RGB_TO_ULONG(r, g, b) (((r) << 16) | ((g) << 8) | (b)) | 85 | #define RGB_TO_ULONG(r, g, b) (((r) << 16) | ((g) << 8) | (b)) |
| 61 | #define ARGB_TO_ULONG(a, r, g, b) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)) | 86 | #define ARGB_TO_ULONG(a, r, g, b) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)) |
| 62 | 87 | ||
| @@ -131,10 +156,13 @@ struct pgtk_display_info | |||
| 131 | /* This says how to access this display through GDK. */ | 156 | /* This says how to access this display through GDK. */ |
| 132 | GdkDisplay *gdpy; | 157 | GdkDisplay *gdpy; |
| 133 | 158 | ||
| 134 | /* An alias defined to make porting X code easier. */ | 159 | /* An alias defined to facilitate porting X code. */ |
| 135 | GdkDisplay *display; | 160 | GdkDisplay *display; |
| 136 | }; | 161 | }; |
| 137 | 162 | ||
| 163 | /* List of active touch-points. */ | ||
| 164 | struct pgtk_touch_point *touchpoints; | ||
| 165 | |||
| 138 | /* This is a cons cell of the form (NAME . FONT-LIST-CACHE). */ | 166 | /* This is a cons cell of the form (NAME . FONT-LIST-CACHE). */ |
| 139 | Lisp_Object name_list_element; | 167 | Lisp_Object name_list_element; |
| 140 | 168 | ||