#include "compositor.h" #include #include #include #include #include /* TODO: Handle wallpaper change and update on root window resize */ static double clock_get_monotonic_seconds(void) { struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = 0; clock_gettime(CLOCK_MONOTONIC, &ts); return (double)ts.tv_sec + (double)ts.tv_nsec * 0.000000001; } static ngxc_window* ngxc_compositor_get_window_by_id(ngxc_compositor *self, Window window, int *index) { for(int i = 0; i < self->num_windows; ++i) { ngxc_window *window_obj = &self->windows[i]; if(window_obj->texture.window == window) { if(index) *index = i; return window_obj; } } return NULL; } static Pixmap x11_wallpaper_get_pixmap(ngxc_compositor *self) { const char *background_props[] = { "_XROOTPMAP_ID", "_XSETROOT_ID", NULL }; const Window root_window = DefaultRootWindow(self->dpy); Pixmap wallpaper_pixmap = None; for(int i = 0; background_props[i]; ++i) { const Atom atom = XInternAtom(self->dpy, background_props[i], False); Atom actual_type; int actual_format; unsigned long items_count; unsigned long bytes_after; unsigned char *prop = NULL; if(XGetWindowProperty(self->dpy, root_window, atom, 0, 1, False, XA_PIXMAP, &actual_type, &actual_format, &items_count, &bytes_after, &prop) == Success && prop) { wallpaper_pixmap = *(Pixmap*)prop; XFree(prop); break; } if(prop) XFree(prop); } return wallpaper_pixmap; } void ngxc_compositor_init(ngxc_compositor *self, Display *dpy, Window composite_window) { memset(self, 0, sizeof(*self)); self->dpy = dpy; self->composite_window = composite_window; self->frame_timer = clock_get_monotonic_seconds(); const Pixmap wallpaper_pixmap = x11_wallpaper_get_pixmap(self); if(!ngxc_pixmap_texture_init(&self->wallpaper_texture, self->dpy, wallpaper_pixmap)) { fprintf(stderr, "error: failed to create texture for wallpaper pixmap %ld\n", wallpaper_pixmap); } } void ngxc_compositor_deinit(ngxc_compositor *self) { /* TODO: Cleanup? */ } void ngxc_compositor_add_window(ngxc_compositor *self, Window window) { if(self->num_windows == NGXC_COMPOSITOR_MAX_WINDOWS) { fprintf(stderr, "error: reached max number of trackable windows (%d), ignoring window with id %ld\n", NGXC_COMPOSITOR_MAX_WINDOWS, window); return; } XWindowAttributes xattr; if(!XGetWindowAttributes(self->dpy, window, &xattr)) { fprintf(stderr, "error: failed to get window attributes, ignoring window with id %ld\n", window); return; } //Window c; //XTranslateCoordinates(self->dpy, window, DefaultRootWindow(self->dpy), 0, 0, &xattr.x, &xattr.y, &c); ngxc_window *window_obj = &self->windows[self->num_windows]; if(window_texture_init(&window_obj->texture, self->dpy, window) != 0) { fprintf(stderr, "error: failed to create window texture, ignoring window with id %ld\n", window); //return; } window_obj->x = xattr.x; window_obj->y = xattr.y; self->num_windows++; fprintf(stderr, "ADDED WINDOW (#%d): %ld, x: %d, y: %d\n", self->num_windows, window, xattr.x, xattr.y); } static void ngxc_compositor_remove_window_index(ngxc_compositor *self, int window_index) { for(int i = window_index + 1; i < self->num_windows; ++i) { self->windows[i - 1] = self->windows[i]; } self->num_windows--; } static void ngxc_compositor_insert_window_index(ngxc_compositor *self, const ngxc_window *window, int index) { if(self->num_windows == NGXC_COMPOSITOR_MAX_WINDOWS) { fprintf(stderr, "error: reached max number of trackable windows (%d), ignoring window with id %ld\n", NGXC_COMPOSITOR_MAX_WINDOWS, window->texture.window); return; } self->num_windows++; for(int i = self->num_windows - 1; i > index; --i) { self->windows[i] = self->windows[i - 1]; } self->windows[index] = *window; } static void ngxc_compositor_append_window(ngxc_compositor *self, const ngxc_window *window) { if(self->num_windows == NGXC_COMPOSITOR_MAX_WINDOWS) { fprintf(stderr, "error: reached max number of trackable windows (%d), ignoring window with id %ld\n", NGXC_COMPOSITOR_MAX_WINDOWS, window->texture.window); return; } self->windows[self->num_windows] = *window; self->num_windows++; } void ngxc_compositor_remove_window(ngxc_compositor *self, Window window) { int window_index; if(ngxc_compositor_get_window_by_id(self, window, &window_index)) { window_texture_deinit(&self->windows[window_index].texture); ngxc_compositor_remove_window_index(self, window_index); } } static void render_texture(GLuint texture_id, int x, int y, int width, int height) { float matrix[16] = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, }; matrix[0] = 1.0f / (float)width; matrix[5] = 1.0f / (float)height; glMatrixMode(GL_TEXTURE); glLoadMatrixf(matrix); glMatrixMode(GL_MODELVIEW); glBindTexture(GL_TEXTURE_2D, texture_id); glColor4ub(255, 255, 255, 255); //glTranslatef(x, y, 0.0f); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex2f(x, y); glTexCoord2f(width, 0.0f); glVertex2f(x + width, y); glTexCoord2f(width, height); glVertex2f(x + width, y + height); glTexCoord2f(0.0f, height); glVertex2f(x, y + height); glEnd(); glLoadIdentity(); glBindTexture(GL_TEXTURE_2D, 0); } /* TODO: Skip windows that are not visible on the screen */ /* TODO: Disable compositing for fullscreen windows, if that option is enabled. Or only draw the fullscreen window */ void ngxc_compositor_render(ngxc_compositor *self) { glClearColor(0.5f, 0.5f, 0.5f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); if(self->wallpaper_texture.texture_id > 0) { //render_texture(self->wallpaper_texture.texture_id, 0, 0, self->wallpaper_texture.width, self->wallpaper_texture.height); } for(int i = 0; i < self->num_windows; ++i) { const ngxc_window *window_obj = &self->windows[i]; if(window_obj->texture.texture_id > 0) render_texture(window_obj->texture.texture_id, window_obj->x, window_obj->y, window_obj->texture.width, window_obj->texture.height); } const double now = clock_get_monotonic_seconds(); const double frame_duration = now - self->frame_timer; glXSwapBuffers(self->dpy, self->composite_window); glFlush(); glFinish(); const double time_limit = 1.0f / 60.0f; // TODO: Get from randr. This might not be a good way to do this when using multiple monitors with different refresh rate and opengl is synced to one monitor const double delayed_update = time_limit - frame_duration - 0.002; //fprintf(stderr, "delayed update: %f\n", delayed_update); if(delayed_update > 0.0) usleep(delayed_update * 1000000.0); self->frame_timer = clock_get_monotonic_seconds(); } static void ngxc_compositor_move_window_after(ngxc_compositor *self, int window_index, Window above_window) { if(above_window == None) { const ngxc_window window_obj_copy = self->windows[window_index]; ngxc_compositor_remove_window_index(self, window_index); ngxc_compositor_append_window(self, &window_obj_copy); fprintf(stderr, "above window none, move to top\n"); return; } int above_window_index; if(!ngxc_compositor_get_window_by_id(self, above_window, &above_window_index)) { fprintf(stderr, "error: unable to find above window %ld in compositor list\n", above_window); return; //ngxc_compositor_move_window_after(self, window_index, None); //return; } const ngxc_window window_obj_copy = self->windows[window_index]; ngxc_compositor_remove_window_index(self, window_index); if(above_window_index + 1 >= self->num_windows) { ngxc_compositor_append_window(self, &window_obj_copy); fprintf(stderr, "above, move to top\n"); } else { ngxc_compositor_insert_window_index(self, &window_obj_copy, above_window_index + 1); fprintf(stderr, "above window after %ld\n", above_window); } } static Window ngxc_compositor_get_above_window(ngxc_compositor *self, int window_index) { if(window_index == 0) return None; return self->windows[window_index - 1].texture.window; } void ngxc_compositor_on_configure(ngxc_compositor *self, const XConfigureEvent *configure_event) { int window_index; ngxc_window *window_obj = ngxc_compositor_get_window_by_id(self, configure_event->window, &window_index); if(!window_obj) return; window_obj->x = configure_event->x; window_obj->y = configure_event->y; if(configure_event->width != window_obj->texture.width || configure_event->height != window_obj->texture.height) { window_texture_on_resize(&window_obj->texture); } if(configure_event->above != ngxc_compositor_get_above_window(self, window_index) && configure_event->above != window_obj->texture.window) { ngxc_compositor_move_window_after(self, window_index, configure_event->above); } } void ngxc_compositor_on_circulate(ngxc_compositor *self, const XCirculateEvent *circulate_event) { int window_index; ngxc_window *window_obj = ngxc_compositor_get_window_by_id(self, circulate_event->window, &window_index); if(!window_obj) return; Window above_window = None; if(circulate_event->place == PlaceOnTop) above_window = None; else if(circulate_event->place == PlaceOnBottom) above_window = self->windows[self->num_windows - 1].texture.window; if(above_window != ngxc_compositor_get_above_window(self, window_index) && above_window != window_obj->texture.window) { ngxc_compositor_move_window_after(self, window_index, above_window); } }