summaryrefslogtreecommitdiffstats
path: root/kcontrol/randr/lowlevel_randr.c
diff options
context:
space:
mode:
Diffstat (limited to 'kcontrol/randr/lowlevel_randr.c')
-rw-r--r--kcontrol/randr/lowlevel_randr.c700
1 files changed, 700 insertions, 0 deletions
diff --git a/kcontrol/randr/lowlevel_randr.c b/kcontrol/randr/lowlevel_randr.c
new file mode 100644
index 000000000..a6d54dbcd
--- /dev/null
+++ b/kcontrol/randr/lowlevel_randr.c
@@ -0,0 +1,700 @@
+/*
+ * Copyright © 2007 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "lowlevel_randr.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+Status crtc_disable (struct CrtcInfo *crtc);
+
+char * get_output_name (struct ScreenInfo *screen_info, RROutput id)
+{
+ char *output_name = NULL;
+ int i;
+
+ for (i = 0; i < screen_info->n_output; i++) {
+ if (id == screen_info->outputs[i]->id) {
+ output_name = screen_info->outputs[i]->info->name;
+ }
+ }
+
+ if (!output_name) {
+ output_name = "Unknown";
+ }
+
+ return output_name;
+}
+
+XRRModeInfo * find_mode_by_xid (struct ScreenInfo *screen_info, RRMode mode_id)
+{
+ XRRModeInfo *mode_info = NULL;
+ XRRScreenResources *res;
+ int i;
+
+ res = screen_info->res;
+ for (i = 0; i < res->nmode; i++) {
+ if (mode_id == res->modes[i].id) {
+ mode_info = &res->modes[i];
+ break;
+ }
+ }
+
+ return mode_info;
+}
+
+static XRRCrtcInfo * find_crtc_by_xid (struct ScreenInfo *screen_info, RRCrtc crtc_id)
+{
+ XRRCrtcInfo *crtc_info;
+ Display *dpy;
+ XRRScreenResources *res;
+
+ dpy = screen_info->dpy;
+ res = screen_info->res;
+
+ crtc_info = XRRGetCrtcInfo (dpy, res, crtc_id);
+
+ return crtc_info;
+}
+
+int get_width_by_output_id (struct ScreenInfo *screen_info, RROutput output_id)
+{
+ struct OutputInfo *output_info;
+ struct CrtcInfo *crtc_info;
+ RRMode mode_id;
+ XRRModeInfo *mode_info;
+ int i;
+ int width = -1;
+
+ for (i = 0; i < screen_info->n_output; i++) {
+ if (output_id == screen_info->outputs[i]->id) {
+ crtc_info = screen_info->outputs[i]->cur_crtc;
+ if (!crtc_info) {
+ width = 0;
+ break;
+ }
+ mode_id = crtc_info->cur_mode_id;
+ mode_info = find_mode_by_xid (screen_info, mode_id);
+
+ width = mode_width (mode_info, crtc_info->cur_rotation);
+
+ break;
+ }
+ }
+
+ return width;
+}
+
+int get_height_by_output_id (struct ScreenInfo *screen_info, RROutput output_id)
+{
+ struct OutputInfo *output_info;
+ struct CrtcInfo *crtc_info;
+ RRMode mode_id;
+ XRRModeInfo *mode_info;
+ int i;
+ int height = -1;
+
+ for (i = 0; i < screen_info->n_output; i++) {
+ if (output_id == screen_info->outputs[i]->id) {
+ crtc_info = screen_info->outputs[i]->cur_crtc;
+ if (!crtc_info) {
+ height = 0;
+ break;
+ }
+ mode_id = crtc_info->cur_mode_id;
+ mode_info = find_mode_by_xid (screen_info, mode_id);
+
+ height = mode_height (mode_info, crtc_info->cur_rotation);
+
+ break;
+ }
+ }
+
+ return height;
+}
+
+int mode_height (XRRModeInfo *mode_info, Rotation rotation)
+{
+ switch (rotation & 0xf) {
+ case RR_Rotate_0:
+ case RR_Rotate_180:
+ return mode_info->height;
+ case RR_Rotate_90:
+ case RR_Rotate_270:
+ return mode_info->width;
+ default:
+ return 0;
+ }
+}
+
+int mode_width (XRRModeInfo *mode_info, Rotation rotation)
+{
+ switch (rotation & 0xf) {
+ case RR_Rotate_0:
+ case RR_Rotate_180:
+ return mode_info->width;
+ case RR_Rotate_90:
+ case RR_Rotate_270:
+ return mode_info->height;
+ default:
+ return 0;
+ }
+}
+
+
+static struct CrtcInfo * find_crtc (struct ScreenInfo *screen_info, XRROutputInfo *output)
+{
+ struct CrtcInfo *crtc_info = NULL;
+ int i;
+
+ for (i = 0; i < screen_info->n_crtc; i++) {
+ if (screen_info->crtcs[i]->id == output->crtc) {
+ crtc_info = screen_info->crtcs[i];
+ break;
+ }
+ }
+
+ return crtc_info;
+}
+
+struct CrtcInfo * auto_find_crtc (struct ScreenInfo *screen_info, struct OutputInfo *output_info)
+{
+ struct CrtcInfo *crtc_info = NULL;
+ int i;
+
+ for (i = 0; i < screen_info->n_crtc; i++) {
+ if (0 == screen_info->crtcs[i]->cur_noutput) {
+ crtc_info = screen_info->crtcs[i];
+ break;
+ }
+ }
+
+ if (NULL == crtc_info) {
+ crtc_info = screen_info->crtcs[0];
+ }
+
+ return crtc_info;
+}
+
+int set_screen_size (struct ScreenInfo *screen_info)
+{
+ Display *dpy;
+ int screen;
+ struct CrtcInfo *crtc;
+ XRRModeInfo *mode_info;
+ int cur_x = 0, cur_y = 0;
+ int w = 0, h = 0;
+ int mmW, mmH;
+ int max_width = 0, max_height = 0;
+ int i;
+
+ dpy = screen_info->dpy;
+ screen = DefaultScreen (dpy);
+
+ for (i = 0; i < screen_info->n_crtc; i++) {
+ crtc = screen_info->crtcs[i];
+ if (!crtc->cur_mode_id) {
+ continue;
+ }
+ mode_info = find_mode_by_xid (screen_info, crtc->cur_mode_id);
+ cur_x = crtc->cur_x;
+ cur_y = crtc->cur_y;
+
+ w = mode_width (mode_info, crtc->cur_rotation);
+ h = mode_height (mode_info, crtc->cur_rotation);
+
+ if (cur_x + w > max_width) {
+ max_width = cur_x + w;
+ }
+ if (cur_y + h > max_height) {
+ max_height = cur_y + h;
+ }
+ }
+
+ if (max_width > screen_info->max_width) {
+ #if RANDR_GUI_DEBUG
+ fprintf (stderr, "user set screen width %d, larger than max width %d, set to max width\n",
+ cur_x + w, screen_info->max_width);
+ #endif
+ return 0;
+ } else if (max_width < screen_info->min_width) {
+ screen_info->cur_width = screen_info->min_width;
+ } else {
+ screen_info->cur_width = max_width;
+ }
+
+ if (max_height > screen_info->max_height) {
+ #if RANDR_GUI_DEBUG
+ fprintf (stderr, "user set screen height %d, larger than max height %d, set to max height\n",
+ cur_y + h, screen_info->max_height);
+ #endif
+ return 0;
+ } else if (max_height < screen_info->min_height) {
+ screen_info->cur_height = screen_info->min_height;
+ } else {
+ screen_info->cur_height = max_height;
+ }
+
+ /* calculate mmWidth, mmHeight */
+ if (screen_info->cur_width != DisplayWidth (dpy, screen) ||
+ screen_info->cur_height != DisplayHeight (dpy, screen) ) {
+ double dpi;
+
+ dpi = (25.4 * DisplayHeight (dpy, screen)) / DisplayHeightMM(dpy, screen);
+ mmW = (25.4 * screen_info->cur_width) / dpi;
+ mmH = (25.4 * screen_info->cur_height) / dpi;
+ } else {
+ mmW = DisplayWidthMM (dpy, screen);
+ mmH = DisplayHeightMM (dpy, screen);
+ }
+
+ screen_info->cur_mmWidth = mmW;
+ screen_info->cur_mmHeight = mmH;
+
+ return 1;
+}
+
+void screen_apply (struct ScreenInfo *screen_info)
+{
+ int width, height;
+ int mmWidth, mmHeight;
+ Display *dpy, *cur_dpy;
+ Window window;
+ int screen;
+ static int first = 1;
+
+ width = screen_info->cur_width;
+ height = screen_info->cur_height;
+ mmWidth = screen_info->cur_mmWidth;
+ mmHeight = screen_info->cur_mmHeight;
+ dpy = screen_info->dpy;
+ window = screen_info->window;
+ screen = DefaultScreen (dpy);
+
+ cur_dpy = XOpenDisplay (NULL);
+
+ if (width == DisplayWidth (cur_dpy, screen) &&
+ height == DisplayHeight (cur_dpy, screen) &&
+ mmWidth == DisplayWidthMM (cur_dpy, screen) &&
+ mmHeight == DisplayHeightMM (cur_dpy, screen) ) {
+ return;
+ } else {
+ XRRSetScreenSize (dpy, window, width, height, mmWidth, mmHeight);
+ }
+}
+
+Status crtc_apply (struct CrtcInfo *crtc_info)
+{
+ struct ScreenInfo *screen_info;
+ XRRCrtcInfo *rr_crtc_info;
+ Display *dpy;
+ XRRScreenResources *res;
+ RRCrtc crtc_id;
+ int x, y;
+ RRMode mode_id;
+ Rotation rotation;
+ RROutput *outputs;
+ int noutput;
+ Status s;
+ int i;
+
+ /*if (!crtc_info->changed) {
+ return RRSetConfigSuccess;
+ }*/
+
+ screen_info = crtc_info->screen_info;
+ dpy = screen_info->dpy;
+ res = screen_info->res;
+ crtc_id = crtc_info->id;
+ x = crtc_info->cur_x;
+ y = crtc_info->cur_y;
+
+ mode_id = crtc_info->cur_mode_id;
+ rotation = crtc_info->cur_rotation;
+
+ noutput = crtc_info->cur_noutput;
+
+ if (0 == noutput) {
+ return crtc_disable (crtc_info);
+ }
+
+ outputs = malloc (sizeof (RROutput) * noutput);
+ noutput = 0;
+ for (i = 0; i < screen_info->n_output; i++) {
+ struct OutputInfo *output_info = screen_info->outputs[i];
+
+ if (output_info->cur_crtc && crtc_id == output_info->cur_crtc->id) {
+ outputs[noutput++] = output_info->id;
+ }
+ }
+
+
+ s = XRRSetCrtcConfig (dpy, res, crtc_id, CurrentTime,
+ x, y, mode_id, rotation,
+ outputs, noutput);
+
+ if (RRSetConfigSuccess == s) {
+ crtc_info->changed = 0;
+ }
+
+ free (outputs);
+
+ return s;
+}
+
+Status crtc_disable (struct CrtcInfo *crtc)
+{
+ struct ScreenInfo *screen_info;
+
+ screen_info = crtc->screen_info;
+
+ return XRRSetCrtcConfig (screen_info->dpy, screen_info->res, crtc->id, CurrentTime,
+ 0, 0, None, RR_Rotate_0, NULL, 0);
+}
+
+struct ScreenInfo* read_screen_info (Display *display)
+{
+ struct ScreenInfo *screen_info;
+ int screen_num;
+ Window root_window;
+ XRRScreenResources *sr;
+ int i;
+
+ screen_num = DefaultScreen (display);
+ root_window = RootWindow (display, screen_num);
+
+ sr = XRRGetScreenResources (display, root_window);
+
+ screen_info = malloc (sizeof (struct ScreenInfo));
+ screen_info->dpy = display;
+ screen_info->window = root_window;
+ screen_info->res = sr;
+ screen_info->cur_width = DisplayWidth (display, screen_num);
+ screen_info->cur_height = DisplayHeight (display, screen_num);
+ screen_info->cur_mmWidth = DisplayWidthMM (display, screen_num);
+ screen_info->cur_mmHeight = DisplayHeightMM (display, screen_num);
+ screen_info->n_output = sr->noutput;
+ screen_info->n_crtc = sr->ncrtc;
+ screen_info->outputs = malloc (sizeof (struct OutputInfo *) * sr->noutput);
+ screen_info->crtcs = malloc (sizeof (struct CrtcInfo *) * sr->ncrtc);
+ screen_info->clone = 0;
+
+ XRRGetScreenSizeRange (display, root_window, &screen_info->min_width, &screen_info->min_height, &screen_info->max_width, &screen_info->max_height);
+
+ /* get crtc */
+ for (i = 0; i < sr->ncrtc; i++) {
+ struct CrtcInfo *crtc_info;
+ screen_info->crtcs[i] = malloc (sizeof (struct CrtcInfo));
+ crtc_info = screen_info->crtcs[i];
+ XRRCrtcInfo *xrr_crtc_info = XRRGetCrtcInfo (display, sr, sr->crtcs[i]);
+
+ crtc_info->id = sr->crtcs[i];
+ crtc_info->info = xrr_crtc_info;
+ crtc_info->cur_x = xrr_crtc_info->x;
+ crtc_info->cur_y = xrr_crtc_info->y;
+ crtc_info->cur_mode_id = xrr_crtc_info->mode;
+ crtc_info->cur_rotation = xrr_crtc_info->rotation;
+ crtc_info->rotations = xrr_crtc_info->rotations;
+ crtc_info->cur_noutput = xrr_crtc_info->noutput;
+
+ crtc_info->changed = 0;
+ crtc_info->screen_info = screen_info;
+ }
+
+
+ /* get output */
+ for (i = 0; i < sr->noutput; i++) {
+ struct OutputInfo *output;
+ screen_info->outputs[i] = malloc (sizeof (struct OutputInfo));
+ output = screen_info->outputs[i];
+
+ output->id = sr->outputs[i];
+ output->info = XRRGetOutputInfo (display, sr, sr->outputs[i]);
+ output->cur_crtc = find_crtc (screen_info, output->info);
+ output->auto_set = 0;
+ if (output->cur_crtc) {
+ output->off_set = 0;
+ } else {
+ output->off_set = 1;
+ }
+
+ }
+
+ /* set current crtc */
+ screen_info->cur_crtc = screen_info->outputs[0]->cur_crtc;
+ screen_info->primary_crtc = screen_info->cur_crtc;
+ screen_info->cur_output = screen_info->outputs[0];
+
+ return screen_info;
+}
+
+void free_screen_info (struct ScreenInfo *screen_info)
+{
+ free (screen_info->outputs);
+ free (screen_info->crtcs);
+ free (screen_info);
+}
+
+
+
+static char * get_mode_name (struct ScreenInfo *screen_info, RRMode mode_id)
+{
+ XRRScreenResources *sr;
+ char *mode_name = NULL;
+ int i;
+
+ sr = screen_info->res;
+
+ for (i = 0; i < sr->nmode; i++) {
+ if (sr->modes[i].id == mode_id) {
+ break;
+ }
+ }
+
+ if (i == sr->nmode) {
+ mode_name = g_strdup ("Unknown mode");
+ } else {
+ double rate;
+ if (sr->modes[i].hTotal && sr->modes[i].vTotal) {
+ rate = ((double) sr->modes[i].dotClock /
+ ((double) sr->modes[i].hTotal * (double) sr->modes[i].vTotal));
+ } else {
+ rate = 0;
+ }
+ mode_name = g_strdup_printf ("%s%6.1fHz", sr->modes[i].name, rate);
+ }
+
+ return mode_name;
+}
+
+/*check if other outputs that connected to the same crtc support this mode*/
+static int check_mode (struct ScreenInfo *screen_info, struct OutputInfo *output, RRMode mode_id)
+{
+ XRRCrtcInfo *crtc_info;
+ /* XRR */
+ int i, j;
+ int mode_ok = 1;
+
+ if (!output->cur_crtc) {
+ return 1;
+ }
+
+ crtc_info = output->cur_crtc->info;
+ for (i = 0; i < crtc_info->noutput; i++) {
+ XRROutputInfo *output_info;
+ int nmode;
+
+ if (output->id == crtc_info->outputs[i]) {
+ continue;
+ }
+
+ mode_ok = 0;
+ output_info = XRRGetOutputInfo (screen_info->dpy, screen_info->res, crtc_info->outputs[i]);
+ nmode = output_info->nmode;
+ for (j = 0; j < nmode; j++) {
+ if (mode_id == output_info->modes[j]) {
+ mode_ok = 1;
+ break;
+ }
+ }
+ if (!mode_ok) {
+ break;
+ }
+ }
+
+ return mode_ok;
+}
+
+static RRCrtc get_crtc_id_by_output_id (struct ScreenInfo *screen_info, RROutput output_id)
+{
+ int i;
+ RRCrtc crtc_id = -1;
+
+ for (i = 0; i < screen_info->n_output; i++) {
+ if (output_id == screen_info->outputs[i]->id) {
+ if (screen_info->outputs[i]->cur_crtc) {
+ crtc_id = screen_info->outputs[i]->cur_crtc->id;
+ } else {
+ crtc_id = 0; /* this output is off */
+ }
+ break;
+ }
+ }
+
+ return crtc_id;
+}
+
+static struct CrtcInfo *
+get_crtc_info_by_xid (struct ScreenInfo *screen_info, RRCrtc crtc_id)
+{
+ struct CrtcInfo *crtc_info = NULL;
+ int i;
+
+ for (i = 0; i < screen_info->n_crtc; i++) {
+ if (crtc_id == screen_info->crtcs[i]->id) {
+ crtc_info = screen_info->crtcs[i];
+ break;
+ }
+ }
+
+ return crtc_info;
+}
+
+static XRRModeInfo *
+preferred_mode (struct ScreenInfo *screen_info, struct OutputInfo *output)
+{
+ XRROutputInfo *output_info = output->info;
+ Display *dpy;
+ int screen;
+ int m;
+ XRRModeInfo *best;
+ int bestDist;
+
+ dpy = screen_info->dpy;
+ screen = DefaultScreen (dpy);
+ best = NULL;
+ bestDist = 0;
+ for (m = 0; m < output_info->nmode; m++) {
+ XRRModeInfo *mode_info = find_mode_by_xid (screen_info, output_info->modes[m]);
+ int dist;
+
+ if (m < output_info->npreferred)
+ dist = 0;
+ else if (output_info->mm_height)
+ dist = (1000 * DisplayHeight(dpy, screen) / DisplayHeightMM(dpy, screen) -
+ 1000 * mode_info->height / output_info->mm_height);
+ else
+ dist = DisplayHeight(dpy, screen) - mode_info->height;
+
+ if (dist < 0) dist = -dist;
+ if (!best || dist < bestDist) {
+ best = mode_info;
+ bestDist = dist;
+ }
+ }
+ return best;
+}
+
+int main_low_apply (struct ScreenInfo *screen_info)
+{
+ int i;
+ struct CrtcInfo *crtc_info;
+
+ /* set_positions (screen_info); */
+
+ if (!set_screen_size (screen_info)) {
+ printf("Screen Size FAILURE\n\r");
+ return 0;
+ }
+
+ for (i = 0; i < screen_info->n_crtc; i++) {
+ int old_x, old_y, old_w, old_h;
+
+ XRRCrtcInfo *crtc_info = XRRGetCrtcInfo (screen_info->dpy, screen_info->res, screen_info->crtcs[i]->id);
+ XRRModeInfo *old_mode = find_mode_by_xid (screen_info, crtc_info->mode);
+
+ if (crtc_info->mode == None) {
+ continue;
+ }
+
+ old_x = crtc_info->x;
+ old_y = crtc_info->y;
+ old_w = mode_width (old_mode, crtc_info->rotation);
+ old_h = mode_height (old_mode, crtc_info->rotation);
+
+ if (old_x + old_w <= screen_info->cur_width &&
+ old_y + old_h <= screen_info->cur_height ) {
+ continue;
+ } else {
+ crtc_disable (screen_info->crtcs[i]);
+ }
+ }
+
+ screen_apply (screen_info);
+
+ for (i = 0; i < screen_info->n_crtc; i++) {
+ Status s;
+ crtc_info = screen_info->crtcs[i];
+
+ s = crtc_apply (crtc_info);
+ if (RRSetConfigSuccess != s) {
+ fprintf (stderr, "crtc apply error\n");
+ }
+ }
+
+ return 1;
+}
+
+void output_auto (struct ScreenInfo *screen_info, struct OutputInfo *output_info)
+{
+ XRRModeInfo *mode_info;
+ RRMode mode_id;
+ struct CrtcInfo *crtc_info;
+ XRROutputInfo *probe_output_info;
+
+ if (RR_Disconnected == output_info->info->connection) {
+ XRRScreenResources *cur_res;
+
+ cur_res = XRRGetScreenResources (screen_info->dpy, screen_info->window);
+ probe_output_info = XRRGetOutputInfo (screen_info->dpy, cur_res, output_info->id);
+ if (RR_Disconnected != probe_output_info->connection) {
+ output_info->info = probe_output_info;
+ output_info->cur_crtc = auto_find_crtc (screen_info, output_info);
+ }
+ }
+
+ mode_info = preferred_mode (screen_info, output_info);
+ if (!mode_info) {
+ return;
+ }
+ mode_id = mode_info->id;
+
+ crtc_info = output_info->cur_crtc;
+ if (crtc_info) {
+ crtc_info->cur_mode_id = mode_id;
+ } else {
+ crtc_info = auto_find_crtc (screen_info, output_info);
+ if (!crtc_info) {
+#if RANDR_GUI_DEBUG
+ fprintf (stderr, "Can not find usable CRTC\n");
+#endif
+ return;
+ } else {
+ screen_info->cur_output->cur_crtc = crtc_info;
+ screen_info->cur_crtc = crtc_info;
+ screen_info->cur_crtc->cur_noutput++;
+ fprintf (stderr, "n output: %d\n", screen_info->cur_crtc->cur_noutput);
+ screen_info->cur_crtc->cur_mode_id = mode_id;
+ screen_info->cur_crtc->changed = 1;
+ }
+ }
+
+}
+
+void output_off (struct ScreenInfo *screen_info, struct OutputInfo *output)
+{
+ if (output->cur_crtc) {
+ output->cur_crtc->cur_noutput--;
+ }
+ output->cur_crtc = NULL;
+ screen_info->cur_crtc = NULL;
+ output->off_set = 1;
+}