diff options
| author | Po Lu | 2022-07-29 16:20:32 +0800 |
|---|---|---|
| committer | Po Lu | 2022-07-29 17:27:36 +0800 |
| commit | ee93a06b8b1922b31e12cfe60566779f185ddeba (patch) | |
| tree | 31d1e4e2ec3c64ea78c077b91a5546e6322ef274 /src | |
| parent | db03eda6369a9d4af3c72a8ab6ec29e3cc58acc4 (diff) | |
| download | emacs-ee93a06b8b1922b31e12cfe60566779f185ddeba.tar.gz emacs-ee93a06b8b1922b31e12cfe60566779f185ddeba.zip | |
Implement monitor refresh rate synchronization on X
* src/xfns.c (x_set_parent_frame, Fx_create_frame): Disable
vsync on child and embedded frames.
* src/xmenu.c (x_menu_show): Fix XMenu position calculation in
child frames.
* src/xterm.c (x_sync_is_frame_drawn_event)
(x_sync_wait_for_frame_drawn_event): New functions.
(x_sync_update_begin): Wait for frame to be drawn if not double
buffered.
(x_sync_update_finish): Set FRAME_X_WAITING_FOR_DRAW (f).
(show_back_buffer): Wait for frame to be drawn before flipping
buffers.
(XTframe_up_to_date): Set FRAME_X_WAITING_FOR_DRAW if bumped.
(handle_one_xevent): Handle frame drawn events.
* src/xterm.h (struct x_output): New fields for frame dirtyness
and vsync.
Diffstat (limited to 'src')
| -rw-r--r-- | src/xfns.c | 21 | ||||
| -rw-r--r-- | src/xmenu.c | 23 | ||||
| -rw-r--r-- | src/xterm.c | 93 | ||||
| -rw-r--r-- | src/xterm.h | 25 |
4 files changed, 147 insertions, 15 deletions
diff --git a/src/xfns.c b/src/xfns.c index 076cd97875a..579237068a2 100644 --- a/src/xfns.c +++ b/src/xfns.c | |||
| @@ -976,6 +976,16 @@ x_set_parent_frame (struct frame *f, Lisp_Object new_value, Lisp_Object old_valu | |||
| 976 | gdk_x11_window_set_frame_sync_enabled (window, FALSE); | 976 | gdk_x11_window_set_frame_sync_enabled (window, FALSE); |
| 977 | } | 977 | } |
| 978 | #endif | 978 | #endif |
| 979 | |||
| 980 | #if defined HAVE_XSYNC && !defined USE_GTK | ||
| 981 | /* Frame synchronization can't be used in child frames since | ||
| 982 | they are not directly managed by the compositing manager. | ||
| 983 | Re-enabling vsync in former child frames also leads to | ||
| 984 | inconsistent display. In addition, they can only be updated | ||
| 985 | outside of a toplevel frame. */ | ||
| 986 | FRAME_X_OUTPUT (f)->use_vsync_p = false; | ||
| 987 | FRAME_X_WAITING_FOR_DRAW (f) = false; | ||
| 988 | #endif | ||
| 979 | unblock_input (); | 989 | unblock_input (); |
| 980 | 990 | ||
| 981 | fset_parent_frame (f, new_value); | 991 | fset_parent_frame (f, new_value); |
| @@ -5113,7 +5123,10 @@ This function is an internal primitive--use `make-frame' instead. */) | |||
| 5113 | } | 5123 | } |
| 5114 | 5124 | ||
| 5115 | #ifdef HAVE_XSYNC | 5125 | #ifdef HAVE_XSYNC |
| 5116 | if (dpyinfo->xsync_supported_p) | 5126 | if (dpyinfo->xsync_supported_p |
| 5127 | /* Frame synchronization isn't supported in child frames. */ | ||
| 5128 | && NILP (parent_frame) | ||
| 5129 | && !f->output_data.x->explicit_parent) | ||
| 5117 | { | 5130 | { |
| 5118 | #ifndef HAVE_GTK3 | 5131 | #ifndef HAVE_GTK3 |
| 5119 | XSyncValue initial_value; | 5132 | XSyncValue initial_value; |
| @@ -5149,6 +5162,12 @@ This function is an internal primitive--use `make-frame' instead. */) | |||
| 5149 | ((STRINGP (value) | 5162 | ((STRINGP (value) |
| 5150 | && !strcmp (SSDATA (value), "extended")) ? 2 : 1)); | 5163 | && !strcmp (SSDATA (value), "extended")) ? 2 : 1)); |
| 5151 | #endif | 5164 | #endif |
| 5165 | |||
| 5166 | #ifndef USE_GTK | ||
| 5167 | if (FRAME_X_EXTENDED_COUNTER (f)) | ||
| 5168 | FRAME_X_OUTPUT (f)->use_vsync_p | ||
| 5169 | = x_wm_supports (f, dpyinfo->Xatom_net_wm_frame_drawn); | ||
| 5170 | #endif | ||
| 5152 | } | 5171 | } |
| 5153 | #endif | 5172 | #endif |
| 5154 | 5173 | ||
diff --git a/src/xmenu.c b/src/xmenu.c index e5e24b87d16..3be0fb18766 100644 --- a/src/xmenu.c +++ b/src/xmenu.c | |||
| @@ -2536,6 +2536,9 @@ Lisp_Object | |||
| 2536 | x_menu_show (struct frame *f, int x, int y, int menuflags, | 2536 | x_menu_show (struct frame *f, int x, int y, int menuflags, |
| 2537 | Lisp_Object title, const char **error_name) | 2537 | Lisp_Object title, const char **error_name) |
| 2538 | { | 2538 | { |
| 2539 | #ifdef HAVE_X_WINDOWS | ||
| 2540 | Window dummy_window; | ||
| 2541 | #endif | ||
| 2539 | Window root; | 2542 | Window root; |
| 2540 | XMenu *menu; | 2543 | XMenu *menu; |
| 2541 | int pane, selidx, lpane, status; | 2544 | int pane, selidx, lpane, status; |
| @@ -2584,20 +2587,22 @@ x_menu_show (struct frame *f, int x, int y, int menuflags, | |||
| 2584 | inhibit_garbage_collection (); | 2587 | inhibit_garbage_collection (); |
| 2585 | 2588 | ||
| 2586 | #ifdef HAVE_X_WINDOWS | 2589 | #ifdef HAVE_X_WINDOWS |
| 2587 | { | 2590 | XTranslateCoordinates (FRAME_X_DISPLAY (f), |
| 2588 | /* Adjust coordinates to relative to the outer (window manager) window. */ | ||
| 2589 | int left_off, top_off; | ||
| 2590 | 2591 | ||
| 2591 | x_real_pos_and_offsets (f, &left_off, NULL, &top_off, NULL, | 2592 | /* From-window, to-window. */ |
| 2592 | NULL, NULL, NULL, NULL, NULL); | 2593 | FRAME_X_WINDOW (f), |
| 2594 | FRAME_DISPLAY_INFO (f)->root_window, | ||
| 2593 | 2595 | ||
| 2594 | x += left_off; | 2596 | /* From-position, to-position. */ |
| 2595 | y += top_off; | 2597 | x, y, &x, &y, |
| 2596 | } | ||
| 2597 | #endif /* HAVE_X_WINDOWS */ | ||
| 2598 | 2598 | ||
| 2599 | /* Child of win. */ | ||
| 2600 | &dummy_window); | ||
| 2601 | #else | ||
| 2602 | /* MSDOS without X support. */ | ||
| 2599 | x += f->left_pos; | 2603 | x += f->left_pos; |
| 2600 | y += f->top_pos; | 2604 | y += f->top_pos; |
| 2605 | #endif | ||
| 2601 | 2606 | ||
| 2602 | /* Create all the necessary panes and their items. */ | 2607 | /* Create all the necessary panes and their items. */ |
| 2603 | maxwidth = maxlines = lines = i = 0; | 2608 | maxwidth = maxlines = lines = i = 0; |
diff --git a/src/xterm.c b/src/xterm.c index e9db4b364fb..d3ffd432dd2 100644 --- a/src/xterm.c +++ b/src/xterm.c | |||
| @@ -6597,6 +6597,43 @@ x_set_frame_alpha (struct frame *f) | |||
| 6597 | ***********************************************************************/ | 6597 | ***********************************************************************/ |
| 6598 | 6598 | ||
| 6599 | #if defined HAVE_XSYNC && !defined USE_GTK | 6599 | #if defined HAVE_XSYNC && !defined USE_GTK |
| 6600 | static Bool | ||
| 6601 | x_sync_is_frame_drawn_event (Display *dpy, XEvent *event, | ||
| 6602 | XPointer user_data) | ||
| 6603 | { | ||
| 6604 | struct frame *f; | ||
| 6605 | struct x_display_info *dpyinfo; | ||
| 6606 | |||
| 6607 | f = (struct frame *) user_data; | ||
| 6608 | dpyinfo = FRAME_DISPLAY_INFO (f); | ||
| 6609 | |||
| 6610 | if (event->type == ClientMessage | ||
| 6611 | && (event->xclient.message_type | ||
| 6612 | == dpyinfo->Xatom_net_wm_frame_drawn) | ||
| 6613 | && event->xclient.window == FRAME_OUTER_WINDOW (f)) | ||
| 6614 | return True; | ||
| 6615 | |||
| 6616 | return False; | ||
| 6617 | } | ||
| 6618 | |||
| 6619 | /* Wait for the compositing manager to finish drawing the last frame. | ||
| 6620 | If the compositing manager has already drawn everything, do | ||
| 6621 | nothing. */ | ||
| 6622 | |||
| 6623 | static void | ||
| 6624 | x_sync_wait_for_frame_drawn_event (struct frame *f) | ||
| 6625 | { | ||
| 6626 | XEvent event; | ||
| 6627 | |||
| 6628 | if (!FRAME_X_WAITING_FOR_DRAW (f)) | ||
| 6629 | return; | ||
| 6630 | |||
| 6631 | /* Wait for the frame drawn message to arrive. */ | ||
| 6632 | XIfEvent (FRAME_X_DISPLAY (f), &event, | ||
| 6633 | x_sync_is_frame_drawn_event, (XPointer) f); | ||
| 6634 | FRAME_X_WAITING_FOR_DRAW (f) = false; | ||
| 6635 | } | ||
| 6636 | |||
| 6600 | /* Tell the compositing manager to postpone updates of F until a frame | 6637 | /* Tell the compositing manager to postpone updates of F until a frame |
| 6601 | has finished drawing. */ | 6638 | has finished drawing. */ |
| 6602 | 6639 | ||
| @@ -6616,6 +6653,15 @@ x_sync_update_begin (struct frame *f) | |||
| 6616 | if (XSyncValueLow32 (value) % 2) | 6653 | if (XSyncValueLow32 (value) % 2) |
| 6617 | return; | 6654 | return; |
| 6618 | 6655 | ||
| 6656 | /* Wait for a pending frame draw event if the last frame has not yet | ||
| 6657 | been drawn if F isn't double buffered. (In double buffered | ||
| 6658 | frames, this happens before buffer flipping). */ | ||
| 6659 | |||
| 6660 | #ifdef HAVE_XDBE | ||
| 6661 | if (!FRAME_X_DOUBLE_BUFFERED_P (f)) | ||
| 6662 | #endif | ||
| 6663 | x_sync_wait_for_frame_drawn_event (f); | ||
| 6664 | |||
| 6619 | /* Since Emacs needs a non-urgent redraw, ensure that value % 4 == | 6665 | /* Since Emacs needs a non-urgent redraw, ensure that value % 4 == |
| 6620 | 0. */ | 6666 | 0. */ |
| 6621 | if (XSyncValueLow32 (value) % 4 == 2) | 6667 | if (XSyncValueLow32 (value) % 4 == 2) |
| @@ -6668,7 +6714,20 @@ x_sync_update_finish (struct frame *f) | |||
| 6668 | FRAME_X_EXTENDED_COUNTER (f), | 6714 | FRAME_X_EXTENDED_COUNTER (f), |
| 6669 | FRAME_X_COUNTER_VALUE (f)); | 6715 | FRAME_X_COUNTER_VALUE (f)); |
| 6670 | 6716 | ||
| 6671 | /* TODO: implement sync fences. */ | 6717 | /* FIXME: this leads to freezes if the compositing manager crashes |
| 6718 | in the meantime. */ | ||
| 6719 | if (FRAME_OUTPUT_DATA (f)->use_vsync_p) | ||
| 6720 | FRAME_X_WAITING_FOR_DRAW (f) = true; | ||
| 6721 | } | ||
| 6722 | |||
| 6723 | /* Handle a _NET_WM_FRAME_DRAWN message from the compositor. */ | ||
| 6724 | |||
| 6725 | static void | ||
| 6726 | x_sync_handle_frame_drawn (struct x_display_info *dpyinfo, | ||
| 6727 | XEvent *message, struct frame *f) | ||
| 6728 | { | ||
| 6729 | if (FRAME_OUTER_WINDOW (f) == message->xclient.window) | ||
| 6730 | FRAME_X_WAITING_FOR_DRAW (f) = false; | ||
| 6672 | } | 6731 | } |
| 6673 | #endif | 6732 | #endif |
| 6674 | 6733 | ||
| @@ -6775,16 +6834,26 @@ x_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1) | |||
| 6775 | static void | 6834 | static void |
| 6776 | show_back_buffer (struct frame *f) | 6835 | show_back_buffer (struct frame *f) |
| 6777 | { | 6836 | { |
| 6837 | XdbeSwapInfo swap_info; | ||
| 6838 | #ifdef USE_CAIRO | ||
| 6839 | cairo_t *cr; | ||
| 6840 | #endif | ||
| 6841 | |||
| 6778 | block_input (); | 6842 | block_input (); |
| 6779 | 6843 | ||
| 6780 | if (FRAME_X_DOUBLE_BUFFERED_P (f)) | 6844 | if (FRAME_X_DOUBLE_BUFFERED_P (f)) |
| 6781 | { | 6845 | { |
| 6846 | #if defined HAVE_XSYNC && !defined USE_GTK | ||
| 6847 | /* Wait for drawing of the previous frame to complete before | ||
| 6848 | displaying this new frame. */ | ||
| 6849 | x_sync_wait_for_frame_drawn_event (f); | ||
| 6850 | #endif | ||
| 6851 | |||
| 6782 | #ifdef USE_CAIRO | 6852 | #ifdef USE_CAIRO |
| 6783 | cairo_t *cr = FRAME_CR_CONTEXT (f); | 6853 | cr = FRAME_CR_CONTEXT (f); |
| 6784 | if (cr) | 6854 | if (cr) |
| 6785 | cairo_surface_flush (cairo_get_target (cr)); | 6855 | cairo_surface_flush (cairo_get_target (cr)); |
| 6786 | #endif | 6856 | #endif |
| 6787 | XdbeSwapInfo swap_info; | ||
| 6788 | memset (&swap_info, 0, sizeof (swap_info)); | 6857 | memset (&swap_info, 0, sizeof (swap_info)); |
| 6789 | swap_info.swap_window = FRAME_X_WINDOW (f); | 6858 | swap_info.swap_window = FRAME_X_WINDOW (f); |
| 6790 | swap_info.swap_action = XdbeCopied; | 6859 | swap_info.swap_action = XdbeCopied; |
| @@ -6911,6 +6980,11 @@ XTframe_up_to_date (struct frame *f) | |||
| 6911 | FRAME_X_COUNTER_VALUE (f)); | 6980 | FRAME_X_COUNTER_VALUE (f)); |
| 6912 | 6981 | ||
| 6913 | FRAME_X_OUTPUT (f)->ext_sync_end_pending_p = false; | 6982 | FRAME_X_OUTPUT (f)->ext_sync_end_pending_p = false; |
| 6983 | |||
| 6984 | #ifndef USE_GTK | ||
| 6985 | if (FRAME_OUTPUT_DATA (f)->use_vsync_p) | ||
| 6986 | FRAME_X_WAITING_FOR_DRAW (f) = true; | ||
| 6987 | #endif | ||
| 6914 | } | 6988 | } |
| 6915 | #else | 6989 | #else |
| 6916 | if (FRAME_X_OUTPUT (f)->xg_sync_end_pending_p) | 6990 | if (FRAME_X_OUTPUT (f)->xg_sync_end_pending_p) |
| @@ -17072,8 +17146,17 @@ handle_one_xevent (struct x_display_info *dpyinfo, | |||
| 17072 | #if defined HAVE_XSYNC && !defined USE_GTK | 17146 | #if defined HAVE_XSYNC && !defined USE_GTK |
| 17073 | /* These messages are sent by the compositing manager after a | 17147 | /* These messages are sent by the compositing manager after a |
| 17074 | frame is drawn under extended synchronization. */ | 17148 | frame is drawn under extended synchronization. */ |
| 17075 | if (event->xclient.message_type == dpyinfo->Xatom_net_wm_frame_drawn | 17149 | if (event->xclient.message_type |
| 17076 | || event->xclient.message_type == dpyinfo->Xatom_net_wm_frame_timings) | 17150 | == dpyinfo->Xatom_net_wm_frame_drawn) |
| 17151 | { | ||
| 17152 | if (any) | ||
| 17153 | x_sync_handle_frame_drawn (dpyinfo, (XEvent *) event, any); | ||
| 17154 | |||
| 17155 | goto done; | ||
| 17156 | } | ||
| 17157 | |||
| 17158 | if (event->xclient.message_type | ||
| 17159 | == dpyinfo->Xatom_net_wm_frame_timings) | ||
| 17077 | goto done; | 17160 | goto done; |
| 17078 | #endif | 17161 | #endif |
| 17079 | 17162 | ||
diff --git a/src/xterm.h b/src/xterm.h index 3e237158e7e..1163dd5cd1e 100644 --- a/src/xterm.h +++ b/src/xterm.h | |||
| @@ -1026,16 +1026,39 @@ struct x_output | |||
| 1026 | #endif | 1026 | #endif |
| 1027 | 1027 | ||
| 1028 | #ifdef HAVE_XSYNC | 1028 | #ifdef HAVE_XSYNC |
| 1029 | /* The "basic frame counter" used for resize synchronization. */ | ||
| 1029 | XSyncCounter basic_frame_counter; | 1030 | XSyncCounter basic_frame_counter; |
| 1031 | |||
| 1032 | /* The "extended frame counter" used for frame synchronization. */ | ||
| 1030 | XSyncCounter extended_frame_counter; | 1033 | XSyncCounter extended_frame_counter; |
| 1034 | |||
| 1035 | /* The pending value of the basic counter. */ | ||
| 1031 | XSyncValue pending_basic_counter_value; | 1036 | XSyncValue pending_basic_counter_value; |
| 1037 | |||
| 1038 | /* The current value of the extended counter. */ | ||
| 1032 | XSyncValue current_extended_counter_value; | 1039 | XSyncValue current_extended_counter_value; |
| 1033 | 1040 | ||
| 1041 | /* Whether or not basic resize synchronization is in progress. */ | ||
| 1034 | bool_bf sync_end_pending_p : 1; | 1042 | bool_bf sync_end_pending_p : 1; |
| 1043 | |||
| 1044 | /* Whether or not extended resize synchronization is in | ||
| 1045 | progress. */ | ||
| 1035 | bool_bf ext_sync_end_pending_p : 1; | 1046 | bool_bf ext_sync_end_pending_p : 1; |
| 1047 | |||
| 1036 | #ifdef HAVE_GTK3 | 1048 | #ifdef HAVE_GTK3 |
| 1049 | /* Whether or not GDK resize synchronization is in progress. */ | ||
| 1037 | bool_bf xg_sync_end_pending_p : 1; | 1050 | bool_bf xg_sync_end_pending_p : 1; |
| 1038 | #endif | 1051 | #endif |
| 1052 | |||
| 1053 | /* Whether or Emacs is waiting for the compositing manager to draw a | ||
| 1054 | frame. */ | ||
| 1055 | bool_bf waiting_for_frame_p : 1; | ||
| 1056 | |||
| 1057 | #ifndef USE_GTK | ||
| 1058 | /* Whether or not Emacs should wait for the compositing manager to | ||
| 1059 | draw frames before starting a new frame. */ | ||
| 1060 | bool_bf use_vsync_p : 1; | ||
| 1061 | #endif | ||
| 1039 | #endif | 1062 | #endif |
| 1040 | 1063 | ||
| 1041 | /* Relief GCs, colors etc. */ | 1064 | /* Relief GCs, colors etc. */ |
| @@ -1215,6 +1238,8 @@ extern void x_mark_frame_dirty (struct frame *f); | |||
| 1215 | FRAME_X_OUTPUT (f)->basic_frame_counter | 1238 | FRAME_X_OUTPUT (f)->basic_frame_counter |
| 1216 | #define FRAME_X_EXTENDED_COUNTER(f) \ | 1239 | #define FRAME_X_EXTENDED_COUNTER(f) \ |
| 1217 | FRAME_X_OUTPUT (f)->extended_frame_counter | 1240 | FRAME_X_OUTPUT (f)->extended_frame_counter |
| 1241 | #define FRAME_X_WAITING_FOR_DRAW(f) \ | ||
| 1242 | FRAME_X_OUTPUT (f)->waiting_for_frame_p | ||
| 1218 | #define FRAME_X_COUNTER_VALUE(f) \ | 1243 | #define FRAME_X_COUNTER_VALUE(f) \ |
| 1219 | FRAME_X_OUTPUT (f)->current_extended_counter_value | 1244 | FRAME_X_OUTPUT (f)->current_extended_counter_value |
| 1220 | #endif | 1245 | #endif |