[weston,v2,1/8] xwm: Implement _NET_WM_SYNC_REQUEST protocol

Submitted by Louis-Francis Ratté-Boulianne on Nov. 13, 2017, 9:20 p.m.

Details

Message ID 20171113212056.18397-2-lfrb@collabora.com
State New
Headers show
Series "Implement NET_WM_SYNC_REQUEST basic support" ( rev: 2 ) in Wayland

Not browsing as part of any series.

Commit Message

Louis-Francis Ratté-Boulianne Nov. 13, 2017, 9:20 p.m.
v2: Remove some useless checks and don't be too verbose

Signed-off-by: Louis-Francis Ratté-Boulianne <lfrb@collabora.com>

fixup: Implement _NET_WM_SYNC_REQUEST
---
 configure.ac              |   2 +-
 xwayland/window-manager.c | 207 +++++++++++++++++++++++++++++++++++++++++++---
 xwayland/xwayland.h       |   5 ++
 3 files changed, 201 insertions(+), 13 deletions(-)

Patch hide | download patch | download mbox

diff --git a/configure.ac b/configure.ac
index c287fac6..77012dcc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -163,7 +163,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 3e8c4c7c..149d7fe4 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;
 
@@ -1045,6 +1057,81 @@  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;
+
+	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 =
@@ -1102,6 +1189,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. */
@@ -1154,6 +1251,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)
@@ -1368,6 +1471,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);
@@ -1588,9 +1696,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 =
@@ -1739,6 +1844,35 @@  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;
+
+	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,
@@ -2148,6 +2282,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++;
 	}
@@ -2237,6 +2377,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) },
@@ -2274,6 +2417,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;
@@ -2281,6 +2426,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);
 
@@ -2297,6 +2443,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");
@@ -2396,7 +2558,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)
@@ -2409,11 +2571,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;
@@ -2450,6 +2620,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,
@@ -2492,6 +2664,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);
@@ -2532,6 +2705,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;
@@ -2554,6 +2732,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);
@@ -2591,9 +2770,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..5c34b99a 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;