aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPo Lu2022-07-29 16:20:32 +0800
committerPo Lu2022-07-29 17:27:36 +0800
commitee93a06b8b1922b31e12cfe60566779f185ddeba (patch)
tree31d1e4e2ec3c64ea78c077b91a5546e6322ef274 /src
parentdb03eda6369a9d4af3c72a8ab6ec29e3cc58acc4 (diff)
downloademacs-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.c21
-rw-r--r--src/xmenu.c23
-rw-r--r--src/xterm.c93
-rw-r--r--src/xterm.h25
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
2536x_menu_show (struct frame *f, int x, int y, int menuflags, 2536x_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
6600static Bool
6601x_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
6623static void
6624x_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
6725static void
6726x_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)
6775static void 6834static void
6776show_back_buffer (struct frame *f) 6835show_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