[weston,v3,01/17] tablet: Add initial tablet support to weston

Submitted by Maniraj Devadoss on Dec. 19, 2017, 8:33 a.m.

Details

Message ID 1513672422-6010-2-git-send-email-Maniraj.Devadoss@in.bosch.com
State New
Headers show
Series "Tablet device support for weston" ( rev: 3 ) in Wayland

Not browsing as part of any series.

Commit Message

Maniraj Devadoss Dec. 19, 2017, 8:33 a.m.
From: Lyude Paul <thatslyude@gmail.com>

Introduces two new structs, weston_tablet and weston_tablet_tool with the
respective information as it's used on the protocol.

Note that tools are independent of tablets, many tools can be used across
multiple tablets.

The nesting on the protocol level requires a global tablet manager, a tablet
seat nested into weston_seat. The list of tablets and tools are also part of
the weston_seat.

Most functions are stubs except for the actual tablet and tablet tool
creation and removal.

Co-authored-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Lyude Paul <thatslyude@gmail.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Bastian Farkas <bfarkas@de.adit-jv.com>
---
 Makefile.am                 |  13 +-
 configure.ac                |   2 +-
 libweston/compositor.c      |   1 +
 libweston/compositor.h      |  98 +++++++++++
 libweston/input.c           | 416 ++++++++++++++++++++++++++++++++++++++++++++
 libweston/libinput-device.c | 187 ++++++++++++++++++++
 libweston/libinput-device.h |   4 +-
 7 files changed, 716 insertions(+), 5 deletions(-)

Patch hide | download patch | download mbox

diff --git a/Makefile.am b/Makefile.am
index cdf82ab..88739f8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -178,7 +178,9 @@  nodist_libweston_@LIBWESTON_MAJOR@_la_SOURCES =				\
 	protocol/relative-pointer-unstable-v1-protocol.c		\
 	protocol/relative-pointer-unstable-v1-server-protocol.h		\
 	protocol/pointer-constraints-unstable-v1-protocol.c		\
-	protocol/pointer-constraints-unstable-v1-server-protocol.h
+	protocol/pointer-constraints-unstable-v1-server-protocol.h \
+	protocol/tablet-unstable-v1-protocol.c \
+	protocol/tablet-unstable-v1-server-protocol.h
 
 BUILT_SOURCES += $(nodist_libweston_@LIBWESTON_MAJOR@_la_SOURCES)
 
@@ -661,7 +663,10 @@  nodist_libtoytoolkit_la_SOURCES =			\
 	protocol/pointer-constraints-unstable-v1-protocol.c		\
 	protocol/pointer-constraints-unstable-v1-client-protocol.h	\
 	protocol/relative-pointer-unstable-v1-protocol.c		\
-	protocol/relative-pointer-unstable-v1-client-protocol.h
+	protocol/relative-pointer-unstable-v1-client-protocol.h \
+	protocol/tablet-unstable-v1-protocol.c		\
+	protocol/tablet-unstable-v1-client-protocol.h
+
 
 BUILT_SOURCES += $(nodist_libtoytoolkit_la_SOURCES)
 
@@ -878,7 +883,9 @@  BUILT_SOURCES +=					\
 	protocol/ivi-hmi-controller-protocol.c		\
 	protocol/ivi-hmi-controller-client-protocol.h	\
 	protocol/ivi-application-protocol.c		\
-	protocol/ivi-application-client-protocol.h
+	protocol/ivi-application-client-protocol.h \
+	protocol/tablet-unstable-v1-protocol.c		\
+	protocol/tablet-unstable-v1-client-protocol.h
 
 westondatadir = $(datadir)/weston
 dist_westondata_DATA =				\
diff --git a/configure.ac b/configure.ac
index 618c570..63a5a72 100644
--- a/configure.ac
+++ b/configure.ac
@@ -212,7 +212,7 @@  if test x$enable_drm_compositor = xyes; then
 fi
 
 
-PKG_CHECK_MODULES(LIBINPUT_BACKEND, [libinput >= 0.8.0])
+PKG_CHECK_MODULES(LIBINPUT_BACKEND, [libinput >= 1.1.901])
 PKG_CHECK_MODULES(COMPOSITOR, [$COMPOSITOR_MODULES])
 
 PKG_CHECK_MODULES(WAYLAND_PROTOCOLS, [wayland-protocols >= 1.7],
diff --git a/libweston/compositor.c b/libweston/compositor.c
index 4cad784..71eaf02 100644
--- a/libweston/compositor.c
+++ b/libweston/compositor.c
@@ -5121,6 +5121,7 @@  weston_compositor_create(struct wl_display *display, void *user_data)
 	wl_list_init(&ec->touch_binding_list);
 	wl_list_init(&ec->axis_binding_list);
 	wl_list_init(&ec->debug_binding_list);
+	wl_list_init(&ec->tablet_manager_resource_list);
 
 	wl_list_init(&ec->plugin_api_list);
 
diff --git a/libweston/compositor.h b/libweston/compositor.h
index 3853b26..3513492 100644
--- a/libweston/compositor.h
+++ b/libweston/compositor.h
@@ -39,6 +39,7 @@  extern "C" {
 
 #define WL_HIDE_DEPRECATED
 #include <wayland-server.h>
+#include <tablet-unstable-v1-server-protocol.h>
 
 #include "matrix.h"
 #include "config-parser.h"
@@ -398,6 +399,34 @@  struct weston_touch {
 	uint32_t grab_time;
 };
 
+struct weston_tablet_tool {
+	struct weston_seat *seat;
+	enum zwp_tablet_tool_v1_type type;
+
+	struct wl_list resource_list;
+
+	struct wl_list link;
+
+	uint64_t serial;
+	uint64_t hwid;
+	uint32_t capabilities;
+};
+
+struct weston_tablet {
+	struct weston_seat *seat;
+	struct evdev_device *device;
+
+	struct wl_list resource_list;
+
+	struct wl_list link;
+
+	char *name;
+	uint32_t vid;
+	uint32_t pid;
+	const char *path;
+	struct weston_output *output;
+};
+
 void
 weston_pointer_motion_to_abs(struct weston_pointer *pointer,
 			     struct weston_pointer_motion_event *event,
@@ -494,6 +523,16 @@  weston_touch_start_grab(struct weston_touch *device,
 void
 weston_touch_end_grab(struct weston_touch *touch);
 
+struct weston_tablet *
+weston_tablet_create(void);
+void
+weston_tablet_destroy(struct weston_tablet *tablet);
+
+struct weston_tablet_tool *
+weston_tablet_tool_create(void);
+void
+weston_tablet_tool_destroy(struct weston_tablet_tool *tool);
+
 bool
 weston_touch_has_focus_resource(struct weston_touch *touch);
 void
@@ -593,6 +632,8 @@  struct weston_seat {
 	struct weston_pointer *pointer_state;
 	struct weston_keyboard *keyboard_state;
 	struct weston_touch *touch_state;
+	struct wl_list tablet_list;
+	struct wl_list tablet_tool_list;
 	int pointer_device_count;
 	int keyboard_device_count;
 	int touch_device_count;
@@ -618,6 +659,8 @@  struct weston_seat {
 
 	struct input_method *input_method;
 	char *seat_name;
+
+	struct wl_list tablet_seat_resource_list;
 };
 
 enum {
@@ -899,6 +942,9 @@  struct weston_compositor {
 	void *user_data;
 	void (*exit)(struct weston_compositor *c);
 
+	struct wl_global *tablet_manager;
+	struct wl_list tablet_manager_resource_list;
+
 	/* Whether to let the compositor run without any input device. */
 	bool require_input;
 
@@ -1365,6 +1411,47 @@  void
 notify_touch_cancel(struct weston_seat *seat);
 
 void
+notify_tablet_added(struct weston_tablet *tablet);
+
+void
+notify_tablet_tool_added(struct weston_tablet_tool *tool);
+
+void
+notify_tablet_tool_proximity_in(struct weston_tablet_tool *tool,
+				uint32_t time,
+				struct weston_tablet *tablet);
+void
+notify_tablet_tool_proximity_out(struct weston_tablet_tool *tool,
+				 uint32_t time);
+void
+notify_tablet_tool_motion(struct weston_tablet_tool *tool,
+			  uint32_t time,
+			  wl_fixed_t x, wl_fixed_t y);
+void
+notify_tablet_tool_pressure(struct weston_tablet_tool *tool,
+			    uint32_t time, uint32_t pressure);
+void
+notify_tablet_tool_distance(struct weston_tablet_tool *tool,
+			    uint32_t time, uint32_t distance);
+void
+notify_tablet_tool_tilt(struct weston_tablet_tool *tool,
+			uint32_t time, int32_t tilt_x, int32_t tilt_y);
+void
+notify_tablet_tool_button(struct weston_tablet_tool *tool,
+			  uint32_t time,
+			  uint32_t button,
+			  enum zwp_tablet_tool_v1_button_state state);
+void
+notify_tablet_tool_up(struct weston_tablet_tool *tool,
+		      uint32_t time);
+void
+notify_tablet_tool_down(struct weston_tablet_tool *tool,
+			uint32_t time);
+void
+notify_tablet_tool_frame(struct weston_tablet_tool *tool,
+			 uint32_t time);
+
+void
 weston_layer_entry_insert(struct weston_layer_entry *list,
 			  struct weston_layer_entry *entry);
 void
@@ -1702,6 +1789,14 @@  void
 weston_seat_init_touch(struct weston_seat *seat);
 void
 weston_seat_release_touch(struct weston_seat *seat);
+struct weston_tablet *
+weston_seat_add_tablet(struct weston_seat *seat);
+struct weston_tablet_tool *
+weston_seat_add_tablet_tool(struct weston_seat *seat);
+void
+weston_seat_release_tablet_tool(struct weston_tablet_tool *tablet_tool);
+void
+weston_seat_release_tablet(struct weston_tablet *tablet);
 void
 weston_seat_repick(struct weston_seat *seat);
 void
@@ -1715,6 +1810,9 @@  weston_compositor_set_xkb_rule_names(struct weston_compositor *ec,
 void
 weston_compositor_xkb_destroy(struct weston_compositor *ec);
 
+void
+weston_tablet_manager_init(struct weston_compositor *ec);
+
 /* String literal of spaces, the same width as the timestamp. */
 #define STAMP_SPACE "               "
 
diff --git a/libweston/input.c b/libweston/input.c
index 4fedc55..946621c 100644
--- a/libweston/input.c
+++ b/libweston/input.c
@@ -1176,6 +1176,57 @@  weston_touch_destroy(struct weston_touch *touch)
 	free(touch);
 }
 
+WL_EXPORT struct weston_tablet *
+weston_tablet_create(void)
+{
+	struct weston_tablet *tablet;
+
+	tablet = zalloc(sizeof *tablet);
+	if (tablet == NULL)
+		return NULL;
+
+	wl_list_init(&tablet->resource_list);
+
+	return tablet;
+}
+
+WL_EXPORT void
+weston_tablet_destroy(struct weston_tablet *tablet)
+{
+	struct wl_resource *resource;
+
+	wl_resource_for_each(resource, &tablet->resource_list)
+		zwp_tablet_v1_send_removed(resource);
+
+	wl_list_remove(&tablet->link);
+}
+
+WL_EXPORT struct weston_tablet_tool *
+weston_tablet_tool_create(void)
+{
+	struct weston_tablet_tool *tool;
+
+	tool = zalloc(sizeof *tool);
+	if (tool == NULL)
+		return NULL;
+
+	wl_list_init(&tool->resource_list);
+
+	return tool;
+}
+
+WL_EXPORT void
+weston_tablet_tool_destroy(struct weston_tablet_tool *tool)
+{
+	struct wl_resource *resource, *tmp;
+
+	wl_resource_for_each_safe(resource, tmp, &tool->resource_list)
+		zwp_tablet_tool_v1_send_removed(resource);
+
+	wl_list_remove(&tool->link);
+}
+
+
 static void
 seat_send_updated_caps(struct weston_seat *seat)
 {
@@ -2261,6 +2312,237 @@  pointer_cursor_surface_get_label(struct weston_surface *surface,
 }
 
 static void
+tablet_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+	wl_resource_destroy(resource);
+}
+
+static const struct zwp_tablet_v1_interface tablet_interface = {
+	tablet_destroy,
+};
+
+static void
+send_tablet_added(struct weston_tablet *tablet,
+		  struct wl_resource *tablet_seat_resource,
+		  struct wl_resource *tablet_resource)
+{
+	zwp_tablet_seat_v1_send_tablet_added(tablet_seat_resource, tablet_resource);
+	zwp_tablet_v1_send_name(tablet_resource, tablet->name);
+	zwp_tablet_v1_send_id(tablet_resource, tablet->vid, tablet->pid);
+	zwp_tablet_v1_send_path(tablet_resource, tablet->path);
+	zwp_tablet_v1_send_done(tablet_resource);
+}
+
+static void
+tablet_unbind_resource(struct wl_resource *resource)
+{
+	struct weston_tablet *tablet;
+
+	tablet = wl_resource_get_user_data(resource);
+
+	wl_list_remove(wl_resource_get_link(resource));
+
+	if (wl_list_empty(&tablet->resource_list)) {
+		free(tablet->name);
+		free(tablet);
+	}
+}
+
+static void
+tablet_add_resource(struct weston_tablet *tablet,
+		    struct wl_client *client,
+		    struct wl_resource *tablet_seat_resource)
+{
+	struct wl_resource *tablet_resource;
+
+	tablet_resource = wl_resource_create(client,
+					     &zwp_tablet_v1_interface,
+					     1, 0);
+
+	wl_list_insert(&tablet->resource_list,
+		       wl_resource_get_link(tablet_resource));
+	wl_resource_set_implementation(tablet_resource,
+				       &tablet_interface,
+				       tablet,
+				       tablet_unbind_resource);
+
+	wl_resource_set_user_data(tablet_resource, tablet);
+	send_tablet_added(tablet, tablet_seat_resource, tablet_resource);
+}
+
+WL_EXPORT void
+notify_tablet_added(struct weston_tablet *tablet)
+{
+	struct wl_resource *tablet_seat_resource;
+	struct weston_seat *seat = tablet->seat;
+
+	wl_resource_for_each(tablet_seat_resource,
+			     &seat->tablet_seat_resource_list) {
+		tablet_add_resource(tablet,
+				    wl_resource_get_client(tablet_seat_resource),
+				    tablet_seat_resource);
+	}
+}
+
+static void
+tablet_tool_set_cursor(struct wl_client *client, struct wl_resource *resource,
+		       uint32_t serial, struct wl_resource *surface_resource,
+		       int32_t hotspot_x, int32_t hotspot_y)
+{
+}
+
+static void
+tablet_tool_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+	wl_resource_destroy(resource);
+}
+
+static const struct zwp_tablet_tool_v1_interface tablet_tool_interface = {
+	tablet_tool_set_cursor,
+	tablet_tool_destroy,
+};
+
+static void
+send_tool_added(struct weston_tablet_tool *tool,
+		struct wl_resource *tool_seat_resource,
+		struct wl_resource *tool_resource)
+{
+	uint32_t caps, cap;
+	zwp_tablet_seat_v1_send_tool_added(tool_seat_resource, tool_resource);
+	zwp_tablet_tool_v1_send_type(tool_resource, tool->type);
+	zwp_tablet_tool_v1_send_hardware_serial(tool_resource,
+						tool->serial >> 32,
+						tool->serial & 0xFFFFFFFF);
+	zwp_tablet_tool_v1_send_hardware_id_wacom(tool_resource,
+						  tool->hwid >> 32,
+						  tool->hwid & 0xFFFFFFFF);
+	caps = tool->capabilities;
+	while (caps != 0) {
+		cap = ffs(caps) - 1;
+		zwp_tablet_tool_v1_send_capability(tool_resource, cap);
+		caps &= ~(1 << cap);
+	}
+
+	zwp_tablet_tool_v1_send_done(tool_resource);
+}
+
+static void
+tablet_tool_unbind_resource(struct wl_resource *resource)
+{
+	struct weston_tablet_tool *tool;
+
+	tool = wl_resource_get_user_data(resource);
+
+	wl_list_remove(wl_resource_get_link(resource));
+
+	if (wl_list_empty(&tool->resource_list)) {
+		free(tool);
+	}
+}
+
+static void
+tablet_tool_add_resource(struct weston_tablet_tool *tool,
+			 struct wl_client *client,
+			 struct wl_resource *tablet_seat_resource)
+{
+	struct wl_resource *tool_resource;
+
+	tool_resource = wl_resource_create(client,
+					   &zwp_tablet_tool_v1_interface,
+					   1, 0);
+
+	wl_list_insert(&tool->resource_list,
+		       wl_resource_get_link(tool_resource));
+	wl_resource_set_implementation(tool_resource,
+				       &tablet_tool_interface,
+				       tool,
+				       tablet_tool_unbind_resource);
+
+	wl_resource_set_user_data(tool_resource, tool);
+	send_tool_added(tool, tablet_seat_resource, tool_resource);
+}
+
+WL_EXPORT void
+notify_tablet_tool_added(struct weston_tablet_tool *tool)
+{
+	struct wl_resource *tablet_seat_resource;
+	struct weston_seat *seat = tool->seat;
+	struct wl_client *client;
+
+	wl_resource_for_each(tablet_seat_resource,
+			     &seat->tablet_seat_resource_list) {
+		client = wl_resource_get_client(tablet_seat_resource);
+		tablet_tool_add_resource(tool, client,
+					 tablet_seat_resource);
+	}
+}
+
+WL_EXPORT void
+notify_tablet_tool_proximity_in(struct weston_tablet_tool *tool,
+				 uint32_t time,
+				 struct weston_tablet *tablet)
+{
+}
+
+WL_EXPORT void
+notify_tablet_tool_proximity_out(struct weston_tablet_tool *tool,
+				 uint32_t time)
+{
+}
+
+WL_EXPORT void
+notify_tablet_tool_motion(struct weston_tablet_tool *tool,
+			  uint32_t time,
+			  wl_fixed_t x, wl_fixed_t y)
+{
+}
+
+WL_EXPORT void
+notify_tablet_tool_pressure(struct weston_tablet_tool *tool,
+			    uint32_t time, uint32_t pressure)
+{
+}
+
+WL_EXPORT void
+notify_tablet_tool_distance(struct weston_tablet_tool *tool,
+			    uint32_t time, uint32_t distance)
+{
+}
+
+WL_EXPORT void
+notify_tablet_tool_tilt(struct weston_tablet_tool *tool,
+			uint32_t time, int32_t tilt_x, int32_t tilt_y)
+{
+}
+
+WL_EXPORT void
+notify_tablet_tool_button(struct weston_tablet_tool *tool,
+			  uint32_t time,
+			  uint32_t button,
+			  enum zwp_tablet_tool_v1_button_state state)
+{
+}
+
+WL_EXPORT void
+notify_tablet_tool_down(struct weston_tablet_tool *tool,
+			uint32_t time)
+{
+}
+
+WL_EXPORT void
+notify_tablet_tool_up(struct weston_tablet_tool *tool,
+		      uint32_t time)
+{
+}
+
+WL_EXPORT void
+notify_tablet_tool_frame(struct weston_tablet_tool *tool,
+			 uint32_t time)
+{
+}
+
+
+static void
 pointer_cursor_surface_committed(struct weston_surface *es,
 				 int32_t dx, int32_t dy)
 {
@@ -3024,6 +3306,20 @@  weston_seat_release_pointer(struct weston_seat *seat)
 }
 
 WL_EXPORT void
+weston_seat_release_tablet_tool(struct weston_tablet_tool *tool)
+{
+	/* FIXME: nothing is calling this function yet, tools are only
+	   released on shutdown when the seat goes away */
+	weston_tablet_tool_destroy(tool);
+}
+
+WL_EXPORT void
+weston_seat_release_tablet(struct weston_tablet *tablet)
+{
+	weston_tablet_destroy(tablet);
+}
+
+WL_EXPORT void
 weston_seat_init_touch(struct weston_seat *seat)
 {
 	struct weston_touch *touch;
@@ -3046,6 +3342,39 @@  weston_seat_init_touch(struct weston_seat *seat)
 	seat_send_updated_caps(seat);
 }
 
+WL_EXPORT struct weston_tablet *
+weston_seat_add_tablet(struct weston_seat *seat)
+{
+	struct weston_tablet *tablet;
+
+	weston_tablet_manager_init(seat->compositor);
+
+	tablet = weston_tablet_create();
+	if (tablet == NULL)
+		return NULL;
+
+	tablet->seat = seat;
+
+	return tablet;
+}
+
+WL_EXPORT struct weston_tablet_tool *
+weston_seat_add_tablet_tool(struct weston_seat *seat)
+{
+	struct weston_tablet_tool *tool;
+
+	weston_tablet_manager_init(seat->compositor);
+
+	tool = weston_tablet_tool_create();
+	if (tool == NULL)
+		return NULL;
+
+	wl_list_init(&tool->resource_list);
+	tool->seat = seat;
+
+	return tool;
+}
+
 WL_EXPORT void
 weston_seat_release_touch(struct weston_seat *seat)
 {
@@ -3070,6 +3399,9 @@  weston_seat_init(struct weston_seat *seat, struct weston_compositor *ec,
 	wl_list_init(&seat->drag_resource_list);
 	wl_signal_init(&seat->destroy_signal);
 	wl_signal_init(&seat->updated_caps_signal);
+	wl_list_init(&seat->tablet_seat_resource_list);
+	wl_list_init(&seat->tablet_list);
+	wl_list_init(&seat->tablet_tool_list);
 
 	seat->global = wl_global_create(ec->wl_display, &wl_seat_interface, 5,
 					seat, bind_seat);
@@ -3088,6 +3420,9 @@  weston_seat_init(struct weston_seat *seat, struct weston_compositor *ec,
 WL_EXPORT void
 weston_seat_release(struct weston_seat *seat)
 {
+	struct weston_tablet *tablet, *tmp;
+	struct weston_tablet_tool *tool, *tmp_tool;
+
 	wl_list_remove(&seat->link);
 
 	if (seat->saved_kbd_focus)
@@ -3099,6 +3434,10 @@  weston_seat_release(struct weston_seat *seat)
 		weston_keyboard_destroy(seat->keyboard_state);
 	if (seat->touch_state)
 		weston_touch_destroy(seat->touch_state);
+	wl_list_for_each_safe(tablet, tmp, &seat->tablet_list, link)
+		weston_tablet_destroy(tablet);
+	wl_list_for_each_safe(tool, tmp_tool, &seat->tablet_tool_list, link)
+		weston_tablet_tool_destroy(tool);
 
 	free (seat->seat_name);
 
@@ -3237,6 +3576,83 @@  weston_seat_get_touch(struct weston_seat *seat)
 	return NULL;
 }
 
+static void
+tablet_seat_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+}
+
+static const struct zwp_tablet_seat_v1_interface tablet_seat_interface = {
+	tablet_seat_destroy,
+};
+
+static void
+tablet_manager_get_tablet_seat(struct wl_client *client, struct wl_resource *resource,
+			       uint32_t id, struct wl_resource *seat_resource)
+{
+	struct weston_seat *seat = wl_resource_get_user_data(seat_resource);
+	struct wl_resource *cr;
+	struct weston_tablet *tablet;
+	struct weston_tablet_tool *tool;
+
+	cr = wl_resource_create(client, &zwp_tablet_seat_v1_interface,
+				wl_resource_get_version(resource), id);
+	if (cr == NULL) {
+		wl_client_post_no_memory(client);
+		return;
+	}
+
+	/* store the resource in the weston_seat */
+	wl_list_insert(&seat->tablet_seat_resource_list, wl_resource_get_link(cr));
+	wl_resource_set_implementation(cr, &tablet_seat_interface, seat,
+				       unbind_resource);
+
+	/* Notify client of any tablets already connected to the system */
+	wl_list_for_each(tablet, &seat->tablet_list, link)
+		tablet_add_resource(tablet, client, cr);
+
+	/* Notify client of any tools already known */
+	wl_list_for_each(tool, &seat->tablet_tool_list, link)
+		tablet_tool_add_resource(tool, client, cr);
+}
+
+static void
+tablet_manager_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+
+}
+
+static const struct zwp_tablet_manager_v1_interface tablet_manager_interface = {
+	tablet_manager_get_tablet_seat,
+	tablet_manager_destroy,
+};
+
+static void
+bind_tablet_manager(struct wl_client *client, void *data, uint32_t version,
+		    uint32_t id)
+{
+	struct weston_compositor *compositor = data;
+	struct wl_resource *resource;
+
+	resource = wl_resource_create(client, &zwp_tablet_manager_v1_interface,
+				      MIN(version, 1), id);
+	wl_resource_set_implementation(resource, &tablet_manager_interface,
+				       data, unbind_resource);
+	wl_list_insert(&compositor->tablet_manager_resource_list,
+		       wl_resource_get_link(resource));
+}
+
+WL_EXPORT void
+weston_tablet_manager_init(struct weston_compositor *compositor)
+{
+	if (compositor->tablet_manager)
+		return;
+
+	compositor->tablet_manager = wl_global_create(compositor->wl_display,
+						      &zwp_tablet_manager_v1_interface,
+						      1, compositor,
+						      bind_tablet_manager);
+}
+
 /** Sets the keyboard focus to the given surface
  *
  * \param seat The seat to query
diff --git a/libweston/libinput-device.c b/libweston/libinput-device.c
index f97afcf..74d8b2a 100644
--- a/libweston/libinput-device.c
+++ b/libweston/libinput-device.c
@@ -41,6 +41,14 @@ 
 #include "libinput-device.h"
 #include "shared/helpers.h"
 
+struct tablet_output_listener {
+	struct wl_listener base;
+	struct wl_list tablet_list;
+};
+
+static bool
+tablet_bind_output(struct weston_tablet *tablet, struct weston_output *output);
+
 void
 evdev_led_update(struct evdev_device *device, enum weston_led weston_leds)
 {
@@ -355,6 +363,79 @@  handle_touch_frame(struct libinput_device *libinput_device,
 	notify_touch_frame(seat);
 }
 
+static void
+handle_tablet_proximity(struct libinput_device *libinput_device,
+			struct libinput_event_tablet_tool *proximity_event)
+{
+	struct evdev_device *device;
+	struct weston_tablet *tablet;
+	struct weston_tablet_tool *tool;
+	struct libinput_tablet_tool *libinput_tool;
+	enum libinput_tablet_tool_type libinput_tool_type;
+	uint32_t serial, type;
+	uint32_t time;
+	bool create = true;
+
+	device = libinput_device_get_user_data(libinput_device);
+	time = libinput_event_tablet_tool_get_time(proximity_event);
+	libinput_tool = libinput_event_tablet_tool_get_tool(proximity_event);
+	serial = libinput_tablet_tool_get_serial(libinput_tool);
+	libinput_tool_type = libinput_tablet_tool_get_type(libinput_tool);
+
+	tool = libinput_tablet_tool_get_user_data(libinput_tool);
+	tablet = device->tablet;
+
+	if (libinput_event_tablet_tool_get_proximity_state(proximity_event) ==
+	    LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT) {
+		notify_tablet_tool_proximity_out(tool, time);
+		return;
+	}
+
+	switch (libinput_tool_type) {
+	case LIBINPUT_TABLET_TOOL_TYPE_PEN:
+		type = ZWP_TABLET_TOOL_V1_TYPE_PEN;
+		break;
+	case LIBINPUT_TABLET_TOOL_TYPE_ERASER:
+		type = ZWP_TABLET_TOOL_V1_TYPE_ERASER;
+		break;
+	default:
+		fprintf(stderr, "Unknown libinput tool type %d\n",
+			libinput_tool_type);
+		return;
+	}
+
+	wl_list_for_each(tool, &device->seat->tablet_tool_list, link) {
+		if (tool->serial == serial && tool->type == type) {
+			create = false;
+			break;
+		}
+	}
+
+	if (create) {
+		tool = weston_seat_add_tablet_tool(device->seat);
+		tool->serial = serial;
+		tool->hwid = libinput_tablet_tool_get_tool_id(libinput_tool);
+		tool->type = type;
+		tool->capabilities = 0;
+
+		if (libinput_tablet_tool_has_distance(libinput_tool))
+		    tool->capabilities |= 1 << ZWP_TABLET_TOOL_V1_CAPABILITY_DISTANCE;
+		if (libinput_tablet_tool_has_pressure(libinput_tool))
+		    tool->capabilities |= 1 << ZWP_TABLET_TOOL_V1_CAPABILITY_PRESSURE;
+		if (libinput_tablet_tool_has_tilt(libinput_tool))
+		    tool->capabilities |= 1 << ZWP_TABLET_TOOL_V1_CAPABILITY_TILT;
+
+		wl_list_insert(&device->seat->tablet_tool_list, &tool->link);
+		notify_tablet_tool_added(tool);
+
+		libinput_tablet_tool_set_user_data(libinput_tool, tool);
+	}
+
+	notify_tablet_tool_proximity_in(tool, time, tablet);
+	/* FIXME: we should send axis updates  here */
+	notify_tablet_tool_frame(tool, time);
+}
+
 int
 evdev_device_process_event(struct libinput_event *event)
 {
@@ -404,6 +485,10 @@  evdev_device_process_event(struct libinput_event *event)
 		handle_touch_frame(libinput_device,
 				   libinput_event_get_touch_event(event));
 		break;
+	case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY:
+		handle_tablet_proximity(libinput_device,
+				libinput_event_get_tablet_tool_event(event));
+		break;
 	default:
 		handled = 0;
 		weston_log("unknown libinput event %d\n",
@@ -534,6 +619,102 @@  evdev_device_set_output(struct evdev_device *device,
 	evdev_device_set_calibration(device);
 }
 
+static void
+bind_unbound_tablets(struct wl_listener *listener_base, void *data)
+{
+	struct tablet_output_listener *listener =
+		wl_container_of(listener_base, listener, base);
+	struct weston_tablet *tablet, *tmp;
+
+	wl_list_for_each_safe(tablet, tmp, &listener->tablet_list, link) {
+		if (tablet_bind_output(tablet, data)) {
+			wl_list_remove(&tablet->link);
+			wl_list_insert(&tablet->seat->tablet_list,
+				       &tablet->link);
+			tablet->device->seat_caps |= EVDEV_SEAT_TABLET;
+			notify_tablet_added(tablet);
+		}
+	}
+
+	if (wl_list_empty(&listener->tablet_list)) {
+		wl_list_remove(&listener_base->link);
+		free(listener);
+	}
+}
+
+static bool
+tablet_bind_output(struct weston_tablet *tablet, struct weston_output *output)
+{
+	struct wl_list *output_list = &tablet->seat->compositor->output_list;
+	struct weston_compositor *compositor = tablet->seat->compositor;
+	struct tablet_output_listener *listener;
+	struct wl_listener *listener_base;
+
+	if (output) {
+		tablet->output = output;
+	} else if (!wl_list_empty(output_list)) {
+		/* Find the first available display */
+		wl_list_for_each(output, output_list, link)
+			break;
+		tablet->output = output;
+	}
+
+	if (tablet->output)
+		return true;
+
+	listener_base = wl_signal_get(&compositor->output_created_signal,
+				      bind_unbound_tablets);
+	if (listener_base == NULL) {
+		listener = zalloc(sizeof(*listener));
+
+		wl_list_init(&listener->tablet_list);
+
+		listener_base = &listener->base;
+		listener_base->notify = bind_unbound_tablets;
+
+		wl_signal_add(&compositor->output_created_signal,
+			      listener_base);
+	} else {
+		listener = wl_container_of(listener_base, listener, base);
+	}
+
+	wl_list_insert(&listener->tablet_list, &tablet->link);
+	return false;
+}
+
+static void
+evdev_device_init_tablet(struct evdev_device *device,
+			 struct libinput_device *libinput_device,
+			 struct weston_seat *seat)
+{
+	struct weston_tablet *tablet;
+	struct udev_device *udev_device;
+
+	tablet = weston_seat_add_tablet(seat);
+	tablet->name = strdup(libinput_device_get_name(libinput_device));
+	tablet->vid = libinput_device_get_id_vendor(libinput_device);
+	tablet->pid = libinput_device_get_id_product(libinput_device);
+
+	udev_device = libinput_device_get_udev_device(libinput_device);
+	if (udev_device) {
+		tablet->path = udev_device_get_devnode(udev_device);
+		udev_device_unref(udev_device);
+	}
+
+	/* If we can successfully bind the tablet to an output, then
+	 * it's ready to get added to the seat's tablet list, otherwise
+	 * it will get added when an appropriate output is available */
+	if (tablet_bind_output(tablet, NULL)) {
+		wl_list_insert(&seat->tablet_list, &tablet->link);
+		device->seat_caps |= EVDEV_SEAT_TABLET;
+
+		notify_tablet_added(tablet);
+	}
+
+	device->tablet = tablet;
+	tablet->device = device;
+}
+
 struct evdev_device *
 evdev_device_create(struct libinput_device *libinput_device,
 		    struct weston_seat *seat)
@@ -563,6 +744,10 @@  evdev_device_create(struct libinput_device *libinput_device,
 		weston_seat_init_touch(seat);
 		device->seat_caps |= EVDEV_SEAT_TOUCH;
 	}
+	if (libinput_device_has_capability(libinput_device,
+					   LIBINPUT_DEVICE_CAP_TABLET_TOOL)) {
+		evdev_device_init_tablet(device, libinput_device, seat);
+	}
 
 	libinput_device_set_user_data(libinput_device, device);
 	libinput_device_ref(libinput_device);
@@ -579,6 +764,8 @@  evdev_device_destroy(struct evdev_device *device)
 		weston_seat_release_keyboard(device->seat);
 	if (device->seat_caps & EVDEV_SEAT_TOUCH)
 		weston_seat_release_touch(device->seat);
+	if (device->seat_caps & EVDEV_SEAT_TABLET)
+		weston_seat_release_tablet(device->tablet);
 
 	if (device->output)
 		wl_list_remove(&device->output_destroy_listener.link);
diff --git a/libweston/libinput-device.h b/libweston/libinput-device.h
index 5041a4a..2c269ba 100644
--- a/libweston/libinput-device.h
+++ b/libweston/libinput-device.h
@@ -37,7 +37,8 @@ 
 enum evdev_device_seat_capability {
 	EVDEV_SEAT_POINTER = (1 << 0),
 	EVDEV_SEAT_KEYBOARD = (1 << 1),
-	EVDEV_SEAT_TOUCH = (1 << 2)
+	EVDEV_SEAT_TOUCH = (1 << 2),
+	EVDEV_SEAT_TABLET = (1 << 3)
 };
 
 struct evdev_device {
@@ -47,6 +48,7 @@  struct evdev_device {
 	struct wl_list link;
 	struct weston_output *output;
 	struct wl_listener output_destroy_listener;
+	struct weston_tablet *tablet;
 	char *devnode;
 	char *output_name;
 	int fd;