]> git.tdb.fi Git - geometrycompositor.git/blob - source/control.c
Initialize correction values in the control program
[geometrycompositor.git] / source / control.c
1 #define GL_GLEXT_PROTOTYPES
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <X11/Xlib.h>
6 #include <X11/Xatom.h>
7 #include <GL/gl.h>
8 #include <GL/glx.h>
9
10 typedef struct GeometryCorrection
11 {
12         char *monitor_name;
13         float keystone_vertical;
14         float cylinder_depth;
15         float vertical_center;
16         float perspective;
17 } GeometryCorrection;
18
19 typedef struct InteractiveView
20 {
21         Window window;
22         GLXContext glx_context;
23         GLXWindow glx_window;
24         Atom geometry_data_atom;
25         unsigned shaders[5];
26         unsigned programs[2];
27         unsigned buffers[2];
28         unsigned nelements;
29 } InteractiveView;
30
31 static unsigned short frustum_indices[] = { 2, 1, 3, 0, 1, 0xFFFF, 3, 4, 2, 0, 4, 0xFFFF };
32 static float view_matrix[] =
33 {
34          0.70711f,  0.40825f, -0.57735f, 0.0f,
35          0.0f,      0.81650f,  0.57735f, 0.0f,
36          0.70711f, -0.40725f,  0.57735f, 0.0f,
37         -0.2f,      0.0f,     -2.5f,     1.0f
38 };
39 static float projection_matrix[] =
40 {
41         2.0f, 0.0f,     0.0f,   0.0f,
42         0.0f, 2.6667f,  0.0f,   0.0f,
43         0.0f, 0.0f,    -1.1f,  -1.0f,
44         0.0f, 0.0f,    -1.05f,  0.0f
45 };
46
47 static const char *vshader_src =
48         "#version 150\n"
49         "uniform mat4 view;\n"
50         "in vec4 vertex;\n"
51         "void main()\n"
52         "{\n"
53         "  gl_Position = view*vertex;\n"
54         "}\n";
55
56 static const char *gshader_src =
57         "#version 150\n"
58         "layout(triangles) in;\n"
59         "layout(triangle_strip, max_vertices=3) out;\n"
60         "uniform mat4 projection;\n"
61         "out vec3 normal;\n"
62         "void main()\n"
63         "{\n"
64         "  vec3 v0 = gl_in[0].gl_Position.xyz;\n"
65         "  vec3 v1 = gl_in[1].gl_Position.xyz;\n"
66         "  vec3 v2 = gl_in[2].gl_Position.xyz;\n"
67         "  vec3 tri_normal = normalize(cross(v1-v0, v2-v0));\n"
68         "  for(int i=0; i<3; ++i)\n"
69         "  {\n"
70         "    normal = tri_normal;\n"
71         "    gl_Position = projection*gl_in[i].gl_Position;\n"
72         "    EmitVertex();\n"
73         "  }\n"
74         "  EndPrimitive();\n"
75         "}\n";
76
77 static const char *fshader_src =
78         "#version 150\n"
79         "in vec3 normal;\n"
80         "out vec4 frag_color;\n"
81         "void main()\n"
82         "{\n"
83         "  frag_color = vec4(vec3(0.5+clamp(normal.z, 0.0, 1.0)*0.5), 1.0);\n"
84         "}\n";
85
86 static const char *flat_vshader_src =
87         "#version 150\n"
88         "uniform mat4 view;\n"
89         "uniform mat4 projection;\n"
90         "in vec4 vertex;\n"
91         "void main()\n"
92         "{\n"
93         "  gl_Position = projection*view*vertex;\n"
94         "}\n";
95
96 static const char *flat_fshader_src =
97         "#version 150\n"
98         "uniform vec4 color;\n"
99         "out vec4 frag_color;\n"
100         "void main()\n"
101         "{\n"
102         "  frag_color = color;\n"
103         "}\n";
104
105
106 GeometryCorrection *get_corrections(Display *display)
107 {
108         Window root;
109         Atom monitors_atom;
110         Atom correction_atom;
111         Atom prop_type;
112         int prop_format;
113         unsigned long overflow;
114         unsigned long names_length;
115         char *names;
116         unsigned long values_length;
117         short *values;
118         unsigned ncorrections;
119         unsigned i;
120         GeometryCorrection *corrections;
121         char *name_ptr;
122
123         root = DefaultRootWindow(display);
124         monitors_atom = XInternAtom(display, "_MSP_GEOMETRY_CORRECTION_MONITORS", False);
125         correction_atom = XInternAtom(display, "_MSP_GEOMETRY_CORRECTION", False);
126
127         XGetWindowProperty(display, root, monitors_atom, 0, 64, False, XA_STRING,
128                 &prop_type, &prop_format, &names_length, &overflow, (unsigned char **)&names);
129         if(prop_type!=XA_STRING || prop_format!=8)
130                 return NULL;
131
132         XGetWindowProperty(display, root, correction_atom, 0, 64, False, XA_INTEGER,
133                 &prop_type, &prop_format, &values_length, &overflow, (unsigned char **)&values);
134         if(prop_type!=XA_INTEGER || prop_format!=16)
135         {
136                 XFree(names);
137                 return NULL;
138         }
139
140         ncorrections = (names_length>0);
141         for(i=0; i<names_length; ++i)
142                 if(!names[i])
143                         ++ncorrections;
144         if(ncorrections*4>values_length)
145                 ncorrections = values_length/4;
146         corrections = (GeometryCorrection *)malloc((ncorrections+1)*sizeof(GeometryCorrection));
147
148         name_ptr = names;
149         for(i=0; i<ncorrections; ++i)
150         {
151                 unsigned namelen;
152
153                 namelen = strlen(name_ptr);
154                 corrections[i].monitor_name = (char *)malloc(namelen+1);
155                 strcpy(corrections[i].monitor_name, name_ptr);
156
157                 corrections[i].keystone_vertical = values[i*4]/10000.0f;
158                 corrections[i].cylinder_depth = values[i*4+1]/10000.0f;
159                 corrections[i].vertical_center = values[i*4+2]/10000.0f;
160                 corrections[i].perspective = values[i*4+3]/10000.0f;
161
162                 name_ptr += namelen+1;
163         }
164
165         corrections[ncorrections].monitor_name = NULL;
166
167         XFree(names);
168         XFree(values);
169
170         return corrections;
171 }
172
173 void set_corrections(Display *display, GeometryCorrection *corrections)
174 {
175         unsigned ncorrections;
176         unsigned total_len;
177         unsigned i;
178         char *names;
179         char *name_ptr;
180         short *values;
181         Window root;
182         Atom monitors_atom;
183         Atom correction_atom;
184
185         ncorrections = 0;
186         total_len = 0;
187         for(i=0; corrections[i].monitor_name; ++i)
188         {
189                 ++ncorrections;
190                 total_len += strlen(corrections[i].monitor_name);
191         }
192
193         names = (char *)malloc(total_len+ncorrections);
194         values = (short *)malloc(ncorrections*4*sizeof(short));
195         name_ptr = names;
196         for(i=0; i<ncorrections; ++i)
197         {
198                 strcpy(name_ptr, corrections[i].monitor_name);
199                 name_ptr += strlen(name_ptr)+1;
200
201                 values[i*4] = corrections[i].keystone_vertical*10000;
202                 values[i*4+1] = corrections[i].cylinder_depth*10000;
203                 values[i*4+2] = corrections[i].vertical_center*10000;
204                 values[i*4+3] = corrections[i].perspective*10000;
205         }
206
207         root = DefaultRootWindow(display);
208         monitors_atom = XInternAtom(display, "_MSP_GEOMETRY_CORRECTION_MONITORS", False);
209         correction_atom = XInternAtom(display, "_MSP_GEOMETRY_CORRECTION", False);
210         XChangeProperty(display, root, monitors_atom, XA_STRING, 8, PropModeReplace, (unsigned char *)names, total_len+ncorrections-1);
211         XChangeProperty(display, root, correction_atom, XA_INTEGER, 16, PropModeReplace, (unsigned char *)values, ncorrections*4);
212
213         free(names);
214         free(values);
215 }
216
217 unsigned create_shader(GLenum type, const char *src)
218 {
219         unsigned shader;
220
221         shader = glCreateShader(type);
222         glShaderSource(shader, 1, &src, NULL);
223         glCompileShader(shader);
224
225         return shader;
226 }
227
228 unsigned create_program(unsigned *shaders, unsigned nshaders)
229 {
230         unsigned program;
231         unsigned i;
232
233         program = glCreateProgram();
234         for(i=0; i<nshaders; ++i)
235                 glAttachShader(program, shaders[i]);
236         glLinkProgram(program);
237
238         glUseProgram(program);
239         glUniformMatrix4fv(glGetUniformLocation(program, "view"), 1, GL_FALSE, view_matrix);
240         glUniformMatrix4fv(glGetUniformLocation(program, "projection"), 1, GL_FALSE, projection_matrix);
241
242         return program;
243 }
244
245 int initialize_view(Display *display, InteractiveView *view)
246 {
247         int major_ver;
248         int minor_ver;
249         int attribs[5];
250         unsigned i;
251         GLXFBConfig *configs;
252         int nconfigs;
253         XVisualInfo *vi;
254         XSetWindowAttributes win_attr;
255         Window root;
256
257         if(!glXQueryVersion(display, &major_ver, &minor_ver) || major_ver<1 || (major_ver==1 && minor_ver<4))
258                 return 0;
259
260         i = 0;
261         attribs[i++] = GLX_DOUBLEBUFFER;
262         attribs[i++] = True;
263         attribs[i++] = GLX_DEPTH_SIZE;
264         attribs[i++] = 16;
265         attribs[i] = None;
266
267         configs = glXChooseFBConfig(display, DefaultScreen(display), attribs, &nconfigs);
268         if(!configs || !nconfigs)
269                 return 0;
270
271         vi = glXGetVisualFromFBConfig(display, configs[0]);
272         root = DefaultRootWindow(display);
273         win_attr.colormap = XCreateColormap(display, root, vi->visual, AllocNone);
274         view->window = XCreateWindow(display, root, 0, 0, 1024, 768, 0, vi->depth, InputOutput, vi->visual, CWColormap, &win_attr);
275         XStoreName(display, view->window, "Geometry correction control");
276         XMapWindow(display, view->window);
277
278         view->glx_window = glXCreateWindow(display, configs[0], view->window, NULL);
279         view->glx_context = glXCreateNewContext(display, configs[0], GLX_RGBA_TYPE, NULL, True);
280         glXMakeContextCurrent(display, view->glx_window, view->glx_window, view->glx_context);
281
282         XFree(configs);
283         XFree(vi);
284
285         view->shaders[0] = create_shader(GL_VERTEX_SHADER, vshader_src);
286         view->shaders[1] = create_shader(GL_GEOMETRY_SHADER, gshader_src);
287         view->shaders[2] = create_shader(GL_FRAGMENT_SHADER, fshader_src);
288         view->programs[0] = create_program(view->shaders, 3);
289         view->shaders[3] = create_shader(GL_VERTEX_SHADER, flat_vshader_src);
290         view->shaders[4] = create_shader(GL_FRAGMENT_SHADER, flat_fshader_src);
291         view->programs[1] = create_program(view->shaders+3, 2);
292         glUniform4f(glGetUniformLocation(view->programs[1], "color"), 0.0f, 0.6f, 1.0f, 1.0f);
293
294         glGenBuffers(2, view->buffers);
295         glBindBuffer(GL_ARRAY_BUFFER, view->buffers[0]);
296         glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), NULL);
297         glEnableVertexAttribArray(0);
298         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, view->buffers[1]);
299
300         glEnable(GL_PRIMITIVE_RESTART);
301         glPrimitiveRestartIndex(0xFFFF);
302
303         glEnable(GL_DEPTH_TEST);
304         glDepthFunc(GL_LEQUAL);
305
306         view->geometry_data_atom = XInternAtom(display, "_MSP_GEOMETRY_CORRECTION_DATA", False);
307         XSelectInput(display, view->window, KeyPressMask|ExposureMask);
308
309         return 1;
310 }
311
312 void request_geometry_data(Display *display, InteractiveView *view, GeometryCorrection *target)
313 {
314         XChangeProperty(display, view->window, view->geometry_data_atom, XA_STRING, 8, PropModeReplace, (unsigned char *)target->monitor_name, strlen(target->monitor_name));
315         XConvertSelection(display, view->geometry_data_atom, view->geometry_data_atom, view->geometry_data_atom, view->window, CurrentTime);
316 }
317
318 void update_view(Display *display, InteractiveView *view, XSelectionEvent *event)
319 {
320         Atom prop_type;
321         int prop_format;
322         unsigned long overflow;
323         unsigned long data_length;
324         short *geometry_data;
325         unsigned tessellation;
326         unsigned nvertices;
327         float *vertex_data;
328         unsigned nindices;
329         unsigned short *index_data;
330         unsigned i;
331         int x, y;
332
333         XGetWindowProperty(display, event->requestor, event->property, 0, 32262, False, XA_INTEGER,
334                 &prop_type, &prop_format, &data_length, &overflow, (unsigned char **)&geometry_data);
335
336         tessellation = geometry_data[0];
337         nvertices = 5+(tessellation+1)*(tessellation+1);
338
339         vertex_data = (float *)malloc(nvertices*3*sizeof(float));
340         for(i=0; i<nvertices*3; ++i)
341                 vertex_data[i] = geometry_data[1+i]/10000.0f;
342
343         view->nelements = tessellation*((tessellation+1)*2+1)-1;
344
345         nindices = 12+view->nelements;
346         index_data = (unsigned short *)malloc(nindices*sizeof(unsigned short));
347         memcpy(index_data, frustum_indices, sizeof(frustum_indices));
348         i = 12;
349         for(y=0; y<(int)tessellation; ++y)
350         {
351                 if(y>0)
352                         index_data[i++] = 0xFFFF;
353                 for(x=0; x<=(int)tessellation; ++x)
354                 {
355                         index_data[i++] = 5+(y+1)*(tessellation+1)+x;
356                         index_data[i++] = 5+y*(tessellation+1)+x;
357                 }
358         }
359
360         glBufferData(GL_ARRAY_BUFFER, nvertices*3*sizeof(float), vertex_data, GL_STATIC_DRAW);
361         glBufferData(GL_ELEMENT_ARRAY_BUFFER, nindices*sizeof(unsigned short), index_data, GL_STATIC_DRAW);
362
363         free(vertex_data);
364         free(index_data);
365         XFree(geometry_data);
366 }
367
368 int interactive(Display *display, GeometryCorrection *corrections, GeometryCorrection *target)
369 {
370         InteractiveView view;
371         int update_pending;
372         int done;
373
374         initialize_view(display, &view);
375         request_geometry_data(display, &view, target);
376
377         done = 0;
378         update_pending = 0;
379         while(!done)
380         {
381                 XEvent event;
382                 KeySym keysym;
383
384                 XNextEvent(display, &event);
385
386                 switch(event.type)
387                 {
388                 case KeyPress:
389                         keysym = XLookupKeysym(&event.xkey, 0);
390                         if(keysym==XK_Escape)
391                         {
392                                 done = 1;
393                                 break;
394                         }
395                         else if(update_pending)
396                                 break;
397                         else if(keysym==XK_q)
398                                 target->keystone_vertical += 1.0f/64;
399                         else if(keysym==XK_a)
400                                 target->keystone_vertical -= 1.0f/64;
401                         else if(keysym==XK_w)
402                                 target->cylinder_depth += 1.0f/256;
403                         else if(keysym==XK_s)
404                                 target->cylinder_depth -= 1.0f/256;
405                         else if(keysym==XK_e)
406                                 target->vertical_center += 1.0f/32;
407                         else if(keysym==XK_d)
408                                 target->vertical_center -= 1.0f/32;
409                         else if(keysym==XK_r)
410                                 target->perspective += 1.0f/16;
411                         else if(keysym==XK_f)
412                                 target->perspective -= 1.0f/16;
413                         else
414                                 break;
415
416                         set_corrections(display, corrections);
417                         request_geometry_data(display, &view, target);
418                         update_pending = 1;
419
420                         break;
421                 case SelectionNotify:
422                         if(event.xselection.property==view.geometry_data_atom)
423                         {
424                                 update_view(display, &view, &event.xselection);
425                                 update_pending = 0;
426                         }
427                         break;
428                 default:
429                         printf("event %d\n", event.type);
430                         break;
431                 }
432
433                 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
434                 glUseProgram(view.programs[0]);
435                 glDrawElements(GL_TRIANGLE_STRIP, view.nelements, GL_UNSIGNED_SHORT, (void *)(12*sizeof(unsigned short)));
436                 glUseProgram(view.programs[1]);
437                 glDrawElements(GL_LINE_STRIP, 11, GL_UNSIGNED_SHORT, NULL);
438                 glXSwapBuffers(display, view.glx_window);
439         }
440
441         return 0;
442 }
443
444 int main(int argc, char **argv)
445 {
446         Display *display;
447         GeometryCorrection *corrections;
448         unsigned i;
449
450         if(argc!=2 && argc!=6)
451         {
452                 fprintf(stderr, "Usage: %s <monitor> [<keystone> <cylinder> <vcenter> <perspective>]\n", argv[0]);
453                 return 1;
454         }
455
456         display = XOpenDisplay(NULL);
457
458         corrections = get_corrections(display);
459         if(corrections)
460         {
461                 for(i=0; corrections[i].monitor_name; ++i)
462                         if(!strcmp(corrections[i].monitor_name, argv[1]))
463                                 break;
464         }
465         else
466                 i = 0;
467
468         if(!corrections || !corrections[i].monitor_name)
469         {
470                 unsigned namelen;
471
472                 corrections = (GeometryCorrection *)realloc(corrections, (i+2)*sizeof(GeometryCorrection));
473                 namelen = strlen(argv[1]);
474                 corrections[i].monitor_name = (char *)malloc(namelen+1);
475                 strcpy(corrections[i].monitor_name, argv[1]);
476                 corrections[i+1].monitor_name = NULL;
477                 corrections[i].keystone_vertical = 0.0f;
478                 corrections[i].cylinder_depth = 0.0f;
479                 corrections[i].vertical_center = 0.5f;
480                 corrections[i].perspective = 1.0f;
481         }
482
483         if(argc==2)
484                 interactive(display, corrections, &corrections[i]);
485         else if(argc==6)
486         {
487                 corrections[i].keystone_vertical = strtod(argv[2], NULL);
488                 corrections[i].cylinder_depth = strtod(argv[3], NULL);
489                 corrections[i].vertical_center = strtod(argv[4], NULL);
490                 corrections[i].perspective = strtod(argv[5], NULL);
491
492                 set_corrections(display, corrections);
493         }
494
495         XCloseDisplay(display);
496         for(i=0; corrections[i].monitor_name; ++i)
497                 free(corrections[i].monitor_name);
498         free(corrections);
499
500         return 0;
501 }