diff --git a/include/rootston/config.h b/include/rootston/config.h index 97a8baab..86699c6a 100644 --- a/include/rootston/config.h +++ b/include/rootston/config.h @@ -1,11 +1,17 @@ #ifndef ROOTSTON_CONFIG_H #define ROOTSTON_CONFIG_H +#include #include #include #define ROOTS_CONFIG_DEFAULT_SEAT_NAME "seat0" +struct roots_output_mode_config { + drmModeModeInfo info; + struct wl_list link; +}; + struct roots_output_config { char *name; bool enable; @@ -17,6 +23,7 @@ struct roots_output_config { int width, height; float refresh_rate; } mode; + struct wl_list modes; }; struct roots_device_config { diff --git a/rootston/config.c b/rootston/config.c index 9a4d77fd..7ef5e629 100644 --- a/rootston/config.c +++ b/rootston/config.c @@ -116,6 +116,51 @@ static uint32_t parse_modifier(const char *symname) { } } +static bool parse_modeline(const char *s, drmModeModeInfo *mode) { + char hsync[16]; + char vsync[16]; + float fclock; + + mode->type = DRM_MODE_TYPE_USERDEF; + + if (sscanf(s, "%f %hd %hd %hd %hd %hd %hd %hd %hd %15s %15s", + &fclock, + &mode->hdisplay, + &mode->hsync_start, + &mode->hsync_end, + &mode->htotal, + &mode->vdisplay, + &mode->vsync_start, + &mode->vsync_end, + &mode->vtotal, hsync, vsync) != 11) { + return false; + } + + mode->clock = fclock * 1000; + mode->vrefresh = mode->clock * 1000.0 * 1000.0 + / mode->htotal / mode->vtotal; + if (strcasecmp(hsync, "+hsync") == 0) { + mode->flags |= DRM_MODE_FLAG_PHSYNC; + } else if (strcasecmp(hsync, "-hsync") == 0) { + mode->flags |= DRM_MODE_FLAG_NHSYNC; + } else { + return false; + } + + if (strcasecmp(vsync, "+vsync") == 0) { + mode->flags |= DRM_MODE_FLAG_PVSYNC; + } else if (strcasecmp(vsync, "-vsync") == 0) { + mode->flags |= DRM_MODE_FLAG_NVSYNC; + } else { + return false; + } + + snprintf(mode->name, sizeof(mode->name), "%dx%d@%d", + mode->hdisplay, mode->vdisplay, mode->vrefresh / 1000); + + return true; +} + void add_binding_config(struct wl_list *bindings, const char* combination, const char* command) { struct roots_binding_config *bc = @@ -269,6 +314,7 @@ static int config_ini_handler(void *user, const char *section, const char *name, oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; oc->scale = 1; oc->enable = true; + wl_list_init(&oc->modes); wl_list_insert(&config->outputs, &oc->link); } @@ -322,6 +368,15 @@ static int config_ini_handler(void *user, const char *section, const char *name, wlr_log(L_DEBUG, "Configured output %s with mode %dx%d@%f", oc->name, oc->mode.width, oc->mode.height, oc->mode.refresh_rate); + } else if (strcmp(name, "modeline") == 0) { + struct roots_output_mode_config *mode = calloc(1, sizeof(*mode)); + + if (parse_modeline(value, &mode->info)) { + wl_list_insert(&oc->modes, &mode->link); + } else { + free(mode); + wlr_log(L_ERROR, "Invalid modeline: %s", value); + } } } else if (strncmp(cursor_prefix, section, strlen(cursor_prefix)) == 0) { const char *seat_name = section + strlen(cursor_prefix); @@ -459,6 +514,10 @@ struct roots_config *roots_config_create_from_args(int argc, char *argv[]) { void roots_config_destroy(struct roots_config *config) { struct roots_output_config *oc, *otmp = NULL; wl_list_for_each_safe(oc, otmp, &config->outputs, link) { + struct roots_output_mode_config *omc, *omctmp = NULL; + wl_list_for_each_safe(omc, omctmp, &oc->modes, link) { + free(omc); + } free(oc->name); free(oc); } diff --git a/rootston/output.c b/rootston/output.c index 353d431f..fda2fe89 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -16,6 +16,7 @@ #include "rootston/layers.h" #include "rootston/output.h" #include "rootston/server.h" +#include "backend/drm/drm.h" /** * Rotate a child's position relative to a parent. The parent size is (pw, ph), @@ -841,6 +842,17 @@ void handle_new_output(struct wl_listener *listener, void *data) { roots_config_get_output(config, wlr_output); if (output_config) { if (output_config->enable) { + struct roots_output_mode_config *mode_config; + + if (wlr_output_is_drm(wlr_output)) { + wl_list_for_each(mode_config, &output_config->modes, link) { + wlr_drm_connector_add_mode(wlr_output, &mode_config->info); + } + } else { + if (!wl_list_empty(&output_config->modes)) { + wlr_log(L_ERROR, "Can only add modes for DRM backend"); + } + } if (output_config->mode.width) { set_mode(wlr_output, output_config); } diff --git a/rootston/rootston.ini.example b/rootston/rootston.ini.example index 556cbefb..bb0efa44 100644 --- a/rootston/rootston.ini.example +++ b/rootston/rootston.ini.example @@ -19,6 +19,13 @@ y = 0 # and rotate by specified angle rotate = 90 +# Additional video mode to add +# Format is generated by cvt and is documented in x.org.conf(5) +modeline = 87.25 720 776 848 976 1440 1443 1453 1493 -hsync +vsync +modeline = 65.13 768 816 896 1024 1024 1025 1028 1060 -HSync +VSync +# Select one of the above modes +mode = 768x1024 + [cursor] # Restrict cursor movements to single output map-to-output = VGA-1