#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/Xinerama.h>
#include <X11/extensions/xf86vmode.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TRACE(a,b...) printf(a,##b)
/*
possible flow: (once we know we have xrandr 1.2)
Check if xrandr mode and X Screen size are the same
if Xinerama has more than one screen, we have a faulty Xrandr implementation,
fallback to broken xrandr code
else
use xrandr
endif
*/
struct _mode_info {
int id; // XID for XRR, index into xf86vm.modes for XF86VidMode
int width, height;
int rate;
};
struct _crtc_info {
int x, y;
int mmwidth, mmheight;
int rotation;
int cur_mode;
int nmode;
int *modes;
};
struct ALLEGRO_SYSTEM_XGLX
{
Display *x11display;
int nscreen;
int xrandr_avail;
int xinerama_avail;
int xf86vm_avail;
int ncrtc;
struct _crtc_info **crtcs;
int nmode;
struct _mode_info *modes;
#ifdef ALLEGRO_XWINDOWS_WITH_XRANDR
struct {
int broken; // if xrandr claims to be 1.2 but is actually Xorg's broken wrapper over a 1.1 driver.
XRRScreenResources *res; // need this handle to call many XRR functions.
} xrandr;
#endif
#ifdef ALLEGRO_XWINDOWS_WITH_XINERAMA
struct {
int nscreen;
XineramaScreenInfo *screens;
} xinerama;
#endif
#ifdef ALLEGRO_XWINDOWS_WITH_XF86VM
struct {
int xinerama; // if xf86vm queries xinerama screens instead of X screens
int nmode;
XF86VidModeModeInfo **modes;
} xf86vm;
#endif
};
int _al_xsys_mmon_init(struct ALLEGRO_SYSTEM_XGLX *s)
{
int i = 0;
s->xrandr_avail = 0;
s->xf86vm_avail = 0;
s->xinerama_avail = 0;
if(_al_xsys_xinerama_init(s))
s->xinerama_avail = 1;
if(_al_xsys_xrandr_init(s))
s->xrandr_avail = 1;
if(_al_xsys_xf86vm_init(s))
s->xf86vm_avail = 1;
if(s->xinerama_avail)
s->xinerama_avail = _al_xsys_xinerama_query(s);
if(s->xrandr_avail) {
s->xrandr_avail = _al_xsys_xrandr_query(s);
}
s->nscreen = ScreenCount(s->x11display);
if(s->xf86vm_avail) {
/*
xf86vm uses Xinerama screens when using true Xinerama,
which is the case if its available, and XRandR isn't
*/
int nscr = s->xrandr_avail ? s->nscreen : s->xinerama.nscreen;
for(i = 0; i < nscr; i++) {
_al_xsys_xf86vm_query(s, i);
}
}
}
int _al_xsys_mmon_new_crtc(struct ALLEGRO_SYSTEM_XGLX *s)
{
}
int _al_xsys_mmon_set_crtc_nmode(struct ALLEGRO_SYSTEM_XGLX *s, int nmode)
{
}
struct _mode_info *_al_xsys_mmon_get_crtc_mode(struct ALLEGRO_SYSTEM_XGLX *s, int nmode)
{
}
int _al_xsys_xrandr_init(struct ALLEGRO_SYSTEM_XGLX *s)
{
#ifdef ALLEGRO_XWINDOWS_WITH_XRANDR
int event_basep = 0, error_basep = 0;
XRRScreenResources *res = NULL;
s->xrandr.res = NULL;
s->xrandr.ncrtc = 0;
s->xrandr.crtcs = NULL;
s->xrandr.nmode = 0;
s->xrandr.modes = NULL;
if(XRRQueryExtension (s->x11display, &event_basep, &error_basep)) {
int xrrv_major = 0, xrrv_minor = 0;
Status status;
status = XRRQueryVersion (s->x11display, &xrrv_major, &xrrv_minor);
if(status && (xrrv_major > 1 || (xrrv_major == 1 && xrrv_minor >= 2))) {
TRACE("xrandrtest: xrandr version: %i.%i\n", xrrv_major, xrrv_minor);
s->xrandr.res = res = XRRGetScreenResources (s->x11display, XRootWindow(s->x11display, DefaultScreen(s->x11display)));
if(!res)
goto _uninit;
if(!res->nmode) {
fprintf(stderr, "xrandrtest: Got ScreenResources, but we're missing all the modes :o\n");
goto _uninit;
}
}
else {
TRACE("xrandr_init: xrandr version too old, need >=1.2, have %i.%i\n", xrrv_major, xrrv_minor);
goto _uninit;
}
}
else {
TRACE("xrandr_init: xrandr extension not available.\n");
goto _uninit;
}
return 1;
_uninit:
if(res) {
XRRFreeScreenResources(res);
res = s->xrandr.res = NULL;
}
#endif /* ALLEGRO_XWINDOWS_WITH_XRANDR */
return 0;
}
int _al_xsys_xrandr_query(struct ALLEGRO_SYSTEM_XGLX *s) {
int use_xrandr = 0;
XRRScreenResources *res = NULL;
int ncrtc = 0, nmode = 0;
struct _crtc_info **crtcs = NULL;
struct _mode_info *modes = NULL;
/* comment out old init code
//#ifdef ALLEGRO_XWINDOWS_WITH_XRANDR
int event_basep = 0, error_basep = 0;
if(XRRQueryExtension (s->x11display, &event_basep, &error_basep)) {
Status status;
int xrrv_major = 0, xrrv_minor = 0;
status = XRRQueryVersion (s->x11display, &xrrv_major, &xrrv_minor);
if(status && (xrrv_major > 1 || (xrrv_major == 1 && xrrv_minor >= 2))) {
int screen = DefaultScreen(s->x11display);
fprintf(stderr, "xrandrtest: XRandR version: %i.%i\n", xrrv_major, xrrv_minor);
res = XRRGetScreenResources (s->x11display, XRootWindow(s->x11display, screen));
if(!res)
goto _uninit;
if(!res->nmode) {
fprintf(stderr, "xrandrtest: Got ScreenResources, but we're missing all the modes :o\n");
goto _uninit;
}
use_xrandr = 1;
}
else {
fprintf(stderr, "xrandrtest: failed to query XRandR extension, got version %i.%i\n", xrrv_major, xrrv_minor);
}
}
else {
fprintf(stderr, "xrandrtest: XRandR extension is not available\n");
}
*/
if(use_xrandr) {
int i = 0;
printf("xrandrtest: ncrtc:%i noutput:%i nmode:%i\n", res->ncrtc, res->noutput, res->nmode);
#ifdef ALLEGRO_XWINDOWS_WITH_XINERAMA
/* try and filter out broken xrandr information */
if(s->xinerama_avail) {
if(s->xinerama.nscreen > res->ncrtc) {
/* going to assume xinerama is correct here,
it'll make sure we'll at least beable to get current screen modes from ati and nvidia binary drivers.
Just going to ignore xrandr for now.
*/
s->xrandr_avail = 0;
return 0;
}
}
#endif
nmode = res->nmode;
modes = calloc(nmode, sizeof(struct _mode_info));
if(!modes)
goto _uninit;
for(i = 0; i < nmode; i++) {
modes[i].xid = res->modes[i].id;
modes[i].width = res->modes[i].width;
modes[i].height = res->modes[i].height;
// calculate the refresh rate
if (res->modes[i].hTotal && res->modes[i].vTotal)
modes[i].rate = ((float) res->modes[i].dotClock / ((float) res->modes[i].hTotal * (float) res->modes[i].vTotal));
else
modes[i].rate = 0;
//printf("got mode[%i]: %ix%i @ %i\n", modes[i].xid, modes[i].width, modes[i].height, modes[i].rate);
}
crtcs = calloc(res->ncrtc, sizeof(struct _crtc_info *));
if(!crtcs)
goto _uninit;
for(i = 0; i < res->ncrtc; i++) {
XRRCrtcInfo *crtc_info = NULL;
XRROutputInfo *output_info = NULL;
XRROutputInfo *possible_info = NULL;
int j = 0, k = 0, p = 0;
crtcs[i] = calloc(1, sizeof(struct _crtc_info));
if(!crtcs[i])
goto _uninit;
crtc_info = XRRGetCrtcInfo (s->x11display, res, res->crtcs[i]);
if(!crtc_info) {
continue;
}
printf("xrandrtest: crtc[%i]: noutput:%i\n", i, crtc_info->noutput);
crtcs[i]->x = crtc_info->x;
crtcs[i]->y = crtc_info->y;
for(j = 0; j < crtc_info->noutput; j++) {
output_info = XRRGetOutputInfo (s->x11display, res, crtc_info->outputs[j]);
if(output_info && output_info->nmode) {
int l = 0, m = 0;
printf("xrandrtest: crtc[%i]: output[%s]: choosing %s output\n", i, output_info->name, output_info->name);
for(m = 0; m < output_info->nmode; m++) {
for(l = 0; l < nmode; l++) {
if(modes[l].xid == output_info->modes[m]) {
printf("\toutput mode[%i]: %ix%i @ %i\n", modes[l].xid, modes[l].width, modes[l].height, modes[l].rate);
break;
}
}
}
break;
}
XRRFreeOutputInfo(output_info);
}
//output_info = XRRGetOutputInfo (s->x11display, res, crtc_info->outputs[0]);
if(!output_info)
continue;
/*
printf("xrandrtest: crtc[%i]: npossible: %i\n", i, crtc_info->npossible);
for(p = 0; p < crtc_info->npossible; p++) {
possible_info = XRRGetOutputInfo (s->x11display, res, crtc_info->possible[p]);
if(possible_info) {
printf("xrandrtest: crtc[%i]: npossible[%s] modes: %i\n", i, possible_info->name, possible_info->nmode);
if(possible_info->nmode) {
int l = 0, m = 0;
for(m = 0; m < possible_info->nmode; m++) {
for(l = 0; l < nmode; l++) {
if(modes[l].xid == possible_info->modes[m]) {
printf("\tpossible mode[%i]: %ix%i @ %i\n", modes[l].xid, modes[l].width, modes[l].height, modes[l].rate);
break;
}
}
}
}
}
XRRFreeOutputInfo(possible_info);
}
*/
crtcs[i]->mmwidth = output_info->mm_width;
crtcs[i]->mmheight = output_info->mm_height;
printf("xrandrtest: crtc[%i]: %ix%i (%immx%imm)\n", i, crtcs[i]->x, crtcs[i]->y, crtcs[i]->mmwidth, crtcs[i]->mmheight);
crtcs[i]->nmode = output_info->nmode;
if(!crtcs[i]->nmode)
continue;
crtcs[i]->modes = calloc(output_info->nmode, sizeof(int));
if(!crtcs[i]->modes)
continue;
for(k = 0; k < output_info->nmode; k++) {
int l = 0;
crtcs[i]->modes[k] = output_info->modes[k];
for(l = 0; l < nmode; l++) {
if(modes[l].xid == output_info->modes[k]) {
printf("\tmode[%i]: %ix%i @ %i\n", l, modes[l].width, modes[l].height, modes[l].rate);
break;
}
}
}
XRRFreeOutputInfo(output_info);
XRRFreeCrtcInfo(crtc_info);
}
}
//#endif /* ALLEGRO_XWINDOWS_WITH_XINERAMA */
return use_xrandr;
_uninit:
return 0;
}
int _al_xsys_xf86vm_init(struct ALLEGRO_SYSTEM_XGLX *s)
{
int x = 0, y = 0;
if(!XF86VidModeQueryExtension(s->x11display, &x, &y)) {
printf("xrandrtest: XF86VidMode Extension is not available: Extension Query failed.\n");
return 0;
}
if(!XF86VidModeQueryVersion(s->x11display, &x, &y)) {
printf("xrandrtest: XF86VidMode Extension is not available: Version Query failed.\n");
return 0;
}
return 1;
}
int get_xfmode_lines(struct ALLEGRO_SYSTEM_XGLX *s, int screen)
{
XF86VidModeModeInfo **mode_info = NULL;
XF86VidModeMonitor *monitor = NULL;
int nmode = 0;
int i = 0, j = 0;
if(!XF86VidModeGetAllModeLines(s->x11display, screen, &nmode, &mode_info)) {
printf("xrandrtest: XF86VidMode: screen[%i]: GetAllModeLines failed.\n", screen);
return 0;
}
monitor = calloc(1, sizeof(XF86VidModeMonitor));
if(!monitor) {
printf("xrandrtest: XF86VidMode: calloc failed :(\n");
return 0;
}
if(!XF86VidModeGetMonitor(s->x11display, screen, monitor)) {
printf("xrandrtest: XF86VidMode: GetMonitor failed.\n");
free(monitor);
return 0;
}
printf("xrandrtest: XF86VidMode: screen[%i]: hsync(%i): ", screen, monitor->nhsync);
for(j = 0; j < monitor->nhsync; j++) {
printf("[%i: %f-%f] ", j, monitor->hsync[i].lo, monitor->hsync[i].hi);
}
printf(" vsync(%i): ", monitor->nvsync);
for(j = 0; j < monitor->nvsync; j++) {
printf("[%i: %f-%f] ", j, monitor->vsync[i].lo, monitor->vsync[i].hi);
}
printf("\n");
for(i = 0; i < nmode; i++) {
float rate = 0.0f;
if (mode_info[i]->htotal && mode_info[i]->vtotal) {
rate = (mode_info[i]->dotclock * 1000L / ( mode_info[i]->htotal * mode_info[i]->vtotal));
}
printf("\tmode[%i]: %ix%i @ %f\n", i, mode_info[i]->hdisplay, mode_info[i]->vdisplay, rate);
}
}
/*
int init_nvctrl(struct ALLEGRO_SYSTEM_XGLX *s)
{
int event_base = 0, error_base = 0;
int major = 0, minor = 0;
if(!XNVCTRLQueryExtension (s->x11display, &event_base, &error_base))
{
printf("xrandrtest: NV-CONTROL extension is not available.\n");
return 0;
}
if(!XNVCTRLQueryVersion (s->x11display, &major, &minor)) {
printf("xrandrtest: NV-CONTROL Version Query failed.\n");
return 0;
}
printf("xrandrtest: NV-CONTROL Version: %i.%i\n", major, minor);
return 1;
}
*/
int nvctrl_query(struct ALLEGRO_SYSTEM_XGLX *s, int screen)
{
if(!XNVCTRLIsNvScreen (s->x11display, screen)) {
printf("xrandrtest: NV-CONTROL: screen[%i] is not controlled by NVidia.\n", screen);
return 0;
}
// NV_CTRL_TWINVIEW, can be used to shortcircuit out of here... if no twinview, theres separate screens, and xrandr/xinerama can fill in.
// NV_CTRL_ENABLED_DISPLAYS, get enabled displays via XNVCTRLQueryTargetAttribute())
}
int main(int argc, char **argv)
{
int default_screen = 0, screen_count = 0, screen = 0;
char *display_name = NULL;
int event_base = 0, error_base = 0;
int use_xrandr = 0;
struct ALLEGRO_SYSTEM_XGLX *system = calloc(1, sizeof(struct ALLEGRO_SYSTEM_XGLX));
display_name = getenv("DISPLAY");
if(!display_name) {
fprintf(stderr, "%s: DISPLAY not set >:(\n", argv[0]);
exit(-1);
}
if ((system->x11display=XOpenDisplay(display_name)) == NULL)
{
fprintf(stderr,"%s: cannot connect to X server %s\n", argv[0], XDisplayName(display_name));
exit(-1);
}
printf("xrandrtest: display name: %s\n", DisplayString(system->x11display));
printf("xrandrtest: server vendor: %s\n", ServerVendor(system->x11display));
if (strstr(ServerVendor(system->x11display), "X.Org")) {
int vendrel = VendorRelease(system->x11display);
printf("xrandrtest: X.Org version: ");
printf("%d.%d.%d", vendrel / 10000000, (vendrel / 100000) % 100, (vendrel / 1000) % 100);
if (vendrel % 1000) {
printf(".%d", vendrel % 1000);
}
printf("\n");
}
else if (strstr(ServerVendor (system->x11display), "XFree86")) {
int vendrel = VendorRelease(system->x11display);
printf("XFree86 version: ");
if (vendrel < 336) {
/*
* vendrel was set incorrectly for 3.3.4 and 3.3.5, so handle
* those cases here.
*/
printf("%d.%d.%d", vendrel / 100,
(vendrel / 10) % 10,
vendrel % 10);
} else if (vendrel < 3900) {
/* 3.3.x versions, other than the exceptions handled above */
printf("%d.%d", vendrel / 1000,
(vendrel / 100) % 10);
if (((vendrel / 10) % 10) || (vendrel % 10)) {
printf(".%d", (vendrel / 10) % 10);
if (vendrel % 10) {
printf(".%d", vendrel % 10);
}
}
} else if (vendrel < 40000000) {
/* 4.0.x versions */
printf("%d.%d", vendrel / 1000,
(vendrel / 10) % 10);
if (vendrel % 10) {
printf(".%d", vendrel % 10);
}
} else {
/* post-4.0.x */
printf("%d.%d.%d", vendrel / 10000000,
(vendrel / 100000) % 100,
(vendrel / 1000) % 100);
if (vendrel % 1000) {
printf(".%d", vendrel % 1000);
}
}
printf("\n");
}
else if (strstr(ServerVendor (system->x11display), "DMX")) {
int vendrel = VendorRelease(system->x11display);
int major, minor, year, month, day;
major = vendrel / 100000000;
vendrel -= major * 100000000;
minor = vendrel / 1000000;
vendrel -= minor * 1000000;
year = vendrel / 10000;
vendrel -= year * 10000;
month = vendrel / 100;
vendrel -= month * 100;
day = vendrel;
/* Add other epoch tests here */
if (major > 0 && minor > 0) year += 2000;
/* Do some sanity tests in case there is
* another server with the same vendor
* string. That server could easily use
* values < 100000000, which would have
* the effect of keeping our major
* number 0. */
if (major > 0 && major <= 20
&& minor >= 0 && minor <= 99
&& year >= 2000
&& month >= 1 && month <= 12
&& day >= 1 && day <= 31)
printf("DMX version: %d.%d.%04d%02d%02d\n",
major, minor, year, month, day);
}
else {
printf("xrandrtest: server release: '%i'\n", VendorRelease(system->x11display));
}
printf("xrandrtest: proto version: %i.%i\n", ProtocolVersion(system->x11display),ProtocolRevision(system->x11display));
screen_count = ScreenCount(system->x11display);
default_screen = DefaultScreen(system->x11display);
printf("xrandrtest: X Screen count: %i default screen: %i\n", screen_count, default_screen);
for(screen = 0; screen < screen_count; screen++) {
Screen *scr = ScreenOfDisplay(system->x11display, screen);
printf("\tscreen[%i]: width:%i height:%i\n", screen, WidthOfScreen(scr), HeightOfScreen(scr));
}
if(XineramaQueryExtension(system->x11display, &event_base, &error_base)) {
int minor_version = 0, major_version = 0;
int status = XineramaQueryVersion(system->x11display, &major_version, &minor_version);
printf("xrandrtest: Xinerama version: %i.%i\n", major_version, minor_version);
if(!XineramaIsActive(system->x11display)) {
printf("xrandrtest: Xinerama is not active\n");
}
else {
int screen_num = 0;
XineramaScreenInfo *info = XineramaQueryScreens(system->x11display, &screen_num);
if(!info) {
fprintf(stderr, "xrandrtest: XineramaQueryScreens failed.\n");
}
else {
int i = 0;
printf("xrandrtest: Xinerama: Got %i Screens\n", screen_num);
for(i = 0; i < screen_num; i++) {
printf("\tscreen[%i]: %ix%i+%i+%i (%immx%imm)\n", i, info[i].width, info[i].height, info[i].x_org, info[i].y_org, 0, 0);
}
}
}
}
else {
fprintf(stderr, "xrandrtest: Xinerama extension is not available.\n");
}
if(init_xfmode(system)) {
int si = 0;
for(si = 0; si < screen_count; si++) {
get_xfmode_lines(system, si);
}
}
use_xrandr = _al_xsys_xrandr_init(system);
XCloseDisplay(system->x11display);
return 0;
}