// This is for asprintf support. // TODO: Don't use asprintf and remove this. #define _GNU_SOURCE #include #include #include #include #include #include "accel_monitor.h" #include "device_information.h" #include "output_manager.h" #include "usb_device_detection.h" static struct device_information get_device_info_for_name( char const* const device_name) { if (strcmp(device_name, "generic") == 0) { fprintf( stderr, "WARNING: Using a 'generic' device. This may not work properly.\n"); return DEVICE_INFORMATION_GENERIC; } static size_t const device_info_count = sizeof(DEVICE_INFOS) / sizeof(*DEVICE_INFOS); for (size_t i = 0; i < device_info_count; ++i) { struct device_information current = DEVICE_INFOS[i]; if (strcmp(current.device_name, device_name) == 0) return current; } fprintf(stderr, "No matching configuration for '%s'. Using the generic " "configuration.\n", device_name); return DEVICE_INFORMATION_GENERIC; } static char* get_hook_path(char const* const hook_name) { char* path; char* xdg_config_dir = getenv("XDG_CONFIG_HOME"); if (xdg_config_dir) asprintf( &path, "%s/convertablet/hooks/%s-hook", xdg_config_dir, hook_name); else asprintf(&path, "%s/.config/convertablet/hooks/%s-hook", getenv("HOME"), hook_name); return path; } static void on_basestation_connected() { printf("Basestation connected\n"); char* hook_path = get_hook_path("basestation-connected"); if (access(hook_path, X_OK) != 0) { fprintf(stderr, "Couldn't read or execute hook at %s\n", hook_path); free(hook_path); return; } pid_t pid = fork(); if (pid < 0) { // Error fprintf(stderr, "Unable to run hook script!\n"); perror("fork"); } else if (pid == 0) { // In the child char* argv[] = {hook_path, 0}; execve(hook_path, argv, environ); exit(-1); } else { free(hook_path); } } static void on_basestation_disconnected() { printf("Basestation disconnected\n"); char* hook_path = get_hook_path("basestation-disconnected"); if (access(hook_path, X_OK) != 0) { // Hook is innaccessible fprintf(stderr, "Couldn't read or execute hook at %s\n", hook_path); free(hook_path); return; } pid_t pid = fork(); if (pid < 0) { // Error fprintf(stderr, "Unable to run hook script!\n"); perror("fork"); } else if (pid == 0) { // In the child char* argv[] = {hook_path, 0}; execve(hook_path, argv, environ); exit(-1); } else { free(hook_path); } } static enum accel_rotation last_accel_rotation = ACCEL_ROTATION_NO_CHANGE; int main(int argc, char** argv) { if (argc < 2) { fprintf(stderr, "No device specified! Please specify your device's name or use " "'generic' for sane defaults.\n"); fprintf(stderr, "\tUsage: %s \n", argv[0]); fprintf(stderr, "\t %s list-devices \n", argv[0]); return -1; } if (strcmp(argv[1], "list-devices") == 0) { printf("Device names:\n"); static size_t const device_info_count = sizeof(DEVICE_INFOS) / sizeof(*DEVICE_INFOS); for (size_t i = 0; i < device_info_count; ++i) printf("\t- %s\n", DEVICE_INFOS[i].device_name); return 0; } struct device_information device_info = get_device_info_for_name(argv[1]); struct accel_monitor* monitor = 0; struct convertablet_wlr_state* state = 0; if (device_info.screen_rotation_supported) { monitor = accel_start_monitor(device_info.target_motion_sensor); if (!monitor) { fprintf(stderr, "Failed to start iio motion monitoring\n"); return -1; } state = convertablet_wlr_init(); if (!state) { fprintf(stderr, "Failed to connect to wayland server.\n"); return -1; } } else fprintf(stderr, "Screen rotation not supported.\n"); struct usb_detector* base_station_detector = 0; if (device_info.basestation_detection_supported) { base_station_detector = create_detector(device_info.basestation_vendor_id, device_info.basestation_product_id); if (!base_station_detector) { fprintf(stderr, "Failed to create basestation detector.\n"); return -1; } base_station_detector->on_connected = &on_basestation_connected; base_station_detector->on_disconnected = &on_basestation_disconnected; base_station_detector->has_hotplug_callbacks = true; } else fprintf(stderr, "Base station detection not supported.\n"); for (;;) { // Main loop that handles the rotation detection. // If screen rotation isn't supported, let's just do nothing. if (!device_info.screen_rotation_supported) continue; thrd_sleep(&(struct timespec){0, 10000000UL}, NULL); if (!monitor->data_is_ready) continue; if (base_station_detector && base_station_detector->is_connected) { if (last_accel_rotation != ACCEL_ROTATION_0DEG) { convertablet_wlr_head_set_transform( state, device_info.target_output, device_info.sensor_to_output_rotation[ACCEL_ROTATION_0DEG]); last_accel_rotation = ACCEL_ROTATION_0DEG; } continue; } enum accel_rotation rotation = accel_get_current_rotation(monitor); if (rotation == ACCEL_ROTATION_NO_CHANGE || rotation == last_accel_rotation) continue; convertablet_wlr_head_set_transform( state, device_info.target_output, device_info.sensor_to_output_rotation[rotation]); last_accel_rotation = rotation; } if (device_info.screen_rotation_supported) { accel_stop_monitor(monitor); convertablet_wlr_cleanup(state); } if (device_info.basestation_detection_supported) destroy_detector(base_station_detector); }