Xrandr.c revision 0597fb56
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 *awire = (xRROutputChangeNotifyEvent *) wire; 223 XRROutputChangeNotifyEvent *aevent = (XRROutputChangeNotifyEvent *) event; 224 awire->window = aevent->window; 225 awire->output = aevent->output; 226 awire->crtc = aevent->crtc; 227 awire->mode = aevent->mode; 228 awire->rotation = aevent->rotation; 229 awire->connection = aevent->connection; 230 awire->subpixelOrder = aevent->subpixel_order; 231 return True; 232 } 233 case RRNotify_CrtcChange: { 234 xRRCrtcChangeNotifyEvent *awire = (xRRCrtcChangeNotifyEvent *) wire; 235 XRRCrtcChangeNotifyEvent *aevent = (XRRCrtcChangeNotifyEvent *) event; 236 awire->window = aevent->window; 237 awire->crtc = aevent->crtc; 238 awire->mode = aevent->mode; 239 awire->rotation = aevent->rotation; 240 awire->x = aevent->x; 241 awire->y = aevent->y; 242 awire->width = aevent->width; 243 awire->height = aevent->height; 244 return True; 245 } 246 case RRNotify_OutputProperty: { 247 xRROutputPropertyNotifyEvent *awire = (xRROutputPropertyNotifyEvent *) wire; 248 XRROutputPropertyNotifyEvent *aevent = (XRROutputPropertyNotifyEvent *) event; 249 awire->window = aevent->window; 250 awire->output = aevent->output; 251 awire->atom = aevent->property; 252 awire->timestamp = aevent->timestamp; 253 awire->state = aevent->state; 254 return True; 255 } 256 case RRNotify_ProviderChange: { 257 xRRProviderChangeNotifyEvent *awire = (xRRProviderChangeNotifyEvent *) wire; 258 XRRProviderChangeNotifyEvent *aevent = (XRRProviderChangeNotifyEvent *) event; 259 awire->window = aevent->window; 260 awire->provider = aevent->provider; 261 return True; 262 } 263 case RRNotify_ProviderProperty: { 264 xRRProviderPropertyNotifyEvent *awire = (xRRProviderPropertyNotifyEvent *) wire; 265 XRRProviderPropertyNotifyEvent *aevent = (XRRProviderPropertyNotifyEvent *) event; 266 awire->window = aevent->window; 267 awire->provider = aevent->provider; 268 awire->atom = aevent->property; 269 awire->timestamp = aevent->timestamp; 270 awire->state = aevent->state; 271 return True; 272 } 273 case RRNotify_ResourceChange: { 274 xRRResourceChangeNotifyEvent *awire = (xRRResourceChangeNotifyEvent *) wire; 275 XRRResourceChangeNotifyEvent *aevent = (XRRResourceChangeNotifyEvent *) event; 276 awire->window = aevent->window; 277 awire->timestamp = aevent->timestamp; 278 return True; 279 } 280 } 281 } 282 } 283 return False; 284} 285 286_X_HIDDEN XExtDisplayInfo * 287XRRFindDisplay (Display *dpy) 288{ 289 XExtDisplayInfo *dpyinfo; 290 XRandRInfo *xrri; 291 int i, numscreens; 292 293 dpyinfo = XextFindDisplay (&XRRExtensionInfo, dpy); 294 if (!dpyinfo) { 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(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 int i; 315 XRRScreenConfiguration **configs; 316 XExtDisplayInfo *info = XRRFindDisplay (dpy); 317 XRandRInfo *xrri; 318 319 LockDisplay(dpy); 320 /* 321 * free cached data 322 */ 323 if (XextHasExtension(info)) { 324 xrri = (XRandRInfo *) info->data; 325 if (xrri) { 326 configs = xrri->config; 327 328 for (i = 0; i < ScreenCount(dpy); i++) { 329 if (configs[i] != NULL) XFree (configs[i]); 330 } 331 XFree (xrri); 332 } 333 } 334 UnlockDisplay(dpy); 335 return XextRemoveDisplay (&XRRExtensionInfo, dpy); 336} 337 338int XRRRootToScreen(Display *dpy, Window root) 339{ 340 int snum; 341 for (snum = 0; snum < ScreenCount(dpy); snum++) { 342 if (RootWindow(dpy, snum) == root) return snum; 343 } 344 return -1; 345} 346 347 348Bool XRRQueryExtension (Display *dpy, 349 int *event_base_return, 350 int *error_base_return) 351{ 352 XExtDisplayInfo *info = XRRFindDisplay (dpy); 353 354 if (XextHasExtension(info)) { 355 *event_base_return = info->codes->first_event; 356 *error_base_return = info->codes->first_error; 357 return True; 358 } else { 359 return False; 360 } 361} 362 363_X_HIDDEN Bool 364_XRRHasRates (int major, int minor) 365{ 366 return major > 1 || (major == 1 && minor >= 1); 367} 368 369Status XRRQueryVersion (Display *dpy, 370 int *major_versionp, 371 int *minor_versionp) 372{ 373 XExtDisplayInfo *info = XRRFindDisplay (dpy); 374 xRRQueryVersionReply rep; 375 xRRQueryVersionReq *req; 376 XRandRInfo *xrri; 377 378 RRCheckExtension (dpy, info, 0); 379 380 xrri = (XRandRInfo *) info->data; 381 382 /* 383 * only get the version information from the server if we don't have it already 384 */ 385 if (xrri->major_version == -1) { 386 LockDisplay (dpy); 387 GetReq (RRQueryVersion, req); 388 req->reqType = info->codes->major_opcode; 389 req->randrReqType = X_RRQueryVersion; 390 req->majorVersion = RANDR_MAJOR; 391 req->minorVersion = RANDR_MINOR; 392 if (!_XReply (dpy, (xReply *) &rep, 0, xTrue)) { 393 UnlockDisplay (dpy); 394 SyncHandle (); 395 return 0; 396 } 397 xrri->major_version = rep.majorVersion; 398 xrri->minor_version = rep.minorVersion; 399 xrri->has_rates = _XRRHasRates (xrri->major_version, xrri->minor_version); 400 UnlockDisplay (dpy); 401 SyncHandle (); 402 } 403 *major_versionp = xrri->major_version; 404 *minor_versionp = xrri->minor_version; 405 return 1; 406} 407 408_X_HIDDEN Bool 409_XRRVersionHandler (Display *dpy, 410 xReply *rep, 411 char *buf, 412 int len, 413 XPointer data) 414{ 415 xRRQueryVersionReply replbuf; 416 xRRQueryVersionReply *repl; 417 _XRRVersionState *state = (_XRRVersionState *) data; 418 419 if (dpy->last_request_read != state->version_seq) 420 return False; 421 if (rep->generic.type == X_Error) 422 { 423 state->error = True; 424 return False; 425 } 426 repl = (xRRQueryVersionReply *) 427 _XGetAsyncReply(dpy, (char *)&replbuf, rep, buf, len, 428 (SIZEOF(xRRQueryVersionReply) - SIZEOF(xReply)) >> 2, 429 True); 430 state->major_version = repl->majorVersion; 431 state->minor_version = repl->minorVersion; 432 return True; 433} 434 435/* 436 * in protocol version 0.1, routine added to allow selecting for new events. 437 */ 438 439void XRRSelectInput (Display *dpy, Window window, int mask) 440{ 441 XExtDisplayInfo *info = XRRFindDisplay (dpy); 442 xRRSelectInputReq *req; 443 444 RRSimpleCheckExtension (dpy, info); 445 446 LockDisplay (dpy); 447 GetReq (RRSelectInput, req); 448 req->reqType = info->codes->major_opcode; 449 req->randrReqType = X_RRSelectInput; 450 req->window = window; 451 req->enable = 0; 452 if (mask) req->enable = mask; 453 UnlockDisplay (dpy); 454 SyncHandle (); 455 return; 456} 457 458int XRRUpdateConfiguration(XEvent *event) 459{ 460 XRRScreenChangeNotifyEvent *scevent; 461 XConfigureEvent *rcevent; 462 Display *dpy = event->xany.display; 463 XExtDisplayInfo *info; 464 XRandRInfo *xrri; 465 int snum; 466 467 /* first, see if it is a vanilla configure notify event */ 468 if (event->type == ConfigureNotify) { 469 rcevent = (XConfigureEvent *) event; 470 snum = XRRRootToScreen(dpy, rcevent->window); 471 if (snum != -1) { 472 dpy->screens[snum].width = rcevent->width; 473 dpy->screens[snum].height = rcevent->height; 474 return 1; 475 } 476 } 477 478 info = XRRFindDisplay(dpy); 479 RRCheckExtension (dpy, info, 0); 480 481 switch (event->type - info->codes->first_event) { 482 case RRScreenChangeNotify: 483 scevent = (XRRScreenChangeNotifyEvent *) event; 484 snum = XRRRootToScreen(dpy, 485 ((XRRScreenChangeNotifyEvent *) event)->root); 486 if (scevent->rotation & (RR_Rotate_90 | RR_Rotate_270)) { 487 dpy->screens[snum].width = scevent->height; 488 dpy->screens[snum].height = scevent->width; 489 dpy->screens[snum].mwidth = scevent->mheight; 490 dpy->screens[snum].mheight = scevent->mwidth; 491 } else { 492 dpy->screens[snum].width = scevent->width; 493 dpy->screens[snum].height = scevent->height; 494 dpy->screens[snum].mwidth = scevent->mwidth; 495 dpy->screens[snum].mheight = scevent->mheight; 496 } 497 XRenderSetSubpixelOrder (dpy, snum, scevent->subpixel_order); 498 break; 499 default: 500 return 0; 501 } 502 xrri = (XRandRInfo *) info->data; 503 /* 504 * so the next time someone wants some data, it will be fetched; 505 * it might be better to force the round trip immediately, but 506 * I dislike pounding the server simultaneously when not necessary 507 */ 508 if (xrri->config[snum] != NULL) { 509 XFree (xrri->config[snum]); 510 xrri->config[snum] = NULL; 511 } 512 return 1; 513} 514