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