+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+typedef struct GeometryCorrection
+{
+ char *monitor_name;
+ float keystone_vertical;
+ float cylinder_depth;
+ float vertical_center;
+ float perspective;
+} GeometryCorrection;
+
+GeometryCorrection *get_corrections(Display *display)
+{
+ Window root;
+ Atom monitors_atom;
+ Atom correction_atom;
+ Atom prop_type;
+ int prop_format;
+ unsigned long overflow;
+ unsigned long names_length;
+ char *names;
+ unsigned long values_length;
+ short *values;
+ unsigned ncorrections;
+ unsigned i;
+ GeometryCorrection *corrections;
+ char *name_ptr;
+
+ root = DefaultRootWindow(display);
+ monitors_atom = XInternAtom(display, "GEOMETRY_CORRECTION_MONITORS", False);
+ correction_atom = XInternAtom(display, "GEOMETRY_CORRECTION", False);
+
+ XGetWindowProperty(display, root, monitors_atom, 0, 64, False, XA_STRING,
+ &prop_type, &prop_format, &names_length, &overflow, (unsigned char **)&names);
+ if(prop_type!=XA_STRING || prop_format!=8)
+ return NULL;
+
+ XGetWindowProperty(display, root, correction_atom, 0, 64, False, XA_INTEGER,
+ &prop_type, &prop_format, &values_length, &overflow, (unsigned char **)&values);
+ if(prop_type!=XA_INTEGER || prop_format!=16)
+ {
+ XFree(names);
+ return NULL;
+ }
+
+ ncorrections = 0;
+ for(i=0; i<names_length; ++i)
+ if(!names[i])
+ ++ncorrections;
+ if(ncorrections*4>values_length)
+ ncorrections = values_length/4;
+ corrections = (GeometryCorrection *)malloc((ncorrections+1)*sizeof(GeometryCorrection));
+
+ name_ptr = names;
+ for(i=0; i<ncorrections; ++i)
+ {
+ unsigned namelen;
+
+ namelen = strlen(name_ptr);
+ corrections[i].monitor_name = (char *)malloc(namelen+1);
+ strcpy(corrections[i].monitor_name, name_ptr);
+
+ corrections[i].keystone_vertical = values[i*4]/10000.0f;
+ corrections[i].cylinder_depth = values[i*4+1]/10000.0f;
+ corrections[i].vertical_center = values[i*4+2]/10000.0f;
+ corrections[i].perspective = values[i*4+3]/10000.0f;
+
+ name_ptr += namelen+1;
+ }
+
+ corrections[ncorrections].monitor_name = NULL;
+
+ XFree(names);
+ XFree(values);
+
+ return corrections;
+}
+
+void set_corrections(Display *display, GeometryCorrection *corrections)
+{
+ unsigned ncorrections;
+ unsigned total_len;
+ unsigned i;
+ char *names;
+ char *name_ptr;
+ short *values;
+ Window root;
+ Atom monitors_atom;
+ Atom correction_atom;
+
+ ncorrections = 0;
+ total_len = 0;
+ for(i=0; corrections[i].monitor_name; ++i)
+ {
+ ++ncorrections;
+ total_len += strlen(corrections[i].monitor_name);
+ }
+
+ names = (char *)malloc(total_len+ncorrections);
+ values = (short *)malloc(ncorrections*4*sizeof(short));
+ name_ptr = names;
+ for(i=0; i<ncorrections; ++i)
+ {
+ strcpy(name_ptr, corrections[i].monitor_name);
+ name_ptr += strlen(name_ptr)+1;
+
+ values[i*4] = corrections[i].keystone_vertical*10000;
+ values[i*4+1] = corrections[i].cylinder_depth*10000;
+ values[i*4+2] = corrections[i].vertical_center*10000;
+ values[i*4+3] = corrections[i].perspective*10000;
+ }
+
+ root = DefaultRootWindow(display);
+ monitors_atom = XInternAtom(display, "GEOMETRY_CORRECTION_MONITORS", False);
+ correction_atom = XInternAtom(display, "GEOMETRY_CORRECTION", False);
+ XChangeProperty(display, root, monitors_atom, XA_STRING, 8, PropModeReplace, (unsigned char *)names, total_len+ncorrections-1);
+ XChangeProperty(display, root, correction_atom, XA_INTEGER, 16, PropModeReplace, (unsigned char *)values, ncorrections*4);
+
+ free(names);
+ free(values);
+}
+
+int main(int argc, char **argv)
+{
+ Display *display;
+ GeometryCorrection *corrections;
+ unsigned i;
+
+ if(argc!=6)
+ {
+ fprintf(stderr, "Usage: %s <monitor> <keystone> <cylinder> <vcenter> <perspective>\n", argv[0]);
+ return 1;
+ }
+
+ display = XOpenDisplay(NULL);
+
+ corrections = get_corrections(display);
+ if(corrections)
+ {
+ for(i=0; corrections[i].monitor_name; ++i)
+ if(!strcmp(corrections[i].monitor_name, argv[1]))
+ break;
+ }
+ else
+ i = 0;
+
+ if(!corrections || !corrections[i].monitor_name)
+ {
+ unsigned namelen;
+
+ corrections = (GeometryCorrection *)realloc(corrections, (i+2)*sizeof(GeometryCorrection));
+ namelen = strlen(argv[1]);
+ corrections[i].monitor_name = (char *)malloc(namelen+1);
+ strcpy(corrections[i].monitor_name, argv[1]);
+ corrections[i+1].monitor_name = NULL;
+ }
+
+ corrections[i].keystone_vertical = strtod(argv[2], NULL);
+ corrections[i].cylinder_depth = strtod(argv[3], NULL);
+ corrections[i].vertical_center = strtod(argv[4], NULL);
+ corrections[i].perspective = strtod(argv[5], NULL);
+
+ set_corrections(display, corrections);
+
+ XCloseDisplay(display);
+
+ return 0;
+}