/* * Copyright © 2010 Raptor Engineering * 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 #include #include Status internal_crtc_disable (struct CrtcInfo *crtc); char * internal_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 * internal_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 * internal_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 internal_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 = internal_find_mode_by_xid (screen_info, mode_id); width = internal_mode_width (mode_info, crtc_info->cur_rotation); break; } } return width; } int internal_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 = internal_find_mode_by_xid (screen_info, mode_id); height = internal_mode_height (mode_info, crtc_info->cur_rotation); break; } } return height; } int internal_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 internal_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 * internal_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 * internal_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 internal_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 = internal_find_mode_by_xid (screen_info, crtc->cur_mode_id); cur_x = crtc->cur_x; cur_y = crtc->cur_y; w = internal_mode_width (mode_info, crtc->cur_rotation); h = internal_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 internal_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 internal_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 internal_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 internal_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* internal_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); if (sr == NULL) { return NULL; } 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 = internal_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 internal_free_screen_info (struct ScreenInfo *screen_info) { free (screen_info->outputs); free (screen_info->crtcs); free (screen_info); } /*check if other outputs that connected to the same crtc support this mode*/ static int internal_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 internal_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 * internal_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 * internal_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 = internal_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 internal_main_low_apply (struct ScreenInfo *screen_info) { int i; struct CrtcInfo *crtc_info; /* set_positions (screen_info); */ if (!internal_set_screen_size (screen_info)) { printf("Screen Size FAILURE\n"); 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 = internal_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 = internal_mode_width (old_mode, crtc_info->rotation); old_h = internal_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 { internal_crtc_disable (screen_info->crtcs[i]); } } internal_screen_apply (screen_info); for (i = 0; i < screen_info->n_crtc; i++) { Status s; crtc_info = screen_info->crtcs[i]; s = internal_crtc_apply (crtc_info); if (RRSetConfigSuccess != s) { fprintf (stderr, "crtc apply error\n"); } } return 1; } void internal_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 = internal_auto_find_crtc (screen_info, output_info); } } mode_info = internal_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 = internal_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 internal_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; } void internal_output_set_primary (struct ScreenInfo *screen_info, RROutput output_id) { XRRSetOutputPrimary(screen_info->dpy, screen_info->window, output_id); }