Files
2025-01-29 10:55:49 +01:00

222 lines
7.0 KiB
C

#include "x11.h"
#include <string.h>
// TODO: deal with monitor hotplugging and such
void kinc_x11_display_init(void) {
int eventBase;
int errorBase;
bool hasXinerama = (xlib.XineramaQueryExtension(x11_ctx.display, &eventBase, &errorBase) && xlib.XineramaIsActive(x11_ctx.display));
XineramaScreenInfo *xinerama_screens = NULL;
int xinerama_screen_count = 0;
if (hasXinerama) {
xinerama_screens = xlib.XineramaQueryScreens(x11_ctx.display, &xinerama_screen_count);
}
Window root_window = RootWindow(x11_ctx.display, DefaultScreen(x11_ctx.display));
XRRScreenResources *screen_resources = xlib.XRRGetScreenResourcesCurrent(x11_ctx.display, root_window);
RROutput primary_output = xlib.XRRGetOutputPrimary(x11_ctx.display, root_window);
for (int i = 0; i < screen_resources->noutput; i++) {
if (i >= MAXIMUM_DISPLAYS) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Too many screens (maximum %i)", MAXIMUM_DISPLAYS);
break;
}
XRROutputInfo *output_info = xlib.XRRGetOutputInfo(x11_ctx.display, screen_resources, screen_resources->outputs[i]);
if (output_info->connection != RR_Connected || output_info->crtc == None) {
xlib.XRRFreeOutputInfo(output_info);
continue;
}
XRRCrtcInfo *crtc_info = xlib.XRRGetCrtcInfo(x11_ctx.display, screen_resources, output_info->crtc);
struct kinc_x11_display *display = &x11_ctx.displays[x11_ctx.num_displays++];
display->index = i;
strncpy(display->name, output_info->name, sizeof(display->name));
display->x = crtc_info->x;
display->y = crtc_info->y;
display->width = crtc_info->width;
display->height = crtc_info->height;
display->primary = screen_resources->outputs[i] == primary_output;
display->crtc = output_info->crtc;
display->output = screen_resources->outputs[i];
xlib.XRRFreeOutputInfo(output_info);
xlib.XRRFreeCrtcInfo(crtc_info);
}
xlib.XRRFreeScreenResources(screen_resources);
if (hasXinerama) {
xlib.XFree(xinerama_screens);
}
}
int kinc_x11_display_primary(void) {
for (int i = 0; i < x11_ctx.num_displays; i++) {
if (x11_ctx.displays[i].primary) {
return i;
}
}
return 0;
}
int kinc_x11_count_displays(void) {
return x11_ctx.num_displays;
}
bool kinc_x11_display_available(int display_index) {
if (display_index >= MAXIMUM_DISPLAYS) {
return false;
}
return x11_ctx.displays[display_index].output != None;
}
const char *kinc_x11_display_name(int display_index) {
if (display_index >= MAXIMUM_DISPLAYS) {
return "";
}
return x11_ctx.displays[display_index].name;
}
kinc_display_mode_t kinc_x11_display_current_mode(int display_index) {
if (display_index >= MAXIMUM_DISPLAYS)
display_index = 0;
struct kinc_x11_display *display = &x11_ctx.displays[display_index];
kinc_display_mode_t mode;
mode.x = 0;
mode.y = 0;
mode.width = display->width;
mode.height = display->height;
mode.frequency = 60;
mode.bits_per_pixel = 32;
mode.pixels_per_inch = 96;
Window root_window = DefaultRootWindow(x11_ctx.display);
XRRScreenResources *screen_resources = xlib.XRRGetScreenResourcesCurrent(x11_ctx.display, root_window);
XRROutputInfo *output_info = xlib.XRRGetOutputInfo(x11_ctx.display, screen_resources, screen_resources->outputs[display->index]);
if (output_info->connection != RR_Connected || output_info->crtc == None) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Display %i not connected.", display_index);
xlib.XRRFreeOutputInfo(output_info);
xlib.XRRFreeScreenResources(screen_resources);
return mode;
}
XRRCrtcInfo *crtc_info = xlib.XRRGetCrtcInfo(x11_ctx.display, screen_resources, output_info->crtc);
for (int j = 0; j < output_info->nmode; j++) {
RRMode rr_mode = crtc_info->mode;
XRRModeInfo *mode_info = NULL;
for (int k = 0; k < screen_resources->nmode; k++) {
if (screen_resources->modes[k].id == rr_mode) {
mode_info = &screen_resources->modes[k];
break;
}
}
if (mode_info == NULL) {
continue;
}
mode.x = display->x;
mode.y = display->y;
mode.width = mode_info->width;
mode.height = mode_info->height;
mode.pixels_per_inch = 96;
mode.bits_per_pixel = 32;
if (mode_info->hTotal && mode_info->vTotal) {
mode.frequency = (mode_info->dotClock / (mode_info->hTotal * mode_info->vTotal));
}
else {
mode.frequency = 60;
}
}
xlib.XRRFreeOutputInfo(output_info);
xlib.XRRFreeCrtcInfo(crtc_info);
xlib.XRRFreeScreenResources(screen_resources);
return mode;
}
int kinc_x11_display_count_available_modes(int display_index) {
if (display_index >= MAXIMUM_DISPLAYS)
display_index = 0;
struct kinc_x11_display *display = &x11_ctx.displays[display_index];
Window root_window = RootWindow(x11_ctx.display, DefaultScreen(x11_ctx.display));
XRRScreenResources *screen_resources = xlib.XRRGetScreenResourcesCurrent(x11_ctx.display, root_window);
XRROutputInfo *output_info = xlib.XRRGetOutputInfo(x11_ctx.display, screen_resources, screen_resources->outputs[display->index]);
if (output_info->connection != RR_Connected || output_info->crtc == None) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Display %i not connected.", display_index);
xlib.XRRFreeOutputInfo(output_info);
xlib.XRRFreeScreenResources(screen_resources);
return 0;
}
int num_modes = output_info->nmode;
xlib.XRRFreeOutputInfo(output_info);
xlib.XRRFreeScreenResources(screen_resources);
return num_modes;
}
kinc_display_mode_t kinc_x11_display_available_mode(int display_index, int mode_index) {
if (display_index >= MAXIMUM_DISPLAYS)
display_index = 0;
struct kinc_x11_display *display = &x11_ctx.displays[display_index];
kinc_display_mode_t mode;
mode.x = 0;
mode.y = 0;
mode.width = display->width;
mode.height = display->height;
mode.frequency = 60;
mode.bits_per_pixel = 32;
mode.pixels_per_inch = 96;
Window root_window = RootWindow(x11_ctx.display, DefaultScreen(x11_ctx.display));
XRRScreenResources *screen_resources = xlib.XRRGetScreenResourcesCurrent(x11_ctx.display, root_window);
XRROutputInfo *output_info = xlib.XRRGetOutputInfo(x11_ctx.display, screen_resources, screen_resources->outputs[display->index]);
if (output_info->connection != RR_Connected || output_info->crtc == None) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Display %i not connected.", display_index);
xlib.XRRFreeOutputInfo(output_info);
xlib.XRRFreeScreenResources(screen_resources);
return mode;
}
if (mode_index >= output_info->nmode) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Invalid mode index %i.", mode_index);
}
RRMode rr_mode = output_info->modes[mode_index];
XRRModeInfo *mode_info = NULL;
for (int k = 0; k < screen_resources->nmode; k++) {
if (screen_resources->modes[k].id == rr_mode) {
mode_info = &screen_resources->modes[k];
break;
}
}
if (mode_info != NULL) {
mode.x = display->x;
mode.y = display->y;
mode.width = mode_info->width;
mode.height = mode_info->height;
mode.pixels_per_inch = 96;
mode.bits_per_pixel = 32;
if (mode_info->hTotal && mode_info->vTotal) {
mode.frequency = (mode_info->dotClock / (mode_info->hTotal * mode_info->vTotal));
}
else {
mode.frequency = 60;
}
}
xlib.XRRFreeOutputInfo(output_info);
xlib.XRRFreeScreenResources(screen_resources);
return mode;
}