1/*
2 * Copyright notice...
3 */
4
5#include "ctwm.h"
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <X11/extensions/Xrandr.h>
11
12#include "r_area_list.h"
13#include "r_area.h"
14#include "r_layout.h"
15#include "xrandr.h"
16
17
18/**
19 * Use XRANDR to figure out how our monitors are laid out.
20 */
21RLayout *
22XrandrNewLayout(Display *disp, Window rootw)
23{
24	int i_nmonitors = 0;
25	XRRMonitorInfo *ps_monitors;
26	char **monitor_names;
27	RAreaList *areas;
28	int evt_base, err_base, ver_maj, ver_min;
29	// XXX *_base and ver_* should move more globally if we start doing
30	// randr stuff anywhere else.
31
32	// If the server doesn't talk RANDR, we have nothing to do.
33	if(XRRQueryExtension(disp, &evt_base, &err_base) != True) {
34		// No RANDR
35#ifdef DEBUG
36		fprintf(stderr, "No RANDR on the server.\n");
37#endif
38		return NULL;
39	}
40
41	// XRRGetMonitors() wraps the RRGetMonitors request, which requires
42	// 1.5.
43	if(XRRQueryVersion(disp, &ver_maj, &ver_min) == 0) {
44		// Couldn't get the version
45#ifdef DEBUG
46		fprintf(stderr, "Couldn't get server RANDR version.\n");
47#endif
48		return NULL;
49	}
50	if(ver_maj < 1 || (ver_maj == 1 && ver_min < 5)) {
51#ifdef DEBUG
52		fprintf(stderr, "Server has RANDR %d.%d, we need 1.5+.\n",
53		        ver_maj, ver_min);
54#endif
55		return NULL;
56	}
57
58	// RANDR 1.5 function to get info about 'em.
59	ps_monitors = XRRGetMonitors(disp, rootw, 1, &i_nmonitors);
60	if(ps_monitors == NULL || i_nmonitors == 0) {
61		fprintf(stderr, "XRRGetMonitors failed\n");
62		return NULL;
63	}
64
65	// Useful note: vtwm also apparently has RANDR support.  It uses
66	// XRRGetScreenResources() and looping XRRGetCrtcInfo() to load info
67	// about the screen, and its conditionals suggest that's RANDR 1.2.
68	// Look into that if we decide to worry about earlier versions.
69
70	// Add space for all their names (plus trailing NULL)
71	monitor_names = calloc((i_nmonitors + 1), sizeof(char *));
72	if(monitor_names == NULL) {
73		abort();
74	}
75
76	// Add each and its name into an RAreaList
77	areas = RAreaListNew(i_nmonitors, NULL);
78	for(int i = 0; i < i_nmonitors; i++) {
79		RArea cur_area = RAreaNew(ps_monitors[i].x,
80		                          ps_monitors[i].y,
81		                          ps_monitors[i].width,
82		                          ps_monitors[i].height);
83
84		char *name = XGetAtomName(disp, ps_monitors[i].name);
85#ifdef DEBUG
86		fprintf(stderr, "NEW area: %s%s",
87		        name != NULL ? name : "",
88		        name != NULL ? ":" : "");
89		RAreaPrint(&cur_area);
90		fprintf(stderr, "\n");
91#endif
92		if(name != NULL) {
93			monitor_names[i] = strdup(name);
94			XFree(name);
95		}
96		else {
97			monitor_names[i] = strdup("");
98		}
99
100		RAreaListAdd(areas, &cur_area);
101	}
102
103	XRRFreeMonitors(ps_monitors);
104
105	// Build up an RLayout of those areas and their names, and hand it
106	// back.
107	return RLayoutSetMonitorsNames(RLayoutNew(areas), monitor_names);
108}
109