]> git.tdb.fi Git - geometrycompositor.git/blobdiff - source/main.c
Dynamically reconfigure monitors based on XRandR changes
[geometrycompositor.git] / source / main.c
index a14ea8ded2f01568528d14eeae2b4eda03b5b3ca..ea508d285693031c08ea7b2d8827ed9413847be9 100644 (file)
@@ -32,6 +32,12 @@ typedef struct CompositedWindow
        int recreate_mask;
 } CompositedWindow;
 
+enum
+{
+       CYLINDRICAL = 1,
+       SPHERICAL
+};
+
 typedef struct CompositedMonitor
 {
        char *name;
@@ -41,7 +47,8 @@ typedef struct CompositedMonitor
        unsigned width;
        unsigned height;
        float keystone_vertical;
-       float cylinder_depth;
+       float curvature_type;
+       float curvature_depth;
        float vertical_center;
        float perspective;
        unsigned vertex_buffer;
@@ -91,6 +98,7 @@ typedef struct Compositor
        unsigned nscreens;
        int damage_event;
        int shape_event;
+       int randr_event;
        PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB;
        PFNGLXBINDTEXIMAGEEXTPROC glXBindTexImageEXT;
        PFNGLXRELEASETEXIMAGEEXTPROC glXReleaseTexImageEXT;
@@ -415,6 +423,8 @@ void update_window_mask(Compositor *compositor, CompositedWindow *window)
                return;
 
        rects = XShapeGetRectangles(compositor->display, window->window, ShapeBounding, &rect_count, &rect_order);
+       if(!rects)
+               return;
 
        width = window->width+2*window->border;
        height = window->height+2*window->border;
@@ -587,6 +597,48 @@ CompositedScreen *find_screen_by_root(Compositor *compositor, Window root)
        return NULL;
 }
 
+void create_geometry_correction(CompositedMonitor *monitor)
+{
+       unsigned buffers[2];
+       unsigned stride;
+
+       glGenBuffers(2, buffers);
+       monitor->vertex_buffer = buffers[0];
+       monitor->index_buffer = buffers[1];
+
+       monitor->tessellation = 50;
+       monitor->geometry_data_size = (monitor->tessellation+1)*(monitor->tessellation+1)*3+16;
+       monitor->geometry_data = (short *)malloc(monitor->geometry_data_size*sizeof(short));
+       stride = 4*sizeof(float);
+
+       glGenVertexArrays(1, &monitor->vertex_array);
+       glBindVertexArray(monitor->vertex_array);
+       glBindBuffer(GL_ARRAY_BUFFER, monitor->vertex_buffer);
+       glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, stride, NULL);
+       glEnableVertexAttribArray(0);
+       glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (void *)(2*sizeof(float)));
+       glEnableVertexAttribArray(1);
+       glBindBuffer(GL_ARRAY_BUFFER, 0);
+       glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, monitor->index_buffer);
+       glBindVertexArray(0);
+}
+
+void free_geometry_correction(CompositedMonitor *monitor)
+{
+       unsigned buffers[2];
+
+       buffers[0] = monitor->vertex_buffer;
+       buffers[1] = monitor->index_buffer;
+       glDeleteBuffers(2, buffers);
+       monitor->vertex_buffer = 0;
+       monitor->index_buffer = 0;
+
+       glDeleteVertexArrays(1, &monitor->vertex_array);
+
+       free(monitor->geometry_data);
+       monitor->geometry_data = 0;
+}
+
 void update_monitor_vertices(CompositedScreen *screen, CompositedMonitor *monitor)
 {
        unsigned t;
@@ -596,8 +648,8 @@ void update_monitor_vertices(CompositedScreen *screen, CompositedMonitor *monito
        unsigned x, y;
        unsigned i;
        float aspect;
-       float cyl_radius;
-       float cyl_arc;
+       float curve_radius;
+       float curve_arc;
        float sin_ksv;
        float cos_ksv;
        float distance;
@@ -611,15 +663,17 @@ void update_monitor_vertices(CompositedScreen *screen, CompositedMonitor *monito
 
        aspect = (float)monitor->width/monitor->height;
 
-       if(monitor->cylinder_depth)
+       if(monitor->curvature_depth)
        {
-               cyl_radius = (monitor->cylinder_depth*monitor->cylinder_depth+0.25f)/(2.0f*monitor->cylinder_depth);
-               cyl_arc = 2.0f*asin(0.5f/cyl_radius);
+               curve_radius = (monitor->curvature_depth*monitor->curvature_depth+0.25f)/(2.0f*monitor->curvature_depth);
+               curve_arc = 2.0f*asin(0.5f/curve_radius);
        }
 
        sin_ksv = monitor->keystone_vertical/sqrt(1.0f+monitor->keystone_vertical*monitor->keystone_vertical);
        cos_ksv = sqrt(1.0f-sin_ksv*sin_ksv);
        distance = monitor->perspective+sin_ksv*((sin_ksv>0)-monitor->vertical_center)/aspect;
+       if(monitor->curvature_depth<0)
+               distance += -monitor->curvature_depth;
 
        eye[0] = 0.0f;
        eye[1] = (monitor->vertical_center-0.5f)/aspect+sin_ksv*distance;
@@ -638,26 +692,40 @@ void update_monitor_vertices(CompositedScreen *screen, CompositedMonitor *monito
                        v[0] = (float)x/t-0.5f;
                        v[1] = ((float)y/t-0.5f)/aspect;
                        v[2] = 0;
-                       if(monitor->cylinder_depth)
+                       if(monitor->curvature_depth)
                        {
-                               v[2] = (1.0f-cos(v[0]*cyl_arc))*cyl_radius-monitor->cylinder_depth;
-                               v[0] = sin(v[0]*cyl_arc)*cyl_radius;
+                               if(monitor->curvature_type==CYLINDRICAL)
+                               {
+                                       v[2] = (1.0f-cos(v[0]*curve_arc))*curve_radius-monitor->curvature_depth;
+                                       v[0] = sin(v[0]*curve_arc)*curve_radius;
+                               }
+                               else if(monitor->curvature_type==SPHERICAL)
+                               {
+                                       float r;
+
+                                       v[0] = tan(v[0]*curve_arc)*curve_radius;
+                                       v[1] = tan(v[1]*curve_arc)*curve_radius;
+                                       r = sqrt(v[0]*v[0]+v[1]*v[1]+curve_radius*curve_radius)/fabs(curve_radius);
+                                       v[0] /= r;
+                                       v[1] /= r;
+                                       v[2] = curve_radius-curve_radius/r-monitor->curvature_depth;
+                               }
                        }
                }
 
        monitor->geometry_data[0] = t;
        for(i=0; i<3; ++i)
-               monitor->geometry_data[1+i] = eye[i]*10000;
+               monitor->geometry_data[1+i] = eye[i]*4096;
        for(y=0; y<2; ++y)
                for(x=0; x<2; ++x)
                {
                        i = 1+(1+y*2+x)*3;
-                       monitor->geometry_data[i] = ((x-0.5f)*distance/monitor->perspective)*10000;
-                       monitor->geometry_data[i+1] = (eye[1]+look[1]*distance-(y-0.5f)*look[2]*distance/monitor->perspective/aspect)*10000;
-                       monitor->geometry_data[i+2] = (eye[2]+look[2]*distance+(y-0.5f)*look[1]*distance/monitor->perspective/aspect)*10000;
+                       monitor->geometry_data[i] = ((x-0.5f)*distance/monitor->perspective)*4096;
+                       monitor->geometry_data[i+1] = (eye[1]+look[1]*distance-(y-monitor->vertical_center)*look[2]*distance/monitor->perspective/aspect)*4096;
+                       monitor->geometry_data[i+2] = (eye[2]+look[2]*distance+(y-monitor->vertical_center)*look[1]*distance/monitor->perspective/aspect)*4096;
                }
        for(i=0; i<(t+1)*(t+1)*3; ++i)
-               monitor->geometry_data[16+i] = vertex_data[i]*10000;
+               monitor->geometry_data[16+i] = vertex_data[i]*4096;
 
        for(y=t; y<=t; --y)
                for(x=t; x<=t; --x)
@@ -710,8 +778,6 @@ int initialize_monitor(Compositor *compositor, CompositedScreen *screen, XRRScre
        XRROutputInfo *output;
        int namelen;
        XRRCrtcInfo *crtc;
-       unsigned buffers[2];
-       unsigned stride;
 
        monitor = &screen->monitors[index];
 
@@ -723,6 +789,9 @@ int initialize_monitor(Compositor *compositor, CompositedScreen *screen, XRRScre
        if(!monitor->enabled)
        {
                XRRFreeOutputInfo(output);
+               monitor->vertex_buffer = 0;
+               monitor->index_buffer = 0;
+               monitor->vertex_array = 0;
                monitor->geometry_data = NULL;
                return 1;
        }
@@ -736,30 +805,13 @@ int initialize_monitor(Compositor *compositor, CompositedScreen *screen, XRRScre
        XRRFreeOutputInfo(output);
 
        monitor->keystone_vertical = 0.0f;
-       monitor->cylinder_depth = 0.0f;
+       monitor->curvature_type = CYLINDRICAL;
+       monitor->curvature_depth = 0.0f;
        monitor->vertical_center = 0.5f;
        monitor->perspective = 1.0f;
 
-       glGenBuffers(2, buffers);
-       monitor->vertex_buffer = buffers[0];
-       monitor->index_buffer = buffers[1];
-
-       monitor->tessellation = 50;
-       monitor->geometry_data_size = (monitor->tessellation+1)*(monitor->tessellation+1)*3+16;
-       monitor->geometry_data = (short *)malloc(monitor->geometry_data_size*sizeof(short));
+       create_geometry_correction(monitor);
        update_monitor_vertices(screen, monitor);
-       stride = 4*sizeof(float);
-
-       glGenVertexArrays(1, &monitor->vertex_array);
-       glBindVertexArray(monitor->vertex_array);
-       glBindBuffer(GL_ARRAY_BUFFER, monitor->vertex_buffer);
-       glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, stride, NULL);
-       glEnableVertexAttribArray(0);
-       glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (void *)(2*sizeof(float)));
-       glEnableVertexAttribArray(1);
-       glBindBuffer(GL_ARRAY_BUFFER, 0);
-       glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, monitor->index_buffer);
-       glBindVertexArray(0);
 
        return 1;
 }
@@ -793,6 +845,50 @@ CompositedMonitor *find_monitor_by_name_global(Compositor *compositor, char *nam
        return NULL;
 }
 
+void update_monitors(Compositor *compositor, CompositedScreen *screen)
+{
+       XRRScreenResources *xrr_res;
+       int i;
+
+       xrr_res = XRRGetScreenResources(compositor->display, screen->root);
+       for(i=0; i<xrr_res->noutput; ++i)
+       {
+               XRROutputInfo *output;
+               CompositedMonitor *monitor;
+               int was_enabled;
+               XRRCrtcInfo *crtc;
+
+               output = XRRGetOutputInfo(compositor->display, xrr_res, xrr_res->outputs[i]);
+               monitor = find_monitor_by_name(screen, output->name);
+               if(!monitor)
+                       continue;
+
+               was_enabled = monitor->enabled;
+               monitor->enabled = !!output->crtc;
+               if(!monitor->enabled)
+               {
+                       XRRFreeOutputInfo(output);
+                       if(was_enabled)
+                               free_geometry_correction(monitor);
+                       continue;
+               }
+
+               crtc = XRRGetCrtcInfo(compositor->display, xrr_res, output->crtc);
+               monitor->x = crtc->x;
+               monitor->y = crtc->y;
+               monitor->width = crtc->width;
+               monitor->height = crtc->height;
+               XRRFreeCrtcInfo(crtc);
+               XRRFreeOutputInfo(output);
+
+               if(!was_enabled)
+                       create_geometry_correction(monitor);
+
+               update_monitor_vertices(screen, monitor);
+       }
+       XRRFreeScreenResources(xrr_res);
+}
+
 void update_geometry_correction(Compositor *compositor, CompositedScreen *screen)
 {
        Atom prop_type;
@@ -821,7 +917,7 @@ void update_geometry_correction(Compositor *compositor, CompositedScreen *screen
        use_gl(compositor, screen);
 
        name_ptr = names;
-       for(i=0; i*4<values_length; ++i)
+       for(i=0; i*5+4<values_length; ++i)
        {
                CompositedMonitor *monitor;
 
@@ -831,10 +927,11 @@ void update_geometry_correction(Compositor *compositor, CompositedScreen *screen
                monitor = find_monitor_by_name(screen, name_ptr);
                if(monitor)
                {
-                       monitor->keystone_vertical = values[i*4]/10000.0f;
-                       monitor->cylinder_depth = values[i*4+1]/10000.0f;
-                       monitor->vertical_center = values[i*4+2]/10000.0f;
-                       monitor->perspective = values[i*4+3]/10000.0f;
+                       monitor->keystone_vertical = values[i*5]/4096.0f;
+                       monitor->curvature_type = values[i*5+1];
+                       monitor->curvature_depth = values[i*5+2]/4096.0f;
+                       monitor->vertical_center = values[i*5+3]/4096.0f;
+                       monitor->perspective = values[i*5+4]/4096.0f;
 
                        if(monitor->enabled)
                                update_monitor_vertices(screen, monitor);
@@ -912,6 +1009,7 @@ int initialize_screen(Compositor *compositor, unsigned number)
        screen->root = RootWindow(compositor->display, screen->number);
        XSelectInput(compositor->display, screen->root, SubstructureNotifyMask|PropertyChangeMask);
        screen->overlay = XCompositeGetOverlayWindow(compositor->display, screen->root);
+       XSelectInput(compositor->display, screen->overlay, StructureNotifyMask);
        XGetGeometry(compositor->display, screen->overlay, &dummy_root, &x, &y, &screen->width, &screen->height, &border, &depth);
        XShapeCombineRectangles(compositor->display, screen->overlay, ShapeInput, 0, 0, NULL, 0, ShapeSet, Unsorted);
 
@@ -930,6 +1028,7 @@ int initialize_screen(Compositor *compositor, unsigned number)
                if(!initialize_monitor(compositor, screen, xrr_res, i))
                        return 0;
        XRRFreeScreenResources(xrr_res);
+       XRRSelectInput(compositor->display, screen->root, RRScreenChangeNotifyMask);
 
        screen->root_pixmap = None;
        screen->root_glx_pixmap = None;
@@ -995,7 +1094,7 @@ int initialize_compositor(Compositor *compositor)
        else if(major_ver<1 || (major_ver==1 && minor_ver<1))
                return with_error("XShape 1.1 or later is required");
 
-       if(!XRRQueryExtension(compositor->display, &event_base, &error_base))
+       if(!XRRQueryExtension(compositor->display, &compositor->randr_event, &error_base))
                return with_error("XRandR is required but was not found");
        else if(!XRRQueryVersion(compositor->display, &major_ver, &minor_ver))
                return with_error("Cannot determine XRandR version");
@@ -1047,13 +1146,7 @@ void shutdown_screen(Compositor *compositor, CompositedScreen *screen)
        {
                free(screen->monitors[i].name);
                if(screen->monitors[i].enabled)
-               {
-                       glDeleteBuffers(1, &screen->monitors[i].vertex_buffer);
-                       glDeleteBuffers(1, &screen->monitors[i].index_buffer);
-                       glDeleteVertexArrays(1, &screen->monitors[i].vertex_array);
-               }
-               if(screen->monitors[i].geometry_data)
-                       free(screen->monitors[i].geometry_data);
+                       free_geometry_correction(&screen->monitors[i]);
        }
 
        glDeleteTextures(1, &screen->root_texture);
@@ -1188,6 +1281,19 @@ void process_configure_event(Compositor *compositor, XConfigureEvent *event)
        if(!screen)
                return;
 
+       if(event->window==screen->overlay)
+       {
+               screen->width = event->width;
+               screen->height = event->height;
+
+               XResizeWindow(compositor->display, screen->render_window, screen->width, screen->height);
+               use_gl(compositor, screen);
+               glBindTexture(GL_TEXTURE_2D, screen->fb_texture);
+               glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, screen->width, screen->height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
+               glBindTexture(GL_TEXTURE_2D, 0);
+               return;
+       }
+
        window = find_window(screen, event->window);
        if(!window)
                return;
@@ -1279,6 +1385,14 @@ void process_shape_event(Compositor *compositor, XShapeEvent *event)
        }
 }
 
+void process_randr_event(Compositor *compositor, XRRScreenChangeNotifyEvent *event)
+{
+       CompositedScreen *screen;
+
+       if((screen = find_screen_by_root(compositor, event->root)))
+               update_monitors(compositor, screen);
+}
+
 void process_events(Compositor *compositor)
 {
        int pending;
@@ -1325,8 +1439,8 @@ void process_events(Compositor *compositor)
                                process_damage_event(compositor, (XDamageNotifyEvent *)&event);
                        else if(event.type==compositor->shape_event+ShapeNotify)
                                process_shape_event(compositor, (XShapeEvent *)&event);
-                       else
-                               printf("Event %d\n", event.type);
+                       else if(event.type==compositor->randr_event+RRScreenChangeNotify)
+                               process_randr_event(compositor, (XRRScreenChangeNotifyEvent *)&event);
                }
        }
 }
@@ -1343,6 +1457,7 @@ void refresh_screen(Compositor *compositor, CompositedScreen *screen)
 
        use_gl(compositor, screen);
 
+       glViewport(0, 0, screen->width, screen->height);
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, screen->framebuffer);