This client uses libiio to retrieve accelerometer values from the iio subsystem on Linux (and maybe some other kernels), and automatically rotate the output whenever orientation changed.
I tested it with a mma8453 accelerometer, but everything needed should be available in the configuration to make it work with any other iio device. Signed-off-by: Emmanuel Gil Peyrot <[email protected]> --- Makefile.am | 24 +++ clients/autorotater.c | 472 ++++++++++++++++++++++++++++++++++++++++++++ configure.ac | 15 ++ desktop-shell/shell.c | 1 + protocol/weston-rotater.xml | 26 +++ src/compositor.h | 3 + src/rotater.c | 150 ++++++++++++++ 7 files changed, 691 insertions(+) create mode 100644 clients/autorotater.c create mode 100644 protocol/weston-rotater.xml create mode 100644 src/rotater.c diff --git a/Makefile.am b/Makefile.am index 00b74e5..b1e0ade 100644 --- a/Makefile.am +++ b/Makefile.am @@ -122,6 +122,8 @@ endif nodist_weston_SOURCES = \ protocol/weston-screenshooter-protocol.c \ protocol/weston-screenshooter-server-protocol.h \ + protocol/weston-rotater-protocol.c \ + protocol/weston-rotater-server-protocol.h \ protocol/text-cursor-position-protocol.c \ protocol/text-cursor-position-server-protocol.h \ protocol/text-input-unstable-v1-protocol.c \ @@ -611,6 +613,28 @@ nodist_weston_screenshooter_SOURCES = \ weston_screenshooter_LDADD = $(CLIENT_LIBS) libshared.la weston_screenshooter_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) +if BUILD_AUTOROTATER +libexec_PROGRAMS += weston-autorotater + +weston_autorotater_SOURCES = \ + clients/autorotater.c +nodist_weston_autorotater_SOURCES = \ + protocol/weston-rotater-protocol.c \ + protocol/weston-rotater-client-protocol.h +weston_autorotater_LDADD = $(CLIENT_LIBS) $(LIBIIO_LIBS) libshared.la +weston_autorotater_CFLAGS = $(AM_CFLAGS) $(CLIENT_CFLAGS) $(LIBIIO_CFLAGS) + +weston_SOURCES += \ + src/rotater.c + +BUILT_SOURCES += \ + protocol/weston-rotater-protocol.c \ + protocol/weston-rotater-client-protocol.h + +EXTRA_DIST += \ + protocol/weston-rotater.xml +endif + weston_terminal_SOURCES = \ clients/terminal.c \ shared/helpers.h diff --git a/clients/autorotater.c b/clients/autorotater.c new file mode 100644 index 0000000..0547f83 --- /dev/null +++ b/clients/autorotater.c @@ -0,0 +1,472 @@ +/* + * Copyright © 2016 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include <stdint.h> +#include <errno.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <wayland-client.h> +#include "weston-rotater-client-protocol.h" +#include "shared/config-parser.h" +#include "shared/helpers.h" +#include "shared/xalloc.h" + +#include <iio.h> + +#define BUFFER_SIZE 256 + +static struct weston_rotater *rotater; +static struct wl_list output_list; + +struct rotater_output { + struct wl_output *output; + struct wl_list link; +}; + +#define DEFAULT_SAMPLING_FREQUENCY 100. + +struct config { + char *device_name; + char *channel_x_name, *channel_y_name; + char *sampling_frequency_name; + double wanted_sampling_frequency; + int threshold; + int smooth_capture; + struct wl_array allowed_rotations; +}; + +#define MAX_CAPTURE_BUFFER_LENGTH 16 + +struct accelerometer { + struct iio_context *context; + struct iio_device *device; + struct iio_channel *channel_x, *channel_y; + double sampling_frequency; + + struct { + int x[MAX_CAPTURE_BUFFER_LENGTH]; + int y[MAX_CAPTURE_BUFFER_LENGTH]; + } capture_buffer; +}; + +static const struct { + const char *name; + int token; +} transforms[] = { + { "normal", WESTON_ROTATER_TRANSFORM_NORMAL }, + { "90", WESTON_ROTATER_TRANSFORM_90 }, + { "180", WESTON_ROTATER_TRANSFORM_180 }, + { "270", WESTON_ROTATER_TRANSFORM_270 }, + { "flipped", WESTON_ROTATER_TRANSFORM_FLIPPED }, + { "flipped-90", WESTON_ROTATER_TRANSFORM_FLIPPED_90 }, + { "flipped-180", WESTON_ROTATER_TRANSFORM_FLIPPED_180 }, + { "flipped-270", WESTON_ROTATER_TRANSFORM_FLIPPED_270 }, +}; + +static bool +parse_transform(const char *transform, int *out) +{ + unsigned int i; + + for (i = 0; i < ARRAY_LENGTH(transforms); i++) + if (strncmp(transforms[i].name, transform, + strlen(transforms[i].name)) == 0) { + *out = transforms[i].token; + return true; + } + + return false; +} + +static struct config * +read_configuration(void) +{ + struct config *configuration; + const char *config_file; + struct weston_config *config; + struct weston_config_section *s; + + char *allowed_rotations, *p; + int transform, *array_items; + int i; + + configuration = zalloc(sizeof *configuration); + if (!configuration) + return NULL; + + config_file = weston_config_get_name_from_env(); + config = weston_config_parse(config_file); + s = weston_config_get_section(config, "autorotater", NULL, NULL); + weston_config_section_get_string(s, "device", + &configuration->device_name, NULL); + weston_config_section_get_string(s, "channel_x", + &configuration->channel_x_name, NULL); + weston_config_section_get_string(s, "channel_y", + &configuration->channel_y_name, NULL); + weston_config_section_get_int(s, "threshold", + &configuration->threshold, 128); + weston_config_section_get_string(s, "sampling_frequency_attr", + &configuration->sampling_frequency_name, + NULL); + weston_config_section_get_double(s, "wanted_sampling_frequency", + &configuration->wanted_sampling_frequency, + 0.); + weston_config_section_get_int(s, "smooth_capture", + &configuration->smooth_capture, 1); + weston_config_section_get_string(s, "allowed_rotations", + &allowed_rotations, NULL); + weston_config_destroy(config); + + if (configuration->smooth_capture < 1) + configuration->smooth_capture = 1; + else if (configuration->smooth_capture > MAX_CAPTURE_BUFFER_LENGTH) + configuration->smooth_capture = MAX_CAPTURE_BUFFER_LENGTH; + + wl_array_init(&configuration->allowed_rotations); + if (allowed_rotations) { + p = allowed_rotations; + for (i = 0; i < 4; ++i) { + if (!parse_transform(p, &transform)) + break; + array_items = wl_array_add(&configuration->allowed_rotations, + sizeof(int)); + *array_items = transform; + for (; *p != ' ' && *p != '\0'; p++); + if (*p++ == '\0') + break; + } + free(allowed_rotations); + } else { + array_items = wl_array_add(&configuration->allowed_rotations, + sizeof(int)); + array_items[0] = WESTON_ROTATER_TRANSFORM_NORMAL; + array_items[1] = WESTON_ROTATER_TRANSFORM_90; + array_items[2] = WESTON_ROTATER_TRANSFORM_180; + array_items[3] = WESTON_ROTATER_TRANSFORM_270; + } + + return configuration; +} + +static void +destroy_configuration(struct config *config) +{ + free(config->device_name); + free(config->channel_x_name); + free(config->channel_y_name); + free(config->sampling_frequency_name); + wl_array_release(&config->allowed_rotations); + free(config); +} + +static struct accelerometer * +initialize_iio(struct config *config) +{ + struct accelerometer *accel; + int ret; + + accel = zalloc(sizeof *accel); + if (!accel) + return NULL; + + accel->context = iio_create_local_context(); + if (!accel->context) + goto err_context; + + if (config->device_name) + accel->device = iio_context_find_device(accel->context, + config->device_name); + + if (!accel->device) { + fprintf(stderr, "couldn't find device %s\n", + config->device_name); + goto err_device; + } + + accel->channel_x = iio_device_find_channel(accel->device, + config->channel_x_name, + false); + if (!accel->channel_x) { + fprintf(stderr, "couldn't find x channel %s\n", + config->channel_x_name); + goto err_channel; + } + + accel->channel_y = iio_device_find_channel(accel->device, + config->channel_y_name, + false); + if (!accel->channel_y) { + fprintf(stderr, "couldn't find y channel %s\n", + config->channel_y_name); + goto err_channel; + } + + if (config->wanted_sampling_frequency) { + ret = iio_device_attr_write_double(accel->device, + config->sampling_frequency_name, + config->wanted_sampling_frequency); + if (ret < 0) { + fprintf(stderr, "couldn't set sampling frequency to " + "%f\n", + config->wanted_sampling_frequency); + goto err_channel; + } + } + + ret = iio_device_attr_read_double(accel->device, + config->sampling_frequency_name, + &accel->sampling_frequency); + if (ret < 0) { + fprintf(stderr, "couldn't retrieve sampling frequency, " + "defaulting to %f\n", + DEFAULT_SAMPLING_FREQUENCY); + accel->sampling_frequency = DEFAULT_SAMPLING_FREQUENCY; + } + + return accel; + +err_channel: +err_device: + iio_context_destroy(accel->context); +err_context: + free(accel); + return NULL; +} + +static void +destroy_accelerometer(struct accelerometer *accel) +{ + if (accel->context) + iio_context_destroy(accel->context); + free(accel); +} + +static bool +capture_rotation(struct accelerometer *accel, int pos) +{ + char buf[BUFFER_SIZE]; + ssize_t ret; + + ret = iio_channel_attr_read(accel->channel_x, "raw", buf, BUFFER_SIZE); + if (ret < 0) { + iio_strerror(-ret, buf, BUFFER_SIZE); + fprintf(stderr, "%s\n", buf); + return false; + } + accel->capture_buffer.x[pos] = atoi(buf); + + ret = iio_channel_attr_read(accel->channel_y, "raw", buf, BUFFER_SIZE); + if (ret < 0) { + iio_strerror(-ret, buf, BUFFER_SIZE); + fprintf(stderr, "%s\n", buf); + return false; + } + accel->capture_buffer.y[pos] = atoi(buf); + return true; +} + +static void +smooth_input(struct accelerometer *accel, int pos, int values_count, int *x, int *y) +{ + int i; + + *x = accel->capture_buffer.x[pos]; + *y = accel->capture_buffer.y[pos]; + + if (values_count > 1) { + for (i = 1; i < values_count; ++i) { + *x += accel->capture_buffer.x[(pos - i) % values_count]; + *y += accel->capture_buffer.y[(pos - i) % values_count]; + } + + *x /= values_count; + *y /= values_count; + } +} + +static int +get_transform(int x, int y, int threshold) +{ + if (y > threshold && y > x) + return WESTON_ROTATER_TRANSFORM_NORMAL; + if (x > threshold && x > y) + return WESTON_ROTATER_TRANSFORM_90; + if (y < -threshold && y < x) + return WESTON_ROTATER_TRANSFORM_180; + if (x < -threshold && x < y) + return WESTON_ROTATER_TRANSFORM_270; + return -1; +} + +static bool +synchronize(struct wl_display *display, int sampling_frequency) +{ + if (wl_display_roundtrip(display) < 0) { + fprintf(stderr, "lost connection, aborting\n"); + return false; + } + usleep(1000 * sampling_frequency); + return true; +} + +static void +handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) +{ + static struct rotater_output *output; + + if (strcmp(interface, "wl_output") == 0) { + output = xmalloc(sizeof *output); + output->output = wl_registry_bind(registry, name, + &wl_output_interface, 1); + wl_list_insert(&output_list, &output->link); + } else if (strcmp(interface, "weston_rotater") == 0) { + rotater = wl_registry_bind(registry, name, + &weston_rotater_interface, + 1); + } +} + +static void +handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) +{ + /* XXX: unimplemented */ +} + +static const struct wl_registry_listener registry_listener = { + handle_global, + handle_global_remove +}; + +int main(int argc, char *argv[]) +{ + struct wl_display *display; + struct wl_registry *registry; + struct rotater_output *output; + struct config *config; + struct accelerometer *accel; + bool ret, found; + int frame = 0; + int x, y; + int transform, previous_transform, *allowed_transform; + + if (getenv("WAYLAND_SOCKET") == NULL) { + fprintf(stderr, "%s must be launched by weston.\n", + program_invocation_short_name); + return 1; + } + + display = wl_display_connect(NULL); + if (display == NULL) { + fprintf(stderr, "failed to create display: %m\n"); + return 2; + } + + wl_list_init(&output_list); + registry = wl_display_get_registry(display); + wl_registry_add_listener(registry, ®istry_listener, NULL); + wl_display_dispatch(display); + wl_display_roundtrip(display); + if (rotater == NULL) { + fprintf(stderr, "display doesn't support rotater\n"); + free(registry); + wl_display_disconnect(display); + return 3; + } + + config = read_configuration(); + if (!config) { + fprintf(stderr, "failed to read configuration\n"); + free(registry); + wl_display_disconnect(display); + return 4; + } + + accel = initialize_iio(config); + if (!accel) { + fprintf(stderr, "failed to initialize iio subsystem\n"); + free(registry); + wl_display_disconnect(display); + destroy_configuration(config); + return 5; + } + + previous_transform = WESTON_ROTATER_TRANSFORM_NORMAL; + for (;;) { + ret = capture_rotation(accel, frame); + if (!ret) { + fprintf(stderr, "failed to capture accelerometer " + "values\n"); + break; + } + + smooth_input(accel, frame, config->smooth_capture, &x, &y); + + transform = get_transform(x, y, config->threshold); + if (transform < 0) { + if (!synchronize(display, accel->sampling_frequency)) + return 6; + continue; + } + + found = false; + wl_array_for_each(allowed_transform, + &config->allowed_rotations) { + if (transform == *allowed_transform) { + found = true; + break; + } + } + + if (!found) { + if (!synchronize(display, accel->sampling_frequency)) + return 6; + continue; + } + + if (transform != previous_transform) { + wl_list_for_each(output, &output_list, link) { + weston_rotater_rotate(rotater, + output->output, + transform); + } + } + previous_transform = transform; + frame = (frame + 1) % config->smooth_capture; + + if (!synchronize(display, accel->sampling_frequency)) + return 6; + } + + wl_display_disconnect(display); + destroy_accelerometer(accel); + destroy_configuration(config); + + return 0; +} diff --git a/configure.ac b/configure.ac index e1300b4..61f9eba 100644 --- a/configure.ac +++ b/configure.ac @@ -384,6 +384,20 @@ if ! test "x$enable_simple_dmabuf_v4l_client" = "xno"; then fi AM_CONDITIONAL(BUILD_SIMPLE_DMABUF_V4L_CLIENT, test "x$enable_simple_dmabuf_v4l_client" = "xyes") +AC_ARG_ENABLE(weston-autorotater, + AS_HELP_STRING([--disable-weston-autorotater], + [do not build the autorotater client]),, + enable_autorotater="auto") +if ! test "x$enable_autorotater" = "xno"; then + PKG_CHECK_MODULES(LIBIIO, [libiio], + have_autorotater=yes, have_autorotater=no) + if test "x$have_autorotater" = "xno" -a "x$enable_autorotater" = "xyes"; then + AC_MSG_ERROR([autorotater client explicitly enabled, but libiio couldn't be found]) + fi + enable_autorotater="$have_autorotater" +fi +AM_CONDITIONAL(BUILD_AUTOROTATER, test "x$enable_autorotater" = "xyes") + AC_ARG_ENABLE(clients, [ --enable-clients],, enable_clients=yes) AM_CONDITIONAL(BUILD_CLIENTS, test x$enable_clients = xyes) if test x$enable_clients = xyes; then @@ -688,6 +702,7 @@ AC_MSG_RESULT([ Build EGL Clients ${have_cairo_egl} Build Simple Clients ${enable_simple_clients} Build Simple EGL Clients ${enable_simple_egl_clients} + Build Autorotater Client ${enable_autorotater} Install Demo Clients ${enable_demo_clients_install} diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c index 7d5bca9..c1750a7 100644 --- a/desktop-shell/shell.c +++ b/desktop-shell/shell.c @@ -6704,6 +6704,7 @@ module_init(struct weston_compositor *ec, wl_signal_add(&ec->seat_created_signal, &shell->seat_create_listener); screenshooter_create(ec); + rotater_create(ec); shell_add_bindings(ec, shell); diff --git a/protocol/weston-rotater.xml b/protocol/weston-rotater.xml new file mode 100644 index 0000000..90a6f84 --- /dev/null +++ b/protocol/weston-rotater.xml @@ -0,0 +1,26 @@ +<protocol name="weston_rotater"> + + <interface name="weston_rotater" version="1"> + + <enum name="transform"> + <description summary="copied from wl_output.transform"/> + <entry name="normal" value="0"/> + <entry name="90" value="1"/> + <entry name="180" value="2"/> + <entry name="270" value="3"/> + <entry name="flipped" value="4"/> + <entry name="flipped_90" value="5"/> + <entry name="flipped_180" value="6"/> + <entry name="flipped_270" value="7"/> + </enum> + + <request name="rotate"> + <arg name="output" type="object" interface="wl_output"/> + <arg name="transform" type="int" enum="transform"/> + </request> + + <event name="done"/> + + </interface> + +</protocol> diff --git a/src/compositor.h b/src/compositor.h index 0bbf458..9a211c1 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -1592,6 +1592,9 @@ int weston_screenshooter_shoot(struct weston_output *output, struct weston_buffer *buffer, weston_screenshooter_done_func_t done, void *data); +void +rotater_create(struct weston_compositor *ec); + struct clipboard * clipboard_create(struct weston_seat *seat); diff --git a/src/rotater.c b/src/rotater.c new file mode 100644 index 0000000..4575ac7 --- /dev/null +++ b/src/rotater.c @@ -0,0 +1,150 @@ +/* + * Copyright © 2016 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <linux/input-event-codes.h> + +#include "compositor.h" +#include "weston-rotater-server-protocol.h" +#include "shared/helpers.h" + +struct rotater { + struct weston_compositor *ec; + struct wl_global *global; + struct wl_client *client; + struct weston_process process; + struct wl_listener destroy_listener; +}; + +static void +rotater_rotate(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *output_resource, + int32_t transform) +{ + int32_t current_width; + struct weston_output *output = + wl_resource_get_user_data(output_resource); + + /* Only swap width and height when the aspect ratio changed. */ + if ((transform ^ output->transform) & 1) { + current_width = output->width; + output->width = output->height; + output->height = current_width; + } + + output->transform = transform; + output->dirty = 1; + weston_output_damage(output); +} + +struct weston_rotater_interface rotater_implementation = { + rotater_rotate +}; + +static void +bind_rotater(struct wl_client *client, + void *data, uint32_t version, uint32_t id) +{ + struct rotater *rotater = data; + struct wl_resource *resource; + + resource = wl_resource_create(client, + &weston_rotater_interface, 1, id); + + if (client != rotater->client) { + wl_resource_post_error(resource, + WL_DISPLAY_ERROR_INVALID_OBJECT, + "rotater failed: permission denied"); + return; + } + + wl_resource_set_implementation(resource, &rotater_implementation, + data, NULL); +} + +static void +rotater_sigchld(struct weston_process *process, int status) +{ + struct rotater *rotater = + container_of(process, struct rotater, process); + + rotater->client = NULL; +} + +static void +rotater_launch_autorotater(struct rotater *rotater) +{ + char *rotater_exe; + int ret; + + ret = asprintf(&rotater_exe, "%s/%s", + weston_config_get_libexec_dir(), + "/weston-autorotater"); + if (ret < 0) { + weston_log("Could not construct rotater path.\n"); + return; + } + + if (!rotater->client) + rotater->client = weston_client_launch(rotater->ec, + &rotater->process, + rotater_exe, rotater_sigchld); + free(rotater_exe); +} + +static void +rotater_destroy(struct wl_listener *listener, void *data) +{ + struct rotater *rotater = + container_of(listener, struct rotater, destroy_listener); + + wl_global_destroy(rotater->global); + free(rotater); +} + +WL_EXPORT void +rotater_create(struct weston_compositor *ec) +{ + struct rotater *rotater; + + rotater = malloc(sizeof *rotater); + if (rotater == NULL) + return; + + rotater->ec = ec; + rotater->client = NULL; + + rotater->global = wl_global_create(ec->wl_display, + &weston_rotater_interface, 1, + rotater, bind_rotater); + rotater->destroy_listener.notify = rotater_destroy; + wl_signal_add(&ec->destroy_signal, &rotater->destroy_listener); + rotater_launch_autorotater(rotater); +} -- 2.8.2 _______________________________________________ wayland-devel mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/wayland-devel
