Created
June 20, 2018 22:19
-
-
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`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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