Compare commits
5 commits
e1ed4a7198
...
8648915028
Author | SHA1 | Date | |
---|---|---|---|
|
8648915028 | ||
|
e40ed731bf | ||
|
bd117adaca | ||
|
42c8902fa8 | ||
|
9856450db7 |
4 changed files with 191 additions and 37 deletions
|
@ -12,8 +12,6 @@ BinPackArguments: false
|
||||||
BinPackParameters: false
|
BinPackParameters: false
|
||||||
|
|
||||||
|
|
||||||
AlignArrayOfStructures: Right
|
|
||||||
AlignArrayOfStructures: Right
|
|
||||||
AlignConsecutiveAssignments: Consecutive
|
AlignConsecutiveAssignments: Consecutive
|
||||||
AlignConsecutiveBitFields: Consecutive
|
AlignConsecutiveBitFields: Consecutive
|
||||||
AlignConsecutiveDeclarations: Consecutive
|
AlignConsecutiveDeclarations: Consecutive
|
||||||
|
|
35
README.md
35
README.md
|
@ -5,9 +5,9 @@ Support for tablet-laptop convertible on wlroots-based compositors.
|
||||||
This project includes both a daemon for handling screen rotation and docking,
|
This project includes both a daemon for handling screen rotation and docking,
|
||||||
and some configuration for other software
|
and some configuration for other software
|
||||||
|
|
||||||
## Installation
|
## Daemon
|
||||||
|
|
||||||
### Daemon
|
### Installation
|
||||||
|
|
||||||
1. Get the code
|
1. Get the code
|
||||||
|
|
||||||
|
@ -33,12 +33,19 @@ cd ../
|
||||||
4. Run it to try it out
|
4. Run it to try it out
|
||||||
|
|
||||||
```
|
```
|
||||||
./convertablet
|
./convertablet generic
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This will run the daemon in 'generic' mode. This assumes "sane defaults" for
|
||||||
|
your display's name and motion sensor. It's likely your device isn't using
|
||||||
|
those.
|
||||||
|
|
||||||
|
You can check the list of supported devices with `./convertablet list-devices`,
|
||||||
|
and load that specific device's config with `./convertablet <device name here>`.
|
||||||
|
|
||||||
5. Copy it to /usr/local/bin & make it run at startup
|
5. Copy it to /usr/local/bin & make it run at startup
|
||||||
|
|
||||||
### Daemon configuration
|
### Configuration
|
||||||
|
|
||||||
You can create scripts at $HOME/.config/convertablet/hooks that will trigger on
|
You can create scripts at $HOME/.config/convertablet/hooks that will trigger on
|
||||||
events
|
events
|
||||||
|
@ -46,6 +53,24 @@ events
|
||||||
- Basestation disconnected: basestation-disconnected-hook
|
- Basestation disconnected: basestation-disconnected-hook
|
||||||
- Basestation connected: basestation-connected-hook
|
- Basestation connected: basestation-connected-hook
|
||||||
|
|
||||||
### Configurations
|
### Device compatibility
|
||||||
|
|
||||||
|
| Product name | Device name | Notes |
|
||||||
|
|---------------------------------------|-------------------------|----------------------------------------------|
|
||||||
|
| Lenovo Miix 320-10icr | `lenovo-miix-320-10icr` | |
|
||||||
|
| Samsung Chromebook Pro XE510C24-K01US | `google-caroline` | Thanks to nothingneko/@jaiden@ordinary.cafe! |
|
||||||
|
|
||||||
|
## Using fcitx hooks
|
||||||
|
|
||||||
|
If you're using fcitx to get fancy input methods working, on-screen keyboards
|
||||||
|
will not work, because on wayland, only one program can detect input begining
|
||||||
|
events. To work around this issue, you can use the `fcitx5-hooks` module I wrote
|
||||||
|
to run a script when an input field is focused/unfocused. See instructions in
|
||||||
|
[fcitx5-hooks/](./fcitx5-hooks/).
|
||||||
|
|
||||||
|
## 3rd party programs Configurations
|
||||||
|
|
||||||
Still a work in progress...
|
Still a work in progress...
|
||||||
|
|
||||||
|
In the meantime, you can find some example hooks for the convertablet daemon and
|
||||||
|
waybar in [configs/](./configs/).
|
||||||
|
|
71
daemon/device_information.h
Normal file
71
daemon/device_information.h
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct device_information {
|
||||||
|
char const* const device_name;
|
||||||
|
|
||||||
|
bool screen_rotation_supported;
|
||||||
|
char const* const target_output;
|
||||||
|
char const* const target_motion_sensor;
|
||||||
|
enum wl_output_transform const sensor_to_output_rotation[4];
|
||||||
|
|
||||||
|
bool basestation_detection_supported;
|
||||||
|
uint16_t const basestation_vendor_id;
|
||||||
|
uint16_t const basestation_product_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct device_information DEVICE_INFORMATION_GENERIC = {
|
||||||
|
.device_name = "generic",
|
||||||
|
.screen_rotation_supported = true,
|
||||||
|
.target_output = "eDP-1",
|
||||||
|
.target_motion_sensor = "/sys/bus/iio/devices/iio:device0",
|
||||||
|
.sensor_to_output_rotation =
|
||||||
|
{
|
||||||
|
[ACCEL_ROTATION_0DEG] = WL_OUTPUT_TRANSFORM_NORMAL,
|
||||||
|
[ACCEL_ROTATION_90DEG] = WL_OUTPUT_TRANSFORM_90,
|
||||||
|
[ACCEL_ROTATION_180DEG] = WL_OUTPUT_TRANSFORM_180,
|
||||||
|
[ACCEL_ROTATION_270DEG] = WL_OUTPUT_TRANSFORM_270,
|
||||||
|
},
|
||||||
|
.basestation_detection_supported = false,
|
||||||
|
.basestation_vendor_id = 0,
|
||||||
|
.basestation_product_id = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct device_information DEVICE_INFOS[] = {
|
||||||
|
{
|
||||||
|
// Lenovo Miix 320-10icr
|
||||||
|
// https://support.lenovo.com/us/en/solutions/pd104871-product-overview-miix-320-10icr
|
||||||
|
.device_name = "lenovo-miix-320-10icr",
|
||||||
|
.screen_rotation_supported = true,
|
||||||
|
.target_output = "DSI-1",
|
||||||
|
.target_motion_sensor = "/sys/bus/iio/devices/iio:device0",
|
||||||
|
.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,
|
||||||
|
},
|
||||||
|
.basestation_detection_supported = true,
|
||||||
|
.basestation_vendor_id = 0x048d,
|
||||||
|
.basestation_product_id = 0x8911,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Samsung Chromebook Pro
|
||||||
|
// https://www.samsung.com/us/computing/chromebooks/12-14/samsung-chromebook-pro-xe510c24-k01us/
|
||||||
|
.device_name = "google-caroline",
|
||||||
|
.screen_rotation_supported = true,
|
||||||
|
.target_output = "eDP-1",
|
||||||
|
.target_motion_sensor = "/sys/bus/iio/devices/iio:device1",
|
||||||
|
.sensor_to_output_rotation =
|
||||||
|
{
|
||||||
|
[ACCEL_ROTATION_0DEG] = WL_OUTPUT_TRANSFORM_NORAML,
|
||||||
|
[ACCEL_ROTATION_90DEG] = WL_OUTPUT_TRANSFORM_270,
|
||||||
|
[ACCEL_ROTATION_180DEG] = WL_OUTPUT_TRANSFORM_180,
|
||||||
|
[ACCEL_ROTATION_270DEG] = WL_OUTPUT_TRANSFORM_90,
|
||||||
|
},
|
||||||
|
.basestation_detection_supported = false,
|
||||||
|
.basestation_vendor_id = 0,
|
||||||
|
.basestation_product_id = 0,
|
||||||
|
},
|
||||||
|
};
|
120
daemon/main.c
120
daemon/main.c
|
@ -4,34 +4,48 @@
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <libusb-1.0/libusb.h>
|
#include <libusb-1.0/libusb.h>
|
||||||
|
|
||||||
#include "accel_monitor.h"
|
#include "accel_monitor.h"
|
||||||
|
#include "device_information.h"
|
||||||
#include "output_manager.h"
|
#include "output_manager.h"
|
||||||
#include "usb_device_detection.h"
|
#include "usb_device_detection.h"
|
||||||
|
|
||||||
static enum wl_output_transform const SENSOR_TO_OUTPUT_ROTATION[] = {
|
static struct device_information get_device_info_for_name(
|
||||||
[ACCEL_ROTATION_0DEG] = WL_OUTPUT_TRANSFORM_270,
|
char const* const device_name)
|
||||||
[ACCEL_ROTATION_90DEG] = WL_OUTPUT_TRANSFORM_NORMAL,
|
{
|
||||||
[ACCEL_ROTATION_180DEG] = WL_OUTPUT_TRANSFORM_90,
|
if (strcmp(device_name, "generic") == 0) {
|
||||||
[ACCEL_ROTATION_270DEG] = WL_OUTPUT_TRANSFORM_180,
|
fprintf(
|
||||||
};
|
stderr,
|
||||||
|
"WARNING: Using a 'generic' device. This may not work properly.\n");
|
||||||
|
return DEVICE_INFORMATION_GENERIC;
|
||||||
|
}
|
||||||
|
|
||||||
static char const* const DEFAULT_OUTPUT = "DSI-1";
|
static size_t const device_info_count =
|
||||||
static char const* const DEFAULT_MOTION_SENSOR =
|
sizeof(DEVICE_INFOS) / sizeof(*DEVICE_INFOS);
|
||||||
"/sys/bus/iio/devices/iio:device0";
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
static uint16_t const BASESTATION_VENDOR_ID = 0x048d;
|
fprintf(stderr,
|
||||||
static uint16_t const BASESTATION_PRODUCT_ID = 0x8911;
|
"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)
|
static char* get_hook_path(char const* const hook_name)
|
||||||
{
|
{
|
||||||
char* path;
|
char* path;
|
||||||
char* xdg_config_dir = getenv("XDG_CONFIG_HOME");
|
char* xdg_config_dir = getenv("XDG_CONFIG_HOME");
|
||||||
if (xdg_config_dir)
|
if (xdg_config_dir)
|
||||||
asprintf(&path, "%s/convertablet/hooks/%s-hook", xdg_config_dir, hook_name);
|
asprintf(
|
||||||
|
&path, "%s/convertablet/hooks/%s-hook", xdg_config_dir, hook_name);
|
||||||
else
|
else
|
||||||
asprintf(&path,
|
asprintf(&path,
|
||||||
"%s/.config/convertablet/hooks/%s-hook",
|
"%s/.config/convertablet/hooks/%s-hook",
|
||||||
|
@ -99,25 +113,65 @@ static void on_basestation_disconnected()
|
||||||
|
|
||||||
static enum accel_rotation last_accel_rotation = ACCEL_ROTATION_NO_CHANGE;
|
static enum accel_rotation last_accel_rotation = ACCEL_ROTATION_NO_CHANGE;
|
||||||
|
|
||||||
int main(int, char**)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
struct accel_monitor* monitor = accel_start_monitor(DEFAULT_MOTION_SENSOR);
|
if (argc < 2) {
|
||||||
|
fprintf(stderr,
|
||||||
if (!monitor) {
|
"No device specified! Please specify your device's name or use "
|
||||||
fprintf(stderr, "Failed to start iio motion monitoring\n");
|
"'generic' for sane defaults.\n");
|
||||||
|
fprintf(stderr, "\tUsage: %s <device name>\n", argv[0]);
|
||||||
|
fprintf(stderr, "\t %s list-devices \n", argv[0]);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct usb_detector* base_station_detector =
|
if (strcmp(argv[1], "list-devices") == 0) {
|
||||||
create_detector(BASESTATION_VENDOR_ID, BASESTATION_PRODUCT_ID);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
base_station_detector->on_connected = &on_basestation_connected;
|
struct device_information device_info = get_device_info_for_name(argv[1]);
|
||||||
base_station_detector->on_disconnected = &on_basestation_disconnected;
|
|
||||||
base_station_detector->has_hotplug_callbacks = true;
|
|
||||||
|
|
||||||
struct miix_wlr_state* state = miix_wlr_init();
|
struct accel_monitor* monitor = 0;
|
||||||
|
struct miix_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 = miix_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;
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
thrd_sleep(&(struct timespec){0, 10000000UL}, NULL);
|
thrd_sleep(&(struct timespec){0, 10000000UL}, NULL);
|
||||||
if (!monitor->data_is_ready) continue;
|
if (!monitor->data_is_ready) continue;
|
||||||
|
|
||||||
|
@ -125,8 +179,8 @@ int main(int, char**)
|
||||||
if (last_accel_rotation != ACCEL_ROTATION_0DEG) {
|
if (last_accel_rotation != ACCEL_ROTATION_0DEG) {
|
||||||
miix_wlr_head_set_transform(
|
miix_wlr_head_set_transform(
|
||||||
state,
|
state,
|
||||||
DEFAULT_OUTPUT,
|
device_info.target_output,
|
||||||
SENSOR_TO_OUTPUT_ROTATION[ACCEL_ROTATION_0DEG]);
|
device_info.sensor_to_output_rotation[ACCEL_ROTATION_0DEG]);
|
||||||
last_accel_rotation = ACCEL_ROTATION_0DEG;
|
last_accel_rotation = ACCEL_ROTATION_0DEG;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
@ -137,11 +191,17 @@ int main(int, char**)
|
||||||
rotation == last_accel_rotation)
|
rotation == last_accel_rotation)
|
||||||
continue;
|
continue;
|
||||||
miix_wlr_head_set_transform(
|
miix_wlr_head_set_transform(
|
||||||
state, DEFAULT_OUTPUT, SENSOR_TO_OUTPUT_ROTATION[rotation]);
|
state,
|
||||||
|
device_info.target_output,
|
||||||
|
device_info.sensor_to_output_rotation[rotation]);
|
||||||
last_accel_rotation = rotation;
|
last_accel_rotation = rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
accel_stop_monitor(monitor);
|
if (device_info.screen_rotation_supported) {
|
||||||
destroy_detector(base_station_detector);
|
accel_stop_monitor(monitor);
|
||||||
miix_wlr_cleanup(state);
|
miix_wlr_cleanup(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device_info.basestation_detection_supported)
|
||||||
|
destroy_detector(base_station_detector);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue