+unsigned create_shader(GLenum type, const char *src)
+{
+ unsigned shader;
+
+ shader = glCreateShader(type);
+ glShaderSource(shader, 1, &src, NULL);
+ glCompileShader(shader);
+
+ return shader;
+}
+
+unsigned create_program(unsigned *shaders, unsigned nshaders)
+{
+ unsigned program;
+ unsigned i;
+
+ program = glCreateProgram();
+ for(i=0; i<nshaders; ++i)
+ glAttachShader(program, shaders[i]);
+ glLinkProgram(program);
+
+ glUseProgram(program);
+ glUniformMatrix4fv(glGetUniformLocation(program, "view"), 1, GL_FALSE, view_matrix);
+ glUniformMatrix4fv(glGetUniformLocation(program, "projection"), 1, GL_FALSE, projection_matrix);
+
+ return program;
+}
+
+int initialize_view(Display *display, InteractiveView *view)
+{
+ int major_ver;
+ int minor_ver;
+ int attribs[5];
+ unsigned i;
+ GLXFBConfig *configs;
+ int nconfigs;
+ XVisualInfo *vi;
+ XSetWindowAttributes win_attr;
+ Window root;
+
+ if(!glXQueryVersion(display, &major_ver, &minor_ver) || major_ver<1 || (major_ver==1 && minor_ver<4))
+ return 0;
+
+ i = 0;
+ attribs[i++] = GLX_DOUBLEBUFFER;
+ attribs[i++] = True;
+ attribs[i++] = GLX_DEPTH_SIZE;
+ attribs[i++] = 16;
+ attribs[i] = None;
+
+ configs = glXChooseFBConfig(display, DefaultScreen(display), attribs, &nconfigs);
+ if(!configs || !nconfigs)
+ return 0;
+
+ vi = glXGetVisualFromFBConfig(display, configs[0]);
+ root = DefaultRootWindow(display);
+ win_attr.colormap = XCreateColormap(display, root, vi->visual, AllocNone);
+ view->window = XCreateWindow(display, root, 0, 0, 1024, 768, 0, vi->depth, InputOutput, vi->visual, CWColormap, &win_attr);
+ XStoreName(display, view->window, "Geometry correction control");
+ XMapWindow(display, view->window);
+
+ view->glx_window = glXCreateWindow(display, configs[0], view->window, NULL);
+ view->glx_context = glXCreateNewContext(display, configs[0], GLX_RGBA_TYPE, NULL, True);
+ glXMakeContextCurrent(display, view->glx_window, view->glx_window, view->glx_context);
+
+ XFree(configs);
+ XFree(vi);
+
+ view->shaders[0] = create_shader(GL_VERTEX_SHADER, vshader_src);
+ view->shaders[1] = create_shader(GL_GEOMETRY_SHADER, gshader_src);
+ view->shaders[2] = create_shader(GL_FRAGMENT_SHADER, fshader_src);
+ view->programs[0] = create_program(view->shaders, 3);
+ view->shaders[3] = create_shader(GL_VERTEX_SHADER, flat_vshader_src);
+ view->shaders[4] = create_shader(GL_FRAGMENT_SHADER, flat_fshader_src);
+ view->programs[1] = create_program(view->shaders+3, 2);
+ glUniform4f(glGetUniformLocation(view->programs[1], "color"), 0.0f, 0.6f, 1.0f, 1.0f);
+
+ glGenBuffers(2, view->buffers);
+ glBindBuffer(GL_ARRAY_BUFFER, view->buffers[0]);
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), NULL);
+ glEnableVertexAttribArray(0);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, view->buffers[1]);
+
+ glEnable(GL_PRIMITIVE_RESTART);
+ glPrimitiveRestartIndex(0xFFFF);
+
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LEQUAL);
+
+ view->geometry_data_atom = XInternAtom(display, "_MSP_GEOMETRY_CORRECTION_DATA", False);
+ XSelectInput(display, view->window, KeyPressMask|ExposureMask);
+
+ return 1;
+}
+
+void request_geometry_data(Display *display, InteractiveView *view, GeometryCorrection *target)
+{
+ XChangeProperty(display, view->window, view->geometry_data_atom, XA_STRING, 8, PropModeReplace, (unsigned char *)target->monitor_name, strlen(target->monitor_name));
+ XConvertSelection(display, view->geometry_data_atom, view->geometry_data_atom, view->geometry_data_atom, view->window, CurrentTime);
+}
+
+void update_view(Display *display, InteractiveView *view, XSelectionEvent *event)
+{
+ Atom prop_type;
+ int prop_format;
+ unsigned long overflow;
+ unsigned long data_length;
+ short *geometry_data;
+ unsigned tessellation;
+ unsigned nvertices;
+ float *vertex_data;
+ unsigned nindices;
+ unsigned short *index_data;
+ unsigned i;
+ int x, y;
+
+ XGetWindowProperty(display, event->requestor, event->property, 0, 32262, False, XA_INTEGER,
+ &prop_type, &prop_format, &data_length, &overflow, (unsigned char **)&geometry_data);
+
+ tessellation = geometry_data[0];
+ nvertices = 5+(tessellation+1)*(tessellation+1);
+
+ vertex_data = (float *)malloc(nvertices*3*sizeof(float));
+ for(i=0; i<nvertices*3; ++i)
+ vertex_data[i] = geometry_data[1+i]/4096.0f;
+
+ view->nelements = tessellation*((tessellation+1)*2+1)-1;
+
+ nindices = 12+view->nelements;
+ index_data = (unsigned short *)malloc(nindices*sizeof(unsigned short));
+ memcpy(index_data, frustum_indices, sizeof(frustum_indices));
+ i = 12;
+ for(y=0; y<(int)tessellation; ++y)
+ {
+ if(y>0)
+ index_data[i++] = 0xFFFF;
+ for(x=0; x<=(int)tessellation; ++x)
+ {
+ index_data[i++] = 5+(y+1)*(tessellation+1)+x;
+ index_data[i++] = 5+y*(tessellation+1)+x;
+ }
+ }
+
+ glBufferData(GL_ARRAY_BUFFER, nvertices*3*sizeof(float), vertex_data, GL_STATIC_DRAW);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, nindices*sizeof(unsigned short), index_data, GL_STATIC_DRAW);
+
+ free(vertex_data);
+ free(index_data);
+ XFree(geometry_data);
+}
+
+int interactive(Display *display, GeometryCorrection *corrections, GeometryCorrection *target)
+{
+ InteractiveView view;
+ int update_pending;
+ int done;
+
+ initialize_view(display, &view);
+ request_geometry_data(display, &view, target);
+
+ done = 0;
+ update_pending = 0;
+ while(!done)
+ {
+ XEvent event;
+ KeySym keysym;
+ int repaint;
+
+ XNextEvent(display, &event);
+
+ repaint = 0;
+ switch(event.type)
+ {
+ case KeyPress:
+ keysym = XLookupKeysym(&event.xkey, 0);
+ if(keysym==XK_Escape)
+ {
+ done = 1;
+ break;
+ }
+ else if(update_pending)
+ break;
+ else if(keysym==XK_q)
+ target->keystone_vertical += 1.0f/64;
+ else if(keysym==XK_a)
+ target->keystone_vertical -= 1.0f/64;
+ else if(keysym==XK_w)
+ target->curvature_depth += 1.0f/256;
+ else if(keysym==XK_s)
+ target->curvature_depth -= 1.0f/256;
+ else if(keysym==XK_e)
+ target->vertical_center += 1.0f/32;
+ else if(keysym==XK_d)
+ target->vertical_center -= 1.0f/32;
+ else if(keysym==XK_r)
+ target->perspective += 1.0f/16;
+ else if(keysym==XK_f)
+ target->perspective -= 1.0f/16;
+ else if(keysym==XK_z)
+ target->curvature_type = target->curvature_type%2+1;
+ else
+ break;
+
+ set_corrections(display, corrections);
+ request_geometry_data(display, &view, target);
+ update_pending = 1;
+
+ break;
+ case SelectionNotify:
+ if(event.xselection.property==view.geometry_data_atom)
+ {
+ update_view(display, &view, &event.xselection);
+ update_pending = 0;
+ repaint = 1;
+ }
+ break;
+ case Expose:
+ repaint = 1;
+ break;
+ default:
+ printf("event %d\n", event.type);
+ break;
+ }
+
+ if(repaint)
+ {
+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+ glUseProgram(view.programs[0]);
+ glDrawElements(GL_TRIANGLE_STRIP, view.nelements, GL_UNSIGNED_SHORT, (void *)(12*sizeof(unsigned short)));
+ glUseProgram(view.programs[1]);
+ glDrawElements(GL_LINE_STRIP, 11, GL_UNSIGNED_SHORT, NULL);
+ glXSwapBuffers(display, view.glx_window);
+ }
+ }
+
+ return 0;
+}
+