aboutsummaryrefslogtreecommitdiff
path: root/src/damage.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/damage.c')
-rw-r--r--src/damage.c229
1 files changed, 210 insertions, 19 deletions
diff --git a/src/damage.c b/src/damage.c
index 1e68e6e..2eda5d7 100644
--- a/src/damage.c
+++ b/src/damage.c
@@ -1,68 +1,259 @@
#include "../include/damage.h"
+#include "../include/utils.h"
#include <stdio.h>
#include <string.h>
#include <X11/extensions/Xdamage.h>
+#include <X11/extensions/Xrandr.h>
-bool gsr_damage_init(gsr_damage *self, Display *display) {
+typedef struct {
+ vec2i pos;
+ vec2i size;
+} gsr_rectangle;
+
+static bool rectangles_intersect(gsr_rectangle rect1, gsr_rectangle rect2) {
+ return rect1.pos.x < rect2.pos.x + rect2.size.x && rect1.pos.x + rect1.size.x > rect2.pos.x &&
+ rect1.pos.y < rect2.pos.y + rect2.size.y && rect1.pos.y + rect1.size.y > rect2.pos.y;
+}
+
+static bool xrandr_is_supported(Display *display) {
+ int major_version = 0;
+ int minor_version = 0;
+ if(!XRRQueryVersion(display, &major_version, &minor_version))
+ return false;
+
+ return major_version > 1 || (major_version == 1 && minor_version >= 2);
+}
+
+bool gsr_damage_init(gsr_damage *self, gsr_egl *egl, bool track_cursor) {
memset(self, 0, sizeof(*self));
- self->display = display;
+ self->egl = egl;
+ self->track_cursor = track_cursor;
+
+ if(gsr_egl_get_display_server(egl) != GSR_DISPLAY_SERVER_X11) {
+ fprintf(stderr, "gsr warning: gsr_damage_init: damage tracking is not supported on wayland\n");
+ return false;
+ }
- if(!XDamageQueryExtension(self->display, &self->damage_event, &self->damage_error)) {
+ if(!XDamageQueryExtension(self->egl->x11.dpy, &self->damage_event, &self->damage_error)) {
fprintf(stderr, "gsr warning: gsr_damage_init: XDamage is not supported by your X11 server\n");
- self->damage_event = 0;
- self->damage_error = 0;
+ gsr_damage_deinit(self);
return false;
}
+ if(!XRRQueryExtension(self->egl->x11.dpy, &self->randr_event, &self->randr_error)) {
+ fprintf(stderr, "gsr warning: gsr_damage_init: XRandr is not supported by your X11 server\n");
+ gsr_damage_deinit(self);
+ return false;
+ }
+
+ if(!xrandr_is_supported(self->egl->x11.dpy)) {
+ fprintf(stderr, "gsr warning: gsr_damage_init: your X11 randr version is too old\n");
+ gsr_damage_deinit(self);
+ return false;
+ }
+
+ XRRSelectInput(self->egl->x11.dpy, DefaultRootWindow(self->egl->x11.dpy), RRScreenChangeNotifyMask | RRCrtcChangeNotifyMask | RROutputChangeNotifyMask);
+
self->damaged = true;
return true;
}
void gsr_damage_deinit(gsr_damage *self) {
if(self->damage) {
- XDamageDestroy(self->display, self->damage);
+ XDamageDestroy(self->egl->x11.dpy, self->damage);
self->damage = None;
}
+
+ self->damage_event = 0;
+ self->damage_error = 0;
+
+ self->randr_event = 0;
+ self->randr_error = 0;
}
bool gsr_damage_set_target_window(gsr_damage *self, uint64_t window) {
if(self->damage_event == 0)
return false;
+ if(window == self->window)
+ return true;
+
if(self->damage) {
- XDamageDestroy(self->display, self->damage);
+ XDamageDestroy(self->egl->x11.dpy, self->damage);
self->damage = None;
}
- self->damage = XDamageCreate(self->display, window, XDamageReportNonEmpty);
+ self->window = window;
+ self->damage = XDamageCreate(self->egl->x11.dpy, window, XDamageReportNonEmpty);
if(self->damage) {
- XDamageSubtract(self->display, self->damage, None, None);
+ XDamageSubtract(self->egl->x11.dpy, self->damage, None, None);
self->damaged = true;
+ self->track_type = GSR_DAMAGE_TRACK_WINDOW;
return true;
} else {
fprintf(stderr, "gsr warning: gsr_damage_set_target_window: XDamageCreate failed\n");
+ self->track_type = GSR_DAMAGE_TRACK_NONE;
return false;
}
}
-void gsr_damage_update(gsr_damage *self, XEvent *xev) {
- if(self->damage_event == 0 || !self->damage)
+bool gsr_damage_set_target_monitor(gsr_damage *self, const char *monitor_name) {
+ if(self->damage_event == 0)
+ return false;
+
+ if(strcmp(self->monitor_name, monitor_name) == 0)
+ return true;
+
+ if(self->damage) {
+ XDamageDestroy(self->egl->x11.dpy, self->damage);
+ self->damage = None;
+ }
+
+ memset(&self->monitor, 0, sizeof(self->monitor));
+ if(!get_monitor_by_name(self->egl, GSR_CONNECTION_X11, monitor_name, &self->monitor))
+ fprintf(stderr, "gsr warning: gsr_damage_set_target_monitor: failed to find monitor: %s\n", monitor_name);
+
+ self->window = DefaultRootWindow(self->egl->x11.dpy);
+ self->damage = XDamageCreate(self->egl->x11.dpy, self->window, XDamageReportNonEmpty);
+ if(self->damage) {
+ XDamageSubtract(self->egl->x11.dpy, self->damage, None, None);
+ self->damaged = true;
+ snprintf(self->monitor_name, sizeof(self->monitor_name), "%s", monitor_name);
+ self->track_type = GSR_DAMAGE_TRACK_MONITOR;
+ return true;
+ } else {
+ fprintf(stderr, "gsr warning: gsr_damage_set_target_monitor: XDamageCreate failed\n");
+ self->track_type = GSR_DAMAGE_TRACK_NONE;
+ return false;
+ }
+}
+
+static void gsr_damage_on_crtc_change(gsr_damage *self, XEvent *xev) {
+ const XRRCrtcChangeNotifyEvent *rr_crtc_change_event = (XRRCrtcChangeNotifyEvent*)xev;
+ if(rr_crtc_change_event->crtc == 0 || self->monitor.monitor_identifier == 0)
+ return;
+
+ if(rr_crtc_change_event->crtc != self->monitor.monitor_identifier)
+ return;
+
+ if(rr_crtc_change_event->width == 0 || rr_crtc_change_event->height == 0)
return;
- if(self->damage_event && xev->type == self->damage_event + XDamageNotify) {
- XDamageNotifyEvent *de = (XDamageNotifyEvent*)xev;
- XserverRegion region = XFixesCreateRegion(self->display, NULL, 0);
- /* Subtract all the damage, repairing the window */
- XDamageSubtract(self->display, de->damage, None, region);
- XFixesDestroyRegion(self->display, region);
- XFlush(self->display);
+ if(rr_crtc_change_event->x != self->monitor.pos.x || rr_crtc_change_event->y != self->monitor.pos.y ||
+ (int)rr_crtc_change_event->width != self->monitor.size.x || (int)rr_crtc_change_event->height != self->monitor.size.y) {
+ self->monitor.pos.x = rr_crtc_change_event->x;
+ self->monitor.pos.y = rr_crtc_change_event->y;
+
+ self->monitor.size.x = rr_crtc_change_event->width;
+ self->monitor.size.y = rr_crtc_change_event->height;
+ }
+}
+
+static void gsr_damage_on_output_change(gsr_damage *self, XEvent *xev) {
+ const XRROutputChangeNotifyEvent *rr_output_change_event = (XRROutputChangeNotifyEvent*)xev;
+ if(!rr_output_change_event->output || self->monitor.monitor_identifier == 0)
+ return;
+
+ XRRScreenResources *screen_res = XRRGetScreenResources(self->egl->x11.dpy, DefaultRootWindow(self->egl->x11.dpy));
+ if(!screen_res)
+ return;
+
+ XRROutputInfo *out_info = XRRGetOutputInfo(self->egl->x11.dpy, screen_res, rr_output_change_event->output);
+ if(out_info && out_info->crtc && out_info->crtc == self->monitor.monitor_identifier) {
+ XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(self->egl->x11.dpy, screen_res, out_info->crtc);
+ if(crtc_info && (crtc_info->x != self->monitor.pos.x || crtc_info->y != self->monitor.pos.y ||
+ (int)crtc_info->width != self->monitor.size.x || (int)crtc_info->height != self->monitor.size.y))
+ {
+ self->monitor.pos.x = crtc_info->x;
+ self->monitor.pos.y = crtc_info->y;
+
+ self->monitor.size.x = crtc_info->width;
+ self->monitor.size.y = crtc_info->height;
+ }
+
+ if(crtc_info)
+ XRRFreeCrtcInfo(crtc_info);
+ }
+
+ if(out_info)
+ XRRFreeOutputInfo(out_info);
+
+ XRRFreeScreenResources(screen_res);
+}
+
+static void gsr_damage_on_randr_event(gsr_damage *self, XEvent *xev) {
+ const XRRNotifyEvent *rr_event = (XRRNotifyEvent*)xev;
+ switch(rr_event->subtype) {
+ case RRNotify_CrtcChange:
+ gsr_damage_on_crtc_change(self, xev);
+ break;
+ case RRNotify_OutputChange:
+ gsr_damage_on_output_change(self, xev);
+ break;
+ }
+}
+
+static void gsr_damage_on_damage_event(gsr_damage *self, XEvent *xev) {
+ const XDamageNotifyEvent *de = (XDamageNotifyEvent*)xev;
+ XserverRegion region = XFixesCreateRegion(self->egl->x11.dpy, NULL, 0);
+ /* Subtract all the damage, repairing the window */
+ XDamageSubtract(self->egl->x11.dpy, de->damage, None, region);
+
+ if(self->track_type == GSR_DAMAGE_TRACK_WINDOW || (self->track_type == GSR_DAMAGE_TRACK_MONITOR && self->monitor.connector_id == 0)) {
+ self->damaged = true;
+ } else {
+ int num_rectangles = 0;
+ XRectangle *rectangles = XFixesFetchRegion(self->egl->x11.dpy, region, &num_rectangles);
+ if(rectangles) {
+ const gsr_rectangle monitor_region = { self->monitor.pos, self->monitor.size };
+ for(int i = 0; i < num_rectangles; ++i) {
+ const gsr_rectangle damage_region = { (vec2i){rectangles[i].x, rectangles[i].y}, (vec2i){rectangles[i].width, rectangles[i].height} };
+ self->damaged = rectangles_intersect(monitor_region, damage_region);
+ if(self->damaged)
+ break;
+ }
+ XFree(rectangles);
+ }
+ }
+
+ XFixesDestroyRegion(self->egl->x11.dpy, region);
+ XFlush(self->egl->x11.dpy);
+}
+
+static void gsr_damage_update_cursor(gsr_damage *self) {
+ Window dummy_window;
+ int dummy_i;
+ unsigned int dummy_u;
+ vec2i cursor_position = {0, 0};
+ XQueryPointer(self->egl->x11.dpy, self->window, &dummy_window, &dummy_window, &dummy_i, &dummy_i, &cursor_position.x, &cursor_position.y, &dummy_u);
+ if(cursor_position.x != self->cursor_position.x || cursor_position.y != self->cursor_position.y) {
+ self->cursor_position = cursor_position;
self->damaged = true;
}
}
+void gsr_damage_update(gsr_damage *self, XEvent *xev) {
+ if(self->damage_event == 0 || self->track_type == GSR_DAMAGE_TRACK_NONE)
+ return;
+
+ if(self->randr_event) {
+ if(xev->type == self->randr_event + RRScreenChangeNotify)
+ XRRUpdateConfiguration(xev);
+
+ if(xev->type == self->randr_event + RRNotify)
+ gsr_damage_on_randr_event(self, xev);
+ }
+
+ if(self->damage_event && xev->type == self->damage_event + XDamageNotify)
+ gsr_damage_on_damage_event(self, xev);
+
+ if(self->track_cursor && !self->damaged)
+ gsr_damage_update_cursor(self);
+}
+
bool gsr_damage_is_damaged(gsr_damage *self) {
- return self->damage_event == 0 || !self->damage || self->damaged;
+ return self->damage_event == 0 || !self->damage || self->damaged || self->track_type == GSR_DAMAGE_TRACK_NONE;
}
void gsr_damage_clear(gsr_damage *self) {