/* * Copyright © 2014 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #include #include "dix/dix_priv.h" #include "randr/randrstr_priv.h" #include "swaprep.h" static Atom RRMonitorCrtcName(RRCrtcPtr crtc) { char name[20]; if (crtc->numOutputs) { RROutputPtr output = crtc->outputs[0]; return MakeAtom(output->name, output->nameLength, TRUE); } sprintf(name, "Monitor-%08lx", (unsigned long int)crtc->id); return MakeAtom(name, strlen(name), TRUE); } static Bool RRMonitorCrtcPrimary(RRCrtcPtr crtc) { ScreenPtr screen = crtc->pScreen; rrScrPrivPtr pScrPriv = rrGetScrPriv(screen); int o; for (o = 0; o < crtc->numOutputs; o++) if (crtc->outputs[o] == pScrPriv->primaryOutput) return TRUE; return FALSE; } #define DEFAULT_PIXELS_PER_MM (96.0 / 25.4) static void RRMonitorGetCrtcGeometry(RRCrtcPtr crtc, RRMonitorGeometryPtr geometry) { ScreenPtr screen = crtc->pScreen; rrScrPrivPtr pScrPriv = rrGetScrPriv(screen); BoxRec panned_area; /* Check to see if crtc is panned and return the full area when applicable. */ if (pScrPriv && pScrPriv->rrGetPanning && pScrPriv->rrGetPanning(screen, crtc, &panned_area, NULL, NULL) && (panned_area.x2 > panned_area.x1) && (panned_area.y2 > panned_area.y1)) { geometry->box = panned_area; } else { int width, height; RRCrtcGetScanoutSize(crtc, &width, &height); geometry->box.x1 = crtc->x; geometry->box.y1 = crtc->y; geometry->box.x2 = geometry->box.x1 + width; geometry->box.y2 = geometry->box.y1 + height; } if (crtc->numOutputs && crtc->outputs[0]->mmWidth && crtc->outputs[0]->mmHeight) { RROutputPtr output = crtc->outputs[0]; geometry->mmWidth = output->mmWidth; geometry->mmHeight = output->mmHeight; } else { geometry->mmWidth = floor ((geometry->box.x2 - geometry->box.x1) / DEFAULT_PIXELS_PER_MM + 0.5); geometry->mmHeight = floor ((geometry->box.y2 - geometry->box.y1) / DEFAULT_PIXELS_PER_MM + 0.5); } } static Bool RRMonitorSetFromServer(RRCrtcPtr crtc, RRMonitorPtr monitor) { int o; monitor->name = RRMonitorCrtcName(crtc); monitor->pScreen = crtc->pScreen; monitor->numOutputs = crtc->numOutputs; monitor->outputs = calloc(crtc->numOutputs, sizeof(RROutput)); if (!monitor->outputs) return FALSE; for (o = 0; o < crtc->numOutputs; o++) monitor->outputs[o] = crtc->outputs[o]->id; monitor->primary = RRMonitorCrtcPrimary(crtc); monitor->automatic = TRUE; RRMonitorGetCrtcGeometry(crtc, &monitor->geometry); return TRUE; } static Bool RRMonitorAutomaticGeometry(RRMonitorPtr monitor) { return (monitor->geometry.box.x1 == 0 && monitor->geometry.box.y1 == 0 && monitor->geometry.box.x2 == 0 && monitor->geometry.box.y2 == 0); } static void RRMonitorGetGeometry(RRMonitorPtr monitor, RRMonitorGeometryPtr geometry) { if (RRMonitorAutomaticGeometry(monitor) && monitor->numOutputs > 0) { ScreenPtr screen = monitor->pScreen; rrScrPrivPtr pScrPriv = rrGetScrPriv(screen); RRMonitorGeometryRec first = { .box = { 0, 0, 0, 0 }, .mmWidth = 0, .mmHeight = 0 }; RRMonitorGeometryRec this; int c, o, co; int active_crtcs = 0; *geometry = first; for (o = 0; o < monitor->numOutputs; o++) { RRCrtcPtr crtc = NULL; Bool in_use = FALSE; for (c = 0; !in_use && c < pScrPriv->numCrtcs; c++) { crtc = pScrPriv->crtcs[c]; if (!crtc->mode) continue; for (co = 0; !in_use && co < crtc->numOutputs; co++) if (monitor->outputs[o] == crtc->outputs[co]->id) in_use = TRUE; } if (!in_use) continue; RRMonitorGetCrtcGeometry(crtc, &this); if (active_crtcs == 0) { first = this; *geometry = this; } else { geometry->box.x1 = min(this.box.x1, geometry->box.x1); geometry->box.x2 = max(this.box.x2, geometry->box.x2); geometry->box.y1 = min(this.box.y1, geometry->box.y1); geometry->box.y2 = max(this.box.y2, geometry->box.y2); } active_crtcs++; } /* Adjust physical sizes to account for total area */ if (active_crtcs > 1 && first.box.x2 != first.box.x1 && first.box.y2 != first.box.y1) { geometry->mmWidth = ((double)(geometry->box.x2 - geometry->box.x1) / (first.box.x2 - first.box.x1)) * first.mmWidth; geometry->mmHeight = ((double)(geometry->box.y2 - geometry->box.y1) / (first.box.y2 - first.box.y1)) * first.mmHeight; } } else { *geometry = monitor->geometry; } } static Bool RRMonitorSetFromClient(RRMonitorPtr client_monitor, RRMonitorPtr monitor) { monitor->name = client_monitor->name; monitor->pScreen = client_monitor->pScreen; monitor->numOutputs = client_monitor->numOutputs; monitor->outputs = calloc(client_monitor->numOutputs, sizeof (RROutput)); if (!monitor->outputs && client_monitor->numOutputs) return FALSE; memcpy(monitor->outputs, client_monitor->outputs, client_monitor->numOutputs * sizeof (RROutput)); monitor->primary = client_monitor->primary; monitor->automatic = client_monitor->automatic; RRMonitorGetGeometry(client_monitor, &monitor->geometry); return TRUE; } typedef struct _rrMonitorList { int num_client; int num_server; RRCrtcPtr *server_crtc; int num_crtcs; int client_primary; int server_primary; } RRMonitorListRec, *RRMonitorListPtr; static Bool RRMonitorInitList(ScreenPtr screen, RRMonitorListPtr mon_list, Bool get_active) { rrScrPrivPtr pScrPriv = rrGetScrPriv(screen); int m, o, c, sc; int numCrtcs; ScreenPtr secondary; if (!RRGetInfo(screen, FALSE)) return FALSE; /* Count the number of crtcs in this and any secondary screens */ numCrtcs = pScrPriv->numCrtcs; xorg_list_for_each_entry(secondary, &screen->secondary_list, secondary_head) { rrScrPrivPtr pSecondaryPriv; if (!secondary->is_output_secondary) continue; pSecondaryPriv = rrGetScrPriv(secondary); numCrtcs += pSecondaryPriv->numCrtcs; } mon_list->num_crtcs = numCrtcs; mon_list->server_crtc = calloc(numCrtcs * 2, sizeof (RRCrtcPtr)); if (!mon_list->server_crtc) return FALSE; /* Collect pointers to all of the active crtcs */ c = 0; for (sc = 0; sc < pScrPriv->numCrtcs; sc++, c++) { if (pScrPriv->crtcs[sc]->mode != NULL) mon_list->server_crtc[c] = pScrPriv->crtcs[sc]; } xorg_list_for_each_entry(secondary, &screen->secondary_list, secondary_head) { rrScrPrivPtr pSecondaryPriv; if (!secondary->is_output_secondary) continue; pSecondaryPriv = rrGetScrPriv(secondary); for (sc = 0; sc < pSecondaryPriv->numCrtcs; sc++, c++) { if (pSecondaryPriv->crtcs[sc]->mode != NULL) mon_list->server_crtc[c] = pSecondaryPriv->crtcs[sc]; } } /* Walk the list of client-defined monitors, clearing the covered * CRTCs from the full list and finding whether one of the * monitors is primary */ mon_list->num_client = pScrPriv->numMonitors; mon_list->client_primary = -1; for (m = 0; m < pScrPriv->numMonitors; m++) { RRMonitorPtr monitor = pScrPriv->monitors[m]; if (get_active) { RRMonitorGeometryRec geom; RRMonitorGetGeometry(monitor, &geom); if (geom.box.x2 - geom.box.x1 == 0 || geom.box.y2 - geom.box.y1 == 0) { mon_list->num_client--; continue; } } if (monitor->primary && mon_list->client_primary == -1) mon_list->client_primary = m; for (o = 0; o < monitor->numOutputs; o++) { for (c = 0; c < numCrtcs; c++) { RRCrtcPtr crtc = mon_list->server_crtc[c]; if (crtc) { int co; for (co = 0; co < crtc->numOutputs; co++) if (crtc->outputs[co]->id == monitor->outputs[o]) { mon_list->server_crtc[c] = NULL; break; } } } } } /* Now look at the active CRTCs, and count * those not covered by a client monitor, as well * as finding whether one of them is marked primary */ mon_list->num_server = 0; mon_list->server_primary = -1; for (c = 0; c < mon_list->num_crtcs; c++) { RRCrtcPtr crtc = mon_list->server_crtc[c]; if (!crtc) continue; mon_list->num_server++; if (RRMonitorCrtcPrimary(crtc) && mon_list->server_primary == -1) mon_list->server_primary = c; } return TRUE; } static void RRMonitorFiniList(RRMonitorListPtr list) { free(list->server_crtc); } /* Construct a complete list of protocol-visible monitors, including * the manually generated ones as well as those generated * automatically from the remaining CRCTs */ Bool RRMonitorMakeList(ScreenPtr screen, Bool get_active, RRMonitorPtr *monitors_ret, int *nmon_ret) { rrScrPrivPtr pScrPriv = rrGetScrPriv(screen); RRMonitorListRec list; int m, c; RRMonitorPtr mon, monitors; Bool has_primary = FALSE; if (!pScrPriv) return FALSE; if (!RRMonitorInitList(screen, &list, get_active)) return FALSE; monitors = calloc(list.num_client + list.num_server, sizeof (RRMonitorRec)); if (!monitors) { RRMonitorFiniList(&list); return FALSE; } mon = monitors; /* Fill in the primary monitor data first */ if (list.client_primary >= 0) { RRMonitorSetFromClient(pScrPriv->monitors[list.client_primary], mon); mon++; } else if (list.server_primary >= 0) { RRMonitorSetFromServer(list.server_crtc[list.server_primary], mon); mon++; } /* Fill in the client-defined monitors next */ for (m = 0; m < pScrPriv->numMonitors; m++) { if (m == list.client_primary) continue; if (get_active) { RRMonitorGeometryRec geom; RRMonitorGetGeometry(pScrPriv->monitors[m], &geom); if (geom.box.x2 - geom.box.x1 == 0 || geom.box.y2 - geom.box.y1 == 0) { continue; } } RRMonitorSetFromClient(pScrPriv->monitors[m], mon); if (has_primary) mon->primary = FALSE; else if (mon->primary) has_primary = TRUE; mon++; } /* And finish with the list of crtc-inspired monitors */ for (c = 0; c < list.num_crtcs; c++) { RRCrtcPtr crtc = list.server_crtc[c]; if (c == list.server_primary && list.client_primary < 0) continue; if (!list.server_crtc[c]) continue; RRMonitorSetFromServer(crtc, mon); if (has_primary) mon->primary = FALSE; else if (mon->primary) has_primary = TRUE; mon++; } RRMonitorFiniList(&list); *nmon_ret = list.num_client + list.num_server; *monitors_ret = monitors; return TRUE; } int RRMonitorCountList(ScreenPtr screen) { RRMonitorListRec list; int nmon; if (!RRMonitorInitList(screen, &list, FALSE)) return -1; nmon = list.num_client + list.num_server; RRMonitorFiniList(&list); return nmon; } void RRMonitorFree(RRMonitorPtr monitor) { free(monitor); } RRMonitorPtr RRMonitorAlloc(int noutput) { RRMonitorPtr monitor; monitor = calloc(1, sizeof (RRMonitorRec) + noutput * sizeof (RROutput)); if (!monitor) return NULL; monitor->numOutputs = noutput; monitor->outputs = (RROutput *) (monitor + 1); return monitor; } static int RRMonitorDelete(ClientPtr client, ScreenPtr screen, Atom name) { rrScrPrivPtr pScrPriv = rrGetScrPriv(screen); int m; if (!pScrPriv) { client->errorValue = name; return BadAtom; } for (m = 0; m < pScrPriv->numMonitors; m++) { RRMonitorPtr monitor = pScrPriv->monitors[m]; if (monitor->name == name) { memmove(pScrPriv->monitors + m, pScrPriv->monitors + m + 1, (pScrPriv->numMonitors - (m + 1)) * sizeof (RRMonitorPtr)); --pScrPriv->numMonitors; RRMonitorFree(monitor); return Success; } } client->errorValue = name; return BadValue; } static Bool RRMonitorMatchesOutputName(ScreenPtr screen, Atom name) { rrScrPrivPtr pScrPriv = rrGetScrPriv(screen); int o; const char *str = NameForAtom(name); int len = strlen(str); for (o = 0; o < pScrPriv->numOutputs; o++) { RROutputPtr output = pScrPriv->outputs[o]; if (output->nameLength == len && !memcmp(output->name, str, len)) return TRUE; } return FALSE; } int RRMonitorAdd(ClientPtr client, ScreenPtr screen, RRMonitorPtr monitor) { rrScrPrivPtr pScrPriv = rrGetScrPriv(screen); int m; ScreenPtr secondary; RRMonitorPtr *monitors; if (!pScrPriv) return BadAlloc; /* 'name' must not match the name of any Output on the screen, or * a Value error results. */ if (RRMonitorMatchesOutputName(screen, monitor->name)) { client->errorValue = monitor->name; return BadValue; } xorg_list_for_each_entry(secondary, &screen->secondary_list, secondary_head) { if (!secondary->is_output_secondary) continue; if (RRMonitorMatchesOutputName(secondary, monitor->name)) { client->errorValue = monitor->name; return BadValue; } } /* Allocate space for the new pointer. This is done before * removing matching monitors as it may fail, and the request * needs to not have any side-effects on failure */ if (pScrPriv->numMonitors) monitors = reallocarray(pScrPriv->monitors, pScrPriv->numMonitors + 1, sizeof (RRMonitorPtr)); else monitors = malloc(sizeof (RRMonitorPtr)); if (!monitors) return BadAlloc; pScrPriv->monitors = monitors; for (m = 0; m < pScrPriv->numMonitors; m++) { RRMonitorPtr existing = pScrPriv->monitors[m]; /* If 'name' matches an existing Monitor on the screen, the * existing one will be deleted as if RRDeleteMonitor were called. */ if (existing->name == monitor->name) { (void) RRMonitorDelete(client, screen, existing->name); continue; } if (monitor->primary) existing->primary = FALSE; } /* Add the new one to the list */ pScrPriv->monitors[pScrPriv->numMonitors++] = monitor; return Success; } void RRMonitorFreeList(RRMonitorPtr monitors, int nmon) { int m; for (m = 0; m < nmon; m++) free(monitors[m].outputs); free(monitors); } void RRMonitorInit(ScreenPtr screen) { rrScrPrivPtr pScrPriv = rrGetScrPriv(screen); if (!pScrPriv) return; pScrPriv->numMonitors = 0; pScrPriv->monitors = NULL; } void RRMonitorClose(ScreenPtr screen) { rrScrPrivPtr pScrPriv = rrGetScrPriv(screen); int m; if (!pScrPriv) return; for (m = 0; m < pScrPriv->numMonitors; m++) RRMonitorFree(pScrPriv->monitors[m]); free(pScrPriv->monitors); pScrPriv->monitors = NULL; pScrPriv->numMonitors = 0; } static CARD32 RRMonitorTimestamp(ScreenPtr screen) { rrScrPrivPtr pScrPriv = rrGetScrPriv(screen); /* XXX should take client monitor changes into account */ return pScrPriv->lastConfigTime.milliseconds; } int ProcRRGetMonitors(ClientPtr client) { REQUEST(xRRGetMonitorsReq); xRRGetMonitorsReply rep = { .type = X_Reply, .sequenceNumber = client->sequence, .length = 0, }; WindowPtr window; ScreenPtr screen; int r; RRMonitorPtr monitors; int nmonitors; int noutputs; int m; Bool get_active; REQUEST_SIZE_MATCH(xRRGetMonitorsReq); r = dixLookupWindow(&window, stuff->window, client, DixGetAttrAccess); if (r != Success) return r; screen = window->drawable.pScreen; get_active = stuff->get_active; if (!RRMonitorMakeList(screen, get_active, &monitors, &nmonitors)) return BadAlloc; rep.timestamp = RRMonitorTimestamp(screen); noutputs = 0; for (m = 0; m < nmonitors; m++) { rep.length += SIZEOF(xRRMonitorInfo) >> 2; rep.length += monitors[m].numOutputs; noutputs += monitors[m].numOutputs; } rep.nmonitors = nmonitors; rep.noutputs = noutputs; if (client->swapped) { swaps(&rep.sequenceNumber); swapl(&rep.length); swapl(&rep.timestamp); swapl(&rep.nmonitors); swapl(&rep.noutputs); } WriteToClient(client, sizeof(xRRGetMonitorsReply), &rep); client->pSwapReplyFunc = (ReplySwapPtr) CopySwap32Write; for (m = 0; m < nmonitors; m++) { RRMonitorPtr monitor = &monitors[m]; xRRMonitorInfo info = { .name = monitor->name, .primary = monitor->primary, .automatic = monitor->automatic, .noutput = monitor->numOutputs, .x = monitor->geometry.box.x1, .y = monitor->geometry.box.y1, .width = monitor->geometry.box.x2 - monitor->geometry.box.x1, .height = monitor->geometry.box.y2 - monitor->geometry.box.y1, .widthInMillimeters = monitor->geometry.mmWidth, .heightInMillimeters = monitor->geometry.mmHeight, }; if (client->swapped) { swapl(&info.name); swaps(&info.noutput); swaps(&info.x); swaps(&info.y); swaps(&info.width); swaps(&info.height); swapl(&info.widthInMillimeters); swapl(&info.heightInMillimeters); } WriteToClient(client, sizeof(xRRMonitorInfo), &info); WriteSwappedDataToClient(client, monitor->numOutputs * sizeof (RROutput), monitor->outputs); } RRMonitorFreeList(monitors, nmonitors); return Success; } int ProcRRSetMonitor(ClientPtr client) { REQUEST(xRRSetMonitorReq); WindowPtr window; ScreenPtr screen; RRMonitorPtr monitor; int r; REQUEST_AT_LEAST_SIZE(xRRSetMonitorReq); if (stuff->monitor.noutput != stuff->length - (SIZEOF(xRRSetMonitorReq) >> 2)) return BadLength; r = dixLookupWindow(&window, stuff->window, client, DixGetAttrAccess); if (r != Success) return r; screen = window->drawable.pScreen; if (!ValidAtom(stuff->monitor.name)) return BadAtom; /* Allocate the new monitor */ monitor = RRMonitorAlloc(stuff->monitor.noutput); if (!monitor) return BadAlloc; /* Fill in the bits from the request */ monitor->pScreen = screen; monitor->name = stuff->monitor.name; monitor->primary = stuff->monitor.primary; monitor->automatic = FALSE; memcpy(monitor->outputs, stuff + 1, stuff->monitor.noutput * sizeof (RROutput)); monitor->geometry.box.x1 = stuff->monitor.x; monitor->geometry.box.y1 = stuff->monitor.y; monitor->geometry.box.x2 = stuff->monitor.x + stuff->monitor.width; monitor->geometry.box.y2 = stuff->monitor.y + stuff->monitor.height; monitor->geometry.mmWidth = stuff->monitor.widthInMillimeters; monitor->geometry.mmHeight = stuff->monitor.heightInMillimeters; r = RRMonitorAdd(client, screen, monitor); if (r == Success) RRSendConfigNotify(screen); else RRMonitorFree(monitor); return r; } int ProcRRDeleteMonitor(ClientPtr client) { REQUEST(xRRDeleteMonitorReq); WindowPtr window; ScreenPtr screen; int r; REQUEST_SIZE_MATCH(xRRDeleteMonitorReq); r = dixLookupWindow(&window, stuff->window, client, DixGetAttrAccess); if (r != Success) return r; screen = window->drawable.pScreen; if (!ValidAtom(stuff->name)) { client->errorValue = stuff->name; return BadAtom; } r = RRMonitorDelete(client, screen, stuff->name); if (r == Success) RRSendConfigNotify(screen); return r; }