#include "compositor.h" #include #include #include #include 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) { for(int i = 0; i < self->num_windows; ++i) { ngxc_window *window_obj = &self->windows[i]; if(window_obj->texture.window == window) return window_obj; } return NULL; } 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(); } void ngxc_compositor_deinit(ngxc_compositor *self) { } void ngxc_compositor_add_window(ngxc_compositor *self, Window window, Window above_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->above_window = above_window; window_obj->x = xattr.x; window_obj->y = xattr.y; self->num_windows++; fprintf(stderr, "ADDED WINDOW: %ld, x: %d, y: %d\n", 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) { 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) { for(int i = 0; i < self->num_windows; ++i) { if(self->windows[i].texture.window != window) continue; window_texture_deinit(&self->windows[i].texture); ngxc_compositor_remove_window_index(self, i); break; } } static void render_texture(const ngxc_window *window) { 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)window->texture.width; matrix[5] = 1.0f / (float)window->texture.height; glMatrixMode(GL_TEXTURE); glLoadMatrixf(matrix); glMatrixMode(GL_MODELVIEW); glBindTexture(GL_TEXTURE_2D, window->texture.texture_id); glColor4ub(255, 255, 255, 255); //glTranslatef(window->x, window->y, 0.0f); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex2f(window->x, window->y); glTexCoord2f(window->texture.width, 0.0f); glVertex2f(window->x + window->texture.width, window->y); glTexCoord2f(window->texture.width, window->texture.height); glVertex2f(window->x + window->texture.width, window->y + window->texture.height); glTexCoord2f(0.0f, window->texture.height); glVertex2f(window->x, window->y + window->texture.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 */ void ngxc_compositor_render(ngxc_compositor *self) { glClearColor(1.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); for(int i = 0; i < self->num_windows; ++i) { const ngxc_window *window_obj = &self->windows[i]; render_texture(window_obj); } 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 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, ngxc_window *window_obj, Window above_window) { int window_index = -1; for(int i = 0; i < self->num_windows; ++i) { if(self->windows[i].texture.window == window_obj->texture.window) { window_index = i; break; } } if(window_index == -1) { fprintf(stderr, "error: unable to find window %ld in compositor list\n", window_obj->texture.window); return; } if(above_window == None) { const ngxc_window window_obj_copy = *window_obj; 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 = -1; for(int i = 0; i < self->num_windows; ++i) { if(self->windows[i].texture.window == above_window) { above_window_index = i; break; } } if(above_window_index == -1) { fprintf(stderr, "error: unable to find above window %ld in compositor list\n", above_window); return; } const ngxc_window window_obj_copy = *window_obj; 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); } } void ngxc_compositor_on_configure(ngxc_compositor *self, const XConfigureEvent *configure_event) { ngxc_window *window_obj = ngxc_compositor_get_window_by_id(self, configure_event->window); 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 != window_obj->above_window && configure_event->above != window_obj->texture.window) { window_obj->above_window = configure_event->above; // TODO: If above window is none then get the topmost window and use that ngxc_compositor_move_window_after(self, window_obj, configure_event->above); } } void ngxc_compositor_on_circulate(ngxc_compositor *self, const XCirculateEvent *circulate_event) { ngxc_window *window_obj = ngxc_compositor_get_window_by_id(self, circulate_event->window); 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 != window_obj->above_window && above_window != window_obj->texture.window) { window_obj->above_window = above_window; // TODO: If above window is none then get the topmost window and use that ngxc_compositor_move_window_after(self, window_obj, above_window); } }