123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983 |
- /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright (C) 2004-2008 Constantin Kaplinsky. All Rights Reserved.
- * Copyright 2017 Peter Astrand <astrand@cendio.se> for Cendio AB
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
- #ifdef HAVE_CONFIG_H
- #include <config.h>
- #endif
-
- #include <assert.h>
- #include <signal.h>
- #include <unistd.h>
-
- #include <rfb/LogWriter.h>
-
- #include <x0vncserver/XDesktop.h>
-
- #include <X11/XKBlib.h>
- #include <X11/Xutil.h>
- #ifdef HAVE_XTEST
- #include <X11/extensions/XTest.h>
- #endif
- #ifdef HAVE_XDAMAGE
- #include <X11/extensions/Xdamage.h>
- #endif
- #ifdef HAVE_XFIXES
- #include <X11/extensions/Xfixes.h>
- #endif
- #ifdef HAVE_XRANDR
- #include <X11/extensions/Xrandr.h>
- #include <RandrGlue.h>
- extern "C" {
- void vncSetGlueContext(Display *dpy, void *res);
- }
- #endif
- #include <x0vncserver/Geometry.h>
- #include <x0vncserver/XPixelBuffer.h>
-
- using namespace std;
- using namespace rfb;
-
- extern const unsigned short code_map_qnum_to_xorgevdev[];
- extern const unsigned int code_map_qnum_to_xorgevdev_len;
-
- extern const unsigned short code_map_qnum_to_xorgkbd[];
- extern const unsigned int code_map_qnum_to_xorgkbd_len;
-
- BoolParameter useShm("UseSHM", "Use MIT-SHM extension if available", true);
- BoolParameter rawKeyboard("RawKeyboard",
- "Send keyboard events straight through and "
- "avoid mapping them to the current keyboard "
- "layout", false);
- IntParameter queryConnectTimeout("QueryConnectTimeout",
- "Number of seconds to show the Accept Connection dialog before "
- "rejecting the connection",
- 10);
-
- static rfb::LogWriter vlog("XDesktop");
-
- // order is important as it must match RFB extension
- static const char * ledNames[XDESKTOP_N_LEDS] = {
- "Scroll Lock", "Num Lock", "Caps Lock"
- };
-
- XDesktop::XDesktop(Display* dpy_, Geometry *geometry_)
- : dpy(dpy_), geometry(geometry_), pb(0), server(0),
- queryConnectDialog(0), queryConnectSock(0),
- oldButtonMask(0), haveXtest(false), haveDamage(false),
- maxButtons(0), running(false), ledMasks(), ledState(0),
- codeMap(0), codeMapLen(0)
- {
- int major, minor;
-
- int xkbOpcode, xkbErrorBase;
-
- major = XkbMajorVersion;
- minor = XkbMinorVersion;
- if (!XkbQueryExtension(dpy, &xkbOpcode, &xkbEventBase,
- &xkbErrorBase, &major, &minor)) {
- vlog.error("XKEYBOARD extension not present");
- throw Exception();
- }
-
- XkbSelectEvents(dpy, XkbUseCoreKbd, XkbIndicatorStateNotifyMask,
- XkbIndicatorStateNotifyMask);
-
- // figure out bit masks for the indicators we are interested in
- for (int i = 0; i < XDESKTOP_N_LEDS; i++) {
- Atom a;
- int shift;
- Bool on;
-
- a = XInternAtom(dpy, ledNames[i], True);
- if (!a || !XkbGetNamedIndicator(dpy, a, &shift, &on, NULL, NULL))
- continue;
-
- ledMasks[i] = 1u << shift;
- vlog.debug("Mask for '%s' is 0x%x", ledNames[i], ledMasks[i]);
- if (on)
- ledState |= 1u << i;
- }
-
- // X11 unfortunately uses keyboard driver specific keycodes and provides no
- // direct way to query this, so guess based on the keyboard mapping
- XkbDescPtr desc = XkbGetKeyboard(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
- if (desc && desc->names) {
- char *keycodes = XGetAtomName(dpy, desc->names->keycodes);
-
- if (keycodes) {
- if (strncmp("evdev", keycodes, strlen("evdev")) == 0) {
- codeMap = code_map_qnum_to_xorgevdev;
- codeMapLen = code_map_qnum_to_xorgevdev_len;
- vlog.info("Using evdev codemap\n");
- } else if (strncmp("xfree86", keycodes, strlen("xfree86")) == 0) {
- codeMap = code_map_qnum_to_xorgkbd;
- codeMapLen = code_map_qnum_to_xorgkbd_len;
- vlog.info("Using xorgkbd codemap\n");
- } else {
- vlog.info("Unknown keycode '%s', no codemap\n", keycodes);
- }
- XFree(keycodes);
- } else {
- vlog.debug("Unable to get keycode map\n");
- }
-
- XkbFreeKeyboard(desc, XkbAllComponentsMask, True);
- }
-
- #ifdef HAVE_XTEST
- int xtestEventBase;
- int xtestErrorBase;
-
- if (XTestQueryExtension(dpy, &xtestEventBase,
- &xtestErrorBase, &major, &minor)) {
- XTestGrabControl(dpy, True);
- vlog.info("XTest extension present - version %d.%d",major,minor);
- haveXtest = true;
- } else {
- #endif
- vlog.info("XTest extension not present");
- vlog.info("Unable to inject events or display while server is grabbed");
- #ifdef HAVE_XTEST
- }
- #endif
-
- #ifdef HAVE_XDAMAGE
- int xdamageErrorBase;
-
- if (XDamageQueryExtension(dpy, &xdamageEventBase, &xdamageErrorBase)) {
- haveDamage = true;
- } else {
- #endif
- vlog.info("DAMAGE extension not present");
- vlog.info("Will have to poll screen for changes");
- #ifdef HAVE_XDAMAGE
- }
- #endif
-
- #ifdef HAVE_XFIXES
- int xfixesErrorBase;
-
- if (XFixesQueryExtension(dpy, &xfixesEventBase, &xfixesErrorBase)) {
- XFixesSelectCursorInput(dpy, DefaultRootWindow(dpy),
- XFixesDisplayCursorNotifyMask);
- } else {
- #endif
- vlog.info("XFIXES extension not present");
- vlog.info("Will not be able to display cursors");
- #ifdef HAVE_XFIXES
- }
- #endif
-
- #ifdef HAVE_XRANDR
- int xrandrErrorBase;
-
- randrSyncSerial = 0;
- if (XRRQueryExtension(dpy, &xrandrEventBase, &xrandrErrorBase)) {
- XRRSelectInput(dpy, DefaultRootWindow(dpy),
- RRScreenChangeNotifyMask | RRCrtcChangeNotifyMask);
- /* Override TXWindow::init input mask */
- XSelectInput(dpy, DefaultRootWindow(dpy),
- PropertyChangeMask | StructureNotifyMask |
- ExposureMask | EnterWindowMask | LeaveWindowMask);
- } else {
- #endif
- vlog.info("RANDR extension not present");
- vlog.info("Will not be able to handle session resize");
- #ifdef HAVE_XRANDR
- }
- #endif
-
- TXWindow::setGlobalEventHandler(this);
- }
-
- XDesktop::~XDesktop() {
- if (running)
- stop();
- }
-
-
- void XDesktop::poll() {
- if (pb and not haveDamage)
- pb->poll(server);
- if (running) {
- Window root, child;
- int x, y, wx, wy;
- unsigned int mask;
-
- if (XQueryPointer(dpy, DefaultRootWindow(dpy), &root, &child,
- &x, &y, &wx, &wy, &mask)) {
- x -= geometry->offsetLeft();
- y -= geometry->offsetTop();
- server->setCursorPos(rfb::Point(x, y), false);
- }
- }
- }
-
-
- void XDesktop::start(VNCServer* vs) {
-
- // Determine actual number of buttons of the X pointer device.
- unsigned char btnMap[8];
- int numButtons = XGetPointerMapping(dpy, btnMap, 8);
- maxButtons = (numButtons > 8) ? 8 : numButtons;
- vlog.info("Enabling %d button%s of X pointer device",
- maxButtons, (maxButtons != 1) ? "s" : "");
-
- // Create an ImageFactory instance for producing Image objects.
- ImageFactory factory((bool)useShm);
-
- // Create pixel buffer and provide it to the server object.
- pb = new XPixelBuffer(dpy, factory, geometry->getRect());
- vlog.info("Allocated %s", pb->getImage()->classDesc());
-
- server = vs;
- server->setPixelBuffer(pb, computeScreenLayout());
-
- #ifdef HAVE_XDAMAGE
- if (haveDamage) {
- damage = XDamageCreate(dpy, DefaultRootWindow(dpy),
- XDamageReportRawRectangles);
- }
- #endif
-
- #ifdef HAVE_XFIXES
- Window root, child;
- int x, y, wx, wy;
- unsigned int mask;
- // Check whether the cursor is initially on our screen
- if (XQueryPointer(dpy, DefaultRootWindow(dpy), &root, &child,
- &x, &y, &wx, &wy, &mask))
- setCursor();
-
- #endif
-
- server->setLEDState(ledState);
-
- running = true;
- }
-
- void XDesktop::stop() {
- running = false;
-
- // Delete added keycodes
- deleteAddedKeysyms(dpy);
-
- #ifdef HAVE_XDAMAGE
- if (haveDamage)
- XDamageDestroy(dpy, damage);
- #endif
-
- delete queryConnectDialog;
- queryConnectDialog = 0;
-
- server->setPixelBuffer(0);
- server = 0;
-
- delete pb;
- pb = 0;
- }
-
- void XDesktop::terminate() {
- kill(getpid(), SIGTERM);
- }
-
- bool XDesktop::isRunning() {
- return running;
- }
-
- void XDesktop::queryConnection(network::Socket* sock,
- const char* userName)
- {
- assert(isRunning());
-
- // Someone already querying?
- if (queryConnectSock) {
- std::list<network::Socket*> sockets;
- std::list<network::Socket*>::iterator i;
-
- // Check if this socket is still valid
- server->getSockets(&sockets);
- for (i = sockets.begin(); i != sockets.end(); i++) {
- if (*i == queryConnectSock) {
- server->approveConnection(sock, false, "Another connection is currently being queried.");
- return;
- }
- }
- }
-
- if (!userName)
- userName = "(anonymous)";
-
- queryConnectSock = sock;
-
- CharArray address(sock->getPeerAddress());
- delete queryConnectDialog;
- queryConnectDialog = new QueryConnectDialog(dpy, address.buf,
- userName,
- queryConnectTimeout,
- this);
- queryConnectDialog->map();
- }
-
- void XDesktop::pointerEvent(const Point& pos, int buttonMask) {
- #ifdef HAVE_XTEST
- if (!haveXtest) return;
- XTestFakeMotionEvent(dpy, DefaultScreen(dpy),
- geometry->offsetLeft() + pos.x,
- geometry->offsetTop() + pos.y,
- CurrentTime);
- if (buttonMask != oldButtonMask) {
- for (int i = 0; i < maxButtons; i++) {
- if ((buttonMask ^ oldButtonMask) & (1<<i)) {
- if (buttonMask & (1<<i)) {
- XTestFakeButtonEvent(dpy, i+1, True, CurrentTime);
- } else {
- XTestFakeButtonEvent(dpy, i+1, False, CurrentTime);
- }
- }
- }
- }
- oldButtonMask = buttonMask;
- #endif
- }
-
- #ifdef HAVE_XTEST
- KeyCode XDesktop::XkbKeysymToKeycode(Display* dpy, KeySym keysym) {
- XkbDescPtr xkb;
- XkbStateRec state;
- unsigned int mods;
- unsigned keycode;
-
- xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
- if (!xkb)
- return 0;
-
- XkbGetState(dpy, XkbUseCoreKbd, &state);
- // XkbStateFieldFromRec() doesn't work properly because
- // state.lookup_mods isn't properly updated, so we do this manually
- mods = XkbBuildCoreState(XkbStateMods(&state), state.group);
-
- for (keycode = xkb->min_key_code;
- keycode <= xkb->max_key_code;
- keycode++) {
- KeySym cursym;
- unsigned int out_mods;
- XkbTranslateKeyCode(xkb, keycode, mods, &out_mods, &cursym);
- if (cursym == keysym)
- break;
- }
-
- if (keycode > xkb->max_key_code)
- keycode = 0;
-
- XkbFreeKeyboard(xkb, XkbAllComponentsMask, True);
-
- // Shift+Tab is usually ISO_Left_Tab, but RFB hides this fact. Do
- // another attempt if we failed the initial lookup
- if ((keycode == 0) && (keysym == XK_Tab) && (mods & ShiftMask))
- return XkbKeysymToKeycode(dpy, XK_ISO_Left_Tab);
-
- return keycode;
- }
- #endif
-
- KeyCode XDesktop::addKeysym(Display* dpy, KeySym keysym)
- {
- int types[1];
- unsigned int key;
- XkbDescPtr xkb;
- XkbMapChangesRec changes;
- KeySym *syms;
- KeySym upper, lower;
-
- xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
-
- if (!xkb)
- return 0;
-
- for (key = xkb->max_key_code; key >= xkb->min_key_code; key--) {
- if (XkbKeyNumGroups(xkb, key) == 0)
- break;
- }
-
- if (key < xkb->min_key_code)
- return 0;
-
- memset(&changes, 0, sizeof(changes));
-
- XConvertCase(keysym, &lower, &upper);
-
- if (upper == lower)
- types[XkbGroup1Index] = XkbOneLevelIndex;
- else
- types[XkbGroup1Index] = XkbAlphabeticIndex;
-
- XkbChangeTypesOfKey(xkb, key, 1, XkbGroup1Mask, types, &changes);
-
- syms = XkbKeySymsPtr(xkb,key);
- if (upper == lower)
- syms[0] = keysym;
- else {
- syms[0] = lower;
- syms[1] = upper;
- }
-
- changes.changed |= XkbKeySymsMask;
- changes.first_key_sym = key;
- changes.num_key_syms = 1;
-
- if (XkbChangeMap(dpy, xkb, &changes)) {
- vlog.info("Added unknown keysym %s to keycode %d", XKeysymToString(keysym), key);
- addedKeysyms[keysym] = key;
- return key;
- }
-
- return 0;
- }
-
- void XDesktop::deleteAddedKeysyms(Display* dpy) {
- XkbDescPtr xkb;
- xkb = XkbGetMap(dpy, XkbAllComponentsMask, XkbUseCoreKbd);
-
- if (!xkb)
- return;
-
- XkbMapChangesRec changes;
- memset(&changes, 0, sizeof(changes));
-
- KeyCode lowestKeyCode = xkb->max_key_code;
- KeyCode highestKeyCode = xkb->min_key_code;
- std::map<KeySym, KeyCode>::iterator it;
- for (it = addedKeysyms.begin(); it != addedKeysyms.end(); it++) {
- if (XkbKeyNumGroups(xkb, it->second) != 0) {
- // Check if we are removing keysym we added ourself
- if (XkbKeysymToKeycode(dpy, it->first) != it->second)
- continue;
-
- XkbChangeTypesOfKey(xkb, it->second, 0, XkbGroup1Mask, NULL, &changes);
-
- if (it->second < lowestKeyCode)
- lowestKeyCode = it->second;
-
- if (it->second > highestKeyCode)
- highestKeyCode = it->second;
- }
- }
-
- changes.changed |= XkbKeySymsMask;
- changes.first_key_sym = lowestKeyCode;
- changes.num_key_syms = highestKeyCode - lowestKeyCode + 1;
- XkbChangeMap(dpy, xkb, &changes);
-
- addedKeysyms.clear();
- }
-
- KeyCode XDesktop::keysymToKeycode(Display* dpy, KeySym keysym) {
- int keycode = 0;
-
- // XKeysymToKeycode() doesn't respect state, so we have to use
- // something slightly more complex
- keycode = XkbKeysymToKeycode(dpy, keysym);
-
- if (keycode != 0)
- return keycode;
-
- // TODO: try to further guess keycode with all possible mods as Xvnc does
-
- keycode = addKeysym(dpy, keysym);
-
- if (keycode == 0)
- vlog.error("Failure adding new keysym 0x%lx", keysym);
-
- return keycode;
- }
-
-
- void XDesktop::keyEvent(rdr::U32 keysym, rdr::U32 xtcode, bool down) {
- #ifdef HAVE_XTEST
- int keycode = 0;
-
- if (!haveXtest)
- return;
-
- // Use scan code if provided and mapping exists
- if (codeMap && rawKeyboard && xtcode < codeMapLen)
- keycode = codeMap[xtcode];
-
- if (!keycode) {
- if (pressedKeys.find(keysym) != pressedKeys.end())
- keycode = pressedKeys[keysym];
- else {
- keycode = keysymToKeycode(dpy, keysym);
- }
- }
-
- if (!keycode) {
- vlog.error("Could not map key event to X11 key code");
- return;
- }
-
- if (down)
- pressedKeys[keysym] = keycode;
- else
- pressedKeys.erase(keysym);
-
- vlog.debug("%d %s", keycode, down ? "down" : "up");
-
- XTestFakeKeyEvent(dpy, keycode, down, CurrentTime);
- #endif
- }
-
- void XDesktop::clientCutText(const char* /*str*/) {
- }
-
- ScreenSet XDesktop::computeScreenLayout()
- {
- ScreenSet layout;
- char buffer[2048];
-
- #ifdef HAVE_XRANDR
- XRRScreenResources *res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy));
- if (!res) {
- vlog.error("XRRGetScreenResources failed");
- return layout;
- }
- vncSetGlueContext(dpy, res);
-
- layout = ::computeScreenLayout(&outputIdMap);
- XRRFreeScreenResources(res);
-
- // Adjust the layout relative to the geometry
- ScreenSet::iterator iter, iter_next;
- Point offset(-geometry->offsetLeft(), -geometry->offsetTop());
- for (iter = layout.begin();iter != layout.end();iter = iter_next) {
- iter_next = iter; ++iter_next;
- iter->dimensions = iter->dimensions.intersect(geometry->getRect());
- if (iter->dimensions.is_empty())
- layout.remove_screen(iter->id);
- else
- iter->dimensions = iter->dimensions.translate(offset);
- }
- #endif
-
- // Make sure that we have at least one screen
- if (layout.num_screens() == 0)
- layout.add_screen(rfb::Screen(0, 0, 0, geometry->width(),
- geometry->height(), 0));
-
- vlog.debug("Detected screen layout:");
- layout.print(buffer, sizeof(buffer));
- vlog.debug("%s", buffer);
-
- return layout;
- }
-
- #ifdef HAVE_XRANDR
- /* Get the biggest mode which is equal or smaller to requested
- size. If no such mode exists, return the smallest. */
- static void GetSmallerMode(XRRScreenResources *res,
- XRROutputInfo *output,
- unsigned int *width, unsigned int *height)
- {
- XRRModeInfo best = {};
- XRRModeInfo smallest = {};
- smallest.width = -1;
- smallest.height = -1;
-
- for (int i = 0; i < res->nmode; i++) {
- for (int j = 0; j < output->nmode; j++) {
- if (output->modes[j] == res->modes[i].id) {
- if ((res->modes[i].width > best.width && res->modes[i].width <= *width) &&
- (res->modes[i].height > best.height && res->modes[i].height <= *height)) {
- best = res->modes[i];
- }
- if ((res->modes[i].width < smallest.width) && res->modes[i].height < smallest.height) {
- smallest = res->modes[i];
- }
- }
- }
- }
-
- if (best.id == 0 && smallest.id != 0) {
- best = smallest;
- }
-
- *width = best.width;
- *height = best.height;
- }
- #endif /* HAVE_XRANDR */
-
- unsigned int XDesktop::setScreenLayout(int fb_width, int fb_height,
- const rfb::ScreenSet& layout)
- {
- #ifdef HAVE_XRANDR
- XRRScreenResources *res = XRRGetScreenResources(dpy, DefaultRootWindow(dpy));
- if (!res) {
- vlog.error("XRRGetScreenResources failed");
- return rfb::resultProhibited;
- }
- vncSetGlueContext(dpy, res);
-
- /* The client may request a screen layout which is not supported by
- the Xserver. This happens, for example, when adjusting the size
- of a non-fullscreen vncviewer window. To handle this and other
- cases, we first call tryScreenLayout. If this fails, we try to
- adjust the request to one screen with a smaller mode. */
- vlog.debug("Testing screen layout");
- unsigned int tryresult = ::tryScreenLayout(fb_width, fb_height, layout, &outputIdMap);
- rfb::ScreenSet adjustedLayout;
- if (tryresult == rfb::resultSuccess) {
- adjustedLayout = layout;
- } else {
- vlog.debug("Impossible layout - trying to adjust");
-
- ScreenSet::const_iterator firstscreen = layout.begin();
- adjustedLayout.add_screen(*firstscreen);
- ScreenSet::iterator iter = adjustedLayout.begin();
- RROutput outputId = None;
-
- for (int i = 0;i < vncRandRGetOutputCount();i++) {
- unsigned int oi = vncRandRGetOutputId(i);
-
- /* Known? */
- if (outputIdMap.count(oi) == 0)
- continue;
-
- /* Find the corresponding screen... */
- if (iter->id == outputIdMap[oi]) {
- outputId = oi;
- } else {
- outputIdMap.erase(oi);
- }
- }
-
- /* New screen */
- if (outputId == None) {
- int i = getPreferredScreenOutput(&outputIdMap, std::set<unsigned int>());
- if (i != -1) {
- outputId = vncRandRGetOutputId(i);
- }
- }
- if (outputId == None) {
- vlog.debug("Resize adjust: Could not find corresponding screen");
- XRRFreeScreenResources(res);
- return rfb::resultInvalid;
- }
- XRROutputInfo *output = XRRGetOutputInfo(dpy, res, outputId);
- if (!output) {
- vlog.debug("Resize adjust: XRRGetOutputInfo failed");
- XRRFreeScreenResources(res);
- return rfb::resultInvalid;
- }
- if (!output->crtc) {
- vlog.debug("Resize adjust: Selected output has no CRTC");
- XRRFreeScreenResources(res);
- XRRFreeOutputInfo(output);
- return rfb::resultInvalid;
- }
- XRRCrtcInfo *crtc = XRRGetCrtcInfo(dpy, res, output->crtc);
- if (!crtc) {
- vlog.debug("Resize adjust: XRRGetCrtcInfo failed");
- XRRFreeScreenResources(res);
- XRRFreeOutputInfo(output);
- return rfb::resultInvalid;
- }
-
- unsigned int swidth = iter->dimensions.width();
- unsigned int sheight = iter->dimensions.height();
-
- switch (crtc->rotation) {
- case RR_Rotate_90:
- case RR_Rotate_270:
- unsigned int swap = swidth;
- swidth = sheight;
- sheight = swap;
- break;
- }
-
- GetSmallerMode(res, output, &swidth, &sheight);
- XRRFreeOutputInfo(output);
-
- switch (crtc->rotation) {
- case RR_Rotate_90:
- case RR_Rotate_270:
- unsigned int swap = swidth;
- swidth = sheight;
- sheight = swap;
- break;
- }
-
- XRRFreeCrtcInfo(crtc);
-
- if (sheight != 0 && swidth != 0) {
- vlog.debug("Adjusted resize request to %dx%d", swidth, sheight);
- iter->dimensions.setXYWH(0, 0, swidth, sheight);
- fb_width = swidth;
- fb_height = sheight;
- } else {
- vlog.error("Failed to find smaller or equal screen size");
- XRRFreeScreenResources(res);
- return rfb::resultInvalid;
- }
- }
-
- vlog.debug("Changing screen layout");
- unsigned int ret = ::setScreenLayout(fb_width, fb_height, adjustedLayout, &outputIdMap);
- XRRFreeScreenResources(res);
-
- /* Send a dummy event to the root window. When this event is seen,
- earlier change events (ConfigureNotify and/or CrtcChange) have
- been processed. An Expose event is used for simplicity; does not
- require any Atoms, and will not affect other applications. */
- unsigned long serial = XNextRequest(dpy);
- XExposeEvent ev = {}; /* zero x, y, width, height, count */
- ev.type = Expose;
- ev.display = dpy;
- ev.window = DefaultRootWindow(dpy);
- if (XSendEvent(dpy, DefaultRootWindow(dpy), False, ExposureMask, (XEvent*)&ev)) {
- while (randrSyncSerial < serial) {
- TXWindow::handleXEvents(dpy);
- }
- } else {
- vlog.error("XSendEvent failed");
- }
-
- /* The protocol requires that an error is returned if the requested
- layout could not be set. This is checked by
- VNCSConnectionST::setDesktopSize. Another ExtendedDesktopSize
- with reason=0 will be sent in response to the changes seen by the
- event handler. */
- if (adjustedLayout != layout)
- return rfb::resultInvalid;
-
- // Explicitly update the server state with the result as there
- // can be corner cases where we don't get feedback from the X server
- server->setScreenLayout(computeScreenLayout());
-
- return ret;
-
- #else
- return rfb::resultProhibited;
- #endif /* HAVE_XRANDR */
- }
-
-
- bool XDesktop::handleGlobalEvent(XEvent* ev) {
- if (ev->type == xkbEventBase + XkbEventCode) {
- XkbEvent *kb = (XkbEvent *)ev;
-
- if (kb->any.xkb_type != XkbIndicatorStateNotify)
- return false;
-
- vlog.debug("Got indicator update, mask is now 0x%x", kb->indicators.state);
-
- ledState = 0;
- for (int i = 0; i < XDESKTOP_N_LEDS; i++) {
- if (kb->indicators.state & ledMasks[i])
- ledState |= 1u << i;
- }
-
- if (running)
- server->setLEDState(ledState);
-
- return true;
- #ifdef HAVE_XDAMAGE
- } else if (ev->type == xdamageEventBase) {
- XDamageNotifyEvent* dev;
- Rect rect;
-
- if (!running)
- return true;
-
- dev = (XDamageNotifyEvent*)ev;
- rect.setXYWH(dev->area.x, dev->area.y, dev->area.width, dev->area.height);
- rect = rect.translate(Point(-geometry->offsetLeft(),
- -geometry->offsetTop()));
- server->add_changed(rect);
-
- return true;
- #endif
- #ifdef HAVE_XFIXES
- } else if (ev->type == xfixesEventBase + XFixesCursorNotify) {
- XFixesCursorNotifyEvent* cev;
-
- if (!running)
- return true;
-
- cev = (XFixesCursorNotifyEvent*)ev;
-
- if (cev->subtype != XFixesDisplayCursorNotify)
- return false;
-
- Window root, child;
- int x, y, wx, wy;
- unsigned int mask;
-
- // Check whether the cursor is initially on our screen
- if (!XQueryPointer(dpy, DefaultRootWindow(dpy), &root, &child,
- &x, &y, &wx, &wy, &mask))
- return false;
-
- return setCursor();
- #endif
- #ifdef HAVE_XRANDR
- } else if (ev->type == Expose) {
- XExposeEvent* eev = (XExposeEvent*)ev;
- randrSyncSerial = eev->serial;
-
- return false;
-
- } else if (ev->type == ConfigureNotify) {
- XConfigureEvent* cev = (XConfigureEvent*)ev;
-
- if (cev->window != DefaultRootWindow(dpy)) {
- return false;
- }
-
- XRRUpdateConfiguration(ev);
- geometry->recalc(cev->width, cev->height);
-
- if (!running) {
- return false;
- }
-
- if ((cev->width != pb->width() || (cev->height != pb->height()))) {
- // Recreate pixel buffer
- ImageFactory factory((bool)useShm);
- delete pb;
- pb = new XPixelBuffer(dpy, factory, geometry->getRect());
- server->setPixelBuffer(pb, computeScreenLayout());
-
- // Mark entire screen as changed
- server->add_changed(rfb::Region(Rect(0, 0, cev->width, cev->height)));
- }
-
- return true;
-
- } else if (ev->type == xrandrEventBase + RRNotify) {
- XRRNotifyEvent* rev = (XRRNotifyEvent*)ev;
-
- if (rev->window != DefaultRootWindow(dpy)) {
- return false;
- }
-
- if (!running)
- return false;
-
- if (rev->subtype == RRNotify_CrtcChange) {
- server->setScreenLayout(computeScreenLayout());
- }
-
- return true;
- #endif
- #ifdef HAVE_XFIXES
- } else if (ev->type == EnterNotify) {
- XCrossingEvent* cev;
-
- if (!running)
- return true;
-
- cev = (XCrossingEvent*)ev;
-
- if (cev->window != cev->root)
- return false;
-
- return setCursor();
- } else if (ev->type == LeaveNotify) {
- XCrossingEvent* cev;
-
- if (!running)
- return true;
-
- cev = (XCrossingEvent*)ev;
-
- if (cev->window == cev->root)
- return false;
-
- server->setCursor(0, 0, Point(), NULL);
- return true;
- #endif
- }
-
- return false;
- }
-
- void XDesktop::queryApproved()
- {
- assert(isRunning());
- server->approveConnection(queryConnectSock, true, 0);
- queryConnectSock = 0;
- }
-
- void XDesktop::queryRejected()
- {
- assert(isRunning());
- server->approveConnection(queryConnectSock, false,
- "Connection rejected by local user");
- queryConnectSock = 0;
- }
-
- bool XDesktop::setCursor()
- {
- XFixesCursorImage *cim;
-
- cim = XFixesGetCursorImage(dpy);
- if (cim == NULL)
- return false;
-
- // Copied from XserverDesktop::setCursor() in
- // unix/xserver/hw/vnc/XserverDesktop.cc and adapted to
- // handle long -> U32 conversion for 64-bit Xlib
- rdr::U8* cursorData;
- rdr::U8 *out;
- const unsigned long *pixels;
-
- cursorData = new rdr::U8[cim->width * cim->height * 4];
-
- // Un-premultiply alpha
- pixels = cim->pixels;
- out = cursorData;
- for (int y = 0; y < cim->height; y++) {
- for (int x = 0; x < cim->width; x++) {
- rdr::U8 alpha;
- rdr::U32 pixel = *pixels++;
-
- alpha = (pixel >> 24) & 0xff;
- if (alpha == 0)
- alpha = 1; // Avoid division by zero
-
- *out++ = ((pixel >> 16) & 0xff) * 255/alpha;
- *out++ = ((pixel >> 8) & 0xff) * 255/alpha;
- *out++ = ((pixel >> 0) & 0xff) * 255/alpha;
- *out++ = ((pixel >> 24) & 0xff);
- }
- }
-
- try {
- server->setCursor(cim->width, cim->height, Point(cim->xhot, cim->yhot),
- cursorData);
- } catch (rdr::Exception& e) {
- vlog.error("XserverDesktop::setCursor: %s",e.str());
- }
-
- delete [] cursorData;
- XFree(cim);
- return true;
- }
|