From 9c2b4f8e0e2b5d4b5e1102d6eea7bdb4211baa68 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Tue, 31 Mar 2015 11:18:44 +1000 Subject: [PATCH 1/3] xf86Crtc: add tile prop setting Add support for drivers to set the tiling property. This is used by clients to work out the monitor tiles for DisplayID monitors. Reviewed-by: Keith Packard Signed-off-by: Dave Airlie --- hw/xfree86/modes/xf86Crtc.c | 53 +++++++++++++++++++++++++++++++++++++ hw/xfree86/modes/xf86Crtc.h | 26 ++++++++++++++++-- 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/hw/xfree86/modes/xf86Crtc.c b/hw/xfree86/modes/xf86Crtc.c index 9d592a7eb..a1947241b 100644 --- a/hw/xfree86/modes/xf86Crtc.c +++ b/hw/xfree86/modes/xf86Crtc.c @@ -3026,6 +3026,27 @@ xf86OutputSetEDIDProperty(xf86OutputPtr output, void *data, int data_len) } } +#define TILE_ATOM_NAME "TILE" +/* changing this in the future could be tricky as people may hardcode 8 */ +#define TILE_PROP_NUM_ITEMS 8 +static void +xf86OutputSetTileProperty(xf86OutputPtr output) +{ + Atom tile_atom = MakeAtom(TILE_ATOM_NAME, sizeof(TILE_ATOM_NAME) - 1, TRUE); + + /* This may get called before the RandR resources have been created */ + if (output->randr_output == NULL) + return; + + if (output->tile_info.group_id != 0) { + RRChangeOutputProperty(output->randr_output, tile_atom, XA_INTEGER, 32, + PropModeReplace, TILE_PROP_NUM_ITEMS, (uint32_t *)&output->tile_info, FALSE, TRUE); + } + else { + RRDeleteOutputProperty(output->randr_output, tile_atom); + } +} + #endif /* Pull out a phyiscal size from a detailed timing if available. */ @@ -3071,6 +3092,38 @@ handle_detailed_physical_size(struct detailed_monitor_section } } +Bool +xf86OutputParseKMSTile(const char *tile_data, int tile_length, + struct xf86CrtcTileInfo *tile_info) +{ + int ret; + + ret = sscanf(tile_data, "%d:%d:%d:%d:%d:%d:%d:%d", + &tile_info->group_id, + &tile_info->flags, + &tile_info->num_h_tile, + &tile_info->num_v_tile, + &tile_info->tile_h_loc, + &tile_info->tile_v_loc, + &tile_info->tile_h_size, + &tile_info->tile_v_size); + if (ret != 8) + return FALSE; + return TRUE; +} + +void +xf86OutputSetTile(xf86OutputPtr output, struct xf86CrtcTileInfo *tile_info) +{ + if (tile_info) + output->tile_info = *tile_info; + else + memset(&output->tile_info, 0, sizeof(output->tile_info)); +#ifdef RANDR_12_INTERFACE + xf86OutputSetTileProperty(output); +#endif +} + /** * Set the EDID information for the specified output */ diff --git a/hw/xfree86/modes/xf86Crtc.h b/hw/xfree86/modes/xf86Crtc.h index 692bf40b9..3c5bbcfd5 100644 --- a/hw/xfree86/modes/xf86Crtc.h +++ b/hw/xfree86/modes/xf86Crtc.h @@ -70,6 +70,17 @@ typedef enum _xf86OutputStatus { XF86OutputStatusUnknown } xf86OutputStatus; +struct xf86CrtcTileInfo { + uint32_t group_id; + uint32_t flags; + uint32_t num_h_tile; + uint32_t num_v_tile; + uint32_t tile_h_loc; + uint32_t tile_v_loc; + uint32_t tile_h_size; + uint32_t tile_v_size; +}; + typedef struct _xf86CrtcFuncs { /** * Turns the crtc on/off, or sets intermediate power levels if available. @@ -226,7 +237,7 @@ typedef struct _xf86CrtcFuncs { } xf86CrtcFuncsRec, *xf86CrtcFuncsPtr; -#define XF86_CRTC_VERSION 5 +#define XF86_CRTC_VERSION 6 struct _xf86Crtc { /** @@ -500,7 +511,7 @@ typedef struct _xf86OutputFuncs { (*destroy) (xf86OutputPtr output); } xf86OutputFuncsRec, *xf86OutputFuncsPtr; -#define XF86_OUTPUT_VERSION 2 +#define XF86_OUTPUT_VERSION 3 struct _xf86Output { /** @@ -615,6 +626,8 @@ struct _xf86Output { BoxRec initialTotalArea; BoxRec initialTrackingArea; INT16 initialBorder[4]; + + struct xf86CrtcTileInfo tile_info; }; typedef struct _xf86ProviderFuncs { @@ -880,6 +893,15 @@ xf86SetSingleMode(ScrnInfoPtr pScrn, DisplayModePtr desired, Rotation rotation); extern _X_EXPORT void xf86OutputSetEDID(xf86OutputPtr output, xf86MonPtr edid_mon); +/** + * Set the TILE information for the specified output + */ +extern _X_EXPORT void +xf86OutputSetTile(xf86OutputPtr output, struct xf86CrtcTileInfo *tile_info); + +extern _X_EXPORT Bool +xf86OutputParseKMSTile(const char *tile_data, int tile_length, struct xf86CrtcTileInfo *tile_info); + /** * Return the list of modes supported by the EDID information * stored in 'output' From 7e1f86d42b54fb7f6492875e47a718eaeca3069b Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 16 Dec 2014 01:59:03 -0800 Subject: [PATCH 2/3] randr: Add Monitor support (v1.1) Store the user-defined monitors in the RandR screen private. Generate a list of monitors from both the user-defined ones and from any outputs not mentioned in one of the user-defined monitors. This list covers both the outputs in the main screen as well as any slaves. v1.1: airlied: fix up primary skipping bug, fix wrong height initialiser add get_active flag from updated protocol. Reviewed-by: Dave Airlie Signed-off-by: Keith Packard --- include/protocol-versions.h | 2 +- randr/Makefile.am | 1 + randr/randr.c | 4 + randr/randrstr.h | 44 +++ randr/rrdispatch.c | 3 + randr/rrmonitor.c | 748 ++++++++++++++++++++++++++++++++++++ randr/rrsdispatch.c | 38 ++ 7 files changed, 839 insertions(+), 1 deletion(-) create mode 100644 randr/rrmonitor.c diff --git a/include/protocol-versions.h b/include/protocol-versions.h index fc428c8cf..be532ff55 100644 --- a/include/protocol-versions.h +++ b/include/protocol-versions.h @@ -73,7 +73,7 @@ /* RandR */ #define SERVER_RANDR_MAJOR_VERSION 1 -#define SERVER_RANDR_MINOR_VERSION 4 +#define SERVER_RANDR_MINOR_VERSION 5 /* Record */ #define SERVER_RECORD_MAJOR_VERSION 1 diff --git a/randr/Makefile.am b/randr/Makefile.am index ccaff3f02..90dc9ec9a 100644 --- a/randr/Makefile.am +++ b/randr/Makefile.am @@ -15,6 +15,7 @@ librandr_la_SOURCES = \ rrdispatch.c \ rrinfo.c \ rrmode.c \ + rrmonitor.c \ rroutput.c \ rrpointer.c \ rrproperty.c \ diff --git a/randr/randr.c b/randr/randr.c index 6e3f14b4e..ad1dda227 100644 --- a/randr/randr.c +++ b/randr/randr.c @@ -98,6 +98,8 @@ RRCloseScreen(ScreenPtr pScreen) if (pScrPriv->provider) RRProviderDestroy(pScrPriv->provider); + RRMonitorClose(pScreen); + free(pScrPriv->crtcs); free(pScrPriv->outputs); free(pScrPriv); @@ -333,6 +335,8 @@ RRScreenInit(ScreenPtr pScreen) pScrPriv->numCrtcs = 0; pScrPriv->crtcs = NULL; + RRMonitorInit(pScreen); + RRNScreens += 1; /* keep count of screens that implement randr */ return TRUE; } diff --git a/randr/randrstr.h b/randr/randrstr.h index 13e6a8596..438a52aeb 100644 --- a/randr/randrstr.h +++ b/randr/randrstr.h @@ -80,6 +80,7 @@ typedef struct _rrProperty RRPropertyRec, *RRPropertyPtr; typedef struct _rrCrtc RRCrtcRec, *RRCrtcPtr; typedef struct _rrOutput RROutputRec, *RROutputPtr; typedef struct _rrProvider RRProviderRec, *RRProviderPtr; +typedef struct _rrMonitor RRMonitorRec, *RRMonitorPtr; struct _rrMode { int refcnt; @@ -169,6 +170,22 @@ struct _rrProvider { struct _rrProvider *output_source; }; +typedef struct _rrMonitorGeometry { + BoxRec box; + CARD32 mmWidth; + CARD32 mmHeight; +} RRMonitorGeometryRec, *RRMonitorGeometryPtr; + +struct _rrMonitor { + Atom name; + ScreenPtr pScreen; + int numOutputs; + RROutput *outputs; + Bool primary; + Bool automatic; + RRMonitorGeometryRec geometry; +}; + #if RANDR_12_INTERFACE typedef Bool (*RRScreenSetSizeProcPtr) (ScreenPtr pScreen, CARD16 width, @@ -338,6 +355,9 @@ typedef struct _rrScrPriv { RRProviderDestroyProcPtr rrProviderDestroy; + int numMonitors; + RRMonitorPtr *monitors; + } rrScrPrivRec, *rrScrPrivPtr; extern _X_EXPORT DevPrivateKeyRec rrPrivKeyRec; @@ -981,6 +1001,30 @@ extern _X_EXPORT void RRXineramaExtensionInit(void); #endif +void +RRMonitorInit(ScreenPtr screen); + +Bool +RRMonitorMakeList(ScreenPtr screen, Bool get_active, RRMonitorPtr *monitors_ret, int *nmon_ret); + +int +RRMonitorCountList(ScreenPtr screen); + +void +RRMonitorFreeList(RRMonitorPtr monitors, int nmon); + +void +RRMonitorClose(ScreenPtr screen); + +int +ProcRRGetMonitors(ClientPtr client); + +int +ProcRRSetMonitor(ClientPtr client); + +int +ProcRRDeleteMonitor(ClientPtr client); + #endif /* _RANDRSTR_H_ */ /* diff --git a/randr/rrdispatch.c b/randr/rrdispatch.c index b9346d3f9..13ac6b117 100644 --- a/randr/rrdispatch.c +++ b/randr/rrdispatch.c @@ -254,4 +254,7 @@ int (*ProcRandrVector[RRNumberRequests]) (ClientPtr) = { ProcRRChangeProviderProperty, /* 39 */ ProcRRDeleteProviderProperty, /* 40 */ ProcRRGetProviderProperty, /* 41 */ + ProcRRGetMonitors, /* 42 */ + ProcRRSetMonitor, /* 43 */ + ProcRRDeleteMonitor, /* 44 */ }; diff --git a/randr/rrmonitor.c b/randr/rrmonitor.c new file mode 100644 index 000000000..fbdd352f6 --- /dev/null +++ b/randr/rrmonitor.c @@ -0,0 +1,748 @@ +/* + * 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 "randrstr.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-%08x", 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(RRCrtc)); + 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 = (this.box.x2 - this.box.x1) / (first.box.x2 - first.box.x1) * first.mmWidth; + geometry->mmHeight = (this.box.y2 - this.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 slave; + + if (!RRGetInfo(screen, FALSE)) + return FALSE; + + /* Count the number of crtcs in this and any slave screens */ + numCrtcs = pScrPriv->numCrtcs; + xorg_list_for_each_entry(slave, &screen->output_slave_list, output_head) { + rrScrPrivPtr pSlavePriv; + pSlavePriv = rrGetScrPriv(slave); + numCrtcs += pSlavePriv->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(slave, &screen->output_slave_list, output_head) { + rrScrPrivPtr pSlavePriv; + pSlavePriv = rrGetScrPriv(slave); + for (sc = 0; sc < pSlavePriv->numCrtcs; sc++, c++) { + if (pSlavePriv->crtcs[sc]->mode != NULL) + mon_list->server_crtc[c] = pSlavePriv->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(pScrPriv->crtcs[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 < pScrPriv->numCrtcs; c++) { + RRCrtcPtr crtc = pScrPriv->crtcs[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; +} + +static void +RRMonitorFree(RRMonitorPtr monitor) +{ + free(monitor); +} + +static 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; +} + +static int +RRMonitorAdd(ClientPtr client, ScreenPtr screen, RRMonitorPtr monitor) +{ + rrScrPrivPtr pScrPriv = rrGetScrPriv(screen); + int m; + ScreenPtr slave; + 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(slave, &screen->output_slave_list, output_head) { + if (RRMonitorMatchesOutputName(slave, monitor->name)) { + client->errorValue = monitor->name; + return BadValue; + } + } + + /* 'name' must not match the name of any Monitor on the screen, or + * a Value error results. + */ + + for (m = 0; m < pScrPriv->numMonitors; m++) { + if (pScrPriv->monitors[m]->name == 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 = realloc(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]; + int o, eo; + + /* 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; + } + + /* For each output in 'info.outputs', each one is removed from all + * pre-existing Monitors. If removing the output causes the list + * of outputs for that Monitor to become empty, then that + * Monitor will be deleted as if RRDeleteMonitor were called. + */ + + for (eo = 0; eo < existing->numOutputs; eo++) { + for (o = 0; o < monitor->numOutputs; o++) { + if (monitor->outputs[o] == existing->outputs[eo]) { + memmove(existing->outputs + eo, existing->outputs + eo + 1, + (existing->numOutputs - (eo + 1)) * sizeof (RROutput)); + --existing->numOutputs; + --eo; + break; + } + } + if (existing->numOutputs == 0) { + (void) RRMonitorDelete(client, screen, existing->name); + break; + } + } + 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) + 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; + } + + return RRMonitorDelete(client, screen, stuff->name); +} diff --git a/randr/rrsdispatch.c b/randr/rrsdispatch.c index 47558cf75..cf2a3b024 100644 --- a/randr/rrsdispatch.c +++ b/randr/rrsdispatch.c @@ -565,6 +565,41 @@ static int SProcRRGetProviderProperty(ClientPtr client) return ProcRandrVector[stuff->randrReqType] (client); } +static int SProcRRGetMonitors(ClientPtr client) { + REQUEST(xRRGetMonitorsReq); + + REQUEST_SIZE_MATCH(xRRGetMonitorsReq); + swaps(&stuff->length); + swapl(&stuff->window); + return ProcRandrVector[stuff->randrReqType] (client); +} + +static int SProcRRSetMonitor(ClientPtr client) { + REQUEST(xRRSetMonitorReq); + + REQUEST_AT_LEAST_SIZE(xRRGetMonitorsReq); + swaps(&stuff->length); + swapl(&stuff->window); + swapl(&stuff->monitor.name); + swaps(&stuff->monitor.noutput); + swaps(&stuff->monitor.x); + swaps(&stuff->monitor.y); + swaps(&stuff->monitor.width); + swaps(&stuff->monitor.height); + SwapRestL(stuff); + return ProcRandrVector[stuff->randrReqType] (client); +} + +static int SProcRRDeleteMonitor(ClientPtr client) { + REQUEST(xRRDeleteMonitorReq); + + REQUEST_SIZE_MATCH(xRRDeleteMonitorReq); + swaps(&stuff->length); + swapl(&stuff->window); + swapl(&stuff->name); + return ProcRandrVector[stuff->randrReqType] (client); +} + int (*SProcRandrVector[RRNumberRequests]) (ClientPtr) = { SProcRRQueryVersion, /* 0 */ /* we skip 1 to make old clients fail pretty immediately */ @@ -614,4 +649,7 @@ int (*SProcRandrVector[RRNumberRequests]) (ClientPtr) = { SProcRRChangeProviderProperty, /* 39 */ SProcRRDeleteProviderProperty, /* 40 */ SProcRRGetProviderProperty, /* 41 */ + SProcRRGetMonitors, /* 42 */ + SProcRRSetMonitor, /* 43 */ + SProcRRDeleteMonitor, /* 44 */ }; From 5de13830709a7f2d4d112d71e062f710ef466ab6 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 16 Dec 2014 09:56:50 -0800 Subject: [PATCH 3/3] randr: Use Monitor list for Xinerama This replaces the CRTC-based Xinerama implementation with one which uses Monitors instead, allowing clients to manipulate the Xinerama configuration through the RandR Monitor list. Reviewed-by: Dave Airlie Signed-off-by: Keith Packard --- randr/rrxinerama.c | 114 +++++++-------------------------------------- 1 file changed, 18 insertions(+), 96 deletions(-) diff --git a/randr/rrxinerama.c b/randr/rrxinerama.c index 36632c7c7..b6e9586d7 100644 --- a/randr/rrxinerama.c +++ b/randr/rrxinerama.c @@ -149,35 +149,10 @@ ProcRRXineramaGetState(ClientPtr client) return Success; } -static Bool -RRXineramaCrtcActive(RRCrtcPtr crtc) -{ - return crtc->mode != NULL && crtc->numOutputs > 0; -} - static int RRXineramaScreenCount(ScreenPtr pScreen) { - int i, n; - ScreenPtr slave; - - n = 0; - if (rrGetScrPriv(pScreen)) { - rrScrPriv(pScreen); - for (i = 0; i < pScrPriv->numCrtcs; i++) - if (RRXineramaCrtcActive(pScrPriv->crtcs[i])) - n++; - } - - xorg_list_for_each_entry(slave, &pScreen->output_slave_list, output_head) { - rrScrPrivPtr pSlavePriv; - pSlavePriv = rrGetScrPriv(slave); - for (i = 0; i < pSlavePriv->numCrtcs; i++) - if (RRXineramaCrtcActive(pSlavePriv->crtcs[i])) - n++; - } - - return n; + return RRMonitorCountList(pScreen); } static Bool @@ -276,42 +251,16 @@ ProcRRXineramaIsActive(ClientPtr client) } static void -RRXineramaWriteCrtc(ClientPtr client, RRCrtcPtr crtc) +RRXineramaWriteMonitor(ClientPtr client, RRMonitorPtr monitor) { xXineramaScreenInfo scratch; - if (RRXineramaCrtcActive(crtc)) { - ScreenPtr pScreen = crtc->pScreen; - rrScrPrivPtr pScrPriv = rrGetScrPriv(pScreen); - BoxRec panned_area; + scratch.x_org = monitor->geometry.box.x1; + scratch.y_org = monitor->geometry.box.y1; + scratch.width = monitor->geometry.box.x2 - monitor->geometry.box.x1; + scratch.height = monitor->geometry.box.y2 - monitor->geometry.box.y1; - /* Check to see if crtc is panned and return the full area when applicable. */ - if (pScrPriv && pScrPriv->rrGetPanning && - pScrPriv->rrGetPanning(pScreen, crtc, &panned_area, NULL, NULL) && - (panned_area.x2 > panned_area.x1) && - (panned_area.y2 > panned_area.y1)) { - scratch.x_org = panned_area.x1; - scratch.y_org = panned_area.y1; - scratch.width = panned_area.x2 - panned_area.x1; - scratch.height = panned_area.y2 - panned_area.y1; - } - else { - int width, height; - - RRCrtcGetScanoutSize(crtc, &width, &height); - scratch.x_org = crtc->x; - scratch.y_org = crtc->y; - scratch.width = width; - scratch.height = height; - } - if (client->swapped) { - swaps(&scratch.x_org); - swaps(&scratch.y_org); - swaps(&scratch.width); - swaps(&scratch.height); - } - WriteToClient(client, sz_XineramaScreenInfo, &scratch); - } + WriteToClient(client, sz_XineramaScreenInfo, &scratch); } int @@ -319,21 +268,23 @@ ProcRRXineramaQueryScreens(ClientPtr client) { xXineramaQueryScreensReply rep; ScreenPtr pScreen = screenInfo.screens[RR_XINERAMA_SCREEN]; - int n = 0; - int i; + int m; + RRMonitorPtr monitors = NULL; + int nmonitors = 0; REQUEST_SIZE_MATCH(xXineramaQueryScreensReq); if (RRXineramaScreenActive(pScreen)) { RRGetInfo(pScreen, FALSE); - n = RRXineramaScreenCount(pScreen); + if (!RRMonitorMakeList(pScreen, TRUE, &monitors, &nmonitors)) + return BadAlloc; } rep = (xXineramaQueryScreensReply) { .type = X_Reply, .sequenceNumber = client->sequence, - .length = bytes_to_int32(n * sz_XineramaScreenInfo), - .number = n + .length = bytes_to_int32(nmonitors * sz_XineramaScreenInfo), + .number = nmonitors }; if (client->swapped) { swaps(&rep.sequenceNumber); @@ -342,40 +293,11 @@ ProcRRXineramaQueryScreens(ClientPtr client) } WriteToClient(client, sizeof(xXineramaQueryScreensReply), &rep); - if (n) { - ScreenPtr slave; - rrScrPriv(pScreen); - int has_primary = 0; - RRCrtcPtr primary_crtc = NULL; + for (m = 0; m < nmonitors; m++) + RRXineramaWriteMonitor(client, &monitors[m]); - if (pScrPriv->primaryOutput && pScrPriv->primaryOutput->crtc) { - has_primary = 1; - primary_crtc = pScrPriv->primaryOutput->crtc; - RRXineramaWriteCrtc(client, pScrPriv->primaryOutput->crtc); - } - - for (i = 0; i < pScrPriv->numCrtcs; i++) { - if (has_primary && - primary_crtc == pScrPriv->crtcs[i]) { - has_primary = 0; - continue; - } - RRXineramaWriteCrtc(client, pScrPriv->crtcs[i]); - } - - xorg_list_for_each_entry(slave, &pScreen->output_slave_list, output_head) { - rrScrPrivPtr pSlavePriv; - pSlavePriv = rrGetScrPriv(slave); - for (i = 0; i < pSlavePriv->numCrtcs; i++) { - if (has_primary && - primary_crtc == pSlavePriv->crtcs[i]) { - has_primary = 0; - continue; - } - RRXineramaWriteCrtc(client, pSlavePriv->crtcs[i]); - } - } - } + if (monitors) + RRMonitorFreeList(monitors, nmonitors); return Success; }