#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <signal.h>
#include <X11/Xlib.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xdamage.h>
int y;
unsigned width;
unsigned height;
+ unsigned border;
int map_state;
Damage damage;
Pixmap pixmap;
1.0f, 0.0f
};
+int terminate_requested = 0;
+
+int x_error_handler(Display *display, XErrorEvent *event)
+{
+ printf("Ignoring X error %d on resource %lx\n", event->error_code, event->resourceid);
+ (void)display;
+ return 0;
+}
+
int with_error(const char *message)
{
fprintf(stderr, "%s\n", message);
return shader;
}
-int create_gl_resources(Compositor *compositor, CompositedScreen *screen)
+unsigned link_program(unsigned *shaders, unsigned nshaders)
{
+ unsigned program;
+ unsigned i;
int status;
char info_log[1024];
GLsizei length;
+ program = glCreateProgram();
+ for(i=0; i<nshaders; ++i)
+ glAttachShader(program, shaders[i]);
+ glBindAttribLocation(program, 0, "vertex");
+ glLinkProgram(program);
+
+ glGetProgramiv(program, GL_LINK_STATUS, &status);
+ glGetProgramInfoLog(program, sizeof(info_log), &length, info_log);
+ if(!status)
+ {
+ fprintf(stderr, "Program link failed:\n%s\n", info_log);
+ glDeleteProgram(program);
+ return 0;
+ }
+ else if(length)
+ printf("Program info log:\n%s\n", info_log);
+
+ return program;
+}
+
+int create_gl_resources(Compositor *compositor, CompositedScreen *screen)
+{
use_gl(compositor, screen);
screen->shaders[0] = compile_shader(GL_VERTEX_SHADER, vshader);
if(!screen->shaders[0] || !screen->shaders[1])
return 0;
- screen->program = glCreateProgram();
- glAttachShader(screen->program, screen->shaders[0]);
- glAttachShader(screen->program, screen->shaders[1]);
- glBindAttribLocation(screen->program, 0, "vertex");
- glLinkProgram(screen->program);
+ screen->program = link_program(screen->shaders, 2);
+ if(!screen->program)
+ return 0;
screen->geometry_loc = glGetUniformLocation(screen->program, "geometry");
- glGetProgramiv(screen->program, GL_LINK_STATUS, &status);
- glGetProgramInfoLog(screen->program, sizeof(info_log), &length, info_log);
- if(!status)
- {
- fprintf(stderr, "Program link failed:\n%s\n", info_log);
- glDeleteProgram(screen->program);
- return 0;
- }
- else if(length)
- printf("Program info log:\n%s\n", info_log);
-
glGenBuffers(1, &screen->vertex_buffer);
glBindBuffer(GL_ARRAY_BUFFER, screen->vertex_buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
window->glx_pixmap = glXCreatePixmap(compositor->display, screen->fbconfig, window->pixmap, attribs);
}
-void add_window(Compositor *compositor, CompositedScreen *screen, Window w)
+CompositedWindow *add_window(Compositor *compositor, CompositedScreen *screen, Window w)
{
CompositedWindow *window;
XWindowAttributes win_attr;
if(w==screen->root || w==screen->overlay)
- return;
+ return NULL;
- XGetWindowAttributes(compositor->display, w, &win_attr);
+ if(!XGetWindowAttributes(compositor->display, w, &win_attr))
+ {
+ printf("XGetWindowAttributes failed; probably the window was already destroyed\n");
+ return NULL;
+ }
if(win_attr.class==InputOnly)
- return;
+ return NULL;
if(find_window(screen, w))
- return;
+ return NULL;
if(screen->nwindows==screen->windows_capacity)
{
window->y = win_attr.y;
window->width = win_attr.width;
window->height = win_attr.height;
+ window->border = win_attr.border_width;
window->map_state = win_attr.map_state;
window->damage = XDamageCreate(compositor->display, window->window, XDamageReportNonEmpty);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ return window;
+}
+
+void remove_window(Compositor *compositor, CompositedScreen *screen, CompositedWindow *window, int destroyed)
+{
+ unsigned i;
+
+ glDeleteTextures(1, &window->texture);
+ if(!destroyed)
+ {
+ XDamageDestroy(compositor->display, window->damage);
+ if(window->pixmap)
+ {
+ glXDestroyPixmap(compositor->display, window->glx_pixmap);
+ XFreePixmap(compositor->display, window->pixmap);
+ }
+ XCompositeUnredirectWindow(compositor->display, window->window, CompositeRedirectManual);
+ }
+
+ --screen->nwindows;
+ for(i=window-screen->windows; i<screen->nwindows; ++i)
+ screen->windows[i] = screen->windows[i+1];
+}
+
+CompositedWindow *reorder_window(CompositedScreen *screen, CompositedWindow *window, Window above)
+{
+ unsigned i, j;
+ CompositedWindow hold;
+
+ i = window-screen->windows;
+ if(above)
+ {
+ for(j=0; j<screen->nwindows; ++j)
+ if(screen->windows[j].window==above)
+ break;
+
+ if(j>=screen->nwindows || i==j+1)
+ return window;
+
+ if(j<i)
+ ++j;
+ }
+ else
+ j = 0;
+
+ hold = *window;
+ if(i<j)
+ {
+ for(; i<j; ++i)
+ screen->windows[i] = screen->windows[i+1];
+ }
+ else
+ {
+ for(; i>j; --i)
+ screen->windows[i] = screen->windows[i-1];
+ }
+ screen->windows[j] = hold;
+
+ return &screen->windows[j];
}
CompositedScreen *find_screen_by_root(Compositor *compositor, Window root)
XFree(children);
+ screen->dirty = 1;
+
return 1;
}
if(!compositor->display)
return with_error("Could not open X display");
+ XSetErrorHandler(&x_error_handler);
+
if(!XCompositeQueryExtension(compositor->display, &event_base, &error_base))
return with_error("XComposite is required but was not found");
else if(!XCompositeQueryVersion(compositor->display, &major_ver, &minor_ver))
if(!initialize_screen(compositor, i))
return 0;
+ compositor->dirty = 1;
+
return 1;
}
void shutdown_screen(Compositor *compositor, CompositedScreen *screen)
{
+ unsigned i;
+
+ use_gl(compositor, screen);
+
+ for(i=0; i<screen->nwindows; ++i)
+ {
+ glDeleteTextures(1, &screen->windows[i].texture);
+ if(screen->windows[i].pixmap)
+ {
+ glXDestroyPixmap(compositor->display, screen->windows[i].glx_pixmap);
+ XFreePixmap(compositor->display, screen->windows[i].pixmap);
+ XDamageDestroy(compositor->display, screen->windows[i].damage);
+ }
+ }
+
+ glXMakeContextCurrent(compositor->display, 0, 0, NULL);
glXDestroyContext(compositor->display, screen->glx_context);
glXDestroyWindow(compositor->display, screen->glx_window);
XDestroyWindow(compositor->display, screen->render_window);
XCompositeReleaseOverlayWindow(compositor->display, screen->overlay);
+
+ free(screen->windows);
}
void shutdown_compositor(Compositor *compositor)
{
unsigned i;
- glXMakeContextCurrent(compositor->display, 0, 0, NULL);
for(i=0; i<compositor->nscreens; ++i)
shutdown_screen(compositor, &compositor->screens[i]);
+ free(compositor->screens);
+
+ XCloseDisplay(compositor->display);
+}
+
+void mark_dirty(Compositor *compositor, CompositedScreen *screen)
+{
+ compositor->dirty = 1;
+ screen->dirty = 1;
}
void process_create_window_event(Compositor *compositor, XCreateWindowEvent *event)
add_window(compositor, screen, event->window);
}
-void process_map_event(Compositor *compositor, XMapEvent *event)
+void process_destroy_window_event(Compositor *compositor, XDestroyWindowEvent *event)
{
- CompositedScreen *screen = find_screen_by_root(compositor, event->event);
+ CompositedScreen *screen;
+ CompositedWindow *window;
+
+ screen = find_screen_by_root(compositor, event->event);
if(!screen)
return;
- CompositedWindow *window = find_window(screen, event->window);
+ window = find_window(screen, event->window);
if(window)
- {
- window->map_state = IsViewable;
- create_window_pixmap(compositor, screen, window);
- }
+ remove_window(compositor, screen, window, 1);
+}
+
+void process_map_event(Compositor *compositor, XMapEvent *event)
+{
+ CompositedScreen *screen;
+ CompositedWindow *window;
+
+ screen = find_screen_by_root(compositor, event->event);
+ if(!screen)
+ return;
+
+ window = find_window(screen, event->window);
+ if(!window)
+ return;
+
+ window->map_state = IsViewable;
+ create_window_pixmap(compositor, screen, window);
+
+ mark_dirty(compositor, screen);
}
void process_unmap_event(Compositor *compositor, XUnmapEvent *event)
{
- CompositedScreen *screen = find_screen_by_root(compositor, event->event);
+ CompositedScreen *screen;
+ CompositedWindow *window;
+
+ screen = find_screen_by_root(compositor, event->event);
if(!screen)
return;
- CompositedWindow *window = find_window(screen, event->window);
+ window = find_window(screen, event->window);
if(window)
window->map_state = IsUnviewable;
+
+ mark_dirty(compositor, screen);
+}
+
+void process_reparent_event(Compositor *compositor, XReparentEvent *event)
+{
+ CompositedScreen *screen;
+ CompositedWindow *window;
+
+ screen = find_screen_by_root(compositor, event->event);
+ if(!screen)
+ return;
+
+ if(event->parent==screen->root)
+ window = add_window(compositor, screen, event->window);
+ else
+ {
+ window = find_window(screen, event->window);
+ if(!window)
+ return;
+
+ remove_window(compositor, screen, window, 0);
+ }
+
+ if(window && window->map_state==IsViewable)
+ mark_dirty(compositor, screen);
+}
+
+void process_configure_event(Compositor *compositor, XConfigureEvent *event)
+{
+ CompositedScreen *screen;
+ CompositedWindow *window;
+
+ screen = find_screen_by_root(compositor, event->event);
+ if(!screen)
+ return;
+
+ window = find_window(screen, event->window);
+ if(!window)
+ return;
+
+ window->x = event->x;
+ window->y = event->y;
+ if((unsigned)event->width!=window->width || (unsigned)event->height!=window->height || (unsigned)event->border_width!=window->border)
+ {
+ window->width = event->width;
+ window->height = event->height;
+ window->border = event->border_width;
+ create_window_pixmap(compositor, screen, window);
+ }
+ reorder_window(screen, window, event->above);
+
+ if(window->map_state==IsViewable)
+ mark_dirty(compositor, screen);
}
void process_damage_event(Compositor *compositor, XDamageNotifyEvent *event)
{
- CompositedScreen *screen = find_screen_by_window(compositor, event->drawable);
+ CompositedScreen *screen;
+ CompositedWindow *window;
+
+ screen = find_screen_by_window(compositor, event->drawable);
if(!screen)
return;
- screen->dirty = 1;
- compositor->dirty = 1;
+ window = find_window(screen, event->drawable);
+ if(window->map_state==IsViewable)
+ mark_dirty(compositor, screen);
}
int process_event(Compositor *compositor)
case CreateNotify:
process_create_window_event(compositor, &event.xcreatewindow);
break;
+ case DestroyNotify:
+ process_destroy_window_event(compositor, &event.xdestroywindow);
+ break;
case MapNotify:
process_map_event(compositor, &event.xmap);
break;
case UnmapNotify:
process_unmap_event(compositor, &event.xunmap);
break;
+ case ReparentNotify:
+ process_reparent_event(compositor, &event.xreparent);
+ break;
+ case ConfigureNotify:
+ process_configure_event(compositor, &event.xconfigure);
+ break;
default:
if(event.type==compositor->damage_event+XDamageNotify)
process_damage_event(compositor, (XDamageNotifyEvent *)&event);
for(i=0; i<compositor->nscreens; ++i)
{
CompositedScreen *screen = &compositor->screens[i];
+ if(!screen->dirty)
+ continue;
+
use_gl(compositor, screen);
glClearColor(0.5f, 0.5f, 0.5f, 0.0f);
glUseProgram(screen->program);
glBindVertexArray(screen->vertex_array);
- XGrabServer(compositor->display);
- glXWaitX();
for(j=0; j<screen->nwindows; ++j)
{
CompositedWindow *window = &screen->windows[j];
{
glBindTexture(GL_TEXTURE_2D, window->texture);
compositor->glXBindTexImageEXT(compositor->display, window->glx_pixmap, GLX_FRONT_LEFT_EXT, NULL);
- glUniform4f(screen->geometry_loc, (float)window->x/screen->width, (float)(screen->height-window->y-window->height)/screen->height, (float)window->width/screen->width, (float)window->height/screen->height);
+ glUniform4f(screen->geometry_loc, (float)window->x/screen->width, (float)(screen->height-window->y-window->height)/screen->height, (float)(window->width+2*window->border)/screen->width, (float)(window->height+2*window->border)/screen->height);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
compositor->glXReleaseTexImageEXT(compositor->display, window->glx_pixmap, GLX_FRONT_LEFT_EXT);
XDamageSubtract(compositor->display, window->damage, None, None);
}
}
- XUngrabServer(compositor->display);
glXSwapBuffers(compositor->display, screen->glx_window);
screen->dirty = 0;
compositor->dirty = 0;
}
+void sighandler(int sig)
+{
+ terminate_requested = 1;
+ (void)sig;
+}
+
int main()
{
Compositor compositor;
+ signal(SIGINT, &sighandler);
+ signal(SIGTERM, &sighandler);
+
if(!initialize_compositor(&compositor))
return 1;
- while(1)
+ while(!terminate_requested)
{
if(!process_event(&compositor))
refresh_screens(&compositor);