summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPo Lu <luangruo@yahoo.com>2022-07-29 16:20:32 +0800
committerPo Lu <luangruo@yahoo.com>2022-07-29 17:27:36 +0800
commitee93a06b8b1922b31e12cfe60566779f185ddeba (patch)
tree31d1e4e2ec3c64ea78c077b91a5546e6322ef274
parentdb03eda6369a9d4af3c72a8ab6ec29e3cc58acc4 (diff)
downloademacs-ee93a06b8b1922b31e12cfe60566779f185ddeba.tar.gz
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.
-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
gdk_x11_window_set_frame_sync_enabled (window, FALSE);
}
#endif
+
+#if defined HAVE_XSYNC && !defined USE_GTK
+ /* Frame synchronization can't be used in child frames since
+ they are not directly managed by the compositing manager.
+ Re-enabling vsync in former child frames also leads to
+ inconsistent display. In addition, they can only be updated
+ outside of a toplevel frame. */
+ FRAME_X_OUTPUT (f)->use_vsync_p = false;
+ FRAME_X_WAITING_FOR_DRAW (f) = false;
+#endif
unblock_input ();
fset_parent_frame (f, new_value);
@@ -5113,7 +5123,10 @@ This function is an internal primitive--use `make-frame' instead. */)
}
#ifdef HAVE_XSYNC
- if (dpyinfo->xsync_supported_p)
+ if (dpyinfo->xsync_supported_p
+ /* Frame synchronization isn't supported in child frames. */
+ && NILP (parent_frame)
+ && !f->output_data.x->explicit_parent)
{
#ifndef HAVE_GTK3
XSyncValue initial_value;
@@ -5149,6 +5162,12 @@ This function is an internal primitive--use `make-frame' instead. */)
((STRINGP (value)
&& !strcmp (SSDATA (value), "extended")) ? 2 : 1));
#endif
+
+#ifndef USE_GTK
+ if (FRAME_X_EXTENDED_COUNTER (f))
+ FRAME_X_OUTPUT (f)->use_vsync_p
+ = x_wm_supports (f, dpyinfo->Xatom_net_wm_frame_drawn);
+#endif
}
#endif
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
x_menu_show (struct frame *f, int x, int y, int menuflags,
Lisp_Object title, const char **error_name)
{
+#ifdef HAVE_X_WINDOWS
+ Window dummy_window;
+#endif
Window root;
XMenu *menu;
int pane, selidx, lpane, status;
@@ -2584,20 +2587,22 @@ x_menu_show (struct frame *f, int x, int y, int menuflags,
inhibit_garbage_collection ();
#ifdef HAVE_X_WINDOWS
- {
- /* Adjust coordinates to relative to the outer (window manager) window. */
- int left_off, top_off;
+ XTranslateCoordinates (FRAME_X_DISPLAY (f),
- x_real_pos_and_offsets (f, &left_off, NULL, &top_off, NULL,
- NULL, NULL, NULL, NULL, NULL);
+ /* From-window, to-window. */
+ FRAME_X_WINDOW (f),
+ FRAME_DISPLAY_INFO (f)->root_window,
- x += left_off;
- y += top_off;
- }
-#endif /* HAVE_X_WINDOWS */
+ /* From-position, to-position. */
+ x, y, &x, &y,
+ /* Child of win. */
+ &dummy_window);
+#else
+ /* MSDOS without X support. */
x += f->left_pos;
y += f->top_pos;
+#endif
/* Create all the necessary panes and their items. */
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)
***********************************************************************/
#if defined HAVE_XSYNC && !defined USE_GTK
+static Bool
+x_sync_is_frame_drawn_event (Display *dpy, XEvent *event,
+ XPointer user_data)
+{
+ struct frame *f;
+ struct x_display_info *dpyinfo;
+
+ f = (struct frame *) user_data;
+ dpyinfo = FRAME_DISPLAY_INFO (f);
+
+ if (event->type == ClientMessage
+ && (event->xclient.message_type
+ == dpyinfo->Xatom_net_wm_frame_drawn)
+ && event->xclient.window == FRAME_OUTER_WINDOW (f))
+ return True;
+
+ return False;
+}
+
+/* Wait for the compositing manager to finish drawing the last frame.
+ If the compositing manager has already drawn everything, do
+ nothing. */
+
+static void
+x_sync_wait_for_frame_drawn_event (struct frame *f)
+{
+ XEvent event;
+
+ if (!FRAME_X_WAITING_FOR_DRAW (f))
+ return;
+
+ /* Wait for the frame drawn message to arrive. */
+ XIfEvent (FRAME_X_DISPLAY (f), &event,
+ x_sync_is_frame_drawn_event, (XPointer) f);
+ FRAME_X_WAITING_FOR_DRAW (f) = false;
+}
+
/* Tell the compositing manager to postpone updates of F until a frame
has finished drawing. */
@@ -6616,6 +6653,15 @@ x_sync_update_begin (struct frame *f)
if (XSyncValueLow32 (value) % 2)
return;
+ /* Wait for a pending frame draw event if the last frame has not yet
+ been drawn if F isn't double buffered. (In double buffered
+ frames, this happens before buffer flipping). */
+
+#ifdef HAVE_XDBE
+ if (!FRAME_X_DOUBLE_BUFFERED_P (f))
+#endif
+ x_sync_wait_for_frame_drawn_event (f);
+
/* Since Emacs needs a non-urgent redraw, ensure that value % 4 ==
0. */
if (XSyncValueLow32 (value) % 4 == 2)
@@ -6668,7 +6714,20 @@ x_sync_update_finish (struct frame *f)
FRAME_X_EXTENDED_COUNTER (f),
FRAME_X_COUNTER_VALUE (f));
- /* TODO: implement sync fences. */
+ /* FIXME: this leads to freezes if the compositing manager crashes
+ in the meantime. */
+ if (FRAME_OUTPUT_DATA (f)->use_vsync_p)
+ FRAME_X_WAITING_FOR_DRAW (f) = true;
+}
+
+/* Handle a _NET_WM_FRAME_DRAWN message from the compositor. */
+
+static void
+x_sync_handle_frame_drawn (struct x_display_info *dpyinfo,
+ XEvent *message, struct frame *f)
+{
+ if (FRAME_OUTER_WINDOW (f) == message->xclient.window)
+ FRAME_X_WAITING_FOR_DRAW (f) = false;
}
#endif
@@ -6775,16 +6834,26 @@ x_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1)
static void
show_back_buffer (struct frame *f)
{
+ XdbeSwapInfo swap_info;
+#ifdef USE_CAIRO
+ cairo_t *cr;
+#endif
+
block_input ();
if (FRAME_X_DOUBLE_BUFFERED_P (f))
{
+#if defined HAVE_XSYNC && !defined USE_GTK
+ /* Wait for drawing of the previous frame to complete before
+ displaying this new frame. */
+ x_sync_wait_for_frame_drawn_event (f);
+#endif
+
#ifdef USE_CAIRO
- cairo_t *cr = FRAME_CR_CONTEXT (f);
+ cr = FRAME_CR_CONTEXT (f);
if (cr)
cairo_surface_flush (cairo_get_target (cr));
#endif
- XdbeSwapInfo swap_info;
memset (&swap_info, 0, sizeof (swap_info));
swap_info.swap_window = FRAME_X_WINDOW (f);
swap_info.swap_action = XdbeCopied;
@@ -6911,6 +6980,11 @@ XTframe_up_to_date (struct frame *f)
FRAME_X_COUNTER_VALUE (f));
FRAME_X_OUTPUT (f)->ext_sync_end_pending_p = false;
+
+#ifndef USE_GTK
+ if (FRAME_OUTPUT_DATA (f)->use_vsync_p)
+ FRAME_X_WAITING_FOR_DRAW (f) = true;
+#endif
}
#else
if (FRAME_X_OUTPUT (f)->xg_sync_end_pending_p)
@@ -17072,8 +17146,17 @@ handle_one_xevent (struct x_display_info *dpyinfo,
#if defined HAVE_XSYNC && !defined USE_GTK
/* These messages are sent by the compositing manager after a
frame is drawn under extended synchronization. */
- if (event->xclient.message_type == dpyinfo->Xatom_net_wm_frame_drawn
- || event->xclient.message_type == dpyinfo->Xatom_net_wm_frame_timings)
+ if (event->xclient.message_type
+ == dpyinfo->Xatom_net_wm_frame_drawn)
+ {
+ if (any)
+ x_sync_handle_frame_drawn (dpyinfo, (XEvent *) event, any);
+
+ goto done;
+ }
+
+ if (event->xclient.message_type
+ == dpyinfo->Xatom_net_wm_frame_timings)
goto done;
#endif
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
#endif
#ifdef HAVE_XSYNC
+ /* The "basic frame counter" used for resize synchronization. */
XSyncCounter basic_frame_counter;
+
+ /* The "extended frame counter" used for frame synchronization. */
XSyncCounter extended_frame_counter;
+
+ /* The pending value of the basic counter. */
XSyncValue pending_basic_counter_value;
+
+ /* The current value of the extended counter. */
XSyncValue current_extended_counter_value;
+ /* Whether or not basic resize synchronization is in progress. */
bool_bf sync_end_pending_p : 1;
+
+ /* Whether or not extended resize synchronization is in
+ progress. */
bool_bf ext_sync_end_pending_p : 1;
+
#ifdef HAVE_GTK3
+ /* Whether or not GDK resize synchronization is in progress. */
bool_bf xg_sync_end_pending_p : 1;
#endif
+
+ /* Whether or Emacs is waiting for the compositing manager to draw a
+ frame. */
+ bool_bf waiting_for_frame_p : 1;
+
+#ifndef USE_GTK
+ /* Whether or not Emacs should wait for the compositing manager to
+ draw frames before starting a new frame. */
+ bool_bf use_vsync_p : 1;
+#endif
#endif
/* Relief GCs, colors etc. */
@@ -1215,6 +1238,8 @@ extern void x_mark_frame_dirty (struct frame *f);
FRAME_X_OUTPUT (f)->basic_frame_counter
#define FRAME_X_EXTENDED_COUNTER(f) \
FRAME_X_OUTPUT (f)->extended_frame_counter
+#define FRAME_X_WAITING_FOR_DRAW(f) \
+ FRAME_X_OUTPUT (f)->waiting_for_frame_p
#define FRAME_X_COUNTER_VALUE(f) \
FRAME_X_OUTPUT (f)->current_extended_counter_value
#endif