Skip to content

Instantly share code, notes, and snippets.

@sigmaris
Created June 20, 2018 22:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sigmaris/df6863a64f6f768f5fb0e07c3c5f5d63 to your computer and use it in GitHub Desktop.
Save sigmaris/df6863a64f6f768f5fb0e07c3c5f5d63 to your computer and use it in GitHub Desktop.
Linux KMS mode tester, compile with: gcc -g -Wall -Wextra -o kms_mode_test kms_mode_test.c `pkg-config --cflags --libs ncurses form libdrm egl gl gbm`
/*
* Copyright © 2011 Kristian Høgsberg
* Copyright © 2011 Benjamin Franzke
* Copyright © 2018 Hugh Cole-Baker
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that copyright
* notice and this permission notice appear in supporting documentation, and
* that the name of the copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <inttypes.h>
#include <ncurses.h>
#include <form.h>
#define EGL_EGLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES
#include <gbm.h>
#include <GL/gl.h>
#include <GL/glext.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <drm.h>
#include <xf86drmMode.h>
struct kms {
int drm_fd;
drmModeConnector *connector;
drmModeEncoder *encoder;
drmModeModeInfo mode;
uint32_t fb_id;
struct gbm_surface *gs;
struct gbm_bo *bo;
EGLSurface surface;
};
typedef struct s_timing_inputs {
uint32_t pixelclock_hz;
uint32_t h_active;
uint32_t h_front_porch;
uint32_t h_sync_pulse;
uint32_t h_back_porch;
uint32_t v_active;
uint32_t v_front_porch;
uint32_t v_sync_pulse;
uint32_t v_back_porch;
EGLBoolean interlaced;
} timing_inputs;
typedef struct s_input_fields {
FIELD* pixelclock_hz;
FIELD* h_active;
FIELD* h_front_porch;
FIELD* h_sync_pulse;
FIELD* h_back_porch;
FIELD* v_active;
FIELD* v_front_porch;
FIELD* v_sync_pulse;
FIELD* v_back_porch;
FIELD* interlaced;
} input_fields;
typedef struct s_timing_outputs {
double hsync_hz;
double vsync_hz;
double hsync_us;
double vsync_us;
} timing_outputs;
#define NUM_INPUTS 10
const char* booleans[3] = {
"true",
"false",
NULL
};
static EGLBoolean
setup_kms(struct kms *kms)
{
drmModeRes *resources;
drmModeConnector *connector;
drmModeEncoder *encoder;
int i;
resources = drmModeGetResources(kms->drm_fd);
if (!resources) {
fprintf(stderr, "drmModeGetResources failed\n");
return EGL_FALSE;
}
for (i = 0; i < resources->count_connectors; i++) {
connector = drmModeGetConnector(kms->drm_fd, resources->connectors[i]);
if (connector == NULL)
continue;
if (connector->connection == DRM_MODE_CONNECTED &&
connector->count_modes > 0)
break;
drmModeFreeConnector(connector);
}
if (i == resources->count_connectors) {
fprintf(stderr, "No currently active connector found.\n");
return EGL_FALSE;
}
for (i = 0; i < resources->count_encoders; i++) {
encoder = drmModeGetEncoder(kms->drm_fd, resources->encoders[i]);
if (encoder == NULL)
continue;
if (encoder->encoder_id == connector->encoder_id)
break;
drmModeFreeEncoder(encoder);
}
drmModeFreeResources(resources);
kms->connector = connector;
kms->encoder = encoder;
kms->mode = connector->modes[0];
return EGL_TRUE;
}
static void
render_stuff(int width, int height)
{
GLfloat view_rotx = 0.0, view_roty = 0.0, view_rotz = 0.0;
static const GLfloat verts[3][2] = {
{ -1, -1 },
{ 1, -1 },
{ 0, 1 }
};
static const GLfloat colors[3][3] = {
{ 1, 0, 0 },
{ 0, 1, 0 },
{ 0, 0, 1 }
};
GLfloat ar = (GLfloat) width / (GLfloat) height;
glViewport(0, 0, (GLint) width, (GLint) height);
glClearColor(0.f, 1.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(1, 1, (GLint) width-2, (GLint) height-2);
glScissor(1, 1, (GLint) width-2, (GLint) height-2);
glEnable(GL_SCISSOR_TEST);
glClearColor(0.4, 0.4, 0.4, 1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-ar, ar, -1, 1, 5.0, 60.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.0, 0.0, -10.0);
glPushMatrix();
glRotatef(view_rotx, 1, 0, 0);
glRotatef(view_roty, 0, 1, 0);
glRotatef(view_rotz, 0, 0, 1);
glVertexPointer(2, GL_FLOAT, 0, verts);
glColorPointer(3, GL_FLOAT, 0, colors);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_COLOR_ARRAY);
glPopMatrix();
glFinish();
}
static const char device_name[] = "/dev/dri/card0";
static const EGLint attribs[] = {
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_ALPHA_SIZE, 0,
EGL_DEPTH_SIZE, 1,
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
EGL_NONE
};
input_fields* init_fields() {
input_fields* fields = malloc(sizeof(input_fields));
fields->pixelclock_hz = new_field(1, 6, 1, 27, 0, 0);
set_field_back(fields->pixelclock_hz, A_UNDERLINE);
set_field_type(fields->pixelclock_hz, TYPE_NUMERIC, 3, 0, 162);
fields->h_active = new_field(1, 4, 2, 27, 0, 0);
set_field_back(fields->h_active, A_UNDERLINE);
set_field_type(fields->h_active, TYPE_INTEGER, 0, 0, 3840);
fields->h_front_porch = new_field(1, 4, 3, 27, 0, 0);
set_field_back(fields->h_front_porch, A_UNDERLINE);
set_field_type(fields->h_front_porch, TYPE_INTEGER, 0, 0, 3840);
fields->h_sync_pulse = new_field(1, 4, 4, 27, 0, 0);
set_field_back(fields->h_sync_pulse, A_UNDERLINE);
set_field_type(fields->h_sync_pulse, TYPE_INTEGER, 0, 0, 3840);
fields->h_back_porch = new_field(1, 4, 5, 27, 0, 0);
set_field_back(fields->h_back_porch, A_UNDERLINE);
set_field_type(fields->h_back_porch, TYPE_INTEGER, 0, 0, 3840);
fields->interlaced = new_field(1, 6, 1, 62, 0, 0);
set_field_back(fields->interlaced, A_UNDERLINE);
set_field_type(fields->interlaced, TYPE_ENUM, booleans, 0, 0);
field_opts_off(fields->interlaced, O_EDIT);
fields->v_active = new_field(1, 4, 2, 62, 0, 0);
set_field_back(fields->v_active, A_UNDERLINE);
set_field_type(fields->v_active, TYPE_INTEGER, 0, 0, 3840);
fields->v_front_porch = new_field(1, 4, 3, 62, 0, 0);
set_field_back(fields->v_front_porch, A_UNDERLINE);
set_field_type(fields->v_front_porch, TYPE_INTEGER, 0, 0, 3840);
fields->v_sync_pulse = new_field(1, 4, 4, 62, 0, 0);
set_field_back(fields->v_sync_pulse, A_UNDERLINE);
set_field_type(fields->v_sync_pulse, TYPE_INTEGER, 0, 0, 3840);
fields->v_back_porch = new_field(1, 4, 5, 62, 0, 0);
set_field_back(fields->v_back_porch, A_UNDERLINE);
set_field_type(fields->v_back_porch, TYPE_INTEGER, 0, 0, 3840);
return fields;
}
void set_buffers(input_fields* fields, const timing_inputs* const inputs) {
const int max_field_len = 7;
char temp_buf[max_field_len];
snprintf(temp_buf, max_field_len, "%.3f", ((double)inputs->pixelclock_hz) / 1000000.0);
set_field_buffer(fields->pixelclock_hz, 0, temp_buf);
snprintf(temp_buf, max_field_len, "%d", inputs->h_active);
set_field_buffer(fields->h_active, 0, temp_buf);
snprintf(temp_buf, max_field_len, "%d", inputs->h_front_porch);
set_field_buffer(fields->h_front_porch, 0, temp_buf);
snprintf(temp_buf, max_field_len, "%d", inputs->h_sync_pulse);
set_field_buffer(fields->h_sync_pulse, 0, temp_buf);
snprintf(temp_buf, max_field_len, "%d", inputs->h_back_porch);
set_field_buffer(fields->h_back_porch, 0, temp_buf);
snprintf(temp_buf, max_field_len, "%d", inputs->v_active);
set_field_buffer(fields->v_active, 0, temp_buf);
snprintf(temp_buf, max_field_len, "%d", inputs->v_front_porch);
set_field_buffer(fields->v_front_porch, 0, temp_buf);
snprintf(temp_buf, max_field_len, "%d", inputs->v_sync_pulse);
set_field_buffer(fields->v_sync_pulse, 0, temp_buf);
snprintf(temp_buf, max_field_len, "%d", inputs->v_back_porch);
set_field_buffer(fields->v_back_porch, 0, temp_buf);
snprintf(temp_buf, max_field_len, inputs->interlaced ? "true" : "false");
set_field_buffer(fields->interlaced, 0, temp_buf);
}
void set_inputs(const input_fields* const fields, timing_inputs* inputs) {
inputs->pixelclock_hz = (uint32_t)(atof(field_buffer(fields->pixelclock_hz, 0)) * 1000000.0);
inputs->h_active = atoi(field_buffer(fields->h_active, 0));
inputs->h_front_porch = atoi(field_buffer(fields->h_front_porch, 0));
inputs->h_sync_pulse = atoi(field_buffer(fields->h_sync_pulse, 0));
inputs->h_back_porch = atoi(field_buffer(fields->h_back_porch, 0));
inputs->v_active = atoi(field_buffer(fields->v_active, 0));
inputs->v_front_porch = atoi(field_buffer(fields->v_front_porch, 0));
inputs->v_sync_pulse = atoi(field_buffer(fields->v_sync_pulse, 0));
inputs->v_back_porch = atoi(field_buffer(fields->v_back_porch, 0));
inputs->interlaced = (strcasecmp(field_buffer(fields->interlaced, 0), "true ") == 0) ? EGL_TRUE : EGL_FALSE;
}
void calc_outputs(const timing_inputs* const inputs, timing_outputs* outputs) {
// Line calculations
uint32_t line_total_px = inputs->h_active + inputs->h_front_porch
+ inputs->h_sync_pulse + inputs->h_back_porch;
double pixelclock_hz = (double)inputs->pixelclock_hz;
outputs->hsync_hz = pixelclock_hz / ((double)line_total_px);
// Screen calculations
uint32_t v_active = inputs->v_active;
if (inputs->interlaced) v_active /= 2;
uint32_t screen_total_lines = v_active + inputs->v_front_porch
+ inputs->v_sync_pulse + inputs->v_back_porch;
outputs->vsync_hz = outputs->hsync_hz / ((double)screen_total_lines);
outputs->hsync_us = ((double)inputs->h_sync_pulse) * 1000000.0 / pixelclock_hz;
outputs->vsync_us = ((double)(inputs->v_sync_pulse * line_total_px * 1000000.0)) / pixelclock_hz;
}
void write_labels() {
mvprintw(1, 1, " Pixel Clock:");
mvprintw(1, 34, "MHz");
mvprintw(2, 1, "Horizontal Active Pixels:");
mvprintw(3, 1, " Horizontal Front Porch:");
mvprintw(4, 1, " Horizontal Sync Pulse:");
mvprintw(5, 1, " Horizontal Back Porch:");
mvprintw(1, 39, " Interlaced:");
mvprintw(2, 39, "Vertical Active Lines:");
mvprintw(3, 39, " Vertical Front Porch:");
mvprintw(4, 39, " Vertical Sync Pulse:");
mvprintw(5, 39, " Vertical Back Porch:");
}
void write_outputs(const timing_outputs* const outputs) {
mvprintw(8, 1, "Horizontal Sync Frequency: %.3f kHz ", outputs->hsync_hz / 1000.0);
mvprintw(9, 1, " Vertical Sync Frequency: %.2f Hz ", outputs->vsync_hz);
mvprintw(10, 1, " Horizontal Sync Length: %.3f µs ", outputs->hsync_us);
mvprintw(11, 1, " Vertical Sync Length: %.2f µs ", outputs->vsync_us);
}
void free_fields(input_fields* fields) {
free_field(fields->pixelclock_hz);
free_field(fields->h_active);
free_field(fields->h_front_porch);
free_field(fields->h_sync_pulse);
free_field(fields->h_back_porch);
free_field(fields->v_active);
free_field(fields->v_front_porch);
free_field(fields->v_sync_pulse);
free_field(fields->v_back_porch);
free_field(fields->interlaced);
free(fields);
}
int create_mode_from_inputs(const timing_inputs* const inputs, uint32_t vrefresh, struct kms* kms) {
kms->mode.hdisplay = inputs->h_active;
kms->mode.vdisplay = inputs->v_active;
kms->mode.vrefresh = vrefresh;
kms->mode.clock = inputs->pixelclock_hz / 1000;
kms->mode.hsync_start = kms->mode.hdisplay + inputs->h_front_porch;
kms->mode.hsync_end = kms->mode.hsync_start + inputs->h_sync_pulse;
kms->mode.htotal = kms->mode.hsync_end + inputs->h_back_porch;
kms->mode.hskew = 0;
kms->mode.vsync_start = kms->mode.vdisplay + (inputs->v_front_porch * (inputs->interlaced ? 2 : 1));
kms->mode.vsync_end = kms->mode.vsync_start + (inputs->v_sync_pulse * (inputs->interlaced ? 2 : 1));
kms->mode.vtotal = kms->mode.vsync_end + (inputs->v_back_porch * (inputs->interlaced ? 2 : 1));
kms->mode.vscan = 1;
kms->mode.flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC
| (inputs->interlaced ? DRM_MODE_FLAG_INTERLACE : 0);
kms->mode.type = 0;
snprintf(kms->mode.name, DRM_DISPLAY_MODE_LEN, "test_%dx%d@%d", inputs->h_active, inputs->v_active, vrefresh);
return drmModeAttachMode(kms->drm_fd, kms->connector->connector_id, &kms->mode);
}
void setup_test_mode(
struct gbm_device* gbm, EGLDisplay dpy, EGLContext ctx,
EGLConfig config, struct kms* kms
) {
uint32_t handle, stride;
struct gbm_surface *old_gs = kms->gs;
EGLSurface old_surface = kms->surface;
struct gbm_bo *old_bo = kms->bo;
uint32_t old_fb_id = kms->fb_id;
kms->gs = gbm_surface_create(
gbm, kms->mode.hdisplay, kms->mode.vdisplay, GBM_BO_FORMAT_XRGB8888,
GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING
);
kms->surface = eglCreateWindowSurface(dpy, config, kms->gs, NULL);
if (kms->surface == EGL_NO_SURFACE) {
fprintf(stderr, "failed to create EGLSurface\n");
goto gbm_destroy_surface;
}
if (!eglMakeCurrent(dpy, kms->surface, kms->surface, ctx)) {
fprintf(stderr, "failed to make context current\n");
goto egl_destroy_surface;
}
render_stuff(kms->mode.hdisplay, kms->mode.vdisplay);
eglSwapBuffers(dpy, kms->surface);
kms->bo = gbm_surface_lock_front_buffer(kms->gs);
if (kms->bo == NULL) {
fprintf(stderr, "failed to lock front buffer\n");
goto egl_destroy_surface;
}
handle = gbm_bo_get_handle(kms->bo).u32;
stride = gbm_bo_get_stride(kms->bo);
int ret = drmModeAddFB(
kms->drm_fd, kms->mode.hdisplay, kms->mode.vdisplay, 24, 32,
stride, handle, &kms->fb_id
);
if (ret) {
fprintf(stderr, "failed to create fb\n");
goto rm_fb;
}
ret = drmModeSetCrtc(
kms->drm_fd, kms->encoder->crtc_id, kms->fb_id, 0, 0,
&kms->connector->connector_id, 1, &kms->mode
);
if (ret) {
fprintf(stderr, "failed to set mode: %d\n", ret);
}
goto free_old_resources;
// Error handling cleanup:
rm_fb:
drmModeRmFB(kms->drm_fd, kms->fb_id);
gbm_surface_release_buffer(kms->gs, kms->bo);
egl_destroy_surface:
eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
eglDestroySurface(dpy, kms->surface);
gbm_destroy_surface:
gbm_surface_destroy(kms->gs);
// Normal cleanup of old resources:
free_old_resources:
if (old_fb_id > 0) drmModeRmFB(kms->drm_fd, old_fb_id);
if (old_bo) gbm_surface_release_buffer(old_gs, old_bo);
if (old_surface != EGL_NO_SURFACE) eglDestroySurface(dpy, old_surface);
if (old_gs) gbm_surface_destroy(old_gs);
}
void restore_saved_crtc(const struct kms* const kms, drmModeCrtcPtr saved_crtc) {
if (
drmModeSetCrtc(kms->drm_fd, saved_crtc->crtc_id, saved_crtc->buffer_id,
saved_crtc->x, saved_crtc->y, &kms->connector->connector_id, 1, &saved_crtc->mode)
)
fprintf(stderr, "failed to restore crtc\n");
}
int main(int argc, char *argv[]) {
int x, y, ret = 0;
EGLBoolean changed, test_active = EGL_FALSE;
// Setup ncurses
initscr();
cbreak();
keypad(stdscr, TRUE);
noecho();
mousemask(ALL_MOUSE_EVENTS, NULL);
timing_inputs inputs = {
.pixelclock_hz = argc > 1 ? atoi(argv[1]) : 12800000,
.h_active = argc > 2 ? atoi(argv[2]) : 640,
.h_front_porch = argc > 3 ? atoi(argv[3]) : 36,
.h_sync_pulse = argc > 4 ? atoi(argv[4]) : 61,
.h_back_porch = argc > 5 ? atoi(argv[5]) : 77,
.v_active = argc > 6 ? atoi(argv[6]) : 480,
.v_front_porch = argc > 7 ? atoi(argv[7]) : 2,
.v_sync_pulse = argc > 8 ? atoi(argv[8]) : 3,
.v_back_porch = argc > 9 ? atoi(argv[9]) : 17,
.interlaced = argc > 10 ? ((strcasecmp(argv[10], "true") == 0) ? EGL_TRUE : EGL_FALSE) : EGL_TRUE,
};
timing_outputs outputs = {0};
input_fields *fields = init_fields();
FIELD* f[NUM_INPUTS + 1] = {
fields->pixelclock_hz,
fields->h_active,
fields->h_front_porch,
fields->h_sync_pulse,
fields->h_back_porch,
fields->interlaced,
fields->v_active,
fields->v_front_porch,
fields->v_sync_pulse,
fields->v_back_porch,
NULL
};
FORM *inputs_form = new_form(f);
post_form(inputs_form);
refresh();
set_current_field(inputs_form, f[0]);
set_buffers(fields, &inputs);
write_labels();
calc_outputs(&inputs, &outputs);
write_outputs(&outputs);
refresh();
// setup EGL and KMS
struct kms kms = {0};
kms.drm_fd = open(device_name, O_RDWR);
if (kms.drm_fd < 0) {
/* Probably permissions error */
fprintf(stderr, "couldn't open %s\n", device_name);
ret = 1;
goto deinit_curses;
}
struct gbm_device *gbm = gbm_create_device(kms.drm_fd);
if (gbm == NULL) {
fprintf(stderr, "couldn't create gbm device\n");
ret = 2;
goto close_fd;
}
EGLDisplay dpy = eglGetDisplay(gbm);
if (dpy == EGL_NO_DISPLAY) {
fprintf(stderr, "eglGetDisplay() failed\n");
ret = 3;
goto destroy_gbm_device;
}
if (!eglInitialize(dpy, NULL, NULL)) {
fprintf(stderr, "eglInitialize() failed\n");
ret = 4;
goto egl_terminate;
}
eglBindAPI(EGL_OPENGL_API);
EGLConfig config;
EGLint n;
if (!eglChooseConfig(dpy, attribs, &config, 1, &n) || n != 1) {
fprintf(stderr, "failed to choose argb config\n");
goto egl_terminate;
}
EGLContext ctx = eglCreateContext(dpy, config, EGL_NO_CONTEXT, NULL);
if (ctx == NULL) {
fprintf(stderr, "failed to create context\n");
ret = 5;
goto egl_terminate;
}
if (!setup_kms(&kms)) {
ret = 6;
goto free_kms;
}
drmModeCrtcPtr saved_crtc = drmModeGetCrtc(kms.drm_fd, kms.encoder->crtc_id);
if (saved_crtc == NULL) goto free_kms;
int ch;
char* current_error = NULL;
/* Loop through to get user requests */
while((ch = getch()) != '\e') {
current_error = NULL;
switch(ch) {
case KEY_UP:
/* Go to previous field */
form_driver(inputs_form, REQ_PREV_FIELD);
form_driver(inputs_form, REQ_END_LINE);
break;
case KEY_DOWN:
/* Go to next field */
form_driver(inputs_form, REQ_NEXT_FIELD);
/* Go to the end of the present buffer */
form_driver(inputs_form, REQ_END_LINE);
break;
case '\t':
form_driver(inputs_form, REQ_NEXT_CHOICE);
break;
case '\n':
if (test_active) {
restore_saved_crtc(&kms, saved_crtc);
test_active = EGL_FALSE;
} else {
if (create_mode_from_inputs(&inputs, outputs.vsync_hz, &kms)) {
current_error = "Failed to set mode";
} else {
setup_test_mode(gbm, dpy, ctx, config, &kms);
test_active = EGL_TRUE;
}
}
break;
case KEY_LEFT:
form_driver(inputs_form, REQ_PREV_CHAR);
break;
case KEY_RIGHT:
form_driver(inputs_form, REQ_NEXT_CHAR);
break;
case KEY_BACKSPACE:
form_driver(inputs_form, REQ_PREV_CHAR);
case KEY_DC:
form_driver(inputs_form, REQ_DEL_CHAR);
break;
default:
form_driver(inputs_form, ch);
break;
}
getyx(stdscr, y, x);
changed = EGL_FALSE;
for (int i = 0; f[i] != NULL; ++i) {
if (field_status(f[i])) {
if (!changed) {
set_inputs(fields, &inputs);
calc_outputs(&inputs, &outputs);
write_outputs(&outputs);
}
changed = EGL_TRUE;
set_field_status(f[i], FALSE);
}
}
if (test_active) {
mvprintw(13, 0, "Test mode active - CRTC ID: %d - Encoder ID: %d - Connector ID: %d",
kms.encoder->crtc_id, kms.encoder->encoder_id, kms.connector->connector_id);
} else {
mvprintw(13, 0, " ");
}
if (current_error) {
mvprintw(14, 0, "Error: %s", current_error);
} else {
mvprintw(14, 0, " ");
}
move(y, x);
if(changed) {
refresh();
if (test_active) {
if (create_mode_from_inputs(&inputs, outputs.vsync_hz, &kms)) {
current_error = "Failed to set mode";
} else {
setup_test_mode(gbm, dpy, ctx, config, &kms);
}
}
}
}
if (test_active) {
restore_saved_crtc(&kms, saved_crtc);
}
free_kms:
drmModeFreeConnector(kms.connector);
drmModeFreeEncoder(kms.encoder);
drmModeFreeCrtc(saved_crtc);
destroy_context:
eglDestroyContext(dpy, ctx);
egl_terminate:
eglTerminate(dpy);
destroy_gbm_device:
gbm_device_destroy(gbm);
close_fd:
close(kms.drm_fd);
deinit_curses:
unpost_form(inputs_form);
free_form(inputs_form);
free_fields(fields);
endwin();
return ret;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment