diff options
Diffstat (limited to 'kcontrol/randr/lowlevel_randr.c')
-rw-r--r-- | kcontrol/randr/lowlevel_randr.c | 700 |
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; +} |