[weston 1/8] xwm: Implement _NET_WM_SYNC_REQUEST protocol

Louis-Francis Ratté-Boulianne lfrb at collabora.com
Tue Apr 11 20:52:57 UTC 2017


Implement NET_WM_SYNC_REQUEST protocol for throtting X11 window resizes
in Weston's XWM. We wait for the window to be drawn (by setting an
alarm on the sync counter) before configuring the window again.

Signed-off-by: Louis-Francis Ratté-Boulianne <lfrb at collabora.com>
---
 configure.ac              |   2 +-
 xwayland/window-manager.c | 220 +++++++++++++++++++++++++++++++++++++++++++---
 xwayland/xwayland.h       |   5 ++
 3 files changed, 214 insertions(+), 13 deletions(-)

diff --git a/configure.ac b/configure.ac
index 6cc9f26e..eebeba9d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -165,7 +165,7 @@ AC_ARG_ENABLE(xwayland-test, [  --enable-xwayland-test],,
 AM_CONDITIONAL(ENABLE_XWAYLAND, test x$enable_xwayland = xyes)
 AM_CONDITIONAL(ENABLE_XWAYLAND_TEST, test x$enable_xwayland = xyes -a x$enable_xwayland_test = xyes)
 if test x$enable_xwayland = xyes; then
-  PKG_CHECK_MODULES([XWAYLAND], xcb xcb-xfixes xcb-composite xcursor cairo-xcb)
+  PKG_CHECK_MODULES([XWAYLAND], xcb xcb-xfixes xcb-composite xcb-sync xcursor cairo-xcb)
   AC_DEFINE([BUILD_XWAYLAND], [1], [Build the X server launcher])
 
   AC_ARG_WITH(xserver-path, AS_HELP_STRING([--with-xserver-path=PATH],
diff --git a/xwayland/window-manager.c b/xwayland/window-manager.c
index 26080759..a067f741 100644
--- a/xwayland/window-manager.c
+++ b/xwayland/window-manager.c
@@ -43,6 +43,7 @@
 #include "compositor.h"
 #include "xwayland.h"
 #include "xwayland-internal-interface.h"
+#include "wayland-server-core.h"
 
 #include "cairo-util.h"
 #include "hash.h"
@@ -136,6 +137,8 @@ struct weston_wm_window {
 	struct weston_wm *wm;
 	xcb_window_t id;
 	xcb_window_t frame_id;
+	xcb_sync_alarm_t sync_request_alarm;
+	xcb_sync_counter_t sync_request_counter;
 	struct frame *frame;
 	cairo_surface_t *cairo_surface;
 	uint32_t surface_id;
@@ -144,6 +147,7 @@ struct weston_wm_window {
 	struct wl_listener surface_destroy_listener;
 	struct wl_event_source *repaint_source;
 	struct wl_event_source *configure_source;
+	struct wl_event_source *sync_request_timer;
 	int properties_dirty;
 	int pid;
 	char *machine;
@@ -167,6 +171,10 @@ struct weston_wm_window {
 	int delete_window;
 	int maximized_vert;
 	int maximized_horz;
+	int64_t sync_request_serial;
+	int configure_pending;
+	int sync_disabled;
+	int wait_redraw;
 	struct wm_size_hints size_hints;
 	struct motif_wm_hints motif_hints;
 	struct wl_list link;
@@ -179,6 +187,9 @@ static void
 weston_wm_set_net_active_window(struct weston_wm *wm, xcb_window_t window);
 
 static void
+weston_wm_window_configure(void *data);
+
+static void
 weston_wm_window_schedule_repaint(struct weston_wm_window *window);
 
 static int
@@ -472,6 +483,7 @@ weston_wm_window_read_properties(struct weston_wm_window *window)
 		{ wm->atom.net_wm_window_type, XCB_ATOM_ATOM,              F(type) },
 		{ wm->atom.net_wm_name,        XCB_ATOM_STRING,            F(name) },
 		{ wm->atom.net_wm_pid,         XCB_ATOM_CARDINAL,          F(pid) },
+		{ wm->atom.net_wm_sync_request_counter,XCB_SYNC_COUNTER,   F(sync_request_counter) },
 		{ wm->atom.motif_wm_hints,     TYPE_MOTIF_WM_HINTS,        NULL },
 		{ wm->atom.wm_client_machine,  XCB_ATOM_WM_CLIENT_MACHINE, F(machine) },
 	};
@@ -482,6 +494,7 @@ weston_wm_window_read_properties(struct weston_wm_window *window)
 	void *p;
 	uint32_t *xid;
 	xcb_atom_t *atom;
+	xcb_sync_counter_t *counter;
 	uint32_t i;
 	char name[1024];
 
@@ -537,6 +550,10 @@ weston_wm_window_read_properties(struct weston_wm_window *window)
 			atom = xcb_get_property_value(reply);
 			*(xcb_atom_t *) p = *atom;
 			break;
+		case XCB_SYNC_COUNTER:
+			counter = xcb_get_property_value(reply);
+			*(xcb_sync_counter_t *) p = *counter;
+			break;
 		case TYPE_WM_PROTOCOLS:
 			atom = xcb_get_property_value(reply);
 			for (i = 0; i < reply->value_len; i++)
@@ -674,11 +691,6 @@ weston_wm_handle_configure_request(struct weston_wm *wm, xcb_generic_event_t *ev
 	uint32_t mask, values[16];
 	int x, y, width, height, i = 0;
 
-	wm_log("XCB_CONFIGURE_REQUEST (window %d) %d,%d @ %dx%d\n",
-	       configure_request->window,
-	       configure_request->x, configure_request->y,
-	       configure_request->width, configure_request->height);
-
 	if (!wm_lookup_window(wm, configure_request->window, &window))
 		return;
 
@@ -1036,6 +1048,91 @@ weston_wm_window_set_virtual_desktop(struct weston_wm_window *window,
 }
 
 static void
+weston_wm_window_create_sync_alarm(struct weston_wm_window *window)
+{
+	struct weston_wm *wm = window->wm;
+	xcb_sync_int64_t value;
+	uint32_t mask;
+	xcb_sync_create_alarm_value_list_t value_list;
+
+	if (window->sync_request_counter == 0) {
+		weston_log("NET_WM_SYNC_REQUEST isn't supported by the client\n");
+		return;
+	}
+
+	if (window->sync_request_alarm != 0) {
+		weston_log("Sync request alarm has already been created\n");
+		return;
+	}
+
+	value.hi = 0;
+	value.lo = 0;
+	window->sync_request_serial = 0;
+	xcb_sync_set_counter(wm->conn, window->sync_request_counter, value);
+
+	mask = (XCB_SYNC_CA_COUNTER | XCB_SYNC_CA_VALUE_TYPE |
+		XCB_SYNC_CA_VALUE | XCB_SYNC_CA_TEST_TYPE |
+		XCB_SYNC_CA_DELTA | XCB_SYNC_CA_EVENTS);
+	value_list.counter = window->sync_request_counter;
+	value_list.valueType = XCB_SYNC_VALUETYPE_RELATIVE;
+	value_list.value.hi = 0;
+	value_list.value.lo = 1;
+	value_list.testType = XCB_SYNC_TESTTYPE_POSITIVE_COMPARISON;
+	value_list.delta.hi = 0;
+	value_list.delta.lo = 1;
+	value_list.events = 1;
+
+	window->sync_request_alarm = xcb_generate_id(wm->conn);
+	xcb_sync_create_alarm_aux(wm->conn, window->sync_request_alarm, mask, &value_list);
+
+	hash_table_insert(wm->alarm_hash, window->sync_request_alarm, window);
+}
+
+static int
+sync_request_timeout(void *data)
+{
+	struct weston_wm_window *window = data;
+	struct weston_wm *wm = window->wm;
+
+	wl_event_source_timer_update(window->sync_request_timer, 0);
+
+	weston_log("Sync request timed out. Temporarily disabling syncing.\n");
+	window->sync_disabled = 1;
+	window->wait_redraw = 0;
+
+	if (window->configure_pending && !window->configure_source) {
+		window->configure_source =
+			wl_event_loop_add_idle(wm->server->loop,
+					       weston_wm_window_configure, window);
+	}
+
+	return 0;
+}
+
+static void
+weston_wm_window_send_sync_request(struct weston_wm_window *window)
+{
+	xcb_client_message_event_t client_message;
+	struct weston_wm *wm = window->wm;
+
+	window->sync_request_serial++;
+
+	client_message.response_type = XCB_CLIENT_MESSAGE;
+	client_message.format = 32;
+	client_message.window = window->id;
+	client_message.type = wm->atom.wm_protocols;
+	client_message.data.data32[0] = wm->atom.net_wm_sync_request;
+	client_message.data.data32[1] = XCB_TIME_CURRENT_TIME;
+	client_message.data.data32[2] = window->sync_request_serial & 0xffffffff;
+	client_message.data.data32[3] = (window->sync_request_serial >> 32) & 0xffffffff;
+
+	xcb_send_event(wm->conn, 0, window->id, 0,
+		       (char *) &client_message);
+
+	wl_event_source_timer_update(window->sync_request_timer, 1000);
+}
+
+static void
 weston_wm_handle_map_request(struct weston_wm *wm, xcb_generic_event_t *event)
 {
 	xcb_map_request_event_t *map_request =
@@ -1093,6 +1190,16 @@ weston_wm_handle_map_request(struct weston_wm *wm, xcb_generic_event_t *event)
 	xcb_map_window(wm->conn, map_request->window);
 	xcb_map_window(wm->conn, window->frame_id);
 
+	if (window->sync_request_counter != 0) {
+		window->sync_request_timer =
+			wl_event_loop_add_timer(wm->server->loop,
+						sync_request_timeout,
+						window);
+		weston_wm_window_create_sync_alarm(window);
+		weston_wm_window_send_sync_request(window);
+		weston_wm_window_send_configure_notify(window);
+	}
+
 	/* Mapped in the X server, we can draw immediately.
 	 * Cannot set pending state though, no weston_surface until
 	 * xserver_map_shell_surface() time. */
@@ -1145,6 +1252,12 @@ weston_wm_handle_unmap_notify(struct weston_wm *wm, xcb_generic_event_t *event)
 		wl_list_remove(&window->link);
 		window->surface_id = 0;
 	}
+	if (window->sync_request_alarm) {
+		xcb_sync_destroy_alarm(wm->conn, window->sync_request_alarm);
+		hash_table_remove(wm->alarm_hash, window->sync_request_alarm);
+	}
+	if (window->sync_request_timer)
+		wl_event_source_remove(window->sync_request_timer);
 	if (wm->focus_window == window)
 		wm->focus_window = NULL;
 	if (window->surface)
@@ -1359,6 +1472,11 @@ weston_wm_window_create(struct weston_wm *wm,
 	window->pos_dirty = false;
 	window->map_request_x = INT_MIN; /* out of range for valid positions */
 	window->map_request_y = INT_MIN; /* out of range for valid positions */
+	window->sync_request_timer = 0;
+	window->sync_request_alarm = 0;
+	window->configure_pending = 0;
+	window->sync_disabled = 0;
+	window->wait_redraw = 0;
 	weston_output_weak_ref_init(&window->legacy_fullscreen_output);
 
 	geometry_reply = xcb_get_geometry_reply(wm->conn, geometry_cookie, NULL);
@@ -1579,9 +1697,6 @@ update_state(int action, int *state)
 }
 
 static void
-weston_wm_window_configure(void *data);
-
-static void
 weston_wm_window_set_toplevel(struct weston_wm_window *window)
 {
 	const struct weston_desktop_xwayland_interface *xwayland_interface =
@@ -1730,6 +1845,38 @@ weston_wm_handle_client_message(struct weston_wm *wm,
 		weston_wm_window_handle_surface_id(window, client_message);
 }
 
+static void
+weston_wm_handle_sync_alarm_notify(struct weston_wm *wm,
+				   xcb_generic_event_t *event)
+{
+	xcb_sync_alarm_notify_event_t *alarm_event =
+		(xcb_sync_alarm_notify_event_t *) event;
+	struct weston_wm_window *window;
+	int64_t counter_value = 0;
+
+	counter_value = alarm_event->counter_value.lo;
+	counter_value += (int64_t) alarm_event->counter_value.hi << 32;
+
+	weston_log("XCB_SYNC_ALARM_NOTIFY (alarm %d counter_value %li)\n",
+	           alarm_event->alarm, counter_value);
+
+	window = hash_table_lookup(wm->alarm_hash, alarm_event->alarm);
+	if (!window) {
+		weston_log("Alarm doesn't match a window\n");
+		return;
+	}
+
+	wl_event_source_timer_update(window->sync_request_timer, 0);
+	window->sync_disabled = 0;
+	window->wait_redraw = 0;
+
+	if (window->configure_pending && !window->configure_source) {
+		window->configure_source =
+			wl_event_loop_add_idle(wm->server->loop,
+					       weston_wm_window_configure, window);
+	}
+}
+
 enum cursor_type {
 	XWM_CURSOR_TOP,
 	XWM_CURSOR_BOTTOM,
@@ -2133,6 +2280,12 @@ weston_wm_handle_event(int fd, uint32_t mask, void *data)
 			break;
 		}
 
+		if (wm->sync) {
+			uint8_t base = wm->sync->first_event;
+			if (EVENT_TYPE(event) == base + XCB_SYNC_ALARM_NOTIFY)
+				weston_wm_handle_sync_alarm_notify(wm, event);
+		}
+
 		free(event);
 		count++;
 	}
@@ -2222,6 +2375,9 @@ weston_wm_get_resources(struct weston_wm *wm)
 		{ "_NET_WM_WINDOW_TYPE_DND", F(atom.net_wm_window_type_dnd) },
 		{ "_NET_WM_WINDOW_TYPE_NORMAL",	F(atom.net_wm_window_type_normal) },
 
+		{ "_NET_WM_SYNC_REQUEST", F(atom.net_wm_sync_request) },
+		{ "_NET_WM_SYNC_REQUEST_COUNTER", F(atom.net_wm_sync_request_counter) },
+
 		{ "_NET_WM_MOVERESIZE", F(atom.net_wm_moveresize) },
 		{ "_NET_SUPPORTING_WM_CHECK",
 					F(atom.net_supporting_wm_check) },
@@ -2259,6 +2415,8 @@ weston_wm_get_resources(struct weston_wm *wm)
 
 	xcb_xfixes_query_version_cookie_t xfixes_cookie;
 	xcb_xfixes_query_version_reply_t *xfixes_reply;
+	xcb_sync_initialize_cookie_t sync_cookie;
+	xcb_sync_initialize_reply_t *sync_reply;
 	xcb_intern_atom_cookie_t cookies[ARRAY_LENGTH(atoms)];
 	xcb_intern_atom_reply_t *reply;
 	xcb_render_query_pict_formats_reply_t *formats_reply;
@@ -2266,6 +2424,7 @@ weston_wm_get_resources(struct weston_wm *wm)
 	xcb_render_pictforminfo_t *formats;
 	uint32_t i;
 
+	xcb_prefetch_extension_data (wm->conn, &xcb_sync_id);
 	xcb_prefetch_extension_data (wm->conn, &xcb_xfixes_id);
 	xcb_prefetch_extension_data (wm->conn, &xcb_composite_id);
 
@@ -2282,6 +2441,22 @@ weston_wm_get_resources(struct weston_wm *wm)
 		free(reply);
 	}
 
+	wm->sync = xcb_get_extension_data(wm->conn, &xcb_sync_id);
+	if (!wm->sync || !wm->sync->present) {
+		weston_log("sync not available\n");
+	} else {
+		sync_cookie = xcb_sync_initialize(wm->conn,
+						  XCB_SYNC_MAJOR_VERSION,
+						  XCB_SYNC_MINOR_VERSION);
+		sync_reply = xcb_sync_initialize_reply(wm->conn,
+						       sync_cookie, NULL);
+
+		weston_log("sync version: %d.%d\n",
+		       sync_reply->major_version, sync_reply->minor_version);
+
+		free(sync_reply);
+	}
+
 	wm->xfixes = xcb_get_extension_data(wm->conn, &xcb_xfixes_id);
 	if (!wm->xfixes || !wm->xfixes->present)
 		weston_log("xfixes not available\n");
@@ -2381,7 +2556,7 @@ weston_wm_create(struct weston_xserver *wxs, int fd)
 	struct wl_event_loop *loop;
 	xcb_screen_iterator_t s;
 	uint32_t values[1];
-	xcb_atom_t supported[6];
+	xcb_atom_t supported[8];
 
 	wm = zalloc(sizeof *wm);
 	if (wm == NULL)
@@ -2394,11 +2569,19 @@ weston_wm_create(struct weston_xserver *wxs, int fd)
 		return NULL;
 	}
 
+	wm->alarm_hash = hash_table_create();
+	if (wm->alarm_hash == NULL) {
+		hash_table_destroy(wm->window_hash);
+		free(wm);
+		return NULL;
+	}
+
 	/* xcb_connect_to_fd takes ownership of the fd. */
 	wm->conn = xcb_connect_to_fd(fd, NULL);
 	if (xcb_connection_has_error(wm->conn)) {
 		weston_log("xcb_connect_to_fd failed\n");
 		close(fd);
+		hash_table_destroy(wm->alarm_hash);
 		hash_table_destroy(wm->window_hash);
 		free(wm);
 		return NULL;
@@ -2435,6 +2618,8 @@ weston_wm_create(struct weston_xserver *wxs, int fd)
 	supported[3] = wm->atom.net_wm_state_maximized_vert;
 	supported[4] = wm->atom.net_wm_state_maximized_horz;
 	supported[5] = wm->atom.net_active_window;
+	supported[6] = wm->atom.net_wm_sync_request;
+	supported[7] = wm->atom.net_wm_sync_request_counter;
 	xcb_change_property(wm->conn,
 			    XCB_PROP_MODE_REPLACE,
 			    wm->screen->root,
@@ -2477,6 +2662,7 @@ weston_wm_create(struct weston_xserver *wxs, int fd)
 void
 weston_wm_destroy(struct weston_wm *wm)
 {
+	hash_table_destroy(wm->alarm_hash);
 	/* FIXME: Free windows in hash. */
 	hash_table_destroy(wm->window_hash);
 	weston_wm_destroy_cursors(wm);
@@ -2517,6 +2703,11 @@ weston_wm_window_configure(void *data)
 	uint32_t values[4];
 	int x, y, width, height;
 
+	if (window->sync_request_counter != 0 && !window->sync_disabled) {
+		window->wait_redraw = 1;
+		weston_wm_window_send_sync_request(window);
+	}
+
 	weston_wm_window_get_child_position(window, &x, &y);
 	values[0] = x;
 	values[1] = y;
@@ -2539,6 +2730,7 @@ weston_wm_window_configure(void *data)
 			     XCB_CONFIG_WINDOW_HEIGHT,
 			     values);
 
+	window->configure_pending = 0;
 	window->configure_source = NULL;
 
 	weston_wm_window_schedule_repaint(window);
@@ -2576,9 +2768,13 @@ send_configure(struct weston_surface *surface, int32_t width, int32_t height)
 	if (window->configure_source)
 		return;
 
-	window->configure_source =
-		wl_event_loop_add_idle(wm->server->loop,
-				       weston_wm_window_configure, window);
+	if (window->wait_redraw) {
+		window->configure_pending = 1;
+	} else {
+		window->configure_source =
+			wl_event_loop_add_idle(wm->server->loop,
+					       weston_wm_window_configure, window);
+	}
 }
 
 static void
diff --git a/xwayland/xwayland.h b/xwayland/xwayland.h
index ca75f5b7..fcc87785 100644
--- a/xwayland/xwayland.h
+++ b/xwayland/xwayland.h
@@ -27,6 +27,7 @@
 #include <xcb/xcb.h>
 #include <xcb/xfixes.h>
 #include <xcb/composite.h>
+#include <xcb/sync.h>
 #include <cairo/cairo-xcb.h>
 
 #include "compositor.h"
@@ -56,8 +57,10 @@ struct weston_xserver {
 struct weston_wm {
 	xcb_connection_t *conn;
 	const xcb_query_extension_reply_t *xfixes;
+	const xcb_query_extension_reply_t *sync;
 	struct wl_event_source *source;
 	xcb_screen_t *screen;
+	struct hash_table *alarm_hash;
 	struct hash_table *window_hash;
 	struct weston_xserver *server;
 	xcb_window_t wm_window;
@@ -107,6 +110,8 @@ struct weston_wm {
 		xcb_atom_t		 net_wm_state_maximized_vert;
 		xcb_atom_t		 net_wm_state_maximized_horz;
 		xcb_atom_t		 net_wm_state_fullscreen;
+    xcb_atom_t		 net_wm_sync_request;
+    xcb_atom_t		 net_wm_sync_request_counter;
 		xcb_atom_t		 net_wm_user_time;
 		xcb_atom_t		 net_wm_icon_name;
 		xcb_atom_t		 net_wm_desktop;
-- 
2.12.2



More information about the wayland-devel mailing list