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/pgtkterm.c | |
| 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/pgtkterm.c')
| -rw-r--r-- | src/pgtkterm.c | 241 |
1 files changed, 241 insertions, 0 deletions
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 | } |