Xrandr.c revision 8d0bc965
1/* 2 * Copyright © 2000 Compaq Computer Corporation, Inc. 3 * Copyright © 2002 Hewlett Packard Company, Inc. 4 * 5 * Permission to use, copy, modify, distribute, and sell this software and its 6 * documentation for any purpose is hereby granted without fee, provided that 7 * the above copyright notice appear in all copies and that both that 8 * copyright notice and this permission notice appear in supporting 9 * documentation, and that the name of Compaq or HP not be used in advertising 10 * or publicity pertaining to distribution of the software without specific, 11 * written prior permission. HP makes no representations about the 12 * suitability of this software for any purpose. It is provided "as is" 13 * without express or implied warranty. 14 * 15 * HP DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL COMPAQ 17 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 19 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 * 22 * Author: Jim Gettys, HP Labs, HP. 23 */ 24 25#ifdef HAVE_CONFIG_H 26#include <config.h> 27#endif 28 29#include <stdio.h> 30#include <X11/Xlib.h> 31/* we need to be able to manipulate the Display structure on events */ 32#include <X11/Xlibint.h> 33#include <X11/extensions/render.h> 34#include <X11/extensions/Xrender.h> 35#include "Xrandrint.h" 36 37static XExtensionInfo XRRExtensionInfo; 38_X_HIDDEN char XRRExtensionName[] = RANDR_NAME; 39 40static Bool XRRWireToEvent(Display *dpy, XEvent *event, xEvent *wire); 41static Status XRREventToWire(Display *dpy, XEvent *event, xEvent *wire); 42 43static int 44XRRCloseDisplay (Display *dpy, XExtCodes *codes); 45 46static /* const */ XExtensionHooks rr_extension_hooks = { 47 NULL, /* create_gc */ 48 NULL, /* copy_gc */ 49 NULL, /* flush_gc */ 50 NULL, /* free_gc */ 51 NULL, /* create_font */ 52 NULL, /* free_font */ 53 XRRCloseDisplay, /* close_display */ 54 XRRWireToEvent, /* wire_to_event */ 55 XRREventToWire, /* event_to_wire */ 56 NULL, /* error */ 57 NULL, /* error_string */ 58}; 59 60static Bool XRRWireToEvent(Display *dpy, XEvent *event, xEvent *wire) 61{ 62 XExtDisplayInfo *info = XRRFindDisplay(dpy); 63 64 RRCheckExtension(dpy, info, False); 65 66 switch ((wire->u.u.type & 0x7F) - info->codes->first_event) 67 { 68 case RRScreenChangeNotify: { 69 XRRScreenChangeNotifyEvent *aevent= (XRRScreenChangeNotifyEvent *) event; 70 xRRScreenChangeNotifyEvent *awire = (xRRScreenChangeNotifyEvent *) wire; 71 aevent->type = awire->type & 0x7F; 72 aevent->serial = _XSetLastRequestRead(dpy, (xGenericReply *) wire); 73 aevent->send_event = (awire->type & 0x80) != 0; 74 aevent->display = dpy; 75 aevent->window = awire->window; 76 aevent->root = awire->root; 77 aevent->timestamp = awire->timestamp; 78 aevent->config_timestamp = awire->configTimestamp; 79 aevent->size_index = awire->sizeID; 80 aevent->subpixel_order = awire->subpixelOrder; 81 aevent->rotation = awire->rotation; 82 aevent->width = awire->widthInPixels; 83 aevent->height = awire->heightInPixels; 84 aevent->mwidth = awire->widthInMillimeters; 85 aevent->mheight = awire->heightInMillimeters; 86 return True; 87 } 88 case RRNotify: { 89 switch (wire->u.u.detail) { 90 case RRNotify_OutputChange: { 91 XRROutputChangeNotifyEvent *aevent = (XRROutputChangeNotifyEvent *) event; 92 xRROutputChangeNotifyEvent *awire = (xRROutputChangeNotifyEvent *) wire; 93 aevent->type = awire->type & 0x7F; 94 aevent->serial = _XSetLastRequestRead(dpy, (xGenericReply *) wire); 95 aevent->send_event = (awire->type & 0x80) != 0; 96 aevent->display = dpy; 97 aevent->window = awire->window; 98 aevent->subtype = awire->subCode; 99 aevent->output = awire->output; 100 aevent->crtc = awire->crtc; 101 aevent->mode = awire->mode; 102 aevent->rotation = awire->rotation; 103 aevent->connection = awire->connection; 104 aevent->subpixel_order = awire->subpixelOrder; 105 return True; 106 } 107 case RRNotify_CrtcChange: { 108 XRRCrtcChangeNotifyEvent *aevent = (XRRCrtcChangeNotifyEvent *) event; 109 xRRCrtcChangeNotifyEvent *awire = (xRRCrtcChangeNotifyEvent *) wire; 110 aevent->type = awire->type & 0x7F; 111 aevent->serial = _XSetLastRequestRead(dpy, (xGenericReply *) wire); 112 aevent->send_event = (awire->type & 0x80) != 0; 113 aevent->display = dpy; 114 aevent->window = awire->window; 115 aevent->subtype = awire->subCode; 116 aevent->crtc = awire->crtc; 117 aevent->mode = awire->mode; 118 aevent->rotation = awire->rotation; 119 aevent->x = awire->x; 120 aevent->y = awire->y; 121 aevent->width = awire->width; 122 aevent->height = awire->height; 123 return True; 124 } 125 case RRNotify_OutputProperty: { 126 XRROutputPropertyNotifyEvent *aevent = (XRROutputPropertyNotifyEvent *) event; 127 xRROutputPropertyNotifyEvent *awire = (xRROutputPropertyNotifyEvent *) wire; 128 aevent->type = awire->type & 0x7F; 129 aevent->serial = _XSetLastRequestRead(dpy, (xGenericReply *) wire); 130 aevent->send_event = (awire->type & 0x80) != 0; 131 aevent->display = dpy; 132 aevent->window = awire->window; 133 aevent->subtype = awire->subCode; 134 aevent->output = awire->output; 135 aevent->property = awire->atom; 136 aevent->timestamp = awire->timestamp; 137 aevent->state = awire->state; 138 return True; 139 } 140 case RRNotify_ProviderChange: { 141 XRRProviderChangeNotifyEvent *aevent = (XRRProviderChangeNotifyEvent *) event; 142 xRRProviderChangeNotifyEvent *awire = (xRRProviderChangeNotifyEvent *) wire; 143 aevent->type = awire->type & 0x7F; 144 aevent->serial = _XSetLastRequestRead(dpy, (xGenericReply *) wire); 145 aevent->send_event = (awire->type & 0x80) != 0; 146 aevent->display = dpy; 147 aevent->window = awire->window; 148 aevent->subtype = awire->subCode; 149 aevent->provider = awire->provider; 150 aevent->timestamp = awire->timestamp; 151 return True; 152 } 153 case RRNotify_ProviderProperty: { 154 XRRProviderPropertyNotifyEvent *aevent = (XRRProviderPropertyNotifyEvent *) event; 155 xRRProviderPropertyNotifyEvent *awire = (xRRProviderPropertyNotifyEvent *) wire; 156 aevent->type = awire->type & 0x7F; 157 aevent->serial = _XSetLastRequestRead(dpy, (xGenericReply *) wire); 158 aevent->send_event = (awire->type & 0x80) != 0; 159 aevent->display = dpy; 160 aevent->window = awire->window; 161 aevent->subtype = awire->subCode; 162 aevent->provider = awire->provider; 163 aevent->property = awire->atom; 164 aevent->timestamp = awire->timestamp; 165 aevent->state = awire->state; 166 return True; 167 } 168 case RRNotify_ResourceChange: { 169 XRRResourceChangeNotifyEvent *aevent = (XRRResourceChangeNotifyEvent *) event; 170 xRRResourceChangeNotifyEvent *awire = (xRRResourceChangeNotifyEvent *) wire; 171 aevent->type = awire->type & 0x7F; 172 aevent->serial = _XSetLastRequestRead(dpy, (xGenericReply *) wire); 173 aevent->send_event = (awire->type & 0x80) != 0; 174 aevent->display = dpy; 175 aevent->window = awire->window; 176 aevent->subtype = awire->subCode; 177 aevent->timestamp = awire->timestamp; 178 return True; 179 } 180 break; 181 } 182 } 183 } 184 185 return False; 186} 187 188static Status XRREventToWire(Display *dpy, XEvent *event, xEvent *wire) 189{ 190 XExtDisplayInfo *info = XRRFindDisplay(dpy); 191 192 RRCheckExtension(dpy, info, False); 193 194 switch ((event->type & 0x7F) - info->codes->first_event) 195 { 196 case RRScreenChangeNotify: { 197 xRRScreenChangeNotifyEvent *awire = (xRRScreenChangeNotifyEvent *) wire; 198 XRRScreenChangeNotifyEvent *aevent = (XRRScreenChangeNotifyEvent *) event; 199 awire->type = aevent->type | (aevent->send_event ? 0x80 : 0); 200 awire->rotation = (CARD8) aevent->rotation; 201 awire->sequenceNumber = aevent->serial & 0xFFFF; 202 awire->timestamp = aevent->timestamp; 203 awire->configTimestamp = aevent->config_timestamp; 204 awire->root = aevent->root; 205 awire->window = aevent->window; 206 awire->sizeID = aevent->size_index; 207 awire->subpixelOrder = aevent->subpixel_order; 208 awire->widthInPixels = aevent->width; 209 awire->heightInPixels = aevent->height; 210 awire->widthInMillimeters = aevent->mwidth; 211 awire->heightInMillimeters = aevent->mheight; 212 return True; 213 } 214 case RRNotify: { 215 xRRCrtcChangeNotifyEvent *awire = (xRRCrtcChangeNotifyEvent *) wire; 216 XRRNotifyEvent *aevent = (XRRNotifyEvent *) event; 217 awire->type = aevent->type | (aevent->send_event ? 0x80 : 0); 218 awire->sequenceNumber = aevent->serial & 0xFFFF; 219 awire->subCode = aevent->subtype; 220 switch (aevent->subtype) { 221 case RRNotify_OutputChange: { 222 xRROutputChangeNotifyEvent *sawire = (xRROutputChangeNotifyEvent *) wire; 223 XRROutputChangeNotifyEvent *saevent = (XRROutputChangeNotifyEvent *) event; 224 sawire->window = saevent->window; 225 sawire->output = saevent->output; 226 sawire->crtc = saevent->crtc; 227 sawire->mode = saevent->mode; 228 sawire->rotation = saevent->rotation; 229 sawire->connection = saevent->connection; 230 sawire->subpixelOrder = saevent->subpixel_order; 231 return True; 232 } 233 case RRNotify_CrtcChange: { 234 xRRCrtcChangeNotifyEvent *sawire = (xRRCrtcChangeNotifyEvent *) wire; 235 XRRCrtcChangeNotifyEvent *saevent = (XRRCrtcChangeNotifyEvent *) event; 236 sawire->window = saevent->window; 237 sawire->crtc = saevent->crtc; 238 sawire->mode = saevent->mode; 239 sawire->rotation = saevent->rotation; 240 sawire->x = saevent->x; 241 sawire->y = saevent->y; 242 sawire->width = saevent->width; 243 sawire->height = saevent->height; 244 return True; 245 } 246 case RRNotify_OutputProperty: { 247 xRROutputPropertyNotifyEvent *sawire = (xRROutputPropertyNotifyEvent *) wire; 248 XRROutputPropertyNotifyEvent *saevent = (XRROutputPropertyNotifyEvent *) event; 249 sawire->window = saevent->window; 250 sawire->output = saevent->output; 251 sawire->atom = saevent->property; 252 sawire->timestamp = saevent->timestamp; 253 sawire->state = saevent->state; 254 return True; 255 } 256 case RRNotify_ProviderChange: { 257 xRRProviderChangeNotifyEvent *sawire = (xRRProviderChangeNotifyEvent *) wire; 258 XRRProviderChangeNotifyEvent *saevent = (XRRProviderChangeNotifyEvent *) event; 259 sawire->window = saevent->window; 260 sawire->provider = saevent->provider; 261 return True; 262 } 263 case RRNotify_ProviderProperty: { 264 xRRProviderPropertyNotifyEvent *sawire = (xRRProviderPropertyNotifyEvent *) wire; 265 XRRProviderPropertyNotifyEvent *saevent = (XRRProviderPropertyNotifyEvent *) event; 266 sawire->window = saevent->window; 267 sawire->provider = saevent->provider; 268 sawire->atom = saevent->property; 269 sawire->timestamp = saevent->timestamp; 270 sawire->state = saevent->state; 271 return True; 272 } 273 case RRNotify_ResourceChange: { 274 xRRResourceChangeNotifyEvent *sawire = (xRRResourceChangeNotifyEvent *) wire; 275 XRRResourceChangeNotifyEvent *saevent = (XRRResourceChangeNotifyEvent *) event; 276 sawire->window = saevent->window; 277 sawire->timestamp = saevent->timestamp; 278 return True; 279 } 280 } 281 } 282 } 283 return False; 284} 285 286_X_HIDDEN XExtDisplayInfo * 287XRRFindDisplay (Display *dpy) 288{ 289 XExtDisplayInfo *dpyinfo = XextFindDisplay (&XRRExtensionInfo, dpy); 290 291 if (!dpyinfo) { 292 XRandRInfo *xrri; 293 int numscreens; 294 295 dpyinfo = XextAddDisplay (&XRRExtensionInfo, dpy, 296 XRRExtensionName, 297 &rr_extension_hooks, 298 RRNumberEvents, NULL); 299 numscreens = ScreenCount(dpy); 300 xrri = Xmalloc (sizeof(XRandRInfo) + 301 sizeof(char *) * numscreens); 302 xrri->config = (XRRScreenConfiguration **)(xrri + 1); 303 for (int i = 0; i < numscreens; i++) 304 xrri->config[i] = NULL; 305 xrri->major_version = -1; 306 dpyinfo->data = (char *) xrri; 307 } 308 return dpyinfo; 309} 310 311static int 312XRRCloseDisplay (Display *dpy, XExtCodes *codes) 313{ 314 XExtDisplayInfo *info = XRRFindDisplay (dpy); 315 316 LockDisplay(dpy); 317 /* 318 * free cached data 319 */ 320 if (XextHasExtension(info)) { 321 XRandRInfo *xrri = (XRandRInfo *) info->data; 322 if (xrri) { 323 XRRScreenConfiguration **configs = xrri->config; 324 325 for (int i = 0; i < ScreenCount(dpy); i++) { 326 if (configs[i] != NULL) XFree (configs[i]); 327 } 328 XFree (xrri); 329 } 330 } 331 UnlockDisplay(dpy); 332 return XextRemoveDisplay (&XRRExtensionInfo, dpy); 333} 334 335int XRRRootToScreen(Display *dpy, Window root) 336{ 337 for (int snum = 0; snum < ScreenCount(dpy); snum++) { 338 if (RootWindow(dpy, snum) == root) return snum; 339 } 340 return -1; 341} 342 343 344Bool XRRQueryExtension (Display *dpy, 345 int *event_base_return, 346 int *error_base_return) 347{ 348 XExtDisplayInfo *info = XRRFindDisplay (dpy); 349 350 if (XextHasExtension(info)) { 351 *event_base_return = info->codes->first_event; 352 *error_base_return = info->codes->first_error; 353 return True; 354 } else { 355 return False; 356 } 357} 358 359_X_HIDDEN Bool 360_XRRHasRates (int major, int minor) 361{ 362 return major > 1 || (major == 1 && minor >= 1); 363} 364 365Status XRRQueryVersion (Display *dpy, 366 int *major_versionp, 367 int *minor_versionp) 368{ 369 XExtDisplayInfo *info = XRRFindDisplay (dpy); 370 xRRQueryVersionReply rep; 371 xRRQueryVersionReq *req; 372 XRandRInfo *xrri; 373 374 RRCheckExtension (dpy, info, 0); 375 376 xrri = (XRandRInfo *) info->data; 377 378 /* 379 * only get the version information from the server if we don't have it already 380 */ 381 if (xrri->major_version == -1) { 382 LockDisplay (dpy); 383 GetReq (RRQueryVersion, req); 384 req->reqType = info->codes->major_opcode; 385 req->randrReqType = X_RRQueryVersion; 386 req->majorVersion = RANDR_MAJOR; 387 req->minorVersion = RANDR_MINOR; 388 if (!_XReply (dpy, (xReply *) &rep, 0, xTrue)) { 389 UnlockDisplay (dpy); 390 SyncHandle (); 391 return 0; 392 } 393 xrri->major_version = rep.majorVersion; 394 xrri->minor_version = rep.minorVersion; 395 xrri->has_rates = _XRRHasRates (xrri->major_version, xrri->minor_version); 396 UnlockDisplay (dpy); 397 SyncHandle (); 398 } 399 *major_versionp = xrri->major_version; 400 *minor_versionp = xrri->minor_version; 401 return 1; 402} 403 404_X_HIDDEN Bool 405_XRRVersionHandler (Display *dpy, 406 xReply *rep, 407 char *buf, 408 int len, 409 XPointer data) 410{ 411 xRRQueryVersionReply replbuf; 412 xRRQueryVersionReply *repl; 413 _XRRVersionState *state = (_XRRVersionState *) data; 414 415 if (dpy->last_request_read != state->version_seq) 416 return False; 417 if (rep->generic.type == X_Error) 418 { 419 state->error = True; 420 return False; 421 } 422 repl = (xRRQueryVersionReply *) 423 _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len, 424 (SIZEOF(xRRQueryVersionReply) - SIZEOF(xReply)) >> 2, 425 True); 426 state->major_version = repl->majorVersion; 427 state->minor_version = repl->minorVersion; 428 return True; 429} 430 431/* 432 * in protocol version 0.1, routine added to allow selecting for new events. 433 */ 434 435void XRRSelectInput (Display *dpy, Window window, int mask) 436{ 437 XExtDisplayInfo *info = XRRFindDisplay (dpy); 438 xRRSelectInputReq *req; 439 440 RRSimpleCheckExtension (dpy, info); 441 442 LockDisplay (dpy); 443 GetReq (RRSelectInput, req); 444 req->reqType = info->codes->major_opcode; 445 req->randrReqType = X_RRSelectInput; 446 req->window = window; 447 req->enable = 0; 448 if (mask) req->enable = mask; 449 UnlockDisplay (dpy); 450 SyncHandle (); 451 return; 452} 453 454int XRRUpdateConfiguration(XEvent *event) 455{ 456 XRRScreenChangeNotifyEvent *scevent; 457 XConfigureEvent *rcevent; 458 Display *dpy = event->xany.display; 459 XExtDisplayInfo *info; 460 XRandRInfo *xrri; 461 int snum; 462 463 /* first, see if it is a vanilla configure notify event */ 464 if (event->type == ConfigureNotify) { 465 rcevent = (XConfigureEvent *) event; 466 snum = XRRRootToScreen(dpy, rcevent->window); 467 if (snum != -1) { 468 dpy->screens[snum].width = rcevent->width; 469 dpy->screens[snum].height = rcevent->height; 470 return 1; 471 } 472 } 473 474 info = XRRFindDisplay(dpy); 475 RRCheckExtension (dpy, info, 0); 476 477 switch (event->type - info->codes->first_event) { 478 case RRScreenChangeNotify: 479 scevent = (XRRScreenChangeNotifyEvent *) event; 480 snum = XRRRootToScreen(dpy, 481 ((XRRScreenChangeNotifyEvent *) event)->root); 482 if (snum < 0) 483 return 0; 484 485 if (scevent->rotation & (RR_Rotate_90 | RR_Rotate_270)) { 486 dpy->screens[snum].width = scevent->height; 487 dpy->screens[snum].height = scevent->width; 488 dpy->screens[snum].mwidth = scevent->mheight; 489 dpy->screens[snum].mheight = scevent->mwidth; 490 } else { 491 dpy->screens[snum].width = scevent->width; 492 dpy->screens[snum].height = scevent->height; 493 dpy->screens[snum].mwidth = scevent->mwidth; 494 dpy->screens[snum].mheight = scevent->mheight; 495 } 496 XRenderSetSubpixelOrder (dpy, snum, scevent->subpixel_order); 497 break; 498 default: 499 return 0; 500 } 501 xrri = (XRandRInfo *) info->data; 502 /* 503 * so the next time someone wants some data, it will be fetched; 504 * it might be better to force the round trip immediately, but 505 * I dislike pounding the server simultaneously when not necessary 506 */ 507 if (xrri->config[snum] != NULL) { 508 XFree (xrri->config[snum]); 509 xrri->config[snum] = NULL; 510 } 511 return 1; 512} 513