142542f5fSchristos/* 242542f5fSchristos * Copyright © 2013 Intel Corporation 342542f5fSchristos * 442542f5fSchristos * Permission is hereby granted, free of charge, to any person obtaining a 542542f5fSchristos * copy of this software and associated documentation files (the "Software"), 642542f5fSchristos * to deal in the Software without restriction, including without limitation 742542f5fSchristos * the rights to use, copy, modify, merge, publish, distribute, sublicense, 842542f5fSchristos * and/or sell copies of the Software, and to permit persons to whom the 942542f5fSchristos * Software is furnished to do so, subject to the following conditions: 1042542f5fSchristos * 1142542f5fSchristos * The above copyright notice and this permission notice (including the next 1242542f5fSchristos * paragraph) shall be included in all copies or substantial portions of the 1342542f5fSchristos * Software. 1442542f5fSchristos * 1542542f5fSchristos * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1642542f5fSchristos * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1742542f5fSchristos * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1842542f5fSchristos * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1942542f5fSchristos * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 2042542f5fSchristos * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 2142542f5fSchristos * IN THE SOFTWARE. 2242542f5fSchristos * 2342542f5fSchristos */ 2442542f5fSchristos 2542542f5fSchristos#ifdef HAVE_CONFIG_H 2642542f5fSchristos#include "config.h" 2742542f5fSchristos#endif 2842542f5fSchristos 2942542f5fSchristos#include <X11/Xlib.h> 3042542f5fSchristos#include <X11/Xatom.h> 3142542f5fSchristos 3242542f5fSchristos#include <X11/Xlibint.h> 3342542f5fSchristos#include <X11/extensions/record.h> 34fe8aea9eSmrg#include <X11/extensions/scrnsaver.h> 3542542f5fSchristos#include <X11/extensions/XShm.h> 3642542f5fSchristos#if HAVE_X11_EXTENSIONS_SHMPROTO_H 3742542f5fSchristos#include <X11/extensions/shmproto.h> 3842542f5fSchristos#elif HAVE_X11_EXTENSIONS_SHMSTR_H 3942542f5fSchristos#include <X11/extensions/shmstr.h> 4042542f5fSchristos#else 4142542f5fSchristos#error Failed to find the right header for X11 MIT-SHM protocol definitions 4242542f5fSchristos#endif 4342542f5fSchristos#include <X11/extensions/Xdamage.h> 4442542f5fSchristos#if HAVE_X11_EXTENSIONS_XINERAMA_H 4542542f5fSchristos#include <X11/extensions/Xinerama.h> 4642542f5fSchristos#define USE_XINERAMA 4742542f5fSchristos#endif 4842542f5fSchristos#include <X11/extensions/Xrandr.h> 4942542f5fSchristos#include <X11/extensions/Xrender.h> 5042542f5fSchristos#include <X11/Xcursor/Xcursor.h> 5142542f5fSchristos#include <pixman.h> 5242542f5fSchristos 5342542f5fSchristos#include <sys/types.h> 5442542f5fSchristos#include <sys/ipc.h> 5542542f5fSchristos#include <sys/shm.h> 5642542f5fSchristos#include <sys/timerfd.h> 5742542f5fSchristos#include <sys/poll.h> 5842542f5fSchristos#include <sys/socket.h> 5942542f5fSchristos#include <sys/un.h> 6042542f5fSchristos 6142542f5fSchristos#include <stdarg.h> 6242542f5fSchristos#include <stdio.h> 6342542f5fSchristos#include <stdlib.h> 6442542f5fSchristos#include <stdint.h> 6542542f5fSchristos#include <signal.h> 6642542f5fSchristos#include <getopt.h> 6742542f5fSchristos#include <limits.h> 6842542f5fSchristos#include <unistd.h> 6942542f5fSchristos#include <fcntl.h> 7042542f5fSchristos#include <assert.h> 7142542f5fSchristos 7242542f5fSchristos#define FORCE_FULL_REDRAW 0 7342542f5fSchristos#define FORCE_16BIT_XFER 0 7442542f5fSchristos 7542542f5fSchristos#define DBG(v, x) if (verbose & v) printf x 7642542f5fSchristosstatic int verbose; 7742542f5fSchristos#define X11 0x1 7842542f5fSchristos#define XRR 0x1 7942542f5fSchristos#define TIMER 0x4 8042542f5fSchristos#define DRAW 0x8 8142542f5fSchristos#define DAMAGE 0x10 8242542f5fSchristos#define CURSOR 0x20 83fe8aea9eSmrg#define SCREEN 0x40 84fe8aea9eSmrg#define POLL 0x80 8542542f5fSchristos 8642542f5fSchristosstruct display { 8742542f5fSchristos Display *dpy; 8842542f5fSchristos struct clone *clone; 8942542f5fSchristos struct context *ctx; 9042542f5fSchristos 91fe8aea9eSmrg int saver_event, saver_error, saver_active; 9242542f5fSchristos int damage_event, damage_error; 9342542f5fSchristos int xfixes_event, xfixes_error; 9442542f5fSchristos int rr_event, rr_error, rr_active; 9542542f5fSchristos int xinerama_event, xinerama_error, xinerama_active; 9642542f5fSchristos int dri3_active; 9742542f5fSchristos Window root; 9842542f5fSchristos Visual *visual; 9942542f5fSchristos Damage damage; 10042542f5fSchristos 10142542f5fSchristos int width; 10242542f5fSchristos int height; 10342542f5fSchristos int depth; 104fe8aea9eSmrg int active; 10542542f5fSchristos 10642542f5fSchristos XRenderPictFormat *root_format; 10742542f5fSchristos XRenderPictFormat *rgb16_format; 10842542f5fSchristos XRenderPictFormat *rgb24_format; 10942542f5fSchristos 11042542f5fSchristos int has_shm; 11142542f5fSchristos int has_shm_pixmap; 11242542f5fSchristos int shm_opcode; 11342542f5fSchristos int shm_event; 11442542f5fSchristos 11542542f5fSchristos Cursor invisible_cursor; 11642542f5fSchristos Cursor visible_cursor; 11742542f5fSchristos 118fe8aea9eSmrg XcursorImage cursor_image; /* first only */ 11942542f5fSchristos int cursor_serial; 12042542f5fSchristos int cursor_x; 12142542f5fSchristos int cursor_y; 12242542f5fSchristos int cursor_moved; 12342542f5fSchristos int cursor_visible; 12442542f5fSchristos int cursor; 12542542f5fSchristos 12642542f5fSchristos int flush; 12742542f5fSchristos int send; 12842542f5fSchristos int skip_clone; 12942542f5fSchristos int skip_frame; 130fe8aea9eSmrg 131fe8aea9eSmrg struct { 132fe8aea9eSmrg int timeout; 133fe8aea9eSmrg int interval; 134fe8aea9eSmrg int prefer_blank; 135fe8aea9eSmrg int allow_exp; 136fe8aea9eSmrg } saver; 13742542f5fSchristos}; 13842542f5fSchristos 13942542f5fSchristosstruct output { 14042542f5fSchristos struct display *display; 14142542f5fSchristos Display *dpy; 14242542f5fSchristos char *name; 14342542f5fSchristos RROutput rr_output; 14442542f5fSchristos RRCrtc rr_crtc; 14542542f5fSchristos Window window; 14642542f5fSchristos Picture win_picture; 14742542f5fSchristos Picture pix_picture; 14842542f5fSchristos Pixmap pixmap; 14942542f5fSchristos GC gc; 15042542f5fSchristos 15142542f5fSchristos long serial; 15242542f5fSchristos int use_shm; 15342542f5fSchristos int use_shm_pixmap; 15442542f5fSchristos XShmSegmentInfo shm; 15542542f5fSchristos 15642542f5fSchristos XRenderPictFormat *use_render; 15742542f5fSchristos 15842542f5fSchristos int x, y; 159fe8aea9eSmrg int width, height; 16042542f5fSchristos XRRModeInfo mode; 16142542f5fSchristos Rotation rotation; 16242542f5fSchristos}; 16342542f5fSchristos 16442542f5fSchristosstruct clone { 16542542f5fSchristos struct clone *next; 16642542f5fSchristos struct clone *active; 16742542f5fSchristos 16842542f5fSchristos struct output src, dst; 16942542f5fSchristos long timestamp; 17042542f5fSchristos 17142542f5fSchristos XShmSegmentInfo shm; 17242542f5fSchristos XImage image; 17342542f5fSchristos 17442542f5fSchristos int width, height, depth; 17542542f5fSchristos struct { int x1, x2, y1, y2; } damaged; 17642542f5fSchristos int rr_update; 17742542f5fSchristos 17842542f5fSchristos struct dri3_fence { 17942542f5fSchristos XID xid; 18042542f5fSchristos void *addr; 18142542f5fSchristos } dri3; 18242542f5fSchristos}; 18342542f5fSchristos 18442542f5fSchristosstruct context { 18542542f5fSchristos struct display *display; 18642542f5fSchristos struct clone *clones; 18742542f5fSchristos struct clone *active; 18842542f5fSchristos struct pollfd *pfd; 18942542f5fSchristos#define timer pfd[0].fd 19042542f5fSchristos Display *record; 19142542f5fSchristos int nclone; 19242542f5fSchristos int ndisplay; 19342542f5fSchristos int nfd; 19442542f5fSchristos 19542542f5fSchristos int timer_active; 19642542f5fSchristos 19742542f5fSchristos long timestamp; 19842542f5fSchristos long configTimestamp; 19942542f5fSchristos 20042542f5fSchristos Atom singleton; 20142542f5fSchristos char command[1024]; 20242542f5fSchristos int command_continuation; 20342542f5fSchristos}; 20442542f5fSchristos 20542542f5fSchristosstatic inline int is_power_of_2(unsigned long n) 20642542f5fSchristos{ 20742542f5fSchristos return n && ((n & (n - 1)) == 0); 20842542f5fSchristos} 20942542f5fSchristos 21042542f5fSchristosstatic int xlib_vendor_is_xorg(Display *dpy) 21142542f5fSchristos{ 21242542f5fSchristos const char *const vendor = ServerVendor(dpy); 21342542f5fSchristos return strstr(vendor, "X.Org") || strstr(vendor, "Xorg"); 21442542f5fSchristos} 21542542f5fSchristos 21642542f5fSchristosstatic inline XRRScreenResources *_XRRGetScreenResourcesCurrent(Display *dpy, Window window) 21742542f5fSchristos{ 21842542f5fSchristos XRRScreenResources *res; 21942542f5fSchristos 22042542f5fSchristos res = XRRGetScreenResourcesCurrent(dpy, window); 22142542f5fSchristos if (res == NULL) 22242542f5fSchristos res = XRRGetScreenResources(dpy, window); 22342542f5fSchristos 22442542f5fSchristos return res; 22542542f5fSchristos} 22642542f5fSchristos 22742542f5fSchristos#define XORG_VERSION_ENCODE(major,minor,patch,snap) \ 22842542f5fSchristos (((major) * 10000000) + ((minor) * 100000) + ((patch) * 1000) + snap) 22942542f5fSchristos 23042542f5fSchristosstatic int _x_error_occurred; 23142542f5fSchristos 232fe8aea9eSmrgstatic int 233fe8aea9eSmrg_io_error_handler(Display *display) 234fe8aea9eSmrg{ 235fe8aea9eSmrg fprintf(stderr, "XIO error on display %s\n", DisplayString(display)); 236fe8aea9eSmrg abort(); 237fe8aea9eSmrg} 238fe8aea9eSmrg 23942542f5fSchristosstatic int 24042542f5fSchristos_check_error_handler(Display *display, 24142542f5fSchristos XErrorEvent *event) 24242542f5fSchristos{ 24342542f5fSchristos DBG(X11, ("X11 error from display %s, serial=%ld, error=%d, req=%d.%d\n", 24442542f5fSchristos DisplayString(display), 24542542f5fSchristos event->serial, 24642542f5fSchristos event->error_code, 24742542f5fSchristos event->request_code, 24842542f5fSchristos event->minor_code)); 24942542f5fSchristos _x_error_occurred = 1; 25042542f5fSchristos return False; /* ignored */ 25142542f5fSchristos} 25242542f5fSchristos 25342542f5fSchristosstatic int 25442542f5fSchristoscan_use_shm(Display *dpy, 25542542f5fSchristos Window window, 25642542f5fSchristos int *shm_event, 25742542f5fSchristos int *shm_opcode, 25842542f5fSchristos int *shm_pixmap) 25942542f5fSchristos{ 26042542f5fSchristos XShmSegmentInfo shm; 26142542f5fSchristos Status success; 26242542f5fSchristos XExtCodes *codes; 26342542f5fSchristos int major, minor, has_shm, has_pixmap; 26442542f5fSchristos 265fe8aea9eSmrg *shm_event = 0; 266fe8aea9eSmrg *shm_opcode = 0; 267fe8aea9eSmrg *shm_pixmap = 0; 268fe8aea9eSmrg 26942542f5fSchristos if (!XShmQueryExtension(dpy)) 27042542f5fSchristos return 0; 27142542f5fSchristos 27242542f5fSchristos XShmQueryVersion(dpy, &major, &minor, &has_pixmap); 27342542f5fSchristos 27442542f5fSchristos shm.shmid = shmget(IPC_PRIVATE, 0x1000, IPC_CREAT | 0600); 27542542f5fSchristos if (shm.shmid == -1) 27642542f5fSchristos return 0; 27742542f5fSchristos 27842542f5fSchristos shm.readOnly = 0; 27942542f5fSchristos shm.shmaddr = shmat(shm.shmid, NULL, 0); 28042542f5fSchristos if (shm.shmaddr == (char *) -1) { 28142542f5fSchristos shmctl(shm.shmid, IPC_RMID, NULL); 28242542f5fSchristos return 0; 28342542f5fSchristos } 28442542f5fSchristos 28542542f5fSchristos XSync(dpy, False); 28642542f5fSchristos _x_error_occurred = 0; 28742542f5fSchristos 28842542f5fSchristos success = XShmAttach(dpy, &shm); 28942542f5fSchristos 29042542f5fSchristos XSync(dpy, False); 29142542f5fSchristos has_shm = success && _x_error_occurred == 0; 29242542f5fSchristos 29342542f5fSchristos /* As libXext sets the SEND_EVENT bit in the ShmCompletionEvent, 29442542f5fSchristos * the Xserver may crash if it does not take care when processing 29542542f5fSchristos * the event type. For instance versions of Xorg prior to 1.11.1 29642542f5fSchristos * exhibited this bug, and was fixed by: 29742542f5fSchristos * 29842542f5fSchristos * commit 2d2dce558d24eeea0eb011ec9ebaa6c5c2273c39 29942542f5fSchristos * Author: Sam Spilsbury <sam.spilsbury@canonical.com> 30042542f5fSchristos * Date: Wed Sep 14 09:58:34 2011 +0800 30142542f5fSchristos * 30242542f5fSchristos * Remove the SendEvent bit (0x80) before doing range checks on event type. 30342542f5fSchristos */ 30442542f5fSchristos codes = 0; 30542542f5fSchristos if (has_shm) 30642542f5fSchristos codes = XInitExtension(dpy, SHMNAME); 30742542f5fSchristos if (xlib_vendor_is_xorg(dpy) && 30842542f5fSchristos VendorRelease(dpy) < XORG_VERSION_ENCODE(1,11,0,1)) 30942542f5fSchristos codes = 0; 31042542f5fSchristos if (codes) { 31142542f5fSchristos XShmCompletionEvent e; 31242542f5fSchristos 31342542f5fSchristos memset(&e, 0, sizeof(e)); 31442542f5fSchristos 31542542f5fSchristos e.type = codes->first_event; 31642542f5fSchristos e.send_event = 1; 31742542f5fSchristos e.serial = 1; 31842542f5fSchristos e.drawable = window; 31942542f5fSchristos e.major_code = codes->major_opcode; 32042542f5fSchristos e.minor_code = X_ShmPutImage; 32142542f5fSchristos 32242542f5fSchristos e.shmseg = shm.shmid; 32342542f5fSchristos e.offset = 0; 32442542f5fSchristos 32542542f5fSchristos XSendEvent(dpy, e.drawable, False, 0, (XEvent *)&e); 32642542f5fSchristos XSync(dpy, False); 32742542f5fSchristos 32842542f5fSchristos if (_x_error_occurred == 0) { 32942542f5fSchristos *shm_opcode = codes->major_opcode; 33042542f5fSchristos *shm_event = codes->first_event; 33142542f5fSchristos *shm_pixmap = has_pixmap; 33242542f5fSchristos } 33342542f5fSchristos } 33442542f5fSchristos 33542542f5fSchristos XShmDetach(dpy, &shm); 33642542f5fSchristos shmctl(shm.shmid, IPC_RMID, NULL); 33742542f5fSchristos shmdt(shm.shmaddr); 33842542f5fSchristos 33942542f5fSchristos return has_shm; 34042542f5fSchristos} 34142542f5fSchristos 34242542f5fSchristos#ifdef DRI3 34342542f5fSchristos#include <X11/Xlib-xcb.h> 34442542f5fSchristos#include <X11/xshmfence.h> 34542542f5fSchristos#include <xcb/xcb.h> 346fe8aea9eSmrg#include <xcb/xcbext.h> 34742542f5fSchristos#include <xcb/dri3.h> 34842542f5fSchristos#include <xcb/sync.h> 34942542f5fSchristosstatic Pixmap dri3_create_pixmap(Display *dpy, 35042542f5fSchristos Drawable draw, 35142542f5fSchristos int width, int height, int depth, 35242542f5fSchristos int fd, int bpp, int stride, int size) 35342542f5fSchristos{ 35442542f5fSchristos xcb_connection_t *c = XGetXCBConnection(dpy); 35542542f5fSchristos xcb_pixmap_t pixmap = xcb_generate_id(c); 35642542f5fSchristos xcb_dri3_pixmap_from_buffer(c, pixmap, draw, size, width, height, stride, depth, bpp, fd); 35742542f5fSchristos return pixmap; 35842542f5fSchristos} 35942542f5fSchristos 36042542f5fSchristosstatic int dri3_create_fd(Display *dpy, 36142542f5fSchristos Pixmap pixmap, 36242542f5fSchristos int *stride) 36342542f5fSchristos{ 36442542f5fSchristos xcb_connection_t *c = XGetXCBConnection(dpy); 36542542f5fSchristos xcb_dri3_buffer_from_pixmap_cookie_t cookie; 36642542f5fSchristos xcb_dri3_buffer_from_pixmap_reply_t *reply; 36742542f5fSchristos 36842542f5fSchristos cookie = xcb_dri3_buffer_from_pixmap(c, pixmap); 36942542f5fSchristos reply = xcb_dri3_buffer_from_pixmap_reply(c, cookie, NULL); 37042542f5fSchristos if (!reply) 37142542f5fSchristos return -1; 37242542f5fSchristos 37342542f5fSchristos if (reply->nfd != 1) 37442542f5fSchristos return -1; 37542542f5fSchristos 37642542f5fSchristos *stride = reply->stride; 37742542f5fSchristos return xcb_dri3_buffer_from_pixmap_reply_fds(c, reply)[0]; 37842542f5fSchristos} 37942542f5fSchristos 38042542f5fSchristosstatic int dri3_query_version(Display *dpy, int *major, int *minor) 38142542f5fSchristos{ 38242542f5fSchristos xcb_connection_t *c = XGetXCBConnection(dpy); 38342542f5fSchristos xcb_dri3_query_version_reply_t *reply; 384fe8aea9eSmrg xcb_generic_error_t *error; 38542542f5fSchristos 38642542f5fSchristos *major = *minor = -1; 38742542f5fSchristos 38842542f5fSchristos reply = xcb_dri3_query_version_reply(c, 38942542f5fSchristos xcb_dri3_query_version(c, 39042542f5fSchristos XCB_DRI3_MAJOR_VERSION, 39142542f5fSchristos XCB_DRI3_MINOR_VERSION), 392fe8aea9eSmrg &error); 393fe8aea9eSmrg free(error); 39442542f5fSchristos if (reply == NULL) 39542542f5fSchristos return -1; 39642542f5fSchristos 39742542f5fSchristos *major = reply->major_version; 39842542f5fSchristos *minor = reply->minor_version; 39942542f5fSchristos free(reply); 40042542f5fSchristos 40142542f5fSchristos return 0; 40242542f5fSchristos} 40342542f5fSchristos 40442542f5fSchristosstatic int dri3_exists(Display *dpy) 40542542f5fSchristos{ 406fe8aea9eSmrg const xcb_query_extension_reply_t *ext; 40742542f5fSchristos int major, minor; 40842542f5fSchristos 409fe8aea9eSmrg ext = xcb_get_extension_data(XGetXCBConnection(dpy), &xcb_dri3_id); 410fe8aea9eSmrg if (ext == NULL || !ext->present) 411fe8aea9eSmrg return 0; 412fe8aea9eSmrg 41342542f5fSchristos if (dri3_query_version(dpy, &major, &minor) < 0) 41442542f5fSchristos return 0; 41542542f5fSchristos 41642542f5fSchristos return major >= 0; 41742542f5fSchristos} 41842542f5fSchristos 41942542f5fSchristosstatic void dri3_create_fence(Display *dpy, Drawable d, struct dri3_fence *fence) 42042542f5fSchristos{ 42142542f5fSchristos xcb_connection_t *c = XGetXCBConnection(dpy); 42242542f5fSchristos struct dri3_fence f; 42342542f5fSchristos int fd; 42442542f5fSchristos 42542542f5fSchristos fd = xshmfence_alloc_shm(); 42642542f5fSchristos if (fd < 0) 42742542f5fSchristos return; 42842542f5fSchristos 42942542f5fSchristos f.addr = xshmfence_map_shm(fd); 43042542f5fSchristos if (f.addr == NULL) { 43142542f5fSchristos close(fd); 43242542f5fSchristos return; 43342542f5fSchristos } 43442542f5fSchristos 43542542f5fSchristos f.xid = xcb_generate_id(c); 43642542f5fSchristos xcb_dri3_fence_from_fd(c, d, f.xid, 0, fd); 43742542f5fSchristos 43842542f5fSchristos *fence = f; 43942542f5fSchristos} 44042542f5fSchristos 44142542f5fSchristosstatic void dri3_fence_flush(Display *dpy, struct dri3_fence *fence) 44242542f5fSchristos{ 44342542f5fSchristos xcb_sync_trigger_fence(XGetXCBConnection(dpy), fence->xid); 44442542f5fSchristos} 44542542f5fSchristos 44642542f5fSchristosstatic void dri3_fence_free(Display *dpy, struct dri3_fence *fence) 44742542f5fSchristos{ 44842542f5fSchristos xshmfence_unmap_shm(fence->addr); 44942542f5fSchristos xcb_sync_destroy_fence(XGetXCBConnection(dpy), fence->xid); 45042542f5fSchristos} 45142542f5fSchristos 45242542f5fSchristos#else 45342542f5fSchristos 45442542f5fSchristosstatic int dri3_exists(Display *dpy) 45542542f5fSchristos{ 45642542f5fSchristos return 0; 45742542f5fSchristos} 45842542f5fSchristos 45942542f5fSchristosstatic void dri3_create_fence(Display *dpy, Drawable d, struct dri3_fence *fence) 46042542f5fSchristos{ 46142542f5fSchristos} 46242542f5fSchristos 46342542f5fSchristosstatic void dri3_fence_flush(Display *dpy, struct dri3_fence *fence) 46442542f5fSchristos{ 46542542f5fSchristos} 46642542f5fSchristos 46742542f5fSchristosstatic void dri3_fence_free(Display *dpy, struct dri3_fence *fence) 46842542f5fSchristos{ 46942542f5fSchristos} 47042542f5fSchristos 47142542f5fSchristosstatic Pixmap dri3_create_pixmap(Display *dpy, 47242542f5fSchristos Drawable draw, 47342542f5fSchristos int width, int height, int depth, 47442542f5fSchristos int fd, int bpp, int stride, int size) 47542542f5fSchristos{ 47642542f5fSchristos return None; 47742542f5fSchristos} 47842542f5fSchristos 47942542f5fSchristosstatic int dri3_create_fd(Display *dpy, 48042542f5fSchristos Pixmap pixmap, 48142542f5fSchristos int *stride) 48242542f5fSchristos{ 48342542f5fSchristos return -1; 48442542f5fSchristos} 48542542f5fSchristos#endif 48642542f5fSchristos 48742542f5fSchristosstatic int timerfd(int hz) 48842542f5fSchristos{ 48942542f5fSchristos struct itimerspec it; 49042542f5fSchristos int fd; 49142542f5fSchristos 49242542f5fSchristos fd = -1; 49342542f5fSchristos#ifdef CLOCK_MONOTONIC_COARSE 49442542f5fSchristos fd = timerfd_create(CLOCK_MONOTONIC_COARSE, TFD_NONBLOCK); 49542542f5fSchristos#endif 49642542f5fSchristos if (fd < 0) 49742542f5fSchristos fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); 49842542f5fSchristos if (fd < 0) 49942542f5fSchristos return -ETIME; 50042542f5fSchristos 50142542f5fSchristos it.it_interval.tv_sec = 0; 50242542f5fSchristos it.it_interval.tv_nsec = 1000000000 / hz; 50342542f5fSchristos it.it_value = it.it_interval; 50442542f5fSchristos if (timerfd_settime(fd, 0, &it, NULL) < 0) { 50542542f5fSchristos close(fd); 50642542f5fSchristos return -ETIME; 50742542f5fSchristos } 50842542f5fSchristos 50942542f5fSchristos return fd; 51042542f5fSchristos} 51142542f5fSchristos 51242542f5fSchristosstatic int context_init(struct context *ctx) 51342542f5fSchristos{ 51442542f5fSchristos struct pollfd *pfd; 51542542f5fSchristos 51642542f5fSchristos memset(ctx, 0, sizeof(*ctx)); 51742542f5fSchristos 51842542f5fSchristos ctx->pfd = malloc(2*sizeof(struct pollfd)); 51942542f5fSchristos if (ctx->pfd == NULL) 52042542f5fSchristos return -ENOMEM; 52142542f5fSchristos 52242542f5fSchristos ctx->clones = malloc(sizeof(struct clone)); 52342542f5fSchristos if (ctx->clones == NULL) 52442542f5fSchristos return -ENOMEM; 52542542f5fSchristos 52642542f5fSchristos ctx->display = malloc(sizeof(struct display)); 52742542f5fSchristos if (ctx->display == NULL) 52842542f5fSchristos return -ENOMEM; 52942542f5fSchristos 53042542f5fSchristos pfd = memset(&ctx->pfd[ctx->nfd++], 0, sizeof(struct pollfd)); 53142542f5fSchristos pfd->fd = timerfd(60); 53242542f5fSchristos if (pfd->fd < 0) 53342542f5fSchristos return pfd->fd; 53442542f5fSchristos pfd->events = POLLIN; 53542542f5fSchristos 53642542f5fSchristos return 0; 53742542f5fSchristos} 53842542f5fSchristos 53942542f5fSchristosstatic void context_enable_timer(struct context *ctx) 54042542f5fSchristos{ 54142542f5fSchristos uint64_t count; 54242542f5fSchristos 54342542f5fSchristos DBG(TIMER, ("%s timer active? %d\n", __func__, ctx->timer_active)); 54442542f5fSchristos 54542542f5fSchristos if (ctx->timer_active) 54642542f5fSchristos return; 54742542f5fSchristos 54842542f5fSchristos /* reset timer */ 54942542f5fSchristos count = read(ctx->timer, &count, sizeof(count)); 55042542f5fSchristos 55142542f5fSchristos ctx->timer_active = 1; 55242542f5fSchristos} 55342542f5fSchristos 55442542f5fSchristosstatic int add_fd(struct context *ctx, int fd) 55542542f5fSchristos{ 55642542f5fSchristos struct pollfd *pfd; 55742542f5fSchristos 55842542f5fSchristos if (fd < 0) 55942542f5fSchristos return fd; 56042542f5fSchristos 56142542f5fSchristos if (is_power_of_2(ctx->nfd)) { 56242542f5fSchristos ctx->pfd = realloc(ctx->pfd, 2*ctx->nfd*sizeof(struct pollfd)); 56342542f5fSchristos if (ctx->pfd == NULL) 56442542f5fSchristos return -ENOMEM; 56542542f5fSchristos } 56642542f5fSchristos 56742542f5fSchristos pfd = memset(&ctx->pfd[ctx->nfd++], 0, sizeof(struct pollfd)); 56842542f5fSchristos pfd->fd = fd; 56942542f5fSchristos pfd->events = POLLIN; 57042542f5fSchristos return 0; 57142542f5fSchristos} 57242542f5fSchristos 57342542f5fSchristosstatic void display_mark_flush(struct display *display) 57442542f5fSchristos{ 57542542f5fSchristos DBG(DRAW, ("%s mark flush (flush=%d)\n", 57642542f5fSchristos DisplayString(display->dpy), display->flush)); 57742542f5fSchristos 57842542f5fSchristos if (display->flush) 57942542f5fSchristos return; 58042542f5fSchristos 58142542f5fSchristos context_enable_timer(display->ctx); 58242542f5fSchristos display->flush = 1; 58342542f5fSchristos} 58442542f5fSchristos 58542542f5fSchristosstatic int mode_equal(const XRRModeInfo *a, const XRRModeInfo *b) 58642542f5fSchristos{ 58742542f5fSchristos return (a->width == b->width && 58842542f5fSchristos a->height == b->height && 58942542f5fSchristos a->dotClock == b->dotClock && 59042542f5fSchristos a->hSyncStart == b->hSyncStart && 59142542f5fSchristos a->hSyncEnd == b->hSyncEnd && 59242542f5fSchristos a->hTotal == b->hTotal && 59342542f5fSchristos a->hSkew == b->hSkew && 59442542f5fSchristos a->vSyncStart == b->vSyncStart && 59542542f5fSchristos a->vSyncEnd == b->vSyncEnd && 59642542f5fSchristos a->vTotal == b->vTotal && 59742542f5fSchristos a->modeFlags == b->modeFlags); 59842542f5fSchristos} 59942542f5fSchristos 60042542f5fSchristosstatic XRRModeInfo *lookup_mode(XRRScreenResources *res, int id) 60142542f5fSchristos{ 60242542f5fSchristos int i; 60342542f5fSchristos 60442542f5fSchristos for (i = 0; i < res->nmode; i++) { 60542542f5fSchristos if (res->modes[i].id == id) 60642542f5fSchristos return &res->modes[i]; 60742542f5fSchristos } 60842542f5fSchristos 60942542f5fSchristos return NULL; 61042542f5fSchristos} 61142542f5fSchristos 61242542f5fSchristosstatic void clone_update_edid(struct clone *clone) 61342542f5fSchristos{ 61442542f5fSchristos unsigned long nitems, after; 61542542f5fSchristos unsigned char *data; 61642542f5fSchristos int format; 61742542f5fSchristos Atom type; 61842542f5fSchristos 61942542f5fSchristos if (XRRGetOutputProperty(clone->dst.dpy, clone->dst.rr_output, 62042542f5fSchristos XInternAtom(clone->dst.dpy, "EDID", False), 62142542f5fSchristos 0, 100, False, False, AnyPropertyType, 62242542f5fSchristos &type, &format, &nitems, &after, &data) == Success) { 62342542f5fSchristos XRRChangeOutputProperty(clone->src.dpy, clone->src.rr_output, 62442542f5fSchristos XInternAtom(clone->src.dpy, "EDID", False), 62542542f5fSchristos type, format, PropModeReplace, data, nitems); 62642542f5fSchristos } 62742542f5fSchristos} 62842542f5fSchristos 62942542f5fSchristosstatic int disable_crtc(Display *dpy, XRRScreenResources *res, RRCrtc crtc) 63042542f5fSchristos{ 63142542f5fSchristos XRRPanning panning; 63242542f5fSchristos 63342542f5fSchristos if (crtc) { 63442542f5fSchristos XRRSetPanning(dpy, res, crtc, memset(&panning, 0, sizeof(panning))); 63542542f5fSchristos 63642542f5fSchristos if (XRRSetCrtcConfig(dpy, res, crtc, CurrentTime, 0, 0, None, RR_Rotate_0, NULL, 0) != Success) 63742542f5fSchristos return 0; 63842542f5fSchristos 63942542f5fSchristos if (XRRSetPanning(dpy, res, crtc, memset(&panning, 0, sizeof(panning))) != Success) { 64042542f5fSchristos DBG(XRR, ("%s failed to clear panning on CRTC:%ld\n", DisplayString(dpy), (long)crtc)); 64142542f5fSchristos if (verbose) { 64242542f5fSchristos XRRCrtcInfo *c; 64342542f5fSchristos XRRPanning *p; 64442542f5fSchristos 64542542f5fSchristos c = XRRGetCrtcInfo(dpy, res, crtc); 64642542f5fSchristos if (c) { 64742542f5fSchristos DBG(XRR, ("%s CRTC:%ld x=%d, y=%d, rotation=%d, mode=%ld\n", 64842542f5fSchristos DisplayString(dpy), (long)crtc, 64942542f5fSchristos c->x, c->y, c->rotation, c->mode)); 65042542f5fSchristos XRRFreeCrtcInfo(c); 65142542f5fSchristos } 65242542f5fSchristos 65342542f5fSchristos p = XRRGetPanning(dpy, res, crtc); 65442542f5fSchristos if (p) { 65542542f5fSchristos DBG(XRR, ("%s CRTC:%ld panning (%d, %d)x(%d, %d), tracking (%d, %d)x(%d, %d), border (%d, %d),(%d, %d)\n", 65642542f5fSchristos DisplayString(dpy), (long)crtc, 65742542f5fSchristos p->left, p->top, p->width, p->height, 65842542f5fSchristos p->track_left, p->track_top, p->track_width, p->track_height, 65942542f5fSchristos p->border_left, p->border_top, p->border_right, p->border_bottom)); 66042542f5fSchristos XRRFreePanning(p); 66142542f5fSchristos } 66242542f5fSchristos } 66342542f5fSchristos } 66442542f5fSchristos } 66542542f5fSchristos 66642542f5fSchristos return 1; 66742542f5fSchristos} 66842542f5fSchristos 66942542f5fSchristosstatic int clone_update_modes__randr(struct clone *clone) 67042542f5fSchristos{ 67142542f5fSchristos XRRScreenResources *from_res = NULL, *to_res = NULL; 67242542f5fSchristos XRROutputInfo *from_info = NULL, *to_info = NULL; 67342542f5fSchristos int i, j, ret = ENOENT; 67442542f5fSchristos 67542542f5fSchristos assert(clone->src.rr_output); 67642542f5fSchristos assert(clone->dst.rr_output); 67742542f5fSchristos assert(clone->dst.display->rr_event); 67842542f5fSchristos 67942542f5fSchristos from_res = _XRRGetScreenResourcesCurrent(clone->dst.dpy, clone->dst.window); 68042542f5fSchristos if (from_res == NULL) 68142542f5fSchristos goto err; 68242542f5fSchristos 68342542f5fSchristos from_info = XRRGetOutputInfo(clone->dst.dpy, from_res, clone->dst.rr_output); 68442542f5fSchristos if (from_info == NULL) 68542542f5fSchristos goto err; 68642542f5fSchristos 68742542f5fSchristos DBG(XRR, ("%s(%s-%s <- %s-%s): timestamp %ld (last %ld)\n", __func__, 68842542f5fSchristos DisplayString(clone->src.dpy), clone->src.name, 68942542f5fSchristos DisplayString(clone->dst.dpy), clone->dst.name, 69042542f5fSchristos from_info->timestamp, clone->timestamp)); 69142542f5fSchristos 69242542f5fSchristos to_res = _XRRGetScreenResourcesCurrent(clone->src.dpy, clone->src.window); 69342542f5fSchristos if (to_res == NULL) 69442542f5fSchristos goto err; 69542542f5fSchristos 69642542f5fSchristos to_info = XRRGetOutputInfo(clone->src.dpy, to_res, clone->src.rr_output); 69742542f5fSchristos if (to_info == NULL) 69842542f5fSchristos goto err; 69942542f5fSchristos 70042542f5fSchristos DBG(XRR, ("%s: dst.rr_crtc=%ld, now %ld\n", 70142542f5fSchristos __func__, (long)clone->dst.rr_crtc, (long)from_info->crtc)); 70242542f5fSchristos if (clone->dst.rr_crtc == from_info->crtc) { 70342542f5fSchristos for (i = 0; i < to_info->nmode; i++) { 70442542f5fSchristos XRRModeInfo *mode, *old; 70542542f5fSchristos 70642542f5fSchristos mode = lookup_mode(to_res, to_info->modes[i]); 70742542f5fSchristos if (mode == NULL) 70842542f5fSchristos break; 70942542f5fSchristos 71042542f5fSchristos DBG(XRR, ("%s(%s-%s): lookup mode %s\n", __func__, 71142542f5fSchristos DisplayString(clone->src.dpy), clone->src.name, 71242542f5fSchristos mode->name)); 71342542f5fSchristos 71442542f5fSchristos for (j = 0; j < from_info->nmode; j++) { 71542542f5fSchristos old = lookup_mode(from_res, from_info->modes[j]); 71642542f5fSchristos if (old && mode_equal(mode, old)) { 71742542f5fSchristos mode = NULL; 71842542f5fSchristos break; 71942542f5fSchristos } 72042542f5fSchristos } 72142542f5fSchristos if (mode) { 72242542f5fSchristos DBG(XRR, ("%s(%s-%s): unknown mode %s\n", __func__, 72342542f5fSchristos DisplayString(clone->src.dpy), clone->src.name, 72442542f5fSchristos mode->name)); 72542542f5fSchristos break; 72642542f5fSchristos } 72742542f5fSchristos } 72842542f5fSchristos if (i == from_info->nmode && i == to_info->nmode) { 72942542f5fSchristos DBG(XRR, ("%s(%s-%s): no change in output\n", __func__, 73042542f5fSchristos DisplayString(clone->src.dpy), clone->src.name)); 73142542f5fSchristos goto done; 73242542f5fSchristos } 73342542f5fSchristos } 73442542f5fSchristos 73542542f5fSchristos /* Disable the remote output */ 73642542f5fSchristos if (from_info->crtc != clone->dst.rr_crtc) { 73742542f5fSchristos DBG(XRR, ("%s(%s-%s): disabling active CRTC\n", __func__, 73842542f5fSchristos DisplayString(clone->dst.dpy), clone->dst.name)); 73942542f5fSchristos if (disable_crtc(clone->dst.dpy, from_res, from_info->crtc)) { 74042542f5fSchristos clone->dst.rr_crtc = 0; 74142542f5fSchristos clone->dst.mode.id = 0; 74242542f5fSchristos } else { 74342542f5fSchristos XRRCrtcInfo *c = XRRGetCrtcInfo(clone->dst.dpy, from_res, from_info->crtc); 74442542f5fSchristos if (c) { 74542542f5fSchristos clone->dst.x = c->x; 74642542f5fSchristos clone->dst.y = c->y; 74742542f5fSchristos clone->dst.rotation = c->rotation; 74842542f5fSchristos clone->dst.mode.id = c->mode; 74942542f5fSchristos XRRFreeCrtcInfo(c); 75042542f5fSchristos } 75142542f5fSchristos } 75242542f5fSchristos } 75342542f5fSchristos 75442542f5fSchristos /* Create matching modes for the real output on the virtual */ 75542542f5fSchristos XGrabServer(clone->src.dpy); 75642542f5fSchristos 75742542f5fSchristos /* Clear all current UserModes on the output, including any active ones */ 75842542f5fSchristos if (to_info->crtc) { 75942542f5fSchristos DBG(XRR, ("%s(%s-%s): disabling active CRTC\n", __func__, 76042542f5fSchristos DisplayString(clone->src.dpy), clone->src.name)); 76142542f5fSchristos disable_crtc(clone->src.dpy, to_res, to_info->crtc); 76242542f5fSchristos } 76342542f5fSchristos for (i = 0; i < to_info->nmode; i++) { 76442542f5fSchristos DBG(XRR, ("%s(%s-%s): deleting mode %ld\n", __func__, 76542542f5fSchristos DisplayString(clone->src.dpy), clone->src.name, (long)to_info->modes[i])); 76642542f5fSchristos XRRDeleteOutputMode(clone->src.dpy, clone->src.rr_output, to_info->modes[i]); 76742542f5fSchristos } 76842542f5fSchristos 76942542f5fSchristos clone->src.rr_crtc = 0; 77042542f5fSchristos 77142542f5fSchristos for (i = 0; i < from_info->nmode; i++) { 77242542f5fSchristos XRRModeInfo *mode, *old; 77342542f5fSchristos RRMode id; 77442542f5fSchristos 77542542f5fSchristos mode = lookup_mode(from_res, from_info->modes[i]); 77642542f5fSchristos if (mode == NULL) 77742542f5fSchristos continue; 77842542f5fSchristos for (j = 0; j < i; j++) { 77942542f5fSchristos old = lookup_mode(from_res, from_info->modes[j]); 78042542f5fSchristos if (old && mode_equal(mode, old)) { 78142542f5fSchristos mode = NULL; 78242542f5fSchristos break; 78342542f5fSchristos } 78442542f5fSchristos } 78542542f5fSchristos if (mode == NULL) 78642542f5fSchristos continue; 78742542f5fSchristos 78842542f5fSchristos id = 0; 78942542f5fSchristos for (j = 0; j < to_res->nmode; j++) { 79042542f5fSchristos old = &to_res->modes[j]; 79142542f5fSchristos if (mode_equal(mode, old)) { 79242542f5fSchristos id = old->id; 79342542f5fSchristos DBG(XRR, ("%s(%s-%s): reusing mode %ld: %s\n", __func__, 79442542f5fSchristos DisplayString(clone->src.dpy), clone->src.name, id, mode->name)); 79542542f5fSchristos break; 79642542f5fSchristos } 79742542f5fSchristos } 79842542f5fSchristos if (id == 0) { 79942542f5fSchristos XRRModeInfo m; 80042542f5fSchristos char buf[256]; 80142542f5fSchristos 80242542f5fSchristos /* XXX User names must be unique! */ 80342542f5fSchristos m = *mode; 80442542f5fSchristos m.nameLength = snprintf(buf, sizeof(buf), 80542542f5fSchristos "%s.%ld-%s", clone->src.name, (long)from_info->modes[i], mode->name); 80642542f5fSchristos m.name = buf; 80742542f5fSchristos 80842542f5fSchristos id = XRRCreateMode(clone->src.dpy, clone->src.window, &m); 80942542f5fSchristos DBG(XRR, ("%s(%s-%s): adding mode %ld: %s\n", __func__, 81042542f5fSchristos DisplayString(clone->src.dpy), clone->src.name, id, mode->name)); 81142542f5fSchristos } 81242542f5fSchristos 81342542f5fSchristos XRRAddOutputMode(clone->src.dpy, clone->src.rr_output, id); 81442542f5fSchristos } 81542542f5fSchristos clone_update_edid(clone); 81642542f5fSchristos XUngrabServer(clone->src.dpy); 81742542f5fSchristosdone: 81842542f5fSchristos ret = 0; 81942542f5fSchristos clone->timestamp = from_info->timestamp; 82042542f5fSchristos 82142542f5fSchristoserr: 82242542f5fSchristos if (to_info) 82342542f5fSchristos XRRFreeOutputInfo(to_info); 82442542f5fSchristos if (to_res) 82542542f5fSchristos XRRFreeScreenResources(to_res); 82642542f5fSchristos if (from_info) 82742542f5fSchristos XRRFreeOutputInfo(from_info); 82842542f5fSchristos if (from_res) 82942542f5fSchristos XRRFreeScreenResources(from_res); 83042542f5fSchristos 83142542f5fSchristos return ret; 83242542f5fSchristos} 83342542f5fSchristos 83442542f5fSchristosstatic int clone_update_modes__fixed(struct clone *clone) 83542542f5fSchristos{ 83642542f5fSchristos char mode_name[80]; 83742542f5fSchristos XRRScreenResources *res = NULL; 83842542f5fSchristos XRROutputInfo *info = NULL; 83942542f5fSchristos XRRModeInfo mode; 84042542f5fSchristos RRMode id; 84142542f5fSchristos int i, j, ret = ENOENT; 84242542f5fSchristos 843fe8aea9eSmrg DBG(X11, ("%s-%s cloning modes fixed %dx%d\n", 844fe8aea9eSmrg DisplayString(clone->dst.dpy), clone->dst.name, 845fe8aea9eSmrg clone->dst.width, clone->dst.height)); 846fe8aea9eSmrg 84742542f5fSchristos assert(clone->src.rr_output); 84842542f5fSchristos 84942542f5fSchristos res = _XRRGetScreenResourcesCurrent(clone->src.dpy, clone->src.window); 85042542f5fSchristos if (res == NULL) 85142542f5fSchristos goto err; 85242542f5fSchristos 85342542f5fSchristos info = XRRGetOutputInfo(clone->src.dpy, res, clone->src.rr_output); 85442542f5fSchristos if (info == NULL) 85542542f5fSchristos goto err; 85642542f5fSchristos 85742542f5fSchristos XGrabServer(clone->src.dpy); 85842542f5fSchristos 85942542f5fSchristos /* Clear all current UserModes on the output, including any active ones */ 86042542f5fSchristos if (info->crtc) { 86142542f5fSchristos DBG(XRR, ("%s(%s-%s): disabling active CRTC\n", __func__, 86242542f5fSchristos DisplayString(clone->src.dpy), clone->src.name)); 86342542f5fSchristos disable_crtc(clone->src.dpy, res, info->crtc); 86442542f5fSchristos } 86542542f5fSchristos for (i = 0; i < info->nmode; i++) { 86642542f5fSchristos DBG(XRR, ("%s(%s-%s): deleting mode %ld\n", __func__, 86742542f5fSchristos DisplayString(clone->src.dpy), clone->src.name, (long)info->modes[i])); 86842542f5fSchristos XRRDeleteOutputMode(clone->src.dpy, clone->src.rr_output, info->modes[i]); 86942542f5fSchristos } 87042542f5fSchristos 87142542f5fSchristos clone->src.rr_crtc = 0; 87242542f5fSchristos 87342542f5fSchristos /* Create matching mode for the real output on the virtual */ 87442542f5fSchristos memset(&mode, 0, sizeof(mode)); 875fe8aea9eSmrg mode.width = clone->dst.width; 876fe8aea9eSmrg mode.height = clone->dst.height; 87742542f5fSchristos mode.nameLength = sprintf(mode_name, "FAKE-%dx%d", mode.width, mode.height); 87842542f5fSchristos mode.name = mode_name; 87942542f5fSchristos 88042542f5fSchristos id = 0; 88142542f5fSchristos for (j = 0; j < res->nmode; j++) { 88242542f5fSchristos if (mode_equal(&mode, &res->modes[j])) { 88342542f5fSchristos id = res->modes[j].id; 88442542f5fSchristos break; 88542542f5fSchristos } 88642542f5fSchristos } 88742542f5fSchristos if (id == 0) 88842542f5fSchristos id = XRRCreateMode(clone->src.dpy, clone->src.window, &mode); 88942542f5fSchristos 89042542f5fSchristos XRRAddOutputMode(clone->src.dpy, clone->src.rr_output, id); 89142542f5fSchristos 89242542f5fSchristos XUngrabServer(clone->src.dpy); 89342542f5fSchristos ret = 0; 89442542f5fSchristoserr: 89542542f5fSchristos if (info) 89642542f5fSchristos XRRFreeOutputInfo(info); 89742542f5fSchristos if (res) 89842542f5fSchristos XRRFreeScreenResources(res); 89942542f5fSchristos 90042542f5fSchristos return ret; 90142542f5fSchristos} 90242542f5fSchristos 90342542f5fSchristosstatic RROutput claim_virtual(struct display *display, char *output_name, int nclone) 90442542f5fSchristos{ 90542542f5fSchristos char mode_name[] = "ClaimVirtualHead"; 90642542f5fSchristos Display *dpy = display->dpy; 90742542f5fSchristos XRRScreenResources *res; 90842542f5fSchristos XRROutputInfo *output; 90942542f5fSchristos XRRModeInfo mode; 91042542f5fSchristos RRMode id; 91142542f5fSchristos RROutput rr_output = 0; 91242542f5fSchristos int i; 91342542f5fSchristos 91442542f5fSchristos DBG(X11, ("%s(%d)\n", __func__, nclone)); 91542542f5fSchristos XGrabServer(dpy); 91642542f5fSchristos 91742542f5fSchristos res = _XRRGetScreenResourcesCurrent(dpy, display->root); 91842542f5fSchristos if (res == NULL) 91942542f5fSchristos goto out; 92042542f5fSchristos 92142542f5fSchristos sprintf(output_name, "VIRTUAL%d", nclone); 92242542f5fSchristos 92342542f5fSchristos for (i = rr_output = 0; rr_output == 0 && i < res->noutput; i++) { 92442542f5fSchristos output = XRRGetOutputInfo(dpy, res, res->outputs[i]); 92542542f5fSchristos if (output == NULL) 92642542f5fSchristos continue; 92742542f5fSchristos 92842542f5fSchristos if (strcmp(output->name, output_name) == 0) 92942542f5fSchristos rr_output = res->outputs[i]; 93042542f5fSchristos 93142542f5fSchristos XRRFreeOutputInfo(output); 93242542f5fSchristos } 93342542f5fSchristos for (i = id = 0; id == 0 && i < res->nmode; i++) { 93442542f5fSchristos if (strcmp(res->modes[i].name, mode_name) == 0) 93542542f5fSchristos id = res->modes[i].id; 93642542f5fSchristos } 93742542f5fSchristos XRRFreeScreenResources(res); 93842542f5fSchristos 93942542f5fSchristos DBG(XRR, ("%s(%s): rr_output=%ld\n", __func__, output_name, (long)rr_output)); 94042542f5fSchristos if (rr_output == 0) 94142542f5fSchristos goto out; 94242542f5fSchristos 94342542f5fSchristos /* Set any mode on the VirtualHead to make the Xserver allocate another */ 94442542f5fSchristos memset(&mode, 0, sizeof(mode)); 94542542f5fSchristos mode.width = 1024; 94642542f5fSchristos mode.height = 768; 94742542f5fSchristos mode.name = mode_name; 94842542f5fSchristos mode.nameLength = sizeof(mode_name) - 1; 94942542f5fSchristos 95042542f5fSchristos if (id == 0) 95142542f5fSchristos id = XRRCreateMode(dpy, display->root, &mode); 95242542f5fSchristos XRRAddOutputMode(dpy, rr_output, id); 95342542f5fSchristos 95442542f5fSchristos /* Force a redetection for the ddx to spot the new outputs */ 95542542f5fSchristos res = XRRGetScreenResources(dpy, display->root); 95642542f5fSchristos if (res == NULL) 95742542f5fSchristos goto out; 95842542f5fSchristos 95942542f5fSchristos /* Some else may have interrupted us and installed that new mode! */ 96042542f5fSchristos output = XRRGetOutputInfo(dpy, res, rr_output); 96142542f5fSchristos if (output) { 96242542f5fSchristos disable_crtc(dpy, res, output->crtc); 96342542f5fSchristos XRRFreeOutputInfo(output); 96442542f5fSchristos } 96542542f5fSchristos XRRFreeScreenResources(res); 96642542f5fSchristos 96742542f5fSchristos XRRDeleteOutputMode(dpy, rr_output, id); 96842542f5fSchristos XRRDestroyMode(dpy, id); 96942542f5fSchristos 97042542f5fSchristos /* And hide it again */ 97142542f5fSchristos res = XRRGetScreenResources(dpy, display->root); 97242542f5fSchristos if (res != NULL) 97342542f5fSchristos XRRFreeScreenResources(res); 97442542f5fSchristosout: 97542542f5fSchristos XUngrabServer(dpy); 97642542f5fSchristos 97742542f5fSchristos return rr_output; 97842542f5fSchristos} 97942542f5fSchristos 980fe8aea9eSmrgstatic int check_virtual(struct display *display) 981fe8aea9eSmrg{ 982fe8aea9eSmrg XRRScreenResources *res; 983fe8aea9eSmrg int found = -ENOENT; 984fe8aea9eSmrg int i; 985fe8aea9eSmrg 986fe8aea9eSmrg res = _XRRGetScreenResourcesCurrent(display->dpy, display->root); 987fe8aea9eSmrg if (res == NULL) 988fe8aea9eSmrg return -ENOMEM; 989fe8aea9eSmrg 990fe8aea9eSmrg for (i = 0; found == -ENOENT && i < res->noutput; i++) { 991fe8aea9eSmrg XRROutputInfo *output; 992fe8aea9eSmrg 993fe8aea9eSmrg output = XRRGetOutputInfo(display->dpy, res, res->outputs[i]); 994fe8aea9eSmrg if (output == NULL) 995fe8aea9eSmrg continue; 996fe8aea9eSmrg 997fe8aea9eSmrg if (strcmp(output->name, "VIRTUAL1") == 0) 998fe8aea9eSmrg found = 0; 999fe8aea9eSmrg 1000fe8aea9eSmrg XRRFreeOutputInfo(output); 1001fe8aea9eSmrg } 1002fe8aea9eSmrg XRRFreeScreenResources(res); 1003fe8aea9eSmrg 1004fe8aea9eSmrg DBG(XRR, ("%s(%s): has VIRTUAL1? %d\n", 1005fe8aea9eSmrg __func__, DisplayString(display->dpy), found)); 1006fe8aea9eSmrg return found; 1007fe8aea9eSmrg} 1008fe8aea9eSmrg 100942542f5fSchristosstatic int stride_for_depth(int width, int depth) 101042542f5fSchristos{ 101142542f5fSchristos if (depth == 24) 101242542f5fSchristos depth = 32; 101342542f5fSchristos return ((width * depth + 7) / 8 + 3) & ~3; 101442542f5fSchristos} 101542542f5fSchristos 101642542f5fSchristosstatic void init_image(struct clone *clone) 101742542f5fSchristos{ 101842542f5fSchristos XImage *image = &clone->image; 101942542f5fSchristos int ret; 102042542f5fSchristos 102142542f5fSchristos image->width = clone->width; 102242542f5fSchristos image->height = clone->height; 102342542f5fSchristos image->format = ZPixmap; 102442542f5fSchristos image->xoffset = 0; 102542542f5fSchristos image->byte_order = LSBFirst; 102642542f5fSchristos image->bitmap_unit = 32; 102742542f5fSchristos image->bitmap_bit_order = LSBFirst; 102842542f5fSchristos image->bitmap_pad = 32; 102942542f5fSchristos image->data = clone->shm.shmaddr; 103042542f5fSchristos image->bytes_per_line = stride_for_depth(clone->width, clone->depth); 103142542f5fSchristos switch (clone->depth) { 103242542f5fSchristos case 24: 103342542f5fSchristos image->red_mask = 0xff << 16; 103442542f5fSchristos image->green_mask = 0xff << 8; 103542542f5fSchristos image->blue_mask = 0xff << 0;; 103642542f5fSchristos image->depth = 24; 103742542f5fSchristos image->bits_per_pixel = 32; 103842542f5fSchristos break; 103942542f5fSchristos case 16: 104042542f5fSchristos image->red_mask = 0x1f << 11; 104142542f5fSchristos image->green_mask = 0x3f << 5; 104242542f5fSchristos image->blue_mask = 0x1f << 0;; 104342542f5fSchristos image->depth = 16; 104442542f5fSchristos image->bits_per_pixel = 16; 104542542f5fSchristos break; 104642542f5fSchristos } 104742542f5fSchristos 104842542f5fSchristos ret = XInitImage(image); 104942542f5fSchristos assert(ret); 105042542f5fSchristos (void)ret; 105142542f5fSchristos} 105242542f5fSchristos 105342542f5fSchristosstatic int mode_height(const XRRModeInfo *mode, Rotation rotation) 105442542f5fSchristos{ 105542542f5fSchristos switch (rotation & 0xf) { 105642542f5fSchristos case RR_Rotate_0: 105742542f5fSchristos case RR_Rotate_180: 105842542f5fSchristos return mode->height; 105942542f5fSchristos case RR_Rotate_90: 106042542f5fSchristos case RR_Rotate_270: 106142542f5fSchristos return mode->width; 106242542f5fSchristos default: 106342542f5fSchristos return 0; 106442542f5fSchristos } 106542542f5fSchristos} 106642542f5fSchristos 106742542f5fSchristosstatic int mode_width(const XRRModeInfo *mode, Rotation rotation) 106842542f5fSchristos{ 106942542f5fSchristos switch (rotation & 0xf) { 107042542f5fSchristos case RR_Rotate_0: 107142542f5fSchristos case RR_Rotate_180: 107242542f5fSchristos return mode->width; 107342542f5fSchristos case RR_Rotate_90: 107442542f5fSchristos case RR_Rotate_270: 107542542f5fSchristos return mode->height; 107642542f5fSchristos default: 107742542f5fSchristos return 0; 107842542f5fSchristos } 107942542f5fSchristos} 108042542f5fSchristos 108142542f5fSchristosstatic void output_init_xfer(struct clone *clone, struct output *output) 108242542f5fSchristos{ 108342542f5fSchristos if (output->pixmap == None && output->use_shm_pixmap) { 108442542f5fSchristos DBG(DRAW, ("%s-%s: creating shm pixmap\n", DisplayString(output->dpy), output->name)); 108542542f5fSchristos XSync(output->dpy, False); 108642542f5fSchristos _x_error_occurred = 0; 108742542f5fSchristos 108842542f5fSchristos output->pixmap = XShmCreatePixmap(output->dpy, output->window, 108942542f5fSchristos clone->shm.shmaddr, &output->shm, 109042542f5fSchristos clone->width, clone->height, clone->depth); 109142542f5fSchristos if (output->pix_picture) { 109242542f5fSchristos XRenderFreePicture(output->dpy, output->pix_picture); 109342542f5fSchristos output->pix_picture = None; 109442542f5fSchristos } 109542542f5fSchristos 109642542f5fSchristos XSync(output->dpy, False); 109742542f5fSchristos if (_x_error_occurred) { 109842542f5fSchristos XFreePixmap(output->dpy, output->pixmap); 109942542f5fSchristos output->pixmap = None; 110042542f5fSchristos output->use_shm_pixmap = 0; 110142542f5fSchristos } 110242542f5fSchristos } 110342542f5fSchristos if (output->use_render) { 110442542f5fSchristos DBG(DRAW, ("%s-%s: creating picture\n", DisplayString(output->dpy), output->name)); 110542542f5fSchristos if (output->win_picture == None) 110642542f5fSchristos output->win_picture = XRenderCreatePicture(output->dpy, output->window, 110742542f5fSchristos output->display->root_format, 0, NULL); 110842542f5fSchristos if (output->pixmap == None) 110942542f5fSchristos output->pixmap = XCreatePixmap(output->dpy, output->window, 111042542f5fSchristos clone->width, clone->height, clone->depth); 111142542f5fSchristos if (output->pix_picture == None) 111242542f5fSchristos output->pix_picture = XRenderCreatePicture(output->dpy, output->pixmap, 111342542f5fSchristos output->use_render, 0, NULL); 111442542f5fSchristos } 111542542f5fSchristos 111642542f5fSchristos if (output->gc == None) { 111742542f5fSchristos XGCValues gcv; 111842542f5fSchristos 111942542f5fSchristos DBG(DRAW, ("%s-%s: creating gc\n", DisplayString(output->dpy), output->name)); 112042542f5fSchristos 112142542f5fSchristos gcv.graphics_exposures = False; 112242542f5fSchristos gcv.subwindow_mode = IncludeInferiors; 112342542f5fSchristos 112442542f5fSchristos output->gc = XCreateGC(output->dpy, output->pixmap ?: output->window, GCGraphicsExposures | GCSubwindowMode, &gcv); 112542542f5fSchristos } 112642542f5fSchristos} 112742542f5fSchristos 112842542f5fSchristosstatic int bpp_for_depth(int depth) 112942542f5fSchristos{ 113042542f5fSchristos switch (depth) { 113142542f5fSchristos case 1: return 1; 113242542f5fSchristos case 8: return 8; 113342542f5fSchristos case 15: return 16; 113442542f5fSchristos case 16: return 16; 113542542f5fSchristos case 24: return 24; 113642542f5fSchristos case 32: return 32; 113742542f5fSchristos default: return 0; 113842542f5fSchristos } 113942542f5fSchristos} 114042542f5fSchristos 114142542f5fSchristosstatic int clone_init_xfer(struct clone *clone) 114242542f5fSchristos{ 114342542f5fSchristos int width, height; 114442542f5fSchristos 114542542f5fSchristos if (clone->dst.mode.id == 0) { 114613496ba1Ssnj width = 0; 114713496ba1Ssnj height = 0; 114842542f5fSchristos } else if (clone->dri3.xid) { 1149fe8aea9eSmrg width = clone->dst.width; 1150fe8aea9eSmrg height = clone->dst.height; 115142542f5fSchristos } else { 115242542f5fSchristos width = mode_width(&clone->src.mode, clone->src.rotation); 115342542f5fSchristos height = mode_height(&clone->src.mode, clone->src.rotation); 115442542f5fSchristos } 115542542f5fSchristos 1156fe8aea9eSmrg DBG(DRAW, ("%s-%s create xfer, %dx%d (currently %dx%d)\n", 1157fe8aea9eSmrg DisplayString(clone->dst.dpy), clone->dst.name, 1158fe8aea9eSmrg width, height, clone->width, clone->height)); 1159fe8aea9eSmrg 116042542f5fSchristos if (width == clone->width && height == clone->height) 116142542f5fSchristos return 0; 116242542f5fSchristos 116342542f5fSchristos if (clone->shm.shmaddr) { 116442542f5fSchristos if (clone->src.use_shm) 116542542f5fSchristos XShmDetach(clone->src.dpy, &clone->src.shm); 116642542f5fSchristos if (clone->dst.use_shm) 116742542f5fSchristos XShmDetach(clone->dst.dpy, &clone->dst.shm); 116842542f5fSchristos 116942542f5fSchristos shmdt(clone->shm.shmaddr); 117042542f5fSchristos clone->shm.shmaddr = NULL; 117142542f5fSchristos } 117242542f5fSchristos 117342542f5fSchristos if (clone->src.pixmap) { 117442542f5fSchristos XFreePixmap(clone->src.dpy, clone->src.pixmap); 117542542f5fSchristos clone->src.pixmap = 0; 117642542f5fSchristos } 117742542f5fSchristos 117842542f5fSchristos if (clone->dst.pixmap) { 117942542f5fSchristos XFreePixmap(clone->dst.dpy, clone->dst.pixmap); 118042542f5fSchristos clone->dst.pixmap = 0; 118142542f5fSchristos } 118242542f5fSchristos 118342542f5fSchristos if ((width | height) == 0) { 118442542f5fSchristos clone->damaged.x2 = clone->damaged.y2 = INT_MIN; 118542542f5fSchristos clone->damaged.x1 = clone->damaged.y1 = INT_MAX; 118642542f5fSchristos return 0; 118742542f5fSchristos } 118842542f5fSchristos 118942542f5fSchristos if (clone->dri3.xid) { 119042542f5fSchristos int fd, stride; 119142542f5fSchristos Pixmap src; 119242542f5fSchristos 119342542f5fSchristos _x_error_occurred = 0; 119442542f5fSchristos 119542542f5fSchristos DBG(DRAW, ("%s-%s create xfer, trying DRI3\n", 119642542f5fSchristos DisplayString(clone->dst.dpy), clone->dst.name)); 119742542f5fSchristos 119842542f5fSchristos fd = dri3_create_fd(clone->dst.dpy, clone->dst.window, &stride); 119942542f5fSchristos if (fd < 0) 120042542f5fSchristos goto disable_dri3; 120142542f5fSchristos 120242542f5fSchristos DBG(DRAW, ("%s-%s create xfer, DRI3 fd=%d, stride=%d\n", 120342542f5fSchristos DisplayString(clone->dst.dpy), clone->dst.name, 120442542f5fSchristos fd, stride)); 120542542f5fSchristos 120642542f5fSchristos src = dri3_create_pixmap(clone->src.dpy, clone->src.window, 120742542f5fSchristos width, height, clone->depth, 120842542f5fSchristos fd, bpp_for_depth(clone->depth), 120942542f5fSchristos stride, lseek(fd, 0, SEEK_END)); 121042542f5fSchristos 121142542f5fSchristos XSync(clone->src.dpy, False); 121242542f5fSchristos if (!_x_error_occurred) { 121342542f5fSchristos clone->src.pixmap = src; 121442542f5fSchristos clone->width = width; 121542542f5fSchristos clone->height = height; 121642542f5fSchristos } else { 121742542f5fSchristos XFreePixmap(clone->src.dpy, src); 121842542f5fSchristos close(fd); 121942542f5fSchristosdisable_dri3: 122042542f5fSchristos dri3_fence_free(clone->src.dpy, &clone->dri3); 122142542f5fSchristos clone->dri3.xid = 0; 122242542f5fSchristos 122342542f5fSchristos DBG(DRAW, ("%s-%s create xfer, DRI3 failed\n", 122442542f5fSchristos DisplayString(clone->dst.dpy), clone->dst.name)); 122542542f5fSchristos } 122642542f5fSchristos } 122742542f5fSchristos 122842542f5fSchristos width = mode_width(&clone->src.mode, clone->src.rotation); 122942542f5fSchristos height = mode_height(&clone->src.mode, clone->src.rotation); 123042542f5fSchristos 123142542f5fSchristos if (!clone->dri3.xid) { 123242542f5fSchristos DBG(DRAW, ("%s-%s create xfer, trying SHM\n", 123342542f5fSchristos DisplayString(clone->dst.dpy), clone->dst.name)); 123442542f5fSchristos 123542542f5fSchristos clone->shm.shmid = shmget(IPC_PRIVATE, 123642542f5fSchristos height * stride_for_depth(width, clone->depth), 123742542f5fSchristos IPC_CREAT | 0666); 123842542f5fSchristos if (clone->shm.shmid == -1) 123942542f5fSchristos return errno; 124042542f5fSchristos 124142542f5fSchristos clone->shm.shmaddr = shmat(clone->shm.shmid, 0, 0); 124242542f5fSchristos if (clone->shm.shmaddr == (char *) -1) { 124342542f5fSchristos shmctl(clone->shm.shmid, IPC_RMID, NULL); 124442542f5fSchristos return ENOMEM; 124542542f5fSchristos } 124642542f5fSchristos 124742542f5fSchristos if (clone->src.use_shm) { 124842542f5fSchristos clone->src.shm = clone->shm; 124942542f5fSchristos clone->src.shm.readOnly = False; 125042542f5fSchristos XShmAttach(clone->src.dpy, &clone->src.shm); 125142542f5fSchristos XSync(clone->src.dpy, False); 125242542f5fSchristos } 125342542f5fSchristos if (clone->dst.use_shm) { 125442542f5fSchristos clone->dst.shm = clone->shm; 125542542f5fSchristos clone->dst.shm.readOnly = !clone->dst.use_shm_pixmap; 125642542f5fSchristos XShmAttach(clone->dst.dpy, &clone->dst.shm); 125742542f5fSchristos XSync(clone->dst.dpy, False); 125842542f5fSchristos } 125942542f5fSchristos 126042542f5fSchristos shmctl(clone->shm.shmid, IPC_RMID, NULL); 126142542f5fSchristos 126242542f5fSchristos clone->width = width; 126342542f5fSchristos clone->height = height; 126442542f5fSchristos 126542542f5fSchristos init_image(clone); 126642542f5fSchristos } 126742542f5fSchristos 126842542f5fSchristos output_init_xfer(clone, &clone->src); 126942542f5fSchristos output_init_xfer(clone, &clone->dst); 127042542f5fSchristos 127142542f5fSchristos clone->damaged.x1 = clone->src.x; 127242542f5fSchristos clone->damaged.x2 = clone->src.x + width; 127342542f5fSchristos clone->damaged.y1 = clone->src.y; 127442542f5fSchristos clone->damaged.y2 = clone->src.y + height; 127542542f5fSchristos 127642542f5fSchristos display_mark_flush(clone->dst.display); 127742542f5fSchristos return 0; 127842542f5fSchristos} 127942542f5fSchristos 128042542f5fSchristosstatic void clone_update(struct clone *clone) 128142542f5fSchristos{ 128242542f5fSchristos if (!clone->rr_update) 128342542f5fSchristos return; 128442542f5fSchristos 128542542f5fSchristos DBG(X11, ("%s-%s cloning modes\n", 128642542f5fSchristos DisplayString(clone->dst.dpy), clone->dst.name)); 128742542f5fSchristos 128842542f5fSchristos clone_update_modes__randr(clone); 128942542f5fSchristos clone->rr_update = 0; 129042542f5fSchristos} 129142542f5fSchristos 1292fe8aea9eSmrgstatic void screensaver_save(struct display *display) 1293fe8aea9eSmrg{ 1294fe8aea9eSmrg display->saver_active = 1295fe8aea9eSmrg XScreenSaverQueryExtension(display->dpy, 1296fe8aea9eSmrg &display->saver_event, 1297fe8aea9eSmrg &display->saver_error); 1298fe8aea9eSmrg DBG(SCREEN, 1299fe8aea9eSmrg ("%s screen saver active? %d [event=%d, error=%d]\n", 1300fe8aea9eSmrg DisplayString(display->dpy), 1301fe8aea9eSmrg display->saver_active, 1302fe8aea9eSmrg display->saver_event, 1303fe8aea9eSmrg display->saver_error)); 1304fe8aea9eSmrg 1305fe8aea9eSmrg XGetScreenSaver(display->dpy, 1306fe8aea9eSmrg &display->saver.timeout, 1307fe8aea9eSmrg &display->saver.interval, 1308fe8aea9eSmrg &display->saver.prefer_blank, 1309fe8aea9eSmrg &display->saver.allow_exp); 1310fe8aea9eSmrg 1311fe8aea9eSmrg DBG(SCREEN, 1312fe8aea9eSmrg ("%s saving screen saver defaults: timeout=%d interval=%d prefer_blank=%d allow_exp=%d\n", 1313fe8aea9eSmrg DisplayString(display->dpy), 1314fe8aea9eSmrg display->saver.timeout, 1315fe8aea9eSmrg display->saver.interval, 1316fe8aea9eSmrg display->saver.prefer_blank, 1317fe8aea9eSmrg display->saver.allow_exp)); 1318fe8aea9eSmrg} 1319fe8aea9eSmrg 1320fe8aea9eSmrgstatic void screensaver_disable(struct display *display) 1321fe8aea9eSmrg{ 1322fe8aea9eSmrg DBG(SCREEN, 1323fe8aea9eSmrg ("%s disabling screen saver\n", DisplayString(display->dpy))); 1324fe8aea9eSmrg 1325fe8aea9eSmrg XSetScreenSaver(display->dpy, 0, 0, DefaultBlanking, DefaultExposures); 1326fe8aea9eSmrg display_mark_flush(display); 1327fe8aea9eSmrg} 1328fe8aea9eSmrg 1329fe8aea9eSmrgstatic void screensaver_restore(struct display *display) 1330fe8aea9eSmrg{ 1331fe8aea9eSmrg DBG(SCREEN, 1332fe8aea9eSmrg ("%s restoring screen saver\n", DisplayString(display->dpy))); 1333fe8aea9eSmrg 1334fe8aea9eSmrg XSetScreenSaver(display->dpy, 1335fe8aea9eSmrg display->saver.timeout, 1336fe8aea9eSmrg display->saver.interval, 1337fe8aea9eSmrg display->saver.prefer_blank, 1338fe8aea9eSmrg display->saver.allow_exp); 1339fe8aea9eSmrg display_mark_flush(display); 1340fe8aea9eSmrg} 1341fe8aea9eSmrg 134242542f5fSchristosstatic int context_update(struct context *ctx) 134342542f5fSchristos{ 134442542f5fSchristos Display *dpy = ctx->display->dpy; 134542542f5fSchristos XRRScreenResources *res; 134642542f5fSchristos int context_changed = 0; 134742542f5fSchristos int i, n; 134842542f5fSchristos 134942542f5fSchristos DBG(X11, ("%s\n", __func__)); 135042542f5fSchristos 135142542f5fSchristos res = _XRRGetScreenResourcesCurrent(dpy, ctx->display->root); 135242542f5fSchristos if (res == NULL) 135342542f5fSchristos return 0; 135442542f5fSchristos 135542542f5fSchristos DBG(XRR, ("%s timestamp %ld (last %ld), config %ld (last %ld)\n", 135642542f5fSchristos DisplayString(dpy), 135742542f5fSchristos res->timestamp, ctx->timestamp, 135842542f5fSchristos res->configTimestamp, ctx->configTimestamp)); 135942542f5fSchristos if (res->timestamp == ctx->timestamp && 136042542f5fSchristos res->configTimestamp == ctx->configTimestamp && 136142542f5fSchristos res->timestamp != res->configTimestamp) { /* mutter be damned */ 136242542f5fSchristos XRRFreeScreenResources(res); 136342542f5fSchristos return 0; 136442542f5fSchristos } 136542542f5fSchristos 136642542f5fSchristos ctx->timestamp = res->timestamp; 136742542f5fSchristos ctx->configTimestamp = res->configTimestamp; 136842542f5fSchristos 136942542f5fSchristos for (n = 0; n < ctx->nclone; n++) { 137042542f5fSchristos struct output *output = &ctx->clones[n].src; 137142542f5fSchristos XRROutputInfo *o; 137242542f5fSchristos XRRCrtcInfo *c; 137342542f5fSchristos RRMode mode = 0; 137442542f5fSchristos int changed = 0; 137542542f5fSchristos 137642542f5fSchristos o = XRRGetOutputInfo(dpy, res, output->rr_output); 137742542f5fSchristos if (o == NULL) 137842542f5fSchristos continue; 137942542f5fSchristos 138042542f5fSchristos c = NULL; 138142542f5fSchristos if (o->crtc) 138242542f5fSchristos c = XRRGetCrtcInfo(dpy, res, o->crtc); 138342542f5fSchristos if (c) { 138442542f5fSchristos DBG(XRR, ("%s-%s: (x=%d, y=%d, rotation=%d, mode=%ld) -> (x=%d, y=%d, rotation=%d, mode=%ld)\n", 138542542f5fSchristos DisplayString(dpy), output->name, 138642542f5fSchristos output->x, output->y, output->rotation, output->mode.id, 138742542f5fSchristos c->x, c->y, c->rotation, c->mode)); 138842542f5fSchristos 138942542f5fSchristos changed |= output->rotation != c->rotation; 139042542f5fSchristos output->rotation = c->rotation; 139142542f5fSchristos 139242542f5fSchristos changed |= output->x != c->x; 139342542f5fSchristos output->x = c->x; 139442542f5fSchristos 139542542f5fSchristos changed |= output->y != c->y; 139642542f5fSchristos output->y = c->y; 139742542f5fSchristos 139842542f5fSchristos changed |= output->mode.id != c->mode; 139942542f5fSchristos mode = c->mode; 140042542f5fSchristos XRRFreeCrtcInfo(c); 140142542f5fSchristos } else { 140242542f5fSchristos DBG(XRR, ("%s-%s: (x=%d, y=%d, rotation=%d, mode=%ld) -> off\n", 140342542f5fSchristos DisplayString(dpy), output->name, 140442542f5fSchristos output->x, output->y, output->rotation, output->mode.id)); 140542542f5fSchristos } 140642542f5fSchristos output->rr_crtc = o->crtc; 140742542f5fSchristos XRRFreeOutputInfo(o); 140842542f5fSchristos 140942542f5fSchristos DBG(XRR, ("%s-%s crtc changed? %d\n", 141042542f5fSchristos DisplayString(ctx->clones[n].dst.display->dpy), ctx->clones[n].dst.name, changed)); 141142542f5fSchristos 141242542f5fSchristos if (mode) { 141342542f5fSchristos if (output->mode.id != mode) { 141442542f5fSchristos for (i = 0; i < res->nmode; i++) { 141542542f5fSchristos if (res->modes[i].id == mode) { 141642542f5fSchristos output->mode = res->modes[i]; 141742542f5fSchristos break; 141842542f5fSchristos } 141942542f5fSchristos } 142042542f5fSchristos } 142142542f5fSchristos } else { 142242542f5fSchristos changed = output->mode.id != 0; 142342542f5fSchristos output->mode.id = 0; 142442542f5fSchristos } 142542542f5fSchristos 142642542f5fSchristos DBG(XRR, ("%s-%s output changed? %d\n", 142742542f5fSchristos DisplayString(ctx->clones[n].dst.display->dpy), ctx->clones[n].dst.name, changed)); 142842542f5fSchristos 142942542f5fSchristos context_changed |= changed; 143042542f5fSchristos } 143142542f5fSchristos XRRFreeScreenResources(res); 143242542f5fSchristos 143342542f5fSchristos DBG(XRR, ("%s changed? %d\n", DisplayString(dpy), context_changed)); 143442542f5fSchristos if (!context_changed) 143542542f5fSchristos return 0; 143642542f5fSchristos 143742542f5fSchristos for (n = 1; n < ctx->ndisplay; n++) { 143842542f5fSchristos struct display *display = &ctx->display[n]; 143942542f5fSchristos struct clone *clone; 144042542f5fSchristos int x1, x2, y1, y2; 144142542f5fSchristos 1442fe8aea9eSmrg if (display->rr_active == 0) { 1443fe8aea9eSmrg for (clone = display->clone; clone; clone = clone->next) { 1444fe8aea9eSmrg struct output *output = &clone->src; 1445fe8aea9eSmrg if (output->mode.id) { 1446fe8aea9eSmrg clone->dst.mode.id = -1; 1447fe8aea9eSmrg clone->dst.rr_crtc = -1; 1448fe8aea9eSmrg } else { 1449fe8aea9eSmrg clone->dst.mode.id = 0; 1450fe8aea9eSmrg clone->dst.rr_crtc = 0; 1451fe8aea9eSmrg } 1452fe8aea9eSmrg } 145342542f5fSchristos continue; 1454fe8aea9eSmrg } 145542542f5fSchristos 145642542f5fSchristos x1 = y1 = INT_MAX; 145742542f5fSchristos x2 = y2 = INT_MIN; 145842542f5fSchristos 145942542f5fSchristos for (clone = display->clone; clone; clone = clone->next) { 146042542f5fSchristos struct output *output = &clone->src; 146142542f5fSchristos int v; 146242542f5fSchristos 146342542f5fSchristos assert(clone->dst.display == display); 146442542f5fSchristos 146542542f5fSchristos if (output->mode.id == 0) 146642542f5fSchristos continue; 146742542f5fSchristos 146842542f5fSchristos DBG(XRR, ("%s: source %s enabled (%d, %d)x(%d, %d)\n", 146942542f5fSchristos DisplayString(clone->dst.dpy), output->name, 147042542f5fSchristos output->x, output->y, 147142542f5fSchristos mode_width(&output->mode, output->rotation), 147242542f5fSchristos mode_height(&output->mode, output->rotation))); 147342542f5fSchristos 147442542f5fSchristos if (output->x < x1) 147542542f5fSchristos x1 = output->x; 147642542f5fSchristos if (output->y < y1) 147742542f5fSchristos y1 = output->y; 147842542f5fSchristos 147942542f5fSchristos v = (int)output->x + mode_width(&output->mode, output->rotation); 148042542f5fSchristos if (v > x2) 148142542f5fSchristos x2 = v; 148242542f5fSchristos v = (int)output->y + mode_height(&output->mode, output->rotation); 148342542f5fSchristos if (v > y2) 148442542f5fSchristos y2 = v; 148542542f5fSchristos } 148642542f5fSchristos 148742542f5fSchristos DBG(XRR, ("%s fb bounds (%d, %d)x(%d, %d)\n", DisplayString(display->dpy), 148842542f5fSchristos x1, y1, x2, y2)); 148942542f5fSchristos 149042542f5fSchristos XGrabServer(display->dpy); 149142542f5fSchristos res = _XRRGetScreenResourcesCurrent(display->dpy, display->root); 149242542f5fSchristos if (res == NULL) 149342542f5fSchristos goto ungrab; 149442542f5fSchristos 149542542f5fSchristos if (x2 <= x1 || y2 <= y1) { 149642542f5fSchristos /* Nothing enabled, preserve the current fb, and turn everything off */ 149742542f5fSchristos for (clone = display->clone; clone; clone = clone->next) { 149842542f5fSchristos struct output *dst = &clone->dst; 149942542f5fSchristos 150042542f5fSchristos if (!dst->rr_crtc) 150142542f5fSchristos continue; 150242542f5fSchristos 150342542f5fSchristos DBG(XRR, ("%s: disabling output '%s'\n", 150442542f5fSchristos DisplayString(display->dpy), dst->name)); 150542542f5fSchristos assert(clone->dst.display == display); 150642542f5fSchristos if (disable_crtc(display->dpy, res, dst->rr_crtc)) { 150742542f5fSchristos dst->rr_crtc = 0; 150842542f5fSchristos dst->mode.id = 0; 150942542f5fSchristos } 151042542f5fSchristos } 151142542f5fSchristos goto free_res; 151242542f5fSchristos } 151342542f5fSchristos 151442542f5fSchristos x2 -= x1; 151542542f5fSchristos y2 -= y1; 151642542f5fSchristos DBG(XRR, ("%s: current size %dx%d, need %dx%d\n", 151742542f5fSchristos DisplayString(display->dpy), 151842542f5fSchristos display->width, display->height, 151942542f5fSchristos x2, y2)); 152042542f5fSchristos 152142542f5fSchristos if (display->width != x2 || display->height != y2) { 152242542f5fSchristos /* When shrinking we have to manually resize the fb */ 152342542f5fSchristos for (clone = display->clone; clone; clone = clone->next) { 152442542f5fSchristos struct output *dst = &clone->dst; 152542542f5fSchristos 152642542f5fSchristos if (!dst->rr_crtc) 152742542f5fSchristos continue; 152842542f5fSchristos 152942542f5fSchristos DBG(XRR, ("%s: disabling output '%s'\n", 153042542f5fSchristos DisplayString(display->dpy), dst->name)); 153142542f5fSchristos assert(clone->dst.display == display); 153242542f5fSchristos if (disable_crtc(display->dpy, res, dst->rr_crtc)) { 153342542f5fSchristos dst->rr_crtc = 0; 153442542f5fSchristos dst->mode.id = 0; 153542542f5fSchristos } 153642542f5fSchristos } 153742542f5fSchristos 153842542f5fSchristos DBG(XRR, ("%s: XRRSetScreenSize %dx%d\n", DisplayString(display->dpy), x2, y2)); 153942542f5fSchristos XRRSetScreenSize(display->dpy, display->root, x2, y2, x2 * 96 / 25.4, y2 * 96 / 25.4); 154042542f5fSchristos display->width = x2; 154142542f5fSchristos display->height = y2; 154242542f5fSchristos } 154342542f5fSchristos 154442542f5fSchristos for (clone = display->clone; clone; clone = clone->next) { 154542542f5fSchristos struct output *src = &clone->src; 154642542f5fSchristos struct output *dst = &clone->dst; 154742542f5fSchristos XRROutputInfo *o; 154842542f5fSchristos XRRPanning panning; 154942542f5fSchristos struct clone *set; 155042542f5fSchristos RRCrtc rr_crtc; 155142542f5fSchristos Status ret; 155242542f5fSchristos 155342542f5fSchristos DBG(XRR, ("%s: copying configuration from %s (mode=%ld: %dx%d) to %s\n", 155442542f5fSchristos DisplayString(display->dpy), 155542542f5fSchristos src->name, (long)src->mode.id, src->mode.width, src->mode.height, 155642542f5fSchristos dst->name)); 155742542f5fSchristos 155842542f5fSchristos if (src->mode.id == 0) { 155942542f5fSchristoserr: 156042542f5fSchristos if (dst->rr_crtc) { 156142542f5fSchristos DBG(XRR, ("%s: disabling unused output '%s'\n", 156242542f5fSchristos DisplayString(display->dpy), dst->name)); 156342542f5fSchristos assert(clone->dst.display == display); 156442542f5fSchristos if (disable_crtc(display->dpy, res, dst->rr_crtc)) { 156542542f5fSchristos dst->rr_crtc = 0; 156642542f5fSchristos dst->mode.id = 0; 156742542f5fSchristos } 156842542f5fSchristos } 156942542f5fSchristos continue; 157042542f5fSchristos } 157142542f5fSchristos 157242542f5fSchristos dst->x = src->x - x1; 157342542f5fSchristos dst->y = src->y - y1; 157442542f5fSchristos dst->rotation = src->rotation; 157542542f5fSchristos dst->mode = src->mode; 157642542f5fSchristos 157742542f5fSchristos dst->mode.id = 0; 157842542f5fSchristos for (i = 0; i < res->nmode; i++) { 157942542f5fSchristos if (mode_equal(&src->mode, &res->modes[i])) { 158042542f5fSchristos dst->mode.id = res->modes[i].id; 158142542f5fSchristos break; 158242542f5fSchristos } 158342542f5fSchristos } 158442542f5fSchristos if (dst->mode.id == 0) { 158542542f5fSchristos XRRModeInfo m; 158642542f5fSchristos char buf[256]; 158742542f5fSchristos RRMode id; 158842542f5fSchristos 158942542f5fSchristos /* XXX User names must be unique! */ 159042542f5fSchristos m = src->mode; 159142542f5fSchristos m.nameLength = snprintf(buf, sizeof(buf), 159242542f5fSchristos "%s.%ld-%dx%d", src->name, 159342542f5fSchristos (long)src->mode.id, 159442542f5fSchristos src->mode.width, 159542542f5fSchristos src->mode.height); 159642542f5fSchristos m.name = buf; 159742542f5fSchristos 159842542f5fSchristos id = XRRCreateMode(dst->dpy, dst->window, &m); 159942542f5fSchristos if (id) { 160042542f5fSchristos DBG(XRR, ("%s: adding mode %ld: %dx%d to %s, new mode %ld\n", 160142542f5fSchristos DisplayString(dst->dpy), 160242542f5fSchristos (long)src->mode.id, 160342542f5fSchristos src->mode.width, 160442542f5fSchristos src->mode.height, 160542542f5fSchristos dst->name, (long)id)); 160642542f5fSchristos XRRAddOutputMode(dst->dpy, dst->rr_output, id); 160742542f5fSchristos dst->mode.id = id; 160842542f5fSchristos } else { 160942542f5fSchristos DBG(XRR, ("%s: failed to find suitable mode for %s\n", 161042542f5fSchristos DisplayString(dst->dpy), dst->name)); 161142542f5fSchristos goto err; 161242542f5fSchristos } 161342542f5fSchristos } 161442542f5fSchristos 161542542f5fSchristos rr_crtc = dst->rr_crtc; 161642542f5fSchristos if (rr_crtc) { 161742542f5fSchristos for (set = display->clone; set != clone; set = set->next) { 161842542f5fSchristos if (set->dst.rr_crtc == rr_crtc) { 161942542f5fSchristos DBG(XRR, ("%s: CRTC reassigned from %s\n", 162042542f5fSchristos DisplayString(dst->dpy), dst->name)); 162142542f5fSchristos rr_crtc = 0; 162242542f5fSchristos break; 162342542f5fSchristos } 162442542f5fSchristos } 162542542f5fSchristos } 162642542f5fSchristos if (rr_crtc == 0) { 162742542f5fSchristos o = XRRGetOutputInfo(dst->dpy, res, dst->rr_output); 162842542f5fSchristos for (i = 0; i < o->ncrtc; i++) { 162942542f5fSchristos DBG(XRR, ("%s: checking whether CRTC:%ld is available\n", 163042542f5fSchristos DisplayString(dst->dpy), (long)o->crtcs[i])); 163142542f5fSchristos for (set = display->clone; set != clone; set = set->next) { 163242542f5fSchristos if (set->dst.rr_crtc == o->crtcs[i]) { 163342542f5fSchristos DBG(XRR, ("%s: CRTC:%ld already assigned to %s\n", 163442542f5fSchristos DisplayString(dst->dpy), (long)o->crtcs[i], set->dst.name)); 163542542f5fSchristos break; 163642542f5fSchristos } 163742542f5fSchristos } 163842542f5fSchristos if (set == clone) { 163942542f5fSchristos rr_crtc = o->crtcs[i]; 164042542f5fSchristos break; 164142542f5fSchristos } 164242542f5fSchristos } 164342542f5fSchristos XRRFreeOutputInfo(o); 164442542f5fSchristos } 164542542f5fSchristos if (rr_crtc == 0) { 164642542f5fSchristos DBG(XRR, ("%s: failed to find available CRTC for %s\n", 164742542f5fSchristos DisplayString(dst->dpy), dst->name)); 164842542f5fSchristos goto err; 164942542f5fSchristos } 165042542f5fSchristos 165142542f5fSchristos DBG(XRR, ("%s: enabling output '%s' (%d,%d)x(%d,%d), rotation %d, on CRTC:%ld, using mode %ld\n", 165242542f5fSchristos DisplayString(dst->dpy), dst->name, 165342542f5fSchristos dst->x, dst->y, dst->mode.width, dst->mode.height, 165442542f5fSchristos dst->rotation, (long)rr_crtc, dst->mode.id)); 165542542f5fSchristos 165642542f5fSchristos ret = XRRSetPanning(dst->dpy, res, rr_crtc, memset(&panning, 0, sizeof(panning))); 165742542f5fSchristos DBG(XRR, ("%s-%s: XRRSetPanning %s\n", DisplayString(dst->dpy), dst->name, ret ? "failed" : "success")); 165842542f5fSchristos (void)ret; 165942542f5fSchristos 166042542f5fSchristos ret = XRRSetCrtcConfig(dst->dpy, res, rr_crtc, CurrentTime, 166142542f5fSchristos dst->x, dst->y, dst->mode.id, dst->rotation, 166242542f5fSchristos &dst->rr_output, 1); 166342542f5fSchristos DBG(XRR, ("%s-%s: XRRSetCrtcConfig %s\n", DisplayString(dst->dpy), dst->name, ret ? "failed" : "success")); 166442542f5fSchristos if (ret) 166542542f5fSchristos goto err; 166642542f5fSchristos 166742542f5fSchristos if (verbose & XRR) { 166842542f5fSchristos XRRCrtcInfo *c; 166942542f5fSchristos XRRPanning *p; 167042542f5fSchristos 167142542f5fSchristos c = XRRGetCrtcInfo(dst->dpy, res, rr_crtc); 167242542f5fSchristos if (c) { 167342542f5fSchristos DBG(XRR, ("%s-%s: x=%d, y=%d, rotation=%d, mode=%ld\n", 167442542f5fSchristos DisplayString(dst->dpy), dst->name, 167542542f5fSchristos c->x, c->y, c->rotation, c->mode)); 167642542f5fSchristos XRRFreeCrtcInfo(c); 167742542f5fSchristos } 167842542f5fSchristos 167942542f5fSchristos p = XRRGetPanning(dst->dpy, res, rr_crtc); 168042542f5fSchristos if (p) { 168142542f5fSchristos DBG(XRR, ("%s-%s: panning (%d, %d)x(%d, %d), tracking (%d, %d)x(%d, %d), border (%d, %d),(%d, %d)\n", 168242542f5fSchristos DisplayString(dst->dpy), dst->name, 168342542f5fSchristos p->left, p->top, p->width, p->height, 168442542f5fSchristos p->track_left, p->track_top, p->track_width, p->track_height, 168542542f5fSchristos p->border_left, p->border_top, p->border_right, p->border_bottom)); 168642542f5fSchristos XRRFreePanning(p); 168742542f5fSchristos } 168842542f5fSchristos } 168942542f5fSchristos 169042542f5fSchristos dst->rr_crtc = rr_crtc; 169142542f5fSchristos } 169242542f5fSchristosfree_res: 169342542f5fSchristos XRRFreeScreenResources(res); 169442542f5fSchristosungrab: 169542542f5fSchristos XUngrabServer(display->dpy); 169642542f5fSchristos } 169742542f5fSchristos 1698fe8aea9eSmrg for (n = 1; n < ctx->ndisplay; n++) { 1699fe8aea9eSmrg struct display *display = &ctx->display[n]; 1700fe8aea9eSmrg 1701fe8aea9eSmrg display->active = 0; 1702fe8aea9eSmrg screensaver_restore(display); 1703fe8aea9eSmrg } 1704fe8aea9eSmrg 170542542f5fSchristos ctx->active = NULL; 170642542f5fSchristos for (n = 0; n < ctx->nclone; n++) { 170742542f5fSchristos struct clone *clone = &ctx->clones[n]; 170842542f5fSchristos 170942542f5fSchristos clone_init_xfer(clone); 171042542f5fSchristos 171142542f5fSchristos if (clone->dst.rr_crtc == 0) 171242542f5fSchristos continue; 171342542f5fSchristos 171442542f5fSchristos DBG(XRR, ("%s-%s: added to active list\n", 1715fe8aea9eSmrg DisplayString(clone->dst.display->dpy), clone->dst.name)); 1716fe8aea9eSmrg 1717fe8aea9eSmrg if (clone->dst.display->active++ == 0) 1718fe8aea9eSmrg screensaver_disable(clone->dst.display); 171942542f5fSchristos 172042542f5fSchristos clone->active = ctx->active; 172142542f5fSchristos ctx->active = clone; 172242542f5fSchristos } 172342542f5fSchristos 172442542f5fSchristos return 1; 172542542f5fSchristos} 172642542f5fSchristos 172742542f5fSchristosstatic Cursor display_load_invisible_cursor(struct display *display) 172842542f5fSchristos{ 172942542f5fSchristos char zero[8] = {}; 173042542f5fSchristos XColor black = {}; 173142542f5fSchristos Pixmap bitmap = XCreateBitmapFromData(display->dpy, display->root, zero, 8, 8); 173242542f5fSchristos return XCreatePixmapCursor(display->dpy, bitmap, bitmap, &black, &black, 0, 0); 173342542f5fSchristos} 173442542f5fSchristos 173542542f5fSchristosstatic Cursor display_get_visible_cursor(struct display *display) 173642542f5fSchristos{ 1737fe8aea9eSmrg struct display *first = display->ctx->display; 1738fe8aea9eSmrg 1739fe8aea9eSmrg if (display->cursor_serial != first->cursor_serial) { 1740fe8aea9eSmrg DBG(CURSOR, ("%s updating cursor %dx%d, serial %d\n", 1741fe8aea9eSmrg DisplayString(display->dpy), first->cursor_image.width, first->cursor_image.height, first->cursor_serial)); 174242542f5fSchristos 174342542f5fSchristos if (display->visible_cursor) 174442542f5fSchristos XFreeCursor(display->dpy, display->visible_cursor); 174542542f5fSchristos 1746fe8aea9eSmrg display->visible_cursor = XcursorImageLoadCursor(display->dpy, &first->cursor_image); 1747fe8aea9eSmrg display->cursor_serial = first->cursor_serial; 174842542f5fSchristos } 174942542f5fSchristos 175042542f5fSchristos return display->visible_cursor; 175142542f5fSchristos} 175242542f5fSchristos 175342542f5fSchristosstatic void display_load_visible_cursor(struct display *display, XFixesCursorImage *cur) 175442542f5fSchristos{ 175542542f5fSchristos unsigned long *src; /* XXX deep sigh */ 175642542f5fSchristos XcursorPixel *dst; 175742542f5fSchristos unsigned n; 175842542f5fSchristos 175942542f5fSchristos if (cur->width != display->cursor_image.width || 176042542f5fSchristos cur->height != display->cursor_image.height) 176142542f5fSchristos display->cursor_image.pixels = realloc(display->cursor_image.pixels, 176242542f5fSchristos 4 * cur->width * cur->height); 176342542f5fSchristos if (display->cursor_image.pixels == NULL) 176442542f5fSchristos return; 176542542f5fSchristos 176642542f5fSchristos display->cursor_image.width = cur->width; 176742542f5fSchristos display->cursor_image.height = cur->height; 176842542f5fSchristos display->cursor_image.xhot = cur->xhot; 176942542f5fSchristos display->cursor_image.yhot = cur->yhot; 1770fe8aea9eSmrg display->cursor_serial++; 177142542f5fSchristos 177242542f5fSchristos n = cur->width*cur->height; 177342542f5fSchristos src = cur->pixels; 177442542f5fSchristos dst = display->cursor_image.pixels; 177542542f5fSchristos while (n--) 177642542f5fSchristos *dst++ = *src++; 177742542f5fSchristos 1778fe8aea9eSmrg if (verbose & CURSOR) { 1779fe8aea9eSmrg int x, y; 1780fe8aea9eSmrg 1781fe8aea9eSmrg printf("%s cursor image %dx%d, serial %d:\n", 1782fe8aea9eSmrg DisplayString(display->dpy), 1783fe8aea9eSmrg cur->width, cur->height, 1784fe8aea9eSmrg display->cursor_serial); 1785fe8aea9eSmrg dst = display->cursor_image.pixels; 1786fe8aea9eSmrg for (y = 0; y < cur->height; y++) { 1787fe8aea9eSmrg for (x = 0; x < cur->width; x++) { 1788fe8aea9eSmrg if (x == cur->xhot && y == cur->yhot) 1789fe8aea9eSmrg printf("+"); 1790fe8aea9eSmrg else 1791fe8aea9eSmrg printf("%c", *dst ? *dst >> 24 >= 127 ? 'x' : '.' : ' '); 1792fe8aea9eSmrg dst++; 1793fe8aea9eSmrg } 1794fe8aea9eSmrg printf("\n"); 1795fe8aea9eSmrg } 179642542f5fSchristos } 179742542f5fSchristos} 179842542f5fSchristos 179942542f5fSchristosstatic void display_cursor_move(struct display *display, int x, int y, int visible) 180042542f5fSchristos{ 180142542f5fSchristos DBG(CURSOR, ("%s cursor moved (visible=%d, (%d, %d))\n", 180242542f5fSchristos DisplayString(display->dpy), visible, x, y)); 180342542f5fSchristos display->cursor_moved++; 180442542f5fSchristos display->cursor_visible += visible; 180542542f5fSchristos if (visible) { 180642542f5fSchristos display->cursor_x = x; 180742542f5fSchristos display->cursor_y = y; 180842542f5fSchristos } 180942542f5fSchristos 181042542f5fSchristos context_enable_timer(display->ctx); 181142542f5fSchristos} 181242542f5fSchristos 181342542f5fSchristosstatic void display_flush_cursor(struct display *display) 181442542f5fSchristos{ 181542542f5fSchristos Cursor cursor; 181642542f5fSchristos int x, y; 181742542f5fSchristos 181842542f5fSchristos if (!display->cursor_moved) 181942542f5fSchristos return; 182042542f5fSchristos 182142542f5fSchristos if (display->cursor_visible) { 182242542f5fSchristos x = display->cursor_x; 182342542f5fSchristos y = display->cursor_y; 182442542f5fSchristos } else { 182542542f5fSchristos x = display->cursor_x++ & 31; 182642542f5fSchristos y = display->cursor_y++ & 31; 182742542f5fSchristos } 182842542f5fSchristos 182942542f5fSchristos DBG(CURSOR, ("%s setting cursor position (%d, %d), visible? %d\n", 183042542f5fSchristos DisplayString(display->dpy), x, y, display->cursor_visible)); 183142542f5fSchristos XWarpPointer(display->dpy, None, display->root, 0, 0, 0, 0, x, y); 183242542f5fSchristos 183342542f5fSchristos cursor = None; 183442542f5fSchristos if (display->cursor_visible) 183542542f5fSchristos cursor = display_get_visible_cursor(display); 183642542f5fSchristos if (cursor == None) 183742542f5fSchristos cursor = display->invisible_cursor; 183842542f5fSchristos if (cursor != display->cursor) { 1839fe8aea9eSmrg DBG(CURSOR, ("%s setting cursor shape %lx\n", 1840fe8aea9eSmrg DisplayString(display->dpy), (long)cursor)); 184142542f5fSchristos XDefineCursor(display->dpy, display->root, cursor); 184242542f5fSchristos display->cursor = cursor; 184342542f5fSchristos } 184442542f5fSchristos 184542542f5fSchristos display_mark_flush(display); 184642542f5fSchristos 184742542f5fSchristos display->cursor_moved = 0; 184842542f5fSchristos display->cursor_visible = 0; 184942542f5fSchristos} 185042542f5fSchristos 185142542f5fSchristosstatic void clone_move_cursor(struct clone *c, int x, int y) 185242542f5fSchristos{ 185342542f5fSchristos int visible; 185442542f5fSchristos 185542542f5fSchristos DBG(CURSOR, ("%s-%s moving cursor (%d, %d) [(%d, %d), (%d, %d)]\n", 185642542f5fSchristos DisplayString(c->dst.dpy), c->dst.name, 185742542f5fSchristos x, y, 185842542f5fSchristos c->src.x, c->src.y, 185942542f5fSchristos c->src.x + c->width, c->src.y + c->height)); 186042542f5fSchristos 186142542f5fSchristos visible = (x >= c->src.x && x < c->src.x + c->width && 186242542f5fSchristos y >= c->src.y && y < c->src.y + c->height); 186342542f5fSchristos 186442542f5fSchristos x += c->dst.x - c->src.x; 186542542f5fSchristos y += c->dst.y - c->src.y; 186642542f5fSchristos 186742542f5fSchristos display_cursor_move(c->dst.display, x, y, visible); 186842542f5fSchristos} 186942542f5fSchristos 187042542f5fSchristosstatic int clone_output_init(struct clone *clone, struct output *output, 187142542f5fSchristos struct display *display, const char *name, 187242542f5fSchristos RROutput rr_output) 187342542f5fSchristos{ 187442542f5fSchristos Display *dpy = display->dpy; 187542542f5fSchristos int depth; 187642542f5fSchristos 187742542f5fSchristos DBG(X11, ("%s(%s, %s)\n", __func__, DisplayString(dpy), name)); 187842542f5fSchristos 187942542f5fSchristos output->name = strdup(name); 188042542f5fSchristos if (output->name == NULL) 188142542f5fSchristos return -ENOMEM; 188242542f5fSchristos 188342542f5fSchristos output->display = display; 188442542f5fSchristos output->dpy = dpy; 188542542f5fSchristos 188642542f5fSchristos output->rr_output = rr_output; 188742542f5fSchristos output->rotation = RR_Rotate_0; 188842542f5fSchristos 188942542f5fSchristos output->window = display->root; 189042542f5fSchristos output->use_shm = display->has_shm; 189142542f5fSchristos output->use_shm_pixmap = display->has_shm_pixmap; 189242542f5fSchristos 189342542f5fSchristos DBG(X11, ("%s-%s use shm? %d (use shm pixmap? %d)\n", 189442542f5fSchristos DisplayString(dpy), name, display->has_shm, display->has_shm_pixmap)); 189542542f5fSchristos 189642542f5fSchristos depth = output->use_shm && !FORCE_16BIT_XFER ? display->depth : 16; 189742542f5fSchristos if (depth < clone->depth) 189842542f5fSchristos clone->depth = depth; 189942542f5fSchristos 190042542f5fSchristos return 0; 190142542f5fSchristos} 190242542f5fSchristos 190342542f5fSchristosstatic void ximage_prepare(XImage *image, int width, int height) 190442542f5fSchristos{ 190542542f5fSchristos image->width = width; 190642542f5fSchristos image->height = height; 190742542f5fSchristos image->bytes_per_line = stride_for_depth(width, image->depth); 190842542f5fSchristos} 190942542f5fSchristos 191042542f5fSchristosstatic void get_src(struct clone *c, const XRectangle *clip) 191142542f5fSchristos{ 191242542f5fSchristos DBG(DRAW,("%s-%s get_src(%d,%d)x(%d,%d)\n", DisplayString(c->dst.dpy), c->dst.name, 191342542f5fSchristos clip->x, clip->y, clip->width, clip->height)); 191442542f5fSchristos 191542542f5fSchristos c->image.obdata = (char *)&c->src.shm; 191642542f5fSchristos 191742542f5fSchristos if (c->src.use_render) { 1918fe8aea9eSmrg DBG(DRAW, ("%s-%s get_src via XRender\n", 1919fe8aea9eSmrg DisplayString(c->dst.dpy), c->dst.name)); 192042542f5fSchristos XRenderComposite(c->src.dpy, PictOpSrc, 192142542f5fSchristos c->src.win_picture, 0, c->src.pix_picture, 192242542f5fSchristos clip->x, clip->y, 192342542f5fSchristos 0, 0, 192442542f5fSchristos 0, 0, 192542542f5fSchristos clip->width, clip->height); 192642542f5fSchristos if (c->src.use_shm_pixmap) { 192742542f5fSchristos XSync(c->src.dpy, False); 192842542f5fSchristos } else if (c->src.use_shm) { 192942542f5fSchristos ximage_prepare(&c->image, clip->width, clip->height); 193042542f5fSchristos XShmGetImage(c->src.dpy, c->src.pixmap, &c->image, 193142542f5fSchristos clip->x, clip->y, AllPlanes); 193242542f5fSchristos } else { 193342542f5fSchristos ximage_prepare(&c->image, c->width, c->height); 193442542f5fSchristos XGetSubImage(c->src.dpy, c->src.pixmap, 193542542f5fSchristos clip->x, clip->y, clip->width, clip->height, 193642542f5fSchristos AllPlanes, ZPixmap, 193742542f5fSchristos &c->image, 0, 0); 193842542f5fSchristos } 193942542f5fSchristos } else if (c->src.pixmap) { 1940fe8aea9eSmrg DBG(DRAW, ("%s-%s get_src XCopyArea (SHM/DRI3)\n", 1941fe8aea9eSmrg DisplayString(c->dst.dpy), c->dst.name)); 194242542f5fSchristos XCopyArea(c->src.dpy, c->src.window, c->src.pixmap, c->src.gc, 194342542f5fSchristos clip->x, clip->y, 194442542f5fSchristos clip->width, clip->height, 194542542f5fSchristos 0, 0); 194642542f5fSchristos XSync(c->src.dpy, False); 194742542f5fSchristos } else if (c->src.use_shm) { 1948fe8aea9eSmrg DBG(DRAW, ("%s-%s get_src XShmGetImage\n", 1949fe8aea9eSmrg DisplayString(c->dst.dpy), c->dst.name)); 195042542f5fSchristos ximage_prepare(&c->image, clip->width, clip->height); 195142542f5fSchristos XShmGetImage(c->src.dpy, c->src.window, &c->image, 195242542f5fSchristos clip->x, clip->y, AllPlanes); 195342542f5fSchristos } else { 1954fe8aea9eSmrg DBG(DRAW, ("%s-%s get_src XGetSubImage (slow)\n", 1955fe8aea9eSmrg DisplayString(c->dst.dpy), c->dst.name)); 195642542f5fSchristos ximage_prepare(&c->image, c->width, c->height); 195742542f5fSchristos XGetSubImage(c->src.dpy, c->src.window, 195842542f5fSchristos clip->x, clip->y, clip->width, clip->height, 195942542f5fSchristos AllPlanes, ZPixmap, 196042542f5fSchristos &c->image, 0, 0); 196142542f5fSchristos } 196242542f5fSchristos c->src.display->flush = 0; 196342542f5fSchristos} 196442542f5fSchristos 196542542f5fSchristosstatic void put_dst(struct clone *c, const XRectangle *clip) 196642542f5fSchristos{ 196742542f5fSchristos DBG(DRAW, ("%s-%s put_dst(%d,%d)x(%d,%d)\n", DisplayString(c->dst.dpy), c->dst.name, 196842542f5fSchristos clip->x, clip->y, clip->width, clip->height)); 196942542f5fSchristos 197042542f5fSchristos c->image.obdata = (char *)&c->dst.shm; 197142542f5fSchristos 197242542f5fSchristos if (c->dst.use_render) { 197342542f5fSchristos if (c->dst.use_shm_pixmap) { 197442542f5fSchristos DBG(DRAW, ("%s-%s using SHM pixmap composite\n", 197542542f5fSchristos DisplayString(c->dst.dpy), c->dst.name)); 197642542f5fSchristos } else if (c->dst.use_shm) { 197742542f5fSchristos DBG(DRAW, ("%s-%s using SHM image composite\n", 197842542f5fSchristos DisplayString(c->dst.dpy), c->dst.name)); 197942542f5fSchristos XShmPutImage(c->dst.dpy, c->dst.pixmap, c->dst.gc, &c->image, 198042542f5fSchristos 0, 0, 198142542f5fSchristos 0, 0, 198242542f5fSchristos clip->width, clip->height, 198342542f5fSchristos False); 198442542f5fSchristos } else { 198542542f5fSchristos DBG(DRAW, ("%s-%s using composite\n", 198642542f5fSchristos DisplayString(c->dst.dpy), c->dst.name)); 198742542f5fSchristos XPutImage(c->dst.dpy, c->dst.pixmap, c->dst.gc, &c->image, 198842542f5fSchristos 0, 0, 198942542f5fSchristos 0, 0, 199042542f5fSchristos clip->width, clip->height); 199142542f5fSchristos } 199242542f5fSchristos if (c->dst.use_shm) 199342542f5fSchristos c->dst.serial = NextRequest(c->dst.dpy); 199442542f5fSchristos XRenderComposite(c->dst.dpy, PictOpSrc, 199542542f5fSchristos c->dst.pix_picture, 0, c->dst.win_picture, 199642542f5fSchristos 0, 0, 199742542f5fSchristos 0, 0, 199842542f5fSchristos clip->x, clip->y, 199942542f5fSchristos clip->width, clip->height); 200042542f5fSchristos c->dst.display->send |= c->dst.use_shm; 200142542f5fSchristos } else if (c->dst.pixmap) { 2002fe8aea9eSmrg DBG(DRAW, ("%s-%s using SHM or DRI3 pixmap\n", 200342542f5fSchristos DisplayString(c->dst.dpy), c->dst.name)); 200442542f5fSchristos c->dst.serial = NextRequest(c->dst.dpy); 200542542f5fSchristos XCopyArea(c->dst.dpy, c->dst.pixmap, c->dst.window, c->dst.gc, 200642542f5fSchristos 0, 0, 200742542f5fSchristos clip->width, clip->height, 200842542f5fSchristos clip->x, clip->y); 200942542f5fSchristos c->dst.display->send = 1; 201042542f5fSchristos } else if (c->dst.use_shm) { 201142542f5fSchristos DBG(DRAW, ("%s-%s using SHM image\n", 201242542f5fSchristos DisplayString(c->dst.dpy), c->dst.name)); 201342542f5fSchristos c->dst.serial = NextRequest(c->dst.dpy); 201442542f5fSchristos XShmPutImage(c->dst.dpy, c->dst.window, c->dst.gc, &c->image, 201542542f5fSchristos 0, 0, 201642542f5fSchristos clip->x, clip->y, 201742542f5fSchristos clip->width, clip->height, 201842542f5fSchristos True); 201942542f5fSchristos } else { 202042542f5fSchristos DBG(DRAW, ("%s-%s using image\n", 202142542f5fSchristos DisplayString(c->dst.dpy), c->dst.name)); 202242542f5fSchristos XPutImage(c->dst.dpy, c->dst.window, c->dst.gc, &c->image, 202342542f5fSchristos 0, 0, 202442542f5fSchristos clip->x, clip->y, 202542542f5fSchristos clip->width, clip->height); 202642542f5fSchristos c->dst.serial = 0; 202742542f5fSchristos } 202842542f5fSchristos} 202942542f5fSchristos 203042542f5fSchristosstatic int clone_paint(struct clone *c) 203142542f5fSchristos{ 203242542f5fSchristos XRectangle clip; 203342542f5fSchristos 2034fe8aea9eSmrg if (c->width == 0 || c->height == 0) 2035fe8aea9eSmrg return 0; 2036fe8aea9eSmrg 203742542f5fSchristos DBG(DRAW, ("%s-%s paint clone, damaged (%d, %d), (%d, %d) [(%d, %d), (%d, %d)]\n", 203842542f5fSchristos DisplayString(c->dst.dpy), c->dst.name, 203942542f5fSchristos c->damaged.x1, c->damaged.y1, 204042542f5fSchristos c->damaged.x2, c->damaged.y2, 204142542f5fSchristos c->src.x, c->src.y, 204242542f5fSchristos c->src.x + c->width, c->src.y + c->height)); 204342542f5fSchristos 204442542f5fSchristos if (c->damaged.x1 < c->src.x) 204542542f5fSchristos c->damaged.x1 = c->src.x; 204642542f5fSchristos if (c->damaged.x2 > c->src.x + c->width) 204742542f5fSchristos c->damaged.x2 = c->src.x + c->width; 204842542f5fSchristos if (c->damaged.x2 <= c->damaged.x1) 204942542f5fSchristos goto done; 205042542f5fSchristos 205142542f5fSchristos if (c->damaged.y1 < c->src.y) 205242542f5fSchristos c->damaged.y1 = c->src.y; 205342542f5fSchristos if (c->damaged.y2 > c->src.y + c->height) 205442542f5fSchristos c->damaged.y2 = c->src.y + c->height; 205542542f5fSchristos if (c->damaged.y2 <= c->damaged.y1) 205642542f5fSchristos goto done; 205742542f5fSchristos 205842542f5fSchristos DBG(DRAW, ("%s-%s is damaged, last SHM serial: %ld, now %ld\n", 205942542f5fSchristos DisplayString(c->dst.dpy), c->dst.name, 206042542f5fSchristos (long)c->dst.serial, (long)LastKnownRequestProcessed(c->dst.dpy))); 206142542f5fSchristos if (c->dst.serial > LastKnownRequestProcessed(c->dst.dpy)) { 206242542f5fSchristos struct pollfd pfd; 206342542f5fSchristos 206442542f5fSchristos pfd.fd = ConnectionNumber(c->dst.dpy); 206542542f5fSchristos pfd.events = POLLIN; 206642542f5fSchristos XEventsQueued(c->dst.dpy, 206742542f5fSchristos poll(&pfd, 1, 0) ? QueuedAfterReading : QueuedAfterFlush); 206842542f5fSchristos 206942542f5fSchristos if (c->dst.serial > LastKnownRequestProcessed(c->dst.dpy)) { 207042542f5fSchristos c->dst.display->skip_clone++; 207142542f5fSchristos return EAGAIN; 207242542f5fSchristos } 207342542f5fSchristos } 207442542f5fSchristos 207542542f5fSchristos c->dst.display->skip_clone = 0; 207642542f5fSchristos c->dst.display->skip_frame = 0; 207742542f5fSchristos 207842542f5fSchristos if (FORCE_FULL_REDRAW) { 207942542f5fSchristos c->damaged.x1 = c->src.x; 208042542f5fSchristos c->damaged.y1 = c->src.y; 208142542f5fSchristos c->damaged.x2 = c->src.x + c->width; 208242542f5fSchristos c->damaged.y2 = c->src.y + c->height; 208342542f5fSchristos } 208442542f5fSchristos 208542542f5fSchristos if (c->dri3.xid) { 208642542f5fSchristos if (c->src.use_render) { 208742542f5fSchristos XRenderComposite(c->src.dpy, PictOpSrc, 208842542f5fSchristos c->src.win_picture, 0, c->src.pix_picture, 208942542f5fSchristos c->damaged.x1, c->damaged.y1, 209042542f5fSchristos 0, 0, 209142542f5fSchristos c->damaged.x1 + c->dst.x - c->src.x, 209242542f5fSchristos c->damaged.y1 + c->dst.y - c->src.y, 209342542f5fSchristos c->damaged.x2 - c->damaged.x1, 209442542f5fSchristos c->damaged.y2 - c->damaged.y1); 209542542f5fSchristos } else { 209642542f5fSchristos XCopyArea(c->src.dpy, c->src.window, c->src.pixmap, c->src.gc, 209742542f5fSchristos c->damaged.x1, c->damaged.y1, 209842542f5fSchristos c->damaged.x2 - c->damaged.x1, 209942542f5fSchristos c->damaged.y2 - c->damaged.y1, 210042542f5fSchristos c->damaged.x1 + c->dst.x - c->src.x, 210142542f5fSchristos c->damaged.y1 + c->dst.y - c->src.y); 210242542f5fSchristos } 210342542f5fSchristos dri3_fence_flush(c->src.dpy, &c->dri3); 210442542f5fSchristos } else { 210542542f5fSchristos clip.x = c->damaged.x1; 210642542f5fSchristos clip.y = c->damaged.y1; 210742542f5fSchristos clip.width = c->damaged.x2 - c->damaged.x1; 210842542f5fSchristos clip.height = c->damaged.y2 - c->damaged.y1; 210942542f5fSchristos get_src(c, &clip); 211042542f5fSchristos 2111fe8aea9eSmrg DBG(DRAW, ("%s-%s target offset %dx%d\n", 2112fe8aea9eSmrg DisplayString(c->dst.dpy), c->dst.name, 2113fe8aea9eSmrg c->dst.x - c->src.x, c->dst.y - c->src.y)); 2114fe8aea9eSmrg 211542542f5fSchristos clip.x += c->dst.x - c->src.x; 211642542f5fSchristos clip.y += c->dst.y - c->src.y; 211742542f5fSchristos put_dst(c, &clip); 211842542f5fSchristos } 211942542f5fSchristos display_mark_flush(c->dst.display); 212042542f5fSchristos 212142542f5fSchristosdone: 212242542f5fSchristos c->damaged.x2 = c->damaged.y2 = INT_MIN; 212342542f5fSchristos c->damaged.x1 = c->damaged.y1 = INT_MAX; 212442542f5fSchristos return 0; 212542542f5fSchristos} 212642542f5fSchristos 212742542f5fSchristosstatic void clone_damage(struct clone *c, const XRectangle *rec) 212842542f5fSchristos{ 212942542f5fSchristos int v; 213042542f5fSchristos 213142542f5fSchristos if ((v = rec->x) < c->damaged.x1) 213242542f5fSchristos c->damaged.x1 = v; 213342542f5fSchristos if ((v = (int)rec->x + rec->width) > c->damaged.x2) 213442542f5fSchristos c->damaged.x2 = v; 213542542f5fSchristos if ((v = rec->y) < c->damaged.y1) 213642542f5fSchristos c->damaged.y1 = v; 213742542f5fSchristos if ((v = (int)rec->y + rec->height) > c->damaged.y2) 213842542f5fSchristos c->damaged.y2 = v; 213942542f5fSchristos 2140fe8aea9eSmrg DBG(DAMAGE, ("%s-%s damaged: +(%d,%d)x(%d, %d) -> (%d, %d), (%d, %d)\n", 214142542f5fSchristos DisplayString(c->dst.display->dpy), c->dst.name, 2142fe8aea9eSmrg rec->x, rec->y, rec->width, rec->height, 214342542f5fSchristos c->damaged.x1, c->damaged.y1, 214442542f5fSchristos c->damaged.x2, c->damaged.y2)); 214542542f5fSchristos} 214642542f5fSchristos 214742542f5fSchristosstatic void usage(const char *arg0) 214842542f5fSchristos{ 214942542f5fSchristos printf("Usage: %s [OPTION]... [TARGET_DISPLAY]...\n", arg0); 215042542f5fSchristos printf(" -d <source display> source display\n"); 215142542f5fSchristos printf(" -f keep in foreground (do not detach from console and daemonize)\n"); 215242542f5fSchristos printf(" -b start bumblebee\n"); 215342542f5fSchristos printf(" -a connect to all local displays (e.g. :1, :2, etc)\n"); 215442542f5fSchristos printf(" -S disable use of a singleton and launch a fresh intel-virtual-output process\n"); 215542542f5fSchristos printf(" -v all verbose output, implies -f\n"); 215642542f5fSchristos printf(" -V <category> specific verbose output, implies -f\n"); 215742542f5fSchristos printf(" -h this help\n"); 215842542f5fSchristos printf("If no target displays are parsed on the commandline, \n"); 215942542f5fSchristos printf("intel-virtual-output will attempt to connect to any local display\n"); 216042542f5fSchristos printf("and then start bumblebee.\n"); 216142542f5fSchristos} 216242542f5fSchristos 216342542f5fSchristosstatic void record_callback(XPointer closure, XRecordInterceptData *data) 216442542f5fSchristos{ 216542542f5fSchristos struct context *ctx = (struct context *)closure; 216642542f5fSchristos 216742542f5fSchristos DBG(X11, ("%s\n", __func__)); 216842542f5fSchristos 216942542f5fSchristos if (data->category == XRecordFromServer) { 217042542f5fSchristos const xEvent *e = (const xEvent *)data->data; 217142542f5fSchristos 217242542f5fSchristos DBG(X11, ("%s -- from server, event type %d, root %ld (ours? %d)\n", 217342542f5fSchristos __func__, e->u.u.type, (long)e->u.keyButtonPointer.root, 217442542f5fSchristos ctx->display->root == e->u.keyButtonPointer.root)); 217542542f5fSchristos 217642542f5fSchristos if (e->u.u.type == MotionNotify && 217742542f5fSchristos e->u.keyButtonPointer.root == ctx->display->root) { 217842542f5fSchristos struct clone *clone; 217942542f5fSchristos 218042542f5fSchristos for (clone = ctx->active; clone; clone = clone->active) 218142542f5fSchristos clone_move_cursor(clone, 218242542f5fSchristos e->u.keyButtonPointer.rootX, 218342542f5fSchristos e->u.keyButtonPointer.rootY); 218442542f5fSchristos } 218542542f5fSchristos } 218642542f5fSchristos 218742542f5fSchristos XRecordFreeData(data); 218842542f5fSchristos} 218942542f5fSchristos 219042542f5fSchristosstatic int record_mouse(struct context *ctx) 219142542f5fSchristos{ 219242542f5fSchristos Display *dpy; 219342542f5fSchristos XRecordRange *rr; 219442542f5fSchristos XRecordClientSpec rcs; 219542542f5fSchristos XRecordContext rc; 219642542f5fSchristos 219742542f5fSchristos DBG(X11, ("%s(%s)\n", __func__, DisplayString(ctx->display->dpy))); 219842542f5fSchristos 219942542f5fSchristos dpy = XOpenDisplay(DisplayString(ctx->display->dpy)); 220042542f5fSchristos if (dpy == NULL) 220142542f5fSchristos return -ECONNREFUSED; 220242542f5fSchristos 220342542f5fSchristos rr = XRecordAllocRange(); 220442542f5fSchristos if (rr == NULL) 220542542f5fSchristos return -ENOMEM; 220642542f5fSchristos 220742542f5fSchristos rr->device_events.first = rr->device_events.last = MotionNotify; 220842542f5fSchristos 220942542f5fSchristos rcs = XRecordAllClients; 221042542f5fSchristos rc = XRecordCreateContext(dpy, 0, &rcs, 1, &rr, 1); 221142542f5fSchristos 221242542f5fSchristos XSync(dpy, False); 221342542f5fSchristos 221442542f5fSchristos if (!XRecordEnableContextAsync(dpy, rc, record_callback, (XPointer)ctx)) 221542542f5fSchristos return -EINVAL; 221642542f5fSchristos 221742542f5fSchristos ctx->record = dpy; 221842542f5fSchristos return ConnectionNumber(dpy); 221942542f5fSchristos} 222042542f5fSchristos 222142542f5fSchristosstatic int bad_visual(Visual *visual, int depth) 222242542f5fSchristos{ 222342542f5fSchristos DBG(X11, ("%s? depth=%d, visual: class=%d, bits_per_rgb=%d, red_mask=%08lx, green_mask=%08lx, blue_mask=%08lx\n", 222442542f5fSchristos __func__, depth, 222542542f5fSchristos visual->class, 222642542f5fSchristos visual->bits_per_rgb, 222742542f5fSchristos visual->red_mask, 222842542f5fSchristos visual->green_mask, 222942542f5fSchristos visual->blue_mask)); 223042542f5fSchristos 223142542f5fSchristos if (!(visual->class == TrueColor || visual->class == DirectColor)) 223242542f5fSchristos return 1; 223342542f5fSchristos 223442542f5fSchristos switch (depth) { 223542542f5fSchristos case 16: return (/* visual->bits_per_rgb != 6 || */ 223642542f5fSchristos visual->red_mask != 0x1f << 11 || 223742542f5fSchristos visual->green_mask != 0x3f << 5 || 223842542f5fSchristos visual->blue_mask != 0x1f << 0); 223942542f5fSchristos 224042542f5fSchristos case 24: return (/* visual->bits_per_rgb != 8 || */ 224142542f5fSchristos visual->red_mask != 0xff << 16 || 224242542f5fSchristos visual->green_mask != 0xff << 8 || 224342542f5fSchristos visual->blue_mask != 0xff << 0); 224442542f5fSchristos 224542542f5fSchristos default: return 1; 224642542f5fSchristos } 224742542f5fSchristos} 224842542f5fSchristos 224942542f5fSchristosstatic XRenderPictFormat * 225042542f5fSchristosfind_xrender_format(Display *dpy, pixman_format_code_t format) 225142542f5fSchristos{ 225242542f5fSchristos XRenderPictFormat tmpl; 225342542f5fSchristos int mask; 225442542f5fSchristos 225542542f5fSchristos#define MASK(x) ((1<<(x))-1) 225642542f5fSchristos 225742542f5fSchristos memset(&tmpl, 0, sizeof(tmpl)); 225842542f5fSchristos 225942542f5fSchristos tmpl.depth = PIXMAN_FORMAT_DEPTH(format); 226042542f5fSchristos mask = PictFormatType | PictFormatDepth; 226142542f5fSchristos 226242542f5fSchristos DBG(X11, ("%s(0x%08lx)\n", __func__, (long)format)); 226342542f5fSchristos 226442542f5fSchristos switch (PIXMAN_FORMAT_TYPE(format)) { 226542542f5fSchristos case PIXMAN_TYPE_ARGB: 226642542f5fSchristos tmpl.type = PictTypeDirect; 226742542f5fSchristos 226842542f5fSchristos if (PIXMAN_FORMAT_A(format)) { 226942542f5fSchristos tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format)); 227042542f5fSchristos tmpl.direct.alpha = (PIXMAN_FORMAT_R(format) + 227142542f5fSchristos PIXMAN_FORMAT_G(format) + 227242542f5fSchristos PIXMAN_FORMAT_B(format)); 227342542f5fSchristos } 227442542f5fSchristos 227542542f5fSchristos tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format)); 227642542f5fSchristos tmpl.direct.red = (PIXMAN_FORMAT_G(format) + 227742542f5fSchristos PIXMAN_FORMAT_B(format)); 227842542f5fSchristos 227942542f5fSchristos tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format)); 228042542f5fSchristos tmpl.direct.green = PIXMAN_FORMAT_B(format); 228142542f5fSchristos 228242542f5fSchristos tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format)); 228342542f5fSchristos tmpl.direct.blue = 0; 228442542f5fSchristos 228542542f5fSchristos mask |= PictFormatRed | PictFormatRedMask; 228642542f5fSchristos mask |= PictFormatGreen | PictFormatGreenMask; 228742542f5fSchristos mask |= PictFormatBlue | PictFormatBlueMask; 228842542f5fSchristos mask |= PictFormatAlpha | PictFormatAlphaMask; 228942542f5fSchristos break; 229042542f5fSchristos 229142542f5fSchristos case PIXMAN_TYPE_ABGR: 229242542f5fSchristos tmpl.type = PictTypeDirect; 229342542f5fSchristos 229442542f5fSchristos if (tmpl.direct.alphaMask) { 229542542f5fSchristos tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format)); 229642542f5fSchristos tmpl.direct.alpha = (PIXMAN_FORMAT_B(format) + 229742542f5fSchristos PIXMAN_FORMAT_G(format) + 229842542f5fSchristos PIXMAN_FORMAT_R(format)); 229942542f5fSchristos } 230042542f5fSchristos 230142542f5fSchristos tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format)); 230242542f5fSchristos tmpl.direct.blue = (PIXMAN_FORMAT_G(format) + 230342542f5fSchristos PIXMAN_FORMAT_R(format)); 230442542f5fSchristos 230542542f5fSchristos tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format)); 230642542f5fSchristos tmpl.direct.green = PIXMAN_FORMAT_R(format); 230742542f5fSchristos 230842542f5fSchristos tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format)); 230942542f5fSchristos tmpl.direct.red = 0; 231042542f5fSchristos 231142542f5fSchristos mask |= PictFormatRed | PictFormatRedMask; 231242542f5fSchristos mask |= PictFormatGreen | PictFormatGreenMask; 231342542f5fSchristos mask |= PictFormatBlue | PictFormatBlueMask; 231442542f5fSchristos mask |= PictFormatAlpha | PictFormatAlphaMask; 231542542f5fSchristos break; 231642542f5fSchristos 231742542f5fSchristos case PIXMAN_TYPE_BGRA: 231842542f5fSchristos tmpl.type = PictTypeDirect; 231942542f5fSchristos 232042542f5fSchristos tmpl.direct.blueMask = MASK(PIXMAN_FORMAT_B(format)); 232142542f5fSchristos tmpl.direct.blue = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format)); 232242542f5fSchristos 232342542f5fSchristos tmpl.direct.greenMask = MASK(PIXMAN_FORMAT_G(format)); 232442542f5fSchristos tmpl.direct.green = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format) - 232542542f5fSchristos PIXMAN_FORMAT_G(format)); 232642542f5fSchristos 232742542f5fSchristos tmpl.direct.redMask = MASK(PIXMAN_FORMAT_R(format)); 232842542f5fSchristos tmpl.direct.red = (PIXMAN_FORMAT_BPP(format) - PIXMAN_FORMAT_B(format) - 232942542f5fSchristos PIXMAN_FORMAT_G(format) - PIXMAN_FORMAT_R(format)); 233042542f5fSchristos 233142542f5fSchristos if (tmpl.direct.alphaMask) { 233242542f5fSchristos tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format)); 233342542f5fSchristos tmpl.direct.alpha = 0; 233442542f5fSchristos } 233542542f5fSchristos 233642542f5fSchristos mask |= PictFormatRed | PictFormatRedMask; 233742542f5fSchristos mask |= PictFormatGreen | PictFormatGreenMask; 233842542f5fSchristos mask |= PictFormatBlue | PictFormatBlueMask; 233942542f5fSchristos mask |= PictFormatAlpha | PictFormatAlphaMask; 234042542f5fSchristos break; 234142542f5fSchristos 234242542f5fSchristos case PIXMAN_TYPE_A: 234342542f5fSchristos tmpl.type = PictTypeDirect; 234442542f5fSchristos 234542542f5fSchristos tmpl.direct.alpha = 0; 234642542f5fSchristos tmpl.direct.alphaMask = MASK(PIXMAN_FORMAT_A(format)); 234742542f5fSchristos 234842542f5fSchristos mask |= PictFormatAlpha | PictFormatAlphaMask; 234942542f5fSchristos break; 235042542f5fSchristos 235142542f5fSchristos case PIXMAN_TYPE_COLOR: 235242542f5fSchristos case PIXMAN_TYPE_GRAY: 235342542f5fSchristos /* XXX Find matching visual/colormap */ 235442542f5fSchristos tmpl.type = PictTypeIndexed; 235542542f5fSchristos //tmpl.colormap = screen->visuals[PIXMAN_FORMAT_VIS(format)].vid; 235642542f5fSchristos //mask |= PictFormatColormap; 235742542f5fSchristos return NULL; 235842542f5fSchristos } 235942542f5fSchristos#undef MASK 236042542f5fSchristos 236142542f5fSchristos return XRenderFindFormat(dpy, mask, &tmpl, 0); 236242542f5fSchristos} 236342542f5fSchristos 236442542f5fSchristosstatic int display_init_render(struct display *display, int depth, XRenderPictFormat **use_render) 236542542f5fSchristos{ 236642542f5fSchristos Display *dpy = display->dpy; 236742542f5fSchristos int major, minor; 236842542f5fSchristos 236942542f5fSchristos DBG(X11, ("%s is depth %d, want %d\n", DisplayString(dpy), display->depth, depth)); 237042542f5fSchristos 237142542f5fSchristos *use_render = 0; 237242542f5fSchristos if (depth == display->depth && !bad_visual(display->visual, depth)) 237342542f5fSchristos return 0; 237442542f5fSchristos 237542542f5fSchristos if (display->root_format == 0) { 237642542f5fSchristos if (!XRenderQueryVersion(dpy, &major, &minor)) { 237742542f5fSchristos fprintf(stderr, "Render extension not supported by %s\n", DisplayString(dpy)); 237842542f5fSchristos return -EINVAL; 237942542f5fSchristos } 238042542f5fSchristos 238142542f5fSchristos display->root_format = XRenderFindVisualFormat(dpy, display->visual); 238242542f5fSchristos display->rgb16_format = find_xrender_format(dpy, PIXMAN_r5g6b5); 238342542f5fSchristos display->rgb24_format = XRenderFindStandardFormat(dpy, PictStandardRGB24); 238442542f5fSchristos 238542542f5fSchristos DBG(X11, ("%s: root format=%lx, rgb16 format=%lx, rgb24 format=%lx\n", 238642542f5fSchristos DisplayString(dpy), 238742542f5fSchristos (long)display->root_format, 238842542f5fSchristos (long)display->rgb16_format, 238942542f5fSchristos (long)display->rgb24_format)); 239042542f5fSchristos } 239142542f5fSchristos 239242542f5fSchristos switch (depth) { 239342542f5fSchristos case 16: *use_render = display->rgb16_format; break; 239442542f5fSchristos case 24: *use_render = display->rgb24_format; break; 239542542f5fSchristos } 239642542f5fSchristos if (*use_render == 0) 239742542f5fSchristos return -ENOENT; 239842542f5fSchristos 239942542f5fSchristos return 0; 240042542f5fSchristos} 240142542f5fSchristos 240242542f5fSchristosstatic int clone_init_depth(struct clone *clone) 240342542f5fSchristos{ 240442542f5fSchristos int ret, depth; 240542542f5fSchristos 240642542f5fSchristos DBG(X11,("%s-%s wants depth %d\n", 240742542f5fSchristos DisplayString(clone->dst.dpy), clone->dst.name, clone->depth)); 240842542f5fSchristos 240942542f5fSchristos ret = -1; 241042542f5fSchristos for (depth = clone->depth; depth <= 24; depth += 8) { 241142542f5fSchristos ret = display_init_render(clone->src.display, depth, &clone->src.use_render); 241242542f5fSchristos if (ret) 241342542f5fSchristos continue; 241442542f5fSchristos 241542542f5fSchristos ret = display_init_render(clone->dst.display, depth, &clone->dst.use_render); 241642542f5fSchristos if (ret) 241742542f5fSchristos continue; 241842542f5fSchristos 241942542f5fSchristos break; 242042542f5fSchristos } 242142542f5fSchristos if (ret) 242242542f5fSchristos return ret; 242342542f5fSchristos 2424fe8aea9eSmrg clone->depth = depth; 2425fe8aea9eSmrg 242642542f5fSchristos DBG(X11, ("%s-%s using depth %d, requires xrender for src? %d, for dst? %d\n", 242742542f5fSchristos DisplayString(clone->dst.dpy), clone->dst.name, 242842542f5fSchristos clone->depth, 242942542f5fSchristos clone->src.use_render != NULL, 243042542f5fSchristos clone->dst.use_render != NULL)); 243142542f5fSchristos 243242542f5fSchristos if (!clone->dst.use_render && 243342542f5fSchristos clone->src.display->dri3_active && 243442542f5fSchristos clone->dst.display->dri3_active) 243542542f5fSchristos dri3_create_fence(clone->src.dpy, clone->src.window, &clone->dri3); 243642542f5fSchristos 243742542f5fSchristos return 0; 243842542f5fSchristos} 243942542f5fSchristos 244042542f5fSchristos#if defined(USE_XINERAMA) 244142542f5fSchristosstatic int xinerama_active(struct display *display) 244242542f5fSchristos{ 244342542f5fSchristos int active = 0; 244442542f5fSchristos if (XineramaQueryExtension(display->dpy, &display->xinerama_event, &display->xinerama_error)) 244542542f5fSchristos active = XineramaIsActive(display->dpy); 244642542f5fSchristos return active; 244742542f5fSchristos} 244842542f5fSchristos#else 244942542f5fSchristos#define xinerama_active(d) 0 245042542f5fSchristos#endif 245142542f5fSchristos 245242542f5fSchristosstatic int add_display(struct context *ctx, Display *dpy) 245342542f5fSchristos{ 245442542f5fSchristos struct display *display; 245542542f5fSchristos int first_display = ctx->ndisplay == 0; 245642542f5fSchristos 245742542f5fSchristos if (is_power_of_2(ctx->ndisplay)) { 245842542f5fSchristos struct display *new_display; 245942542f5fSchristos 246042542f5fSchristos new_display = realloc(ctx->display, 2*ctx->ndisplay*sizeof(struct display)); 246142542f5fSchristos if (new_display == NULL) 246242542f5fSchristos return -ENOMEM; 246342542f5fSchristos 246442542f5fSchristos if (new_display != ctx->display) { 246542542f5fSchristos int n; 246642542f5fSchristos 246742542f5fSchristos for (n = 0; n < ctx->nclone; n++) { 246842542f5fSchristos struct clone *clone = &ctx->clones[n]; 246942542f5fSchristos clone->src.display = new_display + (clone->src.display - ctx->display); 247042542f5fSchristos clone->dst.display = new_display + (clone->dst.display - ctx->display); 247142542f5fSchristos } 247242542f5fSchristos } 247342542f5fSchristos 247442542f5fSchristos ctx->display = new_display; 247542542f5fSchristos } 247642542f5fSchristos 247742542f5fSchristos display = memset(&ctx->display[ctx->ndisplay++], 0, sizeof(struct display)); 247842542f5fSchristos 247942542f5fSchristos display->dpy = dpy; 248042542f5fSchristos display->ctx = ctx; 248142542f5fSchristos 248242542f5fSchristos display->root = DefaultRootWindow(dpy); 248342542f5fSchristos display->depth = DefaultDepth(dpy, DefaultScreen(dpy)); 248442542f5fSchristos display->visual = DefaultVisual(dpy, DefaultScreen(dpy)); 248542542f5fSchristos 2486fe8aea9eSmrg XSelectInput(dpy, display->root, ExposureMask); 2487fe8aea9eSmrg 248842542f5fSchristos display->has_shm = can_use_shm(dpy, display->root, 248942542f5fSchristos &display->shm_event, 249042542f5fSchristos &display->shm_opcode, 249142542f5fSchristos &display->has_shm_pixmap); 249242542f5fSchristos DBG(X11, ("%s: has_shm?=%d, event=%d, opcode=%d, has_pixmap?=%d\n", 249342542f5fSchristos DisplayString(dpy), 249442542f5fSchristos display->has_shm, 249542542f5fSchristos display->shm_event, 249642542f5fSchristos display->shm_opcode, 249742542f5fSchristos display->has_shm_pixmap)); 249842542f5fSchristos 2499fe8aea9eSmrg screensaver_save(display); 2500fe8aea9eSmrg 250142542f5fSchristos display->rr_active = XRRQueryExtension(dpy, &display->rr_event, &display->rr_error); 250242542f5fSchristos DBG(X11, ("%s: randr_active?=%d, event=%d, error=%d\n", 250342542f5fSchristos DisplayString(dpy), 250442542f5fSchristos display->rr_active, 250542542f5fSchristos display->rr_event, 250642542f5fSchristos display->rr_error)); 250742542f5fSchristos 250842542f5fSchristos display->xinerama_active = xinerama_active(display); 250942542f5fSchristos DBG(X11, ("%s: xinerama_active?=%d, event=%d, error=%d\n", 251042542f5fSchristos DisplayString(dpy), 251142542f5fSchristos display->xinerama_active, 251242542f5fSchristos display->xinerama_event, 251342542f5fSchristos display->xinerama_error)); 251442542f5fSchristos 251542542f5fSchristos display->dri3_active = dri3_exists(dpy); 251642542f5fSchristos DBG(X11, ("%s: dri3_active?=%d\n", 251742542f5fSchristos DisplayString(dpy), 251842542f5fSchristos display->dri3_active)); 251942542f5fSchristos 252042542f5fSchristos /* first display (source) is slightly special */ 252142542f5fSchristos if (!first_display) { 252242542f5fSchristos display->invisible_cursor = display_load_invisible_cursor(display); 252342542f5fSchristos display_cursor_move(display, 0, 0, 0); 252442542f5fSchristos } 252542542f5fSchristos 252642542f5fSchristos return ConnectionNumber(dpy); 252742542f5fSchristos} 252842542f5fSchristos 252942542f5fSchristosstatic int display_open(struct context *ctx, const char *name) 253042542f5fSchristos{ 253142542f5fSchristos Display *dpy; 253242542f5fSchristos int n; 253342542f5fSchristos 253442542f5fSchristos DBG(X11, ("%s(%s)\n", __func__, name)); 253542542f5fSchristos 253642542f5fSchristos dpy = XOpenDisplay(name); 253742542f5fSchristos if (dpy == NULL) 253842542f5fSchristos return -ECONNREFUSED; 253942542f5fSchristos 254042542f5fSchristos /* Prevent cloning the same display twice */ 254142542f5fSchristos for (n = 0; n < ctx->ndisplay; n++) { 254242542f5fSchristos if (strcmp(DisplayString(dpy), DisplayString(ctx->display[n].dpy)) == 0) { 254342542f5fSchristos DBG(X11, ("%s %s is already connected\n", __func__, name)); 254442542f5fSchristos XCloseDisplay(dpy); 254542542f5fSchristos return -EBUSY; 254642542f5fSchristos } 254742542f5fSchristos } 254842542f5fSchristos 254942542f5fSchristos return add_display(ctx, dpy); 255042542f5fSchristos} 255142542f5fSchristos 255242542f5fSchristosstatic int bumblebee_open(struct context *ctx) 255342542f5fSchristos{ 255442542f5fSchristos char buf[256]; 255542542f5fSchristos struct sockaddr_un addr; 255642542f5fSchristos int fd, len; 255742542f5fSchristos 255842542f5fSchristos fd = socket(PF_UNIX, SOCK_STREAM, 0); 255942542f5fSchristos if (fd < 0) { 256042542f5fSchristos DBG(X11, ("%s unable to create a socket: %d\n", __func__, errno)); 256142542f5fSchristos return -ECONNREFUSED; 256242542f5fSchristos } 256342542f5fSchristos 256442542f5fSchristos addr.sun_family = AF_UNIX; 256542542f5fSchristos snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", 256642542f5fSchristos optarg && *optarg ? optarg : "/var/run/bumblebee.socket"); 256742542f5fSchristos if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { 256842542f5fSchristos DBG(X11, ("%s unable to create a socket: %d\n", __func__, errno)); 256942542f5fSchristos goto err; 257042542f5fSchristos } 257142542f5fSchristos 257242542f5fSchristos /* Ask bumblebee to start the second server */ 257342542f5fSchristos buf[0] = 'C'; 257442542f5fSchristos if (send(fd, &buf, 1, 0) != 1 || (len = recv(fd, &buf, 255, 0)) <= 0) { 257542542f5fSchristos DBG(X11, ("%s startup send/recv failed: %d\n", __func__, errno)); 257642542f5fSchristos goto err; 257742542f5fSchristos } 257842542f5fSchristos buf[len] = '\0'; 257942542f5fSchristos 258042542f5fSchristos /* Query the display name */ 258142542f5fSchristos strcpy(buf, "Q VirtualDisplay"); 258242542f5fSchristos if (send(fd, buf, 17, 0) != 17 || (len = recv(fd, buf, 255, 0)) <= 0) { 258342542f5fSchristos DBG(X11, ("%s query send/recv failed: %d\n", __func__, errno)); 258442542f5fSchristos goto err; 258542542f5fSchristos } 258642542f5fSchristos buf[len] = '\0'; 258742542f5fSchristos 258842542f5fSchristos DBG(X11, ("%s query result '%s'\n", __func__, buf)); 258942542f5fSchristos 259042542f5fSchristos if (strncmp(buf, "Value: ", 7)) 259142542f5fSchristos goto err; 259242542f5fSchristos 259342542f5fSchristos len = 7; 259442542f5fSchristos while (buf[len] != '\n' && buf[len] != '\0') 259542542f5fSchristos len++; 259642542f5fSchristos buf[len] = '\0'; 259742542f5fSchristos 259842542f5fSchristos /* XXX We must keep the control socket open whilst we want to keep 259942542f5fSchristos * the display around. 260042542f5fSchristos * 260142542f5fSchristos * So what we need to do is listen for new bumblee Xservers and 260242542f5fSchristos * bind only for their duration. 260342542f5fSchristos */ 260442542f5fSchristos 260542542f5fSchristos return display_open(ctx, buf+7); 260642542f5fSchristos 260742542f5fSchristoserr: 260842542f5fSchristos close(fd); 260942542f5fSchristos return -ECONNREFUSED; 261042542f5fSchristos} 261142542f5fSchristos 261242542f5fSchristosstatic int display_init_damage(struct display *display) 261342542f5fSchristos{ 261442542f5fSchristos DBG(X11, ("%s(%s)\n", __func__, DisplayString(display->dpy))); 261542542f5fSchristos 261642542f5fSchristos if (!XDamageQueryExtension(display->dpy, &display->damage_event, &display->damage_error) || 261742542f5fSchristos !XFixesQueryExtension(display->dpy, &display->xfixes_event, &display->xfixes_error)) { 261842542f5fSchristos fprintf(stderr, "Damage/Fixes extension not supported by %s\n", DisplayString(display->dpy)); 261942542f5fSchristos return EINVAL; 262042542f5fSchristos } 262142542f5fSchristos 262242542f5fSchristos display->damage = XDamageCreate(display->dpy, display->root, XDamageReportBoundingBox); 262342542f5fSchristos if (display->damage == 0) 262442542f5fSchristos return EACCES; 262542542f5fSchristos 262642542f5fSchristos return 0; 262742542f5fSchristos} 262842542f5fSchristos 262942542f5fSchristosstatic void display_reset_damage(struct display *display) 263042542f5fSchristos{ 263142542f5fSchristos Damage damage; 263242542f5fSchristos 263342542f5fSchristos damage = XDamageCreate(display->dpy, display->root, XDamageReportBoundingBox); 263442542f5fSchristos if (damage) { 263542542f5fSchristos XDamageDestroy(display->dpy, display->damage); 263642542f5fSchristos display->damage = damage; 263742542f5fSchristos XFlush(display->dpy); 263842542f5fSchristos display->flush = 0; 263942542f5fSchristos } 264042542f5fSchristos} 264142542f5fSchristos 264242542f5fSchristosstatic void display_init_randr_hpd(struct display *display) 264342542f5fSchristos{ 264442542f5fSchristos int major, minor; 264542542f5fSchristos 264642542f5fSchristos DBG(X11,("%s(%s)\n", __func__, DisplayString(display->dpy))); 264742542f5fSchristos 264842542f5fSchristos if (!XRRQueryVersion(display->dpy, &major, &minor)) 264942542f5fSchristos return; 265042542f5fSchristos 265142542f5fSchristos DBG(X11, ("%s - randr version %d.%d\n", DisplayString(display->dpy), major, minor)); 265242542f5fSchristos if (major > 1 || (major == 1 && minor >= 2)) 265342542f5fSchristos XRRSelectInput(display->dpy, display->root, RROutputChangeNotifyMask); 265442542f5fSchristos} 265542542f5fSchristos 265642542f5fSchristosstatic void rebuild_clones(struct context *ctx, struct clone *new_clones) 265742542f5fSchristos{ 265842542f5fSchristos int n, m; 265942542f5fSchristos 266042542f5fSchristos for (n = 1; n < ctx->ndisplay; n++) { 266142542f5fSchristos struct display *d = &ctx->display[n]; 266242542f5fSchristos 266342542f5fSchristos d->clone = NULL; 266442542f5fSchristos for (m = 0; m < ctx->nclone; m++) { 266542542f5fSchristos struct clone *c = &new_clones[m]; 266642542f5fSchristos 266742542f5fSchristos if (c->dst.display != d) 266842542f5fSchristos continue; 266942542f5fSchristos 267042542f5fSchristos c->next = d->clone; 267142542f5fSchristos d->clone = c; 267242542f5fSchristos } 267342542f5fSchristos } 267442542f5fSchristos 267542542f5fSchristos ctx->clones = new_clones; 267642542f5fSchristos} 267742542f5fSchristos 267842542f5fSchristosstatic struct clone *add_clone(struct context *ctx) 267942542f5fSchristos{ 268042542f5fSchristos if (is_power_of_2(ctx->nclone)) { 268142542f5fSchristos struct clone *new_clones; 268242542f5fSchristos 268342542f5fSchristos new_clones = realloc(ctx->clones, 2*ctx->nclone*sizeof(struct clone)); 268442542f5fSchristos if (new_clones == NULL) 268542542f5fSchristos return NULL; 268642542f5fSchristos 268742542f5fSchristos if (new_clones != ctx->clones) 268842542f5fSchristos rebuild_clones(ctx, new_clones); 268942542f5fSchristos } 269042542f5fSchristos 269142542f5fSchristos return memset(&ctx->clones[ctx->nclone++], 0, sizeof(struct clone)); 269242542f5fSchristos} 269342542f5fSchristos 269442542f5fSchristosstatic struct display *last_display(struct context *ctx) 269542542f5fSchristos{ 269642542f5fSchristos return &ctx->display[ctx->ndisplay-1]; 269742542f5fSchristos} 269842542f5fSchristos 269942542f5fSchristosstatic void reverse_clone_list(struct display *display) 270042542f5fSchristos{ 270142542f5fSchristos struct clone *list = NULL; 270242542f5fSchristos 270342542f5fSchristos while (display->clone) { 270442542f5fSchristos struct clone *clone = display->clone; 270542542f5fSchristos display->clone = clone->next; 270642542f5fSchristos clone->next = list; 270742542f5fSchristos list = clone; 270842542f5fSchristos } 270942542f5fSchristos 271042542f5fSchristos display->clone = list; 271142542f5fSchristos} 271242542f5fSchristos 271342542f5fSchristosstatic int last_display_add_clones__randr(struct context *ctx) 271442542f5fSchristos{ 271542542f5fSchristos struct display *display = last_display(ctx); 271642542f5fSchristos XRRScreenResources *res; 271742542f5fSchristos char buf[80]; 271842542f5fSchristos int i, ret; 271942542f5fSchristos 272042542f5fSchristos DBG(X11, ("%s(%s)\n", __func__, DisplayString(display->dpy))); 272142542f5fSchristos 272242542f5fSchristos display_init_randr_hpd(display); 272342542f5fSchristos 272442542f5fSchristos /* Force a probe of outputs on initial connection */ 272542542f5fSchristos res = XRRGetScreenResources(display->dpy, display->root); 272642542f5fSchristos if (res == NULL) 272742542f5fSchristos return -ENOMEM; 272842542f5fSchristos 272942542f5fSchristos DBG(X11, ("%s - noutputs=%d\n", DisplayString(display->dpy), res->noutput)); 273042542f5fSchristos for (i = 0; i < res->noutput; i++) { 273142542f5fSchristos XRROutputInfo *o = XRRGetOutputInfo(display->dpy, res, res->outputs[i]); 273242542f5fSchristos struct clone *clone = add_clone(ctx); 273342542f5fSchristos RROutput id; 273442542f5fSchristos 273542542f5fSchristos if (clone == NULL) 273642542f5fSchristos return -ENOMEM; 273742542f5fSchristos 273842542f5fSchristos clone->depth = 24; 273942542f5fSchristos clone->next = display->clone; 274042542f5fSchristos display->clone = clone; 274142542f5fSchristos 274242542f5fSchristos id = claim_virtual(ctx->display, buf, ctx->nclone); 274342542f5fSchristos if (id == 0) { 274442542f5fSchristos fprintf(stderr, "Failed to find available VirtualHead \"%s\" for \"%s\" on display \"%s\"\n", 274542542f5fSchristos buf, o->name, DisplayString(display->dpy)); 274642542f5fSchristos return -ENOSPC; 274742542f5fSchristos } 274842542f5fSchristos 274942542f5fSchristos ret = clone_output_init(clone, &clone->src, ctx->display, buf, id); 275042542f5fSchristos if (ret) { 275142542f5fSchristos fprintf(stderr, "Failed to add output \"%s\" on display \"%s\"\n", 275242542f5fSchristos buf, DisplayString(ctx->display->dpy)); 275342542f5fSchristos return ret; 275442542f5fSchristos } 275542542f5fSchristos 275642542f5fSchristos ret = clone_output_init(clone, &clone->dst, display, o->name, res->outputs[i]); 275742542f5fSchristos if (ret) { 275842542f5fSchristos fprintf(stderr, "Failed to add output \"%s\" on display \"%s\"\n", 275942542f5fSchristos o->name, DisplayString(display->dpy)); 276042542f5fSchristos return ret; 276142542f5fSchristos } 276242542f5fSchristos 276342542f5fSchristos ret = clone_init_depth(clone); 276442542f5fSchristos if (ret) { 276542542f5fSchristos fprintf(stderr, "Failed to negotiate image format for display \"%s\"\n", 276642542f5fSchristos DisplayString(display->dpy)); 276742542f5fSchristos return ret; 276842542f5fSchristos } 276942542f5fSchristos 2770fe8aea9eSmrg clone->dst.x = 0; 2771fe8aea9eSmrg clone->dst.y = 0; 2772fe8aea9eSmrg clone->dst.width = display->width; 2773fe8aea9eSmrg clone->dst.height = display->height; 2774fe8aea9eSmrg 277542542f5fSchristos ret = clone_update_modes__randr(clone); 277642542f5fSchristos if (ret) { 277742542f5fSchristos fprintf(stderr, "Failed to clone output \"%s\" from display \"%s\"\n", 277842542f5fSchristos o->name, DisplayString(display->dpy)); 277942542f5fSchristos return ret; 278042542f5fSchristos } 278142542f5fSchristos 278242542f5fSchristos 278342542f5fSchristos if (o->crtc) { 278442542f5fSchristos DBG(X11, ("%s - disabling active output\n", DisplayString(display->dpy))); 278542542f5fSchristos disable_crtc(display->dpy, res, o->crtc); 278642542f5fSchristos } 278742542f5fSchristos 278842542f5fSchristos XRRFreeOutputInfo(o); 278942542f5fSchristos } 279042542f5fSchristos XRRFreeScreenResources(res); 279142542f5fSchristos 279242542f5fSchristos reverse_clone_list(display); 279342542f5fSchristos return 0; 279442542f5fSchristos} 279542542f5fSchristos 279642542f5fSchristos#if defined(USE_XINERAMA) 279742542f5fSchristosstatic int last_display_add_clones__xinerama(struct context *ctx) 279842542f5fSchristos{ 279942542f5fSchristos struct display *display = last_display(ctx); 280042542f5fSchristos Display *dpy = display->dpy; 280142542f5fSchristos XineramaScreenInfo *xi; 280242542f5fSchristos char buf[80]; 280342542f5fSchristos int n, count, ret; 280442542f5fSchristos 280542542f5fSchristos DBG(X11, ("%s(%s)\n", __func__, DisplayString(display->dpy))); 280642542f5fSchristos 280742542f5fSchristos count = 0; 280842542f5fSchristos xi = XineramaQueryScreens(dpy, &count); 280942542f5fSchristos for (n = 0; n < count; n++) { 281042542f5fSchristos struct clone *clone = add_clone(ctx); 281142542f5fSchristos RROutput id; 281242542f5fSchristos 281342542f5fSchristos if (clone == NULL) 281442542f5fSchristos return -ENOMEM; 281542542f5fSchristos 281642542f5fSchristos if (xi[n].width == 0 || xi[n].height == 0) 281742542f5fSchristos continue; 281842542f5fSchristos 281942542f5fSchristos clone->depth = 24; 282042542f5fSchristos clone->next = display->clone; 282142542f5fSchristos display->clone = clone; 282242542f5fSchristos 282342542f5fSchristos id = claim_virtual(ctx->display, buf, ctx->nclone); 282442542f5fSchristos if (id == 0) { 282542542f5fSchristos fprintf(stderr, "Failed to find available VirtualHead \"%s\" for Xinerama screen %d on display \"%s\"\n", 282642542f5fSchristos buf, n, DisplayString(dpy)); 282742542f5fSchristos } 282842542f5fSchristos ret = clone_output_init(clone, &clone->src, ctx->display, buf, id); 282942542f5fSchristos if (ret) { 283042542f5fSchristos fprintf(stderr, "Failed to add Xinerama screen %d on display \"%s\"\n", 283142542f5fSchristos n, DisplayString(ctx->display->dpy)); 283242542f5fSchristos return ret; 283342542f5fSchristos } 283442542f5fSchristos 283542542f5fSchristos sprintf(buf, "XINERAMA%d", n); 283642542f5fSchristos ret = clone_output_init(clone, &clone->dst, display, buf, 0); 283742542f5fSchristos if (ret) { 283842542f5fSchristos fprintf(stderr, "Failed to add Xinerama screen %d on display \"%s\"\n", 283942542f5fSchristos n, DisplayString(dpy)); 284042542f5fSchristos return ret; 284142542f5fSchristos } 284242542f5fSchristos 284342542f5fSchristos ret = clone_init_depth(clone); 284442542f5fSchristos if (ret) { 284542542f5fSchristos fprintf(stderr, "Failed to negotiate image format for display \"%s\"\n", 284642542f5fSchristos DisplayString(display->dpy)); 284742542f5fSchristos return ret; 284842542f5fSchristos } 284942542f5fSchristos 285042542f5fSchristos /* Replace the modes on the local VIRTUAL output with the remote Screen */ 2851fe8aea9eSmrg clone->dst.width = xi[n].width; 2852fe8aea9eSmrg clone->dst.height = xi[n].height; 285342542f5fSchristos clone->dst.x = xi[n].x_org; 285442542f5fSchristos clone->dst.y = xi[n].y_org; 285542542f5fSchristos clone->dst.rr_crtc = -1; 285642542f5fSchristos ret = clone_update_modes__fixed(clone); 285742542f5fSchristos if (ret) { 285842542f5fSchristos fprintf(stderr, "Failed to clone Xinerama screen %d from display \"%s\"\n", 285942542f5fSchristos n, DisplayString(display->dpy)); 286042542f5fSchristos return ret; 286142542f5fSchristos } 286242542f5fSchristos 286342542f5fSchristos clone->active = ctx->active; 286442542f5fSchristos ctx->active = clone; 286542542f5fSchristos } 286642542f5fSchristos XFree(xi); 286742542f5fSchristos 286842542f5fSchristos reverse_clone_list(display); 286942542f5fSchristos return 0; 287042542f5fSchristos} 287142542f5fSchristos#else 287242542f5fSchristos#define last_display_add_clones__xinerama(ctx) -1 287342542f5fSchristos#endif 287442542f5fSchristos 287542542f5fSchristosstatic int last_display_add_clones__display(struct context *ctx) 287642542f5fSchristos{ 287742542f5fSchristos struct display *display = last_display(ctx); 287842542f5fSchristos Display *dpy = display->dpy; 287942542f5fSchristos struct clone *clone; 288042542f5fSchristos Screen *scr; 2881fe8aea9eSmrg int count, s; 288242542f5fSchristos char buf[80]; 288342542f5fSchristos int ret; 288442542f5fSchristos RROutput id; 288542542f5fSchristos 2886fe8aea9eSmrg count = ScreenCount(dpy); 2887fe8aea9eSmrg DBG(X11, ("%s(%s) - %d screens\n", __func__, DisplayString(dpy), count)); 2888fe8aea9eSmrg for (s = 0; s < count; s++) { 2889fe8aea9eSmrg clone = add_clone(ctx); 2890fe8aea9eSmrg if (clone == NULL) 2891fe8aea9eSmrg return -ENOMEM; 289242542f5fSchristos 2893fe8aea9eSmrg clone->depth = 24; 2894fe8aea9eSmrg clone->next = display->clone; 2895fe8aea9eSmrg display->clone = clone; 289642542f5fSchristos 2897fe8aea9eSmrg id = claim_virtual(ctx->display, buf, ctx->nclone); 2898fe8aea9eSmrg if (id == 0) { 2899fe8aea9eSmrg fprintf(stderr, "Failed to find available VirtualHead \"%s\" for on display \"%s\"\n", 2900fe8aea9eSmrg buf, DisplayString(dpy)); 2901fe8aea9eSmrg } 2902fe8aea9eSmrg ret = clone_output_init(clone, &clone->src, ctx->display, buf, id); 2903fe8aea9eSmrg if (ret) { 2904fe8aea9eSmrg fprintf(stderr, "Failed to add display \"%s\"\n", 2905fe8aea9eSmrg DisplayString(ctx->display->dpy)); 2906fe8aea9eSmrg return ret; 2907fe8aea9eSmrg } 290842542f5fSchristos 2909fe8aea9eSmrg sprintf(buf, "SCREEN%d", s); 2910fe8aea9eSmrg ret = clone_output_init(clone, &clone->dst, display, buf, 0); 2911fe8aea9eSmrg if (ret) { 2912fe8aea9eSmrg fprintf(stderr, "Failed to add display \"%s\"\n", 2913fe8aea9eSmrg DisplayString(dpy)); 2914fe8aea9eSmrg return ret; 2915fe8aea9eSmrg } 291642542f5fSchristos 2917fe8aea9eSmrg ret = clone_init_depth(clone); 2918fe8aea9eSmrg if (ret) { 2919fe8aea9eSmrg fprintf(stderr, "Failed to negotiate image format for display \"%s\"\n", 2920fe8aea9eSmrg DisplayString(dpy)); 2921fe8aea9eSmrg return ret; 2922fe8aea9eSmrg } 292342542f5fSchristos 2924fe8aea9eSmrg /* Replace the modes on the local VIRTUAL output with the remote Screen */ 2925fe8aea9eSmrg scr = ScreenOfDisplay(dpy, s); 2926fe8aea9eSmrg clone->dst.width = scr->width; 2927fe8aea9eSmrg clone->dst.height = scr->height; 2928fe8aea9eSmrg clone->dst.x = 0; 2929fe8aea9eSmrg clone->dst.y = 0; 2930fe8aea9eSmrg clone->dst.rr_crtc = -1; 2931fe8aea9eSmrg ret = clone_update_modes__fixed(clone); 2932fe8aea9eSmrg if (ret) { 2933fe8aea9eSmrg fprintf(stderr, "Failed to clone display \"%s\"\n", 2934fe8aea9eSmrg DisplayString(dpy)); 2935fe8aea9eSmrg return ret; 2936fe8aea9eSmrg } 293742542f5fSchristos 2938fe8aea9eSmrg clone->active = ctx->active; 2939fe8aea9eSmrg ctx->active = clone; 294042542f5fSchristos } 294142542f5fSchristos 294242542f5fSchristos return 0; 294342542f5fSchristos} 294442542f5fSchristos 294542542f5fSchristosstatic int last_display_add_clones(struct context *ctx) 294642542f5fSchristos{ 294742542f5fSchristos struct display *display = last_display(ctx); 294842542f5fSchristos 294942542f5fSchristos display->width = DisplayWidth(display->dpy, DefaultScreen(display->dpy)); 295042542f5fSchristos display->height = DisplayHeight(display->dpy, DefaultScreen(display->dpy)); 295142542f5fSchristos DBG(X11, ("%s - initial size %dx%d\n", DisplayString(display->dpy), display->width, display->height)); 295242542f5fSchristos 295342542f5fSchristos if (display->rr_active) 295442542f5fSchristos return last_display_add_clones__randr(ctx); 295542542f5fSchristos 295642542f5fSchristos if (display->xinerama_active) 295742542f5fSchristos return last_display_add_clones__xinerama(ctx); 295842542f5fSchristos 295942542f5fSchristos return last_display_add_clones__display(ctx); 296042542f5fSchristos} 296142542f5fSchristos 296242542f5fSchristosstatic int last_display_clone(struct context *ctx, int fd) 296342542f5fSchristos{ 296442542f5fSchristos fd = add_fd(ctx, fd); 296542542f5fSchristos if (fd < 0) 296642542f5fSchristos return fd; 296742542f5fSchristos 296842542f5fSchristos fd = last_display_add_clones(ctx); 296942542f5fSchristos if (fd) 297042542f5fSchristos return fd; 297142542f5fSchristos 297242542f5fSchristos return 0; 297342542f5fSchristos} 297442542f5fSchristos 297542542f5fSchristosstatic int first_display_has_singleton(struct context *ctx) 297642542f5fSchristos{ 297742542f5fSchristos struct display *display = ctx->display; 297842542f5fSchristos unsigned long nitems, bytes; 297942542f5fSchristos unsigned char *prop; 298042542f5fSchristos int format; 298142542f5fSchristos Atom type; 298242542f5fSchristos 298342542f5fSchristos ctx->singleton = XInternAtom(display->dpy, "intel-virtual-output-singleton", False); 298442542f5fSchristos 298542542f5fSchristos XGetWindowProperty(display->dpy, display->root, ctx->singleton, 298642542f5fSchristos 0, 0, 0, AnyPropertyType, &type, &format, &nitems, &bytes, &prop); 298742542f5fSchristos DBG(X11, ("%s: singleton registered? %d\n", DisplayString(display->dpy), type != None)); 298842542f5fSchristos return type != None; 298942542f5fSchristos} 299042542f5fSchristos 299142542f5fSchristosstatic int first_display_wait_for_ack(struct context *ctx, int timeout, int id) 299242542f5fSchristos{ 299342542f5fSchristos struct display *display = ctx->display; 299442542f5fSchristos struct pollfd pfd; 299542542f5fSchristos char expect[6]; /* "1234R\0" */ 299642542f5fSchristos 299742542f5fSchristos sprintf(expect, "%04xR", id); 299842542f5fSchristos DBG(X11, ("%s: wait for act '%c%c%c%c%c'\n", 299942542f5fSchristos DisplayString(display->dpy), 300042542f5fSchristos expect[0], expect[1], expect[2], expect[3], expect[4])); 300142542f5fSchristos 300242542f5fSchristos XFlush(display->dpy); 300342542f5fSchristos 300442542f5fSchristos pfd.fd = ConnectionNumber(display->dpy); 300542542f5fSchristos pfd.events = POLLIN; 300642542f5fSchristos do { 300742542f5fSchristos if (poll(&pfd, 1, timeout) <= 0) 300842542f5fSchristos return -ETIME; 300942542f5fSchristos 301042542f5fSchristos while (XPending(display->dpy)) { 301142542f5fSchristos XEvent e; 301242542f5fSchristos XClientMessageEvent *cme; 301342542f5fSchristos 301442542f5fSchristos XNextEvent(display->dpy, &e); 301542542f5fSchristos DBG(X11, ("%s: reading event type %d\n", DisplayString(display->dpy), e.type)); 301642542f5fSchristos 301742542f5fSchristos if (e.type != ClientMessage) 301842542f5fSchristos continue; 301942542f5fSchristos 302042542f5fSchristos cme = (XClientMessageEvent *)&e; 302142542f5fSchristos if (cme->message_type != ctx->singleton) 302242542f5fSchristos continue; 302342542f5fSchristos if (cme->format != 8) 302442542f5fSchristos continue; 302542542f5fSchristos 302642542f5fSchristos DBG(X11, ("%s: client message '%c%c%c%c%c'\n", 302742542f5fSchristos DisplayString(display->dpy), 302842542f5fSchristos cme->data.b[0], 302942542f5fSchristos cme->data.b[1], 303042542f5fSchristos cme->data.b[2], 303142542f5fSchristos cme->data.b[3], 303242542f5fSchristos cme->data.b[4])); 303342542f5fSchristos if (memcmp(cme->data.b, expect, 5)) 303442542f5fSchristos continue; 303542542f5fSchristos 303642542f5fSchristos return -atoi(cme->data.b + 5); 303742542f5fSchristos } 303842542f5fSchristos } while (1); 303942542f5fSchristos} 304042542f5fSchristos 304142542f5fSchristos#if defined(__GNUC__) && (__GNUC__ > 3) 304242542f5fSchristos__attribute__((format(gnu_printf, 3, 4))) 304342542f5fSchristos#endif 304442542f5fSchristosstatic int first_display_send_command(struct context *ctx, int timeout, 304542542f5fSchristos const char *format, 304642542f5fSchristos ...) 304742542f5fSchristos{ 304842542f5fSchristos struct display *display = ctx->display; 304942542f5fSchristos char buf[1024], *b; 305042542f5fSchristos int len, id; 305142542f5fSchristos va_list va; 305242542f5fSchristos 305342542f5fSchristos id = rand() & 0xffff; 305442542f5fSchristos sprintf(buf, "%04x", id); 305542542f5fSchristos va_start(va, format); 305642542f5fSchristos len = vsnprintf(buf+4, sizeof(buf)-4, format, va)+5; 305742542f5fSchristos va_end(va); 305842542f5fSchristos assert(len < sizeof(buf)); 305942542f5fSchristos 306042542f5fSchristos DBG(X11, ("%s: send command '%s'\n", DisplayString(display->dpy), buf)); 306142542f5fSchristos 306242542f5fSchristos b = buf; 306342542f5fSchristos while (len) { 306442542f5fSchristos XClientMessageEvent msg; 306542542f5fSchristos int n = len; 306642542f5fSchristos if (n > sizeof(msg.data.b)) 306742542f5fSchristos n = sizeof(msg.data.b); 306842542f5fSchristos len -= n; 306942542f5fSchristos 307042542f5fSchristos msg.type = ClientMessage; 307142542f5fSchristos msg.serial = 0; 307242542f5fSchristos msg.message_type = ctx->singleton; 307342542f5fSchristos msg.format = 8; 307442542f5fSchristos memcpy(msg.data.b, b, n); 307542542f5fSchristos b += n; 307642542f5fSchristos 307742542f5fSchristos XSendEvent(display->dpy, display->root, False, PropertyChangeMask, (XEvent *)&msg); 307842542f5fSchristos } 307942542f5fSchristos 308042542f5fSchristos return first_display_wait_for_ack(ctx, timeout, id); 308142542f5fSchristos} 308242542f5fSchristos 308342542f5fSchristosstatic void first_display_reply(struct context *ctx, int result) 308442542f5fSchristos{ 308542542f5fSchristos struct display *display = ctx->display; 308642542f5fSchristos XClientMessageEvent msg; 308742542f5fSchristos 308842542f5fSchristos sprintf(msg.data.b, "%c%c%c%cR%d", 308942542f5fSchristos ctx->command[0], 309042542f5fSchristos ctx->command[1], 309142542f5fSchristos ctx->command[2], 309242542f5fSchristos ctx->command[3], 309342542f5fSchristos -result); 309442542f5fSchristos 309542542f5fSchristos DBG(X11, ("%s: send reply '%s'\n", DisplayString(display->dpy), msg.data.b)); 309642542f5fSchristos 309742542f5fSchristos msg.type = ClientMessage; 309842542f5fSchristos msg.serial = 0; 309942542f5fSchristos msg.message_type = ctx->singleton; 310042542f5fSchristos msg.format = 8; 310142542f5fSchristos 310242542f5fSchristos XSendEvent(display->dpy, display->root, False, PropertyChangeMask, (XEvent *)&msg); 310342542f5fSchristos XFlush(display->dpy); 310442542f5fSchristos} 310542542f5fSchristos 310642542f5fSchristosstatic void first_display_handle_command(struct context *ctx, 310742542f5fSchristos const char *msg) 310842542f5fSchristos{ 310942542f5fSchristos int len; 311042542f5fSchristos 311142542f5fSchristos DBG(X11, ("client message!\n")); 311242542f5fSchristos 311342542f5fSchristos for (len = 0; len < 20 && msg[len]; len++) 311442542f5fSchristos ; 311542542f5fSchristos 311642542f5fSchristos if (ctx->command_continuation + len > sizeof(ctx->command)) { 311742542f5fSchristos ctx->command_continuation = 0; 311842542f5fSchristos return; 311942542f5fSchristos } 312042542f5fSchristos 312142542f5fSchristos memcpy(ctx->command + ctx->command_continuation, msg, len); 312242542f5fSchristos ctx->command_continuation += len; 312342542f5fSchristos 312442542f5fSchristos if (len < 20) { 312542542f5fSchristos ctx->command[ctx->command_continuation] = 0; 312642542f5fSchristos DBG(X11, ("client command complete! '%s'\n", ctx->command)); 312742542f5fSchristos switch (ctx->command[4]) { 312842542f5fSchristos case 'B': 312942542f5fSchristos first_display_reply(ctx, last_display_clone(ctx, bumblebee_open(ctx))); 313042542f5fSchristos break; 313142542f5fSchristos case 'C': 313242542f5fSchristos first_display_reply(ctx, last_display_clone(ctx, display_open(ctx, ctx->command + 5))); 313342542f5fSchristos break; 313442542f5fSchristos case 'P': 313542542f5fSchristos first_display_reply(ctx, 0); 313642542f5fSchristos break; 313742542f5fSchristos case 'R': 313842542f5fSchristos break; 313942542f5fSchristos } 314042542f5fSchristos ctx->command_continuation = 0; 314142542f5fSchristos return; 314242542f5fSchristos } 314342542f5fSchristos} 314442542f5fSchristos 314542542f5fSchristosstatic int first_display_register_as_singleton(struct context *ctx) 314642542f5fSchristos{ 314742542f5fSchristos struct display *display = ctx->display; 314842542f5fSchristos struct pollfd pfd; 314942542f5fSchristos 315042542f5fSchristos XChangeProperty(display->dpy, display->root, ctx->singleton, 315142542f5fSchristos XA_STRING, 8, PropModeReplace, (unsigned char *)".", 1); 315242542f5fSchristos XFlush(display->dpy); 315342542f5fSchristos 315442542f5fSchristos /* And eat the notify (presuming that it is ours!) */ 315542542f5fSchristos 315642542f5fSchristos pfd.fd = ConnectionNumber(display->dpy); 315742542f5fSchristos pfd.events = POLLIN; 315842542f5fSchristos do { 315942542f5fSchristos if (poll(&pfd, 1, 1000) <= 0) { 316042542f5fSchristos fprintf(stderr, "Failed to register as singleton\n"); 316142542f5fSchristos return EBUSY; 316242542f5fSchristos } 316342542f5fSchristos 316442542f5fSchristos while (XPending(display->dpy)) { 316542542f5fSchristos XEvent e; 316642542f5fSchristos 316742542f5fSchristos XNextEvent(display->dpy, &e); 316842542f5fSchristos DBG(X11, ("%s: reading event type %d\n", DisplayString(display->dpy), e.type)); 316942542f5fSchristos 317042542f5fSchristos if (e.type == PropertyNotify && 317142542f5fSchristos ((XPropertyEvent *)&e)->atom == ctx->singleton) 317242542f5fSchristos return 0; 317342542f5fSchristos } 317442542f5fSchristos } while (1); 317542542f5fSchristos} 317642542f5fSchristos 317742542f5fSchristosstatic void display_flush_send(struct display *display) 317842542f5fSchristos{ 317942542f5fSchristos XShmCompletionEvent e; 318042542f5fSchristos 318142542f5fSchristos if (!display->send) 318242542f5fSchristos return; 318342542f5fSchristos 318442542f5fSchristos DBG(X11, ("%s flushing send (serial now %ld) (has shm send? %d)\n", 318542542f5fSchristos DisplayString(display->dpy), 318642542f5fSchristos (long)NextRequest(display->dpy), 318742542f5fSchristos display->shm_event)); 318842542f5fSchristos 318942542f5fSchristos display->send = 0; 319042542f5fSchristos 319142542f5fSchristos if (display->shm_event == 0) { 319242542f5fSchristos XSync(display->dpy, False); 319342542f5fSchristos display->flush = 0; 319442542f5fSchristos return; 319542542f5fSchristos } 319642542f5fSchristos 319742542f5fSchristos memset(&e, 0, sizeof(e)); 319842542f5fSchristos e.type = display->shm_event; 319942542f5fSchristos e.send_event = 1; 320042542f5fSchristos e.drawable = display->root; 320142542f5fSchristos e.major_code = display->shm_opcode; 320242542f5fSchristos e.minor_code = X_ShmPutImage; 320342542f5fSchristos 320442542f5fSchristos XSendEvent(display->dpy, display->root, False, 0, (XEvent *)&e); 320542542f5fSchristos display_mark_flush(display); 320642542f5fSchristos} 320742542f5fSchristos 320842542f5fSchristosstatic void display_sync(struct display *display) 320942542f5fSchristos{ 321042542f5fSchristos if (display->skip_clone == 0) 321142542f5fSchristos return; 321242542f5fSchristos 321342542f5fSchristos if (display->skip_frame++ < 2) 321442542f5fSchristos return; 321542542f5fSchristos 321642542f5fSchristos DBG(X11, ("%s forcing sync\n", DisplayString(display->dpy))); 321742542f5fSchristos XSync(display->dpy, False); 321842542f5fSchristos 321942542f5fSchristos display->flush = 0; 322042542f5fSchristos display->send = 0; 322142542f5fSchristos 322242542f5fSchristos /* Event tracking proven unreliable, disable */ 322342542f5fSchristos display->shm_event = 0; 322442542f5fSchristos} 322542542f5fSchristos 322642542f5fSchristosstatic void display_flush(struct display *display) 322742542f5fSchristos{ 322842542f5fSchristos display_flush_cursor(display); 322942542f5fSchristos display_flush_send(display); 323042542f5fSchristos 323142542f5fSchristos display_sync(display); 323242542f5fSchristos 323342542f5fSchristos if (!display->flush) 323442542f5fSchristos return; 323542542f5fSchristos 323642542f5fSchristos DBG(X11, ("%s(%s)\n", __func__, DisplayString(display->dpy))); 323742542f5fSchristos 323842542f5fSchristos XFlush(display->dpy); 323942542f5fSchristos display->flush = 0; 324042542f5fSchristos} 324142542f5fSchristos 324242542f5fSchristosstatic int first_display_first_sibling(struct context *ctx) 324342542f5fSchristos{ 324442542f5fSchristos const char *str, *colon; 324542542f5fSchristos int dpy, scr, len; 324642542f5fSchristos 324742542f5fSchristos str = DisplayString(ctx->display->dpy); 324842542f5fSchristos colon = strrchr(str, ':'); 324942542f5fSchristos if (colon == NULL) 325042542f5fSchristos return -1; 325142542f5fSchristos 325242542f5fSchristos if (sscanf(colon + 1, "%d.%d", &dpy, &scr) == 1) 325342542f5fSchristos scr = 0; 325442542f5fSchristos 325542542f5fSchristos len = (colon - str) + 1; 325642542f5fSchristos memcpy(ctx->command, str, len); 325742542f5fSchristos len += sprintf(ctx->command + len, "%d.", dpy); 325842542f5fSchristos ctx->command_continuation = len; 325942542f5fSchristos 326042542f5fSchristos return scr + 1; 326142542f5fSchristos} 326242542f5fSchristos 326342542f5fSchristosstatic int first_display_sibling(struct context *ctx, int i) 326442542f5fSchristos{ 326542542f5fSchristos if (i < 0) 326642542f5fSchristos return 0; 326742542f5fSchristos 326842542f5fSchristos sprintf(ctx->command + ctx->command_continuation, "%d", i); 326942542f5fSchristos return 1; 327042542f5fSchristos} 327142542f5fSchristos 327242542f5fSchristos#define first_display_for_each_sibling(CTX, i) \ 327342542f5fSchristos for (i = first_display_first_sibling(CTX); first_display_sibling(CTX, i); i++) 327442542f5fSchristos 327542542f5fSchristosstatic void display_cleanup(struct display *display) 327642542f5fSchristos{ 327742542f5fSchristos Display *dpy = display->dpy; 327842542f5fSchristos XRRScreenResources *res; 327942542f5fSchristos int n; 328042542f5fSchristos 328142542f5fSchristos XGrabServer(dpy); 328242542f5fSchristos 328342542f5fSchristos res = _XRRGetScreenResourcesCurrent(dpy, display->root); 328442542f5fSchristos if (res != NULL) { 328542542f5fSchristos for (n = 0; n < res->ncrtc; n++) 328642542f5fSchristos disable_crtc(display->dpy, res, res->crtcs[n]); 328742542f5fSchristos 328842542f5fSchristos XRRFreeScreenResources(res); 328942542f5fSchristos } 329042542f5fSchristos 329142542f5fSchristos XUngrabServer(dpy); 329242542f5fSchristos} 329342542f5fSchristos 329442542f5fSchristosstatic void context_cleanup(struct context *ctx) 329542542f5fSchristos{ 329642542f5fSchristos Display *dpy = ctx->display->dpy; 329742542f5fSchristos XRRScreenResources *res; 329842542f5fSchristos int i, j; 329942542f5fSchristos 330042542f5fSchristos for (i = 1; i < ctx->ndisplay; i++) 330142542f5fSchristos display_cleanup(&ctx->display[i]); 330242542f5fSchristos 330313496ba1Ssnj if (dpy == NULL) 330413496ba1Ssnj return; 330513496ba1Ssnj 330642542f5fSchristos res = _XRRGetScreenResourcesCurrent(dpy, ctx->display->root); 330742542f5fSchristos if (res == NULL) 330842542f5fSchristos return; 330942542f5fSchristos 331042542f5fSchristos XGrabServer(dpy); 331142542f5fSchristos 331242542f5fSchristos for (i = 0; i < ctx->nclone; i++) { 331342542f5fSchristos struct clone *clone = &ctx->clones[i]; 331442542f5fSchristos XRROutputInfo *output; 331542542f5fSchristos 331642542f5fSchristos assert(clone->src.display == ctx->display); 331742542f5fSchristos 331842542f5fSchristos output = XRRGetOutputInfo(dpy, res, clone->src.rr_output); 331942542f5fSchristos if (output == NULL) 332042542f5fSchristos continue; 332142542f5fSchristos 332242542f5fSchristos disable_crtc(dpy, res, output->crtc); 332342542f5fSchristos for (j = 0; j < output->nmode; j++) 332442542f5fSchristos XRRDeleteOutputMode(dpy, clone->src.rr_output, output->modes[j]); 332542542f5fSchristos 332642542f5fSchristos XRRFreeOutputInfo(output); 332742542f5fSchristos } 332842542f5fSchristos 332942542f5fSchristos for (i = 0; i < res->nmode; i++) { 333042542f5fSchristos if (strncmp(res->modes[i].name, "VIRTUAL", 7) == 0) { 333142542f5fSchristos XRRDestroyMode(dpy, res->modes[i].id); 333242542f5fSchristos continue; 333342542f5fSchristos } 333442542f5fSchristos 333542542f5fSchristos if (strcmp(res->modes[i].name, "ClaimVirtualHead") == 0) { 333642542f5fSchristos XRRDestroyMode(dpy, res->modes[i].id); 333742542f5fSchristos continue; 333842542f5fSchristos } 333942542f5fSchristos } 334042542f5fSchristos XRRFreeScreenResources(res); 334142542f5fSchristos 334242542f5fSchristos /* And hide them again */ 334342542f5fSchristos res = XRRGetScreenResources(dpy, ctx->display->root); 334442542f5fSchristos if (res != NULL) 334542542f5fSchristos XRRFreeScreenResources(res); 334642542f5fSchristos 334742542f5fSchristos XUngrabServer(dpy); 334842542f5fSchristos 334942542f5fSchristos if (ctx->singleton) 335042542f5fSchristos XDeleteProperty(dpy, ctx->display->root, ctx->singleton); 335142542f5fSchristos XCloseDisplay(dpy); 335242542f5fSchristos} 335342542f5fSchristos 3354fe8aea9eSmrgstatic void update_cursor_image(struct context *ctx) 3355fe8aea9eSmrg{ 3356fe8aea9eSmrg XFixesCursorImage *cur; 3357fe8aea9eSmrg int i; 3358fe8aea9eSmrg 3359fe8aea9eSmrg DBG(CURSOR, ("%s cursor changed\n", 3360fe8aea9eSmrg DisplayString(ctx->display->dpy))); 3361fe8aea9eSmrg 3362fe8aea9eSmrg cur = XFixesGetCursorImage(ctx->display->dpy); 3363fe8aea9eSmrg if (cur == NULL) 3364fe8aea9eSmrg return; 3365fe8aea9eSmrg 3366fe8aea9eSmrg display_load_visible_cursor(&ctx->display[0], cur); 3367fe8aea9eSmrg for (i = 1; i < ctx->ndisplay; i++) { 3368fe8aea9eSmrg struct display *display = &ctx->display[i]; 3369fe8aea9eSmrg 3370fe8aea9eSmrg DBG(CURSOR, ("%s marking cursor changed\n", DisplayString(display->dpy))); 3371fe8aea9eSmrg display->cursor_moved++; 3372fe8aea9eSmrg if (display->cursor != display->invisible_cursor) { 3373fe8aea9eSmrg display->cursor_visible++; 3374fe8aea9eSmrg context_enable_timer(display->ctx); 3375fe8aea9eSmrg } 3376fe8aea9eSmrg } 3377fe8aea9eSmrg 3378fe8aea9eSmrg XFree(cur); 3379fe8aea9eSmrg} 3380fe8aea9eSmrg 338142542f5fSchristosstatic int done; 338242542f5fSchristos 338342542f5fSchristosstatic void signal_handler(int sig) 338442542f5fSchristos{ 338542542f5fSchristos done = sig; 338642542f5fSchristos} 338742542f5fSchristos 338842542f5fSchristosint main(int argc, char **argv) 338942542f5fSchristos{ 339042542f5fSchristos struct context ctx; 339142542f5fSchristos const char *src_name = NULL; 339242542f5fSchristos uint64_t count; 339342542f5fSchristos int daemonize = 1, bumblebee = 0, siblings = 0, singleton = 1; 339442542f5fSchristos int i, ret, open, fail; 3395fe8aea9eSmrg int idle; 339642542f5fSchristos 339742542f5fSchristos signal(SIGPIPE, SIG_IGN); 339842542f5fSchristos 339942542f5fSchristos while ((i = getopt(argc, argv, "abd:fhSvV:")) != -1) { 340042542f5fSchristos switch (i) { 340142542f5fSchristos case 'd': 340242542f5fSchristos src_name = optarg; 340342542f5fSchristos break; 340442542f5fSchristos case 'f': 340542542f5fSchristos daemonize = 0; 340642542f5fSchristos break; 340742542f5fSchristos case 'b': 340842542f5fSchristos bumblebee = 1; 340942542f5fSchristos break; 341042542f5fSchristos case 's': 341142542f5fSchristos siblings = 1; 341242542f5fSchristos break; 341342542f5fSchristos case 'S': 341442542f5fSchristos singleton = 0; 341542542f5fSchristos break; 341642542f5fSchristos case 'v': 341742542f5fSchristos verbose = ~0; 341842542f5fSchristos daemonize = 0; 341942542f5fSchristos break; 342042542f5fSchristos case 'V': 342142542f5fSchristos verbose = strtol(optarg, NULL, 0); 342242542f5fSchristos daemonize = 0; 342342542f5fSchristos break; 342442542f5fSchristos case 'h': 342542542f5fSchristos default: 342642542f5fSchristos usage(argv[0]); 342742542f5fSchristos exit(0); 342842542f5fSchristos } 342942542f5fSchristos } 343042542f5fSchristos 343113496ba1Ssnj if (verbose) 343213496ba1Ssnj printf("intel-virtual-output: version %d.%d.%d\n", 343313496ba1Ssnj PACKAGE_VERSION_MAJOR, 343413496ba1Ssnj PACKAGE_VERSION_MINOR, 343513496ba1Ssnj PACKAGE_VERSION_PATCHLEVEL); 343613496ba1Ssnj 343742542f5fSchristos ret = context_init(&ctx); 343842542f5fSchristos if (ret) 343942542f5fSchristos return -ret; 344042542f5fSchristos 344142542f5fSchristos XSetErrorHandler(_check_error_handler); 3442fe8aea9eSmrg XSetIOErrorHandler(_io_error_handler); 344342542f5fSchristos 344442542f5fSchristos ret = add_fd(&ctx, display_open(&ctx, src_name)); 344542542f5fSchristos if (ret) { 344642542f5fSchristos fprintf(stderr, "Unable to connect to \"%s\".\n", src_name ?: getenv("DISPLAY") ?: 344742542f5fSchristos "<unspecified>, set either the DISPLAY environment variable or pass -d <display name> on the commandline"); 344842542f5fSchristos ret = -ret; 344942542f5fSchristos goto out; 345042542f5fSchristos } 345142542f5fSchristos 3452fe8aea9eSmrg ret = check_virtual(ctx.display); 3453fe8aea9eSmrg if (ret) { 3454fe8aea9eSmrg fprintf(stderr, "No VIRTUAL outputs on \"%s\".\n", 3455fe8aea9eSmrg DisplayString(ctx.display->dpy)); 3456fe8aea9eSmrg goto out; 3457fe8aea9eSmrg } 3458fe8aea9eSmrg 345942542f5fSchristos if (singleton) { 346042542f5fSchristos XSelectInput(ctx.display->dpy, ctx.display->root, PropertyChangeMask); 346142542f5fSchristos if (first_display_has_singleton(&ctx)) { 346242542f5fSchristos DBG(X11, ("%s: pinging singleton\n", DisplayString(ctx.display->dpy))); 346342542f5fSchristos ret = first_display_send_command(&ctx, 2000, "P"); 346442542f5fSchristos if (ret) { 346542542f5fSchristos if (ret != -ETIME) { 346642542f5fSchristos ret = -ret; 346742542f5fSchristos goto out; 346842542f5fSchristos } 346942542f5fSchristos DBG(X11, ("No reply from singleton; assuming control\n")); 347042542f5fSchristos } else { 347142542f5fSchristos DBG(X11, ("%s: singleton active, sending open commands\n", DisplayString(ctx.display->dpy))); 347242542f5fSchristos 347342542f5fSchristos open = fail = 0; 347442542f5fSchristos for (i = optind; i < argc; i++) { 347542542f5fSchristos ret = first_display_send_command(&ctx, 5000, "C%s", argv[i]); 347642542f5fSchristos if (ret && ret != -EBUSY) { 347742542f5fSchristos fprintf(stderr, "Unable to connect to \"%s\".\n", argv[i]); 347842542f5fSchristos fail++; 347942542f5fSchristos } else 348042542f5fSchristos open++; 348142542f5fSchristos } 348242542f5fSchristos if (siblings || (optind == argc && !bumblebee)) { 348342542f5fSchristos first_display_for_each_sibling(&ctx, i) { 348442542f5fSchristos ret = first_display_send_command(&ctx, 5000, "C%s", ctx.command); 348542542f5fSchristos if (ret && ret != -EBUSY) 348642542f5fSchristos break; 348742542f5fSchristos else 348842542f5fSchristos open++; 348942542f5fSchristos } 349042542f5fSchristos } 349142542f5fSchristos if (bumblebee || (optind == argc && !siblings)) { 349242542f5fSchristos ret = first_display_send_command(&ctx, 5000, "B"); 349342542f5fSchristos if (ret && ret != -EBUSY) { 349442542f5fSchristos if (bumblebee) 349542542f5fSchristos fprintf(stderr, "Unable to connect to bumblebee.\n"); 349642542f5fSchristos fail++; 349742542f5fSchristos } else 349842542f5fSchristos open++; 349942542f5fSchristos } 350042542f5fSchristos ret = open || !fail ? 0 : ECONNREFUSED; 350142542f5fSchristos goto out; 350242542f5fSchristos } 350342542f5fSchristos } 350442542f5fSchristos ret = first_display_register_as_singleton(&ctx); 350542542f5fSchristos if (ret) 350642542f5fSchristos goto out; 350742542f5fSchristos } 350842542f5fSchristos 350942542f5fSchristos ret = display_init_damage(ctx.display); 351042542f5fSchristos if (ret) 351142542f5fSchristos goto out; 351242542f5fSchristos 3513fe8aea9eSmrg if (ctx.display->saver_active) 3514fe8aea9eSmrg XScreenSaverSelectInput(ctx.display->dpy, 3515fe8aea9eSmrg ctx.display->root, 3516fe8aea9eSmrg ScreenSaverNotifyMask); 3517fe8aea9eSmrg 351842542f5fSchristos if ((ctx.display->rr_event | ctx.display->rr_error) == 0) { 351942542f5fSchristos fprintf(stderr, "RandR extension not supported by %s\n", DisplayString(ctx.display->dpy)); 352042542f5fSchristos ret = EINVAL; 352142542f5fSchristos goto out; 352242542f5fSchristos } 352342542f5fSchristos XRRSelectInput(ctx.display->dpy, ctx.display->root, RRScreenChangeNotifyMask); 352442542f5fSchristos XFixesSelectCursorInput(ctx.display->dpy, ctx.display->root, XFixesDisplayCursorNotifyMask); 352542542f5fSchristos 352642542f5fSchristos ret = add_fd(&ctx, record_mouse(&ctx)); 352742542f5fSchristos if (ret) { 352842542f5fSchristos fprintf(stderr, "XTEST extension not supported by display \"%s\"\n", DisplayString(ctx.display->dpy)); 352942542f5fSchristos ret = -ret; 353042542f5fSchristos goto out; 353142542f5fSchristos } 353242542f5fSchristos 353342542f5fSchristos open = fail = 0; 353442542f5fSchristos for (i = optind; i < argc; i++) { 353542542f5fSchristos ret = last_display_clone(&ctx, display_open(&ctx, argv[i])); 353642542f5fSchristos if (ret && ret != -EBUSY) { 353742542f5fSchristos fprintf(stderr, "Unable to connect to \"%s\".\n", argv[i]); 353842542f5fSchristos fail++; 353942542f5fSchristos } else 354042542f5fSchristos open++; 354142542f5fSchristos } 354242542f5fSchristos if (siblings || (optind == argc && !bumblebee)) { 354342542f5fSchristos first_display_for_each_sibling(&ctx, i) { 354442542f5fSchristos ret = last_display_clone(&ctx, display_open(&ctx, ctx.command)); 354542542f5fSchristos if (ret && ret != -EBUSY) 354642542f5fSchristos break; 354742542f5fSchristos else 354842542f5fSchristos open++; 354942542f5fSchristos } 355042542f5fSchristos } 355142542f5fSchristos if (bumblebee || (optind == argc && !siblings)) { 355242542f5fSchristos ret = last_display_clone(&ctx, bumblebee_open(&ctx)); 355342542f5fSchristos if (ret && ret != -EBUSY) { 355442542f5fSchristos if (bumblebee) 355542542f5fSchristos fprintf(stderr, "Unable to connect to bumblebee.\n"); 355642542f5fSchristos fail++; 355742542f5fSchristos } else 355842542f5fSchristos open++; 355942542f5fSchristos } 356042542f5fSchristos if (open == 0) { 356142542f5fSchristos ret = fail ? ECONNREFUSED : 0; 356242542f5fSchristos goto out; 356342542f5fSchristos } 356442542f5fSchristos 356542542f5fSchristos if (daemonize && daemon(0, 0)) { 356642542f5fSchristos ret = EINVAL; 356742542f5fSchristos goto out; 356842542f5fSchristos } 356942542f5fSchristos 357042542f5fSchristos signal(SIGHUP, signal_handler); 357142542f5fSchristos signal(SIGINT, signal_handler); 357242542f5fSchristos signal(SIGTERM, signal_handler); 357342542f5fSchristos 357442542f5fSchristos ctx.command_continuation = 0; 3575fe8aea9eSmrg update_cursor_image(&ctx); 3576fe8aea9eSmrg 3577fe8aea9eSmrg idle = 0; 357842542f5fSchristos while (!done) { 357942542f5fSchristos XEvent e; 358042542f5fSchristos int reconfigure = 0; 358142542f5fSchristos int rr_update = 0; 358242542f5fSchristos 3583fe8aea9eSmrg if (idle) { 3584fe8aea9eSmrg DBG(POLL, ("polling - enable timer? %d, nfd=%d, ndisplay=%d\n", ctx.timer_active, ctx.nfd, ctx.ndisplay)); 3585fe8aea9eSmrg ret = poll(ctx.pfd + !ctx.timer_active, ctx.nfd - !ctx.timer_active, -1); 3586fe8aea9eSmrg if (ret <= 0) 3587fe8aea9eSmrg break; 3588fe8aea9eSmrg 3589fe8aea9eSmrg DBG(POLL, ("poll reports %d fd awake\n", ret)); 3590fe8aea9eSmrg } 3591fe8aea9eSmrg idle = 1; 359242542f5fSchristos 359342542f5fSchristos /* pfd[0] is the timer, pfd[1] is the local display, pfd[2] is the mouse, pfd[3+] are the remotes */ 359442542f5fSchristos 359542542f5fSchristos if (ctx.pfd[1].revents || XPending(ctx.display[0].dpy)) { 359642542f5fSchristos DBG(POLL,("%s woken up\n", DisplayString(ctx.display[0].dpy))); 3597fe8aea9eSmrg ctx.pfd[1].revents = 0; 3598fe8aea9eSmrg idle = 0; 3599fe8aea9eSmrg 360042542f5fSchristos do { 360142542f5fSchristos XNextEvent(ctx.display->dpy, &e); 360242542f5fSchristos 3603fe8aea9eSmrg DBG(POLL, ("%s received event %d\n", DisplayString(ctx.display[0].dpy), e.type)); 3604fe8aea9eSmrg 3605fe8aea9eSmrg if (e.type == ctx.display->saver_event + ScreenSaverNotify) { 3606fe8aea9eSmrg const XScreenSaverNotifyEvent *se = (const XScreenSaverNotifyEvent *)&e; 3607fe8aea9eSmrg DBG(SCREEN, 3608fe8aea9eSmrg ("%s screen saver: state=%d, kind=%d, forced=%d\n", 3609fe8aea9eSmrg DisplayString(ctx.display->dpy), 3610fe8aea9eSmrg se->state, se->kind, se->forced)); 3611fe8aea9eSmrg for (i = 1; i < ctx.ndisplay; i++) { 3612fe8aea9eSmrg struct display *display = &ctx.display[i]; 3613fe8aea9eSmrg 3614fe8aea9eSmrg if (!display->active) 3615fe8aea9eSmrg continue; 3616fe8aea9eSmrg 3617fe8aea9eSmrg DBG(SCREEN, 3618fe8aea9eSmrg ("%s %s screen saver\n", 3619fe8aea9eSmrg DisplayString(display->dpy), 3620fe8aea9eSmrg se->state == ScreenSaverOn ? "activating" : "resetting\n")); 3621fe8aea9eSmrg 3622fe8aea9eSmrg if (se->state == ScreenSaverOn) 3623fe8aea9eSmrg XActivateScreenSaver(display->dpy); 3624fe8aea9eSmrg else 3625fe8aea9eSmrg XResetScreenSaver(display->dpy); 3626fe8aea9eSmrg XFlush(display->dpy); 3627fe8aea9eSmrg } 3628fe8aea9eSmrg } else if (e.type == ctx.display->damage_event + XDamageNotify) { 362942542f5fSchristos const XDamageNotifyEvent *de = (const XDamageNotifyEvent *)&e; 363042542f5fSchristos struct clone *clone; 363142542f5fSchristos 363242542f5fSchristos DBG(DAMAGE, ("%s damaged: (%d, %d)x(%d, %d)\n", 363342542f5fSchristos DisplayString(ctx.display->dpy), 363442542f5fSchristos de->area.x, de->area.y, de->area.width, de->area.height)); 363542542f5fSchristos 363642542f5fSchristos for (clone = ctx.active; clone; clone = clone->active) 363742542f5fSchristos clone_damage(clone, &de->area); 363842542f5fSchristos 363942542f5fSchristos if (ctx.active) 364042542f5fSchristos context_enable_timer(&ctx); 364142542f5fSchristos } else if (e.type == ctx.display->xfixes_event + XFixesCursorNotify) { 3642fe8aea9eSmrg update_cursor_image(&ctx); 364342542f5fSchristos } else if (e.type == ctx.display->rr_event + RRScreenChangeNotify) { 364442542f5fSchristos DBG(XRR, ("%s screen changed (reconfigure pending? %d)\n", 364542542f5fSchristos DisplayString(ctx.display->dpy), reconfigure)); 364642542f5fSchristos reconfigure = 1; 364742542f5fSchristos } else if (e.type == PropertyNotify) { 364842542f5fSchristos XPropertyEvent *pe = (XPropertyEvent *)&e; 364942542f5fSchristos if (pe->atom == ctx.singleton) { 365042542f5fSchristos DBG(X11, ("lost control of singleton\n")); 365142542f5fSchristos return 0; 365242542f5fSchristos } 365342542f5fSchristos } else if (e.type == ClientMessage) { 365442542f5fSchristos XClientMessageEvent *cme; 365542542f5fSchristos 365642542f5fSchristos DBG(X11, ("%s client message\n", 365742542f5fSchristos DisplayString(ctx.display->dpy))); 365842542f5fSchristos 365942542f5fSchristos cme = (XClientMessageEvent *)&e; 366042542f5fSchristos if (cme->message_type != ctx.singleton) 366142542f5fSchristos continue; 366242542f5fSchristos if (cme->format != 8) 366342542f5fSchristos continue; 366442542f5fSchristos 366542542f5fSchristos first_display_handle_command(&ctx, cme->data.b); 366642542f5fSchristos } else { 366742542f5fSchristos DBG(X11, ("unknown event %d\n", e.type)); 366842542f5fSchristos } 366942542f5fSchristos } while (XEventsQueued(ctx.display->dpy, QueuedAfterReading)); 367042542f5fSchristos } 367142542f5fSchristos 367242542f5fSchristos for (i = 1; i < ctx.ndisplay; i++) { 367342542f5fSchristos if (ctx.pfd[i+2].revents == 0 && !XPending(ctx.display[i].dpy)) 367442542f5fSchristos continue; 367542542f5fSchristos 3676fe8aea9eSmrg ctx.pfd[i+2].revents = 0; 3677fe8aea9eSmrg idle = 0; 3678fe8aea9eSmrg 367942542f5fSchristos DBG(POLL, ("%s woken up\n", DisplayString(ctx.display[i].dpy))); 368042542f5fSchristos do { 368142542f5fSchristos XNextEvent(ctx.display[i].dpy, &e); 368242542f5fSchristos 368342542f5fSchristos DBG(POLL, ("%s received event %d\n", DisplayString(ctx.display[i].dpy), e.type)); 3684fe8aea9eSmrg if (e.type == Expose) { 3685fe8aea9eSmrg const XExposeEvent *xe = (XExposeEvent *)&e; 3686fe8aea9eSmrg struct clone *clone; 3687fe8aea9eSmrg int damaged = 0; 3688fe8aea9eSmrg 3689fe8aea9eSmrg DBG(DAMAGE, ("%s exposed: (%d, %d)x(%d, %d)\n", 3690fe8aea9eSmrg DisplayString(ctx.display[i].dpy), 3691fe8aea9eSmrg xe->x, xe->y, xe->width, xe->height)); 3692fe8aea9eSmrg 3693fe8aea9eSmrg for (clone = ctx.active; clone; clone = clone->active) { 3694fe8aea9eSmrg XRectangle r; 3695fe8aea9eSmrg 3696fe8aea9eSmrg if (clone->dst.display != &ctx.display[i]) 3697fe8aea9eSmrg continue; 3698fe8aea9eSmrg 3699fe8aea9eSmrg r.x = clone->src.x + xe->x; 3700fe8aea9eSmrg r.y = clone->src.y + xe->y; 3701fe8aea9eSmrg r.width = xe->width; 3702fe8aea9eSmrg r.height = xe->height; 3703fe8aea9eSmrg clone_damage(clone, &r); 3704fe8aea9eSmrg damaged++; 3705fe8aea9eSmrg } 3706fe8aea9eSmrg 3707fe8aea9eSmrg if (damaged) 3708fe8aea9eSmrg context_enable_timer(&ctx); 3709fe8aea9eSmrg } else if (ctx.display[i].rr_active && e.type == ctx.display[i].rr_event + RRNotify) { 3710fe8aea9eSmrg const XRRNotifyEvent *re = (XRRNotifyEvent *)&e; 371142542f5fSchristos 371242542f5fSchristos DBG(XRR, ("%s received RRNotify, type %d\n", DisplayString(ctx.display[i].dpy), re->subtype)); 371342542f5fSchristos if (re->subtype == RRNotify_OutputChange) { 371442542f5fSchristos XRROutputPropertyNotifyEvent *ro = (XRROutputPropertyNotifyEvent *)re; 371542542f5fSchristos struct clone *clone; 371642542f5fSchristos 371742542f5fSchristos DBG(XRR, ("%s RRNotify_OutputChange, timestamp %ld\n", DisplayString(ctx.display[i].dpy), ro->timestamp)); 371842542f5fSchristos for (clone = ctx.display[i].clone; clone; clone = clone->next) { 371942542f5fSchristos if (clone->dst.rr_output == ro->output) 372042542f5fSchristos rr_update = clone->rr_update = 1; 372142542f5fSchristos } 372242542f5fSchristos } 372342542f5fSchristos } 372442542f5fSchristos } while (XEventsQueued(ctx.display[i].dpy, QueuedAfterReading)); 372542542f5fSchristos } 372642542f5fSchristos 372742542f5fSchristos if (rr_update) { 372842542f5fSchristos for (i = 0; i < ctx.nclone; i++) 372942542f5fSchristos clone_update(&ctx.clones[i]); 373042542f5fSchristos } 373142542f5fSchristos 373242542f5fSchristos if (reconfigure && context_update(&ctx)) 373342542f5fSchristos display_reset_damage(ctx.display); 373442542f5fSchristos 373542542f5fSchristos while (XPending(ctx.record)) /* discard all implicit events */ 373642542f5fSchristos XNextEvent(ctx.record, &e); 373742542f5fSchristos 373842542f5fSchristos if (ctx.timer_active && read(ctx.timer, &count, sizeof(count)) > 0) { 373942542f5fSchristos struct clone *clone; 374042542f5fSchristos 374142542f5fSchristos DBG(TIMER, ("%s timer expired (count=%ld)\n", DisplayString(ctx.display->dpy), (long)count)); 374242542f5fSchristos ret = 0; 374342542f5fSchristos 374442542f5fSchristos if (ctx.active) { 374542542f5fSchristos DBG(DAMAGE, ("%s clearing damage\n", DisplayString(ctx.display->dpy))); 374642542f5fSchristos XDamageSubtract(ctx.display->dpy, ctx.display->damage, None, None); 374742542f5fSchristos ctx.display->flush = 1; 374842542f5fSchristos } 374942542f5fSchristos 375042542f5fSchristos for (clone = ctx.active; clone; clone = clone->active) 375142542f5fSchristos ret |= clone_paint(clone); 375242542f5fSchristos 375342542f5fSchristos for (i = 0; i < ctx.ndisplay; i++) 375442542f5fSchristos display_flush(&ctx.display[i]); 375542542f5fSchristos 375642542f5fSchristos DBG(TIMER, ("%s timer still active? %d\n", DisplayString(ctx.display->dpy), ret != 0)); 375742542f5fSchristos ctx.timer_active = ret != 0; 3758fe8aea9eSmrg idle = 0; 375942542f5fSchristos } 376042542f5fSchristos } 376142542f5fSchristos 376242542f5fSchristos ret = 0; 376342542f5fSchristosout: 376442542f5fSchristos context_cleanup(&ctx); 376542542f5fSchristos return ret; 376642542f5fSchristos} 3767