diff --git a/clients/meson.build b/clients/meson.build index 539e9db2..2fe10956 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -330,6 +330,13 @@ demo_clients = [ 'basename': 'subsurfaces', 'deps': [ 'egl', 'glesv2', 'wayland-egl' ] }, + { + 'basename': 'tablet', + 'add_sources': [ + tablet_unstable_v2_client_protocol_h, + tablet_unstable_v2_protocol_c, + ], + }, { 'basename': 'transformed' }, ] diff --git a/clients/tablet.c b/clients/tablet.c new file mode 100644 index 00000000..112fc4b5 --- /dev/null +++ b/clients/tablet.c @@ -0,0 +1,254 @@ +/* + * Copyright © 2014 Lyude + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE + */ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "window.h" +#include "tablet-unstable-v2-client-protocol.h" + +struct display *display; +struct window *window; +struct widget *widget; + +cairo_surface_t *draw_buffer; + +int old_x, old_y; +int current_x, current_y; +enum zwp_tablet_tool_v2_type tool_type; + +bool tablet_is_down; + +double current_pressure; + +#define WL_TABLET_AXIS_MAX 65535 + +static void +redraw_handler(struct widget *widget, void *data) +{ + cairo_surface_t *surface; + cairo_t *window_cr, *drawing_cr; + struct rectangle allocation; + + widget_get_allocation(widget, &allocation); + + surface = window_get_surface(window); + + /* Setup the background */ + window_cr = cairo_create(surface); + cairo_set_operator(window_cr, CAIRO_OPERATOR_SOURCE); + cairo_rectangle(window_cr, + allocation.x, + allocation.y, + allocation.width, + allocation.height); + cairo_set_source_rgba(window_cr, 0, 0, 0, 0.8); + cairo_fill(window_cr); + + /* Update the drawing buffer */ + if (tablet_is_down) { + if (old_x != -1 && old_y != -1) { + drawing_cr = cairo_create(draw_buffer); + if (tool_type == ZWP_TABLET_TOOL_V2_TYPE_PEN) { + cairo_set_source_rgb(drawing_cr, 1, 1, 1); + cairo_set_line_width(drawing_cr, + current_pressure / + WL_TABLET_AXIS_MAX * 7 + 1); + } else if (tool_type == ZWP_TABLET_TOOL_V2_TYPE_ERASER) { + cairo_set_operator(drawing_cr, CAIRO_OPERATOR_CLEAR); + cairo_set_source_rgb(drawing_cr, 0, 0, 0); + cairo_set_line_width(drawing_cr, + current_pressure / + WL_TABLET_AXIS_MAX * 30 + 10); + } + + cairo_set_line_cap(drawing_cr, CAIRO_LINE_CAP_ROUND); + + cairo_translate(drawing_cr, + -allocation.x, + -allocation.y); + cairo_move_to(drawing_cr, old_x, old_y); + cairo_line_to(drawing_cr, current_x, current_y); + cairo_stroke(drawing_cr); + + cairo_destroy(drawing_cr); + } + + old_x = current_x; + old_y = current_y; + } + + /* Squash the drawing buffer onto the window's buffer */ + cairo_set_source_surface(window_cr, + draw_buffer, + allocation.x, + allocation.y); + cairo_set_operator(window_cr, CAIRO_OPERATOR_ADD); + cairo_rectangle(window_cr, + allocation.x, + allocation.y, + allocation.width, + allocation.height); + cairo_clip(window_cr); + cairo_paint(window_cr); + + cairo_destroy(window_cr); + + cairo_surface_destroy(surface); +} + +static void +resize_handler(struct widget *widget, + int32_t width, int32_t height, + void *data) +{ + cairo_surface_t *tmp_buffer; + cairo_t *cr; + + tmp_buffer = draw_buffer; + draw_buffer = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + width, height); + cr = cairo_create(draw_buffer); + cairo_set_source_rgba(cr, 0, 0, 0, 0); + cairo_rectangle(cr, 0, 0, width, height); + cairo_fill(cr); + + if (tmp_buffer) { + cairo_set_source_surface(cr, tmp_buffer, 0, 0); + cairo_rectangle(cr, 0, 0, width, height); + cairo_clip(cr); + cairo_paint(cr); + } + + cairo_destroy(cr); + + cairo_surface_destroy(tmp_buffer); +} + +static void +proximity_in_handler(struct widget *widget, struct tablet_tool *tool, + struct tablet *tablet, void *data) +{ + tool_type = tablet_tool_get_type(tool); +} + +static void +pressure_handler(struct widget *widget, struct tablet_tool *tool, + uint32_t pressure, void *data) +{ + current_pressure = pressure; +} + +static int +tablet_motion_handler(struct widget *widget, struct tablet_tool *tool, + float x, float y, void *data) +{ + int cursor; + + current_x = x; + current_y = y; + + if (tablet_is_down) { + widget_schedule_redraw(widget); + cursor = CURSOR_HAND1; + } else { + cursor = CURSOR_LEFT_PTR; + } + + return cursor; +} + +static void +tablet_down_handler(struct widget *widget, struct tablet_tool *tool, void *data) +{ + tablet_is_down = true; +} + +static void +tablet_up_handler(struct widget *widget, struct tablet_tool *tool, void *data) +{ + tablet_is_down = false; + old_x = -1; + old_y = -1; +} + +static void +init_globals(void) +{ + window = window_create(display); + widget = window_frame_create(window, NULL); + window_set_title(window, "Wayland Tablet Demo"); + old_x = -1; + old_y = -1; + + widget_set_tablet_tool_axis_handlers(widget, + tablet_motion_handler, + pressure_handler, + NULL, NULL, + NULL, NULL, NULL); + widget_set_tablet_tool_down_handler(widget, tablet_down_handler); + widget_set_tablet_tool_up_handler(widget, tablet_up_handler); + widget_set_tablet_tool_proximity_handlers(widget, + proximity_in_handler, + NULL); + widget_set_redraw_handler(widget, redraw_handler); + widget_set_resize_handler(widget, resize_handler); + + widget_schedule_resize(widget, 1000, 800); +} + +static void +cleanup(void) +{ + widget_destroy(widget); + window_destroy(window); +} + +int +main(int argc, char *argv[]) +{ + display = display_create(&argc, argv); + if (display == NULL) { + fprintf(stderr, "failed to create display: %m\n"); + return -1; + } + + init_globals(); + + display_run(display); + + cleanup(); + + display_destroy(display); + + return 0; +}