commit 3fa9c8d193294665de03f5dae765ea6bb74cdd7a Author: Yuki Joou Date: Tue Jul 11 17:55:43 2023 +0200 Initial commit: Added current work diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..1abf07c --- /dev/null +++ b/.clang-format @@ -0,0 +1,25 @@ +BasedOnStyle: Microsoft + +UseTab: ForIndentation +TabWidth: 4 +BreakBeforeBraces: Linux +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: WithoutElse +PointerAlignment: Left + +AlignAfterOpenBracket: Align +BinPackArguments: false +BinPackParameters: false + + +AlignArrayOfStructures: Right +AlignArrayOfStructures: Right +AlignConsecutiveAssignments: Consecutive +AlignConsecutiveBitFields: Consecutive +AlignConsecutiveDeclarations: Consecutive +AlignConsecutiveMacros: Consecutive +AlignEscapedNewlines: Right +AlignOperands: AlignAfterOperator +AlignTrailingComments: true + +ColumnLimit: 80 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e198566 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +protocol/*.c +protocol/*.h +protocol/*.xml +*~ +miix-wlr \ No newline at end of file diff --git a/accel_monitor.c b/accel_monitor.c new file mode 100644 index 0000000..5c8928d --- /dev/null +++ b/accel_monitor.c @@ -0,0 +1,162 @@ +#include "accel_monitor.h" + +#include +#include +#include + +static char* sensor_get_axis_path(char const* const base, char axis) +{ + // TODO: Be smarter about the buffer size + char buffer[128]; + snprintf(buffer, sizeof(buffer), "%s/in_accel_%c_raw", base, axis); + return strdup(buffer); +} + +static int monitor_thread(void* data) +{ + struct accel_monitor* monitor = data; + + while (!monitor->monitoring_thread_is_ready) + ; + + char* x_axis_path = sensor_get_axis_path(monitor->sysfs_device_path, 'x'); + FILE* x_axis_fp = fopen(x_axis_path, "r"); + free(x_axis_path); + + char* y_axis_path = sensor_get_axis_path(monitor->sysfs_device_path, 'y'); + FILE* y_axis_fp = fopen(y_axis_path, "r"); + free(y_axis_path); + + char* z_axis_path = sensor_get_axis_path(monitor->sysfs_device_path, 'z'); + FILE* z_axis_fp = fopen(z_axis_path, "r"); + free(z_axis_path); + + while (!monitor->monitoring_thread_should_exit) { + fseek(x_axis_fp, 0, SEEK_SET); + fseek(y_axis_fp, 0, SEEK_SET); + fseek(z_axis_fp, 0, SEEK_SET); + + int16_t x_axis; + int16_t y_axis; + int16_t z_axis; + + fscanf(x_axis_fp, "%hd\n", &x_axis); + fscanf(y_axis_fp, "%hd\n", &y_axis); + fscanf(z_axis_fp, "%hd\n", &z_axis); + + if (mtx_lock(&monitor->current_data_lock) != thrd_success) { + fprintf(stderr, + "Can't lock data from data monitoring thread. Something is " + "going wrong. Giving up on monitoring sensor data.\n"); + break; + } + + monitor->current_data.x = x_axis; + monitor->current_data.y = y_axis; + monitor->current_data.z = z_axis; + + if (mtx_unlock(&monitor->current_data_lock) != thrd_success) { + fprintf(stderr, + "Can't unlock data from data monitoring thread. Something " + "is going wrong. Giving up on monitoring sensor data.\n"); + break; + } + + monitor->data_is_ready = true; + } + + fclose(x_axis_fp); + fclose(y_axis_fp); + fclose(z_axis_fp); + + return 0; +} + +struct accel_monitor* accel_start_monitor( + char const* const sysfs_device_path) +{ + mtx_t current_data_lock; + if (mtx_init(¤t_data_lock, mtx_plain) != thrd_success) return 0; + + if (mtx_lock(¤t_data_lock) != thrd_success) return 0; + + struct accel_monitor* monitor = calloc(1, sizeof(struct accel_monitor)); + + monitor->monitoring_thread_is_ready = false; + monitor->data_is_ready = false; + + thrd_t monitoring_thread; + if (thrd_create(&monitoring_thread, &monitor_thread, monitor) != + thrd_success) { + free(monitor); + return 0; + } + + monitor->sysfs_device_path = sysfs_device_path; + monitor->monitoring_thread = monitoring_thread; + monitor->current_data_lock = current_data_lock; + + monitor->current_data.x = 0; + monitor->current_data.y = 0; + monitor->current_data.z = 0; + + monitor->monitoring_thread_should_exit = false; + + if (mtx_unlock(&monitor->current_data_lock) != thrd_success) + fprintf(stderr, + "Unable to unlock the data after initialisation. We are in UB " + "territory. Something must've went very wrong\n"); + + monitor->monitoring_thread_is_ready = true; + + return monitor; +} + +static bool axis_is_near_0(int16_t axis) +{ + return (axis > -500) && (axis < 500); +} + +static bool axis_is_mostly_positive(int16_t axis) +{ + return axis >= 500; +} + +static bool axis_is_mostly_negative(int16_t axis) +{ + return axis <= -500; +} + +enum accel_rotation accel_get_current_rotation( + struct accel_monitor* monitor) +{ + if (mtx_lock(&monitor->current_data_lock) != thrd_success) + return ACCEL_ROTATION_NO_CHANGE; + struct accel_data data = monitor->current_data; + if (mtx_unlock(&monitor->current_data_lock) != thrd_success) + fprintf(stderr, "Can't unlock data, this is going to cause issues.\n"); + + if (axis_is_near_0(data.x)) { + if (axis_is_mostly_positive(data.y)) return ACCEL_ROTATION_0DEG; + if (axis_is_mostly_negative(data.y)) return ACCEL_ROTATION_180DEG; + } + + if (axis_is_mostly_positive(data.x)) return ACCEL_ROTATION_90DEG; + + if (axis_is_mostly_negative(data.x)) return ACCEL_ROTATION_270DEG; + + return ACCEL_ROTATION_NO_CHANGE; +} + +void accel_stop_monitor(struct accel_monitor* monitor) +{ + monitor->monitoring_thread_should_exit = true; + + if (thrd_join(monitor->monitoring_thread, NULL) != thrd_success) + fprintf(stderr, + "Can't join monitoring thread. The program may not exit " + "cleanly.\n"); + + mtx_destroy(&monitor->current_data_lock); + free(monitor); +} diff --git a/accel_monitor.h b/accel_monitor.h new file mode 100644 index 0000000..9f84053 --- /dev/null +++ b/accel_monitor.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +struct accel_data { + int16_t x; + int16_t y; + int16_t z; +}; + +enum accel_rotation : uint8_t { + ACCEL_ROTATION_0DEG = 0, + ACCEL_ROTATION_90DEG, + ACCEL_ROTATION_180DEG, + ACCEL_ROTATION_270DEG, + ACCEL_ROTATION_NO_CHANGE, +}; + +struct accel_monitor { + atomic_bool monitoring_thread_should_exit; + atomic_bool monitoring_thread_is_ready; + thrd_t monitoring_thread; + + char const* sysfs_device_path; + + mtx_t current_data_lock; + struct accel_data current_data; + atomic_bool data_is_ready; +}; + +struct accel_monitor* accel_start_monitor( + char const* const sysfs_device_path); +enum accel_rotation accel_get_current_rotation(struct accel_monitor*); +void accel_stop_monitor(struct accel_monitor*); diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..9eda679 --- /dev/null +++ b/build.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +set -ex; + +SOURCES="main.c output_manager.c accel_monitor.c" +CFLAGS="-std=c2x -Wall -Wextra -pedantic $CFLAGS" +LIBS="-lwayland-client protocol/wlr-output-management-unstable-v1.c" +OUTPUT=miix-wlr + +clang $CFLAGS $LIBS $SOURCES -o $OUTPUT diff --git a/main.c b/main.c new file mode 100644 index 0000000..0b99d32 --- /dev/null +++ b/main.c @@ -0,0 +1,45 @@ +#include +#include + +#include "output_manager.h" + +#include "accel_monitor.h" + +static enum wl_output_transform const SENSOR_TO_OUTPUT_ROTATION[] = { + [ACCEL_ROTATION_0DEG] = WL_OUTPUT_TRANSFORM_270, + [ACCEL_ROTATION_90DEG] = WL_OUTPUT_TRANSFORM_NORMAL, + [ACCEL_ROTATION_180DEG] = WL_OUTPUT_TRANSFORM_90, + [ACCEL_ROTATION_270DEG] = WL_OUTPUT_TRANSFORM_180, +}; + +static char const* const DEFAULT_OUTPUT = "DSI-1"; +static char const* const DEFAULT_MOTION_SENSOR = + "/sys/bus/iio/devices/iio:device0"; + +static enum accel_rotation last_accel_rotation = ACCEL_ROTATION_NO_CHANGE; + +int main(int, char**) +{ + struct accel_monitor* monitor = + accel_start_monitor(DEFAULT_MOTION_SENSOR); + + if (!monitor) { + fprintf(stderr, "Failed to start iio motion monitoring\n"); + return -1; + } + + struct miix_wlr_state* state = miix_wlr_init(); + + for (;;) { + if (!monitor->data_is_ready) continue; + + enum accel_rotation rotation = accel_get_current_rotation(monitor); + if (rotation == ACCEL_ROTATION_NO_CHANGE || rotation == last_accel_rotation) continue; + miix_wlr_head_set_transform( + state, DEFAULT_OUTPUT, SENSOR_TO_OUTPUT_ROTATION[rotation]); + last_accel_rotation = rotation; + } + + accel_stop_monitor(monitor); + miix_wlr_cleanup(state); +} diff --git a/output_manager.c b/output_manager.c new file mode 100644 index 0000000..120b6c6 --- /dev/null +++ b/output_manager.c @@ -0,0 +1,311 @@ +// NOTE: Most of this code was stolen from wlr-randr, available at +// https://git.sr.ht/~emersion/wlr-randr/tree/master/item/main.c + +#include "output_manager.h" + +#include + +#include +#include +#include + +#include "protocol/wlr-output-management-unstable-v1.h" + +static void head_handle_name(void* data, + struct zwlr_output_head_v1*, + const char* name) +{ + struct miix_wlr_head* head = data; + head->name = strdup(name); +} + +static void head_handle_description(void*, + struct zwlr_output_head_v1*, + const char*) +{ + // NO-OP +} + +static void head_handle_physical_size(void*, + struct zwlr_output_head_v1*, + int32_t, + int32_t) +{ + // NO-OP +} + +static void head_handle_mode(void*, + struct zwlr_output_head_v1*, + struct zwlr_output_mode_v1*) +{ + // NO-OP +} + +static void head_handle_enabled(void*, struct zwlr_output_head_v1*, int32_t) +{ + // NO-OP +} + +static void head_handle_current_mode(void*, + struct zwlr_output_head_v1*, + struct zwlr_output_mode_v1*) +{ + // NO-OP +} + +static void head_handle_position(void*, + struct zwlr_output_head_v1*, + int32_t, + int32_t) +{ + // NO-OP +} + +static void head_handle_transform(void*, struct zwlr_output_head_v1*, int32_t) +{ + // NO-OP +} + +static void head_handle_scale(void*, struct zwlr_output_head_v1*, wl_fixed_t) +{ + // NO-OP +} + +static void head_handle_finished(void*, struct zwlr_output_head_v1*) +{ + // NO-OP +} + +static void head_handle_make(void*, struct zwlr_output_head_v1*, const char*) +{ + // NO-OP +} + +static void head_handle_model(void*, struct zwlr_output_head_v1*, const char*) +{ + // NO-OP +} + +static void head_handle_serial_number(void*, + struct zwlr_output_head_v1*, + const char*) +{ + // NO-OP +} + +static void head_handle_adaptive_sync(void*, + struct zwlr_output_head_v1*, + uint32_t) +{ + // NO-OP +} + +static const struct zwlr_output_head_v1_listener head_listener = { + .name = head_handle_name, + .description = head_handle_description, + .physical_size = head_handle_physical_size, + .mode = head_handle_mode, + .enabled = head_handle_enabled, + .current_mode = head_handle_current_mode, + .position = head_handle_position, + .transform = head_handle_transform, + .scale = head_handle_scale, + .finished = head_handle_finished, + .make = head_handle_make, + .model = head_handle_model, + .serial_number = head_handle_serial_number, + .adaptive_sync = head_handle_adaptive_sync, +}; + +static void output_manager_handle_head(void* data, + struct zwlr_output_manager_v1*, + struct zwlr_output_head_v1* wlr_head) +{ + struct miix_wlr_state* state = data; + + struct miix_wlr_head* head = calloc(1, sizeof(*head)); + head->wlr_head = wlr_head; + wl_list_insert(state->heads.prev, &head->link); + zwlr_output_head_v1_add_listener(wlr_head, &head_listener, head); +} + +static void output_manager_handle_done(void* data, + struct zwlr_output_manager_v1*, + uint32_t serial) +{ + struct miix_wlr_state* state = data; + state->serial = serial; +} + +static void output_manager_handle_finished(void*, + struct zwlr_output_manager_v1*) +{ + // NO-OP +} + +static const struct zwlr_output_manager_v1_listener output_manager_listener = { + .head = output_manager_handle_head, + .done = output_manager_handle_done, + .finished = output_manager_handle_finished, +}; + +static void registry_handle_global(void* data, + struct wl_registry* registry, + uint32_t name, + const char* interface, + uint32_t) +{ + struct miix_wlr_state* state = data; + + if (strcmp(interface, zwlr_output_manager_v1_interface.name) == 0) { + // TODO: Don't always bind version 4? + state->output_manager = wl_registry_bind( + registry, name, &zwlr_output_manager_v1_interface, 4); + zwlr_output_manager_v1_add_listener( + state->output_manager, &output_manager_listener, state); + } +} + +static void registry_handle_global_remove(void*, struct wl_registry*, uint32_t) +{ + // NO-OP +} + +static void config_handle_succeeded(void* data, + struct zwlr_output_configuration_v1* config) +{ + struct miix_wlr_state* state = data; + state->is_working = false; + zwlr_output_configuration_v1_destroy(config); + printf("Applied configuration successfully!\n"); +} + +static void config_handle_failed(void* data, + struct zwlr_output_configuration_v1* config) +{ + struct miix_wlr_state* state = data; + state->is_working = false; + zwlr_output_configuration_v1_destroy(config); + fprintf(stderr, "Failed to apply configuration\n"); +} + +static void config_handle_cancelled(void* data, + struct zwlr_output_configuration_v1* config) +{ + struct miix_wlr_state* state = data; + state->is_working = false; + zwlr_output_configuration_v1_destroy(config); + fprintf(stderr, "Configuration cancelled\n"); +} + +static const struct zwlr_output_configuration_v1_listener config_listener = { + .succeeded = config_handle_succeeded, + .failed = config_handle_failed, + .cancelled = config_handle_cancelled, +}; + +struct miix_wlr_state* miix_wlr_init() +{ + struct wl_display* display = wl_display_connect(NULL); + if (!display) { + fprintf(stderr, "Couldn't connect to display\n"); + return 0; + } + + struct wl_registry* registry = wl_display_get_registry(display); + if (!registry) { + fprintf(stderr, "Couldn't get display registry\n"); + return 0; + } + + struct miix_wlr_state* state = calloc(1, sizeof(struct miix_wlr_state)); + + state->is_working = false; + state->display = display; + + wl_list_init(&state->heads); + + static const struct wl_registry_listener registry_listener = { + .global = registry_handle_global, + .global_remove = registry_handle_global_remove, + }; + wl_registry_add_listener(registry, ®istry_listener, state); + + wl_display_dispatch(display); + wl_display_roundtrip(display); + wl_registry_destroy(registry); + + if (!state->output_manager) { + fprintf(stderr, + "Couldn't get output manager. Does your compositor support " + "wlr-outpput-management-unstable-v1?\n"); + return 0; + } + + return state; +} + +char** miix_wlr_get_head_names(struct miix_wlr_state const* state) +{ + int head_count = wl_list_length(&state->heads); + char** head_name_list = calloc(head_count + 1, sizeof(char*)); + size_t head_index = 0; + + struct miix_wlr_head *head, *tmp_head; + wl_list_for_each_safe(head, tmp_head, &state->heads, link) + { + head_name_list[head_index] = head->name; + ++head_index; + } + + head_name_list[head_index] = 0; + return head_name_list; +} + +void miix_wlr_head_set_transform(struct miix_wlr_state* state, + char const* const head_name, + enum wl_output_transform transform) +{ + struct zwlr_output_configuration_v1* output_config = + zwlr_output_manager_v1_create_configuration(state->output_manager, + state->serial); + + zwlr_output_configuration_v1_add_listener( + output_config, &config_listener, state); + + struct miix_wlr_head* head; + struct miix_wlr_head* tmp_head; + wl_list_for_each_safe(head, tmp_head, &state->heads, link) + { + if (strcmp(head->name, head_name) != 0) continue; + + printf("Found head %s\n", head->name); + + struct zwlr_output_configuration_head_v1* config_head = + zwlr_output_configuration_v1_enable_head(output_config, + head->wlr_head); + + state->is_working = true; + + zwlr_output_configuration_head_v1_set_transform(config_head, transform); + zwlr_output_configuration_v1_apply(output_config); + } + + while (state->is_working && wl_display_dispatch(state->display) > 0) + ; +} + +void miix_wlr_cleanup(struct miix_wlr_state* state) +{ + struct miix_wlr_head* head; + struct miix_wlr_head* tmp_head; + wl_list_for_each_safe(head, tmp_head, &state->heads, link) + { + zwlr_output_head_v1_destroy(head->wlr_head); + free(head->name); + free(head); + } + + zwlr_output_manager_v1_destroy(state->output_manager); + wl_display_disconnect(state->display); +} diff --git a/output_manager.h b/output_manager.h new file mode 100644 index 0000000..97ae92d --- /dev/null +++ b/output_manager.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include + +#include "protocol/wlr-output-management-unstable-v1.h" + +struct miix_wlr_head { + struct zwlr_output_head_v1* wlr_head; + char* name; + struct wl_list link; +}; + +struct miix_wlr_state { + bool is_working; + + struct zwlr_output_manager_v1* output_manager; + struct wl_display* display; + uint32_t serial; + + struct wl_list heads; // struct miix_wlr_head +}; + +struct miix_wlr_state* miix_wlr_init(); + +char** miix_wlr_get_head_names(struct miix_wlr_state const*); +void miix_wlr_head_set_transform(struct miix_wlr_state*, + char const* const head_name, + enum wl_output_transform); + +void miix_wlr_cleanup(struct miix_wlr_state*); diff --git a/protocol/codegen.sh b/protocol/codegen.sh new file mode 100755 index 0000000..2532eb8 --- /dev/null +++ b/protocol/codegen.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +PROTO_NAME=wlr-output-management-unstable-v1 + +wayland-scanner private-code $PROTO_NAME.xml $PROTO_NAME.c +wayland-scanner client-header $PROTO_NAME.xml $PROTO_NAME.h diff --git a/protocol/download_protocols.sh b/protocol/download_protocols.sh new file mode 100755 index 0000000..a8ffe96 --- /dev/null +++ b/protocol/download_protocols.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +URL="https://gitlab.freedesktop.org/wlroots/wlr-protocols/-/raw/master/unstable/wlr-output-management-unstable-v1.xml" + +curl $URL > wlr-output-management-unstable-v1.xml